Merge pull request #17 from matrix-org/room-initial-sync

Room initial sync
This commit is contained in:
Paul Evans 2014-11-18 16:44:25 +00:00
commit 11fd81e398
4 changed files with 175 additions and 26 deletions

View File

@ -293,3 +293,65 @@ class MessageHandler(BaseHandler):
} }
defer.returnValue(ret) 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
})

View File

@ -165,7 +165,7 @@ class PresenceHandler(BaseHandler):
defer.returnValue(False) defer.returnValue(False)
@defer.inlineCallbacks @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: if target_user.is_mine:
visible = yield self.is_presence_visible( visible = yield self.is_presence_visible(
observer_user=auth_user, observer_user=auth_user,
@ -180,9 +180,9 @@ class PresenceHandler(BaseHandler):
state["presence"] = state.pop("state") state["presence"] = state.pop("state")
if target_user in self._user_cachemap: if target_user in self._user_cachemap:
state["last_active"] = ( cached_state = self._user_cachemap[target_user].get_state()
self._user_cachemap[target_user].get_state()["last_active"] if "last_active" in cached_state:
) state["last_active"] = cached_state["last_active"]
else: else:
# TODO(paul): Have remote server send us permissions set # TODO(paul): Have remote server send us permissions set
state = self._get_or_offline_usercache(target_user).get_state() state = self._get_or_offline_usercache(target_user).get_state()
@ -191,6 +191,19 @@ class PresenceHandler(BaseHandler):
state["last_active_ago"] = int( state["last_active_ago"] = int(
self.clock.time_msec() - state.pop("last_active") self.clock.time_msec() - state.pop("last_active")
) )
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.returnValue(state)
@defer.inlineCallbacks @defer.inlineCallbacks

View File

@ -361,27 +361,14 @@ class RoomInitialSyncRestServlet(RestServlet):
@defer.inlineCallbacks @defer.inlineCallbacks
def on_GET(self, request, room_id): def on_GET(self, request, room_id):
yield self.auth.get_user_by_req(request) user = yield self.auth.get_user_by_req(request)
# TODO: Get all the initial sync data for this room and return in the pagination_config = PaginationConfig.from_request(request)
# same format as initial sync, that is: content = yield self.handlers.message_handler.room_initial_sync(
# { room_id=room_id,
# membership: join, user_id=user.to_string(),
# messages: [ pagin_config=pagination_config,
# chunk: [ msg events ], )
# start: s_tok, defer.returnValue((200, content))
# 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, {}))
class RoomTriggerBackfill(RestServlet): class RoomTriggerBackfill(RestServlet):

View File

@ -981,6 +981,93 @@ class RoomMessagesTestCase(RestTestCase):
(code, response) = yield self.mock_resource.trigger("PUT", path, content) (code, response) = yield self.mock_resource.trigger("PUT", path, content)
self.assertEquals(200, code, msg=str(response)) 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) # (code, response) = yield self.mock_resource.trigger("GET", path, None)
# self.assertEquals(200, code, msg=str(response)) # self.assertEquals(200, code, msg=str(response))
# self.assert_dict(json.loads(content), response) # self.assert_dict(json.loads(content), response)