mirror of
https://github.com/element-hq/synapse
synced 2024-09-03 08:30:22 +00:00
Add room_types
/not_room_types
filtering to Sliding Sync /sync
(#17337)
Based on [MSC3575](https://github.com/matrix-org/matrix-spec-proposals/pull/3575): Sliding Sync
This commit is contained in:
parent
fa91655805
commit
7be03d854b
5 changed files with 248 additions and 7 deletions
1
changelog.d/17337.feature
Normal file
1
changelog.d/17337.feature
Normal file
|
@ -0,0 +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.
|
|
@ -23,7 +23,13 @@ from typing import TYPE_CHECKING, Any, Dict, List, Optional, Set, Tuple
|
||||||
import attr
|
import attr
|
||||||
from immutabledict import immutabledict
|
from immutabledict import immutabledict
|
||||||
|
|
||||||
from synapse.api.constants import AccountDataTypes, Direction, EventTypes, Membership
|
from synapse.api.constants import (
|
||||||
|
AccountDataTypes,
|
||||||
|
Direction,
|
||||||
|
EventContentFields,
|
||||||
|
EventTypes,
|
||||||
|
Membership,
|
||||||
|
)
|
||||||
from synapse.events import EventBase
|
from synapse.events import EventBase
|
||||||
from synapse.events.utils import strip_event
|
from synapse.events.utils import strip_event
|
||||||
from synapse.handlers.relations import BundledAggregations
|
from synapse.handlers.relations import BundledAggregations
|
||||||
|
@ -695,6 +701,10 @@ class SlidingSyncHandler:
|
||||||
state_filter=StateFilter.from_types(
|
state_filter=StateFilter.from_types(
|
||||||
[(EventTypes.RoomEncryption, "")]
|
[(EventTypes.RoomEncryption, "")]
|
||||||
),
|
),
|
||||||
|
# Partially stated rooms should have all state events except for the
|
||||||
|
# membership events so we don't need to wait. Plus we don't want to
|
||||||
|
# block the whole sync waiting for this one room.
|
||||||
|
await_full_state=False,
|
||||||
)
|
)
|
||||||
is_encrypted = state_at_to_token.get((EventTypes.RoomEncryption, ""))
|
is_encrypted = state_at_to_token.get((EventTypes.RoomEncryption, ""))
|
||||||
|
|
||||||
|
@ -721,11 +731,26 @@ class SlidingSyncHandler:
|
||||||
):
|
):
|
||||||
filtered_room_id_set.remove(room_id)
|
filtered_room_id_set.remove(room_id)
|
||||||
|
|
||||||
if filters.room_types:
|
# Filter by room type (space vs room, etc). A room must match one of the types
|
||||||
raise NotImplementedError()
|
# provided in the list. `None` is a valid type for rooms which do not have a
|
||||||
|
# room type.
|
||||||
|
if filters.room_types is not None or filters.not_room_types is not None:
|
||||||
|
# Make a copy so we don't run into an error: `Set changed size during
|
||||||
|
# iteration`, when we filter out and remove items
|
||||||
|
for room_id in list(filtered_room_id_set):
|
||||||
|
create_event = await self.store.get_create_event_for_room(room_id)
|
||||||
|
room_type = create_event.content.get(EventContentFields.ROOM_TYPE)
|
||||||
|
if (
|
||||||
|
filters.room_types is not None
|
||||||
|
and room_type not in filters.room_types
|
||||||
|
):
|
||||||
|
filtered_room_id_set.remove(room_id)
|
||||||
|
|
||||||
if filters.not_room_types:
|
if (
|
||||||
raise NotImplementedError()
|
filters.not_room_types is not None
|
||||||
|
and room_type in filters.not_room_types
|
||||||
|
):
|
||||||
|
filtered_room_id_set.remove(room_id)
|
||||||
|
|
||||||
if filters.room_name_like:
|
if filters.room_name_like:
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
|
@ -436,6 +436,9 @@ class StateStorageController:
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# FIXME: This will return incorrect results when there are timeline gaps. For
|
||||||
|
# example, when you try to get a point in the room we haven't backfilled before.
|
||||||
|
|
||||||
if last_event_id:
|
if last_event_id:
|
||||||
state = await self.get_state_after_event(
|
state = await self.get_state_after_event(
|
||||||
last_event_id,
|
last_event_id,
|
||||||
|
|
|
@ -259,7 +259,7 @@ class SlidingSyncBody(RequestBodyModel):
|
||||||
is_encrypted: Optional[StrictBool] = None
|
is_encrypted: Optional[StrictBool] = None
|
||||||
is_invite: Optional[StrictBool] = None
|
is_invite: Optional[StrictBool] = None
|
||||||
room_types: Optional[List[Union[StrictStr, None]]] = None
|
room_types: Optional[List[Union[StrictStr, None]]] = None
|
||||||
not_room_types: Optional[List[StrictStr]] = None
|
not_room_types: Optional[List[Union[StrictStr, None]]] = None
|
||||||
room_name_like: Optional[StrictStr] = None
|
room_name_like: Optional[StrictStr] = None
|
||||||
tags: Optional[List[StrictStr]] = None
|
tags: Optional[List[StrictStr]] = None
|
||||||
not_tags: Optional[List[StrictStr]] = None
|
not_tags: Optional[List[StrictStr]] = None
|
||||||
|
|
|
@ -24,7 +24,14 @@ from parameterized import parameterized
|
||||||
|
|
||||||
from twisted.test.proto_helpers import MemoryReactor
|
from twisted.test.proto_helpers import MemoryReactor
|
||||||
|
|
||||||
from synapse.api.constants import AccountDataTypes, EventTypes, JoinRules, Membership
|
from synapse.api.constants import (
|
||||||
|
AccountDataTypes,
|
||||||
|
EventContentFields,
|
||||||
|
EventTypes,
|
||||||
|
JoinRules,
|
||||||
|
Membership,
|
||||||
|
RoomTypes,
|
||||||
|
)
|
||||||
from synapse.api.room_versions import RoomVersions
|
from synapse.api.room_versions import RoomVersions
|
||||||
from synapse.handlers.sliding_sync import SlidingSyncConfig
|
from synapse.handlers.sliding_sync import SlidingSyncConfig
|
||||||
from synapse.rest import admin
|
from synapse.rest import admin
|
||||||
|
@ -2047,6 +2054,211 @@ class FilterRoomsTestCase(HomeserverTestCase):
|
||||||
|
|
||||||
self.assertEqual(falsy_filtered_room_map.keys(), {room_id})
|
self.assertEqual(falsy_filtered_room_map.keys(), {room_id})
|
||||||
|
|
||||||
|
def test_filter_room_types(self) -> None:
|
||||||
|
"""
|
||||||
|
Test `filter.room_types` for different room types
|
||||||
|
"""
|
||||||
|
user1_id = self.register_user("user1", "pass")
|
||||||
|
user1_tok = self.login(user1_id, "pass")
|
||||||
|
|
||||||
|
# Create a normal room (no room type)
|
||||||
|
room_id = self.helper.create_room_as(user1_id, tok=user1_tok)
|
||||||
|
|
||||||
|
# Create a space room
|
||||||
|
space_room_id = self.helper.create_room_as(
|
||||||
|
user1_id,
|
||||||
|
tok=user1_tok,
|
||||||
|
extra_content={
|
||||||
|
"creation_content": {EventContentFields.ROOM_TYPE: RoomTypes.SPACE}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
# Create an arbitrarily typed room
|
||||||
|
foo_room_id = self.helper.create_room_as(
|
||||||
|
user1_id,
|
||||||
|
tok=user1_tok,
|
||||||
|
extra_content={
|
||||||
|
"creation_content": {
|
||||||
|
EventContentFields.ROOM_TYPE: "org.matrix.foobarbaz"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
after_rooms_token = self.event_sources.get_current_token()
|
||||||
|
|
||||||
|
# Get the rooms the user should be syncing with
|
||||||
|
sync_room_map = self.get_success(
|
||||||
|
self.sliding_sync_handler.get_sync_room_ids_for_user(
|
||||||
|
UserID.from_string(user1_id),
|
||||||
|
from_token=None,
|
||||||
|
to_token=after_rooms_token,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
# Try finding only normal rooms
|
||||||
|
filtered_room_map = self.get_success(
|
||||||
|
self.sliding_sync_handler.filter_rooms(
|
||||||
|
UserID.from_string(user1_id),
|
||||||
|
sync_room_map,
|
||||||
|
SlidingSyncConfig.SlidingSyncList.Filters(room_types=[None]),
|
||||||
|
after_rooms_token,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertEqual(filtered_room_map.keys(), {room_id})
|
||||||
|
|
||||||
|
# Try finding only spaces
|
||||||
|
filtered_room_map = self.get_success(
|
||||||
|
self.sliding_sync_handler.filter_rooms(
|
||||||
|
UserID.from_string(user1_id),
|
||||||
|
sync_room_map,
|
||||||
|
SlidingSyncConfig.SlidingSyncList.Filters(room_types=[RoomTypes.SPACE]),
|
||||||
|
after_rooms_token,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertEqual(filtered_room_map.keys(), {space_room_id})
|
||||||
|
|
||||||
|
# Try finding normal rooms and spaces
|
||||||
|
filtered_room_map = self.get_success(
|
||||||
|
self.sliding_sync_handler.filter_rooms(
|
||||||
|
UserID.from_string(user1_id),
|
||||||
|
sync_room_map,
|
||||||
|
SlidingSyncConfig.SlidingSyncList.Filters(
|
||||||
|
room_types=[None, RoomTypes.SPACE]
|
||||||
|
),
|
||||||
|
after_rooms_token,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertEqual(filtered_room_map.keys(), {room_id, space_room_id})
|
||||||
|
|
||||||
|
# Try finding an arbitrary room type
|
||||||
|
filtered_room_map = self.get_success(
|
||||||
|
self.sliding_sync_handler.filter_rooms(
|
||||||
|
UserID.from_string(user1_id),
|
||||||
|
sync_room_map,
|
||||||
|
SlidingSyncConfig.SlidingSyncList.Filters(
|
||||||
|
room_types=["org.matrix.foobarbaz"]
|
||||||
|
),
|
||||||
|
after_rooms_token,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertEqual(filtered_room_map.keys(), {foo_room_id})
|
||||||
|
|
||||||
|
def test_filter_not_room_types(self) -> None:
|
||||||
|
"""
|
||||||
|
Test `filter.not_room_types` for different room types
|
||||||
|
"""
|
||||||
|
user1_id = self.register_user("user1", "pass")
|
||||||
|
user1_tok = self.login(user1_id, "pass")
|
||||||
|
|
||||||
|
# Create a normal room (no room type)
|
||||||
|
room_id = self.helper.create_room_as(user1_id, tok=user1_tok)
|
||||||
|
|
||||||
|
# Create a space room
|
||||||
|
space_room_id = self.helper.create_room_as(
|
||||||
|
user1_id,
|
||||||
|
tok=user1_tok,
|
||||||
|
extra_content={
|
||||||
|
"creation_content": {EventContentFields.ROOM_TYPE: RoomTypes.SPACE}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
# Create an arbitrarily typed room
|
||||||
|
foo_room_id = self.helper.create_room_as(
|
||||||
|
user1_id,
|
||||||
|
tok=user1_tok,
|
||||||
|
extra_content={
|
||||||
|
"creation_content": {
|
||||||
|
EventContentFields.ROOM_TYPE: "org.matrix.foobarbaz"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
after_rooms_token = self.event_sources.get_current_token()
|
||||||
|
|
||||||
|
# Get the rooms the user should be syncing with
|
||||||
|
sync_room_map = self.get_success(
|
||||||
|
self.sliding_sync_handler.get_sync_room_ids_for_user(
|
||||||
|
UserID.from_string(user1_id),
|
||||||
|
from_token=None,
|
||||||
|
to_token=after_rooms_token,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
# Try finding *NOT* normal rooms
|
||||||
|
filtered_room_map = self.get_success(
|
||||||
|
self.sliding_sync_handler.filter_rooms(
|
||||||
|
UserID.from_string(user1_id),
|
||||||
|
sync_room_map,
|
||||||
|
SlidingSyncConfig.SlidingSyncList.Filters(not_room_types=[None]),
|
||||||
|
after_rooms_token,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertEqual(filtered_room_map.keys(), {space_room_id, foo_room_id})
|
||||||
|
|
||||||
|
# Try finding *NOT* spaces
|
||||||
|
filtered_room_map = self.get_success(
|
||||||
|
self.sliding_sync_handler.filter_rooms(
|
||||||
|
UserID.from_string(user1_id),
|
||||||
|
sync_room_map,
|
||||||
|
SlidingSyncConfig.SlidingSyncList.Filters(
|
||||||
|
not_room_types=[RoomTypes.SPACE]
|
||||||
|
),
|
||||||
|
after_rooms_token,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertEqual(filtered_room_map.keys(), {room_id, foo_room_id})
|
||||||
|
|
||||||
|
# Try finding *NOT* normal rooms or spaces
|
||||||
|
filtered_room_map = self.get_success(
|
||||||
|
self.sliding_sync_handler.filter_rooms(
|
||||||
|
UserID.from_string(user1_id),
|
||||||
|
sync_room_map,
|
||||||
|
SlidingSyncConfig.SlidingSyncList.Filters(
|
||||||
|
not_room_types=[None, RoomTypes.SPACE]
|
||||||
|
),
|
||||||
|
after_rooms_token,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertEqual(filtered_room_map.keys(), {foo_room_id})
|
||||||
|
|
||||||
|
# Test how it behaves when we have both `room_types` and `not_room_types`.
|
||||||
|
# `not_room_types` should win.
|
||||||
|
filtered_room_map = self.get_success(
|
||||||
|
self.sliding_sync_handler.filter_rooms(
|
||||||
|
UserID.from_string(user1_id),
|
||||||
|
sync_room_map,
|
||||||
|
SlidingSyncConfig.SlidingSyncList.Filters(
|
||||||
|
room_types=[None], not_room_types=[None]
|
||||||
|
),
|
||||||
|
after_rooms_token,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
# Nothing matches because nothing is both a normal room and not a normal room
|
||||||
|
self.assertEqual(filtered_room_map.keys(), set())
|
||||||
|
|
||||||
|
# Test how it behaves when we have both `room_types` and `not_room_types`.
|
||||||
|
# `not_room_types` should win.
|
||||||
|
filtered_room_map = self.get_success(
|
||||||
|
self.sliding_sync_handler.filter_rooms(
|
||||||
|
UserID.from_string(user1_id),
|
||||||
|
sync_room_map,
|
||||||
|
SlidingSyncConfig.SlidingSyncList.Filters(
|
||||||
|
room_types=[None, RoomTypes.SPACE], not_room_types=[None]
|
||||||
|
),
|
||||||
|
after_rooms_token,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertEqual(filtered_room_map.keys(), {space_room_id})
|
||||||
|
|
||||||
|
|
||||||
class SortRoomsTestCase(HomeserverTestCase):
|
class SortRoomsTestCase(HomeserverTestCase):
|
||||||
"""
|
"""
|
||||||
|
|
Loading…
Reference in a new issue