Skip to content

Commit

Permalink
Implement the admin part of MUC Light API for GraphQL
Browse files Browse the repository at this point in the history
  • Loading branch information
Premwoik committed Feb 15, 2022
1 parent a957367 commit 6411541
Show file tree
Hide file tree
Showing 18 changed files with 1,005 additions and 197 deletions.
1 change: 1 addition & 0 deletions big_tests/default.spec
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
{suites, "tests", graphql_SUITE}.
{suites, "tests", graphql_account_SUITE}.
{suites, "tests", graphql_domain_SUITE}.
{suites, "tests", graphql_muc_light_SUITE}.
{suites, "tests", graphql_session_SUITE}.
{suites, "tests", graphql_stanza_SUITE}.
{suites, "tests", inbox_SUITE}.
Expand Down
4 changes: 2 additions & 2 deletions big_tests/dynamic_domains.spec
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,11 @@

{suites, "tests", graphql_SUITE}.
{suites, "tests", graphql_account_SUITE}.
{suites, "tests", graphql_domain_SUITE}.
{suites, "tests", graphql_muc_light_SUITE}.
{suites, "tests", graphql_session_SUITE}.
{suites, "tests", graphql_stanza_SUITE}.

{suites, "tests", graphql_domain_SUITE}.

{suites, "tests", inbox_SUITE}.

{suites, "tests", inbox_extensions_SUITE}.
Expand Down
380 changes: 380 additions & 0 deletions big_tests/tests/graphql_muc_light_SUITE.erl

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions big_tests/tests/muc_light_http_api_SUITE.erl
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,7 @@ delete_room_by_non_owner(Config) ->
[{alice, 1}, {bob, 1}, {kate, 1}],
fun(Alice, Bob, Kate)->
{{<<"403">>, <<"Forbidden">>},
<<"you can not delete this room">>} =
<<"You cannot delete this room">>} =
check_delete_room(Config, RoomName, RoomName,
Alice, [Bob, Kate], Bob)
end).
Expand All @@ -205,7 +205,7 @@ delete_non_existent_room(Config) ->
escalus:fresh_story(Config,
[{alice, 1}, {bob, 1}, {kate, 1}],
fun(Alice, Bob, Kate)->
{{<<"404">>, _}, <<"room does not exist">>} =
{{<<"404">>, _}, <<"Cannot remove not existing room">>} =
check_delete_room(Config, RoomName, <<"some_non_existent_room">>,
Alice, [Bob, Kate], Alice)
end).
Expand All @@ -215,7 +215,7 @@ delete_room_without_having_a_membership(Config) ->
escalus:fresh_story(Config,
[{alice, 1}, {bob, 1}, {kate, 1}],
fun(Alice, Bob, Kate)->
{{<<"403">>, _}, <<"given user does not occupy this room">>} =
{{<<"403">>, _}, <<"Given user does not occupy this room">>} =
check_delete_room(Config, RoomName, RoomName,
Alice, [Bob], Kate)
end).
Expand Down
6 changes: 3 additions & 3 deletions big_tests/tests/rest_client_SUITE.erl
Original file line number Diff line number Diff line change
Expand Up @@ -39,14 +39,14 @@ all() ->
{group, security}].

groups() ->
G = [{messages_with_props, [parallel], message_with_props_test_cases()},
_G = [{messages_with_props, [parallel], message_with_props_test_cases()},
{messages_with_thread, [parallel], message_with_thread_test_cases()},
{messages, [parallel], message_test_cases()},
{muc, [pararell], muc_test_cases()},
{muc_config, [], muc_config_cases()},
{roster, [parallel], roster_test_cases()},
{security, [], security_test_cases()}],
ct_helper:repeat_all_until_all_ok(G).
{security, [], security_test_cases()}].
%ct_helper:repeat_all_until_all_ok(G).

