idea93-gridguard-verifiable.../energi_bridge/contracts.py

97 lines
3.3 KiB
Python

"""Graph-of-Contracts registry for versioned adapter/schema metadata."""
from __future__ import annotations
from dataclasses import dataclass, field
from datetime import datetime
from typing import Dict, Iterable, List, Optional, Tuple
from energi_bridge.schemas import RegistryMetadata
def _parse_version(version: str) -> Tuple[int, int, int, str]:
core, _, suffix = version.partition("-")
parts = core.split(".")
while len(parts) < 3:
parts.append("0")
major, minor, patch = (int(parts[0]), int(parts[1]), int(parts[2]))
return major, minor, patch, suffix
@dataclass(frozen=True)
class ContractDefinition:
contract_id: str
version: str
adapter_id: str
schema_name: str
created_at: datetime = field(default_factory=datetime.utcnow)
replay_hint: Optional[str] = None
previous_version: Optional[str] = None
def to_metadata(self) -> RegistryMetadata:
return RegistryMetadata(
contract_version=self.version,
adapter_id=self.adapter_id,
contract_id=self.contract_id,
schema_name=self.schema_name,
created_at=self.created_at,
replay_hint=self.replay_hint,
)
class ContractRegistry:
"""Maintain contract versions for schemas and adapters."""
def __init__(self) -> None:
self._contracts: Dict[str, Dict[str, ContractDefinition]] = {}
def register(
self,
contract_id: str,
version: str,
adapter_id: str,
schema_name: str,
replay_hint: Optional[str] = None,
) -> ContractDefinition:
versions = self._contracts.setdefault(contract_id, {})
if version in versions:
raise ValueError(f"Contract {contract_id}@{version} already exists")
previous_version = self.latest_version(contract_id)
definition = ContractDefinition(
contract_id=contract_id,
version=version,
adapter_id=adapter_id,
schema_name=schema_name,
replay_hint=replay_hint,
previous_version=previous_version,
)
versions[version] = definition
return definition
def latest_version(self, contract_id: str) -> Optional[str]:
versions = self._contracts.get(contract_id, {})
if not versions:
return None
return max(versions, key=_parse_version)
def resolve(self, contract_id: str, version: Optional[str] = None) -> ContractDefinition:
versions = self._contracts.get(contract_id, {})
if not versions:
raise KeyError(contract_id)
selected = version or self.latest_version(contract_id)
if selected is None or selected not in versions:
raise KeyError(f"Unknown contract version: {contract_id}@{version}")
return versions[selected]
def manifest(self) -> List[RegistryMetadata]:
metadata: List[RegistryMetadata] = []
for contract_versions in self._contracts.values():
for definition in contract_versions.values():
metadata.append(definition.to_metadata())
return sorted(metadata, key=lambda item: (item.contract_id or "", _parse_version(item.contract_version)))
def items(self) -> Iterable[ContractDefinition]:
for contract_versions in self._contracts.values():
yield from contract_versions.values()