# HG changeset patch # User Goffi # Date 1597390292 -7200 # Node ID ed28ad7d484c0c35820addd90dfb4c1ec7f7635d # Parent 683e50799d6d17ff05550de7951ae0a07a112a51 browser (cache): new `cache` module to handle cache of roster and identities: the cache is put in local storage and linked to a session. diff -r 683e50799d6d -r ed28ad7d484c libervia/pages/_browser/cache.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libervia/pages/_browser/cache.py Fri Aug 14 09:31:32 2020 +0200 @@ -0,0 +1,156 @@ +from browser import window +from browser.local_storage import storage +from javascript import JSON +from dialog import notification +from bridge import Bridge + +session_uuid = window.session_uuid +bridge = Bridge() + +# XXX: we don't use browser.object_storage because it is affected by +# https://github.com/brython-dev/brython/issues/1467 and mixing local_storage.storage +# and object_storage was resulting in weird behaviour (keys found in one not in the +# other) + + +class Cache: + + def __init__(self): + try: + cache = storage['libervia_cache'] + except KeyError: + self.request_data_from_backend() + else: + cache = JSON.parse(cache) + if cache['metadata']['session_uuid'] != session_uuid: + print("data in cache are not valid for this session, resetting") + del storage['libervia_cache'] + self.request_data_from_backend() + else: + self._cache = cache + print("storage cache is used") + + @property + def roster(self): + return self._cache['roster'] + + @property + def identities(self): + return self._cache['identities'] + + def update(self): + # FIXME: we use window.JSON as a workaround to + # https://github.com/brython-dev/brython/issues/1467 + print(f"updating: {self._cache}") + storage['libervia_cache'] = window.JSON.stringify(self._cache) + print("cache stored") + + def _store_if_complete(self): + self._completed_count -= 1 + if self._completed_count == 0: + del self._completed_count + self.update() + + def getContactsCb(self, contacts): + print("roster received") + roster = self._cache['roster'] + for contact_jid, attributes, groups in contacts: + roster[contact_jid] = { + 'attributes': attributes, + 'groups': groups, + } + self._store_if_complete() + + def identitiesBaseGetCb(self, identities_raw): + print("base identities received") + identities = JSON.parse(identities_raw) + self._cache['identities'].update(identities) + self._store_if_complete() + + def request_failed(self, exc, message): + notification.show(message.format(exc=exc), "error") + self._store_if_complete() + + def request_data_from_backend(self): + self._cache = { + 'metadata': { + "session_uuid": session_uuid, + }, + 'roster': {}, + 'identities': {}, + } + self._completed_count = 2 + print("requesting roster to backend") + bridge.getContacts( + callback=self.getContactsCb, + errback=lambda e: self.request_failed(e, "Can't get contacts: {exc}") + ) + print("requesting base identities to backend") + bridge.identitiesBaseGet( + callback=self.identitiesBaseGetCb, + errback=lambda e: self.request_failed(e, "Can't get base identities: {exc}") + ) + + def _fill_identities_cb(self, new_identities_raw, callback): + new_identities = JSON.parse(new_identities_raw) + print(f"new identities: {new_identities.keys()}") + self._cache['identities'].update(new_identities) + self.update() + if callback: + callback() + + def fill_identities(self, entities, callback=None): + """Check that identities for identites exist, request them otherwise""" + to_get = {e for e in entities if e not in self._cache['identities']} + if to_get: + bridge.identitiesGet( + list(to_get), + ['avatar', 'nicknames'], + callback=lambda identities: self._fill_identities_cb( + identities, callback), + errback=lambda failure_: notification.show( + f"Can't get identities: {failure_}", + "error" + ) + ) + else: + # we already have all identities + print("no missing identity") + if callback: + callback() + + def match_identity(self, entity_jid, text, identity=None): + """Returns True if a text match an entity identity + + identity will be matching if its jid or any of its name contain text + @param entity_jid: jid of the entity to check + @param text: text to use for filtering. Must be in lowercase and stripped + @param identity: identity data + if None, it will be retrieved if jid is not matching + @return: True if entity is matching + """ + if text in entity_jid: + return True + if identity is None: + try: + identity = self.identities[entity_jid] + except KeyError: + print(f"missing identity: {entity_jid}") + return False + return any(text in n.lower() for n in identity['nicknames']) + + def matching_identities(self, text): + """Return identities corresponding to a text + + """ + text = text.lower().strip() + for entity_jid, identity in self._cache['identities'].items(): + if ((text in entity_jid + or any(text in n.lower() for n in identity['nicknames']) + )): + yield entity_jid + + +cache = Cache() +roster = cache.roster +identities = cache.identities diff -r 683e50799d6d -r ed28ad7d484c libervia/server/restricted_bridge.py --- a/libervia/server/restricted_bridge.py Fri Aug 14 09:31:32 2020 +0200 +++ b/libervia/server/restricted_bridge.py Fri Aug 14 09:31:32 2020 +0200 @@ -30,6 +30,21 @@ self.host = host self.security_limit = C.SECURITY_LIMIT + async def getContacts(self, profile): + return await self.host.bridgeCall("getContacts", profile) + + async def identityGet(self, entity, metadata_filter, use_cache, profile): + return await self.host.bridgeCall( + "identityGet", entity, metadata_filter, use_cache, profile) + + async def identitiesGet(self, entities, metadata_filter, profile): + return await self.host.bridgeCall( + "identitiesGet", entities, metadata_filter, profile) + + async def identitiesBaseGet(self, profile): + return await self.host.bridgeCall( + "identitiesBaseGet", profile) + async def fileHTTPUploadGetSlot( self, filename, size, content_type, upload_jid, profile): return await self.host.bridgeCall(