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

Support setting and getting custom configuration options in MUC Light GraphQL API #3742

Merged
merged 8 commits into from
Aug 31, 2022
Merged
Show file tree
Hide file tree
Changes from all 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
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