summary refs log tree commit diff
path: root/synapse/federation
diff options
context:
space:
mode:
Diffstat (limited to 'synapse/federation')
-rw-r--r--synapse/federation/federation_client.py64
1 files changed, 55 insertions, 9 deletions
diff --git a/synapse/federation/federation_client.py b/synapse/federation/federation_client.py
index 0af953a5d6..29979414e3 100644
--- a/synapse/federation/federation_client.py
+++ b/synapse/federation/federation_client.py
@@ -1364,13 +1364,59 @@ class FederationClient(FederationBase):
 
             return room, children, inaccessible_children
 
-        # TODO Fallback to the old federation API and translate the results.
-        return await self._try_destination_list(
-            "fetch room hierarchy",
-            destinations,
-            send_request,
-            failover_on_unknown_endpoint=True,
-        )
+        try:
+            return await self._try_destination_list(
+                "fetch room hierarchy",
+                destinations,
+                send_request,
+                failover_on_unknown_endpoint=True,
+            )
+        except SynapseError as e:
+            # Fallback to the old federation API and translate the results if
+            # no servers implement the new API.
+            #
+            # The algorithm below is a bit inefficient as it only attempts to
+            # get information for the requested room, but the legacy API may
+            # return additional layers.
+            if e.code == 502:
+                legacy_result = await self.get_space_summary(
+                    destinations,
+                    room_id,
+                    suggested_only,
+                    max_rooms_per_space=None,
+                    exclude_rooms=[],
+                )
+
+                # Find the requested room in the response (and remove it).
+                for _i, room in enumerate(legacy_result.rooms):
+                    if room.get("room_id") == room_id:
+                        break
+                else:
+                    # The requested room was not returned, nothing we can do.
+                    raise
+                requested_room = legacy_result.rooms.pop(_i)
+
+                # Find any children events of the requested room.
+                children_events = []
+                children_room_ids = set()
+                for event in legacy_result.events:
+                    if event.room_id == room_id:
+                        children_events.append(event.data)
+                        children_room_ids.add(event.state_key)
+                # And add them under the requested room.
+                requested_room["children_state"] = children_events
+
+                # Find the children rooms.
+                children = []
+                for room in legacy_result.rooms:
+                    if room.get("room_id") in children_room_ids:
+                        children.append(room)
+
+                # It isn't clear from the response whether some of the rooms are
+                # not accessible.
+                return requested_room, children, ()
+
+            raise
 
 
 @attr.s(frozen=True, slots=True, auto_attribs=True)
@@ -1430,7 +1476,7 @@ class FederationSpaceSummaryEventResult:
 class FederationSpaceSummaryResult:
     """Represents the data returned by a successful get_space_summary call."""
 
-    rooms: Sequence[JsonDict]
+    rooms: List[JsonDict]
     events: Sequence[FederationSpaceSummaryEventResult]
 
     @classmethod
@@ -1444,7 +1490,7 @@ class FederationSpaceSummaryResult:
             ValueError if d is not a valid /spaces/ response
         """
         rooms = d.get("rooms")
-        if not isinstance(rooms, Sequence):
+        if not isinstance(rooms, List):
             raise ValueError("'rooms' must be a list")
         if any(not isinstance(r, dict) for r in rooms):
             raise ValueError("Invalid room in 'rooms' list")