"""In-memory asset registry repository.""" from __future__ import annotations from dataclasses import dataclass, field from typing import Iterable from kontextual_engine.core import ( Actor, AssetRepresentation, AssetVersion, AuditEvent, KnowledgeAsset, LifecycleState, MetadataRecord, RepresentationKind, ) from kontextual_engine.errors import NotFoundError, ValidationError @dataclass class InMemoryAssetRegistryRepository: actors: dict[str, Actor] = field(default_factory=dict) assets: dict[str, KnowledgeAsset] = field(default_factory=dict) representations: dict[str, AssetRepresentation] = field(default_factory=dict) metadata_records: dict[str, list[MetadataRecord]] = field(default_factory=dict) versions: dict[str, list[AssetVersion]] = field(default_factory=dict) audit_events: dict[str, AuditEvent] = field(default_factory=dict) def save_actor(self, actor: Actor) -> Actor: self.actors[actor.id] = actor return actor def get_actor(self, actor_id: str) -> Actor: try: return self.actors[actor_id] except KeyError as exc: raise NotFoundError("Actor not found", details={"actor_id": actor_id}) from exc def save_asset(self, asset: KnowledgeAsset) -> KnowledgeAsset: self.assets[asset.id] = asset return asset def get_asset(self, asset_id: str) -> KnowledgeAsset: try: return self.assets[asset_id] except KeyError as exc: raise NotFoundError("Asset not found", details={"asset_id": asset_id}) from exc def list_assets( self, *, lifecycle: LifecycleState | None = None, asset_type: str | None = None, ) -> list[KnowledgeAsset]: assets: Iterable[KnowledgeAsset] = self.assets.values() if lifecycle is not None: assets = [asset for asset in assets if asset.lifecycle == lifecycle] if asset_type is not None: assets = [asset for asset in assets if asset.classification.asset_type == asset_type] return sorted(assets, key=lambda asset: (asset.title, asset.id)) def save_representation(self, representation: AssetRepresentation) -> AssetRepresentation: self.get_asset(representation.asset_id) self.representations[representation.representation_id] = representation return representation def get_representation(self, representation_id: str) -> AssetRepresentation: try: return self.representations[representation_id] except KeyError as exc: raise NotFoundError( "Representation not found", details={"representation_id": representation_id}, ) from exc def list_representations( self, *, asset_id: str | None = None, kind: RepresentationKind | None = None, ) -> list[AssetRepresentation]: representations: Iterable[AssetRepresentation] = self.representations.values() if asset_id is not None: representations = [item for item in representations if item.asset_id == asset_id] if kind is not None: representations = [item for item in representations if item.kind == kind] return sorted(representations, key=lambda item: (item.asset_id, item.kind.value, item.representation_id)) def save_metadata_record(self, asset_id: str, record: MetadataRecord) -> MetadataRecord: self.get_asset(asset_id) self.metadata_records.setdefault(asset_id, []).append(record) return record def list_metadata_records(self, asset_id: str) -> list[MetadataRecord]: self.get_asset(asset_id) return list(self.metadata_records.get(asset_id, [])) def save_version(self, version: AssetVersion) -> AssetVersion: self.get_asset(version.asset_id) current = self.versions.setdefault(version.asset_id, []) if any(existing.sequence == version.sequence for existing in current): raise ValidationError( "Version sequence already exists for asset", details={"asset_id": version.asset_id, "sequence": version.sequence}, ) current.append(version) return version def list_versions(self, asset_id: str) -> list[AssetVersion]: self.get_asset(asset_id) return sorted(self.versions.get(asset_id, []), key=lambda version: version.sequence) def save_audit_event(self, event: AuditEvent) -> AuditEvent: self.audit_events[event.event_id] = event return event def list_audit_events( self, *, target: str | None = None, correlation_id: str | None = None, ) -> list[AuditEvent]: events: Iterable[AuditEvent] = self.audit_events.values() if target is not None: events = [event for event in events if event.target == target] if correlation_id is not None: events = [event for event in events if event.correlation_id == correlation_id] return sorted(events, key=lambda event: (event.occurred_at, event.event_id))