Skip to content
This repository has been archived by the owner on Apr 26, 2024. It is now read-only.

Extend room admin api with additional attributes #7225

Merged
merged 11 commits into from
Apr 22, 2020
1 change: 1 addition & 0 deletions changelog.d/7225.misc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Extend room admin api (`GET /_synapse/admin/v1/rooms`) with additional attributes.
107 changes: 100 additions & 7 deletions docs/admin_api/rooms.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,21 @@ The following query parameters are available:
* `from` - Offset in the returned list. Defaults to `0`.
* `limit` - Maximum amount of rooms to return. Defaults to `100`.
* `order_by` - The method in which to sort the returned list of rooms. Valid values are:
- `alphabetical` - Rooms are ordered alphabetically by room name. This is the default.
- `size` - Rooms are ordered by the number of members. Largest to smallest.
- `alphabetical` - Same as `name`. This is deprecated.
- `size` - Same as `joined_members`. This is deprecated.
- `name` - Rooms are ordered alphabetically by room name. This is the default.
- `canonical_alias` - Rooms are ordered alphabetically by main alias address of the room.
- `joined_members` - Rooms are ordered by the number of members. Largest to smallest.
- `joined_local_members` - Rooms are ordered by the number of local members. Largest to smallest.
- `version` - Rooms are ordered by room version. Largest to smallest.
- `creator` - Rooms are ordered alphabetically by creator of the room.
- `encryption` - Rooms are ordered alphabetically by the end-to-end encryption algorithm.
- `federatable` - Rooms are ordered by whether the room is federatable.
- `public` - Rooms are ordered by visibility in room list.
- `join_rules` - Rooms are ordered alphabetically by join rules of the room.
- `guest_access` - Rooms are ordered alphabetically by guest access option of the room.
- `history_visibility` - Rooms are ordered alphabetically by visibility of history of the room.
- `state_events` - Rooms are ordered by number of state events. Largest to smallest.
* `dir` - Direction of room order. Either `f` for forwards or `b` for backwards. Setting
this value to `b` will reverse the above sort order. Defaults to `f`.
* `search_term` - Filter rooms by their room name. Search term can be contained in any
Expand All @@ -26,6 +39,16 @@ The following fields are possible in the JSON response body:
- `name` - The name of the room.
- `canonical_alias` - The canonical (main) alias address of the room.
- `joined_members` - How many users are currently in the room.
- `joined_local_members` - How many local users are currently in the room.
- `version` - The version of the room as a string.
- `creator` - The `user_id` of the room creator.
- `encryption` - Algorithm of end-to-end encryption of messages. Is `null` if encryption is not active.
- `federatable` - Whether users on other servers can join this room.
- `public` - Whether the room is visible in room directory.
- `join_rules` - The type of rules used for users wishing to join this room. One of: ["public", "knock", "invite", "private"].
- `guest_access` - Whether guests can join the room. One of: ["can_join", "forbidden"].
- `history_visibility` - Who can see the room history. One of: ["invited", "joined", "shared", "world_readable"].
- `state_events` - Total number of state_events of a room. Complexity of the room.
* `offset` - The current pagination offset in rooms. This parameter should be
used instead of `next_token` for room offset as `next_token` is
not intended to be parsed.
Expand Down Expand Up @@ -60,14 +83,34 @@ Response:
"room_id": "!OGEhHVWSdvArJzumhm:matrix.org",
"name": "Matrix HQ",
"canonical_alias": "#matrix:matrix.org",
"joined_members": 8326
"joined_members": 8326,
"joined_local_members": 2,
"version": "1",
"creator": "@foo:matrix.org",
"encryption": null,
"federatable": true,
"public": true,
"join_rules": "invite",
"guest_access": null,
"history_visibility": "shared",
"state_events": 93534
},
... (8 hidden items) ...
{
"room_id": "!xYvNcQPhnkrdUmYczI:matrix.org",
"name": "This Week In Matrix (TWIM)",
"canonical_alias": "#twim:matrix.org",
"joined_members": 314
"joined_members": 314,
"joined_local_members": 20,
"version": "4",
"creator": "@foo:matrix.org",
"encryption": "m.megolm.v1.aes-sha2",
"federatable": true,
"public": false,
"join_rules": "invite",
"guest_access": null,
"history_visibility": "shared",
"state_events": 8345
}
],
"offset": 0,
Expand All @@ -92,7 +135,17 @@ Response:
"room_id": "!xYvNcQPhnkrdUmYczI:matrix.org",
"name": "This Week In Matrix (TWIM)",
"canonical_alias": "#twim:matrix.org",
"joined_members": 314
"joined_members": 314,
"joined_local_members": 20,
"version": "4",
"creator": "@foo:matrix.org",
"encryption": "m.megolm.v1.aes-sha2",
"federatable": true,
"public": false,
"join_rules": "invite",
"guest_access": null,
"history_visibility": "shared",
"state_events": 8
}
],
"offset": 0,
Expand All @@ -117,14 +170,34 @@ Response:
"room_id": "!OGEhHVWSdvArJzumhm:matrix.org",
"name": "Matrix HQ",
"canonical_alias": "#matrix:matrix.org",
"joined_members": 8326
"joined_members": 8326,
"joined_local_members": 2,
"version": "1",
"creator": "@foo:matrix.org",
"encryption": null,
"federatable": true,
"public": true,
"join_rules": "invite",
"guest_access": null,
"history_visibility": "shared",
"state_events": 93534
},
... (98 hidden items) ...
{
"room_id": "!xYvNcQPhnkrdUmYczI:matrix.org",
"name": "This Week In Matrix (TWIM)",
"canonical_alias": "#twim:matrix.org",
"joined_members": 314
"joined_members": 314,
"joined_local_members": 20,
"version": "4",
"creator": "@foo:matrix.org",
"encryption": "m.megolm.v1.aes-sha2",
"federatable": true,
"public": false,
"join_rules": "invite",
"guest_access": null,
"history_visibility": "shared",
"state_events": 8345
}
],
"offset": 0,
Expand Down Expand Up @@ -154,13 +227,33 @@ Response:
"name": "Music Theory",
"canonical_alias": "#musictheory:matrix.org",
"joined_members": 127
"joined_local_members": 2,
"version": "1",
"creator": "@foo:matrix.org",
"encryption": null,
"federatable": true,
"public": true,
"join_rules": "invite",
"guest_access": null,
"history_visibility": "shared",
"state_events": 93534
},
... (48 hidden items) ...
{
"room_id": "!twcBhHVdZlQWuuxBhN:termina.org.uk",
"name": "weechat-matrix",
"canonical_alias": "#weechat-matrix:termina.org.uk",
"joined_members": 137
"joined_local_members": 20,
"version": "4",
"creator": "@foo:termina.org.uk",
"encryption": null,
"federatable": true,
"public": true,
"join_rules": "invite",
"guest_access": null,
"history_visibility": "shared",
"state_events": 8345
}
],
"offset": 100,
Expand Down
15 changes: 14 additions & 1 deletion synapse/rest/admin/rooms.py
Original file line number Diff line number Diff line change
Expand Up @@ -183,10 +183,23 @@ async def on_GET(self, request):
# Extract query parameters
start = parse_integer(request, "from", default=0)
limit = parse_integer(request, "limit", default=100)
order_by = parse_string(request, "order_by", default="alphabetical")
order_by = parse_string(request, "order_by", default=RoomSortOrder.NAME.value)
if order_by not in (
RoomSortOrder.ALPHABETICAL.value,
RoomSortOrder.SIZE.value,
RoomSortOrder.NAME.value,
RoomSortOrder.CANONICAL_ALIAS.value,
RoomSortOrder.JOINED_MEMBERS.value,
RoomSortOrder.JOINED_LOCAL_MEMBERS.value,
RoomSortOrder.VERSION.value,
RoomSortOrder.CREATOR.value,
RoomSortOrder.ENCRYPTION.value,
RoomSortOrder.FEDERATABLE.value,
RoomSortOrder.PUBLIC.value,
RoomSortOrder.JOIN_RULES.value,
RoomSortOrder.GUEST_ACCESS.value,
RoomSortOrder.HISTORY_VISIBILITY.value,
RoomSortOrder.STATE_EVENTS.value,
):
raise SynapseError(
400,
Expand Down
78 changes: 74 additions & 4 deletions synapse/storage/data_stores/main/room.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,12 +52,28 @@ class RoomSortOrder(Enum):
"""
Enum to define the sorting method used when returning rooms with get_rooms_paginate

ALPHABETICAL = sort rooms alphabetically by name
SIZE = sort rooms by membership size, highest to lowest
NAME = sort rooms alphabetically by name
JOINED_MEMBERS = sort rooms by membership size, highest to lowest
"""

# ALPHABETICAL and SIZE are deprecated.
# ALPHABETICAL is the same as NAME.
ALPHABETICAL = "alphabetical"
anoadragon453 marked this conversation as resolved.
Show resolved Hide resolved
# SIZE is the same as JOINED_MEMBERS.
SIZE = "size"
NAME = "name"
CANONICAL_ALIAS = "canonical_alias"
JOINED_MEMBERS = "joined_members"
JOINED_LOCAL_MEMBERS = "joined_local_members"
VERSION = "version"
CREATOR = "creator"
ENCRYPTION = "encryption"
FEDERATABLE = "federatable"
PUBLIC = "public"
JOIN_RULES = "join_rules"
GUEST_ACCESS = "guest_access"
HISTORY_VISIBILITY = "history_visibility"
STATE_EVENTS = "state_events"


class RoomWorkerStore(SQLBaseStore):
Expand Down Expand Up @@ -329,12 +345,52 @@ async def get_rooms_paginate(

# Set ordering
if RoomSortOrder(order_by) == RoomSortOrder.SIZE:
# Deprecated in favour of RoomSortOrder.JOINED_MEMBERS
order_by_column = "curr.joined_members"
order_by_asc = False
elif RoomSortOrder(order_by) == RoomSortOrder.ALPHABETICAL:
# Sort alphabetically
# Deprecated in favour of RoomSortOrder.NAME
order_by_column = "state.name"
order_by_asc = True
elif RoomSortOrder(order_by) == RoomSortOrder.NAME:
order_by_column = "state.name"
order_by_asc = True
clokep marked this conversation as resolved.
Show resolved Hide resolved
elif RoomSortOrder(order_by) == RoomSortOrder.CANONICAL_ALIAS:
order_by_column = "state.canonical_alias"
order_by_asc = True
elif RoomSortOrder(order_by) == RoomSortOrder.JOINED_MEMBERS:
order_by_column = "curr.joined_members"
order_by_asc = False
elif RoomSortOrder(order_by) == RoomSortOrder.JOINED_LOCAL_MEMBERS:
order_by_column = "curr.local_users_in_room"
order_by_asc = False
elif RoomSortOrder(order_by) == RoomSortOrder.VERSION:
order_by_column = "rooms.room_version"
order_by_asc = False
elif RoomSortOrder(order_by) == RoomSortOrder.CREATOR:
order_by_column = "rooms.creator"
order_by_asc = True
elif RoomSortOrder(order_by) == RoomSortOrder.ENCRYPTION:
order_by_column = "state.encryption"
order_by_asc = True
elif RoomSortOrder(order_by) == RoomSortOrder.FEDERATABLE:
order_by_column = "state.is_federatable"
order_by_asc = True
elif RoomSortOrder(order_by) == RoomSortOrder.PUBLIC:
order_by_column = "rooms.is_public"
order_by_asc = True
elif RoomSortOrder(order_by) == RoomSortOrder.JOIN_RULES:
order_by_column = "state.join_rules"
order_by_asc = True
elif RoomSortOrder(order_by) == RoomSortOrder.GUEST_ACCESS:
order_by_column = "state.guest_access"
order_by_asc = True
elif RoomSortOrder(order_by) == RoomSortOrder.HISTORY_VISIBILITY:
order_by_column = "state.history_visibility"
order_by_asc = True
elif RoomSortOrder(order_by) == RoomSortOrder.STATE_EVENTS:
order_by_column = "curr.current_state_events"
order_by_asc = False
else:
raise StoreError(
500, "Incorrect value for order_by provided: %s" % order_by
Expand All @@ -349,9 +405,13 @@ async def get_rooms_paginate(
# for, and another query for getting the total number of events that could be
# returned. Thus allowing us to see if there are more events to paginate through
info_sql = """
SELECT state.room_id, state.name, state.canonical_alias, curr.joined_members
SELECT state.room_id, state.name, state.canonical_alias, curr.joined_members,
curr.local_users_in_room, rooms.room_version, rooms.creator,
state.encryption, state.is_federatable, rooms.is_public, state.join_rules,
state.guest_access, state.history_visibility, curr.current_state_events
FROM room_stats_state state
INNER JOIN room_stats_current curr USING (room_id)
INNER JOIN rooms USING (room_id)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any idea if there are performance impacts to joining rooms here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do not know it. I am not a SQL specialist.

Copy link
Member

@anoadragon453 anoadragon453 Apr 22, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

explain for the previous version with some example values:

synapse=# explain SELECT state.room_id, state.name, state.canonical_alias, curr.joined_members
synapse-# FROM room_stats_state state
synapse-# INNER JOIN room_stats_current curr USING (room_id)
synapse-# ORDER BY state.name
synapse-# LIMIT 200
synapse-# OFFSET 5;
                                             QUERY PLAN                                             
----------------------------------------------------------------------------------------------------
 Limit  (cost=255.31..255.81 rows=200 width=90)
   ->  Sort  (cost=255.30..260.44 rows=2058 width=90)
         Sort Key: state.name
         ->  Hash Join  (cost=76.99..165.99 rows=2058 width=90)
               Hash Cond: (state.room_id = curr.room_id)
               ->  Seq Scan on room_stats_state state  (cost=0.00..83.58 rows=2058 width=86)
               ->  Hash  (cost=50.33..50.33 rows=2133 width=36)
                     ->  Seq Scan on room_stats_current curr  (cost=0.00..50.33 rows=2133 width=36)
(8 rows)

synapse=# 

and with the new version:

synapse=# explain SELECT state.room_id, state.name, state.canonical_alias, curr.joined_members,
synapse-# curr.local_users_in_room, rooms.room_version, rooms.creator,
synapse-# state.encryption, state.is_federatable, rooms.is_public, state.join_rules,
synapse-# state.guest_access, state.history_visibility, curr.current_state_events
synapse-# FROM room_stats_state state
synapse-# INNER JOIN room_stats_current curr USING (room_id)
synapse-# INNER JOIN rooms USING (room_id)
synapse-# ORDER BY rooms.room_version
synapse-# LIMIT 200
synapse-# OFFSET 5;
                                             QUERY PLAN                                             
----------------------------------------------------------------------------------------------------
 Limit  (cost=331.68..332.18 rows=200 width=162)
   ->  Sort  (cost=331.67..336.44 rows=1907 width=162)
         Sort Key: rooms.room_version
         ->  Hash Join  (cost=154.90..248.91 rows=1907 width=162)
               Hash Cond: (state.room_id = curr.room_id)
               ->  Hash Join  (cost=77.91..166.90 rows=1907 width=182)
                     Hash Cond: (state.room_id = rooms.room_id)
                     ->  Seq Scan on room_stats_state state  (cost=0.00..83.58 rows=2058 width=131)
                     ->  Hash  (cost=54.07..54.07 rows=1907 width=51)
                           ->  Seq Scan on rooms  (cost=0.00..54.07 rows=1907 width=51)
               ->  Hash  (cost=50.33..50.33 rows=2133 width=44)
                     ->  Seq Scan on room_stats_current curr  (cost=0.00..50.33 rows=2133 width=44)
(12 rows)

synapse=# 

So a ~30% increase on the upper bound, but this query is currently only run by the admin API, and as we're getting much more information as a result of it I don't think this is too much of an issue.

%s
ORDER BY %s %s
LIMIT ?
Expand Down Expand Up @@ -389,6 +449,16 @@ def _get_rooms_paginate_txn(txn):
"name": room[1],
"canonical_alias": room[2],
"joined_members": room[3],
"joined_local_members": room[4],
"version": room[5],
"creator": room[6],
"encryption": room[7],
"federatable": room[8],
"public": room[9],
"join_rules": room[10],
"guest_access": room[11],
"history_visibility": room[12],
"state_events": room[13],
}
)

Expand Down
Loading