n8n-nodes-onedrive-business 1.1.0 → 1.1.4

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.
@@ -239,18 +239,26 @@ class OneDriveBusinessTrigger {
239
239
  await stateStore.initialize(tenantId, driveId, driveLocation.userId, driveLocation.siteId);
240
240
  // Create webhook handler
241
241
  const webhookHandler = new WebhookHandler_1.WebhookHandler(graphClient, stateStore, subscriptionResource);
242
- // Create subscription
243
- const subscription = await webhookHandler.createOrRenewSubscription(webhookUrl);
244
- // Store subscription ID in workflow static data
245
- const webhookData = this.getWorkflowStaticData('node');
246
- webhookData.subscriptionId = subscription.id;
247
- // Store instances for webhook handling
242
+ // Store instances for webhook handling BEFORE creating subscription
243
+ // This is critical because Microsoft verifies the webhook during creation
248
244
  OneDriveBusinessTrigger.instances.set(nodeId, {
249
245
  stateStore,
250
246
  webhookHandler,
251
247
  });
252
- console.log(`OneDrive Business Trigger activated for node ${nodeId}`);
253
- return true;
248
+ try {
249
+ // Create subscription
250
+ const subscription = await webhookHandler.createOrRenewSubscription(webhookUrl);
251
+ // Store subscription ID in workflow static data
252
+ const webhookData = this.getWorkflowStaticData('node');
253
+ webhookData.subscriptionId = subscription.id;
254
+ console.log(`OneDrive Business Trigger activated for node ${nodeId}`);
255
+ return true;
256
+ }
257
+ catch (error) {
258
+ // Cleanup if subscription fails
259
+ OneDriveBusinessTrigger.instances.delete(nodeId);
260
+ throw error;
261
+ }
254
262
  },
