Add a module API method to retrieve state from a room (#11204)
parent
3ed17ff651
commit
ad4eab9862
|
@ -0,0 +1 @@
|
||||||
|
Add a module API method to retrieve the current state of a room.
|
|
@ -55,6 +55,7 @@ from synapse.types import (
|
||||||
DomainSpecificString,
|
DomainSpecificString,
|
||||||
JsonDict,
|
JsonDict,
|
||||||
Requester,
|
Requester,
|
||||||
|
StateMap,
|
||||||
UserID,
|
UserID,
|
||||||
UserInfo,
|
UserInfo,
|
||||||
create_requester,
|
create_requester,
|
||||||
|
@ -89,6 +90,8 @@ __all__ = [
|
||||||
"PRESENCE_ALL_USERS",
|
"PRESENCE_ALL_USERS",
|
||||||
"LoginResponse",
|
"LoginResponse",
|
||||||
"JsonDict",
|
"JsonDict",
|
||||||
|
"EventBase",
|
||||||
|
"StateMap",
|
||||||
]
|
]
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
@ -964,6 +967,52 @@ class ModuleApi:
|
||||||
else:
|
else:
|
||||||
return []
|
return []
|
||||||
|
|
||||||
|
async def get_room_state(
|
||||||
|
self,
|
||||||
|
room_id: str,
|
||||||
|
event_filter: Optional[Iterable[Tuple[str, Optional[str]]]] = None,
|
||||||
|
) -> StateMap[EventBase]:
|
||||||
|
"""Returns the current state of the given room.
|
||||||
|
|
||||||
|
The events are returned as a mapping, in which the key for each event is a tuple
|
||||||
|
which first element is the event's type and the second one is its state key.
|
||||||
|
|
||||||
|
Added in Synapse v1.47.0
|
||||||
|
|
||||||
|
Args:
|
||||||
|
room_id: The ID of the room to get state from.
|
||||||
|
event_filter: A filter to apply when retrieving events. None if no filter
|
||||||
|
should be applied. If provided, must be an iterable of tuples. A tuple's
|
||||||
|
first element is the event type and the second is the state key, or is
|
||||||
|
None if the state key should not be filtered on.
|
||||||
|
An example of a filter is:
|
||||||
|
[
|
||||||
|
("m.room.member", "@alice:example.com"), # Member event for @alice:example.com
|
||||||
|
("org.matrix.some_event", ""), # State event of type "org.matrix.some_event"
|
||||||
|
# with an empty string as its state key
|
||||||
|
("org.matrix.some_other_event", None), # State events of type "org.matrix.some_other_event"
|
||||||
|
# regardless of their state key
|
||||||
|
]
|
||||||
|
"""
|
||||||
|
if event_filter:
|
||||||
|
# If a filter was provided, turn it into a StateFilter and retrieve a filtered
|
||||||
|
# view of the state.
|
||||||
|
state_filter = StateFilter.from_types(event_filter)
|
||||||
|
state_ids = await self._store.get_filtered_current_state_ids(
|
||||||
|
room_id,
|
||||||
|
state_filter,
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
# If no filter was provided, get the whole state. We could also reuse the call
|
||||||
|
# to get_filtered_current_state_ids above, with `state_filter = StateFilter.all()`,
|
||||||
|
# but get_filtered_current_state_ids isn't cached and `get_current_state_ids`
|
||||||
|
# is, so using the latter when we can is better for perf.
|
||||||
|
state_ids = await self._store.get_current_state_ids(room_id)
|
||||||
|
|
||||||
|
state_events = await self._store.get_events(state_ids.values())
|
||||||
|
|
||||||
|
return {key: state_events[event_id] for key, event_id in state_ids.items()}
|
||||||
|
|
||||||
|
|
||||||
class PublicRoomListManager:
|
class PublicRoomListManager:
|
||||||
"""Contains methods for adding to, removing from and querying whether a room
|
"""Contains methods for adding to, removing from and querying whether a room
|
||||||
|
|
|
@ -15,7 +15,7 @@ from unittest.mock import Mock
|
||||||
|
|
||||||
from twisted.internet import defer
|
from twisted.internet import defer
|
||||||
|
|
||||||
from synapse.api.constants import EduTypes
|
from synapse.api.constants import EduTypes, EventTypes
|
||||||
from synapse.events import EventBase
|
from synapse.events import EventBase
|
||||||
from synapse.federation.units import Transaction
|
from synapse.federation.units import Transaction
|
||||||
from synapse.handlers.presence import UserPresenceState
|
from synapse.handlers.presence import UserPresenceState
|
||||||
|
@ -509,6 +509,29 @@ class ModuleApiTestCase(HomeserverTestCase):
|
||||||
self.assertEqual(res["displayname"], "simone")
|
self.assertEqual(res["displayname"], "simone")
|
||||||
self.assertIsNone(res["avatar_url"])
|
self.assertIsNone(res["avatar_url"])
|
||||||
|
|
||||||
|
def test_get_room_state(self):
|
||||||
|
"""Tests that a module can retrieve the state of a room through the module API."""
|
||||||
|
user_id = self.register_user("peter", "hackme")
|
||||||
|
tok = self.login("peter", "hackme")
|
||||||
|
|
||||||
|
# Create a room and send some custom state in it.
|
||||||
|
room_id = self.helper.create_room_as(tok=tok)
|
||||||
|
self.helper.send_state(room_id, "org.matrix.test", {}, tok=tok)
|
||||||
|
|
||||||
|
# Check that the module API can successfully fetch state for the room.
|
||||||
|
state = self.get_success(
|
||||||
|
defer.ensureDeferred(self.module_api.get_room_state(room_id))
|
||||||
|
)
|
||||||
|
|
||||||
|
# Check that a few standard events are in the returned state.
|
||||||
|
self.assertIn((EventTypes.Create, ""), state)
|
||||||
|
self.assertIn((EventTypes.Member, user_id), state)
|
||||||
|
|
||||||
|
# Check that our custom state event is in the returned state.
|
||||||
|
self.assertEqual(state[("org.matrix.test", "")].sender, user_id)
|
||||||
|
self.assertEqual(state[("org.matrix.test", "")].state_key, "")
|
||||||
|
self.assertEqual(state[("org.matrix.test", "")].content, {})
|
||||||
|
|
||||||
|
|
||||||
class ModuleApiWorkerTestCase(BaseMultiWorkerStreamTestCase):
|
class ModuleApiWorkerTestCase(BaseMultiWorkerStreamTestCase):
|
||||||
"""For testing ModuleApi functionality in a multi-worker setup"""
|
"""For testing ModuleApi functionality in a multi-worker setup"""
|
||||||
|
|
Loading…
Reference in New Issue