UNPKG

n8n-nodes-smartgent

Version:

SmartGent custom nodes for n8n - AI-powered automation and intelligent workflow integrations including LiteLLM chat completions, SharePoint file monitoring, and enterprise search

529 lines 24.6 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.SmartgentSharePoint = void 0; const n8n_workflow_1 = require("n8n-workflow"); const sharepoint_1 = require("../../utils/sharepoint"); class SmartgentSharePoint { constructor() { this.description = { displayName: 'Smartgent SharePoint', name: 'smartgentSharePoint', icon: 'file:sharepoint.svg', group: ['transform'], version: 1, subtitle: '={{$parameter["operation"]}}', description: 'Get documents from SharePoint document libraries using Microsoft Graph API', defaults: { name: 'Smartgent SharePoint', }, inputs: ['main'], outputs: ['main'], credentials: [ { name: 'smartgentMicrosoftSharePointApi', required: true, }, ], properties: [ { displayName: 'Operation', name: 'operation', type: 'options', noDataExpression: true, options: [ { name: 'List Documents', value: 'listDocuments', description: 'Get all documents from a document library', action: 'List all documents in a library', }, { name: 'Get Document Details', value: 'getDocument', description: 'Get details of a specific document', action: 'Get document details', }, { name: 'Download Document', value: 'downloadDocument', description: 'Download a document content', action: 'Download document content', }, { name: 'Move File', value: 'moveFile', description: 'Move a file to a different folder', action: 'Move file to folder', }, ], default: 'listDocuments', }, { displayName: 'Document Library Path', name: 'libraryPath', type: 'string', default: '/', required: true, description: 'Path to the document library. Use "/" for root/main Documents library, or specify a subfolder path.', placeholder: '/', displayOptions: { show: { operation: ['listDocuments'], }, }, }, { displayName: 'Document ID', name: 'documentId', type: 'string', default: '', required: true, description: 'The ID of the document to retrieve', displayOptions: { show: { operation: ['getDocument', 'downloadDocument'], }, }, }, { displayName: 'Source Document ID', name: 'sourceDocumentId', type: 'string', default: '', required: true, description: 'The ID of the document to move', displayOptions: { show: { operation: ['moveFile'], }, }, }, { displayName: 'Destination Folder Path', name: 'destinationFolderPath', type: 'string', default: '', required: true, description: 'The path of the destination folder (e.g., "/NewFolder" or "ParentFolder/SubFolder")', placeholder: '/NewFolder', displayOptions: { show: { operation: ['moveFile'], }, }, }, { displayName: 'Additional Options', name: 'additionalOptions', type: 'collection', placeholder: 'Add Option', default: {}, options: [ { displayName: 'Filter', name: 'filter', type: 'string', default: '', description: 'OData filter expression to filter documents', placeholder: "name eq 'document.pdf'", }, { displayName: 'Select Fields', name: 'select', type: 'string', default: '', description: 'Comma-separated list of fields to include in response', placeholder: 'id,name,lastModifiedDateTime,size', }, { displayName: 'Top', name: 'top', type: 'number', default: 100, description: 'Maximum number of items to return', typeOptions: { minValue: 1, maxValue: 1000, }, }, ], }, ], }; } async execute() { const items = this.getInputData(); const returnData = []; for (let i = 0; i < items.length; i++) { try { const operation = this.getNodeParameter('operation', i); const credentials = await this.getCredentials('smartgentMicrosoftSharePointApi'); const tokenResponse = await SmartgentSharePoint.getAccessToken(credentials, this); const accessToken = tokenResponse.access_token; const siteId = await SmartgentSharePoint.getSiteId(credentials.siteUrl, accessToken, this); let executionData; switch (operation) { case 'listDocuments': const libraryPath = this.getNodeParameter('libraryPath', i); const additionalOptions = this.getNodeParameter('additionalOptions', i); const driveId = await SmartgentSharePoint.getDriveId(siteId, accessToken, this); const documents = await SmartgentSharePoint.listDocuments(driveId, libraryPath, accessToken, additionalOptions, this); executionData = { json: { operation: 'listDocuments', siteId, driveId, libraryPath, documents, totalCount: documents.length, timestamp: new Date().toISOString(), }, pairedItem: { item: i }, }; break; case 'getDocument': const documentId = this.getNodeParameter('documentId', i); const driveIdForGet = await SmartgentSharePoint.getDriveId(siteId, accessToken, this); const documentDetails = await SmartgentSharePoint.getDocument(driveIdForGet, documentId, accessToken, this); executionData = { json: { operation: 'getDocument', siteId, driveId: driveIdForGet, documentId, document: documentDetails, timestamp: new Date().toISOString(), }, pairedItem: { item: i }, }; break; case 'downloadDocument': const downloadDocumentId = this.getNodeParameter('documentId', i); const driveIdForDownload = await SmartgentSharePoint.getDriveId(siteId, accessToken, this); const downloadContent = await SmartgentSharePoint.downloadDocument(driveIdForDownload, downloadDocumentId, accessToken, this); const downloadDocumentDetails = await SmartgentSharePoint.getDocument(driveIdForDownload, downloadDocumentId, accessToken, this); executionData = { json: { operation: 'downloadDocument', siteId, driveId: driveIdForDownload, documentId: downloadDocumentId, fileName: downloadDocumentDetails.name, timestamp: new Date().toISOString(), }, binary: { data: { data: downloadContent.toString('base64'), mimeType: downloadDocumentDetails.mimeType || 'application/octet-stream', fileName: downloadDocumentDetails.name, }, }, pairedItem: { item: i }, }; break; case 'moveFile': const sourceDocumentId = this.getNodeParameter('sourceDocumentId', i); const destinationFolderPath = this.getNodeParameter('destinationFolderPath', i); const driveIdForMove = await SmartgentSharePoint.getDriveId(siteId, accessToken, this); const destinationFolderId = await SmartgentSharePoint.getFolderId(driveIdForMove, destinationFolderPath, accessToken, this); const moveRequest = { driveId: driveIdForMove, fileId: sourceDocumentId, destinationFolderId, token: accessToken, }; await (0, sharepoint_1.moveFile)(moveRequest); const movedFileDetails = await SmartgentSharePoint.getDocument(driveIdForMove, sourceDocumentId, accessToken, this); executionData = { json: { operation: 'moveFile', siteId, driveId: driveIdForMove, sourceDocumentId, destinationFolderPath, destinationFolderId, movedFile: movedFileDetails, success: true, timestamp: new Date().toISOString(), }, pairedItem: { item: i }, }; break; default: throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Unknown operation: ${operation}`); } returnData.push(executionData); } catch (error) { if (this.continueOnFail()) { const executionData = { json: { error: error.message, operation: this.getNodeParameter('operation', i), timestamp: new Date().toISOString(), }, pairedItem: { item: i }, }; returnData.push(executionData); } else { throw new n8n_workflow_1.NodeOperationError(this.getNode(), `SharePoint operation failed: ${error.message}`, { itemIndex: i }); } } } return [returnData]; } static async getAccessToken(credentials, context) { var _a, _b, _c, _d; const tokenUrl = credentials.tokenEndpoint || `https://login.microsoftonline.com/${credentials.tenantId}/oauth2/v2.0/token`; const data = { grant_type: 'client_credentials', client_id: credentials.clientId, client_secret: credentials.clientSecret, scope: 'https://graph.microsoft.com/.default', }; const formDataString = Object.keys(data) .map((key) => `${encodeURIComponent(key)}=${encodeURIComponent(data[key])}`) .join('&'); const options = { method: 'POST', url: tokenUrl, headers: { 'content-type': 'application/x-www-form-urlencoded', }, body: formDataString, json: true, }; try { const response = await context.helpers.httpRequest(options); if (!response.access_token) { throw new n8n_workflow_1.ApplicationError('Failed to obtain access token from Microsoft. Check your tenant ID, client ID, and client secret.'); } return response; } catch (error) { if (((_a = error.response) === null || _a === void 0 ? void 0 : _a.status) === 400) { throw new n8n_workflow_1.ApplicationError(`Authentication failed: ${((_c = (_b = error.response) === null || _b === void 0 ? void 0 : _b.data) === null || _c === void 0 ? void 0 : _c.error_description) || 'Invalid client credentials or tenant ID'}`); } else if (((_d = error.response) === null || _d === void 0 ? void 0 : _d.status) === 401) { throw new n8n_workflow_1.ApplicationError('Authentication failed: Invalid client credentials. Please check your Client ID and Client Secret.'); } throw new n8n_workflow_1.ApplicationError(`Authentication error: ${error.message}`); } } static async getSiteId(siteUrl, token, context) { console.log('Parsing site URL:', siteUrl); try { const urlObj = new URL(siteUrl); const hostname = urlObj.hostname; const relativePath = urlObj.pathname; console.log('Hostname:', hostname, 'Relative path:', relativePath); const directUrl = `https://graph.microsoft.com/v1.0/sites/${hostname}:${relativePath}`; console.log('Trying direct site URL:', directUrl); const directOptions = { method: 'GET', url: directUrl, headers: { Authorization: `Bearer ${token}`, }, json: true, }; const directResponse = await context.helpers.httpRequest(directOptions); console.log('Direct site response:', directResponse); if (directResponse.id) { return directResponse.id; } } catch (directError) { console.log('Direct site access failed, trying search method:', directError.message); } const urlParts = siteUrl.split('sites/'); if (urlParts.length < 2) { throw new n8n_workflow_1.ApplicationError('Invalid SharePoint site URL format'); } const siteName = urlParts[1].split('/')[0]; console.log('Extracted site name:', siteName); const options = { method: 'GET', url: `https://graph.microsoft.com/v1.0/sites?search=${siteName}`, headers: { Authorization: `Bearer ${token}`, }, json: true, }; const response = await context.helpers.httpRequest(options); console.log('Site search response:', response); if (!response.value || response.value.length === 0) { throw new n8n_workflow_1.ApplicationError(`No sites found matching '${siteName}'`); } const site = response.value.find((x) => x.name && x.name.toLowerCase() === siteName.toLowerCase()); if (!site) { const partialMatch = response.value.find((x) => x.name && x.name.toLowerCase().includes(siteName.toLowerCase())); if (!partialMatch) { throw new n8n_workflow_1.ApplicationError(`Site '${siteName}' not found. Available sites: ${response.value.map((x) => x.name).join(', ')}`); } return partialMatch.id; } return site.id; } static async getDriveId(siteId, token, context) { const options = { method: 'GET', url: `https://graph.microsoft.com/v1.0/sites/${siteId}/drives`, headers: { Authorization: `Bearer ${token}`, }, json: true, }; const response = await context.helpers.httpRequest(options); console.log('Drives response:', response); if (!response.value || response.value.length === 0) { throw new n8n_workflow_1.ApplicationError('No drives found for the site'); } const documentsDrive = response.value.find((drive) => drive.name === 'Documents' || drive.webUrl.includes('Shared%20Documents') || drive.webUrl.includes('Shared Documents')); if (documentsDrive) { console.log('Found Documents drive:', documentsDrive); return documentsDrive.id; } console.log('No Documents drive found, using first drive:', response.value[0]); return response.value[0].id; } static async listDocuments(driveId, libraryPath, token, additionalOptions, context) { console.log('Listing documents for drive:', driveId, 'path:', libraryPath); let url; const decodedPath = decodeURIComponent(libraryPath); if (libraryPath === '/' || libraryPath === '' || libraryPath === '/root' || libraryPath === '/Shared Documents' || libraryPath === 'Shared Documents' || libraryPath === '/Shared%20Documents' || libraryPath === 'Shared%20Documents' || decodedPath === '/Shared Documents' || decodedPath === 'Shared Documents') { url = `https://graph.microsoft.com/v1.0/drives/${driveId}/root/children`; console.log('Using root children URL'); } else { const cleanPath = libraryPath.startsWith('/') ? libraryPath.substring(1) : libraryPath; url = `https://graph.microsoft.com/v1.0/drives/${driveId}/root:/${cleanPath}:/children`; console.log('Using path-specific URL with clean path:', cleanPath); } const queryParams = new URLSearchParams(); if (additionalOptions.filter) { queryParams.append('$filter', additionalOptions.filter); } if (additionalOptions.select) { queryParams.append('$select', additionalOptions.select); } else { queryParams.append('$select', 'id,name,lastModifiedDateTime,size,file,folder,webUrl,@microsoft.graph.downloadUrl'); } if (additionalOptions.top) { queryParams.append('$top', additionalOptions.top.toString()); } if (queryParams.toString()) { url += `?${queryParams.toString()}`; } console.log('Documents list URL:', url); const options = { method: 'GET', url, headers: { Authorization: `Bearer ${token}`, }, json: true, }; const response = await context.helpers.httpRequest(options); console.log('Documents list response:', response); const documents = response.value.filter((item) => item.file !== undefined); return documents.map((doc) => { var _a; return ({ id: doc.id, name: doc.name, lastModifiedDateTime: doc.lastModifiedDateTime, size: doc.size, webUrl: doc.webUrl, downloadUrl: doc['@microsoft.graph.downloadUrl'], mimeType: (_a = doc.file) === null || _a === void 0 ? void 0 : _a.mimeType, }); }); } static async getDocument(driveId, documentId, token, context) { var _a; const options = { method: 'GET', url: `https://graph.microsoft.com/v1.0/drives/${driveId}/items/${documentId}`, headers: { Authorization: `Bearer ${token}`, }, json: true, }; const response = await context.helpers.httpRequest(options); return { id: response.id, name: response.name, lastModifiedDateTime: response.lastModifiedDateTime, size: response.size, webUrl: response.webUrl, downloadUrl: response['@microsoft.graph.downloadUrl'], mimeType: (_a = response.file) === null || _a === void 0 ? void 0 : _a.mimeType, createdDateTime: response.createdDateTime, lastModifiedBy: response.lastModifiedBy, createdBy: response.createdBy, }; } static async downloadDocument(driveId, documentId, token, context) { const options = { method: 'GET', url: `https://graph.microsoft.com/v1.0/drives/${driveId}/items/${documentId}/content`, headers: { Authorization: `Bearer ${token}`, }, encoding: 'arraybuffer', }; const response = await context.helpers.httpRequest(options); return Buffer.from(response); } static async getFolderId(driveId, folderPath, token, context) { var _a, _b; let url; if (folderPath === '/' || folderPath === '' || folderPath === '/root') { url = `https://graph.microsoft.com/v1.0/drives/${driveId}/root`; } else { const cleanPath = folderPath.startsWith('/') ? folderPath.substring(1) : folderPath; url = `https://graph.microsoft.com/v1.0/drives/${driveId}/root:/${cleanPath}`; } const options = { method: 'GET', url, headers: { Authorization: `Bearer ${token}`, }, json: true, }; try { const response = await context.helpers.httpRequest(options); if (!response.id) { throw new n8n_workflow_1.ApplicationError(`Could not find folder: ${folderPath}`); } return response.id; } catch (error) { if (((_a = error.response) === null || _a === void 0 ? void 0 : _a.status) === 404) { throw new n8n_workflow_1.ApplicationError(`Destination folder not found: ${folderPath}. Please verify the path exists in SharePoint.`); } else if (((_b = error.response) === null || _b === void 0 ? void 0 : _b.status) === 403) { throw new n8n_workflow_1.ApplicationError(`Access denied to destination folder: ${folderPath}. Please check your permissions.`); } throw new n8n_workflow_1.ApplicationError(`Failed to access destination folder ${folderPath}: ${error.message}`); } } } exports.SmartgentSharePoint = SmartgentSharePoint; //# sourceMappingURL=SmartgentSharePoint.node.js.map