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

Prep work for the updated spaces summary #10527

Merged
merged 3 commits into from
Aug 5, 2021
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/10527.misc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Prepare for the new spaces summary endpoint (updates to [MSC2946](https://github.com/matrix-org/matrix-doc/pull/2946)).
23 changes: 14 additions & 9 deletions synapse/federation/federation_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -1290,7 +1290,7 @@ async def send_request(destination: str) -> FederationSpaceSummaryResult:
)


@attr.s(frozen=True, slots=True)
@attr.s(frozen=True, slots=True, auto_attribs=True)
class FederationSpaceSummaryEventResult:
"""Represents a single event in the result of a successful get_space_summary call.

Expand All @@ -1299,12 +1299,13 @@ class FederationSpaceSummaryEventResult:
object attributes.
"""

event_type = attr.ib(type=str)
state_key = attr.ib(type=str)
via = attr.ib(type=Sequence[str])
event_type: str
room_id: str
state_key: str
via: Sequence[str]

# the raw data, including the above keys
data = attr.ib(type=JsonDict)
data: JsonDict

@classmethod
def from_json_dict(cls, d: JsonDict) -> "FederationSpaceSummaryEventResult":
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 All @@ -1335,15 +1340,15 @@ def from_json_dict(cls, d: JsonDict) -> "FederationSpaceSummaryEventResult":
if any(not isinstance(v, str) for v in via):
raise ValueError("Invalid event: 'via' must be a list of strings")

return cls(event_type, state_key, via, d)
return cls(event_type, room_id, state_key, via, d)
Copy link
Member

Choose a reason for hiding this comment

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

this has ended up in the wrong commit, but nm

Copy link
Member Author

Choose a reason for hiding this comment

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

Good shout, sorry about that. I had much rebasing in these branches... 😢



@attr.s(frozen=True, slots=True)
@attr.s(frozen=True, slots=True, auto_attribs=True)
class FederationSpaceSummaryResult:
"""Represents the data returned by a successful get_space_summary call."""

rooms = attr.ib(type=Sequence[JsonDict])
events = attr.ib(type=Sequence[FederationSpaceSummaryEventResult])
rooms: Sequence[JsonDict]
events: Sequence[FederationSpaceSummaryEventResult]

@classmethod
def from_json_dict(cls, d: JsonDict) -> "FederationSpaceSummaryResult":
Expand Down
125 changes: 76 additions & 49 deletions synapse/handlers/space_summary.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,17 @@
import logging
import re
from collections import deque
from typing import TYPE_CHECKING, Iterable, List, Optional, Sequence, Set, Tuple
from typing import (
TYPE_CHECKING,
Collection,
Dict,
Iterable,
List,
Optional,
Sequence,
Set,
Tuple,
)

import attr

Expand Down Expand Up @@ -116,20 +126,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: Collection[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 +153,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 +199,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 +289,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 +313,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 +332,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 +364,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 +387,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 +411,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 @@ -606,10 +622,21 @@ async def _get_child_events(self, room_id: str) -> Iterable[EventBase]:
return sorted(filter(_has_valid_via, events), key=_child_events_comparison_key)


@attr.s(frozen=True, slots=True)
@attr.s(frozen=True, slots=True, auto_attribs=True)
class _RoomQueueEntry:
room_id = attr.ib(type=str)
via = attr.ib(type=Sequence[str])
room_id: str
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: Collection[JsonDict] = ()


def _has_valid_via(e: EventBase) -> bool:
Expand Down
Loading