Add governed public form intake

This commit is contained in:
2026-08-06 12:42:20 +02:00
parent d4bbdd079f
commit 3c292b8c07
25 changed files with 4715 additions and 47 deletions
+182 -1
View File
@@ -1,6 +1,7 @@
from __future__ import annotations
from collections.abc import Mapping, Sequence
from dataclasses import replace
from datetime import UTC, date, datetime
import hashlib
import json
@@ -38,6 +39,7 @@ from govoplan_forms_runtime.backend.db.models import (
FormInstanceRevision,
)
from govoplan_forms_runtime.backend.domain import FormInstance
from govoplan_forms_runtime.backend.evidence import FormEvidenceCoordinator
CAPABILITY_FORMS_RUNTIME_REGISTRY = "forms_runtime.registry"
@@ -82,6 +84,7 @@ class FormRuntimePolicyEvaluator(Protocol):
class FormRuntimeService:
def __init__(self, registry: object | None) -> None:
self._registry = registry
self._evidence = FormEvidenceCoordinator(registry)
def create_instance(
self,
@@ -200,6 +203,27 @@ class FormRuntimeService:
changed_by=actor_id,
metadata=dict(metadata or {}),
)
evidence_diagnostics, evidence_snapshots = self._inspect_evidence(
session,
principal,
instance=instance,
definition=definition,
values=clean_values,
attachment_refs=attachments,
signature_refs=signatures,
purpose="start Form instance",
final=False,
observed_at=recorded_at,
)
if evidence_diagnostics or evidence_snapshots:
instance = replace(
instance,
validation_results=(*diagnostics, *evidence_diagnostics),
metadata={
**dict(instance.metadata),
"evidence_verification": list(evidence_snapshots),
},
)
return _record_instance(
session,
principal,
@@ -619,6 +643,111 @@ class FormRuntimeService:
for row in rows
)
def create_evidence_grant(
self,
session: Session,
principal: object,
*,
instance_id: str,
expected_revision: int,
provider_id: str,
custodian_ref: str,
purpose: str,
idempotency_key: str,
expires_at: datetime,
max_size_bytes: int | None = None,
allowed_content_types: Sequence[str] = (),
attachment_refs: Sequence[EvidenceReference] = (),
allow_all: bool = False,
):
current, _identity = _current_instance(
session,
principal,
instance_id=instance_id,
lock=True,
allow_all=allow_all,
)
if current.revision != expected_revision:
raise FormRuntimeError(
"Form instance revision conflict: the expected revision is stale."
)
definition = self._definition(
session,
principal,
reference=current.definition_ref,
effective_at=current.recorded_at,
)
try:
return self._evidence.create_upload_grant(
session,
principal,
instance=current,
definition=definition,
provider_id=provider_id,
custodian_ref=custodian_ref,
purpose=purpose,
idempotency_key=idempotency_key,
expires_at=expires_at,
max_size_bytes=max_size_bytes,
allowed_content_types=allowed_content_types,
attachment_refs=attachment_refs,
)
except ValueError as exc:
raise FormRuntimeError(str(exc)) from exc
def acknowledge(
self,
session: Session,
principal: object,
*,
instance_id: str,
expected_revision: int,
statement_id: str,
statement_version: str,
values: Mapping[str, object],
attachment_refs: Sequence[EvidenceReference],
accepted_at: datetime,
idempotency_key: str,
allow_all: bool = False,
) -> EvidenceReference:
current, _identity = _current_instance(
session,
principal,
instance_id=instance_id,
lock=True,
allow_all=allow_all,
)
definition = self._definition(
session,
principal,
reference=current.definition_ref,
effective_at=accepted_at,
)
clean_values = normalize_form_values(
definition,
_mapping_copy(values, "Form acknowledgement values"),
)
attachments = tuple(attachment_refs)
if any(item.tenant_id != current.tenant_id for item in attachments):
raise FormRuntimeError(
"Form acknowledgement attachments cannot cross tenants."
)
try:
return self._evidence.create_acknowledgement(
session,
principal,
instance=current,
expected_revision=expected_revision,
statement_id=statement_id,
statement_version=statement_version,
values=clean_values,
attachment_refs=attachments,
accepted_at=accepted_at,
idempotency_key=idempotency_key,
)
except ValueError as exc:
raise FormRuntimeError(str(exc)) from exc
def _revise(
self,
session: Session,
@@ -657,6 +786,19 @@ class FormRuntimeService:
signature_refs=signatures,
final=final_validation,
)
evidence_diagnostics, evidence_snapshots = self._inspect_evidence(
session,
principal,
instance=current,
definition=definition,
values=clean_values,
attachment_refs=attachments,
signature_refs=signatures,
purpose=operation.replace("_", " "),
final=final_validation,
observed_at=recorded_at,
)
diagnostics = (*diagnostics, *evidence_diagnostics)
request = {
"operation": operation,
"instance_id": current.instance_id,
@@ -716,7 +858,14 @@ class FormRuntimeService:
change_reason=clean_reason,
created_by=current.created_by,
changed_by=actor_id,
metadata=current.metadata,
metadata=(
{
**dict(current.metadata),
"evidence_verification": list(evidence_snapshots),
}
if evidence_snapshots
else current.metadata
),
)
current_row = (
session.query(FormInstanceRevision)
@@ -744,6 +893,38 @@ class FormRuntimeService:
operation=operation,
)
def _inspect_evidence(
self,
session: Session,
principal: object,
*,
instance: FormInstance,
definition: FormDefinition,
values: Mapping[str, object],
attachment_refs: Sequence[EvidenceReference],
signature_refs: Sequence[EvidenceReference],
purpose: str,
final: bool,
observed_at: datetime,
) -> tuple[tuple[Mapping[str, object], ...], tuple[Mapping[str, object], ...]]:
if not attachment_refs and not signature_refs:
return (), ()
try:
return self._evidence.inspect(
session,
principal,
instance=instance,
definition=definition,
values=values,
attachment_refs=attachment_refs,
signature_refs=signature_refs,
purpose=purpose,
final=final,
observed_at=observed_at,
)
except ValueError as exc:
raise FormRuntimeError(str(exc)) from exc
def _definition(
self,
session: Session,