Skip to content

Commit

Permalink
Merge pull request #3742 from esl/graphql/muclightopts
Browse files Browse the repository at this point in the history
Support setting and getting custom configuration options in MUC Light GraphQL API
  • Loading branch information
chrzaszcz committed Aug 31, 2022
2 parents 065c08e + f9d89a0 commit 6b39a79
Show file tree
Hide file tree
Showing 10 changed files with 229 additions and 69 deletions.
131 changes: 130 additions & 1 deletion big_tests/tests/graphql_muc_light_SUITE.erl
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ groups() ->

user_muc_light_tests() ->
[user_create_room,
user_create_room_with_custom_fields,
user_create_identified_room,
user_change_room_config,
user_change_room_config_errors,
Expand All @@ -65,8 +66,10 @@ user_muc_light_tests() ->

admin_muc_light_tests() ->
[admin_create_room,
admin_create_room_with_custom_fields,
admin_create_identified_room,
admin_change_room_config,
admin_change_room_config_with_custom_fields,
admin_change_room_config_errors,
admin_invite_user,
admin_invite_user_errors,
Expand Down Expand Up @@ -100,9 +103,18 @@ init_modules(Config) ->
Config2.

required_modules(_) ->
MucLightOpts = mod_config(mod_muc_light, #{rooms_in_rosters => true}),
MucLightOpts = mod_config(mod_muc_light, #{rooms_in_rosters => true,
config_schema => custom_schema()}),
[{mod_muc_light, MucLightOpts}].

custom_schema() ->
%% Should be sorted
[{<<"background">>, <<>>, background, binary},
{<<"music">>, <<>>, music, binary},
%% Default fields
{<<"roomname">>, <<>>, roomname, binary},
{<<"subject">>, <<>>, subject, binary}].

init_per_group(admin_muc_light_http, Config) ->
graphql_helper:init_admin_handler(Config);
init_per_group(admin_muc_light_cli, Config) ->
Expand Down Expand Up @@ -139,6 +151,24 @@ user_create_room_story(Config, Alice) ->
Res2 = user_create_room(Alice, ?UNKNOWN_DOMAIN, Name, Subject, null, Config),
?assertNotEqual(nomatch, binary:match(get_err_msg(Res2), <<"not found">>)).

user_create_room_with_custom_fields(Config) ->
escalus:fresh_story_with_config(Config, [{alice, 1}], fun user_create_room_with_custom_fields_story/2).

user_create_room_with_custom_fields_story(Config, Alice) ->
MucServer = ?config(muc_light_host, Config),
AliceBinLower = escalus_utils:jid_to_lower(escalus_client:short_jid(Alice)),
Name = <<"room with custom fields">>,
Subject = <<"testing_custom">>,
Opts = [#{<<"key">> => <<"background">>, <<"value">> => <<"red">>},
#{<<"key">> => <<"roomname">>, <<"value">> => Name},
#{<<"key">> => <<"subject">>, <<"value">> => Subject}],
Res = user_create_room_with_options(Alice, MucServer, Name, Subject, null, #{<<"background">> => <<"red">>}, Config),
#{<<"jid">> := JID, <<"name">> := Name, <<"subject">> := Subject,
<<"participants">> := Participants, <<"options">> := Opts}
= get_ok_value(?CREATE_ROOM_PATH, Res),
?assertMatch(#jid{lserver = MucServer}, jid:from_binary(JID)),
?assertEqual([#{<<"jid">> => AliceBinLower, <<"affiliation">> => <<"OWNER">>}], Participants).

user_create_identified_room(Config) ->
escalus:fresh_story_with_config(Config, [{alice, 1}], fun user_create_identified_room_story/2).

Expand Down Expand Up @@ -175,6 +205,24 @@ user_change_room_config_story(Config, Alice) ->
Res = user_change_room_configuration(Alice, jid:to_binary(RoomJID), Name2, Subject2, Config),
?assertMatch(#{<<"name">> := Name2, <<"subject">> := Subject2}, get_ok_value(?CHANGE_CONFIG_PATH, Res)).

user_change_room_config_with_custom_fields(Config) ->
escalus:fresh_story_with_config(Config, [{alice, 1}], fun user_change_room_config_with_custom_fields_story/2).

user_change_room_config_with_custom_fields_story(Config, Alice) ->
AliceBin = escalus_client:short_jid(Alice),
MUCServer = ?config(muc_light_host, Config),
% Create a new room
{ok, #{jid := RoomJID}} = create_room(MUCServer, <<"ornithology">>, <<"birds">>, AliceBin),
% Try to change the room configuration
Name2 = <<"changed room">>,
Subject2 = <<"not testing">>,
Opts2 = #{<<"music">> => <<"sad">>},
Res = user_change_room_configuration_with_custom_fields(Alice, jid:to_binary(RoomJID), Name2, Subject2, Config, Opts2),
Opts3 = [#{<<"key">> => <<"music">>, <<"value">> => <<"sad">>},
#{<<"key">> => <<"roomname">>, <<"value">> => Name2},
#{<<"key">> => <<"subject">>, <<"value">> => Subject2}],
?assertMatch(#{<<"name">> := Name2, <<"subject">> := Subject2, <<"options">> := Opts3}, get_ok_value(?CHANGE_CONFIG_PATH, Res)).

user_change_room_config_errors(Config) ->
escalus:fresh_story_with_config(Config, [{alice, 1}, {bob, 1}],
fun user_change_room_config_errors_story/3).
Expand Down Expand Up @@ -430,6 +478,10 @@ user_get_room_config_story(Config, Alice, Bob) ->
Res = user_get_room_config(Alice, jid:to_binary(RoomJID), Config),
% Owner can get a config
?assertEqual(#{<<"jid">> => RoomJIDBin, <<"subject">> => RoomSubject, <<"name">> => RoomName,
<<"options">> => [#{<<"key">> => <<"background">>, <<"value">> => <<>>},
#{<<"key">> => <<"music">>, <<"value">> => <<>>},
#{<<"key">> => <<"roomname">>, <<"value">> => RoomName},
#{<<"key">> => <<"subject">>, <<"value">> => RoomSubject}],
<<"participants">> => [#{<<"jid">> => AliceLower,
<<"affiliation">> => <<"OWNER">>}]},
get_ok_value(?GET_ROOM_CONFIG_PATH, Res)),
Expand All @@ -446,6 +498,10 @@ user_get_room_config_story(Config, Alice, Bob) ->
{ok, _} = invite_user(RoomJID, AliceBin, BobBin),
Res5 = user_get_room_config(Bob, jid:to_binary(RoomJID), Config),
?assertEqual(#{<<"jid">> => RoomJIDBin, <<"subject">> => RoomSubject, <<"name">> => RoomName,
<<"options">> => [#{<<"key">> => <<"background">>, <<"value">> => <<>>},
#{<<"key">> => <<"music">>, <<"value">> => <<>>},
#{<<"key">> => <<"roomname">>, <<"value">> => RoomName},
#{<<"key">> => <<"subject">>, <<"value">> => RoomSubject}],
<<"participants">> => [#{<<"jid">> => AliceLower,
<<"affiliation">> => <<"OWNER">>},
#{<<"jid">> => BobLower,
Expand Down Expand Up @@ -530,6 +586,25 @@ admin_create_room_story(Config, Alice) ->
Res2 = create_room(?UNKNOWN_DOMAIN, Name, AliceBin, Subject, null, Config),
?assertNotEqual(nomatch, binary:match(get_err_msg(Res2), <<"not found">>)).

admin_create_room_with_custom_fields(Config) ->
escalus:fresh_story_with_config(Config, [{alice, 1}], fun admin_create_room_with_custom_fields_story/2).

admin_create_room_with_custom_fields_story(Config, Alice) ->
AliceBin = escalus_client:short_jid(Alice),
AliceBinLower = escalus_utils:jid_to_lower(AliceBin),
MucServer = ?config(muc_light_host, Config),
Name = <<"first room">>,
Subject = <<"testing">>,
Options = #{<<"background">> => <<"red">>},
Opts = [#{<<"key">> => <<"background">>, <<"value">> => <<"red">>},
#{<<"key">> => <<"roomname">>, <<"value">> => Name},
#{<<"key">> => <<"subject">>, <<"value">> => Subject}],
Res = create_room_with_custom_fields(MucServer, Name, AliceBin, Subject, null, Config, Options),
#{<<"jid">> := JID, <<"name">> := Name, <<"subject">> := Subject,
<<"participants">> := Participants, <<"options">> := Opts} = get_ok_value(?CREATE_ROOM_PATH, Res),
?assertMatch(#jid{lserver = MucServer}, jid:from_binary(JID)),
?assertEqual([#{<<"jid">> => AliceBinLower, <<"affiliation">> => <<"OWNER">>}], Participants).

admin_create_identified_room(Config) ->
escalus:fresh_story_with_config(Config, [{alice, 1}], fun admin_create_identified_room_story/2).

Expand Down Expand Up @@ -570,6 +645,31 @@ admin_change_room_config_story(Config, Alice) ->
?assertMatch(#{<<"name">> := Name2, <<"subject">> := Subject2},
get_ok_value(?CHANGE_CONFIG_PATH, Res)).

admin_change_room_config_with_custom_fields(Config) ->
escalus:fresh_story_with_config(Config, [{alice, 1}], fun admin_change_room_config_with_custom_fields_story/2).

admin_change_room_config_with_custom_fields_story(Config, Alice) ->
AliceBin = escalus_client:short_jid(Alice),
MUCServer = ?config(muc_light_host, Config),
Name = <<"first room">>,
Subject = <<"testing">>,
Opts = #{<<"background">> => <<"red">>},
% Create a new room
Res = create_room_with_custom_fields(MUCServer, Name, AliceBin, Subject, null, Config, Opts),
#{<<"jid">> := RoomJID} = get_ok_value(?CREATE_ROOM_PATH, Res),
% Try to change the room configuration
Name2 = <<"changed room">>,
Subject2 = <<"not testing">>,
Opts2 = #{<<"music">> => <<"sad">>},
Res2 = change_room_configuration_with_custom_fields(jid:to_binary(RoomJID), AliceBin, Name2, Subject2, Config, Opts2),
%% It overwrites old config for all fields
Opts3 = [% #{<<"key">> => <<"background">>, <<"value">> => <<"red">>},
#{<<"key">> => <<"music">>, <<"value">> => <<"sad">>},
#{<<"key">> => <<"roomname">>, <<"value">> => Name2},
#{<<"key">> => <<"subject">>, <<"value">> => Subject2}],
?assertMatch(#{<<"name">> := Name2, <<"subject">> := Subject2, <<"options">> := Opts3},
get_ok_value(?CHANGE_CONFIG_PATH, Res2)).

admin_change_room_config_errors(Config) ->
escalus:fresh_story_with_config(Config, [{alice, 1}, {bob, 1}],
fun admin_change_room_config_errors_story/3).
Expand Down Expand Up @@ -803,6 +903,10 @@ admin_get_room_config_story(Config, Alice) ->
RoomJIDBin = jid:to_binary(RoomJID),
Res = get_room_config(jid:to_binary(RoomJID), Config),
?assertEqual(#{<<"jid">> => RoomJIDBin, <<"subject">> => RoomSubject, <<"name">> => RoomName,
<<"options">> => [#{<<"key">> => <<"background">>, <<"value">> => <<>>},
#{<<"key">> => <<"music">>, <<"value">> => <<>>},
#{<<"key">> => <<"roomname">>, <<"value">> => RoomName},
#{<<"key">> => <<"subject">>, <<"value">> => RoomSubject}],
<<"participants">> => [#{<<"jid">> => AliceLower,
<<"affiliation">> => <<"OWNER">>}]},
get_ok_value([data, muc_light, getRoomConfig], Res)),
Expand Down Expand Up @@ -855,11 +959,23 @@ create_room(MUCDomain, Name, Owner, Subject, Id, Config) ->
<<"subject">> => Subject, <<"id">> => Id},
execute_command(<<"muc_light">>, <<"createRoom">>, Vars, Config).

create_room_with_custom_fields(MUCDomain, Name, Owner, Subject,
Id, Config, CustomFields) ->
Vars = #{<<"mucDomain">> => MUCDomain, <<"name">> => Name, <<"owner">> => Owner,
<<"subject">> => Subject, <<"id">> => Id,
<<"options">> => format_options(CustomFields)},
execute_command(<<"muc_light">>, <<"createRoom">>, Vars, Config).

change_room_configuration(RoomJID, OwnerJID, Name, Subject, Config) ->
Vars = #{<<"room">> => RoomJID, <<"name">> => Name, <<"owner">> => OwnerJID,
<<"subject">> => Subject},
execute_command(<<"muc_light">>, <<"changeRoomConfiguration">>, Vars, Config).

change_room_configuration_with_custom_fields(RoomJID, OwnerJID, Name, Subject, Config, Opts) ->
Vars = #{<<"room">> => RoomJID, <<"name">> => Name, <<"owner">> => OwnerJID,
<<"subject">> => Subject, <<"options">> => format_options(Opts)},
execute_command(<<"muc_light">>, <<"changeRoomConfiguration">>, Vars, Config).

invite_user(RoomJID, Sender, Recipient, Config) ->
Vars = #{<<"room">> => RoomJID, <<"sender">> => Sender, <<"recipient">> => Recipient},
execute_command(<<"muc_light">>, <<"inviteUser">>, Vars, Config).
Expand Down Expand Up @@ -905,10 +1021,23 @@ user_create_room(User, MUCDomain, Name, Subject, Id, Config) ->
<<"id">> => Id},
execute_user_command(<<"muc_light">>, <<"createRoom">>, User, Vars, Config).

user_create_room_with_options(User, MUCDomain, Name, Subject, Id, CustomFields, Config) ->
Vars = #{<<"mucDomain">> => MUCDomain, <<"name">> => Name, <<"subject">> => Subject,
<<"id">> => Id, <<"options">> => format_options(CustomFields)},
execute_user_command(<<"muc_light">>, <<"createRoom">>, User, Vars, Config).

user_change_room_configuration(User, RoomJID, Name, Subject, Config) ->
Vars = #{<<"room">> => RoomJID, <<"name">> => Name, <<"subject">> => Subject},
execute_user_command(<<"muc_light">>, <<"changeRoomConfiguration">>, User, Vars, Config).

user_change_room_configuration_with_custom_fields(User, RoomJID, Name, Subject, Config, Options) ->
Vars = #{<<"room">> => RoomJID, <<"name">> => Name, <<"subject">> => Subject,
<<"options">> => format_options(Options)},
execute_user_command(<<"muc_light">>, <<"changeRoomConfiguration">>, User, Vars, Config).

format_options(Map) ->
[#{<<"key">> => K, <<"value">> => V} || {K, V} <- maps:to_list(Map)].

user_invite_user(User, RoomJID, Recipient, Config) ->
Vars = #{<<"room">> => RoomJID, <<"recipient">> => Recipient},
execute_user_command(<<"muc_light">>, <<"inviteUser">>, User, Vars, Config).
Expand Down
4 changes: 2 additions & 2 deletions priv/graphql/schemas/admin/muc_light.gql
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@ Allow admin to manage Multi-User Chat Light rooms.
"""
type MUCLightAdminMutation @protected{
"Create a MUC light room under the given XMPP hostname"
createRoom(mucDomain: String!, name: String!, owner: JID!, subject: String!, id: NonEmptyString): Room
createRoom(mucDomain: String!, name: String!, owner: JID!, subject: String!, id: NonEmptyString, options: [RoomConfigDictEntryInput!]): Room
@protected(type: DOMAIN, args: ["owner"])
"Change configuration of a MUC Light room"
changeRoomConfiguration(room: JID!, owner: JID!, name: String!, subject: String!): Room
changeRoomConfiguration(room: JID!, owner: JID!, name: String!, subject: String!, options: [RoomConfigDictEntryInput!]): Room
@protected(type: DOMAIN, args: ["room"])
"Invite a user to a MUC Light room"
inviteUser(room: JID!, sender: JID!, recipient: JID!): String
Expand Down
15 changes: 15 additions & 0 deletions priv/graphql/schemas/global/muc_light.gql
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,26 @@ enum BlockedEntityType{
ROOM
}

input RoomConfigDictEntryInput{
"The name of the config option"
key: String!
"Config option value"
value: String!
}

type RoomConfigDictEntry{
"The name of the config option"
key: String!
"Config option value"
value: String!
}

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

type RoomUser{
Expand Down
4 changes: 2 additions & 2 deletions priv/graphql/schemas/user/muc_light.gql
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ 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: NonEmptyString): Room
createRoom(mucDomain: String!, name: String!, subject: String!, id: NonEmptyString, options: [RoomConfigDictEntryInput!]): Room
"Change configuration of a MUC Light room"
changeRoomConfiguration(room: JID!, name: String!, subject: String!): Room
changeRoomConfiguration(room: JID!, name: String!, subject: String!, options: [RoomConfigDictEntryInput!]): Room
"Invite a user to a MUC Light room"
inviteUser(room: JID!, recipient: JID!): String
"Remove a MUC Light room"
Expand Down
23 changes: 10 additions & 13 deletions src/graphql/admin/mongoose_graphql_muc_light_admin_mutation.erl
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@

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

execute(_Ctx, _Obj, <<"createRoom">>, Args) ->
create_room(Args);
Expand All @@ -27,17 +28,10 @@ execute(_Ctx, _Obj, <<"setBlockingList">>, Args) ->
set_blocking_list(Args).

-spec create_room(map()) -> {ok, map()} | {error, resolver_error()}.
create_room(#{<<"id">> := null, <<"mucDomain">> := MUCDomain, <<"name">> := RoomName,
<<"owner">> := CreatorJID, <<"subject">> := Subject}) ->
case mod_muc_light_api:create_room(MUCDomain, CreatorJID, RoomName, Subject) of
{ok, Room} ->
{ok, make_room(Room)};
Err ->
make_error(Err, #{mucDomain => MUCDomain, creator => CreatorJID})
end;
create_room(#{<<"id">> := RoomID, <<"mucDomain">> := MUCDomain, <<"name">> := RoomName,
<<"owner">> := CreatorJID, <<"subject">> := Subject}) ->
case mod_muc_light_api:create_room(MUCDomain, RoomID, CreatorJID, RoomName, Subject) of
<<"owner">> := CreatorJID, <<"subject">> := Subject, <<"options">> := Options}) ->
case mod_muc_light_api:create_room(MUCDomain, null_to_default(RoomID, <<>>), CreatorJID,
RoomName, Subject, options_to_map(Options)) of
{ok, Room} ->
{ok, make_room(Room)};
Err ->
Expand All @@ -46,8 +40,11 @@ create_room(#{<<"id">> := RoomID, <<"mucDomain">> := MUCDomain, <<"name">> := Ro

-spec change_room_config(map()) -> {ok, map()} | {error, resolver_error()}.
change_room_config(#{<<"room">> := RoomJID, <<"name">> := RoomName,
<<"owner">> := OwnerJID, <<"subject">> := Subject}) ->
case mod_muc_light_api:change_room_config(RoomJID, OwnerJID, RoomName, Subject) of
<<"owner">> := OwnerJID, <<"subject">> := Subject,
<<"options">> := Options}) ->
OptMap = options_to_map(Options),
Config = OptMap#{<<"roomname">> => RoomName, <<"subject">> => Subject},
case mod_muc_light_api:change_room_config(RoomJID, OwnerJID, Config) of
{ok, Room} ->
{ok, make_room(Room)};
Err ->
Expand Down
20 changes: 17 additions & 3 deletions src/graphql/mongoose_graphql_muc_light_helper.erl
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
-module(mongoose_graphql_muc_light_helper).

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

-spec page_size_or_max_limit(null | integer(), integer()) -> integer().
page_size_or_max_limit(null, MaxLimit) ->
Expand All @@ -12,10 +13,10 @@ 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}) ->
make_room(#{jid := JID, name := Name, subject := Subject, aff_users := Users, options := Options}) ->
Participants = lists:map(fun make_ok_user/1, Users),
#{<<"jid">> => JID, <<"name">> => Name, <<"subject">> => Subject,
<<"participants">> => Participants}.
<<"participants">> => Participants, <<"options">> => make_options(Options)}.

make_ok_user({JID, Aff}) ->
{ok, #{<<"jid">> => JID, <<"affiliation">> => Aff}}.
Expand All @@ -26,3 +27,16 @@ prepare_blocking_items(Items) ->

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

make_options(Options) ->
[{ok, #{<<"key">> => K, <<"value">> => V}} || {K, V} <- lists:sort(maps:to_list(Options))].

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

options_to_map(null) ->
#{};
options_to_map(Options) ->
maps:from_list([{K, V} || #{<<"key">> := K, <<"value">> := V} <- Options]).
Loading

0 comments on commit 6b39a79

Please sign in to comment.