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

Add admin API to get a list of federated rooms #11658

Merged
merged 15 commits into from
Jan 25, 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
1 change: 1 addition & 0 deletions changelog.d/11658.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add an admin API to get a list of rooms that federate with a given remote homeserver.
60 changes: 60 additions & 0 deletions docs/usage/administration/admin_api/federation.md
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,66 @@ The following parameters should be set in the URL:
The response fields are the same like in the `destinations` array in
[List of destinations](#list-of-destinations) response.

## Destination rooms

This API gets the rooms that federate with a specific remote server.

The API is:

```
GET /_synapse/admin/v1/federation/destinations/<destination>/rooms
```

A response body like the following is returned:

```json
{
"rooms":[
{
"room_id": "!OGEhHVWSdvArJzumhm:matrix.org",
"stream_ordering": 8326
},
{
"room_id": "!xYvNcQPhnkrdUmYczI:matrix.org",
"stream_ordering": 93534
}
],
"total": 2
}
```

To paginate, check for `next_token` and if present, call the endpoint again
with `from` set to the value of `next_token`. This will return a new page.

If the endpoint does not return a `next_token` then there are no more destinations
to paginate through.

**Parameters**

The following parameters should be set in the URL:

- `destination` - Name of the remote server.

The following query parameters are available:

- `from` - Offset in the returned list. Defaults to `0`.
- `limit` - Maximum amount of destinations to return. Defaults to `100`.
- `dir` - Direction of room order by `room_id`. Either `f` for forwards or `b` for
backwards. Defaults to `f`.

**Response**

The following fields are returned in the JSON response body:

- `rooms` - An array of objects, each containing information about a room.
Room objects contain the following fields:
- `room_id` - string - The ID of the room.
- `stream_ordering` - integer - The stream ordering of the most recent
successfully-sent [PDU](understanding_synapse_through_grafana_graphs.md#federation)
to this destination in this room.
- `next_token`: string representing a positive integer - Indication for pagination. See above.
- `total` - integer - Total number of destinations.

## Reset connection timeout

Synapse makes federation requests to other homeservers. If a federation request fails,
Expand Down
2 changes: 2 additions & 0 deletions synapse/rest/admin/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
EventReportsRestServlet,
)
from synapse.rest.admin.federation import (
DestinationMembershipRestServlet,
DestinationResetConnectionRestServlet,
DestinationRestServlet,
ListDestinationsRestServlet,
Expand Down Expand Up @@ -268,6 +269,7 @@ def register_servlets(hs: "HomeServer", http_server: HttpServer) -> None:
ListRegistrationTokensRestServlet(hs).register(http_server)
NewRegistrationTokenRestServlet(hs).register(http_server)
RegistrationTokenRestServlet(hs).register(http_server)
DestinationMembershipRestServlet(hs).register(http_server)
DestinationResetConnectionRestServlet(hs).register(http_server)
DestinationRestServlet(hs).register(http_server)
ListDestinationsRestServlet(hs).register(http_server)
Expand Down
56 changes: 56 additions & 0 deletions synapse/rest/admin/federation.py
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,62 @@ async def on_GET(
return HTTPStatus.OK, response


class DestinationMembershipRestServlet(RestServlet):
"""Get list of rooms of a destination.
This needs user to have administrator access in Synapse.

GET /_synapse/admin/v1/federation/destinations/<destination>/rooms?from=0&limit=10

returns:
200 OK with a list of rooms if success otherwise an error.

The parameters `from` and `limit` are required only for pagination.
By default, a `limit` of 100 is used.
"""

PATTERNS = admin_patterns("/federation/destinations/(?P<destination>[^/]*)/rooms$")

def __init__(self, hs: "HomeServer"):
self._auth = hs.get_auth()
self._store = hs.get_datastore()

async def on_GET(
self, request: SynapseRequest, destination: str
) -> Tuple[int, JsonDict]:
await assert_requester_is_admin(self._auth, request)
DMRobertson marked this conversation as resolved.
Show resolved Hide resolved

if not await self._store.is_destination_known(destination):
raise NotFoundError("Unknown destination")

start = parse_integer(request, "from", default=0)
limit = parse_integer(request, "limit", default=100)

if start < 0:
raise SynapseError(
HTTPStatus.BAD_REQUEST,
"Query parameter from must be a string representing a positive integer.",
errcode=Codes.INVALID_PARAM,
)

if limit < 0:
raise SynapseError(
HTTPStatus.BAD_REQUEST,
"Query parameter limit must be a string representing a positive integer.",
errcode=Codes.INVALID_PARAM,
)

direction = parse_string(request, "dir", default="f", allowed_values=("f", "b"))

rooms, total = await self._store.get_destination_rooms_paginate(
destination, start, limit, direction
)
response = {"rooms": rooms, "total": total}
if (start + limit) < total:
response["next_token"] = str(start + len(rooms))

return HTTPStatus.OK, response


class DestinationResetConnectionRestServlet(RestServlet):
"""Reset destinations' connection timeouts and wake it up.
This needs user to have administrator access in Synapse.
Expand Down
48 changes: 48 additions & 0 deletions synapse/storage/databases/main/transactions.py
Original file line number Diff line number Diff line change
Expand Up @@ -561,6 +561,54 @@ def get_destinations_paginate_txn(
"get_destinations_paginate_txn", get_destinations_paginate_txn
)

async def get_destination_rooms_paginate(
self, destination: str, start: int, limit: int, direction: str = "f"
) -> Tuple[List[JsonDict], int]:
"""Function to retrieve a paginated list of destination's rooms.
This will return a json list of rooms and the
total number of rooms.

Args:
destination: the destination to query
start: start number to begin the query from
limit: number of rows to retrieve
direction: sort ascending or descending by room_id
Returns:
A tuple of a dict of rooms and a count of total rooms.
"""

def get_destination_rooms_paginate_txn(
txn: LoggingTransaction,
) -> Tuple[List[JsonDict], int]:

if direction == "b":
order = "DESC"
else:
order = "ASC"

sql = """
SELECT COUNT(*) as total_rooms
FROM destination_rooms
WHERE destination = ?
"""
txn.execute(sql, [destination])
count = cast(Tuple[int], txn.fetchone())[0]

rooms = self.db_pool.simple_select_list_paginate_txn(
txn=txn,
table="destination_rooms",
orderby="room_id",
start=start,
limit=limit,
retcols=("room_id", "stream_ordering"),
order_direction=order,
DMRobertson marked this conversation as resolved.
Show resolved Hide resolved
)
return rooms, count

return await self.db_pool.runInteraction(
"get_destination_rooms_paginate_txn", get_destination_rooms_paginate_txn
)

async def is_destination_known(self, destination: str) -> bool:
"""Check if a destination is known to the server."""
result = await self.db_pool.simple_select_one_onecol(
Expand Down
Loading