message_test_cases() ->
[msg_is_sent_and_delivered_over_xmpp,
Expand Down
4 changes: 4 additions & 0 deletions priv/graphql/schemas/admin/admin_schema.gql
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ type AdminQuery{
session: SessionAdminQuery
"Stanza management"
stanza: StanzaAdminQuery
"MUC Light room management"
muc_light: MUCLightAdminQuery
}

"""
Expand All @@ -33,4 +35,6 @@ type AdminMutation @protected{
session: SessionAdminMutation
"Stanza management"
stanza: StanzaAdminMutation
"MUC Light room management"
muc_light: MUCLightAdminMutation
}
55 changes: 55 additions & 0 deletions priv/graphql/schemas/admin/muc_light.gql
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
"""
Allow admin to manage Multi-User Chat Light rooms.
"""
type MUCLightAdminMutation @protected{
"Create a MUC light room under the given XMPP hostname"
createRoom(domain: String!, name: String!, owner: JID!, subject: String!, id: String): Room
"Change configuration of MUC Light room"
changeRoomConfiguration(id: String!, domain: String!, name: String!, owner: JID!, subject: String!): Room
"Invite a user to a MUC Light room"
inviteUser(domain: String!, name: String!, sender: JID!, recipient: JID!): String
"Remove a MUC light room"
deleteRoom(id: String!, domain: String!): String
"Remove a user from a room"
kickUser(domain: String!, id: String!, user: JID!): String
"Send a message to a MUC Light room"
sendMessageToRoom(domain: String!, name: String!, from: JID!, body: String!): String
}

"""
Allow admin to get information about Multi-User Chat Light rooms.
"""
type MUCLightAdminQuery @protected{
"Read messages"
getRoomMessages(domain: String!, id: String!): StanzasPayload
"Get configuration of the MUC Light room"
getRoomConfig(domain: String!, id: String!): Room
"Get users list of MUC Light room"
listRoomUsers(domain: String!, id: String!): [RoomUser!]
"Get the list of rooms that the given user participates in"
listUserRooms(user: JID!): [JID!]

}

type RoomPayload{
message: String!
room: Room
}

type Room{
jid: JID
name: String
subject: String
participants: [RoomUser!]
}

type RoomUser{
jid: JID
affiliance: String
}

enum Affiliance{
OWNER
MEMBER
NONE
}
2 changes: 2 additions & 0 deletions src/graphql/admin/mongoose_graphql_admin_mutation.erl
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ execute(_Ctx, _Obj, <<"domains">>, _Args) ->
{ok, admin};
execute(_Ctx, _Obj, <<"account">>, _Args) ->
{ok, account};
execute(_Ctx, _Obj, <<"muc_light">>, _Args) ->
{ok, muc_light};
execute(_Ctx, _Obj, <<"session">>, _Opts) ->
{ok, session};
execute(_Ctx, _Obj, <<"stanza">>, _) ->
Expand Down
2 changes: 2 additions & 0 deletions src/graphql/admin/mongoose_graphql_admin_query.erl
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ execute(_Ctx, _Obj, <<"domains">>, _Args) ->
{ok, admin};
execute(_Ctx, _Obj, <<"account">>, _Args) ->
{ok, account};
execute(_Ctx, _Obj, <<"muc_light">>, _Args) ->
{ok, muc_light};
execute(_Ctx, _Obj, <<"session">>, _Opts) ->
{ok, session};
execute(_Ctx, _Obj, <<"stanza">>, _Opts) ->
Expand Down
64 changes: 64 additions & 0 deletions src/graphql/admin/mongoose_graphql_muc_light_admin_mutation.erl
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
-module(mongoose_graphql_muc_light_admin_mutation).

-export([execute/4]).

-ignore_xref([execute/4]).

-import(mongoose_graphql_helper, [make_error/2, format_result/2]).

-import(mongoose_graphql_muc_light_helper, [make_room/1, make_ok_user/1]).

execute(_Ctx, _Obj, <<"createRoom">>, Args) ->
create_room(Args);
execute(_Ctx, _Obj, <<"changeRoomConfiguration">>, Args) ->
change_room_config(Args);
execute(_Ctx, _Obj, <<"inviteUser">>, Args) ->
invite_user(Args);
execute(_Ctx, _Obj, <<"deleteRoom">>, Args) ->
delete_room(Args);
execute(_Ctx, _Obj, <<"kickUser">>, Args) ->
kick_user(Args);
execute(_Ctx, _Obj, <<"sendMessageToRoom">>, Args) ->
send_msg_to_room(Args).

create_room(#{<<"id">> := null} = Args) ->
create_room(Args#{<<"id">> => <<>>});
create_room(#{<<"id">> := Id, <<"domain">> := Domain, <<"name">> := RoomName,
<<"owner">> := CreatorJID, <<"subject">> := Subject}) ->
case mod_muc_light_api:create_room(Domain, Id, RoomName, CreatorJID, Subject) of
{ok, Room} ->
{ok, make_room(Room)};
Err ->
make_error(Err, #{})
end.

change_room_config(#{<<"id">> := RoomID, <<"domain">> := Domain, <<"name">> := RoomName,
<<"owner">> := OwnerJID, <<"subject">> := Subject}) ->
case mod_muc_light_api:change_room_config(Domain, RoomID, RoomName, OwnerJID, Subject) of
{ok, Room} ->
{ok, make_room(Room)};
Err ->
make_error(Err, #{})
end.

delete_room(#{<<"domain">> := Domain, <<"id">> := RoomID}) ->
Result = mod_muc_light_api:delete_room(Domain, RoomID),
format_result(Result, #{}).

invite_user(#{<<"domain">> := Domain, <<"name">> := Name, <<"sender">> := SenderJID,
<<"recipient">> := RecipientJID}) ->
Result = mod_muc_light_api:invite_to_room(Domain, Name, SenderJID, RecipientJID),
format_result(Result, #{}).

kick_user(#{<<"domain">> := Domain, <<"id">> := RoomID, <<"user">> := UserJID}) ->
Result = mod_muc_light_api:remove_user_from_room(Domain, RoomID, UserJID, UserJID),
format_result(Result, #{}).

send_msg_to_room(#{<<"domain">> := Domain, <<"name">> := RoomName, <<"from">> := FromJID,
<<"body">> := Message}) ->
Result = mod_muc_light_api:send_message(Domain, RoomName, FromJID, Message),
format_result(Result, #{}).

%% Internal


66 changes: 66 additions & 0 deletions src/graphql/admin/mongoose_graphql_muc_light_admin_query.erl
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
-module(mongoose_graphql_muc_light_admin_query).

-export([execute/4]).

-ignore_xref([execute/4]).

-include("../mongoose_graphql_types.hrl").

-import(mongoose_graphql_helper, [make_error/2, format_result/2]).

-import(mongoose_graphql_muc_light_helper, [make_room/1, make_ok_user/1]).

execute(_Ctx, _Obj, <<"listUserRooms">>, Args) ->
list_user_rooms(Args);
execute(_Ctx, _Obj, <<"listRoomUsers">>, Args) ->
list_room_users(Args);
execute(_Ctx, _Obj, <<"getRoomConfig">>, Args) ->
get_room_config(Args);
execute(_Ctx, _Obj, <<"getRoomMessages">>, Args) ->
get_room_messages(Args).

-spec list_user_rooms(map()) -> {ok, [binary()]} | {error, resolver_error()}.
list_user_rooms(#{<<"user">> := UserJID}) ->
case mod_muc_light_api:get_user_rooms(UserJID) of
{ok, Rooms} ->
{ok, [{ok, R} || R <- Rooms]};
Err ->
make_error(Err, #{})
end.

-spec list_room_users(map()) -> {ok, [map()]} | {error, resolver_error()}.
list_room_users(#{<<"domain">> := Domain, <<"id">> := RoomID}) ->
case mod_muc_light_api:get_room_aff(Domain, RoomID) of
{ok, Affs} ->
{ok, [make_ok_user(A) || A <- Affs]};
Err ->
make_error(Err, #{})
end.

-spec get_room_config(map()) -> {ok, map()} | {error, resolver_error()}.
get_room_config(#{<<"domain">> := Domain, <<"id">> := RoomID}) ->
case mod_muc_light_api:get_room_info(Domain, RoomID) of
{ok, Room} ->
{ok, make_room(Room)};
Err ->
make_error(Err, #{})
end.

-spec get_room_messages(map()) -> {ok, map()} | {error, resolver_error()}.
get_room_messages(#{<<"domain">> := Domain, <<"id">> := RoomID}) ->
case mod_muc_light_api:get_room_messages(Domain, RoomID) of
{ok, Rows} ->
Maps = lists:map(fun row_to_map/1, Rows),
{ok, #{<<"stanzas">> => Maps, <<"limit">> => 0}};
Err ->
make_error(Err, #{})
end.

-spec row_to_map(mod_mam:message_row()) -> {ok, map()}.
row_to_map(#{id := Id, jid := From, packet := Msg}) ->
{Microseconds, _} = mod_mam_utils:decode_compact_uuid(Id),
StanzaID = mod_mam_utils:mess_id_to_external_binary(Id),
Map = #{<<"sender">> => From, <<"timestamp">> => Microseconds,
<<"stanza_id">> => StanzaID, <<"stanza">> => Msg},
{ok, Map}.

2 changes: 2 additions & 0 deletions src/graphql/mongoose_graphql.erl
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,8 @@ admin_mapping_rules() ->
'StanzaAdminQuery' => mongoose_graphql_stanza_admin_query,
'AccountAdminQuery' => mongoose_graphql_account_admin_query,
'AccountAdminMutation' => mongoose_graphql_account_admin_mutation,
'MUCLightAdminMutation' => mongoose_graphql_muc_light_admin_mutation,
'MUCLightAdminQuery' => mongoose_graphql_muc_light_admin_query,
'Domain' => mongoose_graphql_domain,
default => mongoose_graphql_default},
interfaces => #{default => mongoose_graphql_default},
Expand Down
3 changes: 2 additions & 1 deletion src/graphql/mongoose_graphql_errors.erl
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@ err(_Ctx, ErrorTerm) ->

%% callback invoked when resolver crashes
-spec crash(map(), term()) -> err_msg().
crash(_Ctx, #{type := Type}) ->
crash(_Ctx, #{type := Type} = E) ->
io:format("~p", [E]),
#{message => <<"Unexpected ", Type/binary, " resolver crash">>,
extensions => #{code => resolver_crash}}.

Expand Down
12 changes: 12 additions & 0 deletions src/graphql/mongoose_graphql_muc_light_helper.erl
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
-module(mongoose_graphql_muc_light_helper).

-export([make_room/1, make_ok_user/1]).

-spec make_room(mod_muc_light_api:room()) -> map().
make_room(#{jid := JID, name := Name, subject := Subject, aff_users := Users}) ->
Participants = lists:map(fun make_ok_user/1, Users),
#{<<"jid">> => JID, <<"name">> => Name, <<"subject">> => Subject,
<<"participants">> => Participants}.

make_ok_user({JID, Aff}) ->
{ok, #{<<"jid">> => JID, <<"affiliance">> => atom_to_binary(Aff)}}.
9 changes: 5 additions & 4 deletions src/mongoose_client_api/mongoose_client_api_rooms_config.erl
Original file line number Diff line number Diff line change
Expand Up @@ -54,11 +54,11 @@ from_json(Req, State) ->

handle_request(Method, JSONData, Req, State) ->
case handle_request_by_method(Method, JSONData, Req, State) of
ok ->
{ok, _} ->
{true, Req, State};
{error, internal, not_allowed} ->
{not_allowed, _} ->
mongoose_client_api:forbidden_request(Req, State);
{error, internal, _} ->
{_, _} ->
{false, Req, State}
end.

Expand All @@ -67,4 +67,5 @@ handle_request_by_method(<<"PUT">>,
Req, State) ->
mongoose_client_api_rooms:assert_room_id_set(Req, State),
#{user := User, jid := #jid{lserver = Server}, room_id := RoomID} = State,
mod_muc_light_commands:change_room_config(Server, RoomID, Name, User, Subject).
UserJID = jid:from_binary(User),
mod_muc_light_api:change_room_config(Server, RoomID, Name, UserJID, Subject).
4 changes: 2 additions & 2 deletions src/muc_light/mod_muc_light.erl
Original file line number Diff line number Diff line change
Expand Up @@ -142,12 +142,12 @@ try_to_create_room(CreatorJid, RoomJID, #create{raw_config = RawConfig} = Creati
Error
end.

-spec change_room_config(UserJid :: jid:jid(), RoomID :: jid:resource(),
-spec change_room_config(UserJid :: jid:jid(), RoomID :: jid:user(),
MUCLightDomain :: jid:server(),
ConfigReq :: config_req_props(),
Acc :: mongoose_acc:t()) ->
{ok, jid:jid(), config_req_props()}
| {error, validation_error() | bad_request | not_allowed}.
| {error, validation_error() | bad_request | not_allowed | item_not_found}.
change_room_config(UserJid, RoomID, MUCLightDomain, ConfigReq, Acc1) ->
RoomJID = jid:make(RoomID, MUCLightDomain, <<>>),
{Acc2, AffUsersRes} = get_room_affiliations_from_acc(Acc1, RoomJID),
Expand Down
Loading

0 comments on commit 6411541

Please sign in to comment.