n8n-nodes-qlik-cloud 1.0.5 → 1.0.7

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.
@@ -7,23 +7,70 @@ const index_1 = require("./resources/apps/index");
7
7
  const index_2 = require("./resources/assistants/index");
8
8
  const index_3 = require("./resources/audits/index");
9
9
  const index_4 = require("./resources/items/index");
10
+ const DEFAULT_PAGE_SIZE = 100;
11
+ function extractPageItems(response) {
12
+ const payload = response?.data ?? response;
13
+ if (Array.isArray(payload)) {
14
+ return payload;
15
+ }
16
+ if (payload === undefined || payload === null) {
17
+ return [];
18
+ }
19
+ return [payload];
20
+ }
21
+ function extractNextCursor(response) {
22
+ const nextLink = response?.links?.next;
23
+ const href = typeof nextLink === 'string' ? nextLink : nextLink?.href;
24
+ if (typeof href === 'string' && href.length) {
25
+ try {
26
+ const url = href.startsWith('http') ? new URL(href) : new URL(href, 'https://qlik.cloud');
27
+ return url.searchParams.get('next') ?? undefined;
28
+ }
29
+ catch {
30
+ return undefined;
31
+ }
32
+ }
33
+ if (typeof response?.next === 'string') {
34
+ return response.next;
35
+ }
36
+ if (typeof response?.meta?.next === 'string') {
37
+ return response.meta.next;
38
+ }
39
+ return undefined;
40
+ }
41
+ async function fetchPaginatedResults(context, endpoint, filters, limit, pageSize = DEFAULT_PAGE_SIZE) {
42
+ const collected = [];
43
+ let cursor;
44
+ do {
45
+ const qs = { ...filters };
46
+ const chunkSize = limit === undefined ? pageSize : Math.min(pageSize, limit - collected.length);
47
+ if (chunkSize <= 0) {
48
+ break;
49
+ }
50
+ qs.limit = chunkSize;
51
+ if (cursor) {
52
+ qs.next = cursor;
53
+ }
54
+ const response = await transport_1.qlikApiRequest.call(context, 'GET', endpoint, undefined, qs);
55
+ const pageItems = extractPageItems(response);
56
+ collected.push(...pageItems);
57
+ cursor = extractNextCursor(response);
58
+ } while (cursor && (limit === undefined || collected.length < limit));
59
+ return limit === undefined ? collected : collected.slice(0, limit);
60
+ }
10
61
  async function handleAppsResource(operation, context) {
11
62
  if (operation === 'getAll') {
12
63
  const returnAll = context.getNodeParameter('returnAll', 0);
13
64
  const limit = returnAll ? undefined : context.getNodeParameter('limit', 0);
14
65
  const options = context.getNodeParameter('options', 0);
15
66
  const qs = {};
16
- if (limit) {
17
- qs.limit = limit;
18
- }
19
67
  if (options.name) {
20
68
  qs.name = options.name;
21
69
  }
22
70
  if (options.spaceId) {
23
71
  qs.spaceId = options.spaceId;
24
72
  }
25
- const response = await transport_1.qlikApiRequest.call(context, 'GET', '/api/v1/apps', undefined, qs);
26
- return response.data || response;
73
+ return await fetchPaginatedResults(context, '/api/v1/apps', qs, limit);
27
74
  }
28
75
  if (operation === 'get') {
29
76
  const appId = context.getNodeParameter('appId', 0);
@@ -216,6 +263,13 @@ async function handleAssistantsResource(operation, context) {
216
263
  }
217
264
  return await transport_1.qlikApiRequest.call(context, 'POST', `/api/v1/assistants/${assistantId}/threads/${threadId}/actions/stream`, body);
218
265
  }
266
+ if (operation === 'invokeThread') {
267
+ const assistantId = context.getNodeParameter('assistantId', 0);
268
+ const threadId = context.getNodeParameter('threadId', 0);
269
+ const message = context.getNodeParameter('message', 0);
270
+ const body = { message };
271
+ return await transport_1.qlikApiRequest.call(context, 'POST', `/api/v1/assistants/${assistantId}/threads/${threadId}/actions/invoke`, body);
272
+ }
219
273
  if (operation === 'delete') {
220
274
  const assistantId = context.getNodeParameter('assistantId', 0);
221
275
  await transport_1.qlikApiRequest.call(context, 'DELETE', `/api/v1/assistants/${assistantId}`);
@@ -229,9 +283,6 @@ async function handleAuditsResource(operation, context) {
229
283
  const limit = returnAll ? undefined : context.getNodeParameter('limit', 0);
230
284
  const options = context.getNodeParameter('options', 0);
231
285
  const qs = {};
232
- if (limit) {
233
- qs.limit = limit;
234
- }
235
286
  if (options.eventType) {
236
287
  qs.eventType = options.eventType;
237
288
  }
@@ -247,8 +298,7 @@ async function handleAuditsResource(operation, context) {
247
298
  if (options.sort) {
248
299
  qs.sort = options.sort;
249
300
  }
250
- const response = await transport_1.qlikApiRequest.call(context, 'GET', '/api/v1/audits', undefined, qs);
251
- return response.data || response;
301
+ return await fetchPaginatedResults(context, '/api/v1/audits', qs, limit);
252
302
  }
253
303
  if (operation === 'get') {
254
304
  const auditId = context.getNodeParameter('auditId', 0);
@@ -274,9 +324,6 @@ async function handleItemsResource(operation, context) {
274
324
  const limit = returnAll ? undefined : context.getNodeParameter('limit', 0);
275
325
  const options = context.getNodeParameter('options', 0);
276
326
  const qs = {};
277
- if (limit) {
278
- qs.limit = limit;
279
- }
280
327
  if (options.name) {
281
328
  qs.name = options.name;
282
329
  }
@@ -292,8 +339,7 @@ async function handleItemsResource(operation, context) {
292
339
  if (options.sort) {
293
340
  qs.sort = options.sort;
294
341
  }
295
- const response = await transport_1.qlikApiRequest.call(context, 'GET', '/api/v1/items', undefined, qs);
296
- return response.data || response;
342
+ return await fetchPaginatedResults(context, '/api/v1/items', qs, limit);
297
343
  }
298
344
  if (operation === 'get') {
299
345
  const itemId = context.getNodeParameter('itemId', 0);
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" id="Layer_1" viewBox="0 0 71.39 29.74"><defs><style>.cls-2{stroke-width:0;fill:#54565a}</style></defs><path d="M70.47 27.52c.05-.07.07-.15.07-.24s-.02-.17-.07-.24-.1-.11-.16-.14a.38.38 0 0 0-.17-.04h-.66v1.31h.29v-.44h.29l.23.44h.32l-.27-.51a.47.47 0 0 0 .15-.13Zm-.71-.45h.27s.07.01.12.04c.04.03.07.08.07.16s-.02.14-.07.18c-.05.04-.09.06-.13.06h-.27v-.44Z" class="cls-2"/><path d="M71.28 26.97c-.07-.17-.17-.32-.31-.45-.13-.13-.28-.23-.45-.31s-.36-.11-.55-.11-.38.04-.55.11-.32.18-.45.31-.23.28-.3.45c-.07.17-.11.36-.11.55s.04.38.11.55c.07.17.17.32.3.45.13.13.28.23.45.31s.36.11.55.11.38-.04.55-.11.32-.18.45-.31.23-.28.31-.45c.07-.17.11-.36.11-.55s-.04-.38-.11-.55Zm-.37 1.1c-.1.16-.23.29-.39.39-.16.1-.34.15-.54.15s-.38-.05-.54-.15c-.16-.1-.29-.23-.39-.39-.1-.16-.15-.34-.15-.54s.05-.38.15-.54c.1-.16.23-.29.39-.39.16-.1.34-.15.54-.15s.38.05.54.15c.16.1.29.23.39.39.1.16.15.34.15.54s-.05.38-.15.54ZM34.9.65h2.96v28.32H34.9zM42.95 9.19h2.95v19.78h-2.95z" class="cls-2"/><circle cx="44.45" cy="2.67" r="2.02" class="cls-2"/><path d="M66.62 9.19h-4.11l-8.52 7.46-.02-16h-2.94v28.32h2.94v-9.14l8.75 9.14h4.1L56.39 18.39l10.23-9.2zM14.87 26.69c-6.53 0-11.82-5.29-11.82-11.82 0-2.77.96-5.32 2.55-7.34L3.44 5.34A14.922 14.922 0 0 0 0 14.87c0 8.21 6.66 14.87 14.87 14.87 3.56 0 6.83-1.25 9.39-3.34l-2.16-2.19c-2 1.55-4.5 2.47-7.23 2.47Z" class="cls-2"/><path fill="#009445" stroke-width="0" d="m26.31 24.34.17-.17a14.83 14.83 0 0 0 3.26-9.3C29.74 6.66 23.08 0 14.87 0c-3.54 0-6.79 1.24-9.34 3.3-.74.6-1.41 1.26-2.03 1.98-.02.02-.03.04-.05.06l2.16 2.19s.04-.05.06-.07c.59-.74 1.27-1.4 2.02-1.97a11.74 11.74 0 0 1 7.18-2.44c6.53 0 11.82 5.29 11.82 11.82 0 2.75-.94 5.29-2.52 7.3-.57.73-1.23 1.39-1.95 1.96l-.12.09 2.16 2.19 2.54 2.57h4.1l-4.59-4.64Z"/></svg>
@@ -12,4 +12,5 @@ export declare const assistantsCreateStarterDescription: INodeProperties[];
12
12
  export declare const assistantsListThreadsDescription: INodeProperties[];
13
13
  export declare const assistantsCreateThreadDescription: INodeProperties[];
14
14
  export declare const assistantsStreamThreadDescription: INodeProperties[];
15
+ export declare const assistantsInvokeThreadDescription: INodeProperties[];
15
16
  export declare const assistantDescription: INodeProperties[];
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.assistantDescription = exports.assistantsStreamThreadDescription = exports.assistantsCreateThreadDescription = exports.assistantsListThreadsDescription = exports.assistantsCreateStarterDescription = exports.assistantsListStartersDescription = exports.assistantsDeleteDescription = exports.assistantsBulkSearchSourcesDescription = exports.assistantsGetFeedbackDescription = exports.assistantsSearchDescription = exports.assistantsUpdateDescription = exports.assistantsCreateDescription = exports.assistantsGetDescription = exports.assistantsGetAllDescription = void 0;
3
+ exports.assistantDescription = exports.assistantsInvokeThreadDescription = exports.assistantsStreamThreadDescription = exports.assistantsCreateThreadDescription = exports.assistantsListThreadsDescription = exports.assistantsCreateStarterDescription = exports.assistantsListStartersDescription = exports.assistantsDeleteDescription = exports.assistantsBulkSearchSourcesDescription = exports.assistantsGetFeedbackDescription = exports.assistantsSearchDescription = exports.assistantsUpdateDescription = exports.assistantsCreateDescription = exports.assistantsGetDescription = exports.assistantsGetAllDescription = void 0;
4
4
  // Get All Assistants
5
5
  exports.assistantsGetAllDescription = [
6
6
  {
@@ -577,6 +577,51 @@ exports.assistantsStreamThreadDescription = [
577
577
  ],
578
578
  },
579
579
  ];
580
+ // Invoke Thread
581
+ exports.assistantsInvokeThreadDescription = [
582
+ {
583
+ displayName: 'Assistant ID',
584
+ name: 'assistantId',
585
+ type: 'string',
586
+ default: '',
587
+ required: true,
588
+ displayOptions: {
589
+ show: {
590
+ resource: ['assistants'],
591
+ operation: ['invokeThread'],
592
+ },
593
+ },
594
+ description: 'The ID of the assistant',
595
+ },
596
+ {
597
+ displayName: 'Thread ID',
598
+ name: 'threadId',
599
+ type: 'string',
600
+ default: '',
601
+ required: true,
602
+ displayOptions: {
603
+ show: {
604
+ resource: ['assistants'],
605
+ operation: ['invokeThread'],
606
+ },
607
+ },
608
+ description: 'The ID of the thread to invoke',
609
+ },
610
+ {
611
+ displayName: 'Message',
612
+ name: 'message',
613
+ type: 'string',
614
+ default: '',
615
+ required: true,
616
+ displayOptions: {
617
+ show: {
618
+ resource: ['assistants'],
619
+ operation: ['invokeThread'],
620
+ },
621
+ },
622
+ description: 'The message to send to the thread',
623
+ },
624
+ ];
580
625
  exports.assistantDescription = [
581
626
  {
582
627
  displayName: 'Operation',
@@ -661,6 +706,12 @@ exports.assistantDescription = [
661
706
  action: 'Stream thread conversation',
662
707
  description: 'Stream messages to a thread with real-time responses',
663
708
  },
709
+ {
710
+ name: 'Invoke Thread',
711
+ value: 'invokeThread',
712
+ action: 'Invoke thread action',
713
+ description: 'Send a message to a thread and get a response',
714
+ },
664
715
  {
665
716
  name: 'Delete',
666
717
  value: 'delete',
@@ -682,5 +733,6 @@ exports.assistantDescription = [
682
733
  ...exports.assistantsListThreadsDescription,
683
734
  ...exports.assistantsCreateThreadDescription,
684
735
  ...exports.assistantsStreamThreadDescription,
736
+ ...exports.assistantsInvokeThreadDescription,
685
737
  ...exports.assistantsDeleteDescription,
686
738
  ];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "n8n-nodes-qlik-cloud",
3
- "version": "1.0.5",
3
+ "version": "1.0.7",
4
4
  "description": "n8n node for Qlik Cloud REST APIs integration",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -12,16 +12,87 @@ import { assistantDescription } from './resources/assistants/index';
12
12
  import { auditDescription } from './resources/audits/index';
13
13
  import { itemDescription } from './resources/items/index';
14
14
 
15
+ const DEFAULT_PAGE_SIZE = 100;
16
+
17
+ function extractPageItems(response: any): any[] {
18
+ const payload = response?.data ?? response;
19
+
20
+ if (Array.isArray(payload)) {
21
+ return payload;
22
+ }
23
+
24
+ if (payload === undefined || payload === null) {
25
+ return [];
26
+ }
27
+
28
+ return [payload];
29
+ }
30
+
31
+ function extractNextCursor(response: any): string | undefined {
32
+ const nextLink = response?.links?.next;
33
+ const href = typeof nextLink === 'string' ? nextLink : nextLink?.href;
34
+
35
+ if (typeof href === 'string' && href.length) {
36
+ try {
37
+ const url = href.startsWith('http') ? new URL(href) : new URL(href, 'https://qlik.cloud');
38
+ return url.searchParams.get('next') ?? undefined;
39
+ } catch {
40
+ return undefined;
41
+ }
42
+ }
43
+
44
+ if (typeof response?.next === 'string') {
45
+ return response.next;
46
+ }
47
+
48
+ if (typeof response?.meta?.next === 'string') {
49
+ return response.meta.next;
50
+ }
51
+
52
+ return undefined;
53
+ }
54
+
55
+ async function fetchPaginatedResults(
56
+ context: IExecuteFunctions,
57
+ endpoint: string,
58
+ filters: Record<string, any>,
59
+ limit?: number,
60
+ pageSize: number = DEFAULT_PAGE_SIZE,
61
+ ): Promise<any[]> {
62
+ const collected: any[] = [];
63
+ let cursor: string | undefined;
64
+
65
+ do {
66
+ const qs: Record<string, any> = { ...filters };
67
+ const chunkSize = limit === undefined ? pageSize : Math.min(pageSize, limit - collected.length);
68
+
69
+ if (chunkSize <= 0) {
70
+ break;
71
+ }
72
+
73
+ qs.limit = chunkSize;
74
+
75
+ if (cursor) {
76
+ qs.next = cursor;
77
+ }
78
+
79
+ const response = await qlikApiRequest.call(context, 'GET', endpoint, undefined, qs);
80
+ const pageItems = extractPageItems(response);
81
+ collected.push(...pageItems);
82
+
83
+ cursor = extractNextCursor(response);
84
+ } while (cursor && (limit === undefined || collected.length < limit));
85
+
86
+ return limit === undefined ? collected : collected.slice(0, limit);
87
+ }
88
+
15
89
  async function handleAppsResource(operation: string, context: IExecuteFunctions): Promise<any> {
16
90
  if (operation === 'getAll') {
17
91
  const returnAll = context.getNodeParameter('returnAll', 0) as boolean;
18
92
  const limit = returnAll ? undefined : (context.getNodeParameter('limit', 0) as number);
19
93
  const options = context.getNodeParameter('options', 0) as any;
20
94
 
21
- const qs: any = {};
22
- if (limit) {
23
- qs.limit = limit;
24
- }
95
+ const qs: Record<string, any> = {};
25
96
  if (options.name) {
26
97
  qs.name = options.name;
27
98
  }
@@ -29,8 +100,7 @@ async function handleAppsResource(operation: string, context: IExecuteFunctions)
29
100
  qs.spaceId = options.spaceId;
30
101
  }
31
102
 
32
- const response = await qlikApiRequest.call(context, 'GET', '/api/v1/apps', undefined, qs);
33
- return response.data || response;
103
+ return await fetchPaginatedResults(context, '/api/v1/apps', qs, limit);
34
104
  }
35
105
 
36
106
  if (operation === 'get') {
@@ -301,6 +371,21 @@ async function handleAssistantsResource(operation: string, context: IExecuteFunc
301
371
  );
302
372
  }
303
373
 
374
+ if (operation === 'invokeThread') {
375
+ const assistantId = context.getNodeParameter('assistantId', 0) as string;
376
+ const threadId = context.getNodeParameter('threadId', 0) as string;
377
+ const message = context.getNodeParameter('message', 0) as string;
378
+
379
+ const body = { message };
380
+
381
+ return await qlikApiRequest.call(
382
+ context,
383
+ 'POST',
384
+ `/api/v1/assistants/${assistantId}/threads/${threadId}/actions/invoke`,
385
+ body,
386
+ );
387
+ }
388
+
304
389
  if (operation === 'delete') {
305
390
  const assistantId = context.getNodeParameter('assistantId', 0) as string;
306
391
  await qlikApiRequest.call(context, 'DELETE', `/api/v1/assistants/${assistantId}`);
@@ -316,10 +401,7 @@ async function handleAuditsResource(operation: string, context: IExecuteFunction
316
401
  const limit = returnAll ? undefined : (context.getNodeParameter('limit', 0) as number);
317
402
  const options = context.getNodeParameter('options', 0) as any;
318
403
 
319
- const qs: any = {};
320
- if (limit) {
321
- qs.limit = limit;
322
- }
404
+ const qs: Record<string, any> = {};
323
405
  if (options.eventType) {
324
406
  qs.eventType = options.eventType;
325
407
  }
@@ -336,8 +418,7 @@ async function handleAuditsResource(operation: string, context: IExecuteFunction
336
418
  qs.sort = options.sort;
337
419
  }
338
420
 
339
- const response = await qlikApiRequest.call(context, 'GET', '/api/v1/audits', undefined, qs);
340
- return response.data || response;
421
+ return await fetchPaginatedResults(context, '/api/v1/audits', qs, limit);
341
422
  }
342
423
 
343
424
  if (operation === 'get') {
@@ -369,10 +450,7 @@ async function handleItemsResource(operation: string, context: IExecuteFunctions
369
450
  const limit = returnAll ? undefined : (context.getNodeParameter('limit', 0) as number);
370
451
  const options = context.getNodeParameter('options', 0) as any;
371
452
 
372
- const qs: any = {};
373
- if (limit) {
374
- qs.limit = limit;
375
- }
453
+ const qs: Record<string, any> = {};
376
454
  if (options.name) {
377
455
  qs.name = options.name;
378
456
  }
@@ -389,8 +467,7 @@ async function handleItemsResource(operation: string, context: IExecuteFunctions
389
467
  qs.sort = options.sort;
390
468
  }
391
469
 
392
- const response = await qlikApiRequest.call(context, 'GET', '/api/v1/items', undefined, qs);
393
- return response.data || response;
470
+ return await fetchPaginatedResults(context, '/api/v1/items', qs, limit);
394
471
  }
395
472
 
396
473
  if (operation === 'get') {
@@ -589,6 +589,52 @@ export const assistantsStreamThreadDescription: INodeProperties[] = [
589
589
  },
590
590
  ];
591
591
 
592
+ // Invoke Thread
593
+ export const assistantsInvokeThreadDescription: INodeProperties[] = [
594
+ {
595
+ displayName: 'Assistant ID',
596
+ name: 'assistantId',
597
+ type: 'string',
598
+ default: '',
599
+ required: true,
600
+ displayOptions: {
601
+ show: {
602
+ resource: ['assistants'],
603
+ operation: ['invokeThread'],
604
+ },
605
+ },
606
+ description: 'The ID of the assistant',
607
+ },
608
+ {
609
+ displayName: 'Thread ID',
610
+ name: 'threadId',
611
+ type: 'string',
612
+ default: '',
613
+ required: true,
614
+ displayOptions: {
615
+ show: {
616
+ resource: ['assistants'],
617
+ operation: ['invokeThread'],
618
+ },
619
+ },
620
+ description: 'The ID of the thread to invoke',
621
+ },
622
+ {
623
+ displayName: 'Message',
624
+ name: 'message',
625
+ type: 'string',
626
+ default: '',
627
+ required: true,
628
+ displayOptions: {
629
+ show: {
630
+ resource: ['assistants'],
631
+ operation: ['invokeThread'],
632
+ },
633
+ },
634
+ description: 'The message to send to the thread',
635
+ },
636
+ ];
637
+
592
638
  export const assistantDescription: INodeProperties[] = [
593
639
  {
594
640
  displayName: 'Operation',
@@ -673,6 +719,12 @@ export const assistantDescription: INodeProperties[] = [
673
719
  action: 'Stream thread conversation',
674
720
  description: 'Stream messages to a thread with real-time responses',
675
721
  },
722
+ {
723
+ name: 'Invoke Thread',
724
+ value: 'invokeThread',
725
+ action: 'Invoke thread action',
726
+ description: 'Send a message to a thread and get a response',
727
+ },
676
728
  {
677
729
  name: 'Delete',
678
730
  value: 'delete',
@@ -694,5 +746,6 @@ export const assistantDescription: INodeProperties[] = [
694
746
  ...assistantsListThreadsDescription,
695
747
  ...assistantsCreateThreadDescription,
696
748
  ...assistantsStreamThreadDescription,
749
+ ...assistantsInvokeThreadDescription,
697
750
  ...assistantsDeleteDescription,
698
751
  ];