Skip to content

Commit 7be03d8

Browse files
Add room_types/not_room_types filtering to Sliding Sync /sync (#17337)
Based on [MSC3575](matrix-org/matrix-spec-proposals#3575): Sliding Sync
1 parent fa91655 commit 7be03d8

File tree

5 files changed

+248
-7
lines changed

5 files changed

+248
-7
lines changed

changelog.d/17337.feature

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Add `room_types`/`not_room_types` filtering to experimental [MSC3575](https://github.com/matrix-org/matrix-spec-proposals/pull/3575) Sliding Sync `/sync` endpoint.

synapse/handlers/sliding_sync.py

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,13 @@
2323
import attr
2424
from immutabledict import immutabledict
2525

26-
from synapse.api.constants import AccountDataTypes, Direction, EventTypes, Membership
26+
from synapse.api.constants import (
27+
AccountDataTypes,
28+
Direction,
29+
EventContentFields,
30+
EventTypes,
31+
Membership,
32+
)
2733
from synapse.events import EventBase
2834
from synapse.events.utils import strip_event
2935
from synapse.handlers.relations import BundledAggregations
@@ -695,6 +701,10 @@ async def filter_rooms(
695701
state_filter=StateFilter.from_types(
696702
[(EventTypes.RoomEncryption, "")]
697703
),
704+
# Partially stated rooms should have all state events except for the
705+
# membership events so we don't need to wait. Plus we don't want to
706+
# block the whole sync waiting for this one room.
707+
await_full_state=False,
698708
)
699709
is_encrypted = state_at_to_token.get((EventTypes.RoomEncryption, ""))
700710

@@ -721,11 +731,26 @@ async def filter_rooms(
721731
):
722732
filtered_room_id_set.remove(room_id)
723733

724-
if filters.room_types:
725-
raise NotImplementedError()
734+
# Filter by room type (space vs room, etc). A room must match one of the types
735+
# provided in the list. `None` is a valid type for rooms which do not have a
736+
# room type.
737+
if filters.room_types is not None or filters.not_room_types is not None:
738+
# Make a copy so we don't run into an error: `Set changed size during
739+
# iteration`, when we filter out and remove items
740+
for room_id in list(filtered_room_id_set):
741+
create_event = await self.store.get_create_event_for_room(room_id)
742+
room_type = create_event.content.get(EventContentFields.ROOM_TYPE)
743+
if (
744+
filters.room_types is not None
745+
and room_type not in filters.room_types
746+
):
747+
filtered_room_id_set.remove(room_id)
726748

727-
if filters.not_room_types:
728-
raise NotImplementedError()
749+
if (
750+
filters.not_room_types is not None
751+
and room_type in filters.not_room_types
752+
):
753+
filtered_room_id_set.remove(room_id)
729754

730755
if filters.room_name_like:
731756
raise NotImplementedError()

synapse/storage/controllers/state.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -436,6 +436,9 @@ async def get_state_at(
436436
)
437437
)
438438

439+
# FIXME: This will return incorrect results when there are timeline gaps. For
440+
# example, when you try to get a point in the room we haven't backfilled before.
441+
439442
if last_event_id:
440443
state = await self.get_state_after_event(
441444
last_event_id,

synapse/types/rest/client/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -259,7 +259,7 @@ class Filters(RequestBodyModel):
259259
is_encrypted: Optional[StrictBool] = None
260260
is_invite: Optional[StrictBool] = None
261261
room_types: Optional[List[Union[StrictStr, None]]] = None
262-
not_room_types: Optional[List[StrictStr]] = None
262+
not_room_types: Optional[List[Union[StrictStr, None]]] = None
263263
room_name_like: Optional[StrictStr] = None
264264
tags: Optional[List[StrictStr]] = None
265265
not_tags: Optional[List[StrictStr]] = None

tests/handlers/test_sliding_sync.py

Lines changed: 213 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,14 @@
2424

2525
from twisted.test.proto_helpers import MemoryReactor
2626

27-
from synapse.api.constants import AccountDataTypes, EventTypes, JoinRules, Membership
27+
from synapse.api.constants import (
28+
AccountDataTypes,
29+
EventContentFields,
30+
EventTypes,
31+
JoinRules,
32+
Membership,
33+
RoomTypes,
34+
)
2835
from synapse.api.room_versions import RoomVersions
2936
from synapse.handlers.sliding_sync import SlidingSyncConfig
3037
from synapse.rest import admin
@@ -2047,6 +2054,211 @@ def test_filter_invite_rooms(self) -> None:
20472054

20482055
self.assertEqual(falsy_filtered_room_map.keys(), {room_id})
20492056

2057+
def test_filter_room_types(self) -> None:
2058+
"""
2059+
Test `filter.room_types` for different room types
2060+
"""
2061+
user1_id = self.register_user("user1", "pass")
2062+
user1_tok = self.login(user1_id, "pass")
2063+
2064+
# Create a normal room (no room type)
2065+
room_id = self.helper.create_room_as(user1_id, tok=user1_tok)
2066+
2067+
# Create a space room
2068+
space_room_id = self.helper.create_room_as(
2069+
user1_id,
2070+
tok=user1_tok,
2071+
extra_content={
2072+
"creation_content": {EventContentFields.ROOM_TYPE: RoomTypes.SPACE}
2073+
},
2074+
)
2075+
2076+
# Create an arbitrarily typed room
2077+
foo_room_id = self.helper.create_room_as(
2078+
user1_id,
2079+
tok=user1_tok,
2080+
extra_content={
2081+
"creation_content": {
2082+
EventContentFields.ROOM_TYPE: "org.matrix.foobarbaz"
2083+
}
2084+
},
2085+
)
2086+
2087+
after_rooms_token = self.event_sources.get_current_token()
2088+
2089+
# Get the rooms the user should be syncing with
2090+
sync_room_map = self.get_success(
2091+
self.sliding_sync_handler.get_sync_room_ids_for_user(
2092+
UserID.from_string(user1_id),
2093+
from_token=None,
2094+
to_token=after_rooms_token,
2095+
)
2096+
)
2097+
2098+
# Try finding only normal rooms
2099+
filtered_room_map = self.get_success(
2100+
self.sliding_sync_handler.filter_rooms(
2101+
UserID.from_string(user1_id),
2102+
sync_room_map,
2103+
SlidingSyncConfig.SlidingSyncList.Filters(room_types=[None]),
2104+
after_rooms_token,
2105+
)
2106+
)
2107+
2108+
self.assertEqual(filtered_room_map.keys(), {room_id})
2109+
2110+
# Try finding only spaces
2111+
filtered_room_map = self.get_success(
2112+
self.sliding_sync_handler.filter_rooms(
2113+
UserID.from_string(user1_id),
2114+
sync_room_map,
2115+
SlidingSyncConfig.SlidingSyncList.Filters(room_types=[RoomTypes.SPACE]),
2116+
after_rooms_token,
2117+
)
2118+
)
2119+
2120+
self.assertEqual(filtered_room_map.keys(), {space_room_id})
2121+
2122+
# Try finding normal rooms and spaces
2123+
filtered_room_map = self.get_success(
2124+
self.sliding_sync_handler.filter_rooms(
2125+
UserID.from_string(user1_id),
2126+
sync_room_map,
2127+
SlidingSyncConfig.SlidingSyncList.Filters(
2128+
room_types=[None, RoomTypes.SPACE]
2129+
),
2130+
after_rooms_token,
2131+
)
2132+
)
2133+
2134+
self.assertEqual(filtered_room_map.keys(), {room_id, space_room_id})
2135+
2136+
# Try finding an arbitrary room type
2137+
filtered_room_map = self.get_success(
2138+
self.sliding_sync_handler.filter_rooms(
2139+
UserID.from_string(user1_id),
2140+
sync_room_map,
2141+
SlidingSyncConfig.SlidingSyncList.Filters(
2142+
room_types=["org.matrix.foobarbaz"]
2143+
),
2144+
after_rooms_token,
2145+
)
2146+
)
2147+
2148+
self.assertEqual(filtered_room_map.keys(), {foo_room_id})
2149+
2150+
def test_filter_not_room_types(self) -> None:
2151+
"""
2152+
Test `filter.not_room_types` for different room types
2153+
"""
2154+
user1_id = self.register_user("user1", "pass")
2155+
user1_tok = self.login(user1_id, "pass")
2156+
2157+
# Create a normal room (no room type)
2158+
room_id = self.helper.create_room_as(user1_id, tok=user1_tok)
2159+
2160+
# Create a space room
2161+
space_room_id = self.helper.create_room_as(
2162+
user1_id,
2163+
tok=user1_tok,
2164+
extra_content={
2165+
"creation_content": {EventContentFields.ROOM_TYPE: RoomTypes.SPACE}
2166+
},
2167+
)
2168+
2169+
# Create an arbitrarily typed room
2170+
foo_room_id = self.helper.create_room_as(
2171+
user1_id,
2172+
tok=user1_tok,
2173+
extra_content={
2174+
"creation_content": {
2175+
EventContentFields.ROOM_TYPE: "org.matrix.foobarbaz"
2176+
}
2177+
},
2178+
)
2179+
2180+
after_rooms_token = self.event_sources.get_current_token()
2181+
2182+
# Get the rooms the user should be syncing with
2183+
sync_room_map = self.get_success(
2184+
self.sliding_sync_handler.get_sync_room_ids_for_user(
2185+
UserID.from_string(user1_id),
2186+
from_token=None,
2187+
to_token=after_rooms_token,
2188+
)
2189+
)
2190+
2191+
# Try finding *NOT* normal rooms
2192+
filtered_room_map = self.get_success(
2193+
self.sliding_sync_handler.filter_rooms(
2194+
UserID.from_string(user1_id),
2195+
sync_room_map,
2196+
SlidingSyncConfig.SlidingSyncList.Filters(not_room_types=[None]),
2197+
after_rooms_token,
2198+
)
2199+
)
2200+
2201+
self.assertEqual(filtered_room_map.keys(), {space_room_id, foo_room_id})
2202+
2203+
# Try finding *NOT* spaces
2204+
filtered_room_map = self.get_success(
2205+
self.sliding_sync_handler.filter_rooms(
2206+
UserID.from_string(user1_id),
2207+
sync_room_map,
2208+
SlidingSyncConfig.SlidingSyncList.Filters(
2209+
not_room_types=[RoomTypes.SPACE]
2210+
),
2211+
after_rooms_token,
2212+
)
2213+
)
2214+
2215+
self.assertEqual(filtered_room_map.keys(), {room_id, foo_room_id})
2216+
2217+
# Try finding *NOT* normal rooms or spaces
2218+
filtered_room_map = self.get_success(
2219+
self.sliding_sync_handler.filter_rooms(
2220+
UserID.from_string(user1_id),
2221+
sync_room_map,
2222+
SlidingSyncConfig.SlidingSyncList.Filters(
2223+
not_room_types=[None, RoomTypes.SPACE]
2224+
),
2225+
after_rooms_token,
2226+
)
2227+
)
2228+
2229+
self.assertEqual(filtered_room_map.keys(), {foo_room_id})
2230+
2231+
# Test how it behaves when we have both `room_types` and `not_room_types`.
2232+
# `not_room_types` should win.
2233+
filtered_room_map = self.get_success(
2234+
self.sliding_sync_handler.filter_rooms(
2235+
UserID.from_string(user1_id),
2236+
sync_room_map,
2237+
SlidingSyncConfig.SlidingSyncList.Filters(
2238+
room_types=[None], not_room_types=[None]
2239+
),
2240+
after_rooms_token,
2241+
)
2242+
)
2243+
2244+
# Nothing matches because nothing is both a normal room and not a normal room
2245+
self.assertEqual(filtered_room_map.keys(), set())
2246+
2247+
# Test how it behaves when we have both `room_types` and `not_room_types`.
2248+
# `not_room_types` should win.
2249+
filtered_room_map = self.get_success(
2250+
self.sliding_sync_handler.filter_rooms(
2251+
UserID.from_string(user1_id),
2252+
sync_room_map,
2253+
SlidingSyncConfig.SlidingSyncList.Filters(
2254+
room_types=[None, RoomTypes.SPACE], not_room_types=[None]
2255+
),
2256+
after_rooms_token,
2257+
)
2258+
)
2259+
2260+
self.assertEqual(filtered_room_map.keys(), {space_room_id})
2261+
20502262

20512263
class SortRoomsTestCase(HomeserverTestCase):
20522264
"""

0 commit comments

Comments
 (0)