feat: prepare ARR for controlled public deployment
This commit is contained in:
305
arr_ingestion/direct_contracts.py
Normal file
305
arr_ingestion/direct_contracts.py
Normal file
@@ -0,0 +1,305 @@
|
||||
"""Strict contracts for one-call ARR direct structured-result ingestion."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import hashlib
|
||||
import json
|
||||
import re
|
||||
from dataclasses import dataclass, field
|
||||
from datetime import date, datetime
|
||||
from typing import Any, Dict, Mapping, Optional
|
||||
|
||||
from arr_ingestion.contracts import (
|
||||
OPAQUE_ID_RE,
|
||||
PROCESSOR_VERSION_RE,
|
||||
RESULT_SCHEMA_VERSION,
|
||||
SHA256_RE,
|
||||
ArtifactRef,
|
||||
IngestionError,
|
||||
)
|
||||
|
||||
|
||||
DIRECT_CONTRACT_VERSION = "arr-direct-ingestion-1"
|
||||
MAX_DIRECT_PAYLOAD_BYTES = 3 * 1024 * 1024
|
||||
DIRECT_REQUEST_FIELDS = {
|
||||
"submission_grant",
|
||||
"job_id",
|
||||
"attempt_no",
|
||||
"payload",
|
||||
}
|
||||
DIRECT_RECEIPT_FIELDS = {
|
||||
"contract_version",
|
||||
"status",
|
||||
"job_id",
|
||||
"attempt_no",
|
||||
"business_date",
|
||||
"daily_version_id",
|
||||
"version_no",
|
||||
"record_count",
|
||||
}
|
||||
GRANT_TOKEN_RE = re.compile(r"^[A-Za-z0-9_-]{32,128}$")
|
||||
|
||||
|
||||
def _fail(code: str, message: str) -> None:
|
||||
raise IngestionError(code, message)
|
||||
|
||||
|
||||
def canonical_json_bytes(value: Mapping[str, Any]) -> bytes:
|
||||
"""Return the one canonical encoding used for hashing and persistence."""
|
||||
|
||||
try:
|
||||
return json.dumps(
|
||||
value,
|
||||
ensure_ascii=False,
|
||||
allow_nan=False,
|
||||
sort_keys=True,
|
||||
separators=(",", ":"),
|
||||
).encode("utf-8")
|
||||
except (TypeError, ValueError):
|
||||
_fail(
|
||||
"DIRECT_SUBMISSION_INVALID",
|
||||
"direct result payload is not strict JSON",
|
||||
)
|
||||
|
||||
|
||||
def _strict_mapping(
|
||||
value: Any,
|
||||
expected: set[str],
|
||||
label: str,
|
||||
*,
|
||||
error_code: str = "DIRECT_SUBMISSION_INVALID",
|
||||
) -> Mapping[str, Any]:
|
||||
if not isinstance(value, Mapping) or set(value) != expected:
|
||||
_fail(
|
||||
error_code,
|
||||
f"{label} field set is invalid",
|
||||
)
|
||||
return value
|
||||
|
||||
|
||||
def _parse_business_date(
|
||||
value: Any,
|
||||
*,
|
||||
error_code: str = "DIRECT_SUBMISSION_INVALID",
|
||||
error_message: str = "direct result business date is invalid",
|
||||
) -> date:
|
||||
if not isinstance(value, str):
|
||||
_fail(error_code, error_message)
|
||||
try:
|
||||
return date.fromisoformat(value)
|
||||
except ValueError:
|
||||
_fail(error_code, error_message)
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class DirectSubmissionRequest:
|
||||
"""Validated transport metadata plus a detached strict-JSON payload."""
|
||||
|
||||
submission_grant: str = field(repr=False)
|
||||
job_id: str
|
||||
attempt_no: int
|
||||
payload: Mapping[str, Any] = field(repr=False)
|
||||
payload_bytes: bytes = field(repr=False)
|
||||
payload_sha256: str
|
||||
business_date: date
|
||||
processor_version: str
|
||||
rule_set_sha256: str
|
||||
result_schema_version: str
|
||||
record_count: int
|
||||
|
||||
@classmethod
|
||||
def from_dict(cls, raw: Any) -> "DirectSubmissionRequest":
|
||||
values = _strict_mapping(raw, DIRECT_REQUEST_FIELDS, "direct submission")
|
||||
grant = values.get("submission_grant")
|
||||
job_id = values.get("job_id")
|
||||
attempt_no = values.get("attempt_no")
|
||||
payload = values.get("payload")
|
||||
if not isinstance(grant, str) or not GRANT_TOKEN_RE.fullmatch(grant):
|
||||
_fail(
|
||||
"SUBMISSION_GRANT_INVALID",
|
||||
"submission grant is invalid",
|
||||
)
|
||||
if not isinstance(job_id, str) or not OPAQUE_ID_RE.fullmatch(job_id):
|
||||
_fail(
|
||||
"DIRECT_SUBMISSION_INVALID",
|
||||
"direct submission job identifier is invalid",
|
||||
)
|
||||
if (
|
||||
not isinstance(attempt_no, int)
|
||||
or isinstance(attempt_no, bool)
|
||||
or not 1 <= attempt_no <= 9999
|
||||
):
|
||||
_fail(
|
||||
"DIRECT_SUBMISSION_INVALID",
|
||||
"direct submission attempt number is invalid",
|
||||
)
|
||||
if not isinstance(payload, Mapping):
|
||||
_fail(
|
||||
"DIRECT_SUBMISSION_INVALID",
|
||||
"direct result payload must be an object",
|
||||
)
|
||||
payload_bytes = canonical_json_bytes(payload)
|
||||
if not 2 <= len(payload_bytes) <= MAX_DIRECT_PAYLOAD_BYTES:
|
||||
_fail(
|
||||
"DIRECT_PAYLOAD_TOO_LARGE",
|
||||
"direct result payload exceeds its size limit",
|
||||
)
|
||||
detached = json.loads(payload_bytes.decode("utf-8"))
|
||||
if (
|
||||
detached.get("status") != "success"
|
||||
or detached.get("activation_eligible") is not True
|
||||
):
|
||||
_fail(
|
||||
"DIRECT_RESULT_NOT_ACTIVATABLE",
|
||||
"direct result is not eligible for Finance activation",
|
||||
)
|
||||
processor_version = detached.get("processor_version")
|
||||
rule_set_sha256 = detached.get("rule_set_sha256")
|
||||
result_schema_version = detached.get("result_schema_version")
|
||||
records = detached.get("records")
|
||||
if (
|
||||
not isinstance(processor_version, str)
|
||||
or not PROCESSOR_VERSION_RE.fullmatch(processor_version)
|
||||
or not isinstance(rule_set_sha256, str)
|
||||
or not SHA256_RE.fullmatch(rule_set_sha256)
|
||||
or result_schema_version != RESULT_SCHEMA_VERSION
|
||||
or not isinstance(records, list)
|
||||
):
|
||||
_fail(
|
||||
"DIRECT_SUBMISSION_INVALID",
|
||||
"direct result identity is invalid",
|
||||
)
|
||||
return cls(
|
||||
submission_grant=grant,
|
||||
job_id=job_id,
|
||||
attempt_no=attempt_no,
|
||||
payload=detached,
|
||||
payload_bytes=payload_bytes,
|
||||
payload_sha256=hashlib.sha256(payload_bytes).hexdigest(),
|
||||
business_date=_parse_business_date(detached.get("business_date")),
|
||||
processor_version=processor_version,
|
||||
rule_set_sha256=rule_set_sha256,
|
||||
result_schema_version=result_schema_version,
|
||||
record_count=len(records),
|
||||
)
|
||||
|
||||
@property
|
||||
def grant_sha256(self) -> str:
|
||||
return hashlib.sha256(self.submission_grant.encode("utf-8")).hexdigest()
|
||||
|
||||
@property
|
||||
def submission_key(self) -> str:
|
||||
identity = f"{self.job_id}\x00{self.attempt_no}".encode("utf-8")
|
||||
return "direct:" + hashlib.sha256(identity).hexdigest()
|
||||
|
||||
def payload_json(self) -> str:
|
||||
return self.payload_bytes.decode("utf-8")
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class DirectSubmissionReceipt:
|
||||
status: str
|
||||
job_id: str
|
||||
attempt_no: int
|
||||
business_date: date
|
||||
daily_version_id: int
|
||||
version_no: int
|
||||
record_count: int
|
||||
|
||||
@classmethod
|
||||
def from_dict(cls, raw: Any) -> "DirectSubmissionReceipt":
|
||||
values = _strict_mapping(
|
||||
raw,
|
||||
DIRECT_RECEIPT_FIELDS,
|
||||
"stored direct receipt",
|
||||
error_code="DATABASE_STATE_INVALID",
|
||||
)
|
||||
if values.get("contract_version") != DIRECT_CONTRACT_VERSION:
|
||||
_fail(
|
||||
"DATABASE_STATE_INVALID",
|
||||
"stored direct receipt contract is invalid",
|
||||
)
|
||||
status = values.get("status")
|
||||
job_id = values.get("job_id")
|
||||
attempt_no = values.get("attempt_no")
|
||||
daily_version_id = values.get("daily_version_id")
|
||||
version_no = values.get("version_no")
|
||||
record_count = values.get("record_count")
|
||||
if (
|
||||
status not in {"committed", "already_committed"}
|
||||
or not isinstance(job_id, str)
|
||||
or not OPAQUE_ID_RE.fullmatch(job_id)
|
||||
or not isinstance(attempt_no, int)
|
||||
or isinstance(attempt_no, bool)
|
||||
or attempt_no < 1
|
||||
or not isinstance(daily_version_id, int)
|
||||
or isinstance(daily_version_id, bool)
|
||||
or daily_version_id < 1
|
||||
or not isinstance(version_no, int)
|
||||
or isinstance(version_no, bool)
|
||||
or version_no < 1
|
||||
or not isinstance(record_count, int)
|
||||
or isinstance(record_count, bool)
|
||||
or record_count < 0
|
||||
):
|
||||
_fail(
|
||||
"DATABASE_STATE_INVALID",
|
||||
"stored direct receipt is invalid",
|
||||
)
|
||||
return cls(
|
||||
status=str(status),
|
||||
job_id=job_id,
|
||||
attempt_no=attempt_no,
|
||||
business_date=_parse_business_date(
|
||||
values.get("business_date"),
|
||||
error_code="DATABASE_STATE_INVALID",
|
||||
error_message="stored direct receipt business date is invalid",
|
||||
),
|
||||
daily_version_id=daily_version_id,
|
||||
version_no=version_no,
|
||||
record_count=record_count,
|
||||
)
|
||||
|
||||
def to_dict(self) -> Dict[str, Any]:
|
||||
return {
|
||||
"contract_version": DIRECT_CONTRACT_VERSION,
|
||||
"status": self.status,
|
||||
"job_id": self.job_id,
|
||||
"attempt_no": self.attempt_no,
|
||||
"business_date": self.business_date.isoformat(),
|
||||
"daily_version_id": self.daily_version_id,
|
||||
"version_no": self.version_no,
|
||||
"record_count": self.record_count,
|
||||
}
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class SubmissionGrant:
|
||||
submission_grant: str = field(repr=False)
|
||||
job_id: str
|
||||
attempt_no: int
|
||||
expires_at: datetime
|
||||
|
||||
def to_dict(self) -> Dict[str, Any]:
|
||||
return {
|
||||
"contract_version": DIRECT_CONTRACT_VERSION,
|
||||
"submission_grant": self.submission_grant,
|
||||
"job_id": self.job_id,
|
||||
"attempt_no": self.attempt_no,
|
||||
"expires_at": self.expires_at.isoformat(),
|
||||
}
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class ReceivedDirectSubmission:
|
||||
submission_id: int
|
||||
status: str
|
||||
request: DirectSubmissionRequest
|
||||
source: ArtifactRef
|
||||
receipt: Optional[DirectSubmissionReceipt] = None
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class VerifiedDirectSubmission:
|
||||
submission: ReceivedDirectSubmission
|
||||
replay_payload_sha256: str
|
||||
Reference in New Issue
Block a user