Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement GraphQL MUC Light user queries #3563

Merged
merged 14 commits into from
Mar 3, 2022
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion big_tests/tests/graphql_helper.erl
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@

-export([execute/3, execute_auth/2, get_listener_port/1, get_listener_config/1]).
-export([init_admin_handler/1]).
-export([get_ok_value/2, get_err_msg/1]).
-export([get_ok_value/2, get_err_msg/1, make_creds/1]).

-include_lib("common_test/include/ct.hrl").
-include_lib("escalus/include/escalus.hrl").

-spec execute(atom(), binary(), {binary(), binary()} | undefined) ->
{Status :: tuple(), Data :: map()}.
Expand Down Expand Up @@ -71,6 +72,11 @@ get_ok_value([errors | Path], {{<<"200">>, <<"OK">>}, #{<<"errors">> := [Error]}
get_ok_value(Path, {{<<"200">>, <<"OK">>}, Data}) ->
get_value(Path, Data).

make_creds(#client{props = Props} = Client) ->
JID = escalus_utils:jid_to_lower(escalus_client:short_jid(Client)),
Password = proplists:get_value(password, Props),
{JID, Password}.

%% Internal

% Gets a nested value given a path
Expand Down
666 changes: 603 additions & 63 deletions big_tests/tests/graphql_muc_light_SUITE.erl

Large diffs are not rendered by default.

18 changes: 5 additions & 13 deletions priv/graphql/schemas/admin/muc_light.gql
Original file line number Diff line number Diff line change
Expand Up @@ -14,30 +14,22 @@ type MUCLightAdminMutation @protected{
kickUser(room: JID!, user: JID!): String
"Send a message to a MUC Light room"
sendMessageToRoom(room: JID!, from: JID!, body: String!): String
"Set the user blocking list"
setBlockingList(user: JID!, items: [BlockingInput!]!): String
}

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

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

type RoomUser{
jid: JID!
affiliation: Affiliation!
"Get the user blocking list"
getBlockingList(user: JID!): [BlockingItem!]
}
34 changes: 34 additions & 0 deletions priv/graphql/schemas/global/muc.gql
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,37 @@ enum Affiliation{
MEMBER
NONE
}

input BlockingInput{
what: BlockingWhat!
action: BlockingAction!
who: JID!
}

type BlockingItem{
what: BlockingWhat!
action: BlockingAction!
who: JID!
chrzaszcz marked this conversation as resolved.
Show resolved Hide resolved
}

enum BlockingAction{
ALLOW,
DENY
}

enum BlockingWhat{
USER,
ROOM
}

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

type RoomUser{
jid: JID!
affiliation: Affiliation!
}
35 changes: 35 additions & 0 deletions priv/graphql/schemas/user/muc_light.gql
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
"""
Allow user to manage Multi-User Chat Light rooms.
"""
type MUCLightUserMutation @protected{
"Create a MUC light room under the given XMPP hostname"
createRoom(mucDomain: String!, name: String!, subject: String!, id: String): Room
"Change configuration of a MUC Light room"
changeRoomConfiguration(room: JID!, name: String!, subject: String!): Room
"Invite a user to a MUC Light room"
inviteUser(room: JID!, recipient: JID!): String
"Remove a MUC Light room"
deleteRoom(room: JID!): String
"Kick a user from a MUC Light room"
kickUser(room: JID!, user: JID): String
"Send a message to a MUC Light room"
sendMessageToRoom(room: JID!, body: String!): String
"Set the user blocking list"
setBlockingList(items: [BlockingInput!]!): String
}

"""
Allow user to get information about Multi-User Chat Light rooms.
"""
type MUCLightUserQuery @protected{
"Get the MUC Light room archived messages"
getRoomMessages(room: JID!, pageSize: Int, before: DateTime): StanzasPayload
"Get configuration of the MUC Light room"
getRoomConfig(room: JID!): Room
"Get users list of given MUC Light room"
listRoomUsers(room: JID!): [RoomUser!]
"Get the list of MUC Light rooms that the user participates in"
listRooms: [JID!]
"Get the user blocking list"
getBlockingList: [BlockingItem!]
}
4 changes: 4 additions & 0 deletions priv/graphql/schemas/user/user_schema.gql
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ type UserQuery{
checkAuth: UserAuthInfo
"Account management"
account: AccountUserQuery
"MUC Light room management"
muc_light: MUCLightUserQuery
"Session management"
session: SessionUserQuery
}
Expand All @@ -23,4 +25,6 @@ Only an authenticated user can execute these mutations.
type UserMutation @protected{
"Account management"
account: AccountUserMutation
"MUC Light room management"
muc_light: MUCLightUserMutation
}
13 changes: 11 additions & 2 deletions src/graphql/admin/mongoose_graphql_muc_light_admin_mutation.erl
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@
-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]).
-import(mongoose_graphql_muc_light_helper, [make_room/1, make_ok_user/1,
prepare_blocking_items/1]).

execute(_Ctx, _Obj, <<"createRoom">>, Args) ->
create_room(Args);
Expand All @@ -20,7 +21,9 @@ execute(_Ctx, _Obj, <<"deleteRoom">>, Args) ->
execute(_Ctx, _Obj, <<"kickUser">>, Args) ->
kick_user(Args);
execute(_Ctx, _Obj, <<"sendMessageToRoom">>, Args) ->
send_msg_to_room(Args).
send_msg_to_room(Args);
execute(_Ctx, _Obj, <<"setBlockingList">>, Args) ->
set_blocking_list(Args).

-spec create_room(map()) -> {ok, map()} | {error, resolver_error()}.
create_room(#{<<"id">> := null} = Args) ->
Expand Down Expand Up @@ -65,3 +68,9 @@ kick_user(#{<<"room">> := RoomJID, <<"user">> := UserJID}) ->
send_msg_to_room(#{<<"room">> := RoomJID, <<"from">> := FromJID, <<"body">> := Message}) ->
Result = mod_muc_light_api:send_message(RoomJID, FromJID, Message),
format_result(Result, #{room => jid:to_binary(RoomJID), from => jid:to_binary(FromJID)}).

-spec set_blocking_list(map()) -> {ok, binary()} | {error, resolver_error()}.
set_blocking_list(#{<<"user">> := UserJID, <<"items">> := Items}) ->
Items2 = prepare_blocking_items(Items),
Result = mod_muc_light_api:set_blocking(UserJID, Items2),
format_result(Result, #{user => jid:to_binary(UserJID)}).
31 changes: 21 additions & 10 deletions src/graphql/admin/mongoose_graphql_muc_light_admin_query.erl
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@
-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]).
-import(mongoose_graphql_muc_light_helper, [make_room/1,
make_ok_user/1,
null_to_undefined/1,
page_size_or_max_limit/2]).

execute(_Ctx, _Obj, <<"listUserRooms">>, Args) ->
list_user_rooms(Args);
Expand All @@ -16,9 +19,11 @@ execute(_Ctx, _Obj, <<"listRoomUsers">>, Args) ->
execute(_Ctx, _Obj, <<"getRoomConfig">>, Args) ->
get_room_config(Args);
execute(_Ctx, _Obj, <<"getRoomMessages">>, Args) ->
get_room_messages(Args).
get_room_messages(Args);
execute(_Ctx, _Obj, <<"getBlockingList">>, Args) ->
get_blocking_list(Args).

-spec list_user_rooms(map()) -> {ok, [binary()]} | {error, resolver_error()}.
-spec list_user_rooms(map()) -> {ok, [{ok, binary()}]} | {error, resolver_error()}.
list_user_rooms(#{<<"user">> := UserJID}) ->
case mod_muc_light_api:get_user_rooms(UserJID) of
{ok, Rooms} ->
Expand All @@ -27,7 +32,7 @@ list_user_rooms(#{<<"user">> := UserJID}) ->
make_error(Err, #{user => UserJID})
end.

-spec list_room_users(map()) -> {ok, [map()]} | {error, resolver_error()}.
-spec list_room_users(map()) -> {ok, [{ok, map()}]} | {error, resolver_error()}.
list_room_users(#{<<"room">> := RoomJID}) ->
case mod_muc_light_api:get_room_aff(RoomJID) of
{ok, Affs} ->
Expand All @@ -49,15 +54,21 @@ get_room_config(#{<<"room">> := RoomJID}) ->
get_room_messages(#{<<"room">> := RoomJID, <<"pageSize">> := PageSize,
<<"before">> := Before}) ->
Before2 = null_to_undefined(Before),
case mod_muc_light_api:get_room_messages(RoomJID, PageSize, Before2) of
PageSize2 = page_size_or_max_limit(PageSize, 50),
case mod_muc_light_api:get_room_messages(RoomJID, PageSize2, Before2) of
{ok, Rows} ->
Maps = lists:map(fun mongoose_graphql_stanza_helper:row_to_map/1, Rows),
{ok, #{<<"stanzas">> => Maps, <<"limit">> => null}};
{ok, #{<<"stanzas">> => Maps, <<"limit">> => PageSize2}};
Err ->
make_error(Err, #{room => RoomJID})
end.

%% Helpers

null_to_undefined(null) -> undefined;
null_to_undefined(V) -> V.
-spec get_blocking_list(map()) -> {ok, [{ok, map()}]} | {error, resolver_error()}.
get_blocking_list(#{<<"user">> := UserJID}) ->
case mod_muc_light_api:get_blocking_list(UserJID) of
{ok, Items} ->
Items2 = lists:map(fun mongoose_graphql_muc_light_helper:blocking_item_to_map/1, Items),
{ok, Items2};
Err ->
make_error(Err, #{user => UserJID})
end.
2 changes: 2 additions & 0 deletions src/graphql/mongoose_graphql.erl
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,8 @@ user_mapping_rules() ->
'UserMutation' => mongoose_graphql_user_mutation,
'AccountUserQuery' => mongoose_graphql_account_user_query,
'AccountUserMutation' => mongoose_graphql_account_user_mutation,
'MUCLightUserMutation' => mongoose_graphql_muc_light_user_mutation,
'MUCLightUserQuery' => mongoose_graphql_muc_light_user_query,
'SessionUserQuery' => mongoose_graphql_session_user_query,
'UserAuthInfo' => mongoose_graphql_user_auth_info,
default => mongoose_graphql_default},
Expand Down
12 changes: 10 additions & 2 deletions src/graphql/mongoose_graphql_enum.erl
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,11 @@ input(<<"AuthStatus">>, <<"AUTHORIZED">>) -> {ok, 'AUTHORIZED'};
input(<<"AuthStatus">>, <<"UNAUTHORIZED">>) -> {ok, 'UNAUTHORIZED'};
input(<<"Affiliation">>, <<"OWNER">>) -> {ok, owner};
input(<<"Affiliation">>, <<"MEMBER">>) -> {ok, member};
input(<<"Affiliation">>, <<"NONE">>) -> {ok, none}.
input(<<"Affiliation">>, <<"NONE">>) -> {ok, none};
input(<<"BlockingAction">>, <<"ALLOW">>) -> {ok, allow};
input(<<"BlockingAction">>, <<"DENY">>) -> {ok, deny};
input(<<"BlockingWhat">>, <<"USER">>) -> {ok, user};
input(<<"BlockingWhat">>, <<"ROOM">>) -> {ok, room}.

output(<<"PresenceShow">>, Show) ->
{ok, list_to_binary(string:to_upper(binary_to_list(Show)))};
Expand All @@ -21,4 +25,8 @@ output(<<"PresenceType">>, Type) ->
output(<<"AuthStatus">>, Status) ->
{ok, atom_to_binary(Status, utf8)};
output(<<"Affiliation">>, Aff) ->
{ok, list_to_binary(string:to_upper(atom_to_list(Aff)))}.
{ok, list_to_binary(string:to_upper(atom_to_list(Aff)))};
output(<<"BlockingAction">>, Action) ->
{ok, list_to_binary(string:to_upper(atom_to_list(Action)))};
output(<<"BlockingWhat">>, What) ->
{ok, list_to_binary(string:to_upper(atom_to_list(What)))}.
22 changes: 21 additions & 1 deletion src/graphql/mongoose_graphql_muc_light_helper.erl
Original file line number Diff line number Diff line change
@@ -1,6 +1,16 @@
-module(mongoose_graphql_muc_light_helper).

-export([make_room/1, make_ok_user/1]).
-export([make_room/1, make_ok_user/1, blocking_item_to_map/1,
null_to_undefined/1, prepare_blocking_items/1,
page_size_or_max_limit/2]).

-spec page_size_or_max_limit(null | integer(), integer()) -> integer().
page_size_or_max_limit(null, MaxLimit) ->
MaxLimit;
page_size_or_max_limit(PageSize, MaxLimit) when PageSize > MaxLimit ->
MaxLimit;
page_size_or_max_limit(PageSize, _MaxLimit) ->
PageSize.

-spec make_room(mod_muc_light_api:room()) -> map().
make_room(#{jid := JID, name := Name, subject := Subject, aff_users := Users}) ->
Expand All @@ -10,3 +20,13 @@ make_room(#{jid := JID, name := Name, subject := Subject, aff_users := Users}) -

make_ok_user({JID, Aff}) ->
{ok, #{<<"jid">> => JID, <<"affiliation">> => Aff}}.

prepare_blocking_items(Items) ->
[{What, Action, jid:to_lus(Who)} || #{<<"who">> := Who, <<"what">> := What,
<<"action">> := Action} <- Items].

blocking_item_to_map({What, Action, Who}) ->
{ok, #{<<"what">> => What, <<"action">> => Action, <<"who">> => Who}}.

null_to_undefined(null) -> undefined;
null_to_undefined(V) -> V.
82 changes: 82 additions & 0 deletions src/graphql/user/mongoose_graphql_muc_light_user_mutation.erl
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
-module(mongoose_graphql_muc_light_user_mutation).

-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, prepare_blocking_items/1]).

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

-spec create_room(map(), map()) -> {ok, map()} | {error, resolver_error()}.
create_room(Ctx, #{<<"id">> := null} = Args) ->
create_room(Ctx, Args#{<<"id">> => <<>>});
chrzaszcz marked this conversation as resolved.
Show resolved Hide resolved
create_room(#{user := UserJID}, #{<<"id">> := RoomID, <<"mucDomain">> := MUCDomain,
<<"name">> := RoomName, <<"subject">> := Subject}) ->
case mod_muc_light_api:create_room(MUCDomain, RoomID, RoomName, UserJID, Subject) of
{ok, Room} ->
{ok, make_room(Room)};
Err ->
make_error(Err, #{mucDomain => MUCDomain, id => RoomID})
end.

-spec change_room_config(map(), map()) -> {ok, map()} | {error, resolver_error()}.
change_room_config(#{user := UserJID}, #{<<"room">> := RoomJID, <<"name">> := RoomName,
<<"subject">> := Subject}) ->
case mod_muc_light_api:change_room_config(RoomJID, UserJID, RoomName, Subject) of
{ok, Room} ->
{ok, make_room(Room)};
Err ->
make_error(Err, #{room => jid:to_binary(RoomJID)})
end.

-spec delete_room(map(), map()) -> {ok, binary()} | {error, resolver_error()}.
delete_room(#{user := UserJID}, #{<<"room">> := RoomJID}) ->
Result = mod_muc_light_api:delete_room(RoomJID, UserJID),
format_result(Result, #{room => jid:to_binary(RoomJID)}).

-spec invite_user(map(), map()) -> {ok, binary()} | {error, resolver_error()}.
invite_user(#{user := UserJID}, #{<<"room">> := RoomJID, <<"recipient">> := RecipientJID}) ->
Result = mod_muc_light_api:invite_to_room(RoomJID, UserJID, RecipientJID),
format_result(Result, #{room => jid:to_binary(RoomJID),
recipient => jid:to_binary(RecipientJID)}).

-spec kick_user(map(), map()) -> {ok, binary()} | {error, resolver_error()}.
kick_user(#{user := UserJID}, #{<<"room">> := RoomJID, <<"user">> := UserToKickJID}) ->
Result = mod_muc_light_api:remove_user_from_room(RoomJID, UserJID,
null_to_default(UserToKickJID, UserJID)),
format_result(Result, #{user => UserToKickJID}).

-spec send_msg_to_room(map(), map()) -> {ok, binary()} | {error, resolver_error()}.
send_msg_to_room(#{user := UserJID}, #{<<"room">> := RoomJID, <<"body">> := Message}) ->
Result = mod_muc_light_api:send_message(RoomJID, UserJID, Message),
format_result(Result, #{room => jid:to_binary(RoomJID)}).

-spec set_blocking_list(map(), map()) -> {ok, binary()} | {error, resolver_error()}.
set_blocking_list(#{user := UserJID}, #{<<"items">> := Items}) ->
Items2 = prepare_blocking_items(Items),
Result = mod_muc_light_api:set_blocking(UserJID, Items2),
format_result(Result, #{user => jid:to_binary(UserJID)}).

%% Helpers

null_to_default(null, Default) ->
Default;
null_to_default(Value, _Default) ->
Value.
Loading