@crono-one/n8n-nodes-crono-public-api 1.1.2 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -29,12 +29,15 @@ function addIfNotEmpty(target, key, value) {
29
29
  }
30
30
  }
31
31
  function getAdditionalFields(executeFunctions, parameterName, itemIndex) {
32
- var _a;
33
- const entries = executeFunctions.getNodeParameter(parameterName, itemIndex, []);
32
+ var _a, _b;
33
+ const rawValue = executeFunctions.getNodeParameter(parameterName, itemIndex, {});
34
34
  const additional = {};
35
+ const entries = Array.isArray(rawValue)
36
+ ? rawValue
37
+ : ((_a = Object.values(rawValue).find((value) => Array.isArray(value))) !== null && _a !== void 0 ? _a : []);
35
38
  for (const entry of entries) {
36
39
  if (entry.field) {
37
- additional[entry.field] = (_a = entry.value) !== null && _a !== void 0 ? _a : '';
40
+ additional[entry.field] = (_b = entry.value) !== null && _b !== void 0 ? _b : '';
38
41
  }
39
42
  }
40
43
  return additional;
@@ -48,6 +51,139 @@ function parseCsv(value) {
48
51
  .map((item) => item.trim())
49
52
  .filter((item) => item.length > 0);
50
53
  }
54
+ function cloneDataObject(value) {
55
+ if (value === undefined) {
56
+ return value;
57
+ }
58
+ return JSON.parse(JSON.stringify(value));
59
+ }
60
+ function getPaginationConfig(qs, body) {
61
+ const queryLimitKey = typeof qs.Limit === 'number' ? 'Limit' : 'limit';
62
+ const queryOffsetKey = typeof qs.Offset === 'number' ? 'Offset' : 'offset';
63
+ const queryLimit = qs[queryLimitKey];
64
+ const queryOffset = qs[queryOffsetKey];
65
+ if (typeof queryLimit === 'number' && typeof queryOffset === 'number') {
66
+ return {
67
+ type: 'query',
68
+ limit: queryLimit,
69
+ offset: queryOffset,
70
+ limitKey: queryLimitKey,
71
+ offsetKey: queryOffsetKey,
72
+ };
73
+ }
74
+ if (!body) {
75
+ return undefined;
76
+ }
77
+ const bodyLimit = body.Limit;
78
+ const bodyOffset = body.Offset;
79
+ if (typeof bodyLimit === 'number' && typeof bodyOffset === 'number') {
80
+ return {
81
+ type: 'body',
82
+ limit: bodyLimit,
83
+ offset: bodyOffset,
84
+ };
85
+ }
86
+ const pagination = body.Pagination;
87
+ if (pagination && typeof pagination.Limit === 'number' && typeof pagination.Offset === 'number') {
88
+ return {
89
+ type: 'bodyPagination',
90
+ limit: pagination.Limit,
91
+ offset: pagination.Offset,
92
+ };
93
+ }
94
+ return undefined;
95
+ }
96
+ function setPaginationOffset(qs, body, pagination, offset) {
97
+ if (pagination.type === 'query') {
98
+ const offsetKey = pagination.offsetKey in qs ? pagination.offsetKey : 'Offset' in qs ? 'Offset' : 'offset';
99
+ qs[offsetKey] = offset;
100
+ return;
101
+ }
102
+ if (!body) {
103
+ return;
104
+ }
105
+ if (pagination.type === 'body') {
106
+ body.Offset = offset;
107
+ return;
108
+ }
109
+ const currentPagination = body.Pagination;
110
+ if (currentPagination) {
111
+ currentPagination.Offset = offset;
112
+ }
113
+ }
114
+ function extractItemsFromResponse(responseData) {
115
+ if (Array.isArray(responseData)) {
116
+ const items = responseData.filter((item) => item !== null && typeof item === 'object' && !Array.isArray(item));
117
+ return items.length > 0 ? items : null;
118
+ }
119
+ if (!responseData || typeof responseData !== 'object') {
120
+ return null;
121
+ }
122
+ const preferredKeys = [
123
+ 'Accounts',
124
+ 'Prospects',
125
+ 'Opportunities',
126
+ 'Notes',
127
+ 'Activities',
128
+ 'Users',
129
+ 'Imports',
130
+ 'CronoLists',
131
+ 'Strategies',
132
+ 'Sequences',
133
+ 'Templates',
134
+ 'Tasks',
135
+ 'Results',
136
+ 'Items',
137
+ 'Data',
138
+ ];
139
+ const responseObject = responseData;
140
+ for (const key of preferredKeys) {
141
+ const value = responseObject[key];
142
+ if (Array.isArray(value)) {
143
+ const items = value.filter((item) => item !== null && typeof item === 'object' && !Array.isArray(item));
144
+ if (items.length > 0) {
145
+ return items;
146
+ }
147
+ }
148
+ }
149
+ for (const value of Object.values(responseObject)) {
150
+ if (Array.isArray(value)) {
151
+ const items = value.filter((item) => item !== null && typeof item === 'object' && !Array.isArray(item));
152
+ if (items.length > 0) {
153
+ return items;
154
+ }
155
+ }
156
+ if (value && typeof value === 'object') {
157
+ const nestedItems = extractItemsFromResponse(value);
158
+ if (nestedItems && nestedItems.length > 0) {
159
+ return nestedItems;
160
+ }
161
+ }
162
+ }
163
+ return null;
164
+ }
165
+ function getTotalCount(responseData) {
166
+ if (!responseData || typeof responseData !== 'object' || Array.isArray(responseData)) {
167
+ return undefined;
168
+ }
169
+ const responseObject = responseData;
170
+ const preferredKeys = ['TotalCount', 'Total', 'Count'];
171
+ for (const key of preferredKeys) {
172
+ const value = responseObject[key];
173
+ if (typeof value === 'number') {
174
+ return value;
175
+ }
176
+ }
177
+ for (const value of Object.values(responseObject)) {
178
+ if (value && typeof value === 'object' && !Array.isArray(value)) {
179
+ const nestedTotal = getTotalCount(value);
180
+ if (nestedTotal !== undefined) {
181
+ return nestedTotal;
182
+ }
183
+ }
184
+ }
185
+ return undefined;
186
+ }
51
187
  async function cronoApiRequest(method, endpoint, qs = {}, body = undefined) {
52
188
  const credentials = await this.getCredentials('cronoPublicApi');
53
189
  const baseUrl = credentials.baseUrl || 'https://ext.crono.one';
@@ -229,10 +365,26 @@ class CronoPublicApi {
229
365
  { name: 'Create', value: 'create', action: 'Create a list' },
230
366
  { name: 'Delete', value: 'delete', action: 'Delete a list' },
231
367
  { name: 'Get', value: 'get', action: 'Get a list' },
232
- { name: 'Remove Companies', value: 'removeCompanies', action: 'Remove companies from a list' },
233
- { name: 'Remove Contacts', value: 'removeContacts', action: 'Remove contacts from a list' },
234
- { name: 'Remove Sequences', value: 'removeSequences', action: 'Remove sequences from a list' },
235
- { name: 'Remove Templates', value: 'removeTemplates', action: 'Remove templates from a list' },
368
+ {
369
+ name: 'Remove Companies',
370
+ value: 'removeCompanies',
371
+ action: 'Remove companies from a list',
372
+ },
373
+ {
374
+ name: 'Remove Contacts',
375
+ value: 'removeContacts',
376
+ action: 'Remove contacts from a list',
377
+ },
378
+ {
379
+ name: 'Remove Sequences',
380
+ value: 'removeSequences',
381
+ action: 'Remove sequences from a list',
382
+ },
383
+ {
384
+ name: 'Remove Templates',
385
+ value: 'removeTemplates',
386
+ action: 'Remove templates from a list',
387
+ },
236
388
  { name: 'Search', value: 'search', action: 'Search lists' },
237
389
  { name: 'Update', value: 'update', action: 'Update a list' },
238
390
  ],
@@ -383,6 +535,19 @@ class CronoPublicApi {
383
535
  },
384
536
  },
385
537
  },
