-
Notifications
You must be signed in to change notification settings - Fork 439
MIM-2749 muc_light: add listRooms admin GraphQL query
#4744
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
base: master
Are you sure you want to change the base?
Changes from 4 commits
bbcfd18
ffb93ff
c0bdd00
6d801d6
18e2409
16e2f7d
414f0ac
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -79,3 +79,27 @@ type RoomUser{ | |
| "User's affiliation" | ||
| affiliation: Affiliation! | ||
| } | ||
|
|
||
| "MUC Light rooms payload" | ||
| type MUCLightRoomsPayload{ | ||
| "List of room descriptions, ordered by room JID" | ||
| rooms: [MUCLightRoomDesc!] | ||
| "Total number of rooms in the domain matching the filter" | ||
| count: NonNegInt | ||
| "JID of the last room in this page; pass it as 'after' to get the next page. Null when there are no more rooms" | ||
| nextCursor: BareJID | ||
|
kamilwaz marked this conversation as resolved.
Outdated
|
||
| } | ||
|
|
||
| "MUC Light room description" | ||
| type MUCLightRoomDesc{ | ||
| "Room's JID" | ||
| jid: BareJID! | ||
| "Room's display name (may be empty)" | ||
| name: String | ||
| "Room's subject (may be empty)" | ||
| subject: String | ||
| "Number of room members" | ||
| usersNumber: NonNegInt | ||
| "JID of the room's owner; null if no owner can be resolved" | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just note that there can be multiple owners if we enable that - so this would just return one of them.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good point, doc updated to say "one of the room's owners".
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm wondering whether we should return all owners here instead. Alternatively, we could remove this field and be consistent with I assume that when we list owners, they will likely be displayed alongside all users, so we would need to call another API anyway. |
||
| ownerJid: JID | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -14,6 +14,7 @@ | |
| get_room_messages/4, | ||
| get_room_messages/5, | ||
| get_user_rooms/1, | ||
| get_rooms/4, | ||
| get_room_info/1, | ||
| get_room_info/2, | ||
| get_room_aff/1, | ||
|
|
@@ -33,7 +34,17 @@ | |
| aff_users := aff_users(), | ||
| options := map()}. | ||
|
|
||
| -export_type([room/0]). | ||
| -type room_desc() :: #{jid := jid:jid(), | ||
| name := binary() | undefined, | ||
| subject := binary() | undefined, | ||
| users_number := non_neg_integer(), | ||
| owner := jid:simple_bare_jid() | undefined}. | ||
|
|
||
| -type list_rooms_result() :: {Rooms :: [room_desc()], | ||
| Count :: non_neg_integer(), | ||
| NextCursor :: jid:jid() | undefined}. | ||
|
|
||
| -export_type([room/0, room_desc/0, list_rooms_result/0]). | ||
|
|
||
| -define(ROOM_DELETED_SUCC_RESULT, {ok, "Room deleted successfully"}). | ||
| -define(USER_NOT_ROOM_MEMBER_RESULT, {not_room_member, "Given user does not occupy this room"}). | ||
|
|
@@ -151,6 +162,17 @@ get_room_aff(RoomJID) -> | |
| get_user_rooms(UserJID) -> | ||
| fold(#{user => UserJID}, [fun check_user/1, fun do_get_user_rooms/1]). | ||
|
|
||
| %% Filter is matched case-insensitively as a substring of the room localpart | ||
| %% or the room name; After is the localpart of the last room of the previous | ||
| %% page (keyset cursor). | ||
| -spec get_rooms(jid:lserver(), binary() | undefined, jid:luser() | undefined, | ||
| pos_integer()) -> | ||
| {ok, list_rooms_result()} | {muc_server_not_found, iolist()}. | ||
| get_rooms(MUCServer, Filter, After, Limit) -> | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not sure about changing the name from |
||
| M = #{muc_server => MUCServer, filter => normalize_filter(Filter), | ||
| after_room => After, limit => Limit}, | ||
| fold(M, [fun check_muc_domain/1, fun do_get_rooms/1]). | ||
|
|
||
| -spec get_blocking_list(jid:jid()) -> {ok, [blocking_item()]} | {user_not_found, iolist()}. | ||
| get_blocking_list(UserJID) -> | ||
| fold(#{user => UserJID}, [fun check_user/1, fun do_get_blocking_list/1]). | ||
|
|
@@ -173,6 +195,17 @@ check_user(M = #{user := UserJID = #jid{lserver = LServer}}) -> | |
| end. | ||
|
|
||
| check_muc_domain(M = #{room := #jid{lserver = LServer}}) -> | ||
| check_muc_lserver(LServer, M); | ||
| check_muc_domain(M = #{muc_server := MUCServer}) -> | ||
|
kamilwaz marked this conversation as resolved.
Outdated
|
||
| case jid:nameprep(MUCServer) of | ||
| error -> | ||
| ?MUC_SERVER_NOT_FOUND_RESULT; | ||
| LServer -> | ||
| %% Store the nameprepped value, as the backends match on it verbatim | ||
|
kamilwaz marked this conversation as resolved.
Outdated
|
||
| check_muc_lserver(LServer, M#{muc_server := LServer}) | ||
| end. | ||
|
|
||
| check_muc_lserver(LServer, M) -> | ||
| case mongoose_domain_api:get_subdomain_host_type(LServer) of | ||
| {ok, HostType} -> | ||
| M#{muc_host_type => HostType}; | ||
|
|
@@ -345,6 +378,23 @@ do_get_user_rooms(#{user := UserJID, user_host_type := HostType}) -> | |
| MUCServer = mod_muc_light_utils:server_host_to_muc_host(HostType, UserJID#jid.lserver), | ||
| {ok, mod_muc_light_db_backend:get_user_rooms(HostType, jid:to_lus(UserJID), MUCServer)}. | ||
|
|
||
| do_get_rooms(#{muc_server := MUCServer, muc_host_type := HostType, | ||
| filter := Filter, after_room := After, limit := Limit}) -> | ||
| %% Fetch one extra room to learn whether a next page exists | ||
| {Raw, Count} = | ||
| mod_muc_light_db_backend:get_room_descs(HostType, MUCServer, Filter, After, Limit + 1), | ||
| Schema = mod_muc_light:config_schema(MUCServer), | ||
| Page = [raw_to_room_desc(R, Schema) || R <- lists:sublist(Raw, Limit)], | ||
| NextCursor = case length(Raw) > Limit of | ||
| true -> maps:get(jid, lists:last(Page)); | ||
| false -> undefined | ||
| end, | ||
| {ok, {Page, Count, NextCursor}}. | ||
|
|
||
| normalize_filter(undefined) -> undefined; | ||
| normalize_filter(<<>>) -> undefined; | ||
| normalize_filter(Filter) -> string:lowercase(Filter). | ||
|
|
||
| do_get_blocking_list(#{user := UserJID, user_host_type := HostType}) -> | ||
| MUCServer = mod_muc_light_utils:server_host_to_muc_host(HostType, UserJID#jid.lserver), | ||
| {ok, mod_muc_light_db_backend:get_blocking(HostType, jid:to_lus(UserJID), MUCServer)}. | ||
|
|
@@ -387,6 +437,32 @@ get_aff(UserUS, Affs) -> | |
| false -> none | ||
| end. | ||
|
|
||
| -spec raw_to_room_desc(mod_muc_light_db_backend:room_desc(), | ||
| mod_muc_light_room_config:schema()) -> room_desc(). | ||
| raw_to_room_desc(#{room := {RoomU, RoomS}, config := Config, aff_users := AffUsers}, Schema) -> | ||
| %% DB rows are already prepped; make_noprep cannot fail on a malformed row | ||
|
kamilwaz marked this conversation as resolved.
Outdated
|
||
| #{jid => jid:make_noprep(RoomU, RoomS, <<>>), | ||
| name => config_field(<<"roomname">>, Config, Schema), | ||
| subject => config_field(<<"subject">>, Config, Schema), | ||
| users_number => length(AffUsers), | ||
| owner => find_owner(AffUsers)}. | ||
|
|
||
| %% Config kv() is keyed by the internal schema key, which may differ from the field name | ||
|
kamilwaz marked this conversation as resolved.
Outdated
|
||
| -spec config_field(binary(), mod_muc_light_room_config:kv(), | ||
| mod_muc_light_room_config:schema()) -> binary() | undefined. | ||
| config_field(FieldName, Config, Schema) -> | ||
| case lists:keyfind(FieldName, 1, Schema) of | ||
| {FieldName, _Default, Key, _Type} -> proplists:get_value(Key, Config, undefined); | ||
| false -> undefined | ||
| end. | ||
|
|
||
| -spec find_owner(aff_users()) -> jid:simple_bare_jid() | undefined. | ||
| find_owner(AffUsers) -> | ||
| case lists:keyfind(owner, 2, AffUsers) of | ||
| {OwnerUS, owner} -> OwnerUS; | ||
| false -> undefined | ||
| end. | ||
|
|
||
| make_room(JID, #config{ raw_config = Options}, AffUsers) -> | ||
| make_room(JID, Options, AffUsers); | ||
| make_room(JID, Options, AffUsers) when is_list(Options) -> | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -43,14 +43,16 @@ | |
| set_blocking/4, | ||
| get_aff_users/2, | ||
| modify_aff_users/5, | ||
| get_info/2 | ||
| get_info/2, | ||
| get_room_descs/5 | ||
| ]). | ||
|
|
||
| %% Extra API for testing | ||
| -export([force_clear/0]). | ||
| -ignore_xref([force_clear/0]). | ||
|
|
||
| -include("mod_muc_light.hrl"). | ||
| -include_lib("stdlib/include/ms_transform.hrl"). | ||
|
|
||
| -record(muc_light_room, { | ||
| room :: jid:simple_bare_jid(), | ||
|
|
@@ -232,6 +234,46 @@ get_info(_HostType, RoomUS) -> | |
| {ok, Config, AffUsers, Version} | ||
| end. | ||
|
|
||
| -spec get_room_descs(mongooseim:host_type(), jid:lserver(), binary() | undefined, | ||
| jid:luser() | undefined, pos_integer()) -> | ||
| {[mod_muc_light_db_backend:room_desc()], non_neg_integer()}. | ||
| get_room_descs(_HostType, MUCServer, Filter, After, Limit) -> | ||
| MS = ets:fun2ms(fun(#muc_light_room{room = {_, RoomS}} = Room) | ||
| when RoomS =:= MUCServer -> Room end), | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think you should be able to encode the
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Did the change, but we still need to do the second dirty_select for the count of all rooms, not sure if this version is better.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let's keep it as it is, especially since RDBMS is the recommended backen. As a note, |
||
| Rooms = mnesia:dirty_select(muc_light_room, MS), | ||
| Schema = mod_muc_light:config_schema(MUCServer), | ||
| Matching = lists:keysort(#muc_light_room.room, | ||
| [R || R <- Rooms, room_matches_filter(R, Filter, Schema)]), | ||
| Page = lists:sublist(drop_up_to_cursor(Matching, After), Limit), | ||
| Descs = [#{room => RoomUS, config => Config, aff_users => AffUsers} | ||
| || #muc_light_room{room = RoomUS, config = Config, aff_users = AffUsers} <- Page], | ||
| {Descs, length(Matching)}. | ||
|
|
||
| drop_up_to_cursor(Rooms, undefined) -> | ||
| Rooms; | ||
| drop_up_to_cursor(Rooms, After) -> | ||
| lists:dropwhile(fun(#muc_light_room{room = {RoomU, _}}) -> RoomU =< After end, Rooms). | ||
|
|
||
| room_matches_filter(_Room, undefined, _Schema) -> | ||
| true; | ||
| room_matches_filter(#muc_light_room{room = {RoomU, _}, config = Config}, Filter, Schema) -> | ||
| binary:match(RoomU, Filter) =/= nomatch | ||
| orelse room_name_matches(Config, Filter, Schema). | ||
|
|
||
| %% Config is keyed by the internal schema key, which may differ from the field name | ||
|
kamilwaz marked this conversation as resolved.
Outdated
|
||
| room_name_matches(Config, Filter, Schema) -> | ||
| case lists:keyfind(<<"roomname">>, 1, Schema) of | ||
| {_FieldName, _Default, Key, _Type} -> | ||
| case proplists:get_value(Key, Config) of | ||
| Name when is_binary(Name) -> | ||
| binary:match(string:lowercase(Name), Filter) =/= nomatch; | ||
| _ -> | ||
| false | ||
| end; | ||
| false -> | ||
| false | ||
| end. | ||
|
|
||
| %%==================================================================== | ||
| %% API for tests | ||
| %%==================================================================== | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.