84 lines
2.7 KiB
Python
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."""
|
|
...
|