./ct_report/coverage/mongoose_admin_api_messages.COVER.html

1 -module(mongoose_admin_api_messages).
2
3 -behaviour(mongoose_admin_api).
4 -export([routes/1]).
5
6 -behaviour(cowboy_rest).
7 -export([init/2,
8 is_authorized/2,
9 content_types_provided/2,
10 content_types_accepted/2,
11 allowed_methods/2,
12 to_json/2,
13 from_json/2]).
14
15 -ignore_xref([to_json/2, from_json/2]).
16
17 -import(mongoose_admin_api, [parse_body/1, parse_qs/1, try_handle_request/3, throw_error/2]).
18
19 -type req() :: cowboy_req:req().
20 -type state() :: mongoose_admin_api:state().
21
22 -spec routes(state()) -> mongoose_http_handler:routes().
23 routes(State) ->
24 105 [{"/messages/:owner/:with", ?MODULE, State},
25 {"/messages/[:owner]", ?MODULE, State}].
26
27 -spec init(req(), state()) -> {cowboy_rest, req(), state()}.
28 init(Req, State) ->
29 10 mongoose_admin_api:init(Req, State).
30
31 -spec is_authorized(req(), state()) -> {true | {false, iodata()}, req(), state()}.
32 is_authorized(Req, State) ->
33 10 mongoose_admin_api:is_authorized(Req, State).
34
35 -spec content_types_provided(req(), state()) ->
36 {[{{binary(), binary(), '*'}, atom()}], req(), state()}.
37 content_types_provided(Req, State) ->
38 10 {[
39 {{<<"application">>, <<"json">>, '*'}, to_json}
40 ], Req, State}.
41
42 -spec content_types_accepted(req(), state()) ->
43 {[{{binary(), binary(), '*'}, atom()}], req(), state()}.
44 content_types_accepted(Req, State) ->
45 9 {[
46 {{<<"application">>, <<"json">>, '*'}, from_json}
47 ], Req, State}.
48
49 -spec allowed_methods(req(), state()) -> {[binary()], req(), state()}.
50 allowed_methods(Req, State) ->
51 10 {[<<"OPTIONS">>, <<"GET">>, <<"POST">>], Req, State}.
52
53 %% @doc Called for a method of type "GET"
54 -spec to_json(req(), state()) -> {iodata() | stop, req(), state()}.
55 to_json(Req, State) ->
56 1 try_handle_request(Req, State, fun handle_get/2).
57
58 %% @doc Called for a method of type "POST"
59 -spec from_json(req(), state()) -> {true | stop, req(), state()}.
60 from_json(Req, State) ->
61 9 try_handle_request(Req, State, fun handle_post/2).
62
63 %% Internal functions
64
65 handle_get(Req, State) ->
66 1 Bindings = cowboy_req:bindings(Req),
67 1 OwnerJid = get_owner_jid(Bindings),
68 1 WithJid = get_with_jid(Bindings),
69 1 Args = parse_qs(Req),
70
:-(
Limit = get_limit(Args),
71
:-(
Before = get_before(Args),
72
:-(
case mongoose_stanza_api:lookup_recent_messages(OwnerJid, WithJid, Before, Limit, true) of
73 {ok, {Rows, _Limit}} ->
74
:-(
Messages = lists:map(fun row_to_map/1, Rows),
75
:-(
{jiffy:encode(Messages), Req, State};
76 {unknown_user, Msg} ->
77
:-(
throw_error(bad_request, Msg)
78 end.
79
80 handle_post(Req, State) ->
81 9 Args = parse_body(Req),
82 9 From = get_caller(Args),
83 7 To = get_to(Args),
84 5 Body = get_body(Args),
85 4 case mongoose_stanza_api:send_chat_message(undefined, From, To, Body) of
86 {ok, _} ->
87 2 {true, Req, State};
88 {_Error, Msg} ->
89 2 throw_error(bad_request, Msg)
90 end.
91
92 -spec row_to_map(mod_mam:message_row()) -> map().
93 row_to_map(#{id := Id, jid := From, packet := Msg}) ->
94
:-(
Jbin = jid:to_binary(From),
95
:-(
{Msec, _} = mod_mam_utils:decode_compact_uuid(Id),
96
:-(
MsgId = case xml:get_tag_attr(<<"id">>, Msg) of
97
:-(
{value, MId} -> MId;
98
:-(
false -> <<"">>
99 end,
100
:-(
Body = exml_query:path(Msg, [{element, <<"body">>}, cdata]),
101
:-(
#{sender => Jbin, timestamp => round(Msec / 1000000), message_id => MsgId, body => Body}.
102
103 get_limit(#{limit := LimitBin}) ->
104
:-(
try
105
:-(
Limit = binary_to_integer(LimitBin),
106
:-(
true = Limit >= 0,
107
:-(
Limit
108 catch
109
:-(
_:_ -> throw_error(bad_request, <<"Invalid limit">>)
110 end;
111
:-(
get_limit(#{}) -> 100.
112
113 get_before(#{before := BeforeBin}) ->
114
:-(
try
115
:-(
1000000 * binary_to_integer(BeforeBin)
116 catch
117
:-(
_:_ -> throw_error(bad_request, <<"Invalid value of 'before'">>)
118 end;
119
:-(
get_before(#{}) -> undefined.
120
121 get_owner_jid(#{owner := Owner}) ->
122 1 case jid:from_binary(Owner) of
123
:-(
error -> throw_error(bad_request, <<"Invalid owner JID">>);
124 1 OwnerJid -> OwnerJid
125 end;
126
:-(
get_owner_jid(#{}) -> throw_error(not_found, <<"Missing owner JID">>).
127
128 get_with_jid(#{with := With}) ->
129 1 case jid:from_binary(With) of
130
:-(
error -> throw_error(bad_request, <<"Invalid interlocutor JID">>);
131 1 WithJid -> WithJid
132 end;
133
:-(
get_with_jid(#{}) -> undefined.
134
135 get_caller(#{caller := Caller}) ->
136 8 case jid:from_binary(Caller) of
137 1 error -> throw_error(bad_request, <<"Invalid sender JID">>);
138 7 CallerJid -> CallerJid
139 end;
140 1 get_caller(#{}) -> throw_error(bad_request, <<"Missing sender JID">>).
141
142 get_to(#{to := To}) ->
143 6 case jid:from_binary(To) of
144 1 error -> throw_error(bad_request, <<"Invalid recipient JID">>);
145 5 ToJid -> ToJid
146 end;
147 1 get_to(#{}) -> throw_error(bad_request, <<"Missing recipient JID">>).
148
149 4 get_body(#{body := Body}) -> Body;
150 1 get_body(#{}) -> throw_error(bad_request, <<"Missing message body">>).
Line Hits Source