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

Commit 6a72c91

Browse files
authored
Add admin API to get a list of federated rooms (#11658)
1 parent 0938f32 commit 6a72c91

File tree

6 files changed

+444
-25
lines changed

6 files changed

+444
-25
lines changed

changelog.d/11658.feature

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Add an admin API to get a list of rooms that federate with a given remote homeserver.

docs/usage/administration/admin_api/federation.md

+60
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,66 @@ The following parameters should be set in the URL:
119119
The response fields are the same like in the `destinations` array in
120120
[List of destinations](#list-of-destinations) response.
121121

122+
## Destination rooms
123+
124+
This API gets the rooms that federate with a specific remote server.
125+
126+
The API is:
127+
128+
```
129+
GET /_synapse/admin/v1/federation/destinations/<destination>/rooms
130+
```
131+
132+
A response body like the following is returned:
133+
134+
```json
135+
{
136+
"rooms":[
137+
{
138+
"room_id": "!OGEhHVWSdvArJzumhm:matrix.org",
139+
"stream_ordering": 8326
140+
},
141+
{
142+
"room_id": "!xYvNcQPhnkrdUmYczI:matrix.org",
143+
"stream_ordering": 93534
144+
}
145+
],
146+
"total": 2
147+
}
148+
```
149+
150+
To paginate, check for `next_token` and if present, call the endpoint again
151+
with `from` set to the value of `next_token`. This will return a new page.
152+
153+
If the endpoint does not return a `next_token` then there are no more destinations
154+
to paginate through.
155+
156+
**Parameters**
157+
158+
The following parameters should be set in the URL:
159+
160+
- `destination` - Name of the remote server.
161+
162+
The following query parameters are available:
163+
164+
- `from` - Offset in the returned list. Defaults to `0`.
165+
- `limit` - Maximum amount of destinations to return. Defaults to `100`.
166+
- `dir` - Direction of room order by `room_id`. Either `f` for forwards or `b` for
167+
backwards. Defaults to `f`.
168+
169+
**Response**
170+
171+
The following fields are returned in the JSON response body:
172+
173+
- `rooms` - An array of objects, each containing information about a room.
174+
Room objects contain the following fields:
175+
- `room_id` - string - The ID of the room.
176+
- `stream_ordering` - integer - The stream ordering of the most recent
177+
successfully-sent [PDU](understanding_synapse_through_grafana_graphs.md#federation)
178+
to this destination in this room.
179+
- `next_token`: string representing a positive integer - Indication for pagination. See above.
180+
- `total` - integer - Total number of destinations.
181+
122182
## Reset connection timeout
123183

124184
Synapse makes federation requests to other homeservers. If a federation request fails,

synapse/rest/admin/__init__.py

+2
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
EventReportsRestServlet,
4242
)
4343
from synapse.rest.admin.federation import (
44+
DestinationMembershipRestServlet,
4445
DestinationResetConnectionRestServlet,
4546
DestinationRestServlet,
4647
ListDestinationsRestServlet,
@@ -268,6 +269,7 @@ def register_servlets(hs: "HomeServer", http_server: HttpServer) -> None:
268269
ListRegistrationTokensRestServlet(hs).register(http_server)
269270
NewRegistrationTokenRestServlet(hs).register(http_server)
270271
RegistrationTokenRestServlet(hs).register(http_server)
272+
DestinationMembershipRestServlet(hs).register(http_server)
271273
DestinationResetConnectionRestServlet(hs).register(http_server)
272274
DestinationRestServlet(hs).register(http_server)
273275
ListDestinationsRestServlet(hs).register(http_server)

synapse/rest/admin/federation.py

+56
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,62 @@ async def on_GET(
148148
return HTTPStatus.OK, response
149149

150150

151+
class DestinationMembershipRestServlet(RestServlet):
152+
"""Get list of rooms of a destination.
153+
This needs user to have administrator access in Synapse.
154+
155+
GET /_synapse/admin/v1/federation/destinations/<destination>/rooms?from=0&limit=10
156+
157+
returns:
158+
200 OK with a list of rooms if success otherwise an error.
159+
160+
The parameters `from` and `limit` are required only for pagination.
161+
By default, a `limit` of 100 is used.
162+
"""
163+
164+
PATTERNS = admin_patterns("/federation/destinations/(?P<destination>[^/]*)/rooms$")
165+
166+
def __init__(self, hs: "HomeServer"):
167+
self._auth = hs.get_auth()
168+
self._store = hs.get_datastore()
169+
170+
async def on_GET(
171+
self, request: SynapseRequest, destination: str
172+
) -> Tuple[int, JsonDict]:
173+
await assert_requester_is_admin(self._auth, request)
174+
175+
if not await self._store.is_destination_known(destination):
176+
raise NotFoundError("Unknown destination")
177+
178+
start = parse_integer(request, "from", default=0)
179+
limit = parse_integer(request, "limit", default=100)
180+
181+
if start < 0:
182+
raise SynapseError(
183+
HTTPStatus.BAD_REQUEST,
184+
"Query parameter from must be a string representing a positive integer.",
185+
errcode=Codes.INVALID_PARAM,
186+
)
187+
188+
if limit < 0:
189+
raise SynapseError(
190+
HTTPStatus.BAD_REQUEST,
191+
"Query parameter limit must be a string representing a positive integer.",
192+
errcode=Codes.INVALID_PARAM,
193+
)
194+
195+
direction = parse_string(request, "dir", default="f", allowed_values=("f", "b"))
196+
197+
rooms, total = await self._store.get_destination_rooms_paginate(
198+
destination, start, limit, direction
199+
)
200+
response = {"rooms": rooms, "total": total}
201+
if (start + limit) < total:
202+
response["next_token"] = str(start + len(rooms))
203+
204+
return HTTPStatus.OK, response
205+
206+
151207
class DestinationResetConnectionRestServlet(RestServlet):
152208
"""Reset destinations' connection timeouts and wake it up.
153209
This needs user to have administrator access in Synapse.

synapse/storage/databases/main/transactions.py

+48
Original file line numberDiff line numberDiff line change
@@ -561,6 +561,54 @@ def get_destinations_paginate_txn(
561561
"get_destinations_paginate_txn", get_destinations_paginate_txn
562562
)
563563

564+
async def get_destination_rooms_paginate(
565+
self, destination: str, start: int, limit: int, direction: str = "f"
566+
) -> Tuple[List[JsonDict], int]:
567+
"""Function to retrieve a paginated list of destination's rooms.
568+
This will return a json list of rooms and the
569+
total number of rooms.
570+
571+
Args:
572+
destination: the destination to query
573+
start: start number to begin the query from
574+
limit: number of rows to retrieve
575+
direction: sort ascending or descending by room_id
576+
Returns:
577+
A tuple of a dict of rooms and a count of total rooms.
578+
"""
579+
580+
def get_destination_rooms_paginate_txn(
581+
txn: LoggingTransaction,
582+
) -> Tuple[List[JsonDict], int]:
583+
584+
if direction == "b":
585+
order = "DESC"
586+
else:
587+
order = "ASC"
588+
589+
sql = """
590+
SELECT COUNT(*) as total_rooms
591+
FROM destination_rooms
592+
WHERE destination = ?
593+
"""
594+
txn.execute(sql, [destination])
595+
count = cast(Tuple[int], txn.fetchone())[0]
596+
597+
rooms = self.db_pool.simple_select_list_paginate_txn(
598+
txn=txn,
599+
table="destination_rooms",
600+
orderby="room_id",
601+
start=start,
602+
limit=limit,
603+
retcols=("room_id", "stream_ordering"),
604+
order_direction=order,
605+
)
606+
return rooms, count
607+
608+
return await self.db_pool.runInteraction(
609+
"get_destination_rooms_paginate_txn", get_destination_rooms_paginate_txn
610+
)
611+
564612
async def is_destination_known(self, destination: str) -> bool:
565613
"""Check if a destination is known to the server."""
566614
result = await self.db_pool.simple_select_one_onecol(

0 commit comments

Comments
 (0)