@trg-admin/n8n-nodes-zoho-desk 0.1.17 → 0.1.20

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.
@@ -85,17 +85,25 @@ async function zohoDeskApiRequest(method, endpoint, body = {}, qs = {}, itemInde
85
85
  async function zohoDeskApiRequestAllItems(method, endpoint, body = {}, qs = {}, itemIndex = 0) {
86
86
  const returnData = [];
87
87
  let responseData;
88
- qs.limit = 100; // Zoho Desk max limit per page
89
- qs.from = 0;
88
+ const query = {
89
+ ...qs,
90
+ limit: qs.limit || 100,
91
+ from: qs.from || 0,
92
+ };
90
93
  do {
91
- responseData = await zohoDeskApiRequest.call(this, method, endpoint, body, qs, itemIndex);
92
- if (responseData.data && Array.isArray(responseData.data)) {
93
- returnData.push(...responseData.data);
94
- if (responseData.data.length < 100) {
94
+ responseData = await zohoDeskApiRequest.call(this, method, endpoint, body, query, itemIndex);
95
+ const items = Array.isArray(responseData?.data)
96
+ ? responseData.data
97
+ : Array.isArray(responseData)
98
+ ? responseData
99
+ : [];
100
+ if (items.length > 0) {
101
+ returnData.push(...items);
102
+ if (items.length < query.limit) {
95
103
  // Last page
96
104
  break;
97
105
  }
98
- qs.from = qs.from + 100;
106
+ query.from = query.from + items.length;
99
107
  }
100
108
  else {
101
109
  break;
@@ -7,6 +7,10 @@ export declare class ZohoDesk implements INodeType {
7
7
  name: string;
8
8
  value: string;
9
9
  }[]>;
10
+ getChannels(this: ILoadOptionsFunctions): Promise<{
11
+ name: string;
12
+ value: string;
13
+ }[]>;
10
14
  };
11
15
  };
12
16
  execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]>;
@@ -154,6 +154,17 @@ class ZohoDesk {
154
154
  default: '',
155
155
  description: 'Agent ID to assign the ticket to',
156
156
  },
157
+ {
158
+ displayName: 'Channel',
159
+ name: 'channel',
160
+ type: 'options',
161
+ typeOptions: {
162
+ loadOptionsMethod: 'getChannels',
163
+ loadOptionsDependsOn: ['organizationId'],
164
+ },
165
+ default: 'Email',
166
+ description: 'Channel through which the ticket was received or created. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code-examples/expressions/">expression</a>.',
167
+ },
157
168
  {
158
169
  displayName: 'Contact ID',
159
170
  name: 'contactId',
@@ -448,13 +459,9 @@ class ZohoDesk {
448
459
  loadOptions: {
449
460
  async getDepartments() {
450
461
  try {
451
- const responseData = await GenericFunctions_1.zohoDeskApiRequest.call(this, 'GET', '/departments', {}, {}, 0);
452
- const departments = Array.isArray(responseData.data)
453
- ? responseData.data
454
- : Array.isArray(responseData)
455
- ? responseData
456
- : [];
457
- return departments.map((department) => {
462
+ const departments = (await GenericFunctions_1.zohoDeskApiRequestAllItems.call(this, 'GET', '/departments', {}, {}, 0));
463
+ return departments
464
+ .map((department) => {
458
465
  const name = department.name ||
459
466
  department.departmentName ||
460
467
  String(department.id);
@@ -462,12 +469,98 @@ class ZohoDesk {
462
469
  name,
463
470
  value: department.id,
464
471
  };
465
- });
472
+ })
473
+ .sort((a, b) => a.name.localeCompare(b.name));
466
474
  }
467
475
  catch (error) {
468
476
  throw new Error(`Failed to load departments. Error: ${error.message}. Ensure Organization ID is correct and Desk API Base URL matches your region.`);
469
477
  }
470
478
  },
479
+ async getChannels() {
480
+ const fallbackChannels = [
481
+ { name: 'Chat', value: 'Chat' },
482
+ { name: 'Email', value: 'Email' },
483
+ { name: 'Facebook', value: 'Facebook' },
484
+ { name: 'Feedback Widget', value: 'Feedback Widget' },
485
+ { name: 'Forums', value: 'Forums' },
486
+ { name: 'Help Center', value: 'Help Center' },
487
+ { name: 'Phone', value: 'Phone' },
488
+ { name: 'Twitter', value: 'Twitter' },
489
+ { name: 'Web', value: 'Web' },
490
+ ];
491
+ try {
492
+ const responseData = await GenericFunctions_1.zohoDeskApiRequest.call(this, 'GET', '/channels', {}, {}, 0);
493
+ const channels = Array.isArray(responseData.data)
494
+ ? responseData.data
495
+ : Array.isArray(responseData)
496
+ ? responseData
497
+ : [];
498
+ if (channels.length > 0) {
499
+ const channelMap = new Map();
500
+ for (const c of channels) {
501
+ const name = c.channelName ||
502
+ c.name ||
503
+ c.channelCode ||
504
+ String(c.id);
505
+ const value = c.channelCode ||
506
+ c.name ||
507
+ c.channelName ||
508
+ String(c.id);
509
+ if (value) {
510
+ channelMap.set(value, name || value);
511
+ }
512
+ }
513
+ for (const fb of fallbackChannels) {
514
+ if (!channelMap.has(fb.value)) {
515
+ channelMap.set(fb.value, fb.name);
516
+ }
517
+ }
518
+ return Array.from(channelMap.entries())
519
+ .map(([value, name]) => ({ name, value }))
520
+ .sort((a, b) => a.name.localeCompare(b.name));
521
+ }
522
+ }
523
+ catch {
524
+ try {
525
+ const fieldsResponse = await GenericFunctions_1.zohoDeskApiRequest.call(this, 'GET', '/organizationFields', {}, { module: 'tickets' }, 0);
526
+ const fields = Array.isArray(fieldsResponse.data)
527
+ ? fieldsResponse.data
528
+ : Array.isArray(fieldsResponse)
529
+ ? fieldsResponse
530
+ : [];
531
+ const channelField = fields.find((f) => String(f.apiName || '').toLowerCase() === 'channel');
532
+ if (channelField && Array.isArray(channelField.allowedValues)) {
533
+ const channelMap = new Map();
534
+ for (const val of channelField.allowedValues) {
535
+ if (typeof val === 'string') {
536
+ channelMap.set(val, val);
537
+ }
538
+ else if (val && typeof val === 'object') {
539
+ const name = val.displayValue ||
540
+ val.label ||
541
+ val.value;
542
+ const value = val.value || val.displayValue;
543
+ if (value) {
544
+ channelMap.set(value, name || value);
545
+ }
546
+ }
547
+ }
548
+ for (const fb of fallbackChannels) {
549
+ if (!channelMap.has(fb.value)) {
550
+ channelMap.set(fb.value, fb.name);
551
+ }
552
+ }
553
+ return Array.from(channelMap.entries())
554
+ .map(([value, name]) => ({ name, value }))
555
+ .sort((a, b) => a.name.localeCompare(b.name));
556
+ }
557
+ }
558
+ catch {
559
+ // Fall back to predefined list
560
+ }
561
+ }
562
+ return fallbackChannels;
563
+ },
471
564
  },
472
565
  };
473
566
  }
@@ -27,7 +27,7 @@ function getDeskBaseUrl(tokenUrl = '', apiDomain = '', overrideBaseUrl = '') {
27
27
  return 'https://desk.zoho.com.cn';
28
28
  return 'https://desk.zoho.com';
29
29
  }
30
- async function deskRequest(context, method, endpoint, organizationId, body = {}) {
30
+ async function deskRequest(context, method, endpoint, organizationId, body = {}, qs = {}) {
31
31
  const credentials = (await context.getCredentials('zohoCRMDeskOAuth2Api'));
32
32
  const { oauthTokenData, accessTokenUrl, authUrl, deskBaseUrl } = credentials;
33
33
  const baseUrl = getDeskBaseUrl(accessTokenUrl || authUrl || '', oauthTokenData?.api_domain || '', deskBaseUrl || '');
@@ -40,6 +40,9 @@ async function deskRequest(context, method, endpoint, organizationId, body = {})
40
40
  },
41
41
  json: true,
42
42
  };
43
+ if (Object.keys(qs).length) {
44
+ options.qs = qs;
45
+ }
43
46
  if (method !== 'GET' && method !== 'DELETE' && Object.keys(body).length) {
44
47
  options.body = body;
45
48
  }
@@ -50,6 +53,34 @@ async function deskRequest(context, method, endpoint, organizationId, body = {})
50
53
  throw new n8n_workflow_1.NodeApiError(context.getNode(), error);
51
54
  }
52
55
  }
56
+ async function deskRequestAllItems(context, method, endpoint, organizationId, body = {}, qs = {}) {
57
+ const returnData = [];
58
+ let responseData;
59
+ const query = {
60
+ ...qs,
61
+ limit: qs.limit || 100,
62
+ from: qs.from || 0,
63
+ };
64
+ do {
65
+ responseData = await deskRequest(context, method, endpoint, organizationId, body, query);
66
+ const items = Array.isArray(responseData?.data)
67
+ ? responseData.data
68
+ : Array.isArray(responseData)
69
+ ? responseData
70
+ : [];
71
+ if (items.length > 0) {
72
+ returnData.push(...items);
73
+ if (items.length < query.limit) {
74
+ break;
75
+ }
76
+ query.from = query.from + items.length;
77
+ }
78
+ else {
79
+ break;
80
+ }
81
+ } while (true);
82
+ return returnData;
83
+ }
53
84
  async function deskWebhookRequest(context, method, endpoint, body = {}) {
54
85
  const organizationId = context.getNodeParameter('organizationId');
55
86
  return deskRequest(context, method, endpoint, organizationId, body);
@@ -208,16 +239,13 @@ class ZohoDeskTrigger {
208
239
  async getDepartments() {
209
240
  const organizationId = this.getNodeParameter('organizationId', 0, '');
210
241
  try {
211
- const responseData = await deskRequest(this, 'GET', '/departments', organizationId);
212
- const departments = Array.isArray(responseData.data)
213
- ? responseData.data
214
- : Array.isArray(responseData)
215
- ? responseData
216
- : [];
217
- return departments.map((dept) => ({
242
+ const departments = await deskRequestAllItems(this, 'GET', '/departments', organizationId);
243
+ return departments
244
+ .map((dept) => ({
218
245
  name: dept.name || dept.departmentName || String(dept.id),
219
246
  value: dept.id,
220
- }));
247
+ }))
248
+ .sort((a, b) => a.name.localeCompare(b.name));
221
249
  }
222
250
  catch (error) {
223
251
  throw new Error(`Failed to load departments: ${error.message}. Ensure Organization ID is set correctly.`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@trg-admin/n8n-nodes-zoho-desk",
3
- "version": "0.1.17",
3
+ "version": "0.1.20",
4
4
  "description": "Community n8n node starter for Zoho Desk using built-in Zoho OAuth2 auth behavior",
5
5
  "license": "MIT",
6
6
  "type": "commonjs",