n8n-nodes-onedrive-business 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,63 @@
1
+ import { IHookFunctions, IWebhookFunctions, IPollFunctions, INodeType, INodeTypeDescription, INodeExecutionData, IWebhookResponseData } from 'n8n-workflow';
2
+ /**
3
+ * OneDrive Business Trigger Node
4
+ *
5
+ * ARCHITECTURE (CRITICAL):
6
+ *
7
+ * 1. Webhook Endpoint
8
+ * - Receives notifications from Microsoft Graph
9
+ * - Validates clientState and validationToken
10
+ * - Returns 200 OK immediately
11
+ * - NEVER emits workflow data directly
12
+ *
13
+ * 2. Microsoft Graph Subscription
14
+ * - Auto-created on activation
15
+ * - Auto-renewed before expiration
16
+ * - Auto-deleted on deactivation
17
+ * - Resource: /users/{userId}/drive/root or /sites/{siteId}/drive
18
+ * - Change types: created,updated
19
+ *
20
+ * 3. Delta Query Processing
21
+ * - Source of truth for all events
22
+ * - Triggered by webhook notifications (via polling)
23
+ * - Fetches actual file metadata
24
+ * - Handles pagination
25
+ * - Persists deltaLink between runs
26
+ *
27
+ * 4. Deduplication Engine
28
+ * - File stability checking (hash, size, timestamps)
29
+ * - Version tracking (${itemId}_${eTag})
30
+ * - 15-second stability window
31
+ * - Event classification (created vs updated)
32
+ *
33
+ * This architecture ensures EXACTLY ONE workflow execution per file version,
34
+ * despite SharePoint generating multiple backend updates per upload.
35
+ */
36
+ export declare class OneDriveBusinessTrigger implements INodeType {
37
+ description: INodeTypeDescription;
38
+ private static instances;
39
+ /**
40
+ * Webhook Setup (on activation)
41
+ */
42
+ webhookMethods: {
43
+ default: {
44
+ checkExists(this: IHookFunctions): Promise<boolean>;
45
+ create(this: IHookFunctions): Promise<boolean>;
46
+ delete(this: IHookFunctions): Promise<boolean>;
47
+ };
48
+ };
49
+ /**
50
+ * Webhook Handler (receives notifications)
51
+ *
52
+ * CRITICAL: This does NOT emit workflow data.
53
+ * It only validates the request and returns 200 OK.
54
+ */
55
+ webhook(this: IWebhookFunctions): Promise<IWebhookResponseData>;
56
+ /**
57
+ * Polling (Source of Truth)
58
+ *
59
+ * This is where the actual workflow triggering happens.
60
+ * Called periodically to check for changes via Delta Query.
61
+ */
62
+ poll(this: IPollFunctions): Promise<INodeExecutionData[][] | null>;
63
+ }
@@ -0,0 +1,332 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.OneDriveBusinessTrigger = void 0;
4
+ const GraphClient_1 = require("../utils/GraphClient");
5
+ const StateStore_1 = require("../utils/StateStore");
6
+ const DeltaProcessor_1 = require("../utils/DeltaProcessor");
7
+ const WebhookHandler_1 = require("../utils/WebhookHandler");
8
+ const helpers_1 = require("../utils/helpers");
9
+ /**
10
+ * OneDrive Business Trigger Node
11
+ *
12
+ * ARCHITECTURE (CRITICAL):
13
+ *
14
+ * 1. Webhook Endpoint
15
+ * - Receives notifications from Microsoft Graph
16
+ * - Validates clientState and validationToken
17
+ * - Returns 200 OK immediately
18
+ * - NEVER emits workflow data directly
19
+ *
20
+ * 2. Microsoft Graph Subscription
21
+ * - Auto-created on activation
22
+ * - Auto-renewed before expiration
23
+ * - Auto-deleted on deactivation
24
+ * - Resource: /users/{userId}/drive/root or /sites/{siteId}/drive
25
+ * - Change types: created,updated
26
+ *
27
+ * 3. Delta Query Processing
28
+ * - Source of truth for all events
29
+ * - Triggered by webhook notifications (via polling)
30
+ * - Fetches actual file metadata
31
+ * - Handles pagination
32
+ * - Persists deltaLink between runs
33
+ *
34
+ * 4. Deduplication Engine
35
+ * - File stability checking (hash, size, timestamps)
36
+ * - Version tracking (${itemId}_${eTag})
37
+ * - 15-second stability window
38
+ * - Event classification (created vs updated)
39
+ *
40
+ * This architecture ensures EXACTLY ONE workflow execution per file version,
41
+ * despite SharePoint generating multiple backend updates per upload.
42
+ */
43
+ class OneDriveBusinessTrigger {
44
+ constructor() {
45
+ this.description = {
46
+ displayName: 'OneDrive Business Trigger',
47
+ name: 'oneDriveBusinessTrigger',
48
+ icon: 'file:onedrive.svg',
49
+ group: ['trigger'],
50
+ version: 1,
51
+ subtitle: '={{$parameter["event"]}}',
52
+ description: 'Triggers when files or folders change in OneDrive Business',
53
+ defaults: {
54
+ name: 'OneDrive Business Trigger',
55
+ },
56
+ inputs: [],
57
+ outputs: ['main'],
58
+ credentials: [
59
+ {
60
+ name: 'oneDriveBusinessOAuth2Api',
61
+ required: true,
62
+ },
63
+ ],
64
+ webhooks: [
65
+ {
66
+ name: 'default',
67
+ httpMethod: 'POST',
68
+ responseMode: 'onReceived',
69
+ path: 'webhook',
70
+ },
71
+ ],
72
+ polling: true,
73
+ properties: [
74
+ // Drive Location
75
+ {
76
+ displayName: 'Drive Type',
77
+ name: 'driveType',
78
+ type: 'options',
79
+ options: [
80
+ {
81
+ name: 'User Drive',
82
+ value: 'user',
83
+ description: 'Monitor a user\'s OneDrive',
84
+ },
85
+ {
86
+ name: 'Site Drive',
87
+ value: 'site',
88
+ description: 'Monitor a SharePoint site\'s document library',
89
+ },
90
+ ],
91
+ default: 'user',
92
+ description: 'Type of drive to monitor',
93
+ },
94
+ {
95
+ displayName: 'User ID',
96
+ name: 'userId',
97
+ type: 'string',
98
+ displayOptions: {
99
+ show: {
100
+ driveType: ['user'],
101
+ },
102
+ },
103
+ default: '',
104
+ required: true,
105
+ placeholder: 'user@contoso.com or user-id',
106
+ description: 'User principal name or ID',
107
+ },
108
+ {
109
+ displayName: 'Site ID',
110
+ name: 'siteId',
111
+ type: 'string',
112
+ displayOptions: {
113
+ show: {
114
+ driveType: ['site'],
115
+ },
116
+ },
117
+ default: '',
118
+ required: true,
119
+ placeholder: 'contoso.sharepoint.com,site-id,web-id',
120
+ description: 'SharePoint site ID',
121
+ },
122
+ // Event Selection
123
+ {
124
+ displayName: 'Trigger On',
125
+ name: 'events',
126
+ type: 'multiOptions',
127
+ options: [
128
+ {
129
+ name: 'File Created',
130
+ value: 'file.created',
131
+ description: 'Trigger when a new file is created',
132
+ },
133
+ {
134
+ name: 'File Updated',
135
+ value: 'file.updated',
136
+ description: 'Trigger when a file is modified',
137
+ },
138
+ {
139
+ name: 'Folder Created',
140
+ value: 'folder.created',
141
+ description: 'Trigger when a new folder is created',
142
+ },
143
+ {
144
+ name: 'Folder Updated',
145
+ value: 'folder.updated',
146
+ description: 'Trigger when a folder is modified',
147
+ },
148
+ ],
149
+ default: ['file.created', 'file.updated'],
150
+ required: true,
151
+ description: 'Events that trigger the workflow',
152
+ },
153
+ // Polling interval (fallback if webhooks fail)
154
+ {
155
+ displayName: 'Polling Interval',
156
+ name: 'pollInterval',
157
+ type: 'number',
158
+ default: 60,
159
+ description: 'How often to check for changes (in seconds) if webhook notifications are not received',
160
+ },
161
+ ],
162
+ };
163
+ /**
164
+ * Webhook Setup (on activation)
165
+ */
166
+ this.webhookMethods = {
167
+ default: {
168
+ async checkExists() {
169
+ const webhookData = this.getWorkflowStaticData('node');
170
+ if (webhookData.subscriptionId) {
171
+ // Check if subscription still exists
172
+ const graphClient = new GraphClient_1.GraphClient(this);
173
+ try {
174
+ await graphClient.get(`/subscriptions/${webhookData.subscriptionId}`);
175
+ return true;
176
+ }
177
+ catch (error) {
178
+ // Subscription doesn't exist
179
+ delete webhookData.subscriptionId;
180
+ return false;
181
+ }
182
+ }
183
+ return false;
184
+ },
185
+ async create() {
186
+ const webhookUrl = this.getNodeWebhookUrl('default');
187
+ const nodeId = this.getNode().id;
188
+ // Initialize components
189
+ const graphClient = new GraphClient_1.GraphClient(this);
190
+ // Get drive location from parameters
191
+ const driveType = this.getNodeParameter('driveType');
192
+ const driveLocation = {
193
+ type: driveType,
194
+ userId: driveType === 'user' ? this.getNodeParameter('userId') : undefined,
195
+ siteId: driveType === 'site' ? this.getNodeParameter('siteId') : undefined,
196
+ };
197
+ const subscriptionResource = (0, helpers_1.resolveSubscriptionResource)(driveLocation);
198
+ const drivePath = (0, helpers_1.resolveDrivePath)(driveLocation);
199
+ // Initialize state store
200
+ const credentials = await this.getCredentials('oneDriveBusinessOAuth2Api');
201
+ const tenantId = credentials.tenantId;
202
+ // Get drive ID
203
+ const drive = await graphClient.get(drivePath);
204
+ const driveId = drive.id;
205
+ const stateStore = new StateStore_1.StateStore(nodeId);
206
+ await stateStore.initialize(tenantId, driveId, driveLocation.userId, driveLocation.siteId);
207
+ // Create webhook handler
208
+ const webhookHandler = new WebhookHandler_1.WebhookHandler(graphClient, stateStore, subscriptionResource);
209
+ // Create subscription
210
+ const subscription = await webhookHandler.createOrRenewSubscription(webhookUrl);
211
+ // Store subscription ID in workflow static data
212
+ const webhookData = this.getWorkflowStaticData('node');
213
+ webhookData.subscriptionId = subscription.id;
214
+ // Store instances for webhook handling
215
+ OneDriveBusinessTrigger.instances.set(nodeId, {
216
+ stateStore,
217
+ webhookHandler,
218
+ });
219
+ console.log(`OneDrive Business Trigger activated for node ${nodeId}`);
220
+ return true;
221
+ },
222
+ async delete() {
223
+ const webhookData = this.getWorkflowStaticData('node');
224
+ const nodeId = this.getNode().id;
225
+ const instance = OneDriveBusinessTrigger.instances.get(nodeId);
226
+ if (instance) {
227
+ // Delete subscription
228
+ try {
229
+ await instance.webhookHandler.deleteSubscription();
230
+ }
231
+ catch (error) {
232
+ console.error('Error deleting subscription:', error);
233
+ }
234
+ // Flush state
235
+ await instance.stateStore.flush();
236
+ // Remove instance
237
+ OneDriveBusinessTrigger.instances.delete(nodeId);
238
+ }
239
+ delete webhookData.subscriptionId;
240
+ console.log(`OneDrive Business Trigger deactivated for node ${nodeId}`);
241
+ return true;
242
+ },
243
+ },
244
+ };
245
+ }
246
+ /**
247
+ * Webhook Handler (receives notifications)
248
+ *
249
+ * CRITICAL: This does NOT emit workflow data.
250
+ * It only validates the request and returns 200 OK.
251
+ */
252
+ async webhook() {
253
+ const req = this.getRequestObject();
254
+ const res = this.getResponseObject();
255
+ const nodeId = this.getNode().id;
256
+ const instance = OneDriveBusinessTrigger.instances.get(nodeId);
257
+ if (instance) {
258
+ await instance.webhookHandler.handleWebhookRequest(req, res);
259
+ }
260
+ else {
261
+ // No instance found, respond with 404
262
+ res.writeHead(404);
263
+ res.end();
264
+ }
265
+ // Return no data (webhook notifications don't trigger workflows)
266
+ return {
267
+ noWebhookResponse: true,
268
+ };
269
+ }
270
+ /**
271
+ * Polling (Source of Truth)
272
+ *
273
+ * This is where the actual workflow triggering happens.
274
+ * Called periodically to check for changes via Delta Query.
275
+ */
276
+ async poll() {
277
+ const nodeId = this.getNode().id;
278
+ const events = this.getNodeParameter('events');
279
+ const eventFilter = new Set(events);
280
+ // Initialize components
281
+ const graphClient = new GraphClient_1.GraphClient(this);
282
+ // Get drive location from parameters
283
+ const driveType = this.getNodeParameter('driveType');
284
+ const driveLocation = {
285
+ type: driveType,
286
+ userId: driveType === 'user' ? this.getNodeParameter('userId') : undefined,
287
+ siteId: driveType === 'site' ? this.getNodeParameter('siteId') : undefined,
288
+ };
289
+ const drivePath = (0, helpers_1.resolveDrivePath)(driveLocation);
290
+ // Initialize state store
291
+ const credentials = await this.getCredentials('oneDriveBusinessOAuth2Api');
292
+ const tenantId = credentials.tenantId;
293
+ // Get drive ID
294
+ const drive = await graphClient.get(drivePath);
295
+ const driveId = drive.id;
296
+ // Get or create state store
297
+ let instance = OneDriveBusinessTrigger.instances.get(nodeId);
298
+ if (!instance) {
299
+ const stateStore = new StateStore_1.StateStore(nodeId);
300
+ await stateStore.initialize(tenantId, driveId, driveLocation.userId, driveLocation.siteId);
301
+ const subscriptionResource = (0, helpers_1.resolveSubscriptionResource)(driveLocation);
302
+ const webhookHandler = new WebhookHandler_1.WebhookHandler(graphClient, stateStore, subscriptionResource);
303
+ instance = { stateStore, webhookHandler };
304
+ OneDriveBusinessTrigger.instances.set(nodeId, instance);
305
+ }
306
+ // Create delta processor
307
+ const deltaProcessor = new DeltaProcessor_1.DeltaProcessor(graphClient, instance.stateStore, drivePath);
308
+ // Process delta query
309
+ const triggerEvents = await deltaProcessor.processDeltaQuery(eventFilter);
310
+ // Clean up old versions periodically
311
+ const state = instance.stateStore.getState();
312
+ const processedCount = Object.keys(state.processedVersions).length;
313
+ if (processedCount > 1000) {
314
+ await instance.stateStore.cleanupOldVersions(10);
315
+ }
316
+ // Convert to workflow data
317
+ const workflowData = [];
318
+ if (triggerEvents.length > 0) {
319
+ workflowData.push(triggerEvents.map(event => ({
320
+ json: {
321
+ event: event.eventType,
322
+ timestamp: event.timestamp,
323
+ item: event.item,
324
+ },
325
+ })));
326
+ }
327
+ return workflowData;
328
+ }
329
+ }
330
+ exports.OneDriveBusinessTrigger = OneDriveBusinessTrigger;
331
+ // Store instances for cleanup
332
+ OneDriveBusinessTrigger.instances = new Map();
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"><path d="M41.8 17H41c-7.3 0-13.4 5.3-14.7 12.3-.6-.1-1.2-.2-1.8-.2-6.5 0-11.9 4.6-13.2 10.8C5 40.8 0 46.1 0 52.5c0 6.4 5.2 11.5 11.5 11.5h31.9c11.4 0 20.6-9.2 20.6-20.6 0-10.4-7.7-19-17.7-20.3-1.1-9.9-9.5-17.6-19.5-17.6z" fill="#0078D4"/></svg>
@@ -0,0 +1,218 @@
1
+ /**
2
+ * TypeScript Type Definitions for OneDrive Business Node
3
+ *
4
+ * These types are based on Microsoft Graph API v1.0 responses
5
+ * and are used throughout the OneDrive Business custom node.
6
+ */
7
+ /**
8
+ * Microsoft Graph DriveItem representation
9
+ * Reference: https://learn.microsoft.com/en-us/graph/api/resources/driveitem
10
+ */
11
+ export interface DriveItem {
12
+ id: string;
13
+ name: string;
14
+ size: number;
15
+ webUrl: string;
16
+ createdDateTime: string;
17
+ lastModifiedDateTime: string;
18
+ eTag: string;
19
+ cTag: string;
20
+ parentReference?: {
21
+ driveId: string;
22
+ driveType: string;
23
+ id: string;
24
+ path: string;
25
+ };
26
+ file?: {
27
+ mimeType: string;
28
+ hashes?: {
29
+ quickXorHash?: string;
30
+ sha1Hash?: string;
31
+ sha256Hash?: string;
32
+ };
33
+ };
34
+ folder?: {
35
+ childCount: number;
36
+ };
37
+ fileSystemInfo?: {
38
+ createdDateTime: string;
39
+ lastModifiedDateTime: string;
40
+ };
41
+ deleted?: {
42
+ state: string;
43
+ };
44
+ shared?: {
45
+ scope: string;
46
+ owner?: {
47
+ user?: {
48
+ displayName: string;
49
+ id: string;
50
+ };
51
+ };
52
+ };
53
+ '@microsoft.graph.downloadUrl'?: string;
54
+ }
55
+ /**
56
+ * Delta Query Response from Microsoft Graph
57
+ * Used for tracking changes in OneDrive
58
+ */
59
+ export interface DeltaResponse {
60
+ value: DriveItem[];
61
+ '@odata.nextLink'?: string;
62
+ '@odata.deltaLink'?: string;
63
+ }
64
+ /**
65
+ * Microsoft Graph Webhook Notification
66
+ * Received when a change occurs in the subscribed resource
67
+ */
68
+ export interface WebhookNotification {
69
+ subscriptionId: string;
70
+ clientState?: string;
71
+ expirationDateTime: string;
72
+ resource: string;
73
+ changeType: string;
74
+ resourceData?: {
75
+ id: string;
76
+ '@odata.type': string;
77
+ '@odata.id': string;
78
+ };
79
+ }
80
+ /**
81
+ * Webhook validation request from Microsoft Graph
82
+ * Sent when creating a new subscription
83
+ */
84
+ export interface WebhookValidationRequest {
85
+ validationToken: string;
86
+ }
87
+ /**
88
+ * Microsoft Graph Subscription (Webhook)
89
+ * Reference: https://learn.microsoft.com/en-us/graph/api/resources/subscription
90
+ */
91
+ export interface GraphSubscription {
92
+ id: string;
93
+ resource: string;
94
+ changeType: string;
95
+ notificationUrl: string;
96
+ expirationDateTime: string;
97
+ clientState?: string;
98
+ creatorId?: string;
99
+ }
100
+ /**
101
+ * Persistent state for OneDrive Business Trigger Node
102
+ * Stored between workflow executions to maintain deduplication
103
+ */
104
+ export interface NodeState {
105
+ tenantId: string;
106
+ driveId: string;
107
+ userId?: string;
108
+ siteId?: string;
109
+ deltaLink: string | null;
110
+ lastDeltaQuery: number;
111
+ processedVersions: Record<string, boolean>;
112
+ lastKnownItems: Record<string, {
113
+ eTag: string;
114
+ lastModifiedDateTime: string;
115
+ }>;
116
+ subscriptionId?: string;
117
+ subscriptionExpiration?: number;
118
+ }
119
+ /**
120
+ * File stability tracking
121
+ * Used to determine if a file is still being uploaded/modified
122
+ */
123
+ export interface FileStabilityInfo {
124
+ itemId: string;
125
+ eTag: string;
126
+ size: number;
127
+ lastModifiedDateTime: string;
128
+ hasHash: boolean;
129
+ isStable: boolean;
130
+ firstSeen: number;
131
+ lastChecked: number;
132
+ }
133
+ /**
134
+ * Event emitted by the trigger node
135
+ */
136
+ export interface TriggerEvent {
137
+ eventType: 'file.created' | 'file.updated' | 'folder.created' | 'folder.updated';
138
+ item: DriveItem;
139
+ timestamp: string;
140
+ }
141
+ /**
142
+ * OAuth2 Credentials structure
143
+ */
144
+ export interface OneDriveBusinessCredentials {
145
+ clientId: string;
146
+ clientSecret: string;
147
+ tenantId: string;
148
+ accessToken: string;
149
+ refreshToken: string;
150
+ expiresIn: number;
151
+ tokenType: string;
152
+ }
153
+ /**
154
+ * Drive resolution options
155
+ */
156
+ export interface DriveLocation {
157
+ type: 'user' | 'site';
158
+ userId?: string;
159
+ siteId?: string;
160
+ }
161
+ /**
162
+ * Graph API Error Response
163
+ */
164
+ export interface GraphError {
165
+ error: {
166
+ code: string;
167
+ message: string;
168
+ innerError?: {
169
+ code: string;
170
+ message: string;
171
+ date: string;
172
+ 'request-id': string;
173
+ 'client-request-id': string;
174
+ };
175
+ };
176
+ }
177
+ /**
178
+ * Share link creation request
179
+ */
180
+ export interface ShareLinkRequest {
181
+ type: 'view' | 'edit' | 'embed';
182
+ scope?: 'anonymous' | 'organization';
183
+ password?: string;
184
+ expirationDateTime?: string;
185
+ }
186
+ /**
187
+ * Share link response
188
+ */
189
+ export interface ShareLinkResponse {
190
+ id: string;
191
+ roles: string[];
192
+ link: {
193
+ type: string;
194
+ scope: string;
195
+ webUrl: string;
196
+ webHtml?: string;
197
+ application?: {
198
+ id: string;
199
+ displayName: string;
200
+ };
201
+ };
202
+ }
203
+ /**
204
+ * Pagination helper for Graph API
205
+ */
206
+ export interface PaginatedResponse<T> {
207
+ value: T[];
208
+ '@odata.nextLink'?: string;
209
+ }
210
+ /**
211
+ * Retry configuration for API calls
212
+ */
213
+ export interface RetryConfig {
214
+ maxRetries: number;
215
+ initialDelayMs: number;
216
+ maxDelayMs: number;
217
+ backoffMultiplier: number;
218
+ }
package/dist/types.js ADDED
@@ -0,0 +1,8 @@
1
+ "use strict";
2
+ /**
3
+ * TypeScript Type Definitions for OneDrive Business Node
4
+ *
5
+ * These types are based on Microsoft Graph API v1.0 responses
6
+ * and are used throughout the OneDrive Business custom node.
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });