gateway/modules/workflows/methods/methodSharepoint/actions/uploadFile.py
2026-03-22 19:46:50 +01:00

158 lines
7.3 KiB
Python

# 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")
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)}")
# 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")
# Upload file
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)