gateway/modules/workflows/methods/methodSharepoint/actions/uploadFile.py

158 lines
7.3 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Copyright (c) 2025 Patrick Motsch
# All rights reserved.
import logging
import json
import base64
from typing import Dict, Any
from modules.datamodels.datamodelChat import ActionResult, ActionDocument
logger = logging.getLogger(__name__)
async def uploadFile(self, parameters: Dict[str, Any]) -> ActionResult:
try:
connectionReference = parameters.get("connectionReference")
if not connectionReference:
return ActionResult.isFailure(error="connectionReference parameter is required")
# Set SharePoint access token first required before siteDiscovery/sharepoint calls
connection = self.connection.getMicrosoftConnection(connectionReference)
if not connection:
return ActionResult.isFailure(error="No valid Microsoft connection found for the provided connection reference")
contentParam = parameters.get("content")
if not contentParam:
return ActionResult.isFailure(error="content parameter is required")
# Resolve siteId and folderPath: pathQuery (path) or explicit siteId+folderPath
pathQuery = (parameters.get("pathQuery") or parameters.get("path") or "").strip()
siteIdParam = parameters.get("siteId")
folderPath = parameters.get("folderPath")
siteId = None
if pathQuery and pathQuery != "*":
# Option 1: pathQuery (e.g. /sites/host,siteId,webId/15. Persoenliche Ordner/Ida Dittrich/)
sites, errorMsg = await self.siteDiscovery.resolveSitesFromPathQuery(pathQuery)
if errorMsg:
return ActionResult.isFailure(error=errorMsg)
if not sites:
return ActionResult.isFailure(error="Could not resolve site from path")
parsedPath = self.services.sharepoint.extractSiteFromStandardPath(pathQuery)
if not parsedPath:
return ActionResult.isFailure(error="path must be a standard SharePoint path (e.g. /sites/SiteName/Shared Documents/Folder)")
innerPath = (parsedPath.get("innerPath") or "").strip().rstrip("/")
siteId = sites[0].get("id")
folderPath = innerPath
elif siteIdParam and folderPath:
# Option 2: explicit siteId + folderPath
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 Exception:
pass
if not siteId:
siteId = siteIdParam
else:
siteId = siteIdParam
else:
return ActionResult.isFailure(error="Either path (e.g. /sites/.../Folder) or both siteId and folderPath are required")
if not siteId:
return ActionResult.isFailure(error="Could not resolve siteId")
# fileName: from param or from content document
fileName = parameters.get("fileName")
if not fileName and contentParam:
content = contentParam[0] if isinstance(contentParam, (list, tuple)) and contentParam else contentParam
if isinstance(content, dict):
fileName = content.get("documentName") or content.get("fileName")
elif hasattr(content, "documentName"):
fileName = getattr(content, "documentName", None) or getattr(content, "fileName", None)
if not fileName:
fileName = "file"
# Get file content: support inline ActionDocument (from automation2 e.g. sharepoint.downloadFile)
# or docItem references (chat workflow)
content = contentParam[0] if isinstance(contentParam, (list, tuple)) and contentParam else contentParam
fileContentBase64 = None
if isinstance(content, dict) and content.get("documentData"):
fileContentBase64 = content.get("documentData")
elif hasattr(content, "documentData") and content.documentData:
fileContentBase64 = content.documentData
elif isinstance(content, dict) and (content.get("validationMetadata") or {}).get("fileId"):
file_id = content["validationMetadata"]["fileId"]
try:
raw = self.services.chat.getFileData(file_id)
fileContentBase64 = base64.b64encode(raw if isinstance(raw, bytes) else str(raw).encode("utf-8")).decode("utf-8")
except Exception as e:
return ActionResult.isFailure(error=f"Could not load file content from fileId {file_id}: {e}")
if not fileContentBase64:
from modules.datamodels.datamodelDocref import DocumentReferenceList
docList = DocumentReferenceList.from_string_list([content] if isinstance(content, str) else content)
chatDocuments = self.services.chat.getChatDocumentsFromDocumentList(docList)
if not chatDocuments or len(chatDocuments) == 0:
return ActionResult.isFailure(error="Could not get file content from document reference")
fileContentBase64 = chatDocuments[0].documentData
# Decode base64
try:
fileContent = base64.b64decode(fileContentBase64)
except Exception as e:
return ActionResult.isFailure(error=f"Could not decode base64 file content: {str(e)}")
# Upload file (connection/token already set above)
uploadResult = await self.services.sharepoint.uploadFile(
siteId=siteId,
folderPath=folderPath,
fileName=fileName,
content=fileContent
)
if "error" in uploadResult:
return ActionResult.isFailure(error=f"Upload failed: {uploadResult['error']}")
logger.info(f"Uploaded file to SharePoint: {folderPath}/{fileName} ({len(fileContent)} bytes)")
# Generate filename
workflowContext = self.services.chat.getWorkflowContext() if hasattr(self.services, 'chat') else None
filename = self._generateMeaningfulFileName(
"file_upload_result",
"json",
workflowContext,
"uploadFile"
)
result = {
"success": True,
"siteId": siteId,
"filePath": f"{folderPath}/{fileName}",
"fileSize": len(fileContent),
"uploadResult": uploadResult
}
validationMetadata = self._createValidationMetadata(
"uploadFile",
siteId=siteId,
filePath=f"{folderPath}/{fileName}",
fileSize=len(fileContent)
)
document = ActionDocument(
documentName=filename,
documentData=json.dumps(result, indent=2),
mimeType="application/json",
validationMetadata=validationMetadata
)
return ActionResult.isSuccess(documents=[document])
except Exception as e:
errorMsg = f"Error uploading file to SharePoint: {str(e)}"
logger.error(errorMsg)
return ActionResult.isFailure(error=errorMsg)