mirror of
https://github.com/element-hq/synapse.git
synced 2024-12-14 11:57:44 +00:00
Add room details admin endpoint (#7317)
This commit is contained in:
parent
5bb26b7c4f
commit
a4a5ec4096
7 changed files with 165 additions and 1 deletions
1
changelog.d/7317.feature
Normal file
1
changelog.d/7317.feature
Normal file
|
@ -0,0 +1 @@
|
|||
Add room details admin endpoint. Contributed by Awesome Technologies Innovationslabor GmbH.
|
|
@ -264,3 +264,57 @@ Response:
|
|||
|
||||
Once the `next_token` parameter is no longer present, we know we've reached the
|
||||
end of the list.
|
||||
|
||||
# DRAFT: Room Details API
|
||||
|
||||
The Room Details admin API allows server admins to get all details of a room.
|
||||
|
||||
This API is still a draft and details might change!
|
||||
|
||||
The following fields are possible in the JSON response body:
|
||||
|
||||
* `room_id` - The ID of the room.
|
||||
* `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.
|
||||
|
||||
## Usage
|
||||
|
||||
A standard request:
|
||||
|
||||
```
|
||||
GET /_synapse/admin/v1/rooms/<room_id>
|
||||
|
||||
{}
|
||||
```
|
||||
|
||||
Response:
|
||||
|
||||
```
|
||||
{
|
||||
"room_id": "!mscvqgqpHYjBGDxNym:matrix.org",
|
||||
"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
|
||||
}
|
||||
```
|
||||
|
|
|
@ -32,6 +32,7 @@ from synapse.rest.admin.purge_room_servlet import PurgeRoomServlet
|
|||
from synapse.rest.admin.rooms import (
|
||||
JoinRoomAliasServlet,
|
||||
ListRoomRestServlet,
|
||||
RoomRestServlet,
|
||||
ShutdownRoomRestServlet,
|
||||
)
|
||||
from synapse.rest.admin.server_notice_servlet import SendServerNoticeServlet
|
||||
|
@ -193,6 +194,7 @@ def register_servlets(hs, http_server):
|
|||
"""
|
||||
register_servlets_for_client_rest_resource(hs, http_server)
|
||||
ListRoomRestServlet(hs).register(http_server)
|
||||
RoomRestServlet(hs).register(http_server)
|
||||
JoinRoomAliasServlet(hs).register(http_server)
|
||||
PurgeRoomServlet(hs).register(http_server)
|
||||
SendServerNoticeServlet(hs).register(http_server)
|
||||
|
|
|
@ -26,6 +26,7 @@ from synapse.http.servlet import (
|
|||
)
|
||||
from synapse.rest.admin._base import (
|
||||
admin_patterns,
|
||||
assert_requester_is_admin,
|
||||
assert_user_is_admin,
|
||||
historical_admin_path_patterns,
|
||||
)
|
||||
|
@ -169,7 +170,7 @@ class ListRoomRestServlet(RestServlet):
|
|||
in a dictionary containing room information. Supports pagination.
|
||||
"""
|
||||
|
||||
PATTERNS = admin_patterns("/rooms")
|
||||
PATTERNS = admin_patterns("/rooms$")
|
||||
|
||||
def __init__(self, hs):
|
||||
self.store = hs.get_datastore()
|
||||
|
@ -253,6 +254,29 @@ class ListRoomRestServlet(RestServlet):
|
|||
return 200, response
|
||||
|
||||
|
||||
class RoomRestServlet(RestServlet):
|
||||
"""Get room details.
|
||||
|
||||
TODO: Add on_POST to allow room creation without joining the room
|
||||
"""
|
||||
|
||||
PATTERNS = admin_patterns("/rooms/(?P<room_id>[^/]+)$")
|
||||
|
||||
def __init__(self, hs):
|
||||
self.hs = hs
|
||||
self.auth = hs.get_auth()
|
||||
self.store = hs.get_datastore()
|
||||
|
||||
async def on_GET(self, request, room_id):
|
||||
await assert_requester_is_admin(self.auth, request)
|
||||
|
||||
ret = await self.store.get_room_with_stats(room_id)
|
||||
if not ret:
|
||||
raise NotFoundError("Room not found")
|
||||
|
||||
return 200, ret
|
||||
|
||||
|
||||
class JoinRoomAliasServlet(RestServlet):
|
||||
|
||||
PATTERNS = admin_patterns("/join/(?P<room_identifier>[^/]*)")
|
||||
|
|
|
@ -98,6 +98,37 @@ class RoomWorkerStore(SQLBaseStore):
|
|||
allow_none=True,
|
||||
)
|
||||
|
||||
def get_room_with_stats(self, room_id: str):
|
||||
"""Retrieve room with statistics.
|
||||
|
||||
Args:
|
||||
room_id: The ID of the room to retrieve.
|
||||
Returns:
|
||||
A dict containing the room information, or None if the room is unknown.
|
||||
"""
|
||||
|
||||
def get_room_with_stats_txn(txn, room_id):
|
||||
sql = """
|
||||
SELECT room_id, state.name, state.canonical_alias, curr.joined_members,
|
||||
curr.local_users_in_room AS joined_local_members, rooms.room_version AS version,
|
||||
rooms.creator, state.encryption, state.is_federatable AS federatable,
|
||||
rooms.is_public AS public, state.join_rules, state.guest_access,
|
||||
state.history_visibility, curr.current_state_events AS state_events
|
||||
FROM rooms
|
||||
LEFT JOIN room_stats_state state USING (room_id)
|
||||
LEFT JOIN room_stats_current curr USING (room_id)
|
||||
WHERE room_id = ?
|
||||
"""
|
||||
txn.execute(sql, [room_id])
|
||||
res = self.db.cursor_to_dict(txn)[0]
|
||||
res["federatable"] = bool(res["federatable"])
|
||||
res["public"] = bool(res["public"])
|
||||
return res
|
||||
|
||||
return self.db.runInteraction(
|
||||
"get_room_with_stats", get_room_with_stats_txn, room_id
|
||||
)
|
||||
|
||||
def get_public_room_ids(self):
|
||||
return self.db.simple_select_onecol(
|
||||
table="rooms",
|
||||
|
|
|
@ -701,6 +701,47 @@ class RoomTestCase(unittest.HomeserverTestCase):
|
|||
_search_test(None, "bar")
|
||||
_search_test(None, "", expected_http_code=400)
|
||||
|
||||
def test_single_room(self):
|
||||
"""Test that a single room can be requested correctly"""
|
||||
# Create two test rooms
|
||||
room_id_1 = self.helper.create_room_as(self.admin_user, tok=self.admin_user_tok)
|
||||
room_id_2 = self.helper.create_room_as(self.admin_user, tok=self.admin_user_tok)
|
||||
|
||||
room_name_1 = "something"
|
||||
room_name_2 = "else"
|
||||
|
||||
# Set the name for each room
|
||||
self.helper.send_state(
|
||||
room_id_1, "m.room.name", {"name": room_name_1}, tok=self.admin_user_tok,
|
||||
)
|
||||
self.helper.send_state(
|
||||
room_id_2, "m.room.name", {"name": room_name_2}, tok=self.admin_user_tok,
|
||||
)
|
||||
|
||||
url = "/_synapse/admin/v1/rooms/%s" % (room_id_1,)
|
||||
request, channel = self.make_request(
|
||||
"GET", url.encode("ascii"), access_token=self.admin_user_tok,
|
||||
)
|
||||
self.render(request)
|
||||
self.assertEqual(200, channel.code, msg=channel.json_body)
|
||||
|
||||
self.assertIn("room_id", channel.json_body)
|
||||
self.assertIn("name", channel.json_body)
|
||||
self.assertIn("canonical_alias", channel.json_body)
|
||||
self.assertIn("joined_members", channel.json_body)
|
||||
self.assertIn("joined_local_members", channel.json_body)
|
||||
self.assertIn("version", channel.json_body)
|
||||
self.assertIn("creator", channel.json_body)
|
||||
self.assertIn("encryption", channel.json_body)
|
||||
self.assertIn("federatable", channel.json_body)
|
||||
self.assertIn("public", channel.json_body)
|
||||
self.assertIn("join_rules", channel.json_body)
|
||||
self.assertIn("guest_access", channel.json_body)
|
||||
self.assertIn("history_visibility", channel.json_body)
|
||||
self.assertIn("state_events", channel.json_body)
|
||||
|
||||
self.assertEqual(room_id_1, channel.json_body["room_id"])
|
||||
|
||||
|
||||
class JoinAliasRoomTestCase(unittest.HomeserverTestCase):
|
||||
|
||||
|
|
|
@ -55,6 +55,17 @@ class RoomStoreTestCase(unittest.TestCase):
|
|||
(yield self.store.get_room(self.room.to_string())),
|
||||
)
|
||||
|
||||
@defer.inlineCallbacks
|
||||
def test_get_room_with_stats(self):
|
||||
self.assertDictContainsSubset(
|
||||
{
|
||||
"room_id": self.room.to_string(),
|
||||
"creator": self.u_creator.to_string(),
|
||||
"public": True,
|
||||
},
|
||||
(yield self.store.get_room_with_stats(self.room.to_string())),
|
||||
)
|
||||
|
||||
|
||||
class RoomEventsStoreTestCase(unittest.TestCase):
|
||||
@defer.inlineCallbacks
|
||||
|
|
Loading…
Reference in a new issue