./ct_report/coverage/mongoose_client_api_rooms.COVER.html

1 -module(mongoose_client_api_rooms).
2
3 -behaviour(mongoose_client_api).
4 -export([routes/0]).
5
6 -behaviour(cowboy_rest).
7 -export([trails/0,
8 init/2,
9 is_authorized/2,
10 content_types_provided/2,
11 content_types_accepted/2,
12 allowed_methods/2,
13 to_json/2,
14 from_json/2]).
15
16 %% Used by mongoose_client_api_rooms_*
17 -export([get_room_jid/3,
18 get_room_name/1,
19 get_room_subject/1]).
20
21 -ignore_xref([from_json/2, to_json/2, trails/0]).
22
23 -import(mongoose_client_api, [parse_body/1, try_handle_request/3, throw_error/2]).
24
25 -type req() :: cowboy_req:req().
26 -type state() :: map().
27
28 -include("jlib.hrl").
29
30 -spec routes() -> mongoose_http_handler:routes().
31 routes() ->
32 57 [{"/rooms/[:id]", ?MODULE, #{}}].
33
34 trails() ->
35 57 mongoose_client_api_rooms_doc:trails().
36
37 -spec init(req(), state()) -> {cowboy_rest, req(), state()}.
38 init(Req, Opts) ->
39 55 mongoose_client_api:init(Req, Opts).
40
41 -spec is_authorized(req(), state()) -> {true | {false, iodata()}, req(), state()}.
42 is_authorized(Req, State) ->
43 55 mongoose_client_api:is_authorized(Req, State).
44
45 -spec content_types_provided(req(), state()) ->
46 {[{{binary(), binary(), '*'}, atom()}], req(), state()}.
47 content_types_provided(Req, State) ->
48 85 {[
49 {{<<"application">>, <<"json">>, '*'}, to_json}
50 ], Req, State}.
51
52 -spec content_types_accepted(req(), state()) ->
53 {[{{binary(), binary(), '*'}, atom()}], req(), state()}.
54 content_types_accepted(Req, State) ->
55 76 {[
56 {{<<"application">>, <<"json">>, '*'}, from_json}
57 ], Req, State}.
58
59 -spec allowed_methods(req(), state()) -> {[binary()], req(), state()}.
60 allowed_methods(Req, State) ->
61 55 {[<<"OPTIONS">>, <<"GET">>, <<"POST">>, <<"PUT">>], Req, State}.
62
63 %% @doc Called for a method of type "GET"
64 -spec to_json(req(), state()) -> {iodata() | stop, req(), state()}.
65 to_json(Req, State) ->
66 19 try_handle_request(Req, State, fun handle_get/2).
67
68 %% @doc Called for a method of type "POST" and "PUT"
69 -spec from_json(req(), state()) -> {stop, req(), state()}.
70 from_json(Req, State) ->
71 36 F = case cowboy_req:method(Req) of
72 25 <<"POST">> -> fun handle_post/2;
73 11 <<"PUT">> -> fun handle_put/2
74 end,
75 36 try_handle_request(Req, State, F).
76
77 %% Internal functions
78
79 handle_get(Req, State = #{jid := UserJid}) ->
80 19 Bindings = cowboy_req:bindings(Req),
81 19 case get_room_jid(Bindings, State, optional) of
82 undefined ->
83 3 {ok, Rooms} = mod_muc_light_api:get_user_rooms(UserJid),
84 3 {jiffy:encode(lists:flatmap(fun room_us_to_json/1, Rooms)), Req, State};
85 RoomJid ->
86 15 case mod_muc_light_api:get_room_info(RoomJid, UserJid) of
87 {ok, Info} ->
88 13 {jiffy:encode(room_info_to_json(Info)), Req, State};
89 {room_not_found, Msg} ->
90 1 throw_error(not_found, Msg);
91 {not_room_member, Msg} ->
92 1 throw_error(denied, Msg)
93 end
94 end.
95
96 handle_post(Req, State = #{jid := UserJid}) ->
97 25 MUCLightDomain = muc_light_domain(State),
98 25 Args = parse_body(Req),
99 25 Name = get_room_name(Args),
100 25 Subject = get_room_subject(Args),
101 25 Config = #{<<"roomname">> => Name, <<"subject">> => Subject},
102 25 {ok, #{jid := RoomJid}} = mod_muc_light_api:create_room(MUCLightDomain, UserJid, Config),
103 25 room_created(Req, State, RoomJid).
104
105 handle_put(Req, State = #{jid := UserJid}) ->
106 11 Bindings = cowboy_req:bindings(Req),
107 11 #jid{luser = RoomId, lserver = MUCLightDomain} = get_room_jid(Bindings, State, required),
108 8 Args = parse_body(Req),
109 8 Name = get_room_name(Args),
110 7 Subject = get_room_subject(Args),
111 6 Config = #{<<"roomname">> => Name, <<"subject">> => Subject},
112 6 case mod_muc_light_api:create_room(MUCLightDomain, RoomId, UserJid, Config) of
113 {ok, #{jid := RoomJid}} ->
114 5 room_created(Req, State, RoomJid);
115 {already_exists, Msg} ->
116 1 throw_error(denied, Msg)
117 end.
118
119 room_created(Req, State, RoomJid) ->
120 30 RespBody = #{<<"id">> => RoomJid#jid.luser},
121 30 Req2 = cowboy_req:set_resp_body(jiffy:encode(RespBody), Req),
122 30 Req3 = cowboy_req:reply(201, Req2),
123 30 {stop, Req3, State}.
124
125 -spec room_us_to_json(jid:simple_bare_jid()) -> [jiffy:json_value()].
126 room_us_to_json({RoomU, RoomS}) ->
127 2 #jid{luser = RoomId} = RoomJid = jid:make_noprep(RoomU, RoomS, <<>>),
128 2 case mod_muc_light_api:get_room_info(RoomJid) of
129 {ok, Info} ->
130 2 NS = room_name_and_subject(Info),
131 2 [NS#{id => RoomId}];
132 {room_not_found, _} ->
133
:-(
[] % room was removed after listing rooms, but before this query
134 end.
135
136 -spec room_info_to_json(mod_muc_light_api:room()) -> jiffy:json_value().
137 room_info_to_json(Info = #{aff_users := AffUsers}) ->
138 13 NS = room_name_and_subject(Info),
139 13 NS#{participants => lists:map(fun user_to_json/1, AffUsers)}.
140
141 room_name_and_subject(#{options := #{<<"roomname">> := Name, <<"subject">> := Subject}}) ->
142 15 #{name => Name, subject => Subject}.
143
144 user_to_json({UserServer, Role}) ->
145 18 #{user => jid:to_binary(UserServer),
146 role => Role}.
147
148 get_room_jid(#{id := IdOrJid}, State, _) ->
149 87 MUCLightDomain = muc_light_domain(State),
150 86 case jid:nodeprep(IdOrJid) of
151 error ->
152 15 case jid:from_binary(IdOrJid) of
153 error ->
154 1 throw_error(bad_request, <<"Invalid room ID">>);
155 #jid{lserver = MUCLightDomain} = Jid ->
156 13 Jid;
157 #jid{} ->
158 1 throw_error(bad_request, <<"Invalid MUC Light domain">>)
159 end;
160 RoomId when RoomId =/= <<>> ->
161 71 jid:make_noprep(RoomId, MUCLightDomain, <<>>)
162 end;
163 1 get_room_jid(#{}, _State, required) -> throw_error(bad_request, <<"Missing room ID">>);
164 3 get_room_jid(#{}, _State, optional) -> undefined.
165
166 muc_light_domain(#{creds := Creds}) ->
167 112 HostType = mongoose_credentials:host_type(Creds),
168 112 LServer = mongoose_credentials:lserver(Creds),
169 112 try
170 112 mod_muc_light_utils:server_host_to_muc_host(HostType, LServer)
171 catch
172 1 _:_ -> throw_error(not_found, <<"MUC Light server not found">>)
173 end.
174
175 38 get_room_name(#{name := Name}) -> Name;
176 1 get_room_name(#{}) -> throw_error(bad_request, <<"Missing room name">>).
177
178 37 get_room_subject(#{subject := Subject}) -> Subject;
179 1 get_room_subject(#{}) -> throw_error(bad_request, <<"Missing room subject">>).
Line Hits Source