538
+ {
539
+ displayName: 'Return All',
540
+ name: 'returnAll',
541
+ type: 'boolean',
542
+ default: false,
543
+ description: 'Whether to return all results or only up to a given limit',
544
+ displayOptions: {
545
+ show: {
546
+ resource: ['company', 'contact', 'deal', 'note', 'activity', 'user', 'import'],
547
+ operation: ['getAll'],
548
+ },
549
+ },
550
+ },
386
551
  {
387
552
  displayName: 'Limit',
388
553
  name: 'limit',
@@ -424,6 +589,31 @@ class CronoPublicApi {
424
589
  },
425
590
  description: 'JSON object of include options to add as query parameters',
426
591
  },
592
+ {
593
+ displayName: 'Return All',
594
+ name: 'returnAll',
595
+ type: 'boolean',
596
+ default: false,
597
+ description: 'Whether to return all results or only up to a given limit',
598
+ displayOptions: {
599
+ show: {
600
+ resource: [
601
+ 'company',
602
+ 'contact',
603
+ 'deal',
604
+ 'note',
605
+ 'activity',
606
+ 'list',
607
+ 'sequence',
608
+ 'template',
609
+ 'user',
610
+ 'task',
611
+ ],
612
+ operation: ['search', 'searchDetails'],
613
+ useRawJsonSearch: [false],
614
+ },
615
+ },
616
+ },
427
617
  {
428
618
  displayName: 'Use Raw JSON',
429
619
  name: 'useRawJsonSearch',
@@ -505,12 +695,11 @@ class CronoPublicApi {
505
695
  {
506
696
  displayName: 'Additional Fields',
507
697
  name: 'dataAdditionalFields',
508
- type: 'collection',
698
+ type: 'fixedCollection',
509
699
  typeOptions: {
510
- multipleValueButtonText: 'Add Field',
511
700
  multipleValues: true,
512
701
  },
513
- default: [],
702
+ default: {},
514
703
  displayOptions: {
515
704
  show: {
516
705
  resource: ['company', 'contact', 'deal', 'note', 'task', 'list', 'sequence'],
@@ -521,28 +710,33 @@ class CronoPublicApi {
521
710
  description: 'Additional data fields to merge into the payload',
522
711
  options: [
523
712
  {
524
- displayName: 'Field',
525
713
  name: 'field',
526
- type: 'string',
527
- default: '',
528
- },
529
- {
530
- displayName: 'Value',
531
- name: 'value',
532
- type: 'string',
533
- default: '',
714
+ displayName: 'Field',
715
+ values: [
716
+ {
717
+ displayName: 'Field',
718
+ name: 'field',
719
+ type: 'string',
720
+ default: '',
721
+ },
722
+ {
723
+ displayName: 'Value',
724
+ name: 'value',
725
+ type: 'string',
726
+ default: '',
727
+ },
728
+ ],
534
729
  },
535
730
  ],
536
731
  },
537
732
  {
538
733
  displayName: 'Additional Fields',
539
734
  name: 'searchAdditionalFields',
540
- type: 'collection',
735
+ type: 'fixedCollection',
541
736
  typeOptions: {
542
- multipleValueButtonText: 'Add Field',
543
737
  multipleValues: true,
544
738
  },
545
- default: [],
739
+ default: {},
546
740
  displayOptions: {
547
741
  show: {
548
742
  resource: [
@@ -565,16 +759,22 @@ class CronoPublicApi {
565
759
  description: 'Additional search fields to merge into the payload',
566
760
  options: [
567
761
  {
568
- displayName: 'Field',
569
762
  name: 'field',
570
- type: 'string',
571
- default: '',
572
- },
573
- {
574
- displayName: 'Value',
575
- name: 'value',
576
- type: 'string',
577
- default: '',
763
+ displayName: 'Field',
764
+ values: [
765
+ {
766
+ displayName: 'Field',
767
+ name: 'field',
768
+ type: 'string',
769
+ default: '',
770
+ },
771
+ {
772
+ displayName: 'Value',
773
+ name: 'value',
774
+ type: 'string',
775
+ default: '',
776
+ },
777
+ ],
578
778
  },
579
779
  ],
580
780
  },
@@ -3798,7 +3998,7 @@ class CronoPublicApi {
3798
3998
  },
3799
3999
  },
3800
4000
  {
3801
- displayName: 'Has Email',
4001
+ displayName: 'Has Email Address',
3802
4002
  name: 'contactSearchHasEmail',
3803
4003
  type: 'boolean',
3804
4004
  default: false,
@@ -3824,7 +4024,7 @@ class CronoPublicApi {
3824
4024
  },
3825
4025
  },
3826
4026
  {
3827
- displayName: 'Has LinkedIn',
4027
+ displayName: 'Has LinkedIn Url',
3828
4028
  name: 'contactSearchHasLinkedin',
3829
4029
  type: 'boolean',
3830
4030
  default: false,
@@ -6201,6 +6401,7 @@ class CronoPublicApi {
6201
6401
  const basePath = `/api/v${apiVersion}`;
6202
6402
  const useRawJsonData = this.getNodeParameter('useRawJsonData', itemIndex, false);
6203
6403
  const useRawJsonSearch = this.getNodeParameter('useRawJsonSearch', itemIndex, false);
6404
+ const returnAll = this.getNodeParameter('returnAll', itemIndex, false);
6204
6405
  let endpoint = '';
6205
6406
  let method = 'GET';
6206
6407
  let qs = {};
@@ -7525,8 +7726,63 @@ class CronoPublicApi {
7525
7726
  itemIndex,
7526
7727
  });
7527
7728
  }
7528
- const responseData = await cronoApiRequest.call(this, method, endpoint, qs, body);
7529
- returnData.push({ json: responseData, pairedItem: { item: itemIndex } });
7729
+ const pagination = returnAll && !useRawJsonSearch ? getPaginationConfig(qs, body) : undefined;
7730
+ if (pagination) {
7731
+ const maxIterations = 100;
7732
+ const aggregatedItems = [];
7733
+ let currentOffset = pagination.offset;
7734
+ let iterationCount = 0;
7735
+ let fallbackResponse;
7736
+ let reachedMaxIterations = false;
7737
+ const pageSize = pagination.limit > 0 ? pagination.limit : 1;
7738
+ while (true) {
7739
+ iterationCount += 1;
7740
+ if (iterationCount > maxIterations) {
7741
+ reachedMaxIterations = true;
7742
+ break;
7743
+ }
7744
+ const requestQs = cloneDataObject(qs);
7745
+ const requestBody = cloneDataObject(body);
7746
+ setPaginationOffset(requestQs, requestBody, pagination, currentOffset);
7747
+ const responseData = await cronoApiRequest.call(this, method, endpoint, requestQs, requestBody);
7748
+ const pageItems = extractItemsFromResponse(responseData);
7749
+ if (!pageItems) {
7750
+ fallbackResponse = responseData;
7751
+ break;
7752
+ }
7753
+ aggregatedItems.push(...pageItems);
7754
+ if (pageItems.length === 0) {
7755
+ break;
7756
+ }
7757
+ const totalCount = getTotalCount(responseData);
7758
+ const nextOffset = currentOffset + pageSize;
7759
+ if (totalCount !== undefined) {
7760
+ if (nextOffset >= totalCount) {
7761
+ break;
7762
+ }
7763
+ }
7764
+ else if (pageItems.length < pageSize) {
7765
+ break;
7766
+ }
7767
+ currentOffset = nextOffset;
7768
+ }
7769
+ if (reachedMaxIterations && fallbackResponse) {
7770
+ returnData.push({ json: fallbackResponse, pairedItem: { item: itemIndex } });
7771
+ }
7772
+ else if (aggregatedItems.length > 0) {
7773
+ returnData.push(...aggregatedItems.map((json) => ({
7774
+ json,
7775
+ pairedItem: { item: itemIndex },
7776
+ })));
7777
+ }
7778
+ else if (fallbackResponse) {
7779
+ returnData.push({ json: fallbackResponse, pairedItem: { item: itemIndex } });
7780
+ }
7781
+ }
7782
+ else {
7783
+ const responseData = await cronoApiRequest.call(this, method, endpoint, qs, body);
7784
+ returnData.push({ json: responseData, pairedItem: { item: itemIndex } });
7785
+ }
7530
7786
  }
7531
7787
  return [returnData];
7532
7788
  }