mirror of
https://github.com/element-hq/synapse
synced 2024-08-19 05:40:58 +00:00
Merge pull request #17 from matrix-org/room-initial-sync
Room initial sync
This commit is contained in:
commit
11fd81e398
4 changed files with 175 additions and 26 deletions
|
@ -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
|
||||||
|
})
|
||||||
|
|
|
@ -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,7 +191,20 @@ 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")
|
||||||
)
|
)
|
||||||
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
|
@defer.inlineCallbacks
|
||||||
@log_function
|
@log_function
|
||||||
|
|
|
@ -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):
|
||||||
|
|
|
@ -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)
|
||||||
|
|
Loading…
Reference in a new issue