aeat.application.live._snapshot_base module¶
Shared lifecycle base classes and helpers for live snapshot services.
This module factors the duplicated state-machine, supersession, and content-
addressed-id derivation logic shared across the bucket-scoped live snapshot
services (Borrador100, Censo, Expedientes, and Notifications). Each concrete
service writes and reads snapshot payloads through a SecureObjectRepository
scoped to the active profile bucket.
Design notes:
SnapshotLifecycleStatecarries the three operator-visible states all stateful snapshot services share. Every stateful service binds payloadstatedirectly to this enum.SnapshotRepositoryis a Protocol — not an abstract class — so concrete per-service repositories (which need to bind a specific TPayload model and a domain-specific object-key prefix) do not need to inherit from it. The service base accepts any object that structurally satisfies the protocol.SnapshotServiceis a generic abstract base whosecapturetemplate method coordinates the dedup-by-content-id, auto-supersession, and late-arrival demotion flow. Subclasses implement_payload_axis_keyand_build_active_payloadto express their domain axis and construction contract; everything else (state transitions, repository orchestration) is shared.StatelessSnapshotServiceis the append-only base for services (Expedientes, Notifications) with no state machine. It acceptsbucket_idper call and constructs a fresh repository for the call from an injectedrepository_factory— the natural shape for services whose public verbs are themselves multi-bucket. Supersession and discard helpers are deliberately absent.
- exception SnapshotNotFoundError(message=None, *, context=None, suggestion=None, translated_message=None)[source]¶
-
Shared base for per-service snapshot-lookup-miss errors.
Inherits from both
aeat.core.errors.AeatErrorandKeyErrorso the class is enrolled in theERROR_REGISTRYvia theAeatError.__init_subclass__hook while preserving the mapping-style lookup-miss type.AeatErroris listed first so MRO routes__init__throughAeatError.__init__(which accepts the structuredsuggestion=/context=kwargs) rather thanKeyError’s C-level constructor.Per-service subclasses (BorradorSnapshotNotFoundError, ExpedientesSnapshotNotFoundError, NotificationsSnapshotNotFoundError, and future siblings) inherit from this base alongside
aeat.core.errors.AeatErrorso callers can either catch the domain-specific class name or the shared parent.- Parameters:
- Return type:
None
- code: ClassVar[ErrorCode]¶
- class SnapshotLifecycleState(*values)[source]¶
Bases:
StrEnumLifecycle states shared across stateful live snapshot services.
ACTIVE— current valid capture; readers consume this.SUPERSEDED— replaced by a newer ACTIVE capture on the same axis; retained for audit. Carriessuperseded_by_snapshot_id.DISCARDED— explicitly retired by an operator; carries actor + reason audit metadata.
- ACTIVE¶
- SUPERSEDED¶
- DISCARDED¶
- class SnapshotRepository(*args, **kwargs)[source]¶
-
Structural contract for bucket-scoped snapshot persistence backends.
Implementations may be SecureObjectRepository-backed (Borrador100, Censo) or file-system-backed (stateless services).
- derive_snapshot_id_from_json(parts)[source]¶
Return a SHA-256 content-addressed id for a canonical JSON dict.
partsis serialized withsort_keys=True, ASCII-safe, and the compact(",", ":")separator, then hashed. Callers must pre-coerce Decimals / datetimes / typed-IDs to JSON-safe scalars; the helper does not introspect Pydantic models.
- enforce_snapshot_state_invariants(*, state, has_supersession_pointer, discarded_at, discarded_by, discard_reason='')[source]¶
Enforce the three-state lifecycle invariants for any snapshot payload.
ACTIVE: no supersession pointer, no discard audit metadata. SUPERSEDED: requires supersession pointer, no discard audit metadata. DISCARDED: forbids supersession pointer, requires actor + timestamp.
Domain-specific Pydantic model validators wrap this helper so the same rules apply across Borrador100, Censo, and future stateful services.
- class SnapshotService(*, bucket_id, repository)[source]¶
-
Abstract lifecycle service base for stateful bucket-scoped snapshots.
Subclasses bind
TPayloadto their concrete Pydantic snapshot model and implement two hooks:_payload_axis_key— returns a tuple identifying the domain axis on which prior ACTIVE snapshots are superseded (e.g.(modelo, year, period)for Borrador100,(profile_id,)for Censo)._build_active_payload— constructs an ACTIVE snapshot from keyword-only capture arguments and a derived snapshot id.
The
capturetemplate orchestrates dedup, auto-supersession of prior ACTIVE snapshots, and late-arrival demotion when a freshly-captured snapshot arrives older than the current ACTIVE on the same axis.- Parameters:
bucket_id (str)
repository (SnapshotRepository[TPayload])
- class StatelessSnapshotService(*, repository_factory)[source]¶
-
Append-only base for stateless snapshot services with per-call buckets.
Subclasses inject a
repository_factorythat returns a freshSnapshotRepositoryfor a given bucket id; each public verb acceptsbucket_idand materialises the repository on demand. The per-bucket repository is responsible for storage layout and bucket isolation; the base provides the shared dedup, list, and resolve logic.Subclasses implement two hooks:
_derive_snapshot_idand_build_payload._build_payloadreceives the resolvedbucket_idso payload models can record it on the persisted record.- Parameters:
repository_factory (Callable[[str], SnapshotRepository[TPayload]])
- class SecureSnapshotRepository(*, bucket_id, payload_model, namespace_definition, object_key, not_found_factory, ambiguous_prefix_factory, domain_label, objects=None)[source]¶
Bases:
GenericGeneric secure-object snapshot repository for one runtime bucket.
The repository preserves the
SnapshotRepositorystructural contract used by stateless live services while replacing one-file-per- bucket JSONL stores with encrypted secure-object rows. Each row is a typedEnvelopewhose object key carries the bucket id and content-addressed snapshot id.- Parameters:
bucket_id (str)
payload_model (type[TPayload])
namespace_definition (SecureObjectNamespaceDefinition)
ambiguous_prefix_factory (Callable[[str, tuple[str, ...]], Exception])
domain_label (str)
objects (SecureObjectRepository | None)