n8n-nodes-idb2b 2.0.1 → 2.0.3
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.
- package/dist/nodes/IDB2B/IDB2B.node.js +150 -1088
- package/dist/nodes/IDB2B/descriptions/companyProperties.d.ts +3 -0
- package/dist/nodes/IDB2B/descriptions/companyProperties.js +278 -0
- package/dist/nodes/IDB2B/descriptions/contactProperties.js +58 -1
- package/package.json +2 -2
- package/dist/nodes/IDB2B/IDB2B.refactored.node.d.ts +0 -5
- package/dist/nodes/IDB2B/IDB2B.refactored.node.js +0 -247
- package/dist/nodes/IDB2B/descriptions/customProperties.d.ts +0 -2
- package/dist/nodes/IDB2B/descriptions/customProperties.js +0 -57
|
@@ -3,10 +3,14 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.IDB2B = void 0;
|
|
4
4
|
const n8n_workflow_1 = require("n8n-workflow");
|
|
5
5
|
const node_crypto_1 = require("node:crypto");
|
|
6
|
+
const contactProperties_1 = require("./descriptions/contactProperties");
|
|
7
|
+
const companyProperties_1 = require("./descriptions/companyProperties");
|
|
6
8
|
class SecureTokenCache {
|
|
7
9
|
constructor() {
|
|
8
10
|
this.cache = new Map();
|
|
9
11
|
this.algorithm = 'aes-256-cbc';
|
|
12
|
+
this.lastCleanup = new Date();
|
|
13
|
+
this.cleanupIntervalMs = 10 * 60 * 1000; // 10 minutes
|
|
10
14
|
}
|
|
11
15
|
getEncryptionKey(credentials) {
|
|
12
16
|
const keyMaterial = `${credentials.email}:${credentials.password}:${credentials.baseUrl}`;
|
|
@@ -81,9 +85,13 @@ class SecureTokenCache {
|
|
|
81
85
|
invalidateAll() {
|
|
82
86
|
this.cache.clear();
|
|
83
87
|
}
|
|
84
|
-
// Clean up expired tokens
|
|
85
|
-
|
|
88
|
+
// Clean up expired tokens if enough time has passed since last cleanup
|
|
89
|
+
cleanupIfNeeded() {
|
|
86
90
|
const now = new Date();
|
|
91
|
+
if (now.getTime() - this.lastCleanup.getTime() < this.cleanupIntervalMs) {
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
this.lastCleanup = now;
|
|
87
95
|
for (const [key, value] of this.cache.entries()) {
|
|
88
96
|
if (value.expires_at <= now) {
|
|
89
97
|
this.cache.delete(key);
|
|
@@ -98,20 +106,6 @@ class SecureTokenCache {
|
|
|
98
106
|
}
|
|
99
107
|
}
|
|
100
108
|
}
|
|
101
|
-
// Get cache statistics for monitoring
|
|
102
|
-
getStats() {
|
|
103
|
-
const now = new Date();
|
|
104
|
-
let expiredCount = 0;
|
|
105
|
-
for (const value of this.cache.values()) {
|
|
106
|
-
if (value.expires_at <= now) {
|
|
107
|
-
expiredCount++;
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
return {
|
|
111
|
-
totalEntries: this.cache.size,
|
|
112
|
-
expiredEntries: expiredCount
|
|
113
|
-
};
|
|
114
|
-
}
|
|
115
109
|
}
|
|
116
110
|
const secureTokenCache = new SecureTokenCache();
|
|
117
111
|
async function makeRequestWithRetry(executeFunctions, options, maxRetries = 3, initialDelay = 1000) {
|
|
@@ -135,18 +129,19 @@ async function makeRequestWithRetry(executeFunctions, options, maxRetries = 3, i
|
|
|
135
129
|
if (((_b = error.response) === null || _b === void 0 ? void 0 : _b.status) === 429) {
|
|
136
130
|
const retryAfter = (_c = error.response.headers) === null || _c === void 0 ? void 0 : _c['retry-after'];
|
|
137
131
|
const delay = retryAfter ? parseInt(retryAfter) * 1000 : initialDelay * Math.pow(2, attempt);
|
|
138
|
-
await new Promise(resolve => setTimeout(resolve, Math.min(delay, 30000)));
|
|
132
|
+
await new Promise(resolve => setTimeout(resolve, Math.min(delay, 30000)));
|
|
139
133
|
}
|
|
140
134
|
else {
|
|
141
135
|
// Exponential backoff for other errors
|
|
142
136
|
const delay = initialDelay * Math.pow(2, attempt);
|
|
143
|
-
await new Promise(resolve => setTimeout(resolve, Math.min(delay, 10000)));
|
|
137
|
+
await new Promise(resolve => setTimeout(resolve, Math.min(delay, 10000)));
|
|
144
138
|
}
|
|
145
139
|
}
|
|
146
140
|
}
|
|
147
141
|
throw lastError;
|
|
148
142
|
}
|
|
149
143
|
async function getAccessToken(executeFunctions, credentials) {
|
|
144
|
+
var _a, _b, _c, _d;
|
|
150
145
|
const cacheKey = `${credentials.baseUrl}:${credentials.email}`;
|
|
151
146
|
// Try to get cached token
|
|
152
147
|
const cachedToken = secureTokenCache.get(cacheKey, credentials);
|
|
@@ -163,7 +158,10 @@ async function getAccessToken(executeFunctions, credentials) {
|
|
|
163
158
|
},
|
|
164
159
|
json: true,
|
|
165
160
|
});
|
|
166
|
-
const accessToken = loginResponse.data.session.access_token
|
|
161
|
+
const accessToken = ((_b = (_a = loginResponse === null || loginResponse === void 0 ? void 0 : loginResponse.data) === null || _a === void 0 ? void 0 : _a.session) === null || _b === void 0 ? void 0 : _b.access_token) ||
|
|
162
|
+
((_c = loginResponse === null || loginResponse === void 0 ? void 0 : loginResponse.data) === null || _c === void 0 ? void 0 : _c.access_token) ||
|
|
163
|
+
((_d = loginResponse === null || loginResponse === void 0 ? void 0 : loginResponse.session) === null || _d === void 0 ? void 0 : _d.access_token) ||
|
|
164
|
+
(loginResponse === null || loginResponse === void 0 ? void 0 : loginResponse.access_token);
|
|
167
165
|
if (!accessToken) {
|
|
168
166
|
throw new Error('No access token received from authentication response');
|
|
169
167
|
}
|
|
@@ -197,6 +195,17 @@ function validateEmail(email) {
|
|
|
197
195
|
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
198
196
|
return emailRegex.test(email);
|
|
199
197
|
}
|
|
198
|
+
function validateEmailField(email) {
|
|
199
|
+
if (typeof email !== 'string' || email.trim().length === 0) {
|
|
200
|
+
throw new Error('Contact email must be a non-empty string');
|
|
201
|
+
}
|
|
202
|
+
if (email.length > 320) {
|
|
203
|
+
throw new Error('Email address cannot exceed 320 characters');
|
|
204
|
+
}
|
|
205
|
+
if (!validateEmail(email)) {
|
|
206
|
+
throw new Error('Invalid email format - please provide a valid email address');
|
|
207
|
+
}
|
|
208
|
+
}
|
|
200
209
|
function validateContactData(name, email) {
|
|
201
210
|
if (!name || typeof name !== 'string' || name.trim().length === 0) {
|
|
202
211
|
throw new Error('Contact name is required and must be a non-empty string');
|
|
@@ -204,14 +213,8 @@ function validateContactData(name, email) {
|
|
|
204
213
|
if (name.trim().length > 255) {
|
|
205
214
|
throw new Error('Contact name cannot exceed 255 characters');
|
|
206
215
|
}
|
|
207
|
-
if (
|
|
208
|
-
|
|
209
|
-
}
|
|
210
|
-
if (email.length > 320) {
|
|
211
|
-
throw new Error('Email address cannot exceed 320 characters');
|
|
212
|
-
}
|
|
213
|
-
if (!validateEmail(email)) {
|
|
214
|
-
throw new Error('Invalid email format - please provide a valid email address');
|
|
216
|
+
if (email) {
|
|
217
|
+
validateEmailField(email);
|
|
215
218
|
}
|
|
216
219
|
}
|
|
217
220
|
function validateLeadData(name) {
|
|
@@ -222,21 +225,6 @@ function validateLeadData(name) {
|
|
|
222
225
|
throw new Error('Lead name cannot exceed 255 characters');
|
|
223
226
|
}
|
|
224
227
|
}
|
|
225
|
-
function safeJsonParse(jsonString, fieldName) {
|
|
226
|
-
if (!jsonString || typeof jsonString !== 'string') {
|
|
227
|
-
throw new Error(`${fieldName} must be a valid JSON string`);
|
|
228
|
-
}
|
|
229
|
-
try {
|
|
230
|
-
const parsed = JSON.parse(jsonString);
|
|
231
|
-
if (parsed === null) {
|
|
232
|
-
throw new Error(`${fieldName} cannot be null`);
|
|
233
|
-
}
|
|
234
|
-
return parsed;
|
|
235
|
-
}
|
|
236
|
-
catch (error) {
|
|
237
|
-
throw new Error(`Invalid JSON in ${fieldName}: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
228
|
function validateBaseUrl(url) {
|
|
241
229
|
try {
|
|
242
230
|
const parsedUrl = new URL(url);
|
|
@@ -246,6 +234,9 @@ function validateBaseUrl(url) {
|
|
|
246
234
|
return false;
|
|
247
235
|
}
|
|
248
236
|
}
|
|
237
|
+
function sanitizeId(id) {
|
|
238
|
+
return encodeURIComponent(id);
|
|
239
|
+
}
|
|
249
240
|
function sanitizeErrorData(data) {
|
|
250
241
|
if (!data || typeof data !== 'object') {
|
|
251
242
|
return {};
|
|
@@ -254,7 +245,6 @@ function sanitizeErrorData(data) {
|
|
|
254
245
|
const allowedFields = ['message', 'error', 'code', 'type', 'validation_errors'];
|
|
255
246
|
for (const field of allowedFields) {
|
|
256
247
|
if (data[field] !== undefined) {
|
|
257
|
-
// Ensure no sensitive data is included
|
|
258
248
|
if (typeof data[field] === 'string' && data[field].length < 500) {
|
|
259
249
|
sanitized[field] = data[field];
|
|
260
250
|
}
|
|
@@ -305,867 +295,43 @@ class IDB2B {
|
|
|
305
295
|
value: 'contact',
|
|
306
296
|
},
|
|
307
297
|
{
|
|
308
|
-
name: '
|
|
309
|
-
value: '
|
|
298
|
+
name: 'Company',
|
|
299
|
+
value: 'company',
|
|
310
300
|
},
|
|
311
301
|
],
|
|
312
302
|
default: 'contact',
|
|
313
303
|
},
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
noDataExpression: true,
|
|
319
|
-
displayOptions: {
|
|
320
|
-
show: {
|
|
321
|
-
resource: ['contact'],
|
|
322
|
-
},
|
|
323
|
-
},
|
|
324
|
-
options: [
|
|
325
|
-
{
|
|
326
|
-
name: 'Get All',
|
|
327
|
-
value: 'getAll',
|
|
328
|
-
action: 'Get all contacts',
|
|
329
|
-
description: 'Retrieve all contacts',
|
|
330
|
-
},
|
|
331
|
-
{
|
|
332
|
-
name: 'Create',
|
|
333
|
-
value: 'create',
|
|
334
|
-
action: 'Create a contact',
|
|
335
|
-
description: 'Create a new contact',
|
|
336
|
-
},
|
|
337
|
-
],
|
|
338
|
-
default: 'getAll',
|
|
339
|
-
},
|
|
340
|
-
{
|
|
341
|
-
displayName: 'Operation',
|
|
342
|
-
name: 'operation',
|
|
343
|
-
type: 'options',
|
|
344
|
-
noDataExpression: true,
|
|
345
|
-
displayOptions: {
|
|
346
|
-
show: {
|
|
347
|
-
resource: ['lead'],
|
|
348
|
-
},
|
|
349
|
-
},
|
|
350
|
-
options: [
|
|
351
|
-
{
|
|
352
|
-
name: 'Get All',
|
|
353
|
-
value: 'getAll',
|
|
354
|
-
action: 'Get all leads',
|
|
355
|
-
description: 'Retrieve all leads',
|
|
356
|
-
},
|
|
357
|
-
{
|
|
358
|
-
name: 'Create',
|
|
359
|
-
value: 'create',
|
|
360
|
-
action: 'Create a lead',
|
|
361
|
-
description: 'Create a new lead',
|
|
362
|
-
},
|
|
363
|
-
],
|
|
364
|
-
default: 'getAll',
|
|
365
|
-
},
|
|
366
|
-
{
|
|
367
|
-
displayName: 'Name',
|
|
368
|
-
name: 'name',
|
|
369
|
-
type: 'string',
|
|
370
|
-
default: '',
|
|
371
|
-
required: true,
|
|
372
|
-
displayOptions: {
|
|
373
|
-
show: {
|
|
374
|
-
resource: ['contact'],
|
|
375
|
-
operation: ['create'],
|
|
376
|
-
},
|
|
377
|
-
},
|
|
378
|
-
description: 'Contact name',
|
|
379
|
-
},
|
|
380
|
-
{
|
|
381
|
-
displayName: 'Email',
|
|
382
|
-
name: 'email',
|
|
383
|
-
type: 'string',
|
|
384
|
-
default: '',
|
|
385
|
-
required: true,
|
|
386
|
-
displayOptions: {
|
|
387
|
-
show: {
|
|
388
|
-
resource: ['contact'],
|
|
389
|
-
operation: ['create'],
|
|
390
|
-
},
|
|
391
|
-
},
|
|
392
|
-
description: 'Contact email',
|
|
393
|
-
},
|
|
394
|
-
{
|
|
395
|
-
displayName: 'Phone Number',
|
|
396
|
-
name: 'phone_number',
|
|
397
|
-
type: 'string',
|
|
398
|
-
default: '',
|
|
399
|
-
displayOptions: {
|
|
400
|
-
show: {
|
|
401
|
-
resource: ['contact'],
|
|
402
|
-
operation: ['create'],
|
|
403
|
-
},
|
|
404
|
-
},
|
|
405
|
-
description: 'Contact phone number',
|
|
406
|
-
},
|
|
407
|
-
{
|
|
408
|
-
displayName: 'Additional Fields',
|
|
409
|
-
name: 'additionalFields',
|
|
410
|
-
type: 'collection',
|
|
411
|
-
placeholder: 'Add Field',
|
|
412
|
-
default: {},
|
|
413
|
-
displayOptions: {
|
|
414
|
-
show: {
|
|
415
|
-
resource: ['contact'],
|
|
416
|
-
operation: ['create'],
|
|
417
|
-
},
|
|
418
|
-
},
|
|
419
|
-
options: [
|
|
420
|
-
{
|
|
421
|
-
displayName: 'User ID',
|
|
422
|
-
name: 'user_id',
|
|
423
|
-
type: 'string',
|
|
424
|
-
default: '',
|
|
425
|
-
description: 'ID of the user to associate with this contact',
|
|
426
|
-
},
|
|
427
|
-
{
|
|
428
|
-
displayName: 'Lead ID',
|
|
429
|
-
name: 'lead_id',
|
|
430
|
-
type: 'string',
|
|
431
|
-
default: '',
|
|
432
|
-
description: 'ID of the lead to associate with this contact',
|
|
433
|
-
},
|
|
434
|
-
{
|
|
435
|
-
displayName: 'Favorites',
|
|
436
|
-
name: 'favorites',
|
|
437
|
-
type: 'boolean',
|
|
438
|
-
default: false,
|
|
439
|
-
description: 'Mark contact as favorite',
|
|
440
|
-
},
|
|
441
|
-
{
|
|
442
|
-
displayName: 'Tags',
|
|
443
|
-
name: 'tags',
|
|
444
|
-
type: 'fixedCollection',
|
|
445
|
-
typeOptions: {
|
|
446
|
-
multipleValues: true,
|
|
447
|
-
},
|
|
448
|
-
default: {},
|
|
449
|
-
placeholder: 'Add Tag',
|
|
450
|
-
description: 'Tags to associate with the contact',
|
|
451
|
-
options: [
|
|
452
|
-
{
|
|
453
|
-
name: 'tag',
|
|
454
|
-
displayName: 'Tag',
|
|
455
|
-
values: [
|
|
456
|
-
{
|
|
457
|
-
displayName: 'Tag Name',
|
|
458
|
-
name: 'name',
|
|
459
|
-
type: 'string',
|
|
460
|
-
default: '',
|
|
461
|
-
required: true,
|
|
462
|
-
},
|
|
463
|
-
],
|
|
464
|
-
},
|
|
465
|
-
],
|
|
466
|
-
},
|
|
467
|
-
],
|
|
468
|
-
},
|
|
469
|
-
{
|
|
470
|
-
displayName: 'Name',
|
|
471
|
-
name: 'name',
|
|
472
|
-
type: 'string',
|
|
473
|
-
default: '',
|
|
474
|
-
required: true,
|
|
475
|
-
displayOptions: {
|
|
476
|
-
show: {
|
|
477
|
-
resource: ['lead'],
|
|
478
|
-
operation: ['create'],
|
|
479
|
-
},
|
|
480
|
-
},
|
|
481
|
-
description: 'Lead name',
|
|
482
|
-
},
|
|
483
|
-
{
|
|
484
|
-
displayName: 'Additional Fields',
|
|
485
|
-
name: 'additionalFields',
|
|
486
|
-
type: 'collection',
|
|
487
|
-
placeholder: 'Add Field',
|
|
488
|
-
default: {},
|
|
489
|
-
displayOptions: {
|
|
490
|
-
show: {
|
|
491
|
-
resource: ['lead'],
|
|
492
|
-
operation: ['create'],
|
|
493
|
-
},
|
|
494
|
-
},
|
|
495
|
-
options: [
|
|
496
|
-
{
|
|
497
|
-
displayName: 'Owner ID',
|
|
498
|
-
name: 'owner_id',
|
|
499
|
-
type: 'string',
|
|
500
|
-
default: '',
|
|
501
|
-
description: 'ID of the user who owns this lead',
|
|
502
|
-
},
|
|
503
|
-
{
|
|
504
|
-
displayName: 'Website',
|
|
505
|
-
name: 'website',
|
|
506
|
-
type: 'string',
|
|
507
|
-
default: '',
|
|
508
|
-
description: 'Lead website URL',
|
|
509
|
-
},
|
|
510
|
-
{
|
|
511
|
-
displayName: 'Description',
|
|
512
|
-
name: 'description',
|
|
513
|
-
type: 'string',
|
|
514
|
-
default: '',
|
|
515
|
-
description: 'Lead description',
|
|
516
|
-
},
|
|
517
|
-
{
|
|
518
|
-
displayName: 'Status ID',
|
|
519
|
-
name: 'status_id',
|
|
520
|
-
type: 'string',
|
|
521
|
-
default: '',
|
|
522
|
-
description: 'Lead status ID',
|
|
523
|
-
},
|
|
524
|
-
{
|
|
525
|
-
displayName: 'Source ID',
|
|
526
|
-
name: 'source_id',
|
|
527
|
-
type: 'string',
|
|
528
|
-
default: '',
|
|
529
|
-
description: 'Lead source ID',
|
|
530
|
-
},
|
|
531
|
-
{
|
|
532
|
-
displayName: 'Size ID',
|
|
533
|
-
name: 'size_id',
|
|
534
|
-
type: 'string',
|
|
535
|
-
default: '',
|
|
536
|
-
description: 'Lead size ID',
|
|
537
|
-
},
|
|
538
|
-
{
|
|
539
|
-
displayName: 'Industry ID',
|
|
540
|
-
name: 'industry_id',
|
|
541
|
-
type: 'string',
|
|
542
|
-
default: '',
|
|
543
|
-
description: 'Lead industry ID',
|
|
544
|
-
},
|
|
545
|
-
{
|
|
546
|
-
displayName: 'Main Contact ID',
|
|
547
|
-
name: 'main_contact_id',
|
|
548
|
-
type: 'string',
|
|
549
|
-
default: '',
|
|
550
|
-
description: 'ID of the main contact for this lead',
|
|
551
|
-
},
|
|
552
|
-
{
|
|
553
|
-
displayName: 'Contact Name',
|
|
554
|
-
name: 'contact_name',
|
|
555
|
-
type: 'string',
|
|
556
|
-
default: '',
|
|
557
|
-
description: 'Name of the main contact',
|
|
558
|
-
},
|
|
559
|
-
{
|
|
560
|
-
displayName: 'Contact Email',
|
|
561
|
-
name: 'contact_email',
|
|
562
|
-
type: 'string',
|
|
563
|
-
default: '',
|
|
564
|
-
description: 'Email of the main contact',
|
|
565
|
-
},
|
|
566
|
-
{
|
|
567
|
-
displayName: 'Contact Phone Number',
|
|
568
|
-
name: 'contact_phone_number',
|
|
569
|
-
type: 'string',
|
|
570
|
-
default: '',
|
|
571
|
-
description: 'Phone number of the main contact',
|
|
572
|
-
},
|
|
573
|
-
],
|
|
574
|
-
},
|
|
575
|
-
{
|
|
576
|
-
displayName: 'Limit',
|
|
577
|
-
name: 'limit',
|
|
578
|
-
type: 'number',
|
|
579
|
-
default: 50,
|
|
580
|
-
description: 'Maximum number of leads to return',
|
|
581
|
-
displayOptions: {
|
|
582
|
-
show: {
|
|
583
|
-
resource: ['lead'],
|
|
584
|
-
operation: ['getAll'],
|
|
585
|
-
},
|
|
586
|
-
},
|
|
587
|
-
},
|
|
588
|
-
{
|
|
589
|
-
displayName: 'Page',
|
|
590
|
-
name: 'page',
|
|
591
|
-
type: 'number',
|
|
592
|
-
default: 1,
|
|
593
|
-
description: 'Page number to retrieve',
|
|
594
|
-
displayOptions: {
|
|
595
|
-
show: {
|
|
596
|
-
resource: ['lead'],
|
|
597
|
-
operation: ['getAll'],
|
|
598
|
-
},
|
|
599
|
-
},
|
|
600
|
-
},
|
|
601
|
-
{
|
|
602
|
-
displayName: 'Fields to Return',
|
|
603
|
-
name: 'fields',
|
|
604
|
-
type: 'multiOptions',
|
|
605
|
-
default: [],
|
|
606
|
-
description: 'Select specific fields to return (leave empty for all fields)',
|
|
607
|
-
displayOptions: {
|
|
608
|
-
show: {
|
|
609
|
-
resource: ['lead'],
|
|
610
|
-
operation: ['getAll'],
|
|
611
|
-
},
|
|
612
|
-
},
|
|
613
|
-
options: [
|
|
614
|
-
{
|
|
615
|
-
name: 'ID',
|
|
616
|
-
value: 'id',
|
|
617
|
-
},
|
|
618
|
-
{
|
|
619
|
-
name: 'Name',
|
|
620
|
-
value: 'name',
|
|
621
|
-
},
|
|
622
|
-
{
|
|
623
|
-
name: 'Website',
|
|
624
|
-
value: 'website',
|
|
625
|
-
},
|
|
626
|
-
{
|
|
627
|
-
name: 'Description',
|
|
628
|
-
value: 'description',
|
|
629
|
-
},
|
|
630
|
-
{
|
|
631
|
-
name: 'Owner ID',
|
|
632
|
-
value: 'owner_id',
|
|
633
|
-
},
|
|
634
|
-
{
|
|
635
|
-
name: 'Status ID',
|
|
636
|
-
value: 'status_id',
|
|
637
|
-
},
|
|
638
|
-
{
|
|
639
|
-
name: 'Source ID',
|
|
640
|
-
value: 'source_id',
|
|
641
|
-
},
|
|
642
|
-
{
|
|
643
|
-
name: 'Size ID',
|
|
644
|
-
value: 'size_id',
|
|
645
|
-
},
|
|
646
|
-
{
|
|
647
|
-
name: 'Industry ID',
|
|
648
|
-
value: 'industry_id',
|
|
649
|
-
},
|
|
650
|
-
{
|
|
651
|
-
name: 'Main Contact ID',
|
|
652
|
-
value: 'main_contact_id',
|
|
653
|
-
},
|
|
654
|
-
{
|
|
655
|
-
name: 'Contact Name',
|
|
656
|
-
value: 'contact_name',
|
|
657
|
-
},
|
|
658
|
-
{
|
|
659
|
-
name: 'Contact Email',
|
|
660
|
-
value: 'contact_email',
|
|
661
|
-
},
|
|
662
|
-
{
|
|
663
|
-
name: 'Contact Phone Number',
|
|
664
|
-
value: 'contact_phone_number',
|
|
665
|
-
},
|
|
666
|
-
{
|
|
667
|
-
name: 'Created At',
|
|
668
|
-
value: 'created_at',
|
|
669
|
-
},
|
|
670
|
-
{
|
|
671
|
-
name: 'Updated At',
|
|
672
|
-
value: 'updated_at',
|
|
673
|
-
},
|
|
674
|
-
],
|
|
675
|
-
},
|
|
676
|
-
{
|
|
677
|
-
displayName: 'Query Parameters',
|
|
678
|
-
name: 'queryParameters',
|
|
679
|
-
type: 'fixedCollection',
|
|
680
|
-
typeOptions: {
|
|
681
|
-
multipleValues: true,
|
|
682
|
-
},
|
|
683
|
-
default: {},
|
|
684
|
-
placeholder: 'Add Parameter',
|
|
685
|
-
description: 'Additional query parameters',
|
|
686
|
-
displayOptions: {
|
|
687
|
-
show: {
|
|
688
|
-
resource: ['lead'],
|
|
689
|
-
operation: ['getAll'],
|
|
690
|
-
},
|
|
691
|
-
},
|
|
692
|
-
options: [
|
|
693
|
-
{
|
|
694
|
-
name: 'parameter',
|
|
695
|
-
displayName: 'Parameter',
|
|
696
|
-
values: [
|
|
697
|
-
{
|
|
698
|
-
displayName: 'Name',
|
|
699
|
-
name: 'name',
|
|
700
|
-
type: 'string',
|
|
701
|
-
default: '',
|
|
702
|
-
},
|
|
703
|
-
{
|
|
704
|
-
displayName: 'Value',
|
|
705
|
-
name: 'value',
|
|
706
|
-
type: 'string',
|
|
707
|
-
default: '',
|
|
708
|
-
},
|
|
709
|
-
],
|
|
710
|
-
},
|
|
711
|
-
],
|
|
712
|
-
},
|
|
713
|
-
{
|
|
714
|
-
displayName: 'Lead ID',
|
|
715
|
-
name: 'lead_id',
|
|
716
|
-
type: 'string',
|
|
717
|
-
default: '',
|
|
718
|
-
required: true,
|
|
719
|
-
displayOptions: {
|
|
720
|
-
show: {
|
|
721
|
-
resource: ['leadActivities'],
|
|
722
|
-
operation: ['create', 'getAll'],
|
|
723
|
-
},
|
|
724
|
-
},
|
|
725
|
-
description: 'ID of the lead to get activities for or add activity to',
|
|
726
|
-
},
|
|
727
|
-
{
|
|
728
|
-
displayName: 'Subject',
|
|
729
|
-
name: 'subject',
|
|
730
|
-
type: 'string',
|
|
731
|
-
default: '',
|
|
732
|
-
required: true,
|
|
733
|
-
displayOptions: {
|
|
734
|
-
show: {
|
|
735
|
-
resource: ['leadActivities'],
|
|
736
|
-
operation: ['create'],
|
|
737
|
-
},
|
|
738
|
-
},
|
|
739
|
-
description: 'Subject of the activity',
|
|
740
|
-
},
|
|
741
|
-
{
|
|
742
|
-
displayName: 'Additional Fields',
|
|
743
|
-
name: 'additionalFields',
|
|
744
|
-
type: 'collection',
|
|
745
|
-
placeholder: 'Add Field',
|
|
746
|
-
default: {},
|
|
747
|
-
displayOptions: {
|
|
748
|
-
show: {
|
|
749
|
-
resource: ['leadActivities'],
|
|
750
|
-
operation: ['create'],
|
|
751
|
-
},
|
|
752
|
-
},
|
|
753
|
-
options: [
|
|
754
|
-
{
|
|
755
|
-
displayName: 'Icon',
|
|
756
|
-
name: 'icon',
|
|
757
|
-
type: 'string',
|
|
758
|
-
default: '',
|
|
759
|
-
description: 'Icon for the activity',
|
|
760
|
-
},
|
|
761
|
-
{
|
|
762
|
-
displayName: 'Description',
|
|
763
|
-
name: 'description',
|
|
764
|
-
type: 'string',
|
|
765
|
-
default: '',
|
|
766
|
-
description: 'Description of the activity',
|
|
767
|
-
},
|
|
768
|
-
{
|
|
769
|
-
displayName: 'Date and Time',
|
|
770
|
-
name: 'datetime',
|
|
771
|
-
type: 'string',
|
|
772
|
-
default: '',
|
|
773
|
-
placeholder: '2025-10-13T10:00:00Z',
|
|
774
|
-
description: 'Date and time of the activity (ISO format)',
|
|
775
|
-
},
|
|
776
|
-
{
|
|
777
|
-
displayName: 'User ID',
|
|
778
|
-
name: 'user_id',
|
|
779
|
-
type: 'string',
|
|
780
|
-
default: '',
|
|
781
|
-
description: 'User ID associated with the activity',
|
|
782
|
-
},
|
|
783
|
-
{
|
|
784
|
-
displayName: 'Attachments',
|
|
785
|
-
name: 'attachments',
|
|
786
|
-
type: 'fixedCollection',
|
|
787
|
-
typeOptions: {
|
|
788
|
-
multipleValues: true,
|
|
789
|
-
},
|
|
790
|
-
default: {},
|
|
791
|
-
placeholder: 'Add Attachment',
|
|
792
|
-
description: 'Attachment files for the activity',
|
|
793
|
-
options: [
|
|
794
|
-
{
|
|
795
|
-
name: 'attachment',
|
|
796
|
-
displayName: 'Attachment',
|
|
797
|
-
values: [
|
|
798
|
-
{
|
|
799
|
-
displayName: 'File Path/URL',
|
|
800
|
-
name: 'file',
|
|
801
|
-
type: 'string',
|
|
802
|
-
default: '',
|
|
803
|
-
required: true,
|
|
804
|
-
},
|
|
805
|
-
],
|
|
806
|
-
},
|
|
807
|
-
],
|
|
808
|
-
},
|
|
809
|
-
{
|
|
810
|
-
displayName: 'Attachments to Delete',
|
|
811
|
-
name: 'attachments_to_delete',
|
|
812
|
-
type: 'fixedCollection',
|
|
813
|
-
typeOptions: {
|
|
814
|
-
multipleValues: true,
|
|
815
|
-
},
|
|
816
|
-
default: {},
|
|
817
|
-
placeholder: 'Add Attachment ID',
|
|
818
|
-
description: 'Array of attachment IDs to delete (for updates)',
|
|
819
|
-
options: [
|
|
820
|
-
{
|
|
821
|
-
name: 'attachment_id',
|
|
822
|
-
displayName: 'Attachment ID',
|
|
823
|
-
values: [
|
|
824
|
-
{
|
|
825
|
-
displayName: 'ID',
|
|
826
|
-
name: 'id',
|
|
827
|
-
type: 'string',
|
|
828
|
-
default: '',
|
|
829
|
-
required: true,
|
|
830
|
-
},
|
|
831
|
-
],
|
|
832
|
-
},
|
|
833
|
-
],
|
|
834
|
-
},
|
|
835
|
-
],
|
|
836
|
-
},
|
|
837
|
-
{
|
|
838
|
-
displayName: 'Limit',
|
|
839
|
-
name: 'limit',
|
|
840
|
-
type: 'number',
|
|
841
|
-
default: 50,
|
|
842
|
-
description: 'Maximum number of activities to return',
|
|
843
|
-
displayOptions: {
|
|
844
|
-
show: {
|
|
845
|
-
resource: ['leadActivities'],
|
|
846
|
-
operation: ['getAll'],
|
|
847
|
-
},
|
|
848
|
-
},
|
|
849
|
-
},
|
|
850
|
-
{
|
|
851
|
-
displayName: 'Page',
|
|
852
|
-
name: 'page',
|
|
853
|
-
type: 'number',
|
|
854
|
-
default: 1,
|
|
855
|
-
description: 'Page number to retrieve',
|
|
856
|
-
displayOptions: {
|
|
857
|
-
show: {
|
|
858
|
-
resource: ['leadActivities'],
|
|
859
|
-
operation: ['getAll'],
|
|
860
|
-
},
|
|
861
|
-
},
|
|
862
|
-
},
|
|
863
|
-
{
|
|
864
|
-
displayName: 'Fields to Return',
|
|
865
|
-
name: 'fields',
|
|
866
|
-
type: 'multiOptions',
|
|
867
|
-
default: [],
|
|
868
|
-
description: 'Select specific fields to return (leave empty for all fields)',
|
|
869
|
-
displayOptions: {
|
|
870
|
-
show: {
|
|
871
|
-
resource: ['leadActivities'],
|
|
872
|
-
operation: ['getAll'],
|
|
873
|
-
},
|
|
874
|
-
},
|
|
875
|
-
options: [
|
|
876
|
-
{
|
|
877
|
-
name: 'ID',
|
|
878
|
-
value: 'id',
|
|
879
|
-
},
|
|
880
|
-
{
|
|
881
|
-
name: 'Lead ID',
|
|
882
|
-
value: 'lead_id',
|
|
883
|
-
},
|
|
884
|
-
{
|
|
885
|
-
name: 'Icon',
|
|
886
|
-
value: 'icon',
|
|
887
|
-
},
|
|
888
|
-
{
|
|
889
|
-
name: 'Subject',
|
|
890
|
-
value: 'subject',
|
|
891
|
-
},
|
|
892
|
-
{
|
|
893
|
-
name: 'Description',
|
|
894
|
-
value: 'description',
|
|
895
|
-
},
|
|
896
|
-
{
|
|
897
|
-
name: 'Date and Time',
|
|
898
|
-
value: 'datetime',
|
|
899
|
-
},
|
|
900
|
-
{
|
|
901
|
-
name: 'User ID',
|
|
902
|
-
value: 'user_id',
|
|
903
|
-
},
|
|
904
|
-
{
|
|
905
|
-
name: 'Attachments',
|
|
906
|
-
value: 'attachments',
|
|
907
|
-
},
|
|
908
|
-
{
|
|
909
|
-
name: 'Created At',
|
|
910
|
-
value: 'created_at',
|
|
911
|
-
},
|
|
912
|
-
{
|
|
913
|
-
name: 'Updated At',
|
|
914
|
-
value: 'updated_at',
|
|
915
|
-
},
|
|
916
|
-
],
|
|
917
|
-
},
|
|
918
|
-
{
|
|
919
|
-
displayName: 'Query Parameters',
|
|
920
|
-
name: 'queryParameters',
|
|
921
|
-
type: 'fixedCollection',
|
|
922
|
-
typeOptions: {
|
|
923
|
-
multipleValues: true,
|
|
924
|
-
},
|
|
925
|
-
default: {},
|
|
926
|
-
placeholder: 'Add Parameter',
|
|
927
|
-
description: 'Additional query parameters',
|
|
928
|
-
displayOptions: {
|
|
929
|
-
show: {
|
|
930
|
-
resource: ['leadActivities'],
|
|
931
|
-
operation: ['getAll'],
|
|
932
|
-
},
|
|
933
|
-
},
|
|
934
|
-
options: [
|
|
935
|
-
{
|
|
936
|
-
name: 'parameter',
|
|
937
|
-
displayName: 'Parameter',
|
|
938
|
-
values: [
|
|
939
|
-
{
|
|
940
|
-
displayName: 'Name',
|
|
941
|
-
name: 'name',
|
|
942
|
-
type: 'string',
|
|
943
|
-
default: '',
|
|
944
|
-
},
|
|
945
|
-
{
|
|
946
|
-
displayName: 'Value',
|
|
947
|
-
name: 'value',
|
|
948
|
-
type: 'string',
|
|
949
|
-
default: '',
|
|
950
|
-
},
|
|
951
|
-
],
|
|
952
|
-
},
|
|
953
|
-
],
|
|
954
|
-
},
|
|
955
|
-
{
|
|
956
|
-
displayName: 'Endpoint',
|
|
957
|
-
name: 'endpoint',
|
|
958
|
-
type: 'string',
|
|
959
|
-
default: '/api/v1/',
|
|
960
|
-
placeholder: '/api/v1/resource',
|
|
961
|
-
description: 'The endpoint to call',
|
|
962
|
-
displayOptions: {
|
|
963
|
-
show: {
|
|
964
|
-
resource: ['custom'],
|
|
965
|
-
},
|
|
966
|
-
},
|
|
967
|
-
routing: {
|
|
968
|
-
request: {
|
|
969
|
-
url: '={{$value}}',
|
|
970
|
-
},
|
|
971
|
-
},
|
|
972
|
-
},
|
|
973
|
-
{
|
|
974
|
-
displayName: 'JSON Body',
|
|
975
|
-
name: 'jsonBody',
|
|
976
|
-
type: 'json',
|
|
977
|
-
default: '{}',
|
|
978
|
-
description: 'JSON body for POST/PUT requests',
|
|
979
|
-
displayOptions: {
|
|
980
|
-
show: {
|
|
981
|
-
operation: ['post', 'put'],
|
|
982
|
-
resource: ['custom'],
|
|
983
|
-
},
|
|
984
|
-
},
|
|
985
|
-
routing: {
|
|
986
|
-
request: {
|
|
987
|
-
body: '={{JSON.parse($value)}}',
|
|
988
|
-
},
|
|
989
|
-
},
|
|
990
|
-
},
|
|
991
|
-
{
|
|
992
|
-
displayName: 'Limit',
|
|
993
|
-
name: 'limit',
|
|
994
|
-
type: 'number',
|
|
995
|
-
default: 50,
|
|
996
|
-
description: 'Maximum number of contacts to return',
|
|
997
|
-
displayOptions: {
|
|
998
|
-
show: {
|
|
999
|
-
resource: ['contact'],
|
|
1000
|
-
operation: ['getAll'],
|
|
1001
|
-
},
|
|
1002
|
-
},
|
|
1003
|
-
},
|
|
1004
|
-
{
|
|
1005
|
-
displayName: 'Page',
|
|
1006
|
-
name: 'page',
|
|
1007
|
-
type: 'number',
|
|
1008
|
-
default: 1,
|
|
1009
|
-
description: 'Page number to retrieve',
|
|
1010
|
-
displayOptions: {
|
|
1011
|
-
show: {
|
|
1012
|
-
resource: ['contact'],
|
|
1013
|
-
operation: ['getAll'],
|
|
1014
|
-
},
|
|
1015
|
-
},
|
|
1016
|
-
},
|
|
1017
|
-
{
|
|
1018
|
-
displayName: 'Fields to Return',
|
|
1019
|
-
name: 'fields',
|
|
1020
|
-
type: 'multiOptions',
|
|
1021
|
-
default: [],
|
|
1022
|
-
description: 'Select specific fields to return (leave empty for all fields)',
|
|
1023
|
-
displayOptions: {
|
|
1024
|
-
show: {
|
|
1025
|
-
resource: ['contact'],
|
|
1026
|
-
operation: ['getAll'],
|
|
1027
|
-
},
|
|
1028
|
-
},
|
|
1029
|
-
options: [
|
|
1030
|
-
{
|
|
1031
|
-
name: 'ID',
|
|
1032
|
-
value: 'id',
|
|
1033
|
-
},
|
|
1034
|
-
{
|
|
1035
|
-
name: 'Name',
|
|
1036
|
-
value: 'name',
|
|
1037
|
-
},
|
|
1038
|
-
{
|
|
1039
|
-
name: 'Email',
|
|
1040
|
-
value: 'email',
|
|
1041
|
-
},
|
|
1042
|
-
{
|
|
1043
|
-
name: 'Phone Number',
|
|
1044
|
-
value: 'phone_number',
|
|
1045
|
-
},
|
|
1046
|
-
{
|
|
1047
|
-
name: 'Organization ID',
|
|
1048
|
-
value: 'organization_id',
|
|
1049
|
-
},
|
|
1050
|
-
{
|
|
1051
|
-
name: 'Created At',
|
|
1052
|
-
value: 'created_at',
|
|
1053
|
-
},
|
|
1054
|
-
{
|
|
1055
|
-
name: 'Updated At',
|
|
1056
|
-
value: 'updated_at',
|
|
1057
|
-
},
|
|
1058
|
-
{
|
|
1059
|
-
name: 'Favorites',
|
|
1060
|
-
value: 'favorites',
|
|
1061
|
-
},
|
|
1062
|
-
{
|
|
1063
|
-
name: 'Tags',
|
|
1064
|
-
value: 'tags',
|
|
1065
|
-
},
|
|
1066
|
-
],
|
|
1067
|
-
},
|
|
1068
|
-
{
|
|
1069
|
-
displayName: 'Query Parameters',
|
|
1070
|
-
name: 'queryParameters',
|
|
1071
|
-
type: 'fixedCollection',
|
|
1072
|
-
typeOptions: {
|
|
1073
|
-
multipleValues: true,
|
|
1074
|
-
},
|
|
1075
|
-
default: {},
|
|
1076
|
-
placeholder: 'Add Parameter',
|
|
1077
|
-
description: 'Additional query parameters',
|
|
1078
|
-
displayOptions: {
|
|
1079
|
-
show: {
|
|
1080
|
-
resource: ['contact'],
|
|
1081
|
-
operation: ['getAll'],
|
|
1082
|
-
},
|
|
1083
|
-
},
|
|
1084
|
-
options: [
|
|
1085
|
-
{
|
|
1086
|
-
name: 'parameter',
|
|
1087
|
-
displayName: 'Parameter',
|
|
1088
|
-
values: [
|
|
1089
|
-
{
|
|
1090
|
-
displayName: 'Name',
|
|
1091
|
-
name: 'name',
|
|
1092
|
-
type: 'string',
|
|
1093
|
-
default: '',
|
|
1094
|
-
},
|
|
1095
|
-
{
|
|
1096
|
-
displayName: 'Value',
|
|
1097
|
-
name: 'value',
|
|
1098
|
-
type: 'string',
|
|
1099
|
-
default: '',
|
|
1100
|
-
},
|
|
1101
|
-
],
|
|
1102
|
-
},
|
|
1103
|
-
],
|
|
1104
|
-
},
|
|
1105
|
-
{
|
|
1106
|
-
displayName: 'Query Parameters',
|
|
1107
|
-
name: 'queryParameters',
|
|
1108
|
-
type: 'fixedCollection',
|
|
1109
|
-
typeOptions: {
|
|
1110
|
-
multipleValues: true,
|
|
1111
|
-
},
|
|
1112
|
-
default: {},
|
|
1113
|
-
placeholder: 'Add Parameter',
|
|
1114
|
-
displayOptions: {
|
|
1115
|
-
show: {
|
|
1116
|
-
resource: ['custom'],
|
|
1117
|
-
},
|
|
1118
|
-
},
|
|
1119
|
-
options: [
|
|
1120
|
-
{
|
|
1121
|
-
name: 'parameter',
|
|
1122
|
-
displayName: 'Parameter',
|
|
1123
|
-
values: [
|
|
1124
|
-
{
|
|
1125
|
-
displayName: 'Name',
|
|
1126
|
-
name: 'name',
|
|
1127
|
-
type: 'string',
|
|
1128
|
-
default: '',
|
|
1129
|
-
},
|
|
1130
|
-
{
|
|
1131
|
-
displayName: 'Value',
|
|
1132
|
-
name: 'value',
|
|
1133
|
-
type: 'string',
|
|
1134
|
-
default: '',
|
|
1135
|
-
},
|
|
1136
|
-
],
|
|
1137
|
-
},
|
|
1138
|
-
],
|
|
1139
|
-
},
|
|
304
|
+
contactProperties_1.contactOperations,
|
|
305
|
+
companyProperties_1.companyOperations,
|
|
306
|
+
...contactProperties_1.contactFields,
|
|
307
|
+
...companyProperties_1.companyFields,
|
|
1140
308
|
],
|
|
1141
309
|
};
|
|
1142
310
|
}
|
|
1143
311
|
async execute() {
|
|
1144
312
|
const items = this.getInputData();
|
|
1145
313
|
const returnData = [];
|
|
1146
|
-
//
|
|
1147
|
-
|
|
1148
|
-
|
|
314
|
+
// Time-based cleanup of expired tokens
|
|
315
|
+
secureTokenCache.cleanupIfNeeded();
|
|
316
|
+
// Fetch credentials and token once before the loop
|
|
317
|
+
const credentials = await this.getCredentials('idb2bApi');
|
|
318
|
+
if (!credentials) {
|
|
319
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'No credentials found. Please configure your IDB2B API credentials.');
|
|
1149
320
|
}
|
|
321
|
+
if (!credentials.email || !credentials.password || !credentials.baseUrl) {
|
|
322
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Incomplete credentials. Email, password, and base URL are required.');
|
|
323
|
+
}
|
|
324
|
+
if (!validateEmail(credentials.email)) {
|
|
325
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Invalid email format in credentials.');
|
|
326
|
+
}
|
|
327
|
+
if (!validateBaseUrl(credentials.baseUrl)) {
|
|
328
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Invalid base URL in credentials. Must be a valid HTTPS URL.');
|
|
329
|
+
}
|
|
330
|
+
const accessToken = await getAccessToken(this, credentials);
|
|
1150
331
|
for (let i = 0; i < items.length; i++) {
|
|
1151
332
|
try {
|
|
1152
333
|
const resource = this.getNodeParameter('resource', i);
|
|
1153
334
|
const operation = this.getNodeParameter('operation', i);
|
|
1154
|
-
const credentials = await this.getCredentials('idb2bApi');
|
|
1155
|
-
// Validate credentials
|
|
1156
|
-
if (!credentials) {
|
|
1157
|
-
throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'No credentials found. Please configure your IDB2B API credentials.', { itemIndex: i });
|
|
1158
|
-
}
|
|
1159
|
-
if (!credentials.email || !credentials.password || !credentials.baseUrl) {
|
|
1160
|
-
throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Incomplete credentials. Email, password, and base URL are required.', { itemIndex: i });
|
|
1161
|
-
}
|
|
1162
|
-
if (!validateEmail(credentials.email)) {
|
|
1163
|
-
throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Invalid email format in credentials.', { itemIndex: i });
|
|
1164
|
-
}
|
|
1165
|
-
if (!validateBaseUrl(credentials.baseUrl)) {
|
|
1166
|
-
throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Invalid base URL in credentials. Must be a valid HTTPS URL.', { itemIndex: i });
|
|
1167
|
-
}
|
|
1168
|
-
const accessToken = await getAccessToken(this, credentials);
|
|
1169
335
|
let method = 'GET';
|
|
1170
336
|
let endpoint = '';
|
|
1171
337
|
let body = undefined;
|
|
@@ -1174,16 +340,19 @@ class IDB2B {
|
|
|
1174
340
|
if (operation === 'getAll') {
|
|
1175
341
|
method = 'GET';
|
|
1176
342
|
endpoint = '/api/contacts';
|
|
1177
|
-
// Add pagination parameters
|
|
1178
343
|
const limit = this.getNodeParameter('limit', i, 50);
|
|
1179
344
|
const page = this.getNodeParameter('page', i, 1);
|
|
1180
345
|
qs.limit = limit;
|
|
1181
346
|
qs.page = page;
|
|
1182
|
-
// Add additional query parameters
|
|
1183
347
|
const queryParameters = this.getNodeParameter('queryParameters', i, {});
|
|
1184
348
|
const additionalQs = buildQueryString(queryParameters);
|
|
1185
349
|
qs = Object.assign(Object.assign({}, qs), additionalQs);
|
|
1186
350
|
}
|
|
351
|
+
else if (operation === 'get') {
|
|
352
|
+
method = 'GET';
|
|
353
|
+
const contactId = this.getNodeParameter('contactId', i);
|
|
354
|
+
endpoint = `/api/contacts/${sanitizeId(contactId)}`;
|
|
355
|
+
}
|
|
1187
356
|
else if (operation === 'create') {
|
|
1188
357
|
method = 'POST';
|
|
1189
358
|
endpoint = '/api/contacts';
|
|
@@ -1191,189 +360,114 @@ class IDB2B {
|
|
|
1191
360
|
const email = this.getNodeParameter('email', i);
|
|
1192
361
|
const phone_number = this.getNodeParameter('phone_number', i, '');
|
|
1193
362
|
const additionalFields = this.getNodeParameter('additionalFields', i, {});
|
|
1194
|
-
// Validate required fields
|
|
1195
363
|
validateContactData(name, email);
|
|
1196
|
-
// Build contact data object
|
|
1197
364
|
body = Object.assign({ name: name.trim(), email: email.trim() }, (phone_number && { phone_number }));
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
}
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
body.
|
|
1223
|
-
}
|
|
1224
|
-
if (
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
365
|
+
Object.keys(additionalFields).forEach(key => {
|
|
366
|
+
if (additionalFields[key] !== undefined && additionalFields[key] !== '') {
|
|
367
|
+
if (key === 'tags' && additionalFields.tags && additionalFields.tags.tag) {
|
|
368
|
+
body.tags = additionalFields.tags.tag.map((tag) => ({
|
|
369
|
+
name: tag.name.trim(),
|
|
370
|
+
}));
|
|
371
|
+
}
|
|
372
|
+
else {
|
|
373
|
+
body[key] = additionalFields[key];
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
});
|
|
377
|
+
}
|
|
378
|
+
else if (operation === 'update') {
|
|
379
|
+
method = 'PUT';
|
|
380
|
+
const contactId = this.getNodeParameter('contactId', i);
|
|
381
|
+
endpoint = `/api/contacts/${sanitizeId(contactId)}`;
|
|
382
|
+
const name = this.getNodeParameter('name', i, '');
|
|
383
|
+
const email = this.getNodeParameter('email', i, '');
|
|
384
|
+
const phone_number = this.getNodeParameter('phone_number', i, '');
|
|
385
|
+
const additionalFields = this.getNodeParameter('additionalFields', i, {});
|
|
386
|
+
body = {};
|
|
387
|
+
if (name) {
|
|
388
|
+
validateContactData(name);
|
|
389
|
+
body.name = name.trim();
|
|
390
|
+
}
|
|
391
|
+
if (email) {
|
|
392
|
+
validateEmailField(email);
|
|
393
|
+
body.email = email.trim();
|
|
394
|
+
}
|
|
395
|
+
if (phone_number) {
|
|
396
|
+
body.phone_number = phone_number;
|
|
397
|
+
}
|
|
398
|
+
Object.keys(additionalFields).forEach(key => {
|
|
399
|
+
if (additionalFields[key] !== undefined && additionalFields[key] !== '') {
|
|
400
|
+
if (key === 'tags' && additionalFields.tags && additionalFields.tags.tag) {
|
|
401
|
+
body.tags = additionalFields.tags.tag.map((tag) => ({
|
|
402
|
+
name: tag.name.trim(),
|
|
403
|
+
}));
|
|
404
|
+
}
|
|
405
|
+
else {
|
|
406
|
+
body[key] = additionalFields[key];
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
});
|
|
410
|
+
}
|
|
411
|
+
else if (operation === 'delete') {
|
|
412
|
+
method = 'DELETE';
|
|
413
|
+
const contactId = this.getNodeParameter('contactId', i);
|
|
414
|
+
endpoint = `/api/contacts/${sanitizeId(contactId)}`;
|
|
1236
415
|
}
|
|
1237
416
|
}
|
|
1238
|
-
else if (resource === '
|
|
417
|
+
else if (resource === 'company') {
|
|
1239
418
|
if (operation === 'getAll') {
|
|
1240
419
|
method = 'GET';
|
|
1241
420
|
endpoint = '/api/leads';
|
|
1242
|
-
// Add pagination parameters
|
|
1243
421
|
const limit = this.getNodeParameter('limit', i, 50);
|
|
1244
422
|
const page = this.getNodeParameter('page', i, 1);
|
|
1245
423
|
qs.limit = limit;
|
|
1246
424
|
qs.page = page;
|
|
1247
|
-
// Add additional query parameters
|
|
1248
425
|
const queryParameters = this.getNodeParameter('queryParameters', i, {});
|
|
1249
426
|
const additionalQs = buildQueryString(queryParameters);
|
|
1250
427
|
qs = Object.assign(Object.assign({}, qs), additionalQs);
|
|
1251
428
|
}
|
|
429
|
+
else if (operation === 'get') {
|
|
430
|
+
method = 'GET';
|
|
431
|
+
const companyId = this.getNodeParameter('companyId', i);
|
|
432
|
+
endpoint = `/api/leads/${sanitizeId(companyId)}`;
|
|
433
|
+
}
|
|
1252
434
|
else if (operation === 'create') {
|
|
1253
435
|
method = 'POST';
|
|
1254
436
|
endpoint = '/api/leads';
|
|
1255
437
|
const name = this.getNodeParameter('name', i);
|
|
1256
438
|
const additionalFields = this.getNodeParameter('additionalFields', i, {});
|
|
1257
|
-
// Validate required fields
|
|
1258
439
|
validateLeadData(name);
|
|
1259
|
-
// Build lead data object
|
|
1260
440
|
body = {
|
|
1261
441
|
name: name.trim(),
|
|
1262
442
|
};
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
body.website = additionalFields.website;
|
|
1269
|
-
}
|
|
1270
|
-
if (additionalFields.description) {
|
|
1271
|
-
body.description = additionalFields.description;
|
|
1272
|
-
}
|
|
1273
|
-
if (additionalFields.status_id) {
|
|
1274
|
-
body.status_id = additionalFields.status_id;
|
|
1275
|
-
}
|
|
1276
|
-
if (additionalFields.source_id) {
|
|
1277
|
-
body.source_id = additionalFields.source_id;
|
|
1278
|
-
}
|
|
1279
|
-
if (additionalFields.size_id) {
|
|
1280
|
-
body.size_id = additionalFields.size_id;
|
|
1281
|
-
}
|
|
1282
|
-
if (additionalFields.industry_id) {
|
|
1283
|
-
body.industry_id = additionalFields.industry_id;
|
|
1284
|
-
}
|
|
1285
|
-
if (additionalFields.main_contact_id) {
|
|
1286
|
-
body.main_contact_id = additionalFields.main_contact_id;
|
|
1287
|
-
}
|
|
1288
|
-
if (additionalFields.contact_name) {
|
|
1289
|
-
body.contact_name = additionalFields.contact_name;
|
|
1290
|
-
}
|
|
1291
|
-
if (additionalFields.contact_email) {
|
|
1292
|
-
body.contact_email = additionalFields.contact_email;
|
|
1293
|
-
}
|
|
1294
|
-
if (additionalFields.contact_phone_number) {
|
|
1295
|
-
body.contact_phone_number = additionalFields.contact_phone_number;
|
|
1296
|
-
}
|
|
1297
|
-
}
|
|
1298
|
-
}
|
|
1299
|
-
else if (resource === 'leadActivities') {
|
|
1300
|
-
if (operation === 'getAll') {
|
|
1301
|
-
method = 'GET';
|
|
1302
|
-
const leadId = this.getNodeParameter('lead_id', i);
|
|
1303
|
-
// Validate required fields
|
|
1304
|
-
if (!leadId || typeof leadId !== 'string' || leadId.trim().length === 0) {
|
|
1305
|
-
throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Lead ID is required and must be a non-empty string', { itemIndex: i });
|
|
1306
|
-
}
|
|
1307
|
-
endpoint = `/leads/${leadId.trim()}/activities`;
|
|
1308
|
-
// Add pagination parameters
|
|
1309
|
-
const limit = this.getNodeParameter('limit', i, 50);
|
|
1310
|
-
const page = this.getNodeParameter('page', i, 1);
|
|
1311
|
-
qs.limit = limit;
|
|
1312
|
-
qs.page = page;
|
|
1313
|
-
// Add additional query parameters
|
|
1314
|
-
const queryParameters = this.getNodeParameter('queryParameters', i, {});
|
|
1315
|
-
const additionalQs = buildQueryString(queryParameters);
|
|
1316
|
-
qs = Object.assign(Object.assign({}, qs), additionalQs);
|
|
443
|
+
Object.keys(additionalFields).forEach(key => {
|
|
444
|
+
if (additionalFields[key] !== undefined && additionalFields[key] !== '') {
|
|
445
|
+
body[key] = additionalFields[key];
|
|
446
|
+
}
|
|
447
|
+
});
|
|
1317
448
|
}
|
|
1318
|
-
else if (operation === '
|
|
1319
|
-
method = '
|
|
1320
|
-
const
|
|
1321
|
-
|
|
449
|
+
else if (operation === 'update') {
|
|
450
|
+
method = 'PUT';
|
|
451
|
+
const companyId = this.getNodeParameter('companyId', i);
|
|
452
|
+
endpoint = `/api/leads/${sanitizeId(companyId)}`;
|
|
453
|
+
const name = this.getNodeParameter('name', i, '');
|
|
1322
454
|
const additionalFields = this.getNodeParameter('additionalFields', i, {});
|
|
1323
|
-
|
|
1324
|
-
if (
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
subject: subject.trim(),
|
|
1334
|
-
};
|
|
1335
|
-
// Add additional fields if provided
|
|
1336
|
-
if (additionalFields.icon) {
|
|
1337
|
-
body.icon = additionalFields.icon;
|
|
1338
|
-
}
|
|
1339
|
-
if (additionalFields.description) {
|
|
1340
|
-
body.description = additionalFields.description;
|
|
1341
|
-
}
|
|
1342
|
-
if (additionalFields.datetime) {
|
|
1343
|
-
body.datetime = additionalFields.datetime;
|
|
1344
|
-
}
|
|
1345
|
-
if (additionalFields.user_id) {
|
|
1346
|
-
body.user_id = additionalFields.user_id;
|
|
1347
|
-
}
|
|
1348
|
-
// Process attachments if provided
|
|
1349
|
-
if (additionalFields.attachments && additionalFields.attachments.attachment) {
|
|
1350
|
-
body.attachments = additionalFields.attachments.attachment.map((attachment) => attachment.file);
|
|
1351
|
-
}
|
|
1352
|
-
// Process attachments to delete if provided
|
|
1353
|
-
if (additionalFields.attachments_to_delete && additionalFields.attachments_to_delete.attachment_id) {
|
|
1354
|
-
body.attachments_to_delete = additionalFields.attachments_to_delete.attachment_id.map((attachment) => attachment.id);
|
|
1355
|
-
}
|
|
1356
|
-
}
|
|
1357
|
-
}
|
|
1358
|
-
else if (resource === 'custom') {
|
|
1359
|
-
endpoint = this.getNodeParameter('endpoint', i);
|
|
1360
|
-
// Validate endpoint
|
|
1361
|
-
if (!endpoint || typeof endpoint !== 'string') {
|
|
1362
|
-
throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Endpoint is required for custom requests', { itemIndex: i });
|
|
1363
|
-
}
|
|
1364
|
-
// Ensure endpoint starts with /
|
|
1365
|
-
if (!endpoint.startsWith('/')) {
|
|
1366
|
-
endpoint = '/' + endpoint;
|
|
455
|
+
body = {};
|
|
456
|
+
if (name) {
|
|
457
|
+
validateLeadData(name);
|
|
458
|
+
body.name = name.trim();
|
|
459
|
+
}
|
|
460
|
+
Object.keys(additionalFields).forEach(key => {
|
|
461
|
+
if (additionalFields[key] !== undefined && additionalFields[key] !== '') {
|
|
462
|
+
body[key] = additionalFields[key];
|
|
463
|
+
}
|
|
464
|
+
});
|
|
1367
465
|
}
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
const
|
|
1371
|
-
|
|
1372
|
-
body = safeJsonParse(jsonBody, 'JSON Body');
|
|
1373
|
-
}
|
|
466
|
+
else if (operation === 'delete') {
|
|
467
|
+
method = 'DELETE';
|
|
468
|
+
const companyId = this.getNodeParameter('companyId', i);
|
|
469
|
+
endpoint = `/api/leads/${sanitizeId(companyId)}`;
|
|
1374
470
|
}
|
|
1375
|
-
const queryParameters = this.getNodeParameter('queryParameters', i, {});
|
|
1376
|
-
qs = buildQueryString(queryParameters);
|
|
1377
471
|
}
|
|
1378
472
|
const response = await makeRequestWithRetry(this, {
|
|
1379
473
|
method,
|
|
@@ -1387,20 +481,8 @@ class IDB2B {
|
|
|
1387
481
|
});
|
|
1388
482
|
let processedResponse = response;
|
|
1389
483
|
// Enhance response for create operations
|
|
1390
|
-
if (operation === 'create' &&
|
|
1391
|
-
|
|
1392
|
-
if (response.message === 'success' && response.data) {
|
|
1393
|
-
// API returned actual data - use it
|
|
1394
|
-
processedResponse = response;
|
|
1395
|
-
}
|
|
1396
|
-
else if (response.message === 'success' && response.data === null) {
|
|
1397
|
-
// Fallback: create synthetic response but try to include any ID from headers or other sources
|
|
1398
|
-
processedResponse = Object.assign(Object.assign({}, response), { data: Object.assign(Object.assign({}, body), { created: true, status: 'success' }) });
|
|
1399
|
-
}
|
|
1400
|
-
}
|
|
1401
|
-
else if (operation === 'create' && response.message === 'success' && response.data === null) {
|
|
1402
|
-
// Original logic for other create operations
|
|
1403
|
-
processedResponse = Object.assign(Object.assign({}, response), { data: Object.assign(Object.assign({}, body), { created: true, status: 'success' }) });
|
|
484
|
+
if (operation === 'create' && response.message === 'success' && response.data === null) {
|
|
485
|
+
processedResponse = Object.assign(Object.assign({}, response), { data: Object.assign(Object.assign({}, body), { created: true, status: 'success', _note: 'Server did not return the created entity. Fields like id and timestamps are unavailable.' }) });
|
|
1404
486
|
}
|
|
1405
487
|
// Apply field filtering for contact getAll operation
|
|
1406
488
|
if (resource === 'contact' && operation === 'getAll') {
|
|
@@ -1417,33 +499,18 @@ class IDB2B {
|
|
|
1417
499
|
}) });
|
|
1418
500
|
}
|
|
1419
501
|
}
|
|
1420
|
-
// Apply field filtering for
|
|
1421
|
-
if (resource === '
|
|
1422
|
-
const fieldsToReturn = this.getNodeParameter('fields', i, []);
|
|
1423
|
-
if (fieldsToReturn.length > 0 && Array.isArray(response.data)) {
|
|
1424
|
-
processedResponse = Object.assign(Object.assign({}, response), { data: response.data.map((lead) => {
|
|
1425
|
-
const filteredLead = {};
|
|
1426
|
-
fieldsToReturn.forEach(field => {
|
|
1427
|
-
if (field in lead) {
|
|
1428
|
-
filteredLead[field] = lead[field];
|
|
1429
|
-
}
|
|
1430
|
-
});
|
|
1431
|
-
return filteredLead;
|
|
1432
|
-
}) });
|
|
1433
|
-
}
|
|
1434
|
-
}
|
|
1435
|
-
// Apply field filtering for leadActivities getAll operation
|
|
1436
|
-
if (resource === 'leadActivities' && operation === 'getAll') {
|
|
502
|
+
// Apply field filtering for company getAll operation
|
|
503
|
+
if (resource === 'company' && operation === 'getAll') {
|
|
1437
504
|
const fieldsToReturn = this.getNodeParameter('fields', i, []);
|
|
1438
505
|
if (fieldsToReturn.length > 0 && Array.isArray(response.data)) {
|
|
1439
|
-
processedResponse = Object.assign(Object.assign({}, response), { data: response.data.map((
|
|
1440
|
-
const
|
|
506
|
+
processedResponse = Object.assign(Object.assign({}, response), { data: response.data.map((company) => {
|
|
507
|
+
const filteredCompany = {};
|
|
1441
508
|
fieldsToReturn.forEach(field => {
|
|
1442
|
-
if (field in
|
|
1443
|
-
|
|
509
|
+
if (field in company) {
|
|
510
|
+
filteredCompany[field] = company[field];
|
|
1444
511
|
}
|
|
1445
512
|
});
|
|
1446
|
-
return
|
|
513
|
+
return filteredCompany;
|
|
1447
514
|
}) });
|
|
1448
515
|
}
|
|
1449
516
|
}
|
|
@@ -1457,24 +524,19 @@ class IDB2B {
|
|
|
1457
524
|
let errorData = { error: 'Unknown error occurred' };
|
|
1458
525
|
if (error instanceof Error) {
|
|
1459
526
|
errorData.error = error.message;
|
|
1460
|
-
// Enhanced error handling for HTTP errors
|
|
1461
527
|
if ('response' in error && error.response) {
|
|
1462
528
|
const response = error.response;
|
|
1463
529
|
errorData.statusCode = response.status || response.statusCode;
|
|
1464
530
|
errorData.statusText = response.statusText;
|
|
1465
|
-
// Sanitize response data to prevent information disclosure
|
|
1466
531
|
if (response.data) {
|
|
1467
|
-
// Only include non-sensitive error details
|
|
1468
532
|
const sanitizedData = sanitizeErrorData(response.data);
|
|
1469
533
|
if (Object.keys(sanitizedData).length > 0) {
|
|
1470
534
|
errorData.details = sanitizedData;
|
|
1471
535
|
}
|
|
1472
536
|
}
|
|
1473
|
-
// Specific error messages based on status codes
|
|
1474
537
|
switch (response.status || response.statusCode) {
|
|
1475
538
|
case 401:
|
|
1476
|
-
//
|
|
1477
|
-
const credentials = await this.getCredentials('idb2bApi');
|
|
539
|
+
// Use outer-scope credentials to invalidate cache
|
|
1478
540
|
const cacheKey = `${credentials.baseUrl}:${credentials.email}`;
|
|
1479
541
|
secureTokenCache.invalidate(cacheKey);
|
|
1480
542
|
errorData.error = 'Authentication failed - check credentials';
|