94 lines
4 KiB
Python
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
|