Source code for cadrumo.application.operations.persistence._journal_snapshot_validation
"""Private validators for durable operation journal snapshots and event batches."""from__future__importannotationsfromdatetimeimportdatetimefromtypingimportTYPE_CHECKINGfrom....core.operationsimport(LIFECYCLES_BEFORE_ANY_CANCELLATION_REQUEST,LIFECYCLES_BEFORE_EXECUTOR_ENTRY,OperationEffect,OperationLifecycle,OperationTerminalCondition,)from....core.time.utcimportvalidate_utc_awarefrom..capabilitiesimportOperationRequestStoragePolicyfrom..secret_submissionimportOperationSecretRequirementifTYPE_CHECKING:from.journalimportOperationPersistedSnapshot
[docs]defvalidate_request_storage(snapshot:OperationPersistedSnapshot)->None:journal_request=snapshot.credential_free_request_jsonifsnapshot.request_storageisOperationRequestStoragePolicy.CREDENTIAL_FREE_JOURNAL:ifjournal_requestisNoneornotjournal_request:raiseValueError("credential-free request storage requires an inline canonical request")elifjournal_requestisnotNone:raiseValueError("secure-reference request storage forbids an inline journal request")
[docs]defvalidate_executor_entry(snapshot:OperationPersistedSnapshot,entered_at:datetime|None)->None:"""Keep executor entry inside the operation timeline and lifecycle."""ifentered_atisnotNone:validate_utc_aware(entered_at)ifentered_at<snapshot.started_atorentered_at>snapshot.updated_at:raiseValueError("executor entry must fall within the persisted operation timeline")ifsnapshot.lifecycleinLIFECYCLES_BEFORE_EXECUTOR_ENTRY:raiseValueError("created or queued operation cannot record executor entry")
[docs]defvalidate_secret_requirement(snapshot:OperationPersistedSnapshot,requirement:OperationSecretRequirement,entered_at:datetime|None,)->None:"""Keep an ephemeral secret requirement bound to its operation."""ifrequirement.identity!=snapshot.identity:raiseValueError("ephemeral secret requirement does not match operation identity")ifrequirement.revision!=0:raiseValueError("ephemeral secret requirement must bind the initial operation revision")ifrequirement.expires_at<=snapshot.started_at:raiseValueError("ephemeral secret requirement must expire after operation creation")if(snapshot.lifecycleisOperationLifecycle.TERMINALandentered_atisNoneandsnapshot.effectisnotOperationEffect.NONE):raiseValueError("pre-entry ephemeral secret settlement must retain none effect")
[docs]defvalidate_deadline_and_cancellation_state(snapshot:OperationPersistedSnapshot)->None:"""Keep durable deadline and cooperative-stop facts ordered and correlated."""execution_deadline=snapshot.execution_deadlinecleanup_deadline=snapshot.cleanup_deadlinerequested_at=snapshot.cancellation_requested_atacknowledged_at=snapshot.cancellation_acknowledged_atvalidate_deferred_cancellation(snapshot,acknowledged_at)validate_safety_timestamps(execution_deadline,cleanup_deadline,requested_at,acknowledged_at)validate_execution_deadline(snapshot.started_at,execution_deadline)ifrequested_atisNone:validate_unrequested_cancellation(cleanup_deadline,acknowledged_at)returnvalidate_requested_cancellation(snapshot,requested_at,cleanup_deadline,acknowledged_at)
[docs]defvalidate_deferred_cancellation(snapshot:OperationPersistedSnapshot,acknowledged_at:datetime|None)->None:"""Require deferred cancellation to retain an active executor handoff."""ifnotsnapshot.cancellation_deferred:returnifsnapshot.executor_entered_atisNone:raiseValueError("deferred cancellation requires executor entry")ifsnapshot.lifecycleisOperationLifecycle.TERMINAL:raiseValueError("terminal operation cannot defer cancellation")ifacknowledged_atisnotNone:raiseValueError("acknowledged cancellation cannot remain deferred")
[docs]defvalidate_safety_timestamps(execution_deadline:datetime|None,cleanup_deadline:datetime|None,requested_at:datetime|None,acknowledged_at:datetime|None,)->None:"""Require every optional deadline and cancellation timestamp to be UTC-aware."""fortimestampin(execution_deadline,cleanup_deadline,requested_at,acknowledged_at):iftimestampisnotNone:validate_utc_aware(timestamp)
[docs]defvalidate_execution_deadline(started_at:datetime,execution_deadline:datetime|None)->None:"""Require an execution deadline to follow operation creation."""ifexecution_deadlineisnotNoneandexecution_deadline<started_at:raiseValueError("execution deadline cannot precede operation start")
[docs]defvalidate_unrequested_cancellation(cleanup_deadline:datetime|None,acknowledged_at:datetime|None)->None:"""Reject cleanup or acknowledgement facts without a cancellation request."""ifcleanup_deadlineisnotNone:raiseValueError("cleanup deadline requires a cancellation request")ifacknowledged_atisnotNone:raiseValueError("cancellation acknowledgement requires a cancellation request")
[docs]defvalidate_requested_cancellation(snapshot:OperationPersistedSnapshot,requested_at:datetime,cleanup_deadline:datetime|None,acknowledged_at:datetime|None,)->None:"""Keep requested cancellation facts ordered through settlement."""ifrequested_at<snapshot.started_atorrequested_at>snapshot.updated_at:raiseValueError("cancellation request must fall within the persisted operation timeline")ifcleanup_deadlineisNoneorcleanup_deadline<=requested_at:raiseValueError("cancellation request requires a later cleanup deadline")ifsnapshot.lifecycleinLIFECYCLES_BEFORE_ANY_CANCELLATION_REQUEST:raiseValueError("cancellation request requires a cancellation or settlement lifecycle")validate_cancellation_acknowledgement(snapshot,requested_at,acknowledged_at)ifsnapshot.terminal_conditionisOperationTerminalCondition.CANCELLEDandacknowledged_atisNone:raiseValueError("cancelled operation requires a durable cancellation acknowledgement")
[docs]defvalidate_cancellation_acknowledgement(snapshot:OperationPersistedSnapshot,requested_at:datetime,acknowledged_at:datetime|None,)->None:"""Require an acknowledgement to follow a cancellation request."""ifacknowledged_atisNone:returnifacknowledged_at<requested_atoracknowledged_at>snapshot.updated_at:raiseValueError("cancellation acknowledgement must follow the request within the operation timeline")ifsnapshot.lifecycleisOperationLifecycle.CANCELLATION_REQUESTED:raiseValueError("cancellation acknowledgement requires settlement lifecycle")
[docs]defvalidate_idempotency_claim(snapshot:OperationPersistedSnapshot)->None:claim=snapshot.idempotency_claimifclaimisNone:returnifclaim.operation_id!=snapshot.operation_idorclaim.definition_id!=snapshot.identity.definition_id:raiseValueError("idempotency claim does not match persisted operation identity")ifclaim.subject_ref!=snapshot.identity.subject_reforclaim.request_reference!=snapshot.request_reference:raiseValueError("idempotency claim does not match persisted request identity")
[docs]defvalidate_pending_interaction(snapshot:OperationPersistedSnapshot)->None:pending=snapshot.pending_interactionifpendingisNone:returnifsnapshot.lifecycleisnotOperationLifecycle.WAITING_FOR_INTERACTION:raiseValueError("pending interaction requires waiting-for-interaction lifecycle")ifpending.request.identity!=snapshot.identityorpending.request.revision!=snapshot.revision:raiseValueError("pending interaction does not match persisted operation revision")
[docs]defvalidate_consumed_interactions(snapshot:OperationPersistedSnapshot)->None:consumed_ids=tuple(item.interaction_idforiteminsnapshot.consumed_interactions)iflen(set(consumed_ids))!=len(consumed_ids):raiseValueError("consumed interaction identities must be unique")pending=snapshot.pending_interactionifpendingisnotNoneandpending.request.interaction_idinconsumed_ids:raiseValueError("pending interaction cannot already be consumed")