def get(self, sender, sender_ref): # try getting /{sender}/{sender_ref}/content.json # de-serialise it and return it as native message object assert sender and sender_ref # logger.info("Retrieving message %s@%s from the message lake", sender, sender_ref) content_path = "{}/{}/content.json".format( sender, miniorepo.slash_chunk(sender_ref) ) metadata_path = "{}/{}/metadata.json".format( sender, miniorepo.slash_chunk(sender_ref) ) try: msg_content = json.loads( self.get_object_content(content_path) ) except ClientError as ex: if ex.response['Error']['Code'] == 'NoSuchKey': logger.error("Retrieve message at %s: no such key", content_path) return None else: raise try: metadata = json.loads(self.get_object_content(metadata_path)) except ClientError as ex: if ex.response['Error']['Code'] == 'NoSuchKey': logger.error("Retrieve message metadata at %s: no such key", metadata_path) metadata = None else: raise if metadata: for key in [ message.CHANNEL_ID_KEY, message.CHANNEL_TXN_ID_KEY, message.STATUS_KEY ]: # Neketek: I'm not changing approach. # What is undefined in metadata will be set to None. # But honestly it's cleaner to not include these keys at all. # But I'm afraid to break something therefore I leave it as is. msg_content[key] = metadata.get(key) return message.Message.from_dict(msg_content)
def search(self, filters=None): """ # TODO: we may do a faster version of it just to check if given jurisdiction # may do this operation Because post() stores each message at the location representing the object, with a name representing the recipient (not implemented) (plus a timestamp, in case they received messages about the same object more than once) , we are able to scan that location to see who has been granted access to the object. """ # if filters is None: # return False assert filters # shoudln't be used without filters, it's a programming error # we only support one filter for now allowed_filters = ('object__eq', ) for f in filters.keys(): if f not in allowed_filters: # TODO ? raise Exception("unsupported filter {} (not in {})".format( f, allowed_filters)) found_objects = self.client.list_objects( Bucket=self.bucket, Prefix=miniorepo.slash_chunk(filters['object__eq']) + '/', # recursive=True ) if not found_objects.get('Contents', []): return [] else: uniq_jurisdictions = set() for obj in found_objects.get('Contents', []): pure_filename = obj['Key'].split('/')[-1] if obj['Key'].endswith('.json'): # based on rx_message, there is message in this file # oname is something like /QmXx/xxx/xxxx/CN.json jurisdiction_name = pure_filename.split('.')[0] else: # based on uploaded document, the file is empty jurisdiction_name = pure_filename uniq_jurisdictions.add(jurisdiction_name) return [Jurisdiction(c) for c in uniq_jurisdictions]
def update_metadata(self, sender, sender_ref, updates): """ Accepts message details and new metadata fields Updates the metadata in the repo There is no locking here, so last requester wins due to the storage nature """ metadata_path = "{}/{}/metadata.json".format( sender, miniorepo.slash_chunk(sender_ref) ) metadata = json.loads(self.get_object_content(metadata_path)) metadata.update(updates) metadata_rendered = json.dumps(metadata) return self.put_message_related_object( sender=sender, sender_ref=sender_ref, rel_path="/metadata.json", content_body=metadata_rendered )
def test_utils(): assert slash_chunk(URI_LEN_5) == URI_LEN_5 assert slash_chunk(URI_LEN_10) == URI_LEN_10 assert slash_chunk(URI_LEN_15) == f'{URI_LEN_5}/{URI_LEN_10}' assert slash_chunk(URI_LEN_25) == f'{URI_LEN_5}/{URI_LEN_10*2}'