Repository: matrix-org/synapse

Issue:
Stabilize support for MSC2946: spaces summary
Once [MSC2946](https://github.com/matrix-org/matrix-doc/pull/2946) lands we should (at some point) switch to a stable endpoint and make some updates. There's few bits to this that we need to do:

## Phase 1

* [x] Support to the stable endpoint.
* [x] ~~Require `limit` to be > 0.~~ (This has always been correct.)
* [x] Ensure the endpoint is ratelimited.

## Phase 2

* [x] Remove the fallback logic of `get_room_hierarchy` to request the `/spaces` endpoint over federation.
* [x] Remove the `/spaces` client and federation endpoints (and corresponding code in the handlers).
* [x] Update the API responses to remove `allowed_spaces` (this was replaced with `allowed_room_ids`).
* [x] Clean-up code around max-children, which is not used by the `/hierarchy` endpoint.
* [x] Remove `room_id` from the serialized `children_state`, it is redundant and removed from the API response.

## Phase 3

* [x] Remove unstable `/hierarchy` client and federation endpoints.
* [x] Remove the typo `join_rules` field from the response.


Discussion:
I think this work needs to be split into a few pieces.

Phase 1:

* Support the stable endpoint.
* Remove some of the legacy code (the `/spaces` endpoint? and surrounding bits?)
* Enforce the `limit` / ratelimiting.

Phase 2:

* Remove the unstable `/hierarchy` endpoint.
> Remove some of the legacy code (the `/spaces` endpoint? and surrounding bits?)

It looks like these endpoints still get some usage on matrix.org, so might be best to just leave them for now and remove all the unstable code at once.

Resolving patch:
diff --git a/synapse/federation/transport/server/federation.py b/synapse/federation/transport/server/federation.py
--- a/synapse/federation/transport/server/federation.py
+++ b/synapse/federation/transport/server/federation.py
@@ -650,10 +650,6 @@ async def on_GET(
         )
 
 
-class FederationRoomHierarchyUnstableServlet(FederationRoomHierarchyServlet):
-    PREFIX = FEDERATION_UNSTABLE_PREFIX + "/org.matrix.msc2946"
-
-
 class RoomComplexityServlet(BaseFederationServlet):
     """
     Indicates to other servers how complex (and therefore likely
@@ -752,7 +748,6 @@ async def on_POST(
     FederationVersionServlet,
     RoomComplexityServlet,
     FederationRoomHierarchyServlet,
-    FederationRoomHierarchyUnstableServlet,
     FederationV1SendKnockServlet,
     FederationMakeKnockServlet,
     FederationAccountStatusServlet,
diff --git a/synapse/handlers/room_summary.py b/synapse/handlers/room_summary.py
--- a/synapse/handlers/room_summary.py
+++ b/synapse/handlers/room_summary.py
@@ -662,7 +662,7 @@ async def _is_remote_room_accessible(
         # The API doesn't return the room version so assume that a
         # join rule of knock is valid.
         if (
-            room.get("join_rules")
+            room.get("join_rule")
             in (JoinRules.PUBLIC, JoinRules.KNOCK, JoinRules.KNOCK_RESTRICTED)
             or room.get("world_readable") is True
         ):
@@ -714,9 +714,6 @@ async def _build_room_entry(self, room_id: str, for_federation: bool) -> JsonDic
             "canonical_alias": stats["canonical_alias"],
             "num_joined_members": stats["joined_members"],
             "avatar_url": stats["avatar"],
-            # plural join_rules is a documentation error but kept for historical
-            # purposes. Should match /publicRooms.
-            "join_rules": stats["join_rules"],
             "join_rule": stats["join_rules"],
             "world_readable": (
                 stats["history_visibility"] == HistoryVisibility.WORLD_READABLE
diff --git a/synapse/rest/client/room.py b/synapse/rest/client/room.py
--- a/synapse/rest/client/room.py
+++ b/synapse/rest/client/room.py
@@ -1193,12 +1193,7 @@ async def on_GET(
 
 
 class RoomHierarchyRestServlet(RestServlet):
-    PATTERNS = (
-        re.compile(
-            "^/_matrix/client/(v1|unstable/org.matrix.msc2946)"
-            "/rooms/(?P<room_id>[^/]*)/hierarchy$"
-        ),
-    )
+    PATTERNS = (re.compile("^/_matrix/client/v1/rooms/(?P<room_id>[^/]*)/hierarchy$"),)
 
     def __init__(self, hs: "HomeServer"):
         super().__init__()


Test patch:
diff --git a/tests/handlers/test_room_summary.py b/tests/handlers/test_room_summary.py
--- a/tests/handlers/test_room_summary.py
+++ b/tests/handlers/test_room_summary.py
@@ -179,7 +179,7 @@ def _assert_hierarchy(
             result_children_ids.append(
                 [
                     (cs["room_id"], cs["state_key"])
-                    for cs in result_room.get("children_state")
+                    for cs in result_room["children_state"]
                 ]
             )
 
@@ -772,7 +772,7 @@ def test_fed_filtering(self):
                 {
                     "room_id": public_room,
                     "world_readable": False,
-                    "join_rules": JoinRules.PUBLIC,
+                    "join_rule": JoinRules.PUBLIC,
                 },
             ),
             (
@@ -780,7 +780,7 @@ def test_fed_filtering(self):
                 {
                     "room_id": knock_room,
                     "world_readable": False,
-                    "join_rules": JoinRules.KNOCK,
+                    "join_rule": JoinRules.KNOCK,
                 },
             ),
             (
@@ -788,7 +788,7 @@ def test_fed_filtering(self):
                 {
                     "room_id": not_invited_room,
                     "world_readable": False,
-                    "join_rules": JoinRules.INVITE,
+                    "join_rule": JoinRules.INVITE,
                 },
             ),
             (
@@ -796,7 +796,7 @@ def test_fed_filtering(self):
                 {
                     "room_id": invited_room,
                     "world_readable": False,
-                    "join_rules": JoinRules.INVITE,
+                    "join_rule": JoinRules.INVITE,
                 },
             ),
             (
@@ -804,7 +804,7 @@ def test_fed_filtering(self):
                 {
                     "room_id": restricted_room,
                     "world_readable": False,
-                    "join_rules": JoinRules.RESTRICTED,
+                    "join_rule": JoinRules.RESTRICTED,
                     "allowed_room_ids": [],
                 },
             ),
@@ -813,7 +813,7 @@ def test_fed_filtering(self):
                 {
                     "room_id": restricted_accessible_room,
                     "world_readable": False,
-                    "join_rules": JoinRules.RESTRICTED,
+                    "join_rule": JoinRules.RESTRICTED,
                     "allowed_room_ids": [self.room],
                 },
             ),
@@ -822,7 +822,7 @@ def test_fed_filtering(self):
                 {
                     "room_id": world_readable_room,
                     "world_readable": True,
-                    "join_rules": JoinRules.INVITE,
+                    "join_rule": JoinRules.INVITE,
                 },
             ),
             (
@@ -830,7 +830,7 @@ def test_fed_filtering(self):
                 {
                     "room_id": joined_room,
                     "world_readable": False,
-                    "join_rules": JoinRules.INVITE,
+                    "join_rule": JoinRules.INVITE,
                 },
             ),
         )
@@ -911,7 +911,7 @@ def test_fed_invited(self):
             {
                 "room_id": fed_room,
                 "world_readable": False,
-                "join_rules": JoinRules.INVITE,
+                "join_rule": JoinRules.INVITE,
             },
         )