Assistant: codex Assistant-Model: gpt-6.1-sol Assistant-Session: 01a1029c-c003-7283-acc9-930b434e6cae
57 lines
3.1 KiB
Python
57 lines
3.1 KiB
Python
"""Bounded delivery to State Hub's retained memo-notification receipts."""
|
|
import hashlib
|
|
import json
|
|
import re
|
|
import uuid
|
|
|
|
from .http_transport import JSONTransport, fixed_origin
|
|
|
|
|
|
class HubDelivery:
|
|
def __init__(self, origin, *, transport=None):
|
|
self.origin = fixed_origin(origin, allow_internal_http=True)
|
|
self.transport = transport or JSONTransport(allow_internal_http=True)
|
|
|
|
def _request(self, method, url, payload, key):
|
|
body = json.dumps(payload, sort_keys=True, separators=(',', ':'), allow_nan=False).encode()
|
|
return self.transport.request(method, url, body=body if method == 'POST' else None,
|
|
headers={'Content-Type': 'application/json', 'Idempotency-Key': key,
|
|
'X-StateHub-Source-Agent': 'informed-decision'})
|
|
|
|
def notify(self, delivery_id, metadata):
|
|
uuid.UUID(delivery_id)
|
|
if set(metadata) != {'memo_id', 'question', 'memo_class', 'submitter'}:
|
|
raise ValueError('invalid notification metadata')
|
|
if any(not isinstance(v, str) or not v or len(v) > 4096 for v in metadata.values()):
|
|
raise ValueError('invalid notification metadata')
|
|
if not re.fullmatch(r'[A-Za-z0-9][A-Za-z0-9._:-]{0,255}', metadata['memo_id']):
|
|
raise ValueError('invalid memo id')
|
|
status, receipt = self._request('POST', self.origin + '/informed-decision/notifications',
|
|
metadata, delivery_id)
|
|
# A queued relay receipt is pending, not successful central delivery.
|
|
if status not in (200, 201) or not isinstance(receipt, dict):
|
|
return False
|
|
try:
|
|
uuid.UUID(receipt['message_id'])
|
|
except (ValueError, TypeError, KeyError, AttributeError):
|
|
return False
|
|
return (receipt.get('delivery_id') == delivery_id and receipt.get('status') == 'accepted'
|
|
and receipt.get('authoritative_review') == 'signed-in-overview'
|
|
and all(receipt.get(k) == v for k, v in metadata.items()))
|
|
|
|
def resolution(self, method, url, payload):
|
|
suffix = '/resolve' if method == 'POST' else ''
|
|
pattern = r'/decisions/[0-9a-f]{8}(?:-[0-9a-f]{4}){3}-[0-9a-f]{12}' + suffix
|
|
if (method not in ('POST', 'GET') or not url.startswith(self.origin + '/decisions/')
|
|
or not re.fullmatch(pattern, url[len(self.origin):])):
|
|
raise ValueError('invalid hub resolution route')
|
|
material = json.dumps([method, url, payload], sort_keys=True, separators=(',', ':')).encode()
|
|
status, receipt = self._request(method, url, payload,
|
|
'infd-resolution:' + hashlib.sha256(material).hexdigest())
|
|
if 200 <= status < 300:
|
|
ident = url[len(self.origin + '/decisions/'):].split('/')[0]
|
|
if (status != 200 or not isinstance(receipt, dict) or receipt.get('id') != ident
|
|
or (method == 'POST' and (receipt.get('status') != 'resolved'
|
|
or any(receipt.get(k) != payload[k] for k in ('rationale', 'decided_by'))))):
|
|
return 503, {} # Queued or mismatched response is not a resolution receipt.
|
|
return status, receipt
|