Skip to content

Commit

Permalink
Test coverage for new endpoints and updated code
Browse files Browse the repository at this point in the history
  • Loading branch information
pfeairheller committed Aug 9, 2023
1 parent 5e729b9 commit 8aeac30
Show file tree
Hide file tree
Showing 12 changed files with 548 additions and 245 deletions.
107 changes: 48 additions & 59 deletions src/keria/app/aiding.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,14 @@
"""
import json
import time
from dataclasses import asdict
from urllib.parse import urlparse

import falcon
from keri import kering
from keri.app import habbing
from keri.app.keeping import Algos
from keri.core import coring
from keri.core import coring, eventing
from keri.core.coring import Ilks
from keri.db import dbing
from keri.help import ogler
Expand All @@ -25,8 +24,8 @@


def loadEnds(app, agency, authn):
agentEnd = AgentResourceEnd(agency=agency, authn=authn)
app.add_route("/agent/{caid}", agentEnd)
groupEnd = AgentResourceEnd(agency=agency, authn=authn)
app.add_route("/agent/{caid}", groupEnd)

aidsEnd = IdentifierCollectionEnd()
app.add_route("/identifiers", aidsEnd)
Expand Down Expand Up @@ -60,8 +59,8 @@ def loadEnds(app, agency, authn):
contactImgEnd = ContactImageResourceEnd()
app.add_route("/contacts/{prefix}/img", contactImgEnd)

agentEnd = GroupMemberCollectionEnd()
app.add_route("/identifiers/{name}/members", agentEnd)
groupEnd = GroupMemberCollectionEnd()
app.add_route("/identifiers/{name}/members", groupEnd)

return aidEnd

Expand Down Expand Up @@ -128,7 +127,7 @@ def on_put(self, req, rep, caid):
"""
agent = self.agency.get(caid)
if agent is None:
raise falcon.HTTPNotFound(f"no agent for {caid}")
raise falcon.HTTPNotFound(description=f"no agent for {caid}")

typ = req.params.get("type")
if typ == "ixn":
Expand Down Expand Up @@ -626,7 +625,7 @@ def on_get(req, rep, name):
for wit in hab.kever.wits:
urls = hab.fetchUrls(eid=wit, scheme=kering.Schemes.http)
if not urls:
raise falcon.HTTPNotFound(f"unable to query witness {wit}, no http endpoint")
raise falcon.HTTPNotFound(description=f"unable to query witness {wit}, no http endpoint")

up = urlparse(urls[kering.Schemes.http])
oobis.append(f"{kering.Schemes.http}://{up.hostname}:{up.port}/oobi/{hab.pre}/witness/{wit}")
Expand All @@ -642,6 +641,9 @@ def on_get(req, rep, name):
res["oobis"] = oobis
elif role in (kering.Roles.agent,): # Fetch URL OOBIs for all witnesses
roleUrls = hab.fetchRoleUrls(cid=hab.pre, role=kering.Roles.agent, scheme=kering.Schemes.http)
if kering.Roles.agent not in roleUrls:
raise falcon.HTTPNotFound(description=f"unable to query agent roles for {hab.pre}, no http endpoint")

aoobis = roleUrls[kering.Roles.agent]

oobis = list()
Expand Down Expand Up @@ -670,12 +672,12 @@ def on_get(req, rep, name=None, aid=None, role=None):
if name is not None:
hab = agent.hby.habByName(name)
if hab is None:
raise falcon.errors.HTTPNotFound(f"invalid alias {name}")
raise falcon.errors.HTTPNotFound(description=f"invalid alias {name}")
pre = hab.pre
elif aid is not None:
pre = aid
else:
raise falcon.HTTPBadRequest("either `aid` or `name` are required in the path")
raise falcon.HTTPBadRequest(description="either `aid` or `name` are required in the path")

