gateway/modules/connectors/connectorProviderBase.py
2026-04-21 00:50:36 +02:00

84 lines
2.7 KiB
Python

# Copyright (c) 2025 Patrick Motsch
# All rights reserved.
"""Abstract base classes for the Provider-Connector architecture (1:n).
One ProviderConnector per vendor (e.g. MsftConnector, GoogleConnector).
Each ProviderConnector exposes n ServiceAdapters (e.g. SharepointAdapter, OutlookAdapter).
All ServiceAdapters share the same access token from the UserConnection.
"""
from abc import ABC, abstractmethod
from dataclasses import dataclass, field
from typing import List, Optional, Union
@dataclass
class DownloadResult:
"""Rich return type for ServiceAdapter.download() when metadata is available."""
data: bytes = field(default=b"", repr=False)
fileName: str = ""
mimeType: str = ""
class ServiceAdapter(ABC):
"""Standardized operations for a single service of a provider."""
@abstractmethod
async def browse(
self,
path: str,
filter: Optional[str] = None,
limit: Optional[int] = None,
) -> list:
"""List items (files/folders) at the given path.
``limit`` is an optional upper bound for the number of returned entries.
Adapters that talk to paginated APIs should keep paging until either
the API is exhausted OR ``limit`` is reached. ``None`` means "use the
adapter's sensible default" (NOT "unlimited") so an over-eager caller
cannot accidentally pull millions of records. Adapters that have no
pagination (single page result) may ignore this parameter.
"""
...
@abstractmethod
async def download(self, path: str) -> Union[bytes, DownloadResult]:
"""Download a file. Return bytes or DownloadResult with metadata."""
...
@abstractmethod
async def upload(self, path: str, data: bytes, fileName: str) -> dict:
"""Upload a file to the given path. Returns metadata of the created entry."""
...
@abstractmethod
async def search(
self,
query: str,
path: Optional[str] = None,
limit: Optional[int] = None,
) -> list:
"""Search for items matching the query.
See :meth:`browse` for the semantics of ``limit``.
"""
...
class ProviderConnector(ABC):
"""One connector per provider. Manages a UserConnection + token.
Provides access to n services of the provider."""
def __init__(self, connection, accessToken: str):
self.connection = connection
self.accessToken = accessToken
@abstractmethod
def getAvailableServices(self) -> List[str]:
"""Which services does this provider offer?"""
...
@abstractmethod
def getServiceAdapter(self, service: str) -> ServiceAdapter:
"""Return the ServiceAdapter for a specific service."""
...