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 |
46 |
[{"/rooms/[:id]", ?MODULE, #{}}]. |
33 |
|
|
34 |
|
trails() -> |
35 |
46 |
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">>). |