gateway/modules/connectors/connectorResolver.py
2026-03-15 23:38:21 +01:00

94 lines
4 KiB
Python

# Copyright (c) 2025 Patrick Motsch
# All rights reserved.
"""ConnectorResolver -- resolves a connectionId to the correct ProviderConnector and ServiceAdapter.
Registry maps authority values to ProviderConnector classes.
The resolver loads the UserConnection, obtains a fresh token via SecurityService,
and instantiates the appropriate connector.
"""
import logging
from typing import Dict, Any, Type, Optional
from modules.connectors.connectorProviderBase import ProviderConnector, ServiceAdapter
logger = logging.getLogger(__name__)
class ConnectorResolver:
"""Resolves connectionId → ProviderConnector (with fresh token) → ServiceAdapter."""
_providerRegistry: Dict[str, Type[ProviderConnector]] = {}
def __init__(self, securityService, dbInterface):
"""
Args:
securityService: SecurityService instance (for getFreshToken)
dbInterface: DB interface with getUserConnection(connectionId)
"""
self._security = securityService
self._db = dbInterface
self._ensureRegistered()
def _ensureRegistered(self):
"""Lazy-register known providers on first instantiation."""
if ConnectorResolver._providerRegistry:
return
try:
from modules.connectors.providerMsft.connectorMsft import MsftConnector
ConnectorResolver._providerRegistry["msft"] = MsftConnector
except ImportError:
logger.warning("MsftConnector not available")
try:
from modules.connectors.providerGoogle.connectorGoogle import GoogleConnector
ConnectorResolver._providerRegistry["google"] = GoogleConnector
except ImportError:
logger.debug("GoogleConnector not available (stub)")
try:
from modules.connectors.providerFtp.connectorFtp import FtpConnector
ConnectorResolver._providerRegistry["local:ftp"] = FtpConnector
except ImportError:
logger.debug("FtpConnector not available (stub)")
async def resolve(self, connectionId: str) -> ProviderConnector:
"""Resolve connectionId to a ProviderConnector with a fresh access token."""
connection = await self._loadConnection(connectionId)
if not connection:
raise ValueError(f"UserConnection not found: {connectionId}")
authority = getattr(connection, "authority", None)
if not authority:
raise ValueError(f"Connection {connectionId} has no authority")
authorityStr = authority.value if hasattr(authority, "value") else str(authority)
providerClass = self._providerRegistry.get(authorityStr)
if not providerClass:
raise ValueError(f"No ProviderConnector registered for authority: {authorityStr}")
token = self._security.getFreshToken(connectionId)
if not token or not token.tokenAccess:
raise ValueError(f"No valid token for connection {connectionId}")
return providerClass(connection, token.tokenAccess)
async def resolveService(self, connectionId: str, service: str) -> ServiceAdapter:
"""Resolve connectionId + service name to a concrete ServiceAdapter."""
provider = await self.resolve(connectionId)
available = provider.getAvailableServices()
if service not in available:
raise ValueError(f"Service '{service}' not available. Options: {available}")
return provider.getServiceAdapter(service)
async def _loadConnection(self, connectionId: str) -> Optional[Any]:
"""Load UserConnection from DB."""
try:
if hasattr(self._db, "getUserConnection"):
return self._db.getUserConnection(connectionId)
if hasattr(self._db, "loadRecord"):
from modules.datamodels.datamodelUam import UserConnection
return self._db.loadRecord(UserConnection, connectionId)
except Exception as e:
logger.error(f"Failed to load connection {connectionId}: {e}")
return None