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

Commit 23e48a1

Browse files
committed
Add a module API method to retrieve state from a room (#11204)
1 parent c9c1bed commit 23e48a1

File tree

3 files changed

+81
-2
lines changed

3 files changed

+81
-2
lines changed

changelog.d/11204.feature

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Add a module API method to retrieve the current state of a room.

synapse/module_api/__init__.py

+56-1
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,14 @@
4545
from synapse.storage.database import DatabasePool, LoggingTransaction
4646
from synapse.storage.databases.main.roommember import ProfileInfo
4747
from synapse.storage.state import StateFilter
48-
from synapse.types import JsonDict, Requester, UserID, UserInfo, create_requester
48+
from synapse.types import (
49+
JsonDict,
50+
Requester,
51+
StateMap,
52+
UserID,
53+
UserInfo,
54+
create_requester,
55+
)
4956
from synapse.util import Clock
5057
from synapse.util.caches.descriptors import cached
5158

@@ -70,6 +77,8 @@
7077
"DirectServeHtmlResource",
7178
"DirectServeJsonResource",
7279
"ModuleApi",
80+
"EventBase",
81+
"StateMap",
7382
]
7483

7584
logger = logging.getLogger(__name__)
@@ -690,6 +699,52 @@ def read_templates(
690699
(td for td in (self.custom_template_dir, custom_template_directory) if td),
691700
)
692701

702+
async def get_room_state(
703+
self,
704+
room_id: str,
705+
event_filter: Optional[Iterable[Tuple[str, Optional[str]]]] = None,
706+
) -> StateMap[EventBase]:
707+
"""Returns the current state of the given room.
708+
709+
The events are returned as a mapping, in which the key for each event is a tuple
710+
which first element is the event's type and the second one is its state key.
711+
712+
Added in Synapse v1.47.0
713+
714+
Args:
715+
room_id: The ID of the room to get state from.
716+
event_filter: A filter to apply when retrieving events. None if no filter
717+
should be applied. If provided, must be an iterable of tuples. A tuple's
718+
first element is the event type and the second is the state key, or is
719+
None if the state key should not be filtered on.
720+
An example of a filter is:
721+
[
722+
("m.room.member", "@alice:example.com"), # Member event for @alice:example.com
723+
("org.matrix.some_event", ""), # State event of type "org.matrix.some_event"
724+
# with an empty string as its state key
725+
("org.matrix.some_other_event", None), # State events of type "org.matrix.some_other_event"
726+
# regardless of their state key
727+
]
728+
"""
729+
if event_filter:
730+
# If a filter was provided, turn it into a StateFilter and retrieve a filtered
731+
# view of the state.
732+
state_filter = StateFilter.from_types(event_filter)
733+
state_ids = await self._store.get_filtered_current_state_ids(
734+
room_id,
735+
state_filter,
736+
)
737+
else:
738+
# If no filter was provided, get the whole state. We could also reuse the call
739+
# to get_filtered_current_state_ids above, with `state_filter = StateFilter.all()`,
740+
# but get_filtered_current_state_ids isn't cached and `get_current_state_ids`
741+
# is, so using the latter when we can is better for perf.
742+
state_ids = await self._store.get_current_state_ids(room_id)
743+
744+
state_events = await self._store.get_events(state_ids.values())
745+
746+
return {key: state_events[event_id] for key, event_id in state_ids.items()}
747+
693748

694749
class PublicRoomListManager:
695750
"""Contains methods for adding to, removing from and querying whether a room

tests/module_api/test_api.py

+24-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515

1616
from twisted.internet import defer
1717

18-
from synapse.api.constants import EduTypes
18+
from synapse.api.constants import EduTypes, EventTypes
1919
from synapse.events import EventBase
2020
from synapse.federation.units import Transaction
2121
from synapse.handlers.presence import UserPresenceState
@@ -308,6 +308,29 @@ def test_send_local_online_presence_to_federation(self):
308308

309309
self.assertTrue(found_update)
310310

311+
def test_get_room_state(self):
312+
"""Tests that a module can retrieve the state of a room through the module API."""
313+
user_id = self.register_user("peter", "hackme")
314+
tok = self.login("peter", "hackme")
315+
316+
# Create a room and send some custom state in it.
317+
room_id = self.helper.create_room_as(tok=tok)
318+
self.helper.send_state(room_id, "org.matrix.test", {}, tok=tok)
319+
320+
# Check that the module API can successfully fetch state for the room.
321+
state = self.get_success(
322+
defer.ensureDeferred(self.module_api.get_room_state(room_id))
323+
)
324+
325+
# Check that a few standard events are in the returned state.
326+
self.assertIn((EventTypes.Create, ""), state)
327+
self.assertIn((EventTypes.Member, user_id), state)
328+
329+
# Check that our custom state event is in the returned state.
330+
self.assertEqual(state[("org.matrix.test", "")].sender, user_id)
331+
self.assertEqual(state[("org.matrix.test", "")].state_key, "")
332+
self.assertEqual(state[("org.matrix.test", "")].content, {})
333+
311334

312335
class ModuleApiWorkerTestCase(BaseMultiWorkerStreamTestCase):
313336
"""For testing ModuleApi functionality in a multi-worker setup"""

0 commit comments

Comments
 (0)