Record native receiver custody and prepare actual Approval lifecycle readback
Assistant: codex Assistant-Model: gpt-6-astra Assistant-Session: 01a13eab-1e6a-7b33-9c37-372e90ada3a2
This commit is contained in:
parent
ce5344da69
commit
6e24b09eaa
8 changed files with 372 additions and 40 deletions
|
|
@ -36,12 +36,15 @@ def stamp(value):
|
|||
|
||||
def paths(source, tenant, instance, since, until, event_ids):
|
||||
require(SCOPES.get(source) == tenant, "exact_source_tenant_required")
|
||||
require(
|
||||
isinstance(instance, str)
|
||||
and 1 <= len(instance) <= 256
|
||||
and instance.strip() == instance,
|
||||
"exact_instance_required",
|
||||
)
|
||||
if source == "qonto-assistant":
|
||||
require(
|
||||
isinstance(instance, str)
|
||||
and 1 <= len(instance) <= 256
|
||||
and instance.strip() == instance,
|
||||
"exact_instance_required",
|
||||
)
|
||||
else:
|
||||
require(instance is None, "approval_has_no_declared_instance_filter")
|
||||
require(
|
||||
stamp(since) <= stamp(until) <= datetime.now(timezone.utc),
|
||||
"invalid_acceptance_window",
|
||||
|
|
@ -57,15 +60,10 @@ def paths(source, tenant, instance, since, until, event_ids):
|
|||
),
|
||||
"invalid_event_id",
|
||||
)
|
||||
query = urlencode(
|
||||
dict(
|
||||
source=source,
|
||||
tenant=tenant,
|
||||
stream_instance_id=instance,
|
||||
since=since,
|
||||
until=until,
|
||||
)
|
||||
)
|
||||
query_values = dict(source=source, tenant=tenant, since=since, until=until)
|
||||
if instance is not None:
|
||||
query_values["stream_instance_id"] = instance
|
||||
query = urlencode(query_values)
|
||||
return ["/v1/reconciliation?" + query, "/v1/stream-findings"] + [
|
||||
"/v1/events/" + x for x in event_ids
|
||||
]
|
||||
|
|
@ -95,11 +93,15 @@ def sanitize_record(record, source, tenant, instance, event_id, since, until):
|
|||
"record_source_tenant_id_mismatch",
|
||||
)
|
||||
detail = record["details"]["data"]
|
||||
require(detail.get("stream_instance_id") == instance, "record_instance_mismatch")
|
||||
require(
|
||||
type(detail.get("stream_sequence")) is int and detail["stream_sequence"] > 0,
|
||||
"record_sequence_missing",
|
||||
)
|
||||
if source == "qonto-assistant":
|
||||
require(
|
||||
detail.get("stream_instance_id") == instance, "record_instance_mismatch"
|
||||
)
|
||||
require(
|
||||
type(detail.get("stream_sequence")) is int
|
||||
and detail["stream_sequence"] > 0,
|
||||
"record_sequence_missing",
|
||||
)
|
||||
accepted = record["accepted_at"]
|
||||
require(
|
||||
stamp(since) <= stamp(accepted) < stamp(until),
|
||||
|
|
@ -118,12 +120,25 @@ def sanitize_record(record, source, tenant, instance, event_id, since, until):
|
|||
)
|
||||
if action == "audit-core.heartbeat":
|
||||
require(coverage == ["audit.deny"], "record_heartbeat_scope_mismatch")
|
||||
if source == "approval-engine":
|
||||
names = {"issuance", "use", "supersession", "revocation"}
|
||||
require(
|
||||
action in {"approval." + name for name in names} | {"audit-core.heartbeat"},
|
||||
"record_undeclared_class",
|
||||
)
|
||||
if action == "audit-core.heartbeat":
|
||||
require(
|
||||
len(coverage) == 1 and coverage[0] in names,
|
||||
"record_heartbeat_scope_mismatch",
|
||||
)
|
||||
return {
|
||||
"event_id": event_id,
|
||||
"source": source,
|
||||
"tenant": tenant,
|
||||
"stream_instance_id": instance,
|
||||
"stream_sequence": detail["stream_sequence"],
|
||||
"stream_sequence": detail.get("stream_sequence")
|
||||
if instance is not None
|
||||
else None,
|
||||
"type": action,
|
||||
"accepted_at": accepted,
|
||||
"observed_at": record["observed_at"],
|
||||
|
|
@ -136,16 +151,11 @@ def readback(origin, token, source, tenant, instance, since, until, event_ids):
|
|||
allowed = paths(source, tenant, instance, since, until, event_ids)
|
||||
reconciliation = read_http(origin, token, allowed[0], allowed)
|
||||
require(
|
||||
all(
|
||||
reconciliation.get(k) == v
|
||||
for k, v in (
|
||||
("source", source),
|
||||
("tenant", tenant),
|
||||
("stream_instance_id", instance),
|
||||
("since", since),
|
||||
("until", until),
|
||||
)
|
||||
),
|
||||
reconciliation.get("source") == source
|
||||
and reconciliation.get("tenant") == tenant
|
||||
and reconciliation.get("stream_instance_id") == instance
|
||||
and stamp(reconciliation["since"]) == stamp(since)
|
||||
and stamp(reconciliation["until"]) == stamp(until),
|
||||
"reconciliation_scope_window_mismatch",
|
||||
)
|
||||
raw_findings = read_http(origin, token, allowed[1], allowed)
|
||||
|
|
@ -183,7 +193,11 @@ def readback(origin, token, source, tenant, instance, since, until, event_ids):
|
|||
"reconciliation": reconciliation,
|
||||
"stream_findings": safe_findings,
|
||||
"events": records,
|
||||
"scope": "bounded same-instance accepted custody; source-global findings require active-instance interpretation",
|
||||
"scope": (
|
||||
"bounded same-instance accepted custody; source-global findings require active-instance interpretation"
|
||||
if instance is not None
|
||||
else "bounded source lifecycle acceptance window; no process-instance completeness claim"
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -192,7 +206,6 @@ def main():
|
|||
p.add_argument("--source", choices=SCOPES, required=True)
|
||||
for key in (
|
||||
"tenant",
|
||||
"instance",
|
||||
"since",
|
||||
"until",
|
||||
"kubeconfig",
|
||||
|
|
@ -200,6 +213,10 @@ def main():
|
|||
"expected-receiver-image",
|
||||
):
|
||||
p.add_argument("--" + key, required=True)
|
||||
p.add_argument(
|
||||
"--instance",
|
||||
help="Required only for Qonto; Approval has no declared instance field",
|
||||
)
|
||||
p.add_argument("--event-id", action="append", required=True)
|
||||
p.add_argument("--receipt", type=Path, required=True)
|
||||
a = p.parse_args()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue