180 lines
6.1 KiB
Python
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,
|
|
),
|
|
)
|