Current section

Files

Jump to
hecate_sdk src hecate_plugin_ws.erl
Raw

src/hecate_plugin_ws.erl

%%%-------------------------------------------------------------------
%%% @doc WebSocket helper for plugin live UIs.
%%%
%%% Provides a base behaviour for plugin WebSocket handlers.
%%% Handles JSON encoding/decoding and standard message framing.
%%%
%%% Usage in a plugin:
%%% -module(my_plugin_ws).
%%% -behaviour(cowboy_websocket).
%%% -export([init/2, websocket_init/1, websocket_handle/2,
%%% websocket_info/2]).
%%%
%%% init(Req, State) ->
%%% hecate_plugin_ws:upgrade(Req, State).
%%%
%%% websocket_handle({text, Data}, State) ->
%%% Msg = hecate_plugin_ws:decode(Data),
%%% %% handle Msg ...
%%% {reply, hecate_plugin_ws:encode(Response), State}.
%%% @end
%%%-------------------------------------------------------------------
-module(hecate_plugin_ws).
-export([upgrade/2, encode/1, decode/1, reply_json/2, reply_error/3]).
%% @doc Upgrade an HTTP request to WebSocket.
-spec upgrade(cowboy_req:req(), term()) ->
{cowboy_websocket, cowboy_req:req(), term()}.
upgrade(Req, State) ->
{cowboy_websocket, Req, State, #{
idle_timeout => 60000,
max_frame_size => 1048576 %% 1MB
}}.
%% @doc Encode a term as a JSON text frame.
-spec encode(term()) -> {text, iodata()}.
encode(Term) ->
{text, json:encode(Term)}.
%% @doc Decode a JSON binary into a map.
-spec decode(binary()) -> map().
decode(Data) ->
json:decode(Data).
%% @doc Send a JSON reply with a type tag.
-spec reply_json(Type :: binary(), Payload :: map()) -> {text, iodata()}.
reply_json(Type, Payload) ->
encode(Payload#{<<"type">> => Type}).
%% @doc Send a JSON error reply.
-spec reply_error(Code :: binary(), Message :: binary(), Details :: map()) ->
{text, iodata()}.
reply_error(Code, Message, Details) ->
encode(#{
<<"type">> => <<"error">>,
<<"code">> => Code,
<<"message">> => Message,
<<"details">> => Details
}).