if role is not None:
keys = (pre, role,)
Expand Down Expand Up @@ -703,7 +705,7 @@ def on_post(req, rep, name, aid=None, role=None):
"""
if role is not None or aid is not None:
raise falcon.HTTPNotFound("route not found")
raise falcon.HTTPNotFound(description="route not found")

agent = req.context.agent
body = req.get_media()
Expand All @@ -719,22 +721,40 @@ def on_post(req, rep, name, aid=None, role=None):

hab = agent.hby.habByName(name)
if hab is None:
raise falcon.errors.HTTPNotFound(f"invalid alias {name}")
raise falcon.errors.HTTPNotFound(description=f"invalid alias {name}")

if pre != hab.pre:
raise falcon.errors.HTTPBadRequest(description=f"error trying to create end role for unknown local AID {pre}")
raise falcon.errors.HTTPBadRequest(
description=f"error trying to create end role for unknown local AID {pre}")

rsigers = [coring.Siger(qb64=rsig) for rsig in rsigs]
tsg = (hab.kever.prefixer, coring.Seqner(sn=hab.kever.sn), hab.kever.serder.saider, rsigers)
agent.hby.rvy.processReply(rserder, tsgs=[tsg])

msg = hab.loadEndRole(cid=pre, role=role, eid=eid)
if msg is None:
raise falcon.errors.HTTPBadRequest(description=f"invalid end role rpy={rserder.ked}")
try:
agent.hby.rvy.processReply(rserder, tsgs=[tsg])
except kering.UnverifiedReplyError:
pass

if isinstance(hab, habbing.SignifyGroupHab):
seal = eventing.SealEvent(i=hab.kever.prefixer.qb64,
s=hex(hab.kever.lastEst.s),
d=hab.kever.lastEst.d)
msg = eventing.messagize(serder=rserder,
sigers=rsigers,
seal=seal,
pipelined=True)
atc = bytes(msg[rserder.size:])

others = [smid for smid in hab.db.signingMembers(hab.pre) if smid != hab.mhab.pre]
for o in others:
agent.postman.send(hab=agent.agentHab, dest=o, topic="multisig", serder=rserder,
attachment=atc)

oid = ".".join([pre, role, eid])
op = agent.monitor.submit(oid, longrunning.OpTypes.endrole, metadata=dict(cid=pre, role=role, eid=eid))

rep.status = falcon.HTTP_200
rep.content_type = "application/json"
rep.data = rserder.raw
rep.status = falcon.HTTP_202
rep.data = op.to_json().encode("utf-8")


class EndRoleResourceEnd:
Expand All @@ -744,16 +764,22 @@ def on_delete(self, req, rep):


class RpyEscrowCollectionEnd:

@staticmethod
def on_get(req, rep):
agent = req.context.agent

# Optional Route parameter
route = req.params.get("route")
keys = (route,) if route is not None else ()
events = []
for saider in agent.hby.db.rpes.get(keys=keys):
serder = agent.hby.db.rpys.get(keys=(saider.qb64,))
events.append(serder.ked)

rep.set_header('Content-Type', "text/event-stream")
rep.set_header("Content-Type", "application/json")
rep.status = falcon.HTTP_200
rep.stream = ReplyEscrowIterable(db=agent.hby.db, route=route)
rep.data = json.dumps(events).encode("utf-8")


class ChallengeCollectionEnd:
Expand Down Expand Up @@ -1312,43 +1338,6 @@ def on_delete(req, rep, prefix):
rep.status = falcon.HTTP_202


class ReplyEscrowIterable:
TimeoutMBX = 300000

def __init__(self, db, route=None, retry=5000):
self.db = db
self.keys = (route,) if route is not None else ()
self.retry = retry
self.sent = []

def __iter__(self):
self.start = self.end = time.perf_counter()
return self

def __next__(self):
if self.end - self.start < self.TimeoutMBX:
if self.start == self.end:
self.end = time.perf_counter()
return bytearray(f"retry: {self.retry}\n\n".encode("utf-8"))

data = bytearray()
for saider in self.db.rpes.get(keys=self.keys):
if saider.qb64 in self.sent: # Send each event only once per connection
continue

serder = self.db.rpys.get(keys=(saider.qb64,))
data.extend(bytearray("id: {}\nevent: {}\nretry: {}\ndata: ".format(serder.said, serder.said, self.retry)
.encode("utf-8")))
data.extend(serder.raw)
data.extend(b'\n\n')
self.sent.append(serder.said)
self.start = time.perf_counter()
self.end = time.perf_counter()
return data

raise StopIteration


class GroupMemberCollectionEnd:

@staticmethod
Expand Down
8 changes: 8 additions & 0 deletions src/keria/app/watching.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# -*- encoding: utf-8 -*-
"""
KERIA
keria.app.watching module
"""


5 changes: 4 additions & 1 deletion src/keria/core/httping.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,11 @@ def parseRangeHeader(header, name, start=0, end=9):

header = header.strip(f"{name}=")
try:
if header.startswith("-"):
return start, int(header[1:])

if header.endswith("-"):
return int(header[:-1]), -1
return int(header[:-1]), end

vals = header.split("-")
if not len(vals) == 2:
Expand Down
24 changes: 21 additions & 3 deletions src/keria/core/longrunning.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,10 @@
from keri.help import helping

# long running operationt types
Typeage = namedtuple("Tierage", 'oobi witness delegation group query registry credential done')
Typeage = namedtuple("Tierage", 'oobi witness delegation group query registry credential endrole done')

OpTypes = Typeage(oobi="oobi", witness='witness', delegation='delegation', group='group', query='query',
registry='registry', credential='credential', done='done')
registry='registry', credential='credential', endrole='endrole', done='done')


@dataclass_json
Expand Down Expand Up @@ -312,6 +312,24 @@ def status(self, op):
else:
operation.done = False

elif op.type in (OpTypes.endrole, ):
if "cid" not in op.metadata or "role" not in op.metadata or "eid" not in op.metadata:
raise kering.ValidationError(
f"invalid long running {op.type} operaiton, metadata missing 'ced' field")

cid = op.metadata['cid']
role = op.metadata['role']
eid = op.metadata['eid']

end = self.hby.db.ends.get(keys=(cid, role, eid))
if end and (end.enabled or end.allowed):
saider = self.hby.db.eans.get(keys=(cid, role, eid))
serder = self.hby.db.rpys.get(keys=(saider.qb64,))
operation.done = True
operation.response = serder.ked
else:
operation.done = False

elif op.type in (OpTypes.done, ):
operation.done = True
operation.response = op.metadata["response"]
Expand Down Expand Up @@ -363,7 +381,7 @@ def on_delete(req, rep, name):

agent = req.context.agent
if agent.monitor.get(name) is None:
raise falcon.HTTPNotFound(f"long running operation '{name}' not found")
raise falcon.HTTPNotFound(title=f"long running operation '{name}' not found")

deleted = agent.monitor.rem(name)
if deleted:
Expand Down
5 changes: 5 additions & 0 deletions src/keria/db/basing.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,8 +90,13 @@ def reopen(self, **kwa):
# Names end with "." as sub DB name must include a non Base64 character
# to avoid namespace collisions with Base64 identifier prefixes.

# Sub-database keyed by qb64 controller AID mapping to the Prefixer object of the AID of an agent
self.agnt = subing.CesrSuber(db=self, subkey='agnt.', klas=coring.Prefixer)

# Sub-database keyed by qb64 agent AID mapping to the Prefixer object of the AID of an controller
self.ctrl = subing.CesrSuber(db=self, subkey='ctrl.', klas=coring.Prefixer)

# Sub-database keyed by qb64 AID mapping to the Prefixer object of the AID of its Agent
self.aids = subing.CesrSuber(db=self,
subkey='aids.',
klas=coring.Prefixer)
Expand Down
7 changes: 4 additions & 3 deletions src/keria/testing/testing_helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -455,7 +455,8 @@ def createEndRole(client, agent, recp, name, salt):
body = dict(rpy=rpy.ked, sigs=sigs)

res = client.simulate_post(path=f"/identifiers/{name}/endroles", json=body)
ked = res.json
op = res.json
ked = op["response"]
serder = coring.Serder(ked=ked)
assert serder.raw == rpy.raw

Expand Down Expand Up @@ -499,8 +500,8 @@ def createRegistry(client, agent, salt, doist, deeds):
return registries[0], issuer.json

@staticmethod
def endrole(cid, eid):
data = dict(cid=cid, role="agent", eid=eid)
def endrole(cid, eid, role="agent"):
data = dict(cid=cid, role=role, eid=eid)
return eventing.reply(route="/end/role/add", data=data)

@staticmethod
Expand Down
2 changes: 2 additions & 0 deletions tests/app/test_agenting.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,8 @@ def test_agency():
# Rcreate the agency to see if agent is reloaded from disk
agency = agenting.Agency(name="agency", base=base, bran=None, configFile="keria",
configDir="scripts")
doist.enter(doers=[agency])

agent = agency.get(caid)
assert agent.pre == "EEXekkGu9IAzav6pZVJhkLnjtjM5v3AcyA-pdKUcaGei"

Expand Down
Loading

0 comments on commit 8aeac30

Please sign in to comment.