Files
kontextual-engine/src/kontextual_engine/adapters/memory/asset_registry.py

131 lines
5.0 KiB
Python

"""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))