Skip to content
This repository was archived by the owner on Apr 26, 2024. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
5 changes: 5 additions & 0 deletions synapse/federation/federation_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -1300,6 +1300,7 @@ class FederationSpaceSummaryEventResult:
"""

event_type: str
room_id: str
state_key: str
via: Sequence[str]

Expand All @@ -1321,6 +1322,10 @@ def from_json_dict(cls, d: JsonDict) -> "FederationSpaceSummaryEventResult":
if not isinstance(event_type, str):
raise ValueError("Invalid event: 'event_type' must be a str")

room_id = d.get("room_id")
if not isinstance(room_id, str):
raise ValueError("Invalid event: 'room_id' must be a str")

state_key = d.get("state_key")
if not isinstance(state_key, str):
raise ValueError("Invalid event: 'state_key' must be a str")
Expand Down
109 changes: 63 additions & 46 deletions synapse/handlers/space_summary.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
import logging
import re
from collections import deque
from typing import TYPE_CHECKING, Iterable, List, Optional, Sequence, Set, Tuple
from typing import TYPE_CHECKING, Dict, Iterable, List, Optional, Sequence, Set, Tuple

import attr

Expand Down Expand Up @@ -116,20 +116,22 @@ async def get_space_summary(
max_children = max_rooms_per_space if processed_rooms else None

if is_in_room:
room, events = await self._summarize_local_room(
room_entry = await self._summarize_local_room(
requester, None, room_id, suggested_only, max_children
)

events: List[JsonDict] = []
if room_entry:
rooms_result.append(room_entry.room)
events = room_entry.children

logger.debug(
"Query of local room %s returned events %s",
room_id,
["%s->%s" % (ev["room_id"], ev["state_key"]) for ev in events],
)

if room:
rooms_result.append(room)
else:
fed_rooms, fed_events = await self._summarize_remote_room(
fed_rooms = await self._summarize_remote_room(
queue_entry,
suggested_only,
max_children,
Expand All @@ -141,12 +143,10 @@ async def get_space_summary(
# user is not permitted see.
#
# Filter the returned results to only what is accessible to the user.
room_ids = set()
events = []
for room in fed_rooms:
fed_room_id = room.get("room_id")
if not fed_room_id or not isinstance(fed_room_id, str):
continue
Comment on lines -147 to -149
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These checks were moved to _summarize_remote_room (since the _RoomEntry has a room_id attribute we need to check it before creating the object).

for room_entry in fed_rooms:
room = room_entry.room
fed_room_id = room_entry.room_id

# The room should only be included in the summary if:
# a. the user is in the room;
Expand Down Expand Up @@ -189,21 +189,17 @@ async def get_space_summary(
# The user can see the room, include it!
if include_room:
rooms_result.append(room)
room_ids.add(fed_room_id)
events.extend(room_entry.children)

# All rooms returned don't need visiting again (even if the user
# didn't have access to them).
processed_rooms.add(fed_room_id)

for event in fed_events:
if event.get("room_id") in room_ids:
events.append(event)

logger.debug(
"Query of %s returned rooms %s, events %s",
room_id,
[room.get("room_id") for room in fed_rooms],
["%s->%s" % (ev["room_id"], ev["state_key"]) for ev in fed_events],
[room_entry.room.get("room_id") for room_entry in fed_rooms],
["%s->%s" % (ev["room_id"], ev["state_key"]) for ev in events],
)

# the room we queried may or may not have been returned, but don't process
Expand Down Expand Up @@ -283,20 +279,20 @@ async def federation_space_summary(
# already done this room
continue

logger.debug("Processing room %s", room_id)

room, events = await self._summarize_local_room(
room_entry = await self._summarize_local_room(
None, origin, room_id, suggested_only, max_rooms_per_space
)

processed_rooms.add(room_id)

if room:
rooms_result.append(room)
events_result.extend(events)
if room_entry:
rooms_result.append(room_entry.room)
events_result.extend(room_entry.children)

# add any children to the queue
room_queue.extend(edge_event["state_key"] for edge_event in events)
# add any children to the queue
room_queue.extend(
edge_event["state_key"] for edge_event in room_entry.children
)

return {"rooms": rooms_result, "events": events_result}

Expand All @@ -307,7 +303,7 @@ async def _summarize_local_room(
room_id: str,
suggested_only: bool,
max_children: Optional[int],
) -> Tuple[Optional[JsonDict], Sequence[JsonDict]]:
) -> Optional["_RoomEntry"]:
"""
Generate a room entry and a list of event entries for a given room.

Expand All @@ -326,21 +322,16 @@ async def _summarize_local_room(
to a server-set limit.

Returns:
A tuple of:
The room information, if the room should be returned to the
user. None, otherwise.

An iterable of the sorted children events. This may be limited
to a maximum size or may include all children.
A room entry if the room should be returned. None, otherwise.
"""
if not await self._is_room_accessible(room_id, requester, origin):
return None, ()
return None

room_entry = await self._build_room_entry(room_id)

# If the room is not a space, return just the room information.
if room_entry.get("room_type") != RoomTypes.SPACE:
return room_entry, ()
return _RoomEntry(room_id, room_entry)

# Otherwise, look for child rooms/spaces.
child_events = await self._get_child_events(room_id)
Expand All @@ -363,15 +354,15 @@ async def _summarize_local_room(
)
)

return room_entry, events_result
return _RoomEntry(room_id, room_entry, events_result)

async def _summarize_remote_room(
self,
room: "_RoomQueueEntry",
suggested_only: bool,
max_children: Optional[int],
exclude_rooms: Iterable[str],
) -> Tuple[Sequence[JsonDict], Sequence[JsonDict]]:
) -> Iterable["_RoomEntry"]:
"""
Request room entries and a list of event entries for a given room by querying a remote server.

Expand All @@ -386,11 +377,7 @@ async def _summarize_remote_room(
Rooms IDs which do not need to be summarized.

Returns:
A tuple of:
An iterable of rooms.

An iterable of the sorted children events. This may be limited
to a maximum size or may include all children.
An iterable of room entries.
"""
room_id = room.room_id
logger.info("Requesting summary for %s via %s", room_id, room.via)
Expand All @@ -414,11 +401,30 @@ async def _summarize_remote_room(
e,
exc_info=logger.isEnabledFor(logging.DEBUG),
)
return (), ()
return ()

# Group the events by their room.
children_by_room: Dict[str, List[JsonDict]] = {}
for ev in res.events:
if ev.event_type == EventTypes.SpaceChild:
children_by_room.setdefault(ev.room_id, []).append(ev.data)

# Generate the final results.
results = []
for fed_room in res.rooms:
fed_room_id = fed_room.get("room_id")
if not fed_room_id or not isinstance(fed_room_id, str):
continue

return res.rooms, tuple(
ev.data for ev in res.events if ev.event_type == EventTypes.SpaceChild
)
results.append(
_RoomEntry(
fed_room_id,
fed_room,
children_by_room.get(fed_room_id, []),
)
)

return results

async def _is_room_accessible(
self, room_id: str, requester: Optional[str], origin: Optional[str]
Expand Down Expand Up @@ -612,6 +618,17 @@ class _RoomQueueEntry:
via: Sequence[str]


@attr.s(frozen=True, slots=True, auto_attribs=True)
class _RoomEntry:
room_id: str
# The room summary for this room.
room: JsonDict
# An iterable of the sorted, stripped children events for children of this room.
#
# This may not include all children.
children: List[JsonDict] = attr.ib(factory=list)


def _has_valid_via(e: EventBase) -> bool:
via = e.content.get("via")
if not via or not isinstance(via, Sequence):
Expand Down
Loading