Initial commit

This commit is contained in:
ThePetrovich 2026-08-18 22:05:57 +08:00
commit fe5ef9132a
15 changed files with 888 additions and 0 deletions

62
yksa_poller/adapters.py Normal file
View file

@ -0,0 +1,62 @@
"""What an adapter is, and how the runner finds one.
An adapter does I/O and parsing and nothing else: it yields records, and the
runner matches, dedups and writes them. Keeping the split means an adapter can be
tested against a captured payload with no database.
"""
from __future__ import annotations
from typing import Iterator
class BaseAdapter:
"""Subclasses implement :meth:`fetch`.
The payload type is the service's own — a frame in tdas, an element set in
odms. That is the cargo, not the machine, so it is not defined here.
"""
def __init__(self, source) -> None:
self.source = source
@property
def config(self) -> dict:
return self.source.config or {}
def api_key(self) -> str | None:
"""This source's API key, or None.
An inline ``api_key`` in the config wins; otherwise ``api_key_secret``
names a Docker secret or env var. Production keeps the key out of the
database that way.
"""
direct = self.config.get("api_key")
if direct:
return str(direct).strip()
secret_name = self.config.get("api_key_secret")
if secret_name:
from yksa_kit.settings import read_secret
value = read_secret(str(secret_name))
return value.strip() if value else None
return None
def fetch(self) -> Iterator:
raise NotImplementedError
class AdapterRegistry(dict):
"""`kind` -> adapter class, with a decorator for declaring one.
A kind with no adapter is legitimate tdas's `internal_push` is fed by an
API, odms's `internal_corrected` by a pipeline — and the runner records those
as skipped rather than failed.
"""
def register(self, kind: str):
def decorator(cls):
self[kind] = cls
return cls
return decorator