feat: implement channel runtime connectors
This commit is contained in:
@ -0,0 +1,93 @@
|
||||
"""Channel connector registry."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Protocol
|
||||
|
||||
from beaver.foundation.config.schema import ChannelConfig
|
||||
|
||||
from .models import ChannelRuntimeSpec, ValidationResult
|
||||
from .store import ChannelConnectionStore, CredentialStore
|
||||
|
||||
|
||||
class ChannelConnector(Protocol):
|
||||
kind: str
|
||||
|
||||
async def validate(self, connection_id: str) -> ValidationResult:
|
||||
...
|
||||
|
||||
async def materialize_runtime(self, connection_id: str) -> ChannelRuntimeSpec:
|
||||
...
|
||||
|
||||
async def revoke(self, connection_id: str) -> None:
|
||||
...
|
||||
|
||||
|
||||
class ChannelConnectorRegistry:
|
||||
def __init__(self, *, connection_store: ChannelConnectionStore, credential_store: CredentialStore) -> None:
|
||||
self.connection_store = connection_store
|
||||
self.credential_store = credential_store
|
||||
self._connectors: dict[str, ChannelConnector] = {}
|
||||
|
||||
def register(self, connector: ChannelConnector) -> None:
|
||||
kind = connector.kind.strip()
|
||||
if not kind:
|
||||
raise ValueError("Connector kind is required")
|
||||
if kind in self._connectors:
|
||||
raise ValueError(f"Connector already registered: {kind}")
|
||||
self._connectors[kind] = connector
|
||||
|
||||
def connectors(self) -> list[dict[str, str]]:
|
||||
return [{"kind": kind} for kind in sorted(self._connectors)]
|
||||
|
||||
def connector_for_kind(self, kind: str) -> ChannelConnector:
|
||||
return self._connector(kind)
|
||||
|
||||
async def validate(self, connection_id: str) -> ValidationResult:
|
||||
connection = self.connection_store.get(connection_id)
|
||||
connector = self._connector(connection.kind)
|
||||
result = await connector.validate(connection_id)
|
||||
self.connection_store.update_status(
|
||||
connection_id,
|
||||
status=result.status,
|
||||
last_error=result.error,
|
||||
)
|
||||
return result
|
||||
|
||||
async def materialize_runtime(self, connection_id: str) -> ChannelRuntimeSpec:
|
||||
connection = self.connection_store.get(connection_id)
|
||||
return await self._connector(connection.kind).materialize_runtime(connection_id)
|
||||
|
||||
async def materialize_connected_runtime_specs(self) -> list[ChannelRuntimeSpec]:
|
||||
specs: list[ChannelRuntimeSpec] = []
|
||||
for connection in self.connection_store.list():
|
||||
if connection.status not in {"connected", "running"}:
|
||||
continue
|
||||
specs.append(await self._connector(connection.kind).materialize_runtime(connection.connection_id))
|
||||
return specs
|
||||
|
||||
async def materialize_channel_configs(self) -> dict[str, ChannelConfig]:
|
||||
channels: dict[str, ChannelConfig] = {}
|
||||
for spec in await self.materialize_connected_runtime_specs():
|
||||
secrets = self.credential_store.get(spec.secrets_ref) if spec.secrets_ref else {}
|
||||
channels[spec.channel_id] = ChannelConfig(
|
||||
enabled=True,
|
||||
kind=spec.kind,
|
||||
mode=spec.mode,
|
||||
account_id=spec.account_id,
|
||||
display_name=spec.display_name,
|
||||
config=dict(spec.config),
|
||||
secrets=secrets,
|
||||
)
|
||||
return channels
|
||||
|
||||
async def revoke(self, connection_id: str) -> None:
|
||||
connection = self.connection_store.get(connection_id)
|
||||
await self._connector(connection.kind).revoke(connection_id)
|
||||
self.connection_store.revoke(connection_id)
|
||||
|
||||
def _connector(self, kind: str) -> ChannelConnector:
|
||||
connector = self._connectors.get(kind)
|
||||
if connector is None:
|
||||
raise KeyError(f"Connector not registered: {kind}")
|
||||
return connector
|
||||
Reference in New Issue
Block a user