gateway/modules/connectors/connectorSharepoint.py
Christopher Gondek 4b9b805632 fix: minor fixes
2025-09-05 15:26:33 +02:00

180 lines
6.1 KiB
Python

"""Connector for CRUD sharepoint operations."""
import asyncio
from concurrent.futures import ThreadPoolExecutor
from dataclasses import dataclass
from datetime import datetime
from io import BytesIO
from typing import Optional
from office365.sharepoint.client_context import ClientContext
from office365.sharepoint.files.file import File
@dataclass
class ConnectorSharepoint:
ctx: ClientContext
@classmethod
async def create(cls, ctx: ClientContext) -> "ConnectorSharepoint":
"""Creates an instance of the Sharepoint connector.
Params:
ctx: The ClientContext instance.
Returns:
ConnectorSharepoint: An instance of the Sharepoint connector.
"""
return cls(ctx=ctx)
@classmethod
def get_client_context_from_username_password(
cls, site_url: str, username: str, password: str
) -> ClientContext:
"""Creates a ClientContext instance from username and password.
Params:
site_url: The URL of the SharePoint site.
username: The username for authentication.
password: The password for authentication.
Returns:
ClientContext: An instance of the ClientContext.
"""
return ClientContext(site_url).with_user_credentials(username, password)
@classmethod
def get_client_context_from_app(
cls, site_url: str, client_id: str, client_secret: str
) -> ClientContext:
"""Creates a ClientContext instance from client ID and client secret.
Params:
site_url: The URL of the SharePoint site.
client_id: The client ID for authentication.
client_secret: The client secret for authentication.
Returns:
ClientContext: An instance of the ClientContext.
"""
return ClientContext(site_url).with_client_credentials(
client_id=client_id, client_secret=client_secret
)
def copy_file(
self, *, source_folder: str, source_file: str, dest_folder: str, dest_file: str
) -> bool:
"""Copy a file from one SharePoint location to another.
Params:
source_folder: Source folder path (server-relative)
source_file: Source file name
dest_folder: Destination folder path (server-relative)
dest_file: Destination file name
Returns:
bool: True if successful, False otherwise
"""
source_path = f"{source_folder.rstrip('/')}/{source_file}"
dest_path = f"{dest_folder.rstrip('/')}/{dest_file}"
source_file_obj = self.ctx.web.get_file_by_server_relative_url(source_path)
source_file_obj.copyto(dest_path).execute_query()
return True
async def copy_file_async(
self, *, source_folder: str, source_file: str, dest_folder: str, dest_file: str
) -> bool:
"""Copy a file from one SharePoint location to another (async version).
Params:
source_folder: Source folder path (server-relative)
source_file: Source file name
dest_folder: Destination folder path (server-relative)
dest_file: Destination file name
Returns:
bool: True if successful, False otherwise
"""
loop = asyncio.get_event_loop()
with ThreadPoolExecutor() as executor:
return await loop.run_in_executor(
executor,
lambda: self.copy_file(
source_folder=source_folder,
source_file=source_file,
dest_folder=dest_folder,
dest_file=dest_file,
),
)
def read_file(self, *, folder_path: str, file_name: str) -> bytes:
"""Read a file from SharePoint and return its content as bytes.
Params:
folder_path: Folder path (server-relative)
file_name: File name
Returns:
bytes: File content as bytes
"""
file_path = f"{folder_path.rstrip('/')}/{file_name}"
response = File.open_binary(self.ctx, file_path)
return response.content
async def read_file_async(self, *, folder_path: str, file_name: str) -> bytes:
"""Read a file from SharePoint and return its content as bytes (async version).
Params:
folder_path: Folder path (server-relative)
file_name: File name
Returns:
bytes: File content as bytes
"""
loop = asyncio.get_event_loop()
with ThreadPoolExecutor() as executor:
return await loop.run_in_executor(
executor,
lambda: self.read_file(folder_path=folder_path, file_name=file_name),
)
def overwrite_file(
self, *, folder_path: str, file_name: str, content: bytes
) -> bool:
"""Write content to a SharePoint file, overwriting if it exists.
Params:
folder_path: Target folder path (server-relative)
file_name: Target file name
content: File content as bytes
Returns:
bool: True if successful, False otherwise
"""
target_folder = self.ctx.web.get_folder_by_server_relative_url(folder_path)
target_folder.upload_file(file_name, content).execute_query()
return True
async def overwrite_file_async(
self, *, folder_path: str, file_name: str, content: bytes
) -> bool:
"""Write content to a SharePoint file, overwriting if it exists (async version).
Params:
folder_path: Target folder path (server-relative)
file_name: Target file name
content: File content as bytes
Returns:
bool: True if successful, False otherwise
"""
loop = asyncio.get_event_loop()
with ThreadPoolExecutor() as executor:
return await loop.run_in_executor(
executor,
lambda: self.overwrite_file(
folder_path=folder_path,
file_name=file_name,
content=content,
),
)