From 02f4e3b3ff613a6e9024c0fef416be0bf92bf48f Mon Sep 17 00:00:00 2001 From: "Paul \"LeoNerd\" Evans" Date: Mon, 1 Sep 2014 14:45:35 +0100 Subject: Rename 'state' presence key to the much more obvious 'presence'; maintain a legacy 'state' copy for now --- synapse/handlers/presence.py | 42 +++++++++++++++++++++++++++++++----------- synapse/rest/presence.py | 6 +++++- 2 files changed, 36 insertions(+), 12 deletions(-) (limited to 'synapse') diff --git a/synapse/handlers/presence.py b/synapse/handlers/presence.py index 93bd07b196..fa5942ba05 100644 --- a/synapse/handlers/presence.py +++ b/synapse/handlers/presence.py @@ -135,7 +135,7 @@ class PresenceHandler(BaseHandler): return self._user_cachemap[user] else: statuscache = UserPresenceCache() - statuscache.update({"state": PresenceState.OFFLINE}, user) + statuscache.update({"presence": PresenceState.OFFLINE}, user) return statuscache def registered_user(self, user): @@ -177,6 +177,7 @@ class PresenceHandler(BaseHandler): state = yield self.store.get_presence_state( target_user.localpart ) + state["presence"] = state["state"] else: raise SynapseError(404, "Presence information not visible") else: @@ -207,15 +208,20 @@ class PresenceHandler(BaseHandler): state["status_msg"] = None for k in state.keys(): - if k not in ("state", "status_msg"): + if k not in ("presence", "state", "status_msg"): raise SynapseError( 400, "Unexpected presence state key '%s'" % (k,) ) + # Handle legacy "state" key for now + if "state" in state: + state["presence"] = state.pop("state") + logger.debug("Updating presence state of %s to %s", - target_user.localpart, state["state"]) + target_user.localpart, state["presence"]) state_to_store = dict(state) + state_to_store["state"] = state_to_store.pop("presence") yield defer.DeferredList([ self.store.set_presence_state( @@ -228,7 +234,7 @@ class PresenceHandler(BaseHandler): state["mtime"] = self.clock.time_msec() - now_online = state["state"] != PresenceState.OFFLINE + now_online = state["presence"] != PresenceState.OFFLINE was_polling = target_user in self._user_cachemap if now_online and not was_polling: @@ -251,12 +257,12 @@ class PresenceHandler(BaseHandler): @log_function def started_user_eventstream(self, user): # TODO(paul): Use "last online" state - self.set_state(user, user, {"state": PresenceState.ONLINE}) + self.set_state(user, user, {"presence": PresenceState.ONLINE}) @log_function def stopped_user_eventstream(self, user): # TODO(paul): Save current state as "last online" state - self.set_state(user, user, {"state": PresenceState.OFFLINE}) + self.set_state(user, user, {"presence": PresenceState.OFFLINE}) @defer.inlineCallbacks def user_joined_room(self, user, room_id): @@ -576,6 +582,7 @@ class PresenceHandler(BaseHandler): def _push_presence_remote(self, user, destination, state=None): if state is None: state = yield self.store.get_presence_state(user.localpart) + state["presence"] = state["state"] yield self.distributor.fire( "collect_presencelike_data", user, state @@ -591,6 +598,8 @@ class PresenceHandler(BaseHandler): "user_id": user.to_string(), } user_state.update(**state) + if "state" in user_state and "presence" not in user_state: + user_state["presence"] = user_state["state"] yield self.federation.send_edu( destination=destination, @@ -622,6 +631,11 @@ class PresenceHandler(BaseHandler): state = dict(push) del state["user_id"] + # Legacy handling + if "presence" not in state: + state["presence"] = state["state"] + del state["state"] + if "mtime_age" in state: state["mtime"] = int( self.clock.time_msec() - state.pop("mtime_age") @@ -639,7 +653,7 @@ class PresenceHandler(BaseHandler): statuscache=statuscache, ) - if state["state"] == PresenceState.OFFLINE: + if state["presence"] == PresenceState.OFFLINE: del self._user_cachemap[user] for poll in content.get("poll", []): @@ -672,10 +686,9 @@ class PresenceHandler(BaseHandler): yield defer.DeferredList(deferreds) @defer.inlineCallbacks - def push_update_to_local_and_remote(self, observed_user, + def push_update_to_local_and_remote(self, observed_user, statuscache, users_to_push=[], room_ids=[], - remote_domains=[], - statuscache=None): + remote_domains=[]): localusers, remoteusers = partitionbool( users_to_push, @@ -804,6 +817,7 @@ class UserPresenceCache(object): def update(self, state, serial): assert("mtime_age" not in state) + assert("state" not in state) self.state.update(state) # Delete keys that are now 'None' @@ -820,7 +834,13 @@ class UserPresenceCache(object): def get_state(self): # clone it so caller can't break our cache - return dict(self.state) + state = dict(self.state) + + # Legacy handling + if "presence" in state: + state["state"] = state["presence"] + + return state def make_event(self, user, clock): content = self.get_state() diff --git a/synapse/rest/presence.py b/synapse/rest/presence.py index e013b20853..bce3943542 100644 --- a/synapse/rest/presence.py +++ b/synapse/rest/presence.py @@ -48,7 +48,11 @@ class PresenceStatusRestServlet(RestServlet): try: content = json.loads(request.content.read()) - state["state"] = content.pop("state") + # Legacy handling + if "state" in content: + state["presence"] = content.pop("state") + else: + state["presence"] = content.pop("presence") if "status_msg" in content: state["status_msg"] = content.pop("status_msg") -- cgit 1.4.1 From 468d94c92066ddb76db7f38771e283c152cabb7d Mon Sep 17 00:00:00 2001 From: "Paul \"LeoNerd\" Evans" Date: Mon, 1 Sep 2014 15:38:37 +0100 Subject: Rename API-visible 'mtime' presence field to 'last_active'; slightly different semantics --- synapse/handlers/presence.py | 72 +++++++++++++++++++++++++------------ tests/handlers/test_presence.py | 29 +++++++++------ tests/handlers/test_presencelike.py | 20 +++++++---- tests/rest/test_presence.py | 2 +- 4 files changed, 82 insertions(+), 41 deletions(-) (limited to 'synapse') diff --git a/synapse/handlers/presence.py b/synapse/handlers/presence.py index fa5942ba05..c1af07133f 100644 --- a/synapse/handlers/presence.py +++ b/synapse/handlers/presence.py @@ -52,6 +52,13 @@ def partitionbool(l, func): class PresenceHandler(BaseHandler): + STATE_LEVELS = { + PresenceState.OFFLINE: 0, + PresenceState.UNAVAILABLE: 1, + PresenceState.ONLINE: 2, + PresenceState.FREE_FOR_CHAT: 3, + } + def __init__(self, hs): super(PresenceHandler, self).__init__(hs) @@ -173,20 +180,24 @@ class PresenceHandler(BaseHandler): observed_user=target_user ) - if visible: - state = yield self.store.get_presence_state( - target_user.localpart - ) - state["presence"] = state["state"] - else: + if not visible: raise SynapseError(404, "Presence information not visible") + state = yield self.store.get_presence_state(target_user.localpart) + if "mtime" in state: + del state["mtime"] + state["presence"] = state["state"] + + if target_user in self._user_cachemap: + state["last_active"] = ( + self._user_cachemap[target_user].get_state()["last_active"] + ) else: # TODO(paul): Have remote server send us permissions set state = self._get_or_offline_usercache(target_user).get_state() - if "mtime" in state and (state["mtime"] is not None): - state["mtime_age"] = int( - self.clock.time_msec() - state.pop("mtime") + if "last_active" in state: + state["last_active_ago"] = int( + self.clock.time_msec() - state.pop("last_active") ) defer.returnValue(state) @@ -203,7 +214,6 @@ class PresenceHandler(BaseHandler): if target_user != auth_user: raise AuthError(400, "Cannot set another user's displayname") - # TODO(paul): Sanity-check 'state' if "status_msg" not in state: state["status_msg"] = None @@ -217,12 +227,21 @@ class PresenceHandler(BaseHandler): if "state" in state: state["presence"] = state.pop("state") + if state["presence"] not in self.STATE_LEVELS: + raise SynapseError(400, "'%s' is not a valid presence state" % + state["presence"] + ) + logger.debug("Updating presence state of %s to %s", target_user.localpart, state["presence"]) state_to_store = dict(state) state_to_store["state"] = state_to_store.pop("presence") + statuscache=self._get_or_offline_usercache(target_user) + was_level = self.STATE_LEVELS[statuscache.get_state()["presence"]] + now_level = self.STATE_LEVELS[state["presence"]] + yield defer.DeferredList([ self.store.set_presence_state( target_user.localpart, state_to_store @@ -232,7 +251,8 @@ class PresenceHandler(BaseHandler): ), ]) - state["mtime"] = self.clock.time_msec() + if now_level > was_level: + state["last_active"] = self.clock.time_msec() now_online = state["presence"] != PresenceState.OFFLINE was_polling = target_user in self._user_cachemap @@ -391,9 +411,9 @@ class PresenceHandler(BaseHandler): observed_user = self.hs.parse_userid(p.pop("observed_user_id")) p["observed_user"] = observed_user p.update(self._get_or_offline_usercache(observed_user).get_state()) - if "mtime" in p: - p["mtime_age"] = int( - self.clock.time_msec() - p.pop("mtime") + if "last_active" in p: + p["last_active_ago"] = int( + self.clock.time_msec() - p.pop("last_active") ) defer.returnValue(presence) @@ -582,16 +602,22 @@ class PresenceHandler(BaseHandler): def _push_presence_remote(self, user, destination, state=None): if state is None: state = yield self.store.get_presence_state(user.localpart) + del state["mtime"] state["presence"] = state["state"] + if user in self._user_cachemap: + state["last_active"] = ( + self._user_cachemap[user].get_state()["last_active"] + ) + yield self.distributor.fire( "collect_presencelike_data", user, state ) - if "mtime" in state: + if "last_active" in state: state = dict(state) - state["mtime_age"] = int( - self.clock.time_msec() - state.pop("mtime") + state["last_active_ago"] = int( + self.clock.time_msec() - state.pop("last_active") ) user_state = { @@ -636,9 +662,9 @@ class PresenceHandler(BaseHandler): state["presence"] = state["state"] del state["state"] - if "mtime_age" in state: - state["mtime"] = int( - self.clock.time_msec() - state.pop("mtime_age") + if "last_active_ago" in state: + state["last_active"] = int( + self.clock.time_msec() - state.pop("last_active_ago") ) statuscache = self._get_or_make_usercache(user) @@ -846,9 +872,9 @@ class UserPresenceCache(object): content = self.get_state() content["user_id"] = user.to_string() - if "mtime" in content: - content["mtime_age"] = int( - clock.time_msec() - content.pop("mtime") + if "last_active" in content: + content["last_active_ago"] = int( + clock.time_msec() - content.pop("last_active") ) return {"type": "m.presence", "content": content} diff --git a/tests/handlers/test_presence.py b/tests/handlers/test_presence.py index f37d9917fe..844c4e3fb7 100644 --- a/tests/handlers/test_presence.py +++ b/tests/handlers/test_presence.py @@ -213,7 +213,7 @@ class PresenceStateTestCase(unittest.TestCase): state={ "presence": UNAVAILABLE, "status_msg": "Away", - "mtime": 1000000, # MockClock + "last_active": 1000000, # MockClock }) yield self.handler.set_state( @@ -621,6 +621,9 @@ class PresencePushTestCase(unittest.TestCase): # TODO(paul): Gut-wrenching self.handler._user_cachemap[self.u_apple] = UserPresenceCache() + self.handler._user_cachemap[self.u_apple].update( + {"presence": OFFLINE}, serial=0 + ) apple_set = self.handler._local_pushmap.setdefault("apple", set()) apple_set.add(self.u_banana) apple_set.add(self.u_clementine) @@ -640,7 +643,7 @@ class PresencePushTestCase(unittest.TestCase): "user_id": "@apple:test", "presence": ONLINE, "state": ONLINE, - "mtime_age": 0, + "last_active_ago": 0, }}, ], ) @@ -673,7 +676,7 @@ class PresencePushTestCase(unittest.TestCase): {"observed_user": self.u_banana, "presence": ONLINE, "state": ONLINE, - "mtime_age": 2000}, + "last_active_ago": 2000}, {"observed_user": self.u_clementine, "presence": OFFLINE, "state": OFFLINE}, @@ -690,7 +693,7 @@ class PresencePushTestCase(unittest.TestCase): "user_id": "@banana:test", "presence": ONLINE, "state": ONLINE, - "mtime_age": 2000 + "last_active_ago": 2000 }}, ] ) @@ -707,7 +710,7 @@ class PresencePushTestCase(unittest.TestCase): {"user_id": "@apple:test", "presence": u"online", "state": u"online", - "mtime_age": 0}, + "last_active_ago": 0}, ], } ) @@ -723,6 +726,9 @@ class PresencePushTestCase(unittest.TestCase): # TODO(paul): Gut-wrenching self.handler._user_cachemap[self.u_apple] = UserPresenceCache() + self.handler._user_cachemap[self.u_apple].update( + {"presence": OFFLINE}, serial=0 + ) apple_set = self.handler._remote_sendmap.setdefault("apple", set()) apple_set.add(self.u_potato.domain) @@ -750,7 +756,7 @@ class PresencePushTestCase(unittest.TestCase): "push": [ {"user_id": "@potato:remote", "state": "online", - "mtime_age": 1000}, + "last_active_ago": 1000}, ], } ) @@ -767,7 +773,7 @@ class PresencePushTestCase(unittest.TestCase): "user_id": "@potato:remote", "presence": ONLINE, "state": ONLINE, - "mtime_age": 1000, + "last_active_ago": 1000, }} ] ) @@ -777,7 +783,7 @@ class PresencePushTestCase(unittest.TestCase): state = yield self.handler.get_state(self.u_potato, self.u_apple) self.assertEquals( - {"state": ONLINE, "presence": ONLINE, "mtime_age": 3000}, + {"state": ONLINE, "presence": ONLINE, "last_active_ago": 3000}, state ) @@ -792,7 +798,7 @@ class PresencePushTestCase(unittest.TestCase): self.handler._user_cachemap[self.u_clementine].update( { "presence": PresenceState.ONLINE, - "mtime": self.clock.time_msec(), + "last_active": self.clock.time_msec(), }, self.u_clementine ) @@ -811,7 +817,7 @@ class PresencePushTestCase(unittest.TestCase): "user_id": "@clementine:test", "presence": ONLINE, "state": ONLINE, - "mtime_age": 0, + "last_active_ago": 0, }} ] ) @@ -967,7 +973,8 @@ class PresencePollingTestCase(unittest.TestCase): def get_presence_state(user_localpart): return defer.succeed( {"state": self.current_user_state[user_localpart], - "status_msg": None} + "status_msg": None, + "mtime": 123456000} ) self.datastore.get_presence_state = get_presence_state diff --git a/tests/handlers/test_presencelike.py b/tests/handlers/test_presencelike.py index 2402bed8d5..38cc34350b 100644 --- a/tests/handlers/test_presencelike.py +++ b/tests/handlers/test_presencelike.py @@ -166,7 +166,11 @@ class PresenceProfilelikeDataTestCase(unittest.TestCase): # TODO(paul): Gut-wrenching from synapse.handlers.presence import UserPresenceCache self.handlers.presence_handler._user_cachemap[self.u_apple] = ( - UserPresenceCache()) + UserPresenceCache() + ) + self.handlers.presence_handler._user_cachemap[self.u_apple].update( + {"presence": OFFLINE}, serial=0 + ) apple_set = self.handlers.presence_handler._local_pushmap.setdefault( "apple", set()) apple_set.add(self.u_banana) @@ -184,7 +188,7 @@ class PresenceProfilelikeDataTestCase(unittest.TestCase): {"observed_user": self.u_banana, "presence": ONLINE, "state": ONLINE, - "mtime_age": 0, + "last_active_ago": 0, "displayname": "Frank", "avatar_url": "http://foo"}, {"observed_user": self.u_clementine, @@ -202,7 +206,7 @@ class PresenceProfilelikeDataTestCase(unittest.TestCase): statuscache = self.mock_update_client.call_args[1]["statuscache"] self.assertEquals({ "presence": ONLINE, - "mtime": 1000000, # MockClock + "last_active": 1000000, # MockClock "displayname": "Frank", "avatar_url": "http://foo", }, statuscache.state) @@ -225,7 +229,7 @@ class PresenceProfilelikeDataTestCase(unittest.TestCase): statuscache = self.mock_update_client.call_args[1]["statuscache"] self.assertEquals({ "presence": ONLINE, - "mtime": 1000000, # MockClock + "last_active": 1000000, # MockClock "displayname": "I am an Apple", "avatar_url": "http://foo", }, statuscache.state) @@ -243,7 +247,11 @@ class PresenceProfilelikeDataTestCase(unittest.TestCase): # TODO(paul): Gut-wrenching from synapse.handlers.presence import UserPresenceCache self.handlers.presence_handler._user_cachemap[self.u_apple] = ( - UserPresenceCache()) + UserPresenceCache() + ) + self.handlers.presence_handler._user_cachemap[self.u_apple].update( + {"presence": OFFLINE}, serial=0 + ) apple_set = self.handlers.presence_handler._remote_sendmap.setdefault( "apple", set()) apple_set.add(self.u_potato.domain) @@ -259,7 +267,7 @@ class PresenceProfilelikeDataTestCase(unittest.TestCase): {"user_id": "@apple:test", "presence": "online", "state": "online", - "mtime_age": 0, + "last_active_ago": 0, "displayname": "Frank", "avatar_url": "http://foo"}, ], diff --git a/tests/rest/test_presence.py b/tests/rest/test_presence.py index f5afa4a256..e2cdd80e07 100644 --- a/tests/rest/test_presence.py +++ b/tests/rest/test_presence.py @@ -330,6 +330,6 @@ class PresenceEventStreamTestCase(unittest.TestCase): "presence": ONLINE, "state": ONLINE, "displayname": "Frank", - "mtime_age": 0, + "last_active_ago": 0, }}, ]}, response) -- cgit 1.4.1 From fd696f124305ce965f24f10f734dc3d5d3ebe2b6 Mon Sep 17 00:00:00 2001 From: "Paul \"LeoNerd\" Evans" Date: Mon, 1 Sep 2014 15:58:16 +0100 Subject: Perform room unit tests with 'local' users actually in the right domain ;) --- synapse/handlers/message.py | 4 ++++ tests/rest/test_rooms.py | 12 ++++++------ 2 files changed, 10 insertions(+), 6 deletions(-) (limited to 'synapse') diff --git a/synapse/handlers/message.py b/synapse/handlers/message.py index c8ff34e5f5..f133458b91 100644 --- a/synapse/handlers/message.py +++ b/synapse/handlers/message.py @@ -76,6 +76,10 @@ class MessageHandler(BaseRoomHandler): Raises: SynapseError if something went wrong. """ + # TODO(paul): Why does 'event' not have a 'user' object? + user = self.hs.parse_userid(event.user_id) + assert(user.is_mine) + if stamp_event: event.content["hsob_ts"] = int(self.clock.time_msec()) diff --git a/tests/rest/test_rooms.py b/tests/rest/test_rooms.py index b432cf254e..cdaf948a3b 100644 --- a/tests/rest/test_rooms.py +++ b/tests/rest/test_rooms.py @@ -51,7 +51,7 @@ class RoomPermissionsTestCase(RestTestCase): persistence_service.get_latest_pdus_in_context.return_value = [] hs = HomeServer( - "test", + "red", db_pool=None, http_client=None, datastore=MemoryDataStore(), @@ -398,7 +398,7 @@ class RoomsMemberListTestCase(RestTestCase): persistence_service.get_latest_pdus_in_context.return_value = [] hs = HomeServer( - "test", + "red", db_pool=None, http_client=None, datastore=MemoryDataStore(), @@ -476,7 +476,7 @@ class RoomsCreateTestCase(RestTestCase): persistence_service.get_latest_pdus_in_context.return_value = [] hs = HomeServer( - "test", + "red", db_pool=None, http_client=None, datastore=MemoryDataStore(), @@ -566,7 +566,7 @@ class RoomTopicTestCase(RestTestCase): persistence_service.get_latest_pdus_in_context.return_value = [] hs = HomeServer( - "test", + "red", db_pool=None, http_client=None, datastore=MemoryDataStore(), @@ -669,7 +669,7 @@ class RoomMemberStateTestCase(RestTestCase): persistence_service.get_latest_pdus_in_context.return_value = [] hs = HomeServer( - "test", + "red", db_pool=None, http_client=None, datastore=MemoryDataStore(), @@ -794,7 +794,7 @@ class RoomMessagesTestCase(RestTestCase): persistence_service.get_latest_pdus_in_context.return_value = [] hs = HomeServer( - "test", + "red", db_pool=None, http_client=None, datastore=MemoryDataStore(), -- cgit 1.4.1 From 1c6ab2d75994bb0b50d7c31a508c4e5a088feb15 Mon Sep 17 00:00:00 2001 From: "Paul \"LeoNerd\" Evans" Date: Mon, 1 Sep 2014 16:16:35 +0100 Subject: Bump a user's presence last_active time every time they send a message to a room --- synapse/handlers/message.py | 4 ++++ synapse/handlers/presence.py | 6 ++++++ 2 files changed, 10 insertions(+) (limited to 'synapse') diff --git a/synapse/handlers/message.py b/synapse/handlers/message.py index f133458b91..4c74ce3eff 100644 --- a/synapse/handlers/message.py +++ b/synapse/handlers/message.py @@ -90,6 +90,10 @@ class MessageHandler(BaseRoomHandler): yield self._on_new_room_event(event, snapshot) + self.hs.get_handlers().presence_handler.bump_presence_active_time( + user + ) + @defer.inlineCallbacks def get_messages(self, user_id=None, room_id=None, pagin_config=None, feedback=False): diff --git a/synapse/handlers/presence.py b/synapse/handlers/presence.py index c1af07133f..f55bea58a5 100644 --- a/synapse/handlers/presence.py +++ b/synapse/handlers/presence.py @@ -266,6 +266,12 @@ class PresenceHandler(BaseHandler): # we don't have to do this all the time self.changed_presencelike_data(target_user, state) + def bump_presence_active_time(self, user, now=None): + if now is None: + now = self.clock.time_msec() + + self.changed_presencelike_data(user, {"last_active": now}) + def changed_presencelike_data(self, user, state): statuscache = self._get_or_make_usercache(user) -- cgit 1.4.1