Add lifecycle registration and revisions with atomic packet uploads
Assistant: codex Assistant-Model: gpt-6-astra Assistant-Session: 01a10780-6d41-7952-9718-dfbc1f41236f
This commit is contained in:
parent
7ce1e589f7
commit
ee472acc87
10 changed files with 332 additions and 35 deletions
|
|
@ -10,11 +10,12 @@ import re
|
|||
from urllib.parse import parse_qs
|
||||
|
||||
from .lifecycle_service import Caller, LifecycleError
|
||||
from .lifecycle_commands import decode_memo
|
||||
from .records import dumps
|
||||
from .store import Conflict, EvidenceUnavailable
|
||||
|
||||
MAX_BODY = 256 * 1024
|
||||
ROUTE = re.compile(r"/v1/decisions/([A-Za-z0-9._:-]{1,256})(?:/(events|comments|sources|evaluations))?")
|
||||
ROUTE = re.compile(r"/v1/decisions/([A-Za-z0-9._:-]{1,256})(?:/(events|comments|sources|evaluations|revisions))?")
|
||||
|
||||
|
||||
def _object(pairs):
|
||||
|
|
@ -63,10 +64,12 @@ class LifecycleAPI:
|
|||
except Exception:
|
||||
raise LifecycleError(401, "authentication_failed") from None
|
||||
self.service._caller(caller)
|
||||
match = ROUTE.fullmatch(environ.get("PATH_INFO", ""))
|
||||
if not match:
|
||||
path = environ.get("PATH_INFO", "")
|
||||
match = ROUTE.fullmatch(path)
|
||||
registration = path == "/v1/decisions"
|
||||
if not match and not registration:
|
||||
raise LifecycleError(404, "route_not_found")
|
||||
memo_id, resource = match.groups()
|
||||
memo_id, resource = match.groups() if match else (None, "register")
|
||||
method = environ.get("REQUEST_METHOD", "GET")
|
||||
query = environ.get("QUERY_STRING", "")
|
||||
if len(query) > 2048:
|
||||
|
|
@ -87,7 +90,7 @@ class LifecycleAPI:
|
|||
return 200, {"events": self.service.events(caller, memo_id,
|
||||
after=int(params.get("after", ["0"])[0]), limit=int(params.get("limit", ["100"])[0]))}
|
||||
kinds = {"comments": "comment", "sources": "source", "evaluations": "evaluation"}
|
||||
if method != "POST" or resource not in kinds:
|
||||
if method != "POST" or resource not in {*kinds, "register", "revisions"}:
|
||||
raise LifecycleError(405, "method_not_allowed")
|
||||
if params:
|
||||
raise ValueError()
|
||||
|
|
@ -103,11 +106,27 @@ class LifecycleAPI:
|
|||
raise ValueError()
|
||||
data = json.loads(raw, object_pairs_hook=_object,
|
||||
parse_constant=lambda value: (_ for _ in ()).throw(ValueError()))
|
||||
if not isinstance(data, dict) or set(data) != {"version", "body"}:
|
||||
raise ValueError()
|
||||
key = environ.get("HTTP_IDEMPOTENCY_KEY", "")
|
||||
if not isinstance(key, str) or not key.strip() or len(key) > 256:
|
||||
raise ValueError()
|
||||
if resource in {"register", "revisions"}:
|
||||
required = {"memo"} if registration else {"memo", "expected_version", "summary"}
|
||||
if (not isinstance(data, dict) or not required <= data.keys()
|
||||
or data.keys() - (required | {"documents"})):
|
||||
raise ValueError()
|
||||
memo = decode_memo(data["memo"])
|
||||
documents = data.get("documents", [])
|
||||
if registration:
|
||||
event_id = self.service.register(caller, memo, key=key, documents=documents)
|
||||
else:
|
||||
if memo.id != memo_id:
|
||||
raise ValueError()
|
||||
event_id = self.service.revise(caller, memo, key=key,
|
||||
expected_version=data["expected_version"], summary=data["summary"], documents=documents)
|
||||
return 200, {"event_id": event_id, "memo_id": memo.id, "version": memo.version,
|
||||
"resource": f"/v1/decisions/{memo.id}?version={memo.version}"}
|
||||
if not isinstance(data, dict) or set(data) != {"version", "body"}:
|
||||
raise ValueError()
|
||||
event_id = self.service.annotate(caller, memo_id, data["version"], kind=kinds[resource],
|
||||
body=data["body"], key=key)
|
||||
return 200, {"event_id": event_id, "memo_id": memo_id, "version": data["version"]}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue