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.
@@ -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 periodically
85
- cleanup() {
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))); // Max 30s delay
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))); // Max 10s delay
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 (!email || typeof email !== 'string' || email.trim().length === 0) {
208
- throw new Error('Contact email is required and must be a non-empty string');
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: 'Lead',
309
- value: 'lead',
298
+ name: 'Company',
299
+ value: 'company',
310
300
  },
311
301
  ],
312
302
  default: 'contact',
313
303
  },
314
- {
315
- displayName: 'Operation',
316
- name: 'operation',
317
- type: 'options',
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
- // Periodic cleanup of expired tokens (run cleanup every 100 executions)
1147
- if (Math.random() < 0.01) {
1148
- secureTokenCache.cleanup();
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
- // Add additional fields
1199
- if (additionalFields.user_id) {
1200
- body.user_id = additionalFields.user_id;
1201
- }
1202
- if (additionalFields.lead_id) {
1203
- body.lead_id = additionalFields.lead_id;
1204
- }
1205
- if (typeof additionalFields.favorites === 'boolean') {
1206
- body.favorites = additionalFields.favorites;
1207
- }
1208
- // Add CRM fields
1209
- if (additionalFields.company) {
1210
- body.company = additionalFields.company;
1211
- }
1212
- if (additionalFields.company_id) {
1213
- body.company_id = additionalFields.company_id;
1214
- }
1215
- if (additionalFields.status) {
1216
- body.status = additionalFields.status;
1217
- }
1218
- if (additionalFields.priority) {
1219
- body.priority = additionalFields.priority;
1220
- }
1221
- if (additionalFields.estimated_value) {
1222
- body.estimated_value = additionalFields.estimated_value;
1223
- }
1224
- if (additionalFields.account_owner) {
1225
- body.account_owner = additionalFields.account_owner;
1226
- }
1227
- if (additionalFields.position) {
1228
- body.position = additionalFields.position;
1229
- }
1230
- // Process tags if provided
1231
- if (additionalFields.tags && additionalFields.tags.tag) {
1232
- body.tags = additionalFields.tags.tag.map((tag) => ({
1233
- name: tag.name.trim(),
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 === 'lead') {
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
- // Add additional fields if provided
1264
- if (additionalFields.owner_id) {
1265
- body.owner_id = additionalFields.owner_id;
1266
- }
1267
- if (additionalFields.website) {
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 === 'create') {
1319
- method = 'POST';
1320
- const leadId = this.getNodeParameter('lead_id', i);
1321
- const subject = this.getNodeParameter('subject', i);
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
- // Validate required fields
1324
- if (!leadId || typeof leadId !== 'string' || leadId.trim().length === 0) {
1325
- throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Lead ID is required and must be a non-empty string', { itemIndex: i });
1326
- }
1327
- if (!subject || typeof subject !== 'string' || subject.trim().length === 0) {
1328
- throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Subject is required and must be a non-empty string', { itemIndex: i });
1329
- }
1330
- endpoint = `/leads/${leadId.trim()}/activities`;
1331
- // Build activity data object
1332
- body = {
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
- method = operation.toUpperCase();
1369
- if (['post', 'put'].includes(operation)) {
1370
- const jsonBody = this.getNodeParameter('jsonBody', i);
1371
- if (jsonBody && jsonBody.trim()) {
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' && (resource === 'lead' || resource === 'leadActivities')) {
1391
- // For lead and leadActivities creation, ensure the response includes the data with ID
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 lead getAll operation
1421
- if (resource === 'lead' && operation === 'getAll') {
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((activity) => {
1440
- const filteredActivity = {};
506
+ processedResponse = Object.assign(Object.assign({}, response), { data: response.data.map((company) => {
507
+ const filteredCompany = {};
1441
508
  fieldsToReturn.forEach(field => {
1442
- if (field in activity) {
1443
- filteredActivity[field] = activity[field];
509
+ if (field in company) {
510
+ filteredCompany[field] = company[field];
1444
511
  }
1445
512
  });
1446
- return filteredActivity;
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
- // Invalidate cached tokens on authentication failure
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';