Source code for keri.app.connecting

# -*- encoding: utf-8 -*-
"""
keri.app.connecting module

"""
import re
import json

from ordered_set import OrderedSet as oset

from keri import kering


[docs] class Organizer: """ Organizes contacts relating contact information to AIDs """
[docs] def __init__(self, hby): """ Create contact Organizer Parameters: hby (Habery): database environment for contact information """ self.hby = hby
[docs] def update(self, pre, data): """ Add or update contact information in data for the identifier prefix Parameters: pre (str): qb64 identifier prefix of contact information to update data (dict): data to add to or update in contact information """ existing = self.get(pre) if existing is None: existing = dict() existing |= data raw = json.dumps(existing).encode("utf-8") cigar = self.hby.signator.sign(ser=raw) self.hby.db.ccigs.pin(keys=(pre,), val=cigar) self.hby.db.cons.pin(keys=(pre,), val=raw) for field, val in data.items(): self.hby.db.cfld.pin(keys=(pre, field), val=val)
[docs] def replace(self, pre, data): """ Replace all contact information for identifier prefix with data Parameters: pre (str): qb64 identifier prefix of contact information to replace data (dict): data to replace contact information with """ self.rem(pre) self.update(pre, data)
[docs] def set(self, pre, field, val): """ Add or replace one value in contact information for identifier prefix Parameters: pre (str): qb64 identifier prefix for contact field (str): field to set val (Union[str,bytes]): data value """ data = self.get(pre) or dict() data[field] = val self.replace(pre, data) self.hby.db.cfld.pin(keys=(pre, field), val=val)
[docs] def unset(self, pre, field): """ Remove field from contact information for identifier prefix Parameters: pre (str): qb64 identifier prefix for contact field (str): field to remove """ data = self.get(pre) del data[field] self.replace(pre, data) self.hby.db.cfld.rem(keys=(pre, field))
[docs] def rem(self, pre): """ Remove all contact information for identifier prefix Parameters: pre (str): qb64 identifier prefix for contact to remove Returns: """ self.hby.db.ccigs.rem(keys=(pre,)) self.hby.db.cons.rem(keys=(pre,)) return self.hby.db.cfld.trim(keys=(pre,))
[docs] def get(self, pre, field=None): """ Retrieve all contact information for identifier prefix Parameters: pre (str): qb64 identifier prefix for contact field (str): optional field name to retrieve a single field value Returns: dict: Contact data """ raw = self.hby.db.cons.get(keys=(pre,)) if raw is None: return None cigar = self.hby.db.ccigs.get(keys=(pre,)) if not self.hby.signator.verify(ser=raw.encode("utf-8"), cigar=cigar): raise kering.ValidationError(f"failed signature on {pre} contact data") data = json.loads(raw) if data is None: return None if field is not None: return data[field] if field in data else None data["id"] = pre return data
[docs] def list(self): """ Return list of all contact information for all remote identifiers Returns: list: All contact information """ key = "" data = None contacts = [] for (pre, field), val in self.hby.db.cfld.getItemIter(): if pre != key: if data is not None: contacts.append(data) data = dict(id=pre) key = pre data[field] = val if data is not None: contacts.append(data) return contacts
[docs] def find(self, field, val): """ Find all contact information for all contacts that have the val in field Parameters: field (str): field name to search for val (Union[str,bytes,list]): value to search for Returns: list: All contacts that match the val in field """ pres = [] prog = re.compile(f".*{val}.*", re.I) for (pre, f), v in self.hby.db.cfld.getItemIter(): if f == field and prog.match(v): pres.append(pre) return [self.get(pre) for pre in pres]
[docs] def values(self, field, val=None): """ Find unique values for field in all contacts Args: field (str): field to load values for val (Optional(str|None): optional filter for the value of the grouped field Returns: list: Unique values from all contacts for field """ prog = re.compile(f".*{val}.*", re.I) if val is not None else None vals = oset() for (pre, f), v in self.hby.db.cfld.getItemIter(): if f == field: if prog is None or prog.match(v): vals.add(v) return list(vals)
[docs] def setImg(self, pre, typ, stream): """ Upload image for identifier prefix Streams image data in 4k chunks into database and sets content type and content length. Performs a full replace of all data for image of specified identifier Parameters: pre (str): qb64 identifier prefix for image typ (str): image content mime type stream (file): file-like stream of image data """ self.hby.db.delTopVal(db=self.hby.db.imgs, key=pre.encode("utf-8")) key = f"{pre}.content-type".encode("utf-8") self.hby.db.setVal(db=self.hby.db.imgs, key=key, val=typ.encode("utf-8")) idx = 0 size = 0 while True: chunk = stream.read(4096) if not chunk: break key = f"{pre}.{idx}".encode("utf-8") self.hby.db.setVal(db=self.hby.db.imgs, key=key, val=chunk) idx += 1 size += len(chunk) key = f"{pre}.content-length".encode("utf-8") self.hby.db.setVal(db=self.hby.db.imgs, key=key, val=size.to_bytes(4, "big"))
[docs] def getImgData(self, pre): """ Get image metadata for identifier image if one exists Parameters: pre (str): qb64 identifier prefix for image Returns: dict: image metadata including length and type """ key = f"{pre}.content-length".encode("utf-8") size = self.hby.db.getVal(db=self.hby.db.imgs, key=key) if size is None: return None key = f"{pre}.content-type".encode("utf-8") typ = self.hby.db.getVal(db=self.hby.db.imgs, key=key) if typ is None: return None return dict( type=bytes(typ).decode("utf-8"), length=int.from_bytes(size, "big") )
[docs] def getImg(self, pre): """ Generator that yields image data in 4k chunks for identifier Parameters: pre (str): qb64 identifier prefix for image """ idx = 0 while True: key = f"{pre}.{idx}".encode("utf-8") chunk = self.hby.db.getVal(db=self.hby.db.imgs, key=key) if not chunk: break yield bytes(chunk) idx += 1