"""Driver registry. Drivers live in :mod:`app.integrations.drivers` and register themselves with ``@register_driver`` at import time. Adding a new provider (Azure, Slack, Workday, BambooHR, …) is exactly: 1. Create ``app/integrations/drivers/.py`` 2. Subclass the right kind mixin (HrisDriver / ChatApprovalDriver / CloudDriver / SoftwareProvisioningDriver) and fill in metadata + methods. 3. Decorate with ``@register_driver``. 4. Import the module from ``drivers/__init__.py``. No DB schema change. No enum migration. No config-center code edit. The registry is the *single source of truth* for "what (kind, provider) combinations does this build know about". The DB stays permissive (kind/provider are plain strings) so retiring a driver doesn't trash stored rows — they just resolve to an "unknown driver" error at load. """ from __future__ import annotations import logging from typing import TYPE_CHECKING from app.integrations.driver_base import IntegrationDriver from app.integrations.schemas import IntegrationDriverMetadata if TYPE_CHECKING: # pragma: no cover from collections.abc import Iterable logger = logging.getLogger(__name__) _DRIVERS: dict[tuple[str, str], type[IntegrationDriver]] = {} def register_driver(cls: type[IntegrationDriver]) -> type[IntegrationDriver]: """Class decorator: register a concrete driver class.""" if not cls.kind or not cls.provider: raise ValueError( f"Driver {cls.__name__} missing .kind / .provider class attrs", ) key = (cls.kind, cls.provider) existing = _DRIVERS.get(key) if existing is not None: if existing is cls: return cls # Startup tolerance: a duplicate registration with a different # class object would historically crash app boot. Log critical # and keep the first-registered driver — the running process # stays up so an admin can fix the conflict via deploy rather # than via the registry. This trades registry purity for # availability, which is the right call for a single-tenant # platform serving ~500 employees. logger.critical( "Driver collision for (%r, %r): keeping %s, ignoring %s", cls.kind, cls.provider, existing.__name__, cls.__name__, ) # Return the EXISTING class so any module-level symbol that # binds the decorator's return value (``MyDup = register_driver(MyDup)`` # which is what ``@register_driver`` desugars to) ends up # pointing at the kept driver — calling ``MyDup.method(...)`` # then dispatches to the right implementation, not the # ignored duplicate. return existing _DRIVERS[key] = cls logger.debug("registered driver %s/%s -> %s", cls.kind, cls.provider, cls.__name__) return cls def get_driver_class(kind: str, provider: str) -> type[IntegrationDriver] | None: """Look up a driver class. Returns ``None`` if not registered.""" return _DRIVERS.get((kind, provider)) def list_drivers() -> list[IntegrationDriverMetadata]: """All registered (kind, provider) pairs as UI-ready metadata. Sorted by kind → display_name so the "new integration" wizard renders a predictable list. """ return sorted( (cls.metadata() for cls in _DRIVERS.values()), key=lambda m: (m.kind, m.display_name.lower()), ) def kinds() -> list[str]: """Distinct kinds with at least one registered driver.""" return sorted({kind for kind, _ in _DRIVERS}) def providers_for_kind(kind: str) -> Iterable[str]: """All providers registered under a given kind.""" return sorted(p for k, p in _DRIVERS if k == kind) def archival_providers() -> frozenset[str]: """Provider strings for every driver flagged ``archival = True``. These are decommissioned tenants that exist only to render migrated historical rows under a real name (e.g. ``ksyun`` — 金山云 归档). Active-management surfaces (half-yearly owner confirmation, sync schedulers) exclude servers under these providers so they don't nag owners about machines the company no longer runs. Provider strings are globally unique across kinds in practice, so callers filter on ``Integration.provider`` alone without also matching kind. """ return frozenset( cls.provider for cls in _DRIVERS.values() if cls.archival ) __all__ = [ "archival_providers", "get_driver_class", "kinds", "list_drivers", "providers_for_kind", "register_driver", ]