from typing import Dict, Any, Optional import logging from datetime import datetime, UTC from office365.runtime.auth.user_credential import UserCredential from office365.sharepoint.client_context import ClientContext from office365.sharepoint.files.file import File from office365.sharepoint.lists.list import List from office365.sharepoint.lists.list_creation_information import ListCreationInformation from modules.methods.methodBase import MethodBase, MethodResult from modules.models.userConnection import UserConnection logger = logging.getLogger(__name__) class MethodSharepoint(MethodBase): """SharePoint method implementation for document operations""" def __init__(self): super().__init__() self.name = "sharepoint" self.description = "Handle SharePoint document operations like search, read, and write" @property def actions(self) -> Dict[str, Dict[str, Any]]: """Available actions and their parameters""" return { "search": { "description": "Search SharePoint documents", "retryMax": 3, "timeout": 30, "parameters": { "query": {"type": "string", "required": True}, "siteUrl": {"type": "string", "required": True}, "listName": {"type": "string", "required": False}, "maxResults": {"type": "number", "required": False} } }, "read": { "description": "Read SharePoint document content", "retryMax": 2, "timeout": 30, "parameters": { "fileUrl": {"type": "string", "required": True}, "siteUrl": {"type": "string", "required": True} } }, "write": { "description": "Write content to SharePoint document", "retryMax": 2, "timeout": 30, "parameters": { "fileUrl": {"type": "string", "required": True}, "siteUrl": {"type": "string", "required": True}, "content": {"type": "string", "required": True}, "contentType": {"type": "string", "required": False} } }, "readList": { "description": "Read items from SharePoint list", "retryMax": 2, "timeout": 30, "parameters": { "siteUrl": {"type": "string", "required": True}, "listName": {"type": "string", "required": True}, "query": {"type": "string", "required": False}, "fields": {"type": "array", "required": False} } }, "writeList": { "description": "Write items to SharePoint list", "retryMax": 2, "timeout": 30, "parameters": { "siteUrl": {"type": "string", "required": True}, "listName": {"type": "string", "required": True}, "items": {"type": "array", "required": True} } }, "createList": { "description": "Create a new SharePoint list", "retryMax": 2, "timeout": 30, "parameters": { "siteUrl": {"type": "string", "required": True}, "listName": {"type": "string", "required": True}, "description": {"type": "string", "required": False}, "template": {"type": "string", "required": False}, "fields": {"type": "array", "required": False} } } } async def execute(self, action: str, parameters: Dict[str, Any], authData: Optional[Dict[str, Any]] = None) -> MethodResult: """Execute SharePoint method""" try: # Validate parameters if not await self.validateParameters(action, parameters): return self._createResult( success=False, data={"error": f"Invalid parameters for {action}"} ) # Get UserConnection from auth_data if not authData or "userConnection" not in authData: return self._createResult( success=False, data={"error": "UserConnection required for SharePoint operations"} ) userConnection: UserConnection = authData["userConnection"] # Execute action if action == "search": return await self._search_documents(parameters, userConnection) elif action == "read": return await self._read_document(parameters, userConnection) elif action == "write": return await self._write_document(parameters, userConnection) elif action == "readList": return await self._readList(parameters, userConnection) elif action == "writeList": return await self._writeList(parameters, userConnection) elif action == "createList": return await self._createList(parameters, userConnection) else: return self._createResult( success=False, data={"error": f"Unknown action: {action}"} ) except Exception as e: logger.error(f"Error executing SharePoint {action}: {e}") return self._createResult( success=False, data={"error": str(e)} ) async def _search_documents(self, parameters: Dict[str, Any], userConnection: UserConnection) -> MethodResult: """Search SharePoint documents""" try: siteUrl = parameters["siteUrl"] query = parameters["query"] listName = parameters.get("listName") maxResults = parameters.get("maxResults", 10) # Create SharePoint context ctx = ClientContext(siteUrl).with_credentials( UserCredential(userConnection.authToken, userConnection.refreshToken) ) # Search in specific list or entire site if listName: targetList = ctx.web.lists.get_by_title(listName) items = targetList.items.filter(f"Title eq '{query}'").top(maxResults).get().execute_query() results = [{ "title": item.properties["Title"], "url": item.properties["FileRef"], "modified": item.properties["Modified"], "created": item.properties["Created"] } for item in items] else: # Search entire site search_results = ctx.search(query).execute_query() results = [{ "title": result.properties["Title"], "url": result.properties["Path"], "modified": result.properties["LastModifiedTime"], "created": result.properties["Created"] } for result in search_results[:maxResults]] return self._createResult( success=True, data={ "query": query, "results": results } ) except Exception as e: logger.error(f"Error searching SharePoint documents: {e}") return self._createResult( success=False, data={"error": f"Search failed: {str(e)}"} ) async def _read_document(self, parameters: Dict[str, Any], userConnection: UserConnection) -> MethodResult: """Read SharePoint document content""" try: siteUrl = parameters["siteUrl"] fileUrl = parameters["fileUrl"] # Create SharePoint context ctx = ClientContext(siteUrl).with_credentials( UserCredential(userConnection.authToken, userConnection.refreshToken) ) # Get file file = ctx.web.get_file_by_server_relative_url(fileUrl) file_content = file.read().execute_query() return self._createResult( success=True, data={ "url": fileUrl, "content": file_content.content.decode('utf-8'), "modified": file.properties["TimeLastModified"], "size": file.properties["Length"] } ) except Exception as e: logger.error(f"Error reading SharePoint document: {e}") return self._createResult( success=False, data={"error": f"Read failed: {str(e)}"} ) async def _write_document(self, parameters: Dict[str, Any], userConnection: UserConnection) -> MethodResult: """Write content to SharePoint document""" try: siteUrl = parameters["siteUrl"] fileUrl = parameters["fileUrl"] content = parameters["content"] contentType = parameters.get("contentType", "text/plain") # Create SharePoint context ctx = ClientContext(siteUrl).with_credentials( UserCredential(userConnection.authToken, userConnection.refreshToken) ) # Get or create file try: file = ctx.web.get_file_by_server_relative_url(fileUrl) except: # Create new file folderUrl = "/".join(fileUrl.split("/")[:-1]) fileName = fileUrl.split("/")[-1] folder = ctx.web.get_folder_by_server_relative_url(folderUrl) file = folder.upload_file(fileName, content.encode('utf-8')).execute_query() # Update file content file.write(content.encode('utf-8')).execute_query() return self._createResult( success=True, data={ "url": fileUrl, "modified": datetime.now(UTC).isoformat(), "size": len(content.encode('utf-8')) } ) except Exception as e: logger.error(f"Error writing SharePoint document: {e}") return self._createResult( success=False, data={"error": f"Write failed: {str(e)}"} ) async def _readList(self, parameters: Dict[str, Any], userConnection: UserConnection) -> MethodResult: """Read items from SharePoint list""" try: siteUrl = parameters["siteUrl"] listName = parameters["listName"] query = parameters.get("query") fields = parameters.get("fields", ["*"]) # Create SharePoint account account = Account( credentials=(userConnection.authToken, userConnection.refreshToken), protocol=MSGraphProtocol() ) # Get site site = account.get_site(siteUrl) # Get list list = site.get_list(listName) # Get items if query: items = list.get_items(query=query, fields=fields) else: items = list.get_items(fields=fields) return self._createResult( success=True, data={ "siteUrl": siteUrl, "listName": listName, "items": items } ) except Exception as e: logger.error(f"Error reading SharePoint list: {e}") return self._createResult( success=False, data={"error": f"Read failed: {str(e)}"} ) async def _writeList(self, parameters: Dict[str, Any], userConnection: UserConnection) -> MethodResult: """Write items to SharePoint list""" try: siteUrl = parameters["siteUrl"] listName = parameters["listName"] items = parameters["items"] # Create SharePoint account account = Account( credentials=(userConnection.authToken, userConnection.refreshToken), protocol=MSGraphProtocol() ) # Get site site = account.get_site(siteUrl) # Get list list = site.get_list(listName) # Add items results = [] for item in items: result = list.add_item(item) results.append({ "id": result.id, "status": "success" }) return self._createResult( success=True, data={ "siteUrl": siteUrl, "listName": listName, "results": results } ) except Exception as e: logger.error(f"Error writing to SharePoint list: {e}") return self._createResult( success=False, data={"error": f"Write failed: {str(e)}"} ) async def _createList(self, parameters: Dict[str, Any], userConnection: UserConnection) -> MethodResult: """Create a new SharePoint list""" try: siteUrl = parameters["siteUrl"] listName = parameters["listName"] description = parameters.get("description") template = parameters.get("template", "generic") fields = parameters.get("fields", []) # Create SharePoint account account = Account( credentials=(userConnection.authToken, userConnection.refreshToken), protocol=MSGraphProtocol() ) # Get site site = account.get_site(siteUrl) # Create list list = site.create_list( name=listName, description=description, template=template ) # Add fields for field in fields: list.add_field( name=field["name"], field_type=field["type"], required=field.get("required", False), description=field.get("description") ) return self._createResult( success=True, data={ "siteUrl": siteUrl, "listName": listName, "id": list.id, "webUrl": list.web_url } ) except Exception as e: logger.error(f"Error creating SharePoint list: {e}") return self._createResult( success=False, data={"error": f"Create failed: {str(e)}"} )