255
263
  async delete() {
256
264
  const webhookData = this.getWorkflowStaticData('node');
@@ -350,9 +358,9 @@ class OneDriveBusinessTrigger {
350
358
  OneDriveBusinessTrigger.instances.set(nodeId, instance);
351
359
  }
352
360
  // Create delta processor
353
- const deltaProcessor = new DeltaProcessor_1.DeltaProcessor(graphClient, instance.stateStore, drivePath);
361
+ const deltaProcessor = new DeltaProcessor_1.DeltaProcessor(graphClient, instance.stateStore, drivePath, watchPath);
354
362
  // Process delta query
355
- const triggerEvents = await deltaProcessor.processDeltaQuery(eventFilter, watchPath);
363
+ const triggerEvents = await deltaProcessor.processDeltaQuery(eventFilter);
356
364
  // Clean up old versions periodically
357
365
  const state = instance.stateStore.getState();
358
366
  const processedCount = Object.keys(state.processedVersions).length;
@@ -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>
@@ -36,21 +36,16 @@ export declare class DeltaProcessor {
36
36
  private graphClient;
37
37
  private stateStore;
38
38
  private drivePath;
39
+ private watchPath?;
39
40
  private stabilityTracker;
40
41
  private readonly STABILITY_WINDOW_MS;
41
- constructor(graphClient: GraphClient, stateStore: StateStore, drivePath: string);
42
+ constructor(graphClient: GraphClient, stateStore: StateStore, drivePath: string, watchPath?: string);
42
43
  /**
43
44
  * Process delta query and return triggerable events
44
45
  *
45
46
  * This is the main entry point called by the trigger node
46
47
  */
47
- /**
48
- * Process delta query and return triggerable events
49
- *
50
- * This is the main entry point called by the trigger node
51
- */
52
- processDeltaQuery(eventFilter: Set<string>, // e.g., ['file.created', 'file.updated']
53
- watchPath?: string): Promise<TriggerEvent[]>;
48
+ processDeltaQuery(eventFilter: Set<string>): Promise<TriggerEvent[]>;
54
49
  /**
55
50
  * Fetch all pages from delta query
56
51
  * Handles @odata.nextLink pagination and stores @odata.deltaLink
@@ -61,10 +56,6 @@ export declare class DeltaProcessor {
61
56
  * Returns a TriggerEvent if the item should trigger a workflow
62
57
  */
63
58
  private processItem;
64
- /**
65
- * Check if item is within the watched path
66
- */
67
- private isItemInScope;
68
59
  /**
69
60
  * Classify event as created vs updated
70
61
  *
@@ -34,7 +34,7 @@ const helpers_1 = require("./helpers");
34
34
  * Without this processor, ONE file upload would trigger FOUR workflow executions.
35
35
  */
36
36
  class DeltaProcessor {
37
- constructor(graphClient, stateStore, drivePath) {
37
+ constructor(graphClient, stateStore, drivePath, watchPath) {
38
38
  // Stability tracking for files currently being uploaded
39
39
  this.stabilityTracker = new Map();
40
40
  // Stability window: wait 15 seconds after last modification
@@ -42,19 +42,15 @@ class DeltaProcessor {
42
42
  this.graphClient = graphClient;
43
43
  this.stateStore = stateStore;
44
44
  this.drivePath = drivePath;
45
+ this.watchPath = watchPath;
45
46
  }
46
47
  /**
47
48
  * Process delta query and return triggerable events
48
49
  *
49
50
  * This is the main entry point called by the trigger node
50
51
  */
51
- /**
52
- * Process delta query and return triggerable events
53
- *
54
- * This is the main entry point called by the trigger node
55
- */
56
- async processDeltaQuery(eventFilter, // e.g., ['file.created', 'file.updated']
57
- watchPath) {
52
+ async processDeltaQuery(eventFilter // e.g., ['file.created', 'file.updated']
53
+ ) {
58
54
  const events = [];
59
55
  try {
60
56
  // Get current state
@@ -69,13 +65,28 @@ class DeltaProcessor {
69
65
  }
70
66
  else {
71
67
  // Initial delta query
72
- deltaEndpoint = `${this.drivePath}/root/delta`;
68
+ if (this.watchPath && this.watchPath !== '' && this.watchPath !== '/') {
69
+ // Scope to specific folder
70
+ let path = this.watchPath;
71
+ if (!path.startsWith('/')) {
72
+ path = '/' + path;
73
+ }
74
+ // Syntax: /drive/root:/path/to/folder:/delta
75
+ // Note the colons wrapping the path
76
+ deltaEndpoint = `${this.drivePath}/root:${encodeURIComponent(path)}:/delta`;
77
+ console.log(`[DeltaProcessor] Initializing Scoped Delta Query: ${deltaEndpoint}`);
78
+ }
79
+ else {
80
+ // Browse entire drive
81
+ deltaEndpoint = `${this.drivePath}/root/delta`;
82
+ console.log(`[DeltaProcessor] Initializing Root Delta Query`);
83
+ }
73
84
  }
74
85
  // Fetch delta changes (handle pagination)
75
86
  const items = await this.fetchAllDeltaPages(deltaEndpoint);
76
87
  // Process each item
77
88
  for (const item of items) {
78
- const event = await this.processItem(item, eventFilter, watchPath);
89
+ const event = await this.processItem(item, eventFilter);
79
90
  if (event) {
80
91
  events.push(event);
81
92
  }
@@ -95,6 +106,8 @@ class DeltaProcessor {
95
106
  const allItems = [];
96
107
  let currentEndpoint = initialEndpoint;
97
108
  while (currentEndpoint) {
109
+ // Log for debugging
110
+ console.log(`[DeltaProcessor] Fetching: ${currentEndpoint}`);
98
111
  const response = await this.graphClient.get(currentEndpoint);
99
112
  allItems.push(...response.value);
100
113
  // Check for next page
@@ -119,19 +132,18 @@ class DeltaProcessor {
119
132
  * Process a single delta item
120
133
  * Returns a TriggerEvent if the item should trigger a workflow
121
134
  */
122
- async processItem(item, eventFilter, watchPath) {
135
+ async processItem(item, eventFilter) {
123
136
  // Skip deleted items (unless specifically requested)
124
137
  if (item.deleted) {
125
138
  return null;
126
139
  }
127
- // Skip root folder
140
+ // Skip root folder (the folder itself, not its children)
128
141
  if (!item.parentReference) {
129
142
  return null;
130
143
  }
131
- // Check scope (folder filtering)
132
- if (watchPath && !this.isItemInScope(item, watchPath)) {
133
- return null;
134
- }
144
+ // Note: We don't need manual scope checking (isItemInScope) anymore
145
+ // because the Delta Query itself is scoped to the target folder.
146
+ // Any item returned here IS in the scope.
135
147
  // Determine event type
136
148
  const eventType = this.classifyEvent(item);
137
149
  // Check if this event type is enabled
@@ -163,42 +175,6 @@ class DeltaProcessor {
163
175
  timestamp: new Date().toISOString(),
164
176
  };
165
177
  }
166
- /**
167
- * Check if item is within the watched path
168
- */
169
- isItemInScope(item, watchPath) {
170
- if (!item.parentReference || !item.parentReference.path) {
171
- return false;
172
- }
173
- // Extract relative path from parentReference.path
174
- // Format: /drives/{drive-id}/root:/path/to/folder
175
- const parts = item.parentReference.path.split('root:');
176
- if (parts.length < 2) {
177
- // Item is in root
178
- // If watchPath is /, then it's a match. Otherwise no.
179
- return watchPath === '/' || watchPath === '';
180
- }
181
- let relativePath = parts[1]; // /path/to/folder
182
- // Ensure paths start with / for consistent comparison
183
- if (!relativePath.startsWith('/')) {
184
- relativePath = '/' + relativePath;
185
- }
186
- // Ensure watchPath starts with /
187
- let normalizedWatchPath = watchPath;
188
- if (!normalizedWatchPath.startsWith('/')) {
189
- normalizedWatchPath = '/' + normalizedWatchPath;
190
- }
191
- // Check if relative path matches or is a subfolder
192
- // 1. Exact match: relativePath === normalizedWatchPath
193
- // 2. Subfolder: relativePath startsWith normalizedWatchPath + '/'
194
- if (relativePath === normalizedWatchPath) {
195
- return true;
196
- }
197
- if (relativePath.startsWith(normalizedWatchPath + '/')) {
198
- return true;
199
- }
200
- return false;
201
- }
202
178
  /**
203
179
  * Classify event as created vs updated
204
180
  *
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "n8n-nodes-onedrive-business",
3
- "version": "1.1.0",
3
+ "version": "1.1.4",
4
4
  "description": "n8n custom node for OneDrive Business with robust deduplication and webhook triggers",
5
5
  "keywords": [
6
6
  "n8n",
@@ -23,7 +23,7 @@
23
23
  "main": "index.js",
24
24
  "scripts": {
25
25
  "build": "tsc && npm run copy-icons",
26
- "copy-icons": "copyfiles -u 1 \"nodes/**/*.{png,svg}\" dist/",
26
+ "copy-icons": "copyfiles \"nodes/**/*.{png,svg}\" dist/",
27
27
  "dev": "tsc --watch",
28
28
  "format": "prettier nodes --write",
29
29
  "lint": "tslint -p tsconfig.json -c tslint.json",