Module adcp.reporting.ledger.notification_models
Closed logical notifications and typed, consumer-qualified status identities.
Functions
def configuration_evidence(configuration: ReportingConfiguration) ‑> ReportingStatusEvidence-
Expand source code
def configuration_evidence(configuration: ReportingConfiguration) -> ReportingStatusEvidence: return ReportingStatusEvidence( "configuration", configuration.delivery_config_id, configuration.delivery_config_version, activated_at=configuration.activated_at, deactivated_at=configuration.deactivated_at, automated_recovery_seconds=configuration.automated_recovery_window.total_seconds(), status_retention_days=configuration.status_retention_days, ) def decode_dirty(value: object) ‑> ReportingStatusDirty-
Expand source code
def decode_dirty(value: object) -> ReportingStatusDirty: try: record = _dirty_adapter().validate_python(value) if dirty_storage(record) == value: return record except (ValidationError, ValueError, TypeError): # Leave the parsing exception context before emitting the closed classification. pass raise ReportingNotificationError("invalid_status_evidence") from None def decode_event(value: object) ‑> ReportingDomainEvent-
Expand source code
def decode_event(value: object) -> ReportingDomainEvent: try: event = _event_adapter().validate_python(value) if event_storage(event) == value: event.body(subscriber_id="validation", idempotency_key="0" * 32) return event except (ValidationError, ValueError, TypeError): # Leave the parsing exception context before emitting the closed classification. pass raise ReportingNotificationError("invalid_event") from None def decode_status_scope(value: object) ‑> ReportingStatusScope-
Expand source code
def decode_status_scope(value: object) -> ReportingStatusScope: try: scope = _scope_adapter().validate_python(value) if asdict(scope) == value: return scope except (ValidationError, ValueError, TypeError): # Leave the parsing exception context before emitting the closed classification. pass raise ReportingNotificationError("invalid_status_scope") from None def dirty_storage(record: ReportingStatusDirty) ‑> dict[str, typing.Any]-
Expand source code
def dirty_storage(record: ReportingStatusDirty) -> dict[str, Any]: return dict(json.loads(json.dumps(asdict(record), default=iso))) def event_storage(event: ReportingDomainEvent) ‑> dict[str, typing.Any]-
Expand source code
def event_storage(event: ReportingDomainEvent) -> dict[str, Any]: return dict(json.loads(json.dumps(asdict(event), default=iso))) def iso(value: datetime) ‑> str-
Expand source code
def iso(value: datetime) -> str: return aware_utc(value).isoformat().replace("+00:00", "Z") def issue_evidence(issue: ReportingIssueLifecycle) ‑> ReportingStatusEvidence-
Expand source code
def issue_evidence(issue: ReportingIssueLifecycle) -> ReportingStatusEvidence: # Attribute allowlist: issue_key and external_ref are intentionally absent. return ReportingStatusEvidence( "issue", issue.issue_id, issue.generation, issue_state=issue.issue_state, opened_at=issue.opened_at, retired_at=issue.retired_at, ) def new_event(account_id: str, cause: NotificationCause, at: datetime) ‑> ReportingDomainEvent-
Expand source code
def new_event(account_id: str, cause: NotificationCause, at: datetime) -> ReportingDomainEvent: event = ReportingDomainEvent(account_id, str(uuid4()), at, cause) event.body(subscriber_id="validation", idempotency_key="0" * 32) return event def validate_notification_payload(value: object) ‑> None-
Expand source code
def validate_notification_payload(value: object) -> None: """Offline full-schema gate, additionally excluding extension metadata.""" if not isinstance(value, dict) or "ext" in value: raise ReportingNotificationError("invalid_payload") notification_type = value.get("notification_type") path = _SCHEMAS.get(notification_type) if isinstance(notification_type, str) else None if path is None: raise ReportingNotificationError("invalid_payload") def scan(item: object) -> None: if isinstance(item, str): principal_reference(item) elif isinstance(item, list): for child in item: scan(child) elif isinstance(item, dict): for child in item.values(): scan(child) try: scan(value) except ValueError: raise ReportingNotificationError("invalid_payload") from None validator = get_named_validator(path) if validator is None or not validator.is_valid(value): raise ReportingNotificationError("invalid_payload")Offline full-schema gate, additionally excluding extension metadata.
def validate_scope_refinement(existing: ReportingStatusScope | None,
requested: ReportingStatusScope) ‑> None-
Expand source code
def validate_scope_refinement( existing: ReportingStatusScope | None, requested: ReportingStatusScope ) -> None: """Only fill unknown detail within the same account and consumer. Ownership of added generation/obligation fields must additionally be checked by the connection-bound store before it persists the refinement. """ if existing is not None and ( existing.account_id != requested.account_id or existing.consumer_id != requested.consumer_id or any( getattr(existing, name) is not None and getattr(existing, name) != getattr(requested, name) for name in ("generation_key", "reporting_obligation_id", "feed_purpose") ) ): raise ReportingNotificationError("invalid_status_scope")Only fill unknown detail within the same account and consumer.
Ownership of added generation/obligation fields must additionally be checked by the connection-bound store before it persists the refinement.
Classes
class AdjustmentPublished (reporting_adjustment_id: str,
consumer_id: str,
adjusts_reporting_revision_id: str,
*,
kind: "Literal['adjustment_published']" = 'adjustment_published')-
Expand source code
@dataclass(frozen=True, slots=True) class AdjustmentPublished(_ClosedValue): reporting_adjustment_id: str consumer_id: str adjusts_reporting_revision_id: str kind: Literal["adjustment_published"] = field(default="adjustment_published", kw_only=True) def __post_init__(self) -> None: _freeze_fields(self) reporting_identifier(self.reporting_adjustment_id, maximum=255) consumer_reference(self.consumer_id) reporting_identifier(self.adjusts_reporting_revision_id, maximum=255)AdjustmentPublished(reporting_adjustment_id: 'str', consumer_id: 'str', adjusts_reporting_revision_id: 'str', *, kind: "Literal['adjustment_published']" = 'adjustment_published')
Ancestors
- adcp.reporting.ledger.delivery_models._ClosedValue
Instance variables
var adjusts_reporting_revision_id : str-
Expand source code
@dataclass(frozen=True, slots=True) class AdjustmentPublished(_ClosedValue): reporting_adjustment_id: str consumer_id: str adjusts_reporting_revision_id: str kind: Literal["adjustment_published"] = field(default="adjustment_published", kw_only=True) def __post_init__(self) -> None: _freeze_fields(self) reporting_identifier(self.reporting_adjustment_id, maximum=255) consumer_reference(self.consumer_id) reporting_identifier(self.adjusts_reporting_revision_id, maximum=255) var consumer_id : str-
Expand source code
@dataclass(frozen=True, slots=True) class AdjustmentPublished(_ClosedValue): reporting_adjustment_id: str consumer_id: str adjusts_reporting_revision_id: str kind: Literal["adjustment_published"] = field(default="adjustment_published", kw_only=True) def __post_init__(self) -> None: _freeze_fields(self) reporting_identifier(self.reporting_adjustment_id, maximum=255) consumer_reference(self.consumer_id) reporting_identifier(self.adjusts_reporting_revision_id, maximum=255) var kind : Literal['adjustment_published']-
Expand source code
@dataclass(frozen=True, slots=True) class AdjustmentPublished(_ClosedValue): reporting_adjustment_id: str consumer_id: str adjusts_reporting_revision_id: str kind: Literal["adjustment_published"] = field(default="adjustment_published", kw_only=True) def __post_init__(self) -> None: _freeze_fields(self) reporting_identifier(self.reporting_adjustment_id, maximum=255) consumer_reference(self.consumer_id) reporting_identifier(self.adjusts_reporting_revision_id, maximum=255) var reporting_adjustment_id : str-
Expand source code
@dataclass(frozen=True, slots=True) class AdjustmentPublished(_ClosedValue): reporting_adjustment_id: str consumer_id: str adjusts_reporting_revision_id: str kind: Literal["adjustment_published"] = field(default="adjustment_published", kw_only=True) def __post_init__(self) -> None: _freeze_fields(self) reporting_identifier(self.reporting_adjustment_id, maximum=255) consumer_reference(self.consumer_id) reporting_identifier(self.adjusts_reporting_revision_id, maximum=255)
class MaterializationReady (generation_key: ReportingConfigurationGenerationKey,
consumer_id: str,
reporting_obligation_id: str,
destination_ref: str,
method: "Literal['file_transfer', 'dataset_share', 'warehouse_materialization']",
reporting_revision_id: str,
reporting_materialization_id: str,
readiness: "Literal['available', 'delivered']",
finality: ReportingFinality,
data_through: datetime | None,
feed_purpose: FeedPurpose,
*,
kind: "Literal['materialization_ready']" = 'materialization_ready')-
Expand source code
@dataclass(frozen=True, slots=True) class MaterializationReady(_ClosedValue): """Snapshot of a verified, frozen Managed binding; never a capability string.""" generation_key: ReportingConfigurationGenerationKey consumer_id: str reporting_obligation_id: str destination_ref: str method: Literal["file_transfer", "dataset_share", "warehouse_materialization"] reporting_revision_id: str reporting_materialization_id: str readiness: Literal["available", "delivered"] finality: ReportingFinality data_through: datetime | None feed_purpose: FeedPurpose kind: Literal["materialization_ready"] = field(default="materialization_ready", kw_only=True) def __post_init__(self) -> None: _freeze_fields(self) consumer_reference(self.consumer_id) if self.consumer_id != self.generation_key.consumer_id: raise ReportingNotificationError("invalid_status_scope") for value in ( self.reporting_obligation_id, self.reporting_revision_id, self.reporting_materialization_id, ): reporting_identifier(value, maximum=255) reporting_identifier(self.generation_key.delivery_config_id, maximum=64) if self.data_through is not None: object.__setattr__(self, "data_through", aware_utc(self.data_through))Snapshot of a verified, frozen Managed binding; never a capability string.
Ancestors
- adcp.reporting.ledger.delivery_models._ClosedValue
Instance variables
var consumer_id : str-
Expand source code
@dataclass(frozen=True, slots=True) class MaterializationReady(_ClosedValue): """Snapshot of a verified, frozen Managed binding; never a capability string.""" generation_key: ReportingConfigurationGenerationKey consumer_id: str reporting_obligation_id: str destination_ref: str method: Literal["file_transfer", "dataset_share", "warehouse_materialization"] reporting_revision_id: str reporting_materialization_id: str readiness: Literal["available", "delivered"] finality: ReportingFinality data_through: datetime | None feed_purpose: FeedPurpose kind: Literal["materialization_ready"] = field(default="materialization_ready", kw_only=True) def __post_init__(self) -> None: _freeze_fields(self) consumer_reference(self.consumer_id) if self.consumer_id != self.generation_key.consumer_id: raise ReportingNotificationError("invalid_status_scope") for value in ( self.reporting_obligation_id, self.reporting_revision_id, self.reporting_materialization_id, ): reporting_identifier(value, maximum=255) reporting_identifier(self.generation_key.delivery_config_id, maximum=64) if self.data_through is not None: object.__setattr__(self, "data_through", aware_utc(self.data_through)) var data_through : datetime.datetime | None-
Expand source code
@dataclass(frozen=True, slots=True) class MaterializationReady(_ClosedValue): """Snapshot of a verified, frozen Managed binding; never a capability string.""" generation_key: ReportingConfigurationGenerationKey consumer_id: str reporting_obligation_id: str destination_ref: str method: Literal["file_transfer", "dataset_share", "warehouse_materialization"] reporting_revision_id: str reporting_materialization_id: str readiness: Literal["available", "delivered"] finality: ReportingFinality data_through: datetime | None feed_purpose: FeedPurpose kind: Literal["materialization_ready"] = field(default="materialization_ready", kw_only=True) def __post_init__(self) -> None: _freeze_fields(self) consumer_reference(self.consumer_id) if self.consumer_id != self.generation_key.consumer_id: raise ReportingNotificationError("invalid_status_scope") for value in ( self.reporting_obligation_id, self.reporting_revision_id, self.reporting_materialization_id, ): reporting_identifier(value, maximum=255) reporting_identifier(self.generation_key.delivery_config_id, maximum=64) if self.data_through is not None: object.__setattr__(self, "data_through", aware_utc(self.data_through)) var destination_ref : str-
Expand source code
@dataclass(frozen=True, slots=True) class MaterializationReady(_ClosedValue): """Snapshot of a verified, frozen Managed binding; never a capability string.""" generation_key: ReportingConfigurationGenerationKey consumer_id: str reporting_obligation_id: str destination_ref: str method: Literal["file_transfer", "dataset_share", "warehouse_materialization"] reporting_revision_id: str reporting_materialization_id: str readiness: Literal["available", "delivered"] finality: ReportingFinality data_through: datetime | None feed_purpose: FeedPurpose kind: Literal["materialization_ready"] = field(default="materialization_ready", kw_only=True) def __post_init__(self) -> None: _freeze_fields(self) consumer_reference(self.consumer_id) if self.consumer_id != self.generation_key.consumer_id: raise ReportingNotificationError("invalid_status_scope") for value in ( self.reporting_obligation_id, self.reporting_revision_id, self.reporting_materialization_id, ): reporting_identifier(value, maximum=255) reporting_identifier(self.generation_key.delivery_config_id, maximum=64) if self.data_through is not None: object.__setattr__(self, "data_through", aware_utc(self.data_through)) var feed_purpose : Literal['pacing', 'analytics', 'billing']-
Expand source code
@dataclass(frozen=True, slots=True) class MaterializationReady(_ClosedValue): """Snapshot of a verified, frozen Managed binding; never a capability string.""" generation_key: ReportingConfigurationGenerationKey consumer_id: str reporting_obligation_id: str destination_ref: str method: Literal["file_transfer", "dataset_share", "warehouse_materialization"] reporting_revision_id: str reporting_materialization_id: str readiness: Literal["available", "delivered"] finality: ReportingFinality data_through: datetime | None feed_purpose: FeedPurpose kind: Literal["materialization_ready"] = field(default="materialization_ready", kw_only=True) def __post_init__(self) -> None: _freeze_fields(self) consumer_reference(self.consumer_id) if self.consumer_id != self.generation_key.consumer_id: raise ReportingNotificationError("invalid_status_scope") for value in ( self.reporting_obligation_id, self.reporting_revision_id, self.reporting_materialization_id, ): reporting_identifier(value, maximum=255) reporting_identifier(self.generation_key.delivery_config_id, maximum=64) if self.data_through is not None: object.__setattr__(self, "data_through", aware_utc(self.data_through)) var finality : Literal['snapshot', 'official']-
Expand source code
@dataclass(frozen=True, slots=True) class MaterializationReady(_ClosedValue): """Snapshot of a verified, frozen Managed binding; never a capability string.""" generation_key: ReportingConfigurationGenerationKey consumer_id: str reporting_obligation_id: str destination_ref: str method: Literal["file_transfer", "dataset_share", "warehouse_materialization"] reporting_revision_id: str reporting_materialization_id: str readiness: Literal["available", "delivered"] finality: ReportingFinality data_through: datetime | None feed_purpose: FeedPurpose kind: Literal["materialization_ready"] = field(default="materialization_ready", kw_only=True) def __post_init__(self) -> None: _freeze_fields(self) consumer_reference(self.consumer_id) if self.consumer_id != self.generation_key.consumer_id: raise ReportingNotificationError("invalid_status_scope") for value in ( self.reporting_obligation_id, self.reporting_revision_id, self.reporting_materialization_id, ): reporting_identifier(value, maximum=255) reporting_identifier(self.generation_key.delivery_config_id, maximum=64) if self.data_through is not None: object.__setattr__(self, "data_through", aware_utc(self.data_through)) var generation_key : ReportingConfigurationGenerationKey-
Expand source code
@dataclass(frozen=True, slots=True) class MaterializationReady(_ClosedValue): """Snapshot of a verified, frozen Managed binding; never a capability string.""" generation_key: ReportingConfigurationGenerationKey consumer_id: str reporting_obligation_id: str destination_ref: str method: Literal["file_transfer", "dataset_share", "warehouse_materialization"] reporting_revision_id: str reporting_materialization_id: str readiness: Literal["available", "delivered"] finality: ReportingFinality data_through: datetime | None feed_purpose: FeedPurpose kind: Literal["materialization_ready"] = field(default="materialization_ready", kw_only=True) def __post_init__(self) -> None: _freeze_fields(self) consumer_reference(self.consumer_id) if self.consumer_id != self.generation_key.consumer_id: raise ReportingNotificationError("invalid_status_scope") for value in ( self.reporting_obligation_id, self.reporting_revision_id, self.reporting_materialization_id, ): reporting_identifier(value, maximum=255) reporting_identifier(self.generation_key.delivery_config_id, maximum=64) if self.data_through is not None: object.__setattr__(self, "data_through", aware_utc(self.data_through)) var kind : Literal['materialization_ready']-
Expand source code
@dataclass(frozen=True, slots=True) class MaterializationReady(_ClosedValue): """Snapshot of a verified, frozen Managed binding; never a capability string.""" generation_key: ReportingConfigurationGenerationKey consumer_id: str reporting_obligation_id: str destination_ref: str method: Literal["file_transfer", "dataset_share", "warehouse_materialization"] reporting_revision_id: str reporting_materialization_id: str readiness: Literal["available", "delivered"] finality: ReportingFinality data_through: datetime | None feed_purpose: FeedPurpose kind: Literal["materialization_ready"] = field(default="materialization_ready", kw_only=True) def __post_init__(self) -> None: _freeze_fields(self) consumer_reference(self.consumer_id) if self.consumer_id != self.generation_key.consumer_id: raise ReportingNotificationError("invalid_status_scope") for value in ( self.reporting_obligation_id, self.reporting_revision_id, self.reporting_materialization_id, ): reporting_identifier(value, maximum=255) reporting_identifier(self.generation_key.delivery_config_id, maximum=64) if self.data_through is not None: object.__setattr__(self, "data_through", aware_utc(self.data_through)) var method : Literal['file_transfer', 'dataset_share', 'warehouse_materialization']-
Expand source code
@dataclass(frozen=True, slots=True) class MaterializationReady(_ClosedValue): """Snapshot of a verified, frozen Managed binding; never a capability string.""" generation_key: ReportingConfigurationGenerationKey consumer_id: str reporting_obligation_id: str destination_ref: str method: Literal["file_transfer", "dataset_share", "warehouse_materialization"] reporting_revision_id: str reporting_materialization_id: str readiness: Literal["available", "delivered"] finality: ReportingFinality data_through: datetime | None feed_purpose: FeedPurpose kind: Literal["materialization_ready"] = field(default="materialization_ready", kw_only=True) def __post_init__(self) -> None: _freeze_fields(self) consumer_reference(self.consumer_id) if self.consumer_id != self.generation_key.consumer_id: raise ReportingNotificationError("invalid_status_scope") for value in ( self.reporting_obligation_id, self.reporting_revision_id, self.reporting_materialization_id, ): reporting_identifier(value, maximum=255) reporting_identifier(self.generation_key.delivery_config_id, maximum=64) if self.data_through is not None: object.__setattr__(self, "data_through", aware_utc(self.data_through)) var readiness : Literal['available', 'delivered']-
Expand source code
@dataclass(frozen=True, slots=True) class MaterializationReady(_ClosedValue): """Snapshot of a verified, frozen Managed binding; never a capability string.""" generation_key: ReportingConfigurationGenerationKey consumer_id: str reporting_obligation_id: str destination_ref: str method: Literal["file_transfer", "dataset_share", "warehouse_materialization"] reporting_revision_id: str reporting_materialization_id: str readiness: Literal["available", "delivered"] finality: ReportingFinality data_through: datetime | None feed_purpose: FeedPurpose kind: Literal["materialization_ready"] = field(default="materialization_ready", kw_only=True) def __post_init__(self) -> None: _freeze_fields(self) consumer_reference(self.consumer_id) if self.consumer_id != self.generation_key.consumer_id: raise ReportingNotificationError("invalid_status_scope") for value in ( self.reporting_obligation_id, self.reporting_revision_id, self.reporting_materialization_id, ): reporting_identifier(value, maximum=255) reporting_identifier(self.generation_key.delivery_config_id, maximum=64) if self.data_through is not None: object.__setattr__(self, "data_through", aware_utc(self.data_through)) var reporting_materialization_id : str-
Expand source code
@dataclass(frozen=True, slots=True) class MaterializationReady(_ClosedValue): """Snapshot of a verified, frozen Managed binding; never a capability string.""" generation_key: ReportingConfigurationGenerationKey consumer_id: str reporting_obligation_id: str destination_ref: str method: Literal["file_transfer", "dataset_share", "warehouse_materialization"] reporting_revision_id: str reporting_materialization_id: str readiness: Literal["available", "delivered"] finality: ReportingFinality data_through: datetime | None feed_purpose: FeedPurpose kind: Literal["materialization_ready"] = field(default="materialization_ready", kw_only=True) def __post_init__(self) -> None: _freeze_fields(self) consumer_reference(self.consumer_id) if self.consumer_id != self.generation_key.consumer_id: raise ReportingNotificationError("invalid_status_scope") for value in ( self.reporting_obligation_id, self.reporting_revision_id, self.reporting_materialization_id, ): reporting_identifier(value, maximum=255) reporting_identifier(self.generation_key.delivery_config_id, maximum=64) if self.data_through is not None: object.__setattr__(self, "data_through", aware_utc(self.data_through)) var reporting_obligation_id : str-
Expand source code
@dataclass(frozen=True, slots=True) class MaterializationReady(_ClosedValue): """Snapshot of a verified, frozen Managed binding; never a capability string.""" generation_key: ReportingConfigurationGenerationKey consumer_id: str reporting_obligation_id: str destination_ref: str method: Literal["file_transfer", "dataset_share", "warehouse_materialization"] reporting_revision_id: str reporting_materialization_id: str readiness: Literal["available", "delivered"] finality: ReportingFinality data_through: datetime | None feed_purpose: FeedPurpose kind: Literal["materialization_ready"] = field(default="materialization_ready", kw_only=True) def __post_init__(self) -> None: _freeze_fields(self) consumer_reference(self.consumer_id) if self.consumer_id != self.generation_key.consumer_id: raise ReportingNotificationError("invalid_status_scope") for value in ( self.reporting_obligation_id, self.reporting_revision_id, self.reporting_materialization_id, ): reporting_identifier(value, maximum=255) reporting_identifier(self.generation_key.delivery_config_id, maximum=64) if self.data_through is not None: object.__setattr__(self, "data_through", aware_utc(self.data_through)) var reporting_revision_id : str-
Expand source code
@dataclass(frozen=True, slots=True) class MaterializationReady(_ClosedValue): """Snapshot of a verified, frozen Managed binding; never a capability string.""" generation_key: ReportingConfigurationGenerationKey consumer_id: str reporting_obligation_id: str destination_ref: str method: Literal["file_transfer", "dataset_share", "warehouse_materialization"] reporting_revision_id: str reporting_materialization_id: str readiness: Literal["available", "delivered"] finality: ReportingFinality data_through: datetime | None feed_purpose: FeedPurpose kind: Literal["materialization_ready"] = field(default="materialization_ready", kw_only=True) def __post_init__(self) -> None: _freeze_fields(self) consumer_reference(self.consumer_id) if self.consumer_id != self.generation_key.consumer_id: raise ReportingNotificationError("invalid_status_scope") for value in ( self.reporting_obligation_id, self.reporting_revision_id, self.reporting_materialization_id, ): reporting_identifier(value, maximum=255) reporting_identifier(self.generation_key.delivery_config_id, maximum=64) if self.data_through is not None: object.__setattr__(self, "data_through", aware_utc(self.data_through))
class ReportingDomainEvent (account_id: str,
notification_id: str,
fired_at: datetime,
cause: NotificationCause,
cause_generation: int = 1)-
Expand source code
@dataclass(frozen=True, slots=True) class ReportingDomainEvent(_ClosedValue): account_id: str notification_id: str fired_at: datetime cause: NotificationCause cause_generation: int = 1 def __post_init__(self) -> None: _freeze_fields(self) principal_reference(self.account_id) reporting_identifier(self.notification_id, maximum=255) object.__setattr__(self, "fired_at", aware_utc(self.fired_at)) if self.cause_generation < 1: raise ReportingNotificationError() if isinstance(self.cause, MaterializationReady): if self.cause.generation_key.account_id != self.account_id: raise ReportingNotificationError() if isinstance(self.cause, StatusChanged) and ( self.cause.scope.account_id != self.account_id or self.cause.checkpoint_generation != self.cause_generation ): raise ReportingNotificationError() @property def notification_type(self) -> NotificationType: if isinstance(self.cause, StatusChanged): return "reporting.status_changed" return ( "reporting.delivery_ready" if isinstance(self.cause, MaterializationReady) else "reporting.ledger_changed" ) @property def cause_id(self) -> str: cause = self.cause if isinstance(cause, RevisionPublished): return cause.reporting_revision_id if isinstance(cause, AdjustmentPublished): return cause.reporting_adjustment_id if isinstance(cause, StatusChanged): from adcp.reporting.canonical_json import canonical_json_utf8_v1 return ( "rpsc_" + hashlib.sha256(canonical_json_utf8_v1(cause.scope.checkpoint_key)).hexdigest() ) # Structured encoding: consumers and materializations may reuse IDs. return json.dumps( [cause.consumer_id, cause.reporting_materialization_id], separators=(",", ":") ) @property def consumer_namespace(self) -> str: if isinstance(self.cause, StatusChanged): return self.cause.scope.consumer_id or "" return self.cause.consumer_id @property def causal_key(self) -> tuple[str, str, str, str, str, int]: return ( self.account_id, self.consumer_namespace, self.notification_type, self.cause.kind, self.cause_id, self.cause_generation, ) def body(self, *, subscriber_id: str, idempotency_key: str) -> bytes: """Explicit wire allowlist, validated against all rc.3 conditionals.""" from adcp.reporting.canonical_json import canonical_json_utf8_v1 value: dict[str, Any] = { "account_id": self.account_id, "notification_id": self.notification_id, "notification_type": self.notification_type, "fired_at": iso(self.fired_at), "subscriber_id": subscriber_id, "idempotency_key": idempotency_key, } cause = self.cause if isinstance(cause, RevisionPublished): value.update( change_kind=cause.kind, reporting_revision_id=cause.reporting_revision_id, finality=cause.finality, ) if cause.supersedes_reporting_revision_id is not None: value["supersedes_reporting_revision_id"] = cause.supersedes_reporting_revision_id elif isinstance(cause, AdjustmentPublished): value.update( change_kind=cause.kind, reporting_adjustment_id=cause.reporting_adjustment_id, adjusts_reporting_revision_id=cause.adjusts_reporting_revision_id, ) elif isinstance(cause, StatusChanged): key = cause.scope.generation_key assert key is not None value.update( delivery_config_id=key.delivery_config_id, delivery_config_version=key.delivery_config_version, feed_purpose=cause.scope.feed_purpose, health=cause.health, ) if cause.scope.reporting_obligation_id is not None: value["reporting_obligation_id"] = cause.scope.reporting_obligation_id if cause.previous_health is not None: value["previous_health"] = cause.previous_health if cause.issue_ids: value["issue_ids"] = list(cause.issue_ids) else: value.update( delivery_config_id=cause.generation_key.delivery_config_id, delivery_config_version=cause.generation_key.delivery_config_version, reporting_revision_id=cause.reporting_revision_id, reporting_materialization_id=cause.reporting_materialization_id, readiness=cause.readiness, finality=cause.finality, data_through=iso(cause.data_through) if cause.data_through is not None else None, feed_purpose=cause.feed_purpose, ) validate_notification_payload(value) return canonical_json_utf8_v1(value)ReportingDomainEvent(account_id: 'str', notification_id: 'str', fired_at: 'datetime', cause: 'NotificationCause', cause_generation: 'int' = 1)
Ancestors
- adcp.reporting.ledger.delivery_models._ClosedValue
Instance variables
var account_id : str-
Expand source code
@dataclass(frozen=True, slots=True) class ReportingDomainEvent(_ClosedValue): account_id: str notification_id: str fired_at: datetime cause: NotificationCause cause_generation: int = 1 def __post_init__(self) -> None: _freeze_fields(self) principal_reference(self.account_id) reporting_identifier(self.notification_id, maximum=255) object.__setattr__(self, "fired_at", aware_utc(self.fired_at)) if self.cause_generation < 1: raise ReportingNotificationError() if isinstance(self.cause, MaterializationReady): if self.cause.generation_key.account_id != self.account_id: raise ReportingNotificationError() if isinstance(self.cause, StatusChanged) and ( self.cause.scope.account_id != self.account_id or self.cause.checkpoint_generation != self.cause_generation ): raise ReportingNotificationError() @property def notification_type(self) -> NotificationType: if isinstance(self.cause, StatusChanged): return "reporting.status_changed" return ( "reporting.delivery_ready" if isinstance(self.cause, MaterializationReady) else "reporting.ledger_changed" ) @property def cause_id(self) -> str: cause = self.cause if isinstance(cause, RevisionPublished): return cause.reporting_revision_id if isinstance(cause, AdjustmentPublished): return cause.reporting_adjustment_id if isinstance(cause, StatusChanged): from adcp.reporting.canonical_json import canonical_json_utf8_v1 return ( "rpsc_" + hashlib.sha256(canonical_json_utf8_v1(cause.scope.checkpoint_key)).hexdigest() ) # Structured encoding: consumers and materializations may reuse IDs. return json.dumps( [cause.consumer_id, cause.reporting_materialization_id], separators=(",", ":") ) @property def consumer_namespace(self) -> str: if isinstance(self.cause, StatusChanged): return self.cause.scope.consumer_id or "" return self.cause.consumer_id @property def causal_key(self) -> tuple[str, str, str, str, str, int]: return ( self.account_id, self.consumer_namespace, self.notification_type, self.cause.kind, self.cause_id, self.cause_generation, ) def body(self, *, subscriber_id: str, idempotency_key: str) -> bytes: """Explicit wire allowlist, validated against all rc.3 conditionals.""" from adcp.reporting.canonical_json import canonical_json_utf8_v1 value: dict[str, Any] = { "account_id": self.account_id, "notification_id": self.notification_id, "notification_type": self.notification_type, "fired_at": iso(self.fired_at), "subscriber_id": subscriber_id, "idempotency_key": idempotency_key, } cause = self.cause if isinstance(cause, RevisionPublished): value.update( change_kind=cause.kind, reporting_revision_id=cause.reporting_revision_id, finality=cause.finality, ) if cause.supersedes_reporting_revision_id is not None: value["supersedes_reporting_revision_id"] = cause.supersedes_reporting_revision_id elif isinstance(cause, AdjustmentPublished): value.update( change_kind=cause.kind, reporting_adjustment_id=cause.reporting_adjustment_id, adjusts_reporting_revision_id=cause.adjusts_reporting_revision_id, ) elif isinstance(cause, StatusChanged): key = cause.scope.generation_key assert key is not None value.update( delivery_config_id=key.delivery_config_id, delivery_config_version=key.delivery_config_version, feed_purpose=cause.scope.feed_purpose, health=cause.health, ) if cause.scope.reporting_obligation_id is not None: value["reporting_obligation_id"] = cause.scope.reporting_obligation_id if cause.previous_health is not None: value["previous_health"] = cause.previous_health if cause.issue_ids: value["issue_ids"] = list(cause.issue_ids) else: value.update( delivery_config_id=cause.generation_key.delivery_config_id, delivery_config_version=cause.generation_key.delivery_config_version, reporting_revision_id=cause.reporting_revision_id, reporting_materialization_id=cause.reporting_materialization_id, readiness=cause.readiness, finality=cause.finality, data_through=iso(cause.data_through) if cause.data_through is not None else None, feed_purpose=cause.feed_purpose, ) validate_notification_payload(value) return canonical_json_utf8_v1(value) prop causal_key : tuple[str, str, str, str, str, int]-
Expand source code
@property def causal_key(self) -> tuple[str, str, str, str, str, int]: return ( self.account_id, self.consumer_namespace, self.notification_type, self.cause.kind, self.cause_id, self.cause_generation, ) var cause : RevisionPublished | AdjustmentPublished | MaterializationReady | StatusChanged-
Expand source code
@dataclass(frozen=True, slots=True) class ReportingDomainEvent(_ClosedValue): account_id: str notification_id: str fired_at: datetime cause: NotificationCause cause_generation: int = 1 def __post_init__(self) -> None: _freeze_fields(self) principal_reference(self.account_id) reporting_identifier(self.notification_id, maximum=255) object.__setattr__(self, "fired_at", aware_utc(self.fired_at)) if self.cause_generation < 1: raise ReportingNotificationError() if isinstance(self.cause, MaterializationReady): if self.cause.generation_key.account_id != self.account_id: raise ReportingNotificationError() if isinstance(self.cause, StatusChanged) and ( self.cause.scope.account_id != self.account_id or self.cause.checkpoint_generation != self.cause_generation ): raise ReportingNotificationError() @property def notification_type(self) -> NotificationType: if isinstance(self.cause, StatusChanged): return "reporting.status_changed" return ( "reporting.delivery_ready" if isinstance(self.cause, MaterializationReady) else "reporting.ledger_changed" ) @property def cause_id(self) -> str: cause = self.cause if isinstance(cause, RevisionPublished): return cause.reporting_revision_id if isinstance(cause, AdjustmentPublished): return cause.reporting_adjustment_id if isinstance(cause, StatusChanged): from adcp.reporting.canonical_json import canonical_json_utf8_v1 return ( "rpsc_" + hashlib.sha256(canonical_json_utf8_v1(cause.scope.checkpoint_key)).hexdigest() ) # Structured encoding: consumers and materializations may reuse IDs. return json.dumps( [cause.consumer_id, cause.reporting_materialization_id], separators=(",", ":") ) @property def consumer_namespace(self) -> str: if isinstance(self.cause, StatusChanged): return self.cause.scope.consumer_id or "" return self.cause.consumer_id @property def causal_key(self) -> tuple[str, str, str, str, str, int]: return ( self.account_id, self.consumer_namespace, self.notification_type, self.cause.kind, self.cause_id, self.cause_generation, ) def body(self, *, subscriber_id: str, idempotency_key: str) -> bytes: """Explicit wire allowlist, validated against all rc.3 conditionals.""" from adcp.reporting.canonical_json import canonical_json_utf8_v1 value: dict[str, Any] = { "account_id": self.account_id, "notification_id": self.notification_id, "notification_type": self.notification_type, "fired_at": iso(self.fired_at), "subscriber_id": subscriber_id, "idempotency_key": idempotency_key, } cause = self.cause if isinstance(cause, RevisionPublished): value.update( change_kind=cause.kind, reporting_revision_id=cause.reporting_revision_id, finality=cause.finality, ) if cause.supersedes_reporting_revision_id is not None: value["supersedes_reporting_revision_id"] = cause.supersedes_reporting_revision_id elif isinstance(cause, AdjustmentPublished): value.update( change_kind=cause.kind, reporting_adjustment_id=cause.reporting_adjustment_id, adjusts_reporting_revision_id=cause.adjusts_reporting_revision_id, ) elif isinstance(cause, StatusChanged): key = cause.scope.generation_key assert key is not None value.update( delivery_config_id=key.delivery_config_id, delivery_config_version=key.delivery_config_version, feed_purpose=cause.scope.feed_purpose, health=cause.health, ) if cause.scope.reporting_obligation_id is not None: value["reporting_obligation_id"] = cause.scope.reporting_obligation_id if cause.previous_health is not None: value["previous_health"] = cause.previous_health if cause.issue_ids: value["issue_ids"] = list(cause.issue_ids) else: value.update( delivery_config_id=cause.generation_key.delivery_config_id, delivery_config_version=cause.generation_key.delivery_config_version, reporting_revision_id=cause.reporting_revision_id, reporting_materialization_id=cause.reporting_materialization_id, readiness=cause.readiness, finality=cause.finality, data_through=iso(cause.data_through) if cause.data_through is not None else None, feed_purpose=cause.feed_purpose, ) validate_notification_payload(value) return canonical_json_utf8_v1(value) var cause_generation : int-
Expand source code
@dataclass(frozen=True, slots=True) class ReportingDomainEvent(_ClosedValue): account_id: str notification_id: str fired_at: datetime cause: NotificationCause cause_generation: int = 1 def __post_init__(self) -> None: _freeze_fields(self) principal_reference(self.account_id) reporting_identifier(self.notification_id, maximum=255) object.__setattr__(self, "fired_at", aware_utc(self.fired_at)) if self.cause_generation < 1: raise ReportingNotificationError() if isinstance(self.cause, MaterializationReady): if self.cause.generation_key.account_id != self.account_id: raise ReportingNotificationError() if isinstance(self.cause, StatusChanged) and ( self.cause.scope.account_id != self.account_id or self.cause.checkpoint_generation != self.cause_generation ): raise ReportingNotificationError() @property def notification_type(self) -> NotificationType: if isinstance(self.cause, StatusChanged): return "reporting.status_changed" return ( "reporting.delivery_ready" if isinstance(self.cause, MaterializationReady) else "reporting.ledger_changed" ) @property def cause_id(self) -> str: cause = self.cause if isinstance(cause, RevisionPublished): return cause.reporting_revision_id if isinstance(cause, AdjustmentPublished): return cause.reporting_adjustment_id if isinstance(cause, StatusChanged): from adcp.reporting.canonical_json import canonical_json_utf8_v1 return ( "rpsc_" + hashlib.sha256(canonical_json_utf8_v1(cause.scope.checkpoint_key)).hexdigest() ) # Structured encoding: consumers and materializations may reuse IDs. return json.dumps( [cause.consumer_id, cause.reporting_materialization_id], separators=(",", ":") ) @property def consumer_namespace(self) -> str: if isinstance(self.cause, StatusChanged): return self.cause.scope.consumer_id or "" return self.cause.consumer_id @property def causal_key(self) -> tuple[str, str, str, str, str, int]: return ( self.account_id, self.consumer_namespace, self.notification_type, self.cause.kind, self.cause_id, self.cause_generation, ) def body(self, *, subscriber_id: str, idempotency_key: str) -> bytes: """Explicit wire allowlist, validated against all rc.3 conditionals.""" from adcp.reporting.canonical_json import canonical_json_utf8_v1 value: dict[str, Any] = { "account_id": self.account_id, "notification_id": self.notification_id, "notification_type": self.notification_type, "fired_at": iso(self.fired_at), "subscriber_id": subscriber_id, "idempotency_key": idempotency_key, } cause = self.cause if isinstance(cause, RevisionPublished): value.update( change_kind=cause.kind, reporting_revision_id=cause.reporting_revision_id, finality=cause.finality, ) if cause.supersedes_reporting_revision_id is not None: value["supersedes_reporting_revision_id"] = cause.supersedes_reporting_revision_id elif isinstance(cause, AdjustmentPublished): value.update( change_kind=cause.kind, reporting_adjustment_id=cause.reporting_adjustment_id, adjusts_reporting_revision_id=cause.adjusts_reporting_revision_id, ) elif isinstance(cause, StatusChanged): key = cause.scope.generation_key assert key is not None value.update( delivery_config_id=key.delivery_config_id, delivery_config_version=key.delivery_config_version, feed_purpose=cause.scope.feed_purpose, health=cause.health, ) if cause.scope.reporting_obligation_id is not None: value["reporting_obligation_id"] = cause.scope.reporting_obligation_id if cause.previous_health is not None: value["previous_health"] = cause.previous_health if cause.issue_ids: value["issue_ids"] = list(cause.issue_ids) else: value.update( delivery_config_id=cause.generation_key.delivery_config_id, delivery_config_version=cause.generation_key.delivery_config_version, reporting_revision_id=cause.reporting_revision_id, reporting_materialization_id=cause.reporting_materialization_id, readiness=cause.readiness, finality=cause.finality, data_through=iso(cause.data_through) if cause.data_through is not None else None, feed_purpose=cause.feed_purpose, ) validate_notification_payload(value) return canonical_json_utf8_v1(value) prop cause_id : str-
Expand source code
@property def cause_id(self) -> str: cause = self.cause if isinstance(cause, RevisionPublished): return cause.reporting_revision_id if isinstance(cause, AdjustmentPublished): return cause.reporting_adjustment_id if isinstance(cause, StatusChanged): from adcp.reporting.canonical_json import canonical_json_utf8_v1 return ( "rpsc_" + hashlib.sha256(canonical_json_utf8_v1(cause.scope.checkpoint_key)).hexdigest() ) # Structured encoding: consumers and materializations may reuse IDs. return json.dumps( [cause.consumer_id, cause.reporting_materialization_id], separators=(",", ":") ) prop consumer_namespace : str-
Expand source code
@property def consumer_namespace(self) -> str: if isinstance(self.cause, StatusChanged): return self.cause.scope.consumer_id or "" return self.cause.consumer_id var fired_at : datetime.datetime-
Expand source code
@dataclass(frozen=True, slots=True) class ReportingDomainEvent(_ClosedValue): account_id: str notification_id: str fired_at: datetime cause: NotificationCause cause_generation: int = 1 def __post_init__(self) -> None: _freeze_fields(self) principal_reference(self.account_id) reporting_identifier(self.notification_id, maximum=255) object.__setattr__(self, "fired_at", aware_utc(self.fired_at)) if self.cause_generation < 1: raise ReportingNotificationError() if isinstance(self.cause, MaterializationReady): if self.cause.generation_key.account_id != self.account_id: raise ReportingNotificationError() if isinstance(self.cause, StatusChanged) and ( self.cause.scope.account_id != self.account_id or self.cause.checkpoint_generation != self.cause_generation ): raise ReportingNotificationError() @property def notification_type(self) -> NotificationType: if isinstance(self.cause, StatusChanged): return "reporting.status_changed" return ( "reporting.delivery_ready" if isinstance(self.cause, MaterializationReady) else "reporting.ledger_changed" ) @property def cause_id(self) -> str: cause = self.cause if isinstance(cause, RevisionPublished): return cause.reporting_revision_id if isinstance(cause, AdjustmentPublished): return cause.reporting_adjustment_id if isinstance(cause, StatusChanged): from adcp.reporting.canonical_json import canonical_json_utf8_v1 return ( "rpsc_" + hashlib.sha256(canonical_json_utf8_v1(cause.scope.checkpoint_key)).hexdigest() ) # Structured encoding: consumers and materializations may reuse IDs. return json.dumps( [cause.consumer_id, cause.reporting_materialization_id], separators=(",", ":") ) @property def consumer_namespace(self) -> str: if isinstance(self.cause, StatusChanged): return self.cause.scope.consumer_id or "" return self.cause.consumer_id @property def causal_key(self) -> tuple[str, str, str, str, str, int]: return ( self.account_id, self.consumer_namespace, self.notification_type, self.cause.kind, self.cause_id, self.cause_generation, ) def body(self, *, subscriber_id: str, idempotency_key: str) -> bytes: """Explicit wire allowlist, validated against all rc.3 conditionals.""" from adcp.reporting.canonical_json import canonical_json_utf8_v1 value: dict[str, Any] = { "account_id": self.account_id, "notification_id": self.notification_id, "notification_type": self.notification_type, "fired_at": iso(self.fired_at), "subscriber_id": subscriber_id, "idempotency_key": idempotency_key, } cause = self.cause if isinstance(cause, RevisionPublished): value.update( change_kind=cause.kind, reporting_revision_id=cause.reporting_revision_id, finality=cause.finality, ) if cause.supersedes_reporting_revision_id is not None: value["supersedes_reporting_revision_id"] = cause.supersedes_reporting_revision_id elif isinstance(cause, AdjustmentPublished): value.update( change_kind=cause.kind, reporting_adjustment_id=cause.reporting_adjustment_id, adjusts_reporting_revision_id=cause.adjusts_reporting_revision_id, ) elif isinstance(cause, StatusChanged): key = cause.scope.generation_key assert key is not None value.update( delivery_config_id=key.delivery_config_id, delivery_config_version=key.delivery_config_version, feed_purpose=cause.scope.feed_purpose, health=cause.health, ) if cause.scope.reporting_obligation_id is not None: value["reporting_obligation_id"] = cause.scope.reporting_obligation_id if cause.previous_health is not None: value["previous_health"] = cause.previous_health if cause.issue_ids: value["issue_ids"] = list(cause.issue_ids) else: value.update( delivery_config_id=cause.generation_key.delivery_config_id, delivery_config_version=cause.generation_key.delivery_config_version, reporting_revision_id=cause.reporting_revision_id, reporting_materialization_id=cause.reporting_materialization_id, readiness=cause.readiness, finality=cause.finality, data_through=iso(cause.data_through) if cause.data_through is not None else None, feed_purpose=cause.feed_purpose, ) validate_notification_payload(value) return canonical_json_utf8_v1(value) var notification_id : str-
Expand source code
@dataclass(frozen=True, slots=True) class ReportingDomainEvent(_ClosedValue): account_id: str notification_id: str fired_at: datetime cause: NotificationCause cause_generation: int = 1 def __post_init__(self) -> None: _freeze_fields(self) principal_reference(self.account_id) reporting_identifier(self.notification_id, maximum=255) object.__setattr__(self, "fired_at", aware_utc(self.fired_at)) if self.cause_generation < 1: raise ReportingNotificationError() if isinstance(self.cause, MaterializationReady): if self.cause.generation_key.account_id != self.account_id: raise ReportingNotificationError() if isinstance(self.cause, StatusChanged) and ( self.cause.scope.account_id != self.account_id or self.cause.checkpoint_generation != self.cause_generation ): raise ReportingNotificationError() @property def notification_type(self) -> NotificationType: if isinstance(self.cause, StatusChanged): return "reporting.status_changed" return ( "reporting.delivery_ready" if isinstance(self.cause, MaterializationReady) else "reporting.ledger_changed" ) @property def cause_id(self) -> str: cause = self.cause if isinstance(cause, RevisionPublished): return cause.reporting_revision_id if isinstance(cause, AdjustmentPublished): return cause.reporting_adjustment_id if isinstance(cause, StatusChanged): from adcp.reporting.canonical_json import canonical_json_utf8_v1 return ( "rpsc_" + hashlib.sha256(canonical_json_utf8_v1(cause.scope.checkpoint_key)).hexdigest() ) # Structured encoding: consumers and materializations may reuse IDs. return json.dumps( [cause.consumer_id, cause.reporting_materialization_id], separators=(",", ":") ) @property def consumer_namespace(self) -> str: if isinstance(self.cause, StatusChanged): return self.cause.scope.consumer_id or "" return self.cause.consumer_id @property def causal_key(self) -> tuple[str, str, str, str, str, int]: return ( self.account_id, self.consumer_namespace, self.notification_type, self.cause.kind, self.cause_id, self.cause_generation, ) def body(self, *, subscriber_id: str, idempotency_key: str) -> bytes: """Explicit wire allowlist, validated against all rc.3 conditionals.""" from adcp.reporting.canonical_json import canonical_json_utf8_v1 value: dict[str, Any] = { "account_id": self.account_id, "notification_id": self.notification_id, "notification_type": self.notification_type, "fired_at": iso(self.fired_at), "subscriber_id": subscriber_id, "idempotency_key": idempotency_key, } cause = self.cause if isinstance(cause, RevisionPublished): value.update( change_kind=cause.kind, reporting_revision_id=cause.reporting_revision_id, finality=cause.finality, ) if cause.supersedes_reporting_revision_id is not None: value["supersedes_reporting_revision_id"] = cause.supersedes_reporting_revision_id elif isinstance(cause, AdjustmentPublished): value.update( change_kind=cause.kind, reporting_adjustment_id=cause.reporting_adjustment_id, adjusts_reporting_revision_id=cause.adjusts_reporting_revision_id, ) elif isinstance(cause, StatusChanged): key = cause.scope.generation_key assert key is not None value.update( delivery_config_id=key.delivery_config_id, delivery_config_version=key.delivery_config_version, feed_purpose=cause.scope.feed_purpose, health=cause.health, ) if cause.scope.reporting_obligation_id is not None: value["reporting_obligation_id"] = cause.scope.reporting_obligation_id if cause.previous_health is not None: value["previous_health"] = cause.previous_health if cause.issue_ids: value["issue_ids"] = list(cause.issue_ids) else: value.update( delivery_config_id=cause.generation_key.delivery_config_id, delivery_config_version=cause.generation_key.delivery_config_version, reporting_revision_id=cause.reporting_revision_id, reporting_materialization_id=cause.reporting_materialization_id, readiness=cause.readiness, finality=cause.finality, data_through=iso(cause.data_through) if cause.data_through is not None else None, feed_purpose=cause.feed_purpose, ) validate_notification_payload(value) return canonical_json_utf8_v1(value) prop notification_type : NotificationType-
Expand source code
@property def notification_type(self) -> NotificationType: if isinstance(self.cause, StatusChanged): return "reporting.status_changed" return ( "reporting.delivery_ready" if isinstance(self.cause, MaterializationReady) else "reporting.ledger_changed" )
Methods
def body(self, *, subscriber_id: str, idempotency_key: str) ‑> bytes-
Expand source code
def body(self, *, subscriber_id: str, idempotency_key: str) -> bytes: """Explicit wire allowlist, validated against all rc.3 conditionals.""" from adcp.reporting.canonical_json import canonical_json_utf8_v1 value: dict[str, Any] = { "account_id": self.account_id, "notification_id": self.notification_id, "notification_type": self.notification_type, "fired_at": iso(self.fired_at), "subscriber_id": subscriber_id, "idempotency_key": idempotency_key, } cause = self.cause if isinstance(cause, RevisionPublished): value.update( change_kind=cause.kind, reporting_revision_id=cause.reporting_revision_id, finality=cause.finality, ) if cause.supersedes_reporting_revision_id is not None: value["supersedes_reporting_revision_id"] = cause.supersedes_reporting_revision_id elif isinstance(cause, AdjustmentPublished): value.update( change_kind=cause.kind, reporting_adjustment_id=cause.reporting_adjustment_id, adjusts_reporting_revision_id=cause.adjusts_reporting_revision_id, ) elif isinstance(cause, StatusChanged): key = cause.scope.generation_key assert key is not None value.update( delivery_config_id=key.delivery_config_id, delivery_config_version=key.delivery_config_version, feed_purpose=cause.scope.feed_purpose, health=cause.health, ) if cause.scope.reporting_obligation_id is not None: value["reporting_obligation_id"] = cause.scope.reporting_obligation_id if cause.previous_health is not None: value["previous_health"] = cause.previous_health if cause.issue_ids: value["issue_ids"] = list(cause.issue_ids) else: value.update( delivery_config_id=cause.generation_key.delivery_config_id, delivery_config_version=cause.generation_key.delivery_config_version, reporting_revision_id=cause.reporting_revision_id, reporting_materialization_id=cause.reporting_materialization_id, readiness=cause.readiness, finality=cause.finality, data_through=iso(cause.data_through) if cause.data_through is not None else None, feed_purpose=cause.feed_purpose, ) validate_notification_payload(value) return canonical_json_utf8_v1(value)Explicit wire allowlist, validated against all rc.3 conditionals.
class ReportingNotificationError (code: str = 'invalid_notification')-
Expand source code
class ReportingNotificationError(ValueError): """A sanitized local classification, never an external diagnostic.""" def __init__(self, code: str = "invalid_notification") -> None: self.code = code super().__init__(code)A sanitized local classification, never an external diagnostic.
Ancestors
- builtins.ValueError
- builtins.Exception
- builtins.BaseException
class ReportingStatusDirty (sequence: int,
scope: ReportingStatusScope,
reason: DirtyReason,
changed_at: datetime,
cause_id: str,
cause_generation: int,
before: ReportingStatusEvidence | None = None,
after: ReportingStatusEvidence | None = None)-
Expand source code
@dataclass(frozen=True, slots=True) class ReportingStatusDirty(_ClosedValue): sequence: int scope: ReportingStatusScope reason: DirtyReason changed_at: datetime cause_id: str cause_generation: int before: ReportingStatusEvidence | None = None after: ReportingStatusEvidence | None = None def __post_init__(self) -> None: _freeze_fields(self) object.__setattr__(self, "changed_at", aware_utc(self.changed_at)) if self.sequence < 1 or self.cause_generation < 1: raise ReportingNotificationError("invalid_status_evidence")ReportingStatusDirty(sequence: 'int', scope: 'ReportingStatusScope', reason: 'DirtyReason', changed_at: 'datetime', cause_id: 'str', cause_generation: 'int', before: 'ReportingStatusEvidence | None' = None, after: 'ReportingStatusEvidence | None' = None)
Ancestors
- adcp.reporting.ledger.delivery_models._ClosedValue
Instance variables
var after : ReportingStatusEvidence | None-
Expand source code
@dataclass(frozen=True, slots=True) class ReportingStatusDirty(_ClosedValue): sequence: int scope: ReportingStatusScope reason: DirtyReason changed_at: datetime cause_id: str cause_generation: int before: ReportingStatusEvidence | None = None after: ReportingStatusEvidence | None = None def __post_init__(self) -> None: _freeze_fields(self) object.__setattr__(self, "changed_at", aware_utc(self.changed_at)) if self.sequence < 1 or self.cause_generation < 1: raise ReportingNotificationError("invalid_status_evidence") var before : ReportingStatusEvidence | None-
Expand source code
@dataclass(frozen=True, slots=True) class ReportingStatusDirty(_ClosedValue): sequence: int scope: ReportingStatusScope reason: DirtyReason changed_at: datetime cause_id: str cause_generation: int before: ReportingStatusEvidence | None = None after: ReportingStatusEvidence | None = None def __post_init__(self) -> None: _freeze_fields(self) object.__setattr__(self, "changed_at", aware_utc(self.changed_at)) if self.sequence < 1 or self.cause_generation < 1: raise ReportingNotificationError("invalid_status_evidence") var cause_generation : int-
Expand source code
@dataclass(frozen=True, slots=True) class ReportingStatusDirty(_ClosedValue): sequence: int scope: ReportingStatusScope reason: DirtyReason changed_at: datetime cause_id: str cause_generation: int before: ReportingStatusEvidence | None = None after: ReportingStatusEvidence | None = None def __post_init__(self) -> None: _freeze_fields(self) object.__setattr__(self, "changed_at", aware_utc(self.changed_at)) if self.sequence < 1 or self.cause_generation < 1: raise ReportingNotificationError("invalid_status_evidence") var cause_id : str-
Expand source code
@dataclass(frozen=True, slots=True) class ReportingStatusDirty(_ClosedValue): sequence: int scope: ReportingStatusScope reason: DirtyReason changed_at: datetime cause_id: str cause_generation: int before: ReportingStatusEvidence | None = None after: ReportingStatusEvidence | None = None def __post_init__(self) -> None: _freeze_fields(self) object.__setattr__(self, "changed_at", aware_utc(self.changed_at)) if self.sequence < 1 or self.cause_generation < 1: raise ReportingNotificationError("invalid_status_evidence") var changed_at : datetime.datetime-
Expand source code
@dataclass(frozen=True, slots=True) class ReportingStatusDirty(_ClosedValue): sequence: int scope: ReportingStatusScope reason: DirtyReason changed_at: datetime cause_id: str cause_generation: int before: ReportingStatusEvidence | None = None after: ReportingStatusEvidence | None = None def __post_init__(self) -> None: _freeze_fields(self) object.__setattr__(self, "changed_at", aware_utc(self.changed_at)) if self.sequence < 1 or self.cause_generation < 1: raise ReportingNotificationError("invalid_status_evidence") var reason : Literal['configuration', 'obligation', 'revision', 'adjustment', 'readability', 'consumer_status', 'issue', 'destination', 'materialization', 'receipt', 'clock']-
Expand source code
@dataclass(frozen=True, slots=True) class ReportingStatusDirty(_ClosedValue): sequence: int scope: ReportingStatusScope reason: DirtyReason changed_at: datetime cause_id: str cause_generation: int before: ReportingStatusEvidence | None = None after: ReportingStatusEvidence | None = None def __post_init__(self) -> None: _freeze_fields(self) object.__setattr__(self, "changed_at", aware_utc(self.changed_at)) if self.sequence < 1 or self.cause_generation < 1: raise ReportingNotificationError("invalid_status_evidence") var scope : ReportingStatusScope-
Expand source code
@dataclass(frozen=True, slots=True) class ReportingStatusDirty(_ClosedValue): sequence: int scope: ReportingStatusScope reason: DirtyReason changed_at: datetime cause_id: str cause_generation: int before: ReportingStatusEvidence | None = None after: ReportingStatusEvidence | None = None def __post_init__(self) -> None: _freeze_fields(self) object.__setattr__(self, "changed_at", aware_utc(self.changed_at)) if self.sequence < 1 or self.cause_generation < 1: raise ReportingNotificationError("invalid_status_evidence") var sequence : int-
Expand source code
@dataclass(frozen=True, slots=True) class ReportingStatusDirty(_ClosedValue): sequence: int scope: ReportingStatusScope reason: DirtyReason changed_at: datetime cause_id: str cause_generation: int before: ReportingStatusEvidence | None = None after: ReportingStatusEvidence | None = None def __post_init__(self) -> None: _freeze_fields(self) object.__setattr__(self, "changed_at", aware_utc(self.changed_at)) if self.sequence < 1 or self.cause_generation < 1: raise ReportingNotificationError("invalid_status_evidence")
class ReportingStatusEvidence (record_kind: "Literal['configuration', 'obligation', 'revision', 'adjustment', 'consumer_status', 'issue', 'destination_binding', 'obligation_delivery', 'materialization_attempt', 'materialization', 'materialization_check', 'revision_receipt', 'adjustment_receipt', 'clock']",
record_id: str,
record_version: int = 1,
readable: bool | None = None,
issue_state: "Literal['open', 'acknowledged', 'waived', 'resolved'] | None" = None,
opened_at: datetime | None = None,
retired_at: datetime | None = None,
supersedes_id: str | None = None,
activated_at: datetime | None = None,
deactivated_at: datetime | None = None,
automated_recovery_seconds: float | None = None,
status_retention_days: int | None = None)-
Expand source code
@dataclass(frozen=True, slots=True) class ReportingStatusEvidence(_ClosedValue): """Replay evidence for one status mutation, containing no adopter prose. Immutable records are referenced in their account/consumer namespace. The mutable inputs (readability and issue lifecycle) are snapshotted on both sides, so rapid reversals cannot disappear before a projector runs. """ record_kind: Literal[ "configuration", "obligation", "revision", "adjustment", "consumer_status", "issue", "destination_binding", "obligation_delivery", "materialization_attempt", "materialization", "materialization_check", "revision_receipt", "adjustment_receipt", "clock", ] record_id: str record_version: int = 1 readable: bool | None = None issue_state: Literal["open", "acknowledged", "waived", "resolved"] | None = None opened_at: datetime | None = None retired_at: datetime | None = None supersedes_id: str | None = None activated_at: datetime | None = None deactivated_at: datetime | None = None automated_recovery_seconds: float | None = None status_retention_days: int | None = None def __post_init__(self) -> None: _freeze_fields(self) reporting_identifier(self.record_id, maximum=255) if self.record_version < 1: raise ReportingNotificationError("invalid_status_evidence") for name in ("opened_at", "retired_at", "activated_at", "deactivated_at"): value = getattr(self, name) if value is not None: object.__setattr__(self, name, aware_utc(value))Replay evidence for one status mutation, containing no adopter prose.
Immutable records are referenced in their account/consumer namespace. The mutable inputs (readability and issue lifecycle) are snapshotted on both sides, so rapid reversals cannot disappear before a projector runs.
Ancestors
- adcp.reporting.ledger.delivery_models._ClosedValue
Instance variables
var activated_at : datetime.datetime | None-
Expand source code
@dataclass(frozen=True, slots=True) class ReportingStatusEvidence(_ClosedValue): """Replay evidence for one status mutation, containing no adopter prose. Immutable records are referenced in their account/consumer namespace. The mutable inputs (readability and issue lifecycle) are snapshotted on both sides, so rapid reversals cannot disappear before a projector runs. """ record_kind: Literal[ "configuration", "obligation", "revision", "adjustment", "consumer_status", "issue", "destination_binding", "obligation_delivery", "materialization_attempt", "materialization", "materialization_check", "revision_receipt", "adjustment_receipt", "clock", ] record_id: str record_version: int = 1 readable: bool | None = None issue_state: Literal["open", "acknowledged", "waived", "resolved"] | None = None opened_at: datetime | None = None retired_at: datetime | None = None supersedes_id: str | None = None activated_at: datetime | None = None deactivated_at: datetime | None = None automated_recovery_seconds: float | None = None status_retention_days: int | None = None def __post_init__(self) -> None: _freeze_fields(self) reporting_identifier(self.record_id, maximum=255) if self.record_version < 1: raise ReportingNotificationError("invalid_status_evidence") for name in ("opened_at", "retired_at", "activated_at", "deactivated_at"): value = getattr(self, name) if value is not None: object.__setattr__(self, name, aware_utc(value)) var automated_recovery_seconds : float | None-
Expand source code
@dataclass(frozen=True, slots=True) class ReportingStatusEvidence(_ClosedValue): """Replay evidence for one status mutation, containing no adopter prose. Immutable records are referenced in their account/consumer namespace. The mutable inputs (readability and issue lifecycle) are snapshotted on both sides, so rapid reversals cannot disappear before a projector runs. """ record_kind: Literal[ "configuration", "obligation", "revision", "adjustment", "consumer_status", "issue", "destination_binding", "obligation_delivery", "materialization_attempt", "materialization", "materialization_check", "revision_receipt", "adjustment_receipt", "clock", ] record_id: str record_version: int = 1 readable: bool | None = None issue_state: Literal["open", "acknowledged", "waived", "resolved"] | None = None opened_at: datetime | None = None retired_at: datetime | None = None supersedes_id: str | None = None activated_at: datetime | None = None deactivated_at: datetime | None = None automated_recovery_seconds: float | None = None status_retention_days: int | None = None def __post_init__(self) -> None: _freeze_fields(self) reporting_identifier(self.record_id, maximum=255) if self.record_version < 1: raise ReportingNotificationError("invalid_status_evidence") for name in ("opened_at", "retired_at", "activated_at", "deactivated_at"): value = getattr(self, name) if value is not None: object.__setattr__(self, name, aware_utc(value)) var deactivated_at : datetime.datetime | None-
Expand source code
@dataclass(frozen=True, slots=True) class ReportingStatusEvidence(_ClosedValue): """Replay evidence for one status mutation, containing no adopter prose. Immutable records are referenced in their account/consumer namespace. The mutable inputs (readability and issue lifecycle) are snapshotted on both sides, so rapid reversals cannot disappear before a projector runs. """ record_kind: Literal[ "configuration", "obligation", "revision", "adjustment", "consumer_status", "issue", "destination_binding", "obligation_delivery", "materialization_attempt", "materialization", "materialization_check", "revision_receipt", "adjustment_receipt", "clock", ] record_id: str record_version: int = 1 readable: bool | None = None issue_state: Literal["open", "acknowledged", "waived", "resolved"] | None = None opened_at: datetime | None = None retired_at: datetime | None = None supersedes_id: str | None = None activated_at: datetime | None = None deactivated_at: datetime | None = None automated_recovery_seconds: float | None = None status_retention_days: int | None = None def __post_init__(self) -> None: _freeze_fields(self) reporting_identifier(self.record_id, maximum=255) if self.record_version < 1: raise ReportingNotificationError("invalid_status_evidence") for name in ("opened_at", "retired_at", "activated_at", "deactivated_at"): value = getattr(self, name) if value is not None: object.__setattr__(self, name, aware_utc(value)) var issue_state : Literal['open', 'acknowledged', 'resolved', 'waived'] | None-
Expand source code
@dataclass(frozen=True, slots=True) class ReportingStatusEvidence(_ClosedValue): """Replay evidence for one status mutation, containing no adopter prose. Immutable records are referenced in their account/consumer namespace. The mutable inputs (readability and issue lifecycle) are snapshotted on both sides, so rapid reversals cannot disappear before a projector runs. """ record_kind: Literal[ "configuration", "obligation", "revision", "adjustment", "consumer_status", "issue", "destination_binding", "obligation_delivery", "materialization_attempt", "materialization", "materialization_check", "revision_receipt", "adjustment_receipt", "clock", ] record_id: str record_version: int = 1 readable: bool | None = None issue_state: Literal["open", "acknowledged", "waived", "resolved"] | None = None opened_at: datetime | None = None retired_at: datetime | None = None supersedes_id: str | None = None activated_at: datetime | None = None deactivated_at: datetime | None = None automated_recovery_seconds: float | None = None status_retention_days: int | None = None def __post_init__(self) -> None: _freeze_fields(self) reporting_identifier(self.record_id, maximum=255) if self.record_version < 1: raise ReportingNotificationError("invalid_status_evidence") for name in ("opened_at", "retired_at", "activated_at", "deactivated_at"): value = getattr(self, name) if value is not None: object.__setattr__(self, name, aware_utc(value)) var opened_at : datetime.datetime | None-
Expand source code
@dataclass(frozen=True, slots=True) class ReportingStatusEvidence(_ClosedValue): """Replay evidence for one status mutation, containing no adopter prose. Immutable records are referenced in their account/consumer namespace. The mutable inputs (readability and issue lifecycle) are snapshotted on both sides, so rapid reversals cannot disappear before a projector runs. """ record_kind: Literal[ "configuration", "obligation", "revision", "adjustment", "consumer_status", "issue", "destination_binding", "obligation_delivery", "materialization_attempt", "materialization", "materialization_check", "revision_receipt", "adjustment_receipt", "clock", ] record_id: str record_version: int = 1 readable: bool | None = None issue_state: Literal["open", "acknowledged", "waived", "resolved"] | None = None opened_at: datetime | None = None retired_at: datetime | None = None supersedes_id: str | None = None activated_at: datetime | None = None deactivated_at: datetime | None = None automated_recovery_seconds: float | None = None status_retention_days: int | None = None def __post_init__(self) -> None: _freeze_fields(self) reporting_identifier(self.record_id, maximum=255) if self.record_version < 1: raise ReportingNotificationError("invalid_status_evidence") for name in ("opened_at", "retired_at", "activated_at", "deactivated_at"): value = getattr(self, name) if value is not None: object.__setattr__(self, name, aware_utc(value)) var readable : bool | None-
Expand source code
@dataclass(frozen=True, slots=True) class ReportingStatusEvidence(_ClosedValue): """Replay evidence for one status mutation, containing no adopter prose. Immutable records are referenced in their account/consumer namespace. The mutable inputs (readability and issue lifecycle) are snapshotted on both sides, so rapid reversals cannot disappear before a projector runs. """ record_kind: Literal[ "configuration", "obligation", "revision", "adjustment", "consumer_status", "issue", "destination_binding", "obligation_delivery", "materialization_attempt", "materialization", "materialization_check", "revision_receipt", "adjustment_receipt", "clock", ] record_id: str record_version: int = 1 readable: bool | None = None issue_state: Literal["open", "acknowledged", "waived", "resolved"] | None = None opened_at: datetime | None = None retired_at: datetime | None = None supersedes_id: str | None = None activated_at: datetime | None = None deactivated_at: datetime | None = None automated_recovery_seconds: float | None = None status_retention_days: int | None = None def __post_init__(self) -> None: _freeze_fields(self) reporting_identifier(self.record_id, maximum=255) if self.record_version < 1: raise ReportingNotificationError("invalid_status_evidence") for name in ("opened_at", "retired_at", "activated_at", "deactivated_at"): value = getattr(self, name) if value is not None: object.__setattr__(self, name, aware_utc(value)) var record_id : str-
Expand source code
@dataclass(frozen=True, slots=True) class ReportingStatusEvidence(_ClosedValue): """Replay evidence for one status mutation, containing no adopter prose. Immutable records are referenced in their account/consumer namespace. The mutable inputs (readability and issue lifecycle) are snapshotted on both sides, so rapid reversals cannot disappear before a projector runs. """ record_kind: Literal[ "configuration", "obligation", "revision", "adjustment", "consumer_status", "issue", "destination_binding", "obligation_delivery", "materialization_attempt", "materialization", "materialization_check", "revision_receipt", "adjustment_receipt", "clock", ] record_id: str record_version: int = 1 readable: bool | None = None issue_state: Literal["open", "acknowledged", "waived", "resolved"] | None = None opened_at: datetime | None = None retired_at: datetime | None = None supersedes_id: str | None = None activated_at: datetime | None = None deactivated_at: datetime | None = None automated_recovery_seconds: float | None = None status_retention_days: int | None = None def __post_init__(self) -> None: _freeze_fields(self) reporting_identifier(self.record_id, maximum=255) if self.record_version < 1: raise ReportingNotificationError("invalid_status_evidence") for name in ("opened_at", "retired_at", "activated_at", "deactivated_at"): value = getattr(self, name) if value is not None: object.__setattr__(self, name, aware_utc(value)) var record_kind : Literal['configuration', 'obligation', 'revision', 'adjustment', 'consumer_status', 'issue', 'destination_binding', 'obligation_delivery', 'materialization_attempt', 'materialization', 'materialization_check', 'revision_receipt', 'adjustment_receipt', 'clock']-
Expand source code
@dataclass(frozen=True, slots=True) class ReportingStatusEvidence(_ClosedValue): """Replay evidence for one status mutation, containing no adopter prose. Immutable records are referenced in their account/consumer namespace. The mutable inputs (readability and issue lifecycle) are snapshotted on both sides, so rapid reversals cannot disappear before a projector runs. """ record_kind: Literal[ "configuration", "obligation", "revision", "adjustment", "consumer_status", "issue", "destination_binding", "obligation_delivery", "materialization_attempt", "materialization", "materialization_check", "revision_receipt", "adjustment_receipt", "clock", ] record_id: str record_version: int = 1 readable: bool | None = None issue_state: Literal["open", "acknowledged", "waived", "resolved"] | None = None opened_at: datetime | None = None retired_at: datetime | None = None supersedes_id: str | None = None activated_at: datetime | None = None deactivated_at: datetime | None = None automated_recovery_seconds: float | None = None status_retention_days: int | None = None def __post_init__(self) -> None: _freeze_fields(self) reporting_identifier(self.record_id, maximum=255) if self.record_version < 1: raise ReportingNotificationError("invalid_status_evidence") for name in ("opened_at", "retired_at", "activated_at", "deactivated_at"): value = getattr(self, name) if value is not None: object.__setattr__(self, name, aware_utc(value)) var record_version : int-
Expand source code
@dataclass(frozen=True, slots=True) class ReportingStatusEvidence(_ClosedValue): """Replay evidence for one status mutation, containing no adopter prose. Immutable records are referenced in their account/consumer namespace. The mutable inputs (readability and issue lifecycle) are snapshotted on both sides, so rapid reversals cannot disappear before a projector runs. """ record_kind: Literal[ "configuration", "obligation", "revision", "adjustment", "consumer_status", "issue", "destination_binding", "obligation_delivery", "materialization_attempt", "materialization", "materialization_check", "revision_receipt", "adjustment_receipt", "clock", ] record_id: str record_version: int = 1 readable: bool | None = None issue_state: Literal["open", "acknowledged", "waived", "resolved"] | None = None opened_at: datetime | None = None retired_at: datetime | None = None supersedes_id: str | None = None activated_at: datetime | None = None deactivated_at: datetime | None = None automated_recovery_seconds: float | None = None status_retention_days: int | None = None def __post_init__(self) -> None: _freeze_fields(self) reporting_identifier(self.record_id, maximum=255) if self.record_version < 1: raise ReportingNotificationError("invalid_status_evidence") for name in ("opened_at", "retired_at", "activated_at", "deactivated_at"): value = getattr(self, name) if value is not None: object.__setattr__(self, name, aware_utc(value)) var retired_at : datetime.datetime | None-
Expand source code
@dataclass(frozen=True, slots=True) class ReportingStatusEvidence(_ClosedValue): """Replay evidence for one status mutation, containing no adopter prose. Immutable records are referenced in their account/consumer namespace. The mutable inputs (readability and issue lifecycle) are snapshotted on both sides, so rapid reversals cannot disappear before a projector runs. """ record_kind: Literal[ "configuration", "obligation", "revision", "adjustment", "consumer_status", "issue", "destination_binding", "obligation_delivery", "materialization_attempt", "materialization", "materialization_check", "revision_receipt", "adjustment_receipt", "clock", ] record_id: str record_version: int = 1 readable: bool | None = None issue_state: Literal["open", "acknowledged", "waived", "resolved"] | None = None opened_at: datetime | None = None retired_at: datetime | None = None supersedes_id: str | None = None activated_at: datetime | None = None deactivated_at: datetime | None = None automated_recovery_seconds: float | None = None status_retention_days: int | None = None def __post_init__(self) -> None: _freeze_fields(self) reporting_identifier(self.record_id, maximum=255) if self.record_version < 1: raise ReportingNotificationError("invalid_status_evidence") for name in ("opened_at", "retired_at", "activated_at", "deactivated_at"): value = getattr(self, name) if value is not None: object.__setattr__(self, name, aware_utc(value)) var status_retention_days : int | None-
Expand source code
@dataclass(frozen=True, slots=True) class ReportingStatusEvidence(_ClosedValue): """Replay evidence for one status mutation, containing no adopter prose. Immutable records are referenced in their account/consumer namespace. The mutable inputs (readability and issue lifecycle) are snapshotted on both sides, so rapid reversals cannot disappear before a projector runs. """ record_kind: Literal[ "configuration", "obligation", "revision", "adjustment", "consumer_status", "issue", "destination_binding", "obligation_delivery", "materialization_attempt", "materialization", "materialization_check", "revision_receipt", "adjustment_receipt", "clock", ] record_id: str record_version: int = 1 readable: bool | None = None issue_state: Literal["open", "acknowledged", "waived", "resolved"] | None = None opened_at: datetime | None = None retired_at: datetime | None = None supersedes_id: str | None = None activated_at: datetime | None = None deactivated_at: datetime | None = None automated_recovery_seconds: float | None = None status_retention_days: int | None = None def __post_init__(self) -> None: _freeze_fields(self) reporting_identifier(self.record_id, maximum=255) if self.record_version < 1: raise ReportingNotificationError("invalid_status_evidence") for name in ("opened_at", "retired_at", "activated_at", "deactivated_at"): value = getattr(self, name) if value is not None: object.__setattr__(self, name, aware_utc(value)) var supersedes_id : str | None-
Expand source code
@dataclass(frozen=True, slots=True) class ReportingStatusEvidence(_ClosedValue): """Replay evidence for one status mutation, containing no adopter prose. Immutable records are referenced in their account/consumer namespace. The mutable inputs (readability and issue lifecycle) are snapshotted on both sides, so rapid reversals cannot disappear before a projector runs. """ record_kind: Literal[ "configuration", "obligation", "revision", "adjustment", "consumer_status", "issue", "destination_binding", "obligation_delivery", "materialization_attempt", "materialization", "materialization_check", "revision_receipt", "adjustment_receipt", "clock", ] record_id: str record_version: int = 1 readable: bool | None = None issue_state: Literal["open", "acknowledged", "waived", "resolved"] | None = None opened_at: datetime | None = None retired_at: datetime | None = None supersedes_id: str | None = None activated_at: datetime | None = None deactivated_at: datetime | None = None automated_recovery_seconds: float | None = None status_retention_days: int | None = None def __post_init__(self) -> None: _freeze_fields(self) reporting_identifier(self.record_id, maximum=255) if self.record_version < 1: raise ReportingNotificationError("invalid_status_evidence") for name in ("opened_at", "retired_at", "activated_at", "deactivated_at"): value = getattr(self, name) if value is not None: object.__setattr__(self, name, aware_utc(value))
class ReportingStatusScope (account_id: str,
generation_key: ReportingConfigurationGenerationKey | None = None,
reporting_obligation_id: str | None = None,
consumer_id: str | None = None,
feed_purpose: FeedPurpose | None = None)-
Expand source code
@dataclass(frozen=True, slots=True) class ReportingStatusScope(_ClosedValue): """A typed projection target. Missing detail means invalidate the account. Legacy issue keys are opaque. They are never parsed to infer a generation, obligation, consumer, or health. Adopters may supply this optional scope to the concrete stores' issue methods without changing ReportingLedgerStore. """ account_id: str generation_key: ReportingConfigurationGenerationKey | None = None reporting_obligation_id: str | None = None consumer_id: str | None = None feed_purpose: FeedPurpose | None = None def __post_init__(self) -> None: _freeze_fields(self) principal_reference(self.account_id) if self.consumer_id is not None: consumer_reference(self.consumer_id) if self.reporting_obligation_id is not None: reporting_identifier(self.reporting_obligation_id, maximum=255) if self.generation_key is not None: if self.generation_key.account_id != self.account_id or self.consumer_id not in { None, self.generation_key.consumer_id, }: raise ReportingNotificationError("invalid_status_scope") object.__setattr__(self, "consumer_id", self.generation_key.consumer_id) @property def checkpoint_key(self) -> tuple[str, str, str, int, str, str]: """Six independent, non-null columns. Scope kinds never share an ID space.""" if self.generation_key is None: raise ReportingNotificationError("invalid_status_scope") return ( self.account_id, self.consumer_id or "", self.generation_key.delivery_config_id, self.generation_key.delivery_config_version, "obligation" if self.reporting_obligation_id is not None else "configuration", self.reporting_obligation_id or "", ) @classmethod def for_obligation( cls, obligation: ReportingObligationRecord, consumer_id: str | None = None ) -> ReportingStatusScope: # Existing ledger fields are strings; validating through the closed # adapter refuses a provider-supplied feed label rather than echoing it. return decode_status_scope( { "account_id": obligation.account_id, "generation_key": asdict(obligation.generation_key), "reporting_obligation_id": obligation.reporting_obligation_id, "consumer_id": obligation.consumer_id if consumer_id is None else consumer_id, "feed_purpose": obligation.feed_purpose, } )A typed projection target. Missing detail means invalidate the account.
Legacy issue keys are opaque. They are never parsed to infer a generation, obligation, consumer, or health. Adopters may supply this optional scope to the concrete stores' issue methods without changing ReportingLedgerStore.
Ancestors
- adcp.reporting.ledger.delivery_models._ClosedValue
Static methods
def for_obligation(obligation: ReportingObligationRecord, consumer_id: str | None = None) ‑> ReportingStatusScope
Instance variables
var account_id : str-
Expand source code
@dataclass(frozen=True, slots=True) class ReportingStatusScope(_ClosedValue): """A typed projection target. Missing detail means invalidate the account. Legacy issue keys are opaque. They are never parsed to infer a generation, obligation, consumer, or health. Adopters may supply this optional scope to the concrete stores' issue methods without changing ReportingLedgerStore. """ account_id: str generation_key: ReportingConfigurationGenerationKey | None = None reporting_obligation_id: str | None = None consumer_id: str | None = None feed_purpose: FeedPurpose | None = None def __post_init__(self) -> None: _freeze_fields(self) principal_reference(self.account_id) if self.consumer_id is not None: consumer_reference(self.consumer_id) if self.reporting_obligation_id is not None: reporting_identifier(self.reporting_obligation_id, maximum=255) if self.generation_key is not None: if self.generation_key.account_id != self.account_id or self.consumer_id not in { None, self.generation_key.consumer_id, }: raise ReportingNotificationError("invalid_status_scope") object.__setattr__(self, "consumer_id", self.generation_key.consumer_id) @property def checkpoint_key(self) -> tuple[str, str, str, int, str, str]: """Six independent, non-null columns. Scope kinds never share an ID space.""" if self.generation_key is None: raise ReportingNotificationError("invalid_status_scope") return ( self.account_id, self.consumer_id or "", self.generation_key.delivery_config_id, self.generation_key.delivery_config_version, "obligation" if self.reporting_obligation_id is not None else "configuration", self.reporting_obligation_id or "", ) @classmethod def for_obligation( cls, obligation: ReportingObligationRecord, consumer_id: str | None = None ) -> ReportingStatusScope: # Existing ledger fields are strings; validating through the closed # adapter refuses a provider-supplied feed label rather than echoing it. return decode_status_scope( { "account_id": obligation.account_id, "generation_key": asdict(obligation.generation_key), "reporting_obligation_id": obligation.reporting_obligation_id, "consumer_id": obligation.consumer_id if consumer_id is None else consumer_id, "feed_purpose": obligation.feed_purpose, } ) prop checkpoint_key : tuple[str, str, str, int, str, str]-
Expand source code
@property def checkpoint_key(self) -> tuple[str, str, str, int, str, str]: """Six independent, non-null columns. Scope kinds never share an ID space.""" if self.generation_key is None: raise ReportingNotificationError("invalid_status_scope") return ( self.account_id, self.consumer_id or "", self.generation_key.delivery_config_id, self.generation_key.delivery_config_version, "obligation" if self.reporting_obligation_id is not None else "configuration", self.reporting_obligation_id or "", )Six independent, non-null columns. Scope kinds never share an ID space.
var consumer_id : str | None-
Expand source code
@dataclass(frozen=True, slots=True) class ReportingStatusScope(_ClosedValue): """A typed projection target. Missing detail means invalidate the account. Legacy issue keys are opaque. They are never parsed to infer a generation, obligation, consumer, or health. Adopters may supply this optional scope to the concrete stores' issue methods without changing ReportingLedgerStore. """ account_id: str generation_key: ReportingConfigurationGenerationKey | None = None reporting_obligation_id: str | None = None consumer_id: str | None = None feed_purpose: FeedPurpose | None = None def __post_init__(self) -> None: _freeze_fields(self) principal_reference(self.account_id) if self.consumer_id is not None: consumer_reference(self.consumer_id) if self.reporting_obligation_id is not None: reporting_identifier(self.reporting_obligation_id, maximum=255) if self.generation_key is not None: if self.generation_key.account_id != self.account_id or self.consumer_id not in { None, self.generation_key.consumer_id, }: raise ReportingNotificationError("invalid_status_scope") object.__setattr__(self, "consumer_id", self.generation_key.consumer_id) @property def checkpoint_key(self) -> tuple[str, str, str, int, str, str]: """Six independent, non-null columns. Scope kinds never share an ID space.""" if self.generation_key is None: raise ReportingNotificationError("invalid_status_scope") return ( self.account_id, self.consumer_id or "", self.generation_key.delivery_config_id, self.generation_key.delivery_config_version, "obligation" if self.reporting_obligation_id is not None else "configuration", self.reporting_obligation_id or "", ) @classmethod def for_obligation( cls, obligation: ReportingObligationRecord, consumer_id: str | None = None ) -> ReportingStatusScope: # Existing ledger fields are strings; validating through the closed # adapter refuses a provider-supplied feed label rather than echoing it. return decode_status_scope( { "account_id": obligation.account_id, "generation_key": asdict(obligation.generation_key), "reporting_obligation_id": obligation.reporting_obligation_id, "consumer_id": obligation.consumer_id if consumer_id is None else consumer_id, "feed_purpose": obligation.feed_purpose, } ) var feed_purpose : Literal['pacing', 'analytics', 'billing'] | None-
Expand source code
@dataclass(frozen=True, slots=True) class ReportingStatusScope(_ClosedValue): """A typed projection target. Missing detail means invalidate the account. Legacy issue keys are opaque. They are never parsed to infer a generation, obligation, consumer, or health. Adopters may supply this optional scope to the concrete stores' issue methods without changing ReportingLedgerStore. """ account_id: str generation_key: ReportingConfigurationGenerationKey | None = None reporting_obligation_id: str | None = None consumer_id: str | None = None feed_purpose: FeedPurpose | None = None def __post_init__(self) -> None: _freeze_fields(self) principal_reference(self.account_id) if self.consumer_id is not None: consumer_reference(self.consumer_id) if self.reporting_obligation_id is not None: reporting_identifier(self.reporting_obligation_id, maximum=255) if self.generation_key is not None: if self.generation_key.account_id != self.account_id or self.consumer_id not in { None, self.generation_key.consumer_id, }: raise ReportingNotificationError("invalid_status_scope") object.__setattr__(self, "consumer_id", self.generation_key.consumer_id) @property def checkpoint_key(self) -> tuple[str, str, str, int, str, str]: """Six independent, non-null columns. Scope kinds never share an ID space.""" if self.generation_key is None: raise ReportingNotificationError("invalid_status_scope") return ( self.account_id, self.consumer_id or "", self.generation_key.delivery_config_id, self.generation_key.delivery_config_version, "obligation" if self.reporting_obligation_id is not None else "configuration", self.reporting_obligation_id or "", ) @classmethod def for_obligation( cls, obligation: ReportingObligationRecord, consumer_id: str | None = None ) -> ReportingStatusScope: # Existing ledger fields are strings; validating through the closed # adapter refuses a provider-supplied feed label rather than echoing it. return decode_status_scope( { "account_id": obligation.account_id, "generation_key": asdict(obligation.generation_key), "reporting_obligation_id": obligation.reporting_obligation_id, "consumer_id": obligation.consumer_id if consumer_id is None else consumer_id, "feed_purpose": obligation.feed_purpose, } ) var generation_key : ReportingConfigurationGenerationKey | None-
Expand source code
@dataclass(frozen=True, slots=True) class ReportingStatusScope(_ClosedValue): """A typed projection target. Missing detail means invalidate the account. Legacy issue keys are opaque. They are never parsed to infer a generation, obligation, consumer, or health. Adopters may supply this optional scope to the concrete stores' issue methods without changing ReportingLedgerStore. """ account_id: str generation_key: ReportingConfigurationGenerationKey | None = None reporting_obligation_id: str | None = None consumer_id: str | None = None feed_purpose: FeedPurpose | None = None def __post_init__(self) -> None: _freeze_fields(self) principal_reference(self.account_id) if self.consumer_id is not None: consumer_reference(self.consumer_id) if self.reporting_obligation_id is not None: reporting_identifier(self.reporting_obligation_id, maximum=255) if self.generation_key is not None: if self.generation_key.account_id != self.account_id or self.consumer_id not in { None, self.generation_key.consumer_id, }: raise ReportingNotificationError("invalid_status_scope") object.__setattr__(self, "consumer_id", self.generation_key.consumer_id) @property def checkpoint_key(self) -> tuple[str, str, str, int, str, str]: """Six independent, non-null columns. Scope kinds never share an ID space.""" if self.generation_key is None: raise ReportingNotificationError("invalid_status_scope") return ( self.account_id, self.consumer_id or "", self.generation_key.delivery_config_id, self.generation_key.delivery_config_version, "obligation" if self.reporting_obligation_id is not None else "configuration", self.reporting_obligation_id or "", ) @classmethod def for_obligation( cls, obligation: ReportingObligationRecord, consumer_id: str | None = None ) -> ReportingStatusScope: # Existing ledger fields are strings; validating through the closed # adapter refuses a provider-supplied feed label rather than echoing it. return decode_status_scope( { "account_id": obligation.account_id, "generation_key": asdict(obligation.generation_key), "reporting_obligation_id": obligation.reporting_obligation_id, "consumer_id": obligation.consumer_id if consumer_id is None else consumer_id, "feed_purpose": obligation.feed_purpose, } ) var reporting_obligation_id : str | None-
Expand source code
@dataclass(frozen=True, slots=True) class ReportingStatusScope(_ClosedValue): """A typed projection target. Missing detail means invalidate the account. Legacy issue keys are opaque. They are never parsed to infer a generation, obligation, consumer, or health. Adopters may supply this optional scope to the concrete stores' issue methods without changing ReportingLedgerStore. """ account_id: str generation_key: ReportingConfigurationGenerationKey | None = None reporting_obligation_id: str | None = None consumer_id: str | None = None feed_purpose: FeedPurpose | None = None def __post_init__(self) -> None: _freeze_fields(self) principal_reference(self.account_id) if self.consumer_id is not None: consumer_reference(self.consumer_id) if self.reporting_obligation_id is not None: reporting_identifier(self.reporting_obligation_id, maximum=255) if self.generation_key is not None: if self.generation_key.account_id != self.account_id or self.consumer_id not in { None, self.generation_key.consumer_id, }: raise ReportingNotificationError("invalid_status_scope") object.__setattr__(self, "consumer_id", self.generation_key.consumer_id) @property def checkpoint_key(self) -> tuple[str, str, str, int, str, str]: """Six independent, non-null columns. Scope kinds never share an ID space.""" if self.generation_key is None: raise ReportingNotificationError("invalid_status_scope") return ( self.account_id, self.consumer_id or "", self.generation_key.delivery_config_id, self.generation_key.delivery_config_version, "obligation" if self.reporting_obligation_id is not None else "configuration", self.reporting_obligation_id or "", ) @classmethod def for_obligation( cls, obligation: ReportingObligationRecord, consumer_id: str | None = None ) -> ReportingStatusScope: # Existing ledger fields are strings; validating through the closed # adapter refuses a provider-supplied feed label rather than echoing it. return decode_status_scope( { "account_id": obligation.account_id, "generation_key": asdict(obligation.generation_key), "reporting_obligation_id": obligation.reporting_obligation_id, "consumer_id": obligation.consumer_id if consumer_id is None else consumer_id, "feed_purpose": obligation.feed_purpose, } )
class RevisionPublished (reporting_revision_id: str,
consumer_id: str,
finality: ReportingFinality,
supersedes_reporting_revision_id: str | None = None,
*,
kind: "Literal['revision_published']" = 'revision_published')-
Expand source code
@dataclass(frozen=True, slots=True) class RevisionPublished(_ClosedValue): reporting_revision_id: str consumer_id: str finality: ReportingFinality supersedes_reporting_revision_id: str | None = None kind: Literal["revision_published"] = field(default="revision_published", kw_only=True) def __post_init__(self) -> None: _freeze_fields(self) reporting_identifier(self.reporting_revision_id, maximum=255) consumer_reference(self.consumer_id) if self.supersedes_reporting_revision_id is not None: reporting_identifier(self.supersedes_reporting_revision_id, maximum=255)RevisionPublished(reporting_revision_id: 'str', consumer_id: 'str', finality: 'ReportingFinality', supersedes_reporting_revision_id: 'str | None' = None, *, kind: "Literal['revision_published']" = 'revision_published')
Ancestors
- adcp.reporting.ledger.delivery_models._ClosedValue
Instance variables
var consumer_id : str-
Expand source code
@dataclass(frozen=True, slots=True) class RevisionPublished(_ClosedValue): reporting_revision_id: str consumer_id: str finality: ReportingFinality supersedes_reporting_revision_id: str | None = None kind: Literal["revision_published"] = field(default="revision_published", kw_only=True) def __post_init__(self) -> None: _freeze_fields(self) reporting_identifier(self.reporting_revision_id, maximum=255) consumer_reference(self.consumer_id) if self.supersedes_reporting_revision_id is not None: reporting_identifier(self.supersedes_reporting_revision_id, maximum=255) var finality : Literal['snapshot', 'official']-
Expand source code
@dataclass(frozen=True, slots=True) class RevisionPublished(_ClosedValue): reporting_revision_id: str consumer_id: str finality: ReportingFinality supersedes_reporting_revision_id: str | None = None kind: Literal["revision_published"] = field(default="revision_published", kw_only=True) def __post_init__(self) -> None: _freeze_fields(self) reporting_identifier(self.reporting_revision_id, maximum=255) consumer_reference(self.consumer_id) if self.supersedes_reporting_revision_id is not None: reporting_identifier(self.supersedes_reporting_revision_id, maximum=255) var kind : Literal['revision_published']-
Expand source code
@dataclass(frozen=True, slots=True) class RevisionPublished(_ClosedValue): reporting_revision_id: str consumer_id: str finality: ReportingFinality supersedes_reporting_revision_id: str | None = None kind: Literal["revision_published"] = field(default="revision_published", kw_only=True) def __post_init__(self) -> None: _freeze_fields(self) reporting_identifier(self.reporting_revision_id, maximum=255) consumer_reference(self.consumer_id) if self.supersedes_reporting_revision_id is not None: reporting_identifier(self.supersedes_reporting_revision_id, maximum=255) var reporting_revision_id : str-
Expand source code
@dataclass(frozen=True, slots=True) class RevisionPublished(_ClosedValue): reporting_revision_id: str consumer_id: str finality: ReportingFinality supersedes_reporting_revision_id: str | None = None kind: Literal["revision_published"] = field(default="revision_published", kw_only=True) def __post_init__(self) -> None: _freeze_fields(self) reporting_identifier(self.reporting_revision_id, maximum=255) consumer_reference(self.consumer_id) if self.supersedes_reporting_revision_id is not None: reporting_identifier(self.supersedes_reporting_revision_id, maximum=255) var supersedes_reporting_revision_id : str | None-
Expand source code
@dataclass(frozen=True, slots=True) class RevisionPublished(_ClosedValue): reporting_revision_id: str consumer_id: str finality: ReportingFinality supersedes_reporting_revision_id: str | None = None kind: Literal["revision_published"] = field(default="revision_published", kw_only=True) def __post_init__(self) -> None: _freeze_fields(self) reporting_identifier(self.reporting_revision_id, maximum=255) consumer_reference(self.consumer_id) if self.supersedes_reporting_revision_id is not None: reporting_identifier(self.supersedes_reporting_revision_id, maximum=255)
class StatusChanged (scope: ReportingStatusScope,
health: ReportingHealth,
fingerprint: str,
checkpoint_generation: int,
previous_health: ReportingHealth | None = None,
issue_ids: tuple[str, ...] = (),
*,
kind: "Literal['status_changed']" = 'status_changed')-
Expand source code
@dataclass(frozen=True, slots=True) class StatusChanged(_ClosedValue): """One locked scope checkpoint generation; private identity stays off the wire.""" scope: ReportingStatusScope health: ReportingHealth fingerprint: str checkpoint_generation: int previous_health: ReportingHealth | None = None issue_ids: tuple[str, ...] = () kind: Literal["status_changed"] = field(default="status_changed", kw_only=True) def __post_init__(self) -> None: _freeze_fields(self) if ( self.scope.generation_key is None or self.scope.feed_purpose is None or self.checkpoint_generation < 1 or len(self.fingerprint) != 64 or any(c not in "0123456789abcdef" for c in self.fingerprint) or tuple(sorted(set(self.issue_ids))) != self.issue_ids or len(self.issue_ids) > 16 or (self.health in {"delayed", "action_required"} and not self.issue_ids) ): raise ReportingNotificationError("invalid_status_projection") for issue_id in self.issue_ids: reporting_identifier(issue_id)One locked scope checkpoint generation; private identity stays off the wire.
Ancestors
- adcp.reporting.ledger.delivery_models._ClosedValue
Instance variables
var checkpoint_generation : int-
Expand source code
@dataclass(frozen=True, slots=True) class StatusChanged(_ClosedValue): """One locked scope checkpoint generation; private identity stays off the wire.""" scope: ReportingStatusScope health: ReportingHealth fingerprint: str checkpoint_generation: int previous_health: ReportingHealth | None = None issue_ids: tuple[str, ...] = () kind: Literal["status_changed"] = field(default="status_changed", kw_only=True) def __post_init__(self) -> None: _freeze_fields(self) if ( self.scope.generation_key is None or self.scope.feed_purpose is None or self.checkpoint_generation < 1 or len(self.fingerprint) != 64 or any(c not in "0123456789abcdef" for c in self.fingerprint) or tuple(sorted(set(self.issue_ids))) != self.issue_ids or len(self.issue_ids) > 16 or (self.health in {"delayed", "action_required"} and not self.issue_ids) ): raise ReportingNotificationError("invalid_status_projection") for issue_id in self.issue_ids: reporting_identifier(issue_id) var fingerprint : str-
Expand source code
@dataclass(frozen=True, slots=True) class StatusChanged(_ClosedValue): """One locked scope checkpoint generation; private identity stays off the wire.""" scope: ReportingStatusScope health: ReportingHealth fingerprint: str checkpoint_generation: int previous_health: ReportingHealth | None = None issue_ids: tuple[str, ...] = () kind: Literal["status_changed"] = field(default="status_changed", kw_only=True) def __post_init__(self) -> None: _freeze_fields(self) if ( self.scope.generation_key is None or self.scope.feed_purpose is None or self.checkpoint_generation < 1 or len(self.fingerprint) != 64 or any(c not in "0123456789abcdef" for c in self.fingerprint) or tuple(sorted(set(self.issue_ids))) != self.issue_ids or len(self.issue_ids) > 16 or (self.health in {"delayed", "action_required"} and not self.issue_ids) ): raise ReportingNotificationError("invalid_status_projection") for issue_id in self.issue_ids: reporting_identifier(issue_id) var health : Literal['healthy', 'waiting', 'delayed', 'action_required', 'complete']-
Expand source code
@dataclass(frozen=True, slots=True) class StatusChanged(_ClosedValue): """One locked scope checkpoint generation; private identity stays off the wire.""" scope: ReportingStatusScope health: ReportingHealth fingerprint: str checkpoint_generation: int previous_health: ReportingHealth | None = None issue_ids: tuple[str, ...] = () kind: Literal["status_changed"] = field(default="status_changed", kw_only=True) def __post_init__(self) -> None: _freeze_fields(self) if ( self.scope.generation_key is None or self.scope.feed_purpose is None or self.checkpoint_generation < 1 or len(self.fingerprint) != 64 or any(c not in "0123456789abcdef" for c in self.fingerprint) or tuple(sorted(set(self.issue_ids))) != self.issue_ids or len(self.issue_ids) > 16 or (self.health in {"delayed", "action_required"} and not self.issue_ids) ): raise ReportingNotificationError("invalid_status_projection") for issue_id in self.issue_ids: reporting_identifier(issue_id) var issue_ids : tuple[str, ...]-
Expand source code
@dataclass(frozen=True, slots=True) class StatusChanged(_ClosedValue): """One locked scope checkpoint generation; private identity stays off the wire.""" scope: ReportingStatusScope health: ReportingHealth fingerprint: str checkpoint_generation: int previous_health: ReportingHealth | None = None issue_ids: tuple[str, ...] = () kind: Literal["status_changed"] = field(default="status_changed", kw_only=True) def __post_init__(self) -> None: _freeze_fields(self) if ( self.scope.generation_key is None or self.scope.feed_purpose is None or self.checkpoint_generation < 1 or len(self.fingerprint) != 64 or any(c not in "0123456789abcdef" for c in self.fingerprint) or tuple(sorted(set(self.issue_ids))) != self.issue_ids or len(self.issue_ids) > 16 or (self.health in {"delayed", "action_required"} and not self.issue_ids) ): raise ReportingNotificationError("invalid_status_projection") for issue_id in self.issue_ids: reporting_identifier(issue_id) var kind : Literal['status_changed']-
Expand source code
@dataclass(frozen=True, slots=True) class StatusChanged(_ClosedValue): """One locked scope checkpoint generation; private identity stays off the wire.""" scope: ReportingStatusScope health: ReportingHealth fingerprint: str checkpoint_generation: int previous_health: ReportingHealth | None = None issue_ids: tuple[str, ...] = () kind: Literal["status_changed"] = field(default="status_changed", kw_only=True) def __post_init__(self) -> None: _freeze_fields(self) if ( self.scope.generation_key is None or self.scope.feed_purpose is None or self.checkpoint_generation < 1 or len(self.fingerprint) != 64 or any(c not in "0123456789abcdef" for c in self.fingerprint) or tuple(sorted(set(self.issue_ids))) != self.issue_ids or len(self.issue_ids) > 16 or (self.health in {"delayed", "action_required"} and not self.issue_ids) ): raise ReportingNotificationError("invalid_status_projection") for issue_id in self.issue_ids: reporting_identifier(issue_id) var previous_health : Literal['healthy', 'waiting', 'delayed', 'action_required', 'complete'] | None-
Expand source code
@dataclass(frozen=True, slots=True) class StatusChanged(_ClosedValue): """One locked scope checkpoint generation; private identity stays off the wire.""" scope: ReportingStatusScope health: ReportingHealth fingerprint: str checkpoint_generation: int previous_health: ReportingHealth | None = None issue_ids: tuple[str, ...] = () kind: Literal["status_changed"] = field(default="status_changed", kw_only=True) def __post_init__(self) -> None: _freeze_fields(self) if ( self.scope.generation_key is None or self.scope.feed_purpose is None or self.checkpoint_generation < 1 or len(self.fingerprint) != 64 or any(c not in "0123456789abcdef" for c in self.fingerprint) or tuple(sorted(set(self.issue_ids))) != self.issue_ids or len(self.issue_ids) > 16 or (self.health in {"delayed", "action_required"} and not self.issue_ids) ): raise ReportingNotificationError("invalid_status_projection") for issue_id in self.issue_ids: reporting_identifier(issue_id) var scope : ReportingStatusScope-
Expand source code
@dataclass(frozen=True, slots=True) class StatusChanged(_ClosedValue): """One locked scope checkpoint generation; private identity stays off the wire.""" scope: ReportingStatusScope health: ReportingHealth fingerprint: str checkpoint_generation: int previous_health: ReportingHealth | None = None issue_ids: tuple[str, ...] = () kind: Literal["status_changed"] = field(default="status_changed", kw_only=True) def __post_init__(self) -> None: _freeze_fields(self) if ( self.scope.generation_key is None or self.scope.feed_purpose is None or self.checkpoint_generation < 1 or len(self.fingerprint) != 64 or any(c not in "0123456789abcdef" for c in self.fingerprint) or tuple(sorted(set(self.issue_ids))) != self.issue_ids or len(self.issue_ids) > 16 or (self.health in {"delayed", "action_required"} and not self.issue_ids) ): raise ReportingNotificationError("invalid_status_projection") for issue_id in self.issue_ids: reporting_identifier(issue_id)