diff --git a/synapse/handlers/message.py b/synapse/handlers/message.py
index d8f8ea80cc..081030dbb8 100644
--- a/synapse/handlers/message.py
+++ b/synapse/handlers/message.py
@@ -293,3 +293,65 @@ class MessageHandler(BaseHandler):
}
defer.returnValue(ret)
+
+ @defer.inlineCallbacks
+ def room_initial_sync(self, user_id, room_id, pagin_config=None,
+ feedback=False):
+ yield self.auth.check_joined_room(room_id, user_id)
+
+ # TODO(paul): I wish I was called with user objects not user_id
+ # strings...
+ auth_user = self.hs.parse_userid(user_id)
+
+ # TODO: These concurrently
+ state_tuples = yield self.store.get_current_state(room_id)
+ state = [self.hs.serialize_event(x) for x in state_tuples]
+
+ member_event = (yield self.store.get_room_member(
+ user_id=user_id,
+ room_id=room_id
+ ))
+
+ now_token = yield self.hs.get_event_sources().get_current_token()
+
+ limit = pagin_config.limit if pagin_config else None
+ if limit is None:
+ limit = 10
+
+ messages, token = yield self.store.get_recent_events_for_room(
+ room_id,
+ limit=limit,
+ end_token=now_token.room_key,
+ )
+
+ start_token = now_token.copy_and_replace("room_key", token[0])
+ end_token = now_token.copy_and_replace("room_key", token[1])
+
+ room_members = yield self.store.get_room_members(room_id)
+
+ presence_handler = self.hs.get_handlers().presence_handler
+ presence = []
+ for m in room_members:
+ try:
+ member_presence = yield presence_handler.get_state(
+ target_user=self.hs.parse_userid(m.user_id),
+ auth_user=auth_user,
+ as_event=True,
+ )
+ presence.append(member_presence)
+ except Exception as e:
+ logger.exception("Failed to get member presence of %r",
+ m.user_id
+ )
+
+ defer.returnValue({
+ "membership": member_event.membership,
+ "room_id": room_id,
+ "messages": {
+ "chunk": [self.hs.serialize_event(m) for m in messages],
+ "start": start_token.to_string(),
+ "end": end_token.to_string(),
+ },
+ "state": state,
+ "presence": presence
+ })
diff --git a/synapse/handlers/presence.py b/synapse/handlers/presence.py
index 2ccc2245b7..325ae45257 100644
--- a/synapse/handlers/presence.py
+++ b/synapse/handlers/presence.py
@@ -165,7 +165,7 @@ class PresenceHandler(BaseHandler):
defer.returnValue(False)
@defer.inlineCallbacks
- def get_state(self, target_user, auth_user):
+ def get_state(self, target_user, auth_user, as_event=False):
if target_user.is_mine:
visible = yield self.is_presence_visible(
observer_user=auth_user,
@@ -180,9 +180,9 @@ class PresenceHandler(BaseHandler):
state["presence"] = state.pop("state")
if target_user in self._user_cachemap:
- state["last_active"] = (
- self._user_cachemap[target_user].get_state()["last_active"]
- )
+ cached_state = self._user_cachemap[target_user].get_state()
+ if "last_active" in cached_state:
+ state["last_active"] = cached_state["last_active"]
else:
# TODO(paul): Have remote server send us permissions set
state = self._get_or_offline_usercache(target_user).get_state()
@@ -191,7 +191,20 @@ class PresenceHandler(BaseHandler):
state["last_active_ago"] = int(
self.clock.time_msec() - state.pop("last_active")
)
- defer.returnValue(state)
+
+ if as_event:
+ content = state
+
+ content["user_id"] = target_user.to_string()
+
+ if "last_active" in content:
+ content["last_active_ago"] = int(
+ self._clock.time_msec() - content.pop("last_active")
+ )
+
+ defer.returnValue({"type": "m.presence", "content": content})
+ else:
+ defer.returnValue(state)
@defer.inlineCallbacks
@log_function
diff --git a/synapse/rest/room.py b/synapse/rest/room.py
index 05da0be090..17322ee9bb 100644
--- a/synapse/rest/room.py
+++ b/synapse/rest/room.py
@@ -361,27 +361,14 @@ class RoomInitialSyncRestServlet(RestServlet):
@defer.inlineCallbacks
def on_GET(self, request, room_id):
- yield self.auth.get_user_by_req(request)
- # TODO: Get all the initial sync data for this room and return in the
- # same format as initial sync, that is:
- # {
- # membership: join,
- # messages: [
- # chunk: [ msg events ],
- # start: s_tok,
- # end: e_tok
- # ],
- # room_id: foo,
- # state: [
- # { state event } , { state event }
- # ]
- # }
- # Probably worth keeping the keys room_id and membership for parity
- # with /initialSync even though they must be joined to sync this and
- # know the room ID, so clients can reuse the same code (room_id and
- # membership are MANDATORY for /initialSync, so the code will expect
- # it to be there)
- defer.returnValue((200, {}))
+ user = yield self.auth.get_user_by_req(request)
+ pagination_config = PaginationConfig.from_request(request)
+ content = yield self.handlers.message_handler.room_initial_sync(
+ room_id=room_id,
+ user_id=user.to_string(),
+ pagin_config=pagination_config,
+ )
+ defer.returnValue((200, content))
class RoomTriggerBackfill(RestServlet):
diff --git a/tests/rest/test_rooms.py b/tests/rest/test_rooms.py
index e27990dace..ad3631d510 100644
--- a/tests/rest/test_rooms.py
+++ b/tests/rest/test_rooms.py
@@ -981,6 +981,93 @@ class RoomMessagesTestCase(RestTestCase):
(code, response) = yield self.mock_resource.trigger("PUT", path, content)
self.assertEquals(200, code, msg=str(response))
+
+class RoomInitialSyncTestCase(RestTestCase):
+ """ Tests /rooms/$room_id/initialSync. """
+ user_id = "@sid1:red"
+
+ @defer.inlineCallbacks
+ def setUp(self):
+ self.mock_resource = MockHttpResource(prefix=PATH_PREFIX)
+ self.auth_user_id = self.user_id
+
+ self.mock_config = NonCallableMock()
+ self.mock_config.signing_key = [MockKey()]
+
+ db_pool = SQLiteMemoryDbPool()
+ yield db_pool.prepare()
+
+ hs = HomeServer(
+ "red",
+ db_pool=db_pool,
+ http_client=None,
+ replication_layer=Mock(),
+ ratelimiter=NonCallableMock(spec_set=[
+ "send_message",
+ ]),
+ config=self.mock_config,
+ )
+ self.ratelimiter = hs.get_ratelimiter()
+ self.ratelimiter.send_message.return_value = (True, 0)
+
+ hs.get_handlers().federation_handler = Mock()
+
+ def _get_user_by_token(token=None):
+ return {
+ "user": hs.parse_userid(self.auth_user_id),
+ "admin": False,
+ "device_id": None,
+ }
+ hs.get_auth().get_user_by_token = _get_user_by_token
+
+ def _insert_client_ip(*args, **kwargs):
+ return defer.succeed(None)
+ hs.get_datastore().insert_client_ip = _insert_client_ip
+
+ synapse.rest.room.register_servlets(hs, self.mock_resource)
+
+ # Since I'm getting my own presence I need to exist as far as presence
+ # is concerned.
+ hs.get_handlers().presence_handler.registered_user(
+ hs.parse_userid(self.user_id)
+ )
+
+ # create the room
+ self.room_id = yield self.create_room_as(self.user_id)
+
+ @defer.inlineCallbacks
+ def test_initial_sync(self):
+ (code, response) = yield self.mock_resource.trigger_get(
+ "/rooms/%s/initialSync" % self.room_id)
+ self.assertEquals(200, code)
+
+ self.assertEquals(self.room_id, response["room_id"])
+ self.assertEquals("join", response["membership"])
+
+ # Room state is easier to assert on if we unpack it into a dict
+ state = {}
+ for event in response["state"]:
+ if "state_key" not in event:
+ continue
+ t = event["type"]
+ if t not in state:
+ state[t] = []
+ state[t].append(event)
+
+ self.assertTrue("m.room.create" in state)
+
+ self.assertTrue("messages" in response)
+ self.assertTrue("chunk" in response["messages"])
+ self.assertTrue("end" in response["messages"])
+
+ self.assertTrue("presence" in response)
+
+ presence_by_user = {e["content"]["user_id"]: e
+ for e in response["presence"]
+ }
+ self.assertTrue(self.user_id in presence_by_user)
+ self.assertEquals("m.presence", presence_by_user[self.user_id]["type"])
+
# (code, response) = yield self.mock_resource.trigger("GET", path, None)
# self.assertEquals(200, code, msg=str(response))
# self.assert_dict(json.loads(content), response)
|