diff --git a/changelog.d/19899.feature b/changelog.d/19899.feature new file mode 100644 index 00000000000..39f522217da --- /dev/null +++ b/changelog.d/19899.feature @@ -0,0 +1,2 @@ +Implemement MSC4497: Filter state events by type in /state endpoint. Requests to `/_matrix/client/v3/rooms/!../state` can now be +made with a `cc.koja.types` key to filter to only specific state types. diff --git a/synapse/config/experimental.py b/synapse/config/experimental.py index 2d74e9b8866..d4554e9611e 100644 --- a/synapse/config/experimental.py +++ b/synapse/config/experimental.py @@ -622,6 +622,10 @@ def read_config( # MSC4455: Preview URL capability # Tracked in: https://github.com/element-hq/synapse/issues/19719 self.msc4452_enabled: bool = experimental.get("msc4452_enabled", False) - # MSC4491: Invite reasons in room creation self.msc4491_enabled: bool = experimental.get("msc4491_enabled", False) + + # MSC4497: State event type filter + self.msc4497_state_event_type_filter: bool = experimental.get( + "msc4497_state_event_type_filter", False + ) diff --git a/synapse/rest/client/room.py b/synapse/rest/client/room.py index 6e00197b6de..9d0782e0e79 100644 --- a/synapse/rest/client/room.py +++ b/synapse/rest/client/room.py @@ -25,7 +25,7 @@ import re from enum import Enum from http import HTTPStatus -from typing import TYPE_CHECKING, Awaitable +from typing import TYPE_CHECKING, Awaitable, Mapping, Sequence from urllib import parse as urlparse import attr @@ -990,15 +990,25 @@ def __init__(self, hs: "HomeServer"): super().__init__() self.message_handler = hs.get_message_handler() self.auth = hs.get_auth() + self.experimental_config = hs.config.experimental @cancellable async def on_GET( self, request: SynapseRequest, room_id: str ) -> tuple[int, list[JsonDict]]: requester = await self.auth.get_user_by_req(request, allow_guest=True) + args: Mapping[bytes, Sequence[bytes]] = request.args # type: ignore + type_filter = ( + parse_strings_from_args(args, "cc.koja.types", required=False) + if self.experimental_config.msc4497_state_event_type_filter + else None + ) # Get all the current state for this room events = await self.message_handler.get_state_events( room_id=room_id, + state_filter=StateFilter.from_types((x, None) for x in type_filter) + if type_filter is not None + else None, requester=requester, ) return 200, events diff --git a/tests/rest/client/test_rooms.py b/tests/rest/client/test_rooms.py index 8d4892ae91f..c295d85e3b8 100644 --- a/tests/rest/client/test_rooms.py +++ b/tests/rest/client/test_rooms.py @@ -601,6 +601,35 @@ def test_get_state_format_event(self) -> None: self.assertEqual(channel.json_body["state_key"], self.user_id) self.assertTrue(type(channel.json_body["origin_server_ts"]) is int) + def test_get_state_type_filter_single(self) -> None: + """MSC4497: filtering by a single type returns only that event type.""" + self.hs.config.experimental.msc4497_state_event_type_filter = True + room_id = self.helper.create_room_as(self.user_id) + channel = self.make_request( + "GET", + "/rooms/%s/state?cc.koja.types=m.room.create" % room_id, + ) + self.assertEqual(channel.code, HTTPStatus.OK, channel.result["body"]) + self.assertEqual( + [event["type"] for event in channel.json_list], + ["m.room.create"], + ) + + def test_get_state_type_filter_multiple(self) -> None: + """MSC4497: filtering by multiple types returns only those event types.""" + self.hs.config.experimental.msc4497_state_event_type_filter = True + room_id = self.helper.create_room_as(self.user_id) + channel = self.make_request( + "GET", + "/rooms/%s/state?cc.koja.types=m.room.create&cc.koja.types=m.room.member" + % room_id, + ) + self.assertEqual(channel.code, HTTPStatus.OK, channel.result["body"]) + self.assertCountEqual( + [event["type"] for event in channel.json_list], + ["m.room.create", "m.room.member"], + ) + class RoomsMemberListTestCase(RoomBase): """Tests /rooms/$room_id/members/list REST events."""