Current section

Files

Jump to
grisp_connect src grisp_connect_api.erl
Raw

src/grisp_connect_api.erl

%% @doc Library module containing the jsonrpc API logic
-module(grisp_connect_api).
-export([request/3]).
-export([notify/3]).
-export([handle_msg/1]).
-include_lib("kernel/include/logger.hrl").
%--- Macros --------------------------------------------------------------------
-define(method_get, <<"get">>).
-define(method_post, <<"post">>).
-define(method_patch, <<"patch">>).
-define(method_delete, <<"delete">>).
%--- API -----------------------------------------------------------------------
% #doc Assembles a jsonrpc request and its uuid
-spec request(Method :: atom() | binary(),
Type :: atom() | binary(),
Params :: map()) -> {ID :: binary(), Encoded :: binary()}.
request(Method, Type, Params) ->
ID = id(),
Rpc = {request, Method, maps:put(type, Type, Params), ID},
Encoded = grisp_connect_jsonrpc:encode(Rpc),
{ID, Encoded}.
% #doc Assembles a jsonrpc notification
-spec notify(Method :: atom() | binary(),
Type :: atom() | binary(),
Params :: map()) -> Encoded :: binary().
notify(Method, Type, Params) ->
Rpc = {notification, Method, maps:put(type, Type, Params)},
grisp_connect_jsonrpc:encode(Rpc).
% @doc Indentifies if the message is a request or a reply to a previous request.
% In case it was a request, returns the reply to be sent to the peer.
% In case it was a response, returns the parsed ID and content to be handled by
% the caller.
-spec handle_msg(JSON :: binary()) ->
{send_response, Response :: binary()} |
{handle_response, ID :: binary(), {ok, Result :: map()} | {error, atom()}}.
handle_msg(JSON) ->
JSON_RPC = grisp_connect_jsonrpc:decode(JSON),
handle_jsonrpc(JSON_RPC).
%--- Internal Funcitons --------------------------------------------------------
handle_jsonrpc({batch, Batch}) ->
handle_rpc_messages(Batch, []);
handle_jsonrpc({single, Rpc}) ->
handle_rpc_messages([Rpc], []).
handle_rpc_messages([], Replies) -> lists:reverse(Replies);
handle_rpc_messages([{request, M, Params, ID} | Batch], Replies)
when M == ?method_post;
M == ?method_get ->
handle_rpc_messages(Batch, [handle_request(M, Params, ID) | Replies]);
handle_rpc_messages([{result, _, _} = Res| Batch], Replies) ->
handle_rpc_messages(Batch, [handle_response(Res)| Replies]);
handle_rpc_messages([{error, _Code, _Msg, _Data, _ID} = E | Batch], Replies) ->
?LOG_INFO("Received JsonRPC error: ~p",[E]),
handle_rpc_messages(Batch, [handle_response(E)| Replies]);
handle_rpc_messages([{internal_error, _, _} = E | Batch], Replies) ->
?LOG_ERROR("JsonRPC: ~p",[E]),
handle_rpc_messages(Batch, Replies).
handle_request(?method_get, #{type := <<"system_info">>} = _Params, ID) ->
Info = grisp_connect_updater:system_info(),
{send_response, grisp_connect_jsonrpc:encode({result, Info, ID})};
handle_request(?method_post, #{type := <<"start_update">>} = Params, ID) ->
try
URL = maps:get(url, Params),
Reply = case grisp_connect_updater:start_update(URL) of
{error, grisp_updater_unavailable} ->
{error, -10, grisp_updater_unavailable, undefined, ID};
{error, already_updating} ->
{error, -11, already_updating, undefined, ID};
{error, boot_system_not_validated} ->
{error, -12, boot_system_not_validated, undefined, ID};
{error, Reason} ->
ReasonBinary = iolist_to_binary(io_lib:format("~p", [Reason])),
grisp_connect_jsonrpc:format_error({internal_error, ReasonBinary, ID});
ok ->
{result, ok, ID}
end,
{send_response, grisp_connect_jsonrpc:encode(Reply)}
catch
throw:bad_key ->
{send_response,
grisp_connect_jsonrpc:format_error(
{internal_error, invalid_params, ID})}
end;
handle_request(?method_post, #{type := <<"validate">>}, ID) ->
Reply = case grisp_connect_updater:validate() of
{error, grisp_updater_unavailable} ->
{error, -10, grisp_updater_unavailable, undefined, ID};
{error, {validate_from_unbooted, PartitionIndex}} ->
{error, -13, validate_from_unbooted, PartitionIndex, ID};
{error, Reason} ->
ReasonBinary = iolist_to_binary(io_lib:format("~p", [Reason])),
grisp_connect_jsonrpc:format_error({internal_error, ReasonBinary, ID});
ok ->
{result, ok, ID}
end,
{send_response, grisp_connect_jsonrpc:encode(Reply)};
handle_request(?method_post, #{type := <<"reboot">>}, ID) ->
grisp_connect_client:reboot(),
{send_response, grisp_connect_jsonrpc:encode({result, ok, ID})};
handle_request(?method_post, #{type := <<"cancel">>}, ID) ->
Reply = case grisp_connect_updater:cancel() of
{error, grisp_updater_unavailable} ->
{error, -10, grisp_updater_unavailable, undefined, ID};
ok ->
{result, ok, ID}
end,
{send_response, grisp_connect_jsonrpc:encode(Reply)};
handle_request(_T, _P, ID) ->
Error = {internal_error, method_not_found, ID},
FormattedError = grisp_connect_jsonrpc:format_error(Error),
{send_response, grisp_connect_jsonrpc:encode(FormattedError)}.
handle_response(Response) ->
{ID, Reply} = case Response of
{result, Result, ID0} ->
{ID0, {ok, Result}};
{error, Code, _Message, _Data, ID0} ->
{ID0, {error, error_atom(Code)}}
end,
{handle_response, ID, Reply}.
error_atom(-1) -> device_not_linked;
error_atom(-2) -> token_expired;
error_atom(-3) -> device_already_linked;
error_atom(-4) -> invalid_token;
error_atom(_) -> jsonrpc_error.
id() ->
list_to_binary(integer_to_list(erlang:unique_integer())).