# Copyright (c) 2025 Patrick Motsch # All rights reserved. """ Copy File action for SharePoint operations. Copies file within SharePoint. """ import logging import json from typing import Dict, Any from modules.workflows.methods.methodBase import action from modules.datamodels.datamodelChat import ActionResult, ActionDocument logger = logging.getLogger(__name__) @action async def copyFile(self, parameters: Dict[str, Any]) -> ActionResult: """ Copy file within SharePoint. Parameters: - connectionReference (str, required): Microsoft connection label. - siteId (str, required): SharePoint site ID (from findSiteByUrl result) or document reference containing site info - sourceFolder (str, required): Source folder path relative to site root - sourceFile (str, required): Source file name - destFolder (str, required): Destination folder path relative to site root - destFile (str, required): Destination file name Returns: - ActionResult with ActionDocument containing copy result """ try: connectionReference = parameters.get("connectionReference") if not connectionReference: return ActionResult.isFailure(error="connectionReference parameter is required") siteIdParam = parameters.get("siteId") if not siteIdParam: return ActionResult.isFailure(error="siteId parameter is required") sourceFolder = parameters.get("sourceFolder") if not sourceFolder: return ActionResult.isFailure(error="sourceFolder parameter is required") sourceFile = parameters.get("sourceFile") if not sourceFile: return ActionResult.isFailure(error="sourceFile parameter is required") destFolder = parameters.get("destFolder") if not destFolder: return ActionResult.isFailure(error="destFolder parameter is required") destFile = parameters.get("destFile") if not destFile: return ActionResult.isFailure(error="destFile parameter is required") # Extract siteId from document if it's a reference siteId = None if isinstance(siteIdParam, str): from modules.datamodels.datamodelDocref import DocumentReferenceList try: docList = DocumentReferenceList.from_string_list([siteIdParam]) chatDocuments = self.services.chat.getChatDocumentsFromDocumentList(docList) if chatDocuments and len(chatDocuments) > 0: siteInfoJson = json.loads(chatDocuments[0].documentData) siteId = siteInfoJson.get("id") except: pass if not siteId: siteId = siteIdParam else: siteId = siteIdParam if not siteId: return ActionResult.isFailure(error="Could not extract siteId from parameter") # Get Microsoft connection connection = self.connection.getMicrosoftConnection(connectionReference) if not connection: return ActionResult.isFailure(error="No valid Microsoft connection found for the provided connection reference") # Copy file await self.services.sharepoint.copyFileAsync( siteId=siteId, sourceFolder=sourceFolder, sourceFile=sourceFile, destFolder=destFolder, destFile=destFile ) logger.info(f"Copied file in SharePoint: {sourceFolder}/{sourceFile} -> {destFolder}/{destFile}") # Generate filename workflowContext = self.services.chat.getWorkflowContext() if hasattr(self.services, 'chat') else None filename = self._generateMeaningfulFileName( "file_copy_result", "json", workflowContext, "copyFile" ) result = { "success": True, "siteId": siteId, "sourcePath": f"{sourceFolder}/{sourceFile}", "destPath": f"{destFolder}/{destFile}" } validationMetadata = self._createValidationMetadata( "copyFile", siteId=siteId, sourcePath=f"{sourceFolder}/{sourceFile}", destPath=f"{destFolder}/{destFile}" ) document = ActionDocument( documentName=filename, documentData=json.dumps(result, indent=2), mimeType="application/json", validationMetadata=validationMetadata ) return ActionResult.isSuccess(documents=[document]) except Exception as e: # Handle file not found gracefully if "itemNotFound" in str(e) or "404" in str(e): logger.warning(f"File not found for copy: {parameters.get('sourceFolder')}/{parameters.get('sourceFile')}") # Return success with skipped status workflowContext = self.services.chat.getWorkflowContext() if hasattr(self.services, 'chat') else None filename = self._generateMeaningfulFileName( "file_copy_result", "json", workflowContext, "copyFile" ) result = { "success": True, "skipped": True, "reason": "File not found (may not exist yet)" } validationMetadata = self._createValidationMetadata( "copyFile", skipped=True ) document = ActionDocument( documentName=filename, documentData=json.dumps(result, indent=2), mimeType="application/json", validationMetadata=validationMetadata ) return ActionResult.isSuccess(documents=[document]) errorMsg = f"Error copying file in SharePoint: {str(e)}" logger.error(errorMsg) return ActionResult.isFailure(error=errorMsg)