@trg-admin/n8n-nodes-zoho-desk 0.1.14 → 0.1.16
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.
|
@@ -4,3 +4,7 @@ export declare function zohoDeskApiRequest(this: IExecuteFunctions | ILoadOption
|
|
|
4
4
|
* Make an API request to paginate through all items
|
|
5
5
|
*/
|
|
6
6
|
export declare function zohoDeskApiRequestAllItems(this: IExecuteFunctions | ILoadOptionsFunctions, method: IHttpRequestMethods, endpoint: string, body?: IDataObject, qs?: IDataObject, itemIndex?: number): Promise<any>;
|
|
7
|
+
/**
|
|
8
|
+
* Helper to look up a contact by email address in Zoho Desk
|
|
9
|
+
*/
|
|
10
|
+
export declare function getContactByEmail(this: IExecuteFunctions | ILoadOptionsFunctions, email: string, itemIndex?: number): Promise<IDataObject>;
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.zohoDeskApiRequest = zohoDeskApiRequest;
|
|
4
4
|
exports.zohoDeskApiRequestAllItems = zohoDeskApiRequestAllItems;
|
|
5
|
+
exports.getContactByEmail = getContactByEmail;
|
|
5
6
|
const n8n_workflow_1 = require("n8n-workflow");
|
|
6
7
|
function getDeskBaseUrl(tokenUrl = '', apiDomain = '', overrideBaseUrl = '') {
|
|
7
8
|
if (overrideBaseUrl) {
|
|
@@ -102,3 +103,23 @@ async function zohoDeskApiRequestAllItems(method, endpoint, body = {}, qs = {},
|
|
|
102
103
|
} while (true);
|
|
103
104
|
return returnData;
|
|
104
105
|
}
|
|
106
|
+
/**
|
|
107
|
+
* Helper to look up a contact by email address in Zoho Desk
|
|
108
|
+
*/
|
|
109
|
+
async function getContactByEmail(email, itemIndex = 0) {
|
|
110
|
+
const trimmedEmail = email.trim();
|
|
111
|
+
if (!trimmedEmail) {
|
|
112
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), new Error('Contact Email cannot be empty'), { itemIndex });
|
|
113
|
+
}
|
|
114
|
+
const responseData = await zohoDeskApiRequest.call(this, 'GET', '/contacts/search', {}, { email: trimmedEmail, searchStr: trimmedEmail }, itemIndex);
|
|
115
|
+
const contacts = Array.isArray(responseData.data)
|
|
116
|
+
? responseData.data
|
|
117
|
+
: Array.isArray(responseData)
|
|
118
|
+
? responseData
|
|
119
|
+
: [];
|
|
120
|
+
if (contacts.length === 0) {
|
|
121
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), new Error(`No contact found with email "${trimmedEmail}" in Zoho Desk`), { itemIndex });
|
|
122
|
+
}
|
|
123
|
+
const exactMatch = contacts.find((c) => String(c.email || '').toLowerCase() === trimmedEmail.toLowerCase());
|
|
124
|
+
return exactMatch || contacts[0];
|
|
125
|
+
}
|
|
@@ -159,7 +159,7 @@ class ZohoDesk {
|
|
|
159
159
|
name: 'contactId',
|
|
160
160
|
type: 'string',
|
|
161
161
|
default: '',
|
|
162
|
-
description: 'ID of existing contact (
|
|
162
|
+
description: 'ID of existing contact (or enter email address)',
|
|
163
163
|
},
|
|
164
164
|
{
|
|
165
165
|
displayName: 'Task ID',
|
|
@@ -169,13 +169,38 @@ class ZohoDesk {
|
|
|
169
169
|
description: 'Custom field Task ID (maps to cf.cf_task_id)',
|
|
170
170
|
},
|
|
171
171
|
{
|
|
172
|
-
displayName: 'Custom Fields
|
|
173
|
-
name: '
|
|
174
|
-
type: '
|
|
175
|
-
typeOptions: {
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
172
|
+
displayName: 'Custom Fields',
|
|
173
|
+
name: 'customFieldsList',
|
|
174
|
+
type: 'fixedCollection',
|
|
175
|
+
typeOptions: {
|
|
176
|
+
multipleValues: true,
|
|
177
|
+
},
|
|
178
|
+
default: {},
|
|
179
|
+
placeholder: 'Add Custom Field',
|
|
180
|
+
description: 'Add one or more custom fields by API name and value',
|
|
181
|
+
options: [
|
|
182
|
+
{
|
|
183
|
+
name: 'field',
|
|
184
|
+
displayName: 'Field',
|
|
185
|
+
values: [
|
|
186
|
+
{
|
|
187
|
+
displayName: 'Field API Name',
|
|
188
|
+
name: 'fieldName',
|
|
189
|
+
type: 'string',
|
|
190
|
+
default: '',
|
|
191
|
+
placeholder: 'cf_serial_number',
|
|
192
|
+
description: 'Zoho custom field API name (for example cf_serial_number)',
|
|
193
|
+
},
|
|
194
|
+
{
|
|
195
|
+
displayName: 'Value',
|
|
196
|
+
name: 'fieldValue',
|
|
197
|
+
type: 'string',
|
|
198
|
+
default: '',
|
|
199
|
+
description: 'Value to set for this custom field',
|
|
200
|
+
},
|
|
201
|
+
],
|
|
202
|
+
},
|
|
203
|
+
],
|
|
179
204
|
},
|
|
180
205
|
{
|
|
181
206
|
displayName: 'Due Date',
|
|
@@ -223,14 +248,61 @@ class ZohoDesk {
|
|
|
223
248
|
description: 'Whether to return all results or limit to specified number',
|
|
224
249
|
},
|
|
225
250
|
// ================== Contact Resource Parameters ==================
|
|
251
|
+
{
|
|
252
|
+
displayName: 'By',
|
|
253
|
+
name: 'by',
|
|
254
|
+
type: 'options',
|
|
255
|
+
options: [
|
|
256
|
+
{
|
|
257
|
+
name: 'Contact ID',
|
|
258
|
+
value: 'id',
|
|
259
|
+
description: 'Identify contact by Zoho Desk Contact ID',
|
|
260
|
+
},
|
|
261
|
+
{
|
|
262
|
+
name: 'Email',
|
|
263
|
+
value: 'email',
|
|
264
|
+
description: 'Identify contact by Email Address',
|
|
265
|
+
},
|
|
266
|
+
],
|
|
267
|
+
default: 'id',
|
|
268
|
+
displayOptions: {
|
|
269
|
+
show: {
|
|
270
|
+
resource: ['contact'],
|
|
271
|
+
operation: ['get', 'update'],
|
|
272
|
+
},
|
|
273
|
+
},
|
|
274
|
+
description: 'How to identify the contact',
|
|
275
|
+
},
|
|
226
276
|
{
|
|
227
277
|
displayName: 'Contact ID',
|
|
228
278
|
name: 'contactId',
|
|
229
279
|
type: 'string',
|
|
230
280
|
default: '',
|
|
231
281
|
required: true,
|
|
232
|
-
displayOptions: {
|
|
233
|
-
|
|
282
|
+
displayOptions: {
|
|
283
|
+
show: {
|
|
284
|
+
resource: ['contact'],
|
|
285
|
+
operation: ['get', 'update'],
|
|
286
|
+
by: ['id'],
|
|
287
|
+
},
|
|
288
|
+
},
|
|
289
|
+
description: 'The unique ID of the contact. You can also enter an email address here.',
|
|
290
|
+
},
|
|
291
|
+
{
|
|
292
|
+
displayName: 'Contact Email',
|
|
293
|
+
name: 'contactEmail',
|
|
294
|
+
type: 'string',
|
|
295
|
+
placeholder: 'e.g. name@example.com',
|
|
296
|
+
default: '',
|
|
297
|
+
required: true,
|
|
298
|
+
displayOptions: {
|
|
299
|
+
show: {
|
|
300
|
+
resource: ['contact'],
|
|
301
|
+
operation: ['get', 'update'],
|
|
302
|
+
by: ['email'],
|
|
303
|
+
},
|
|
304
|
+
},
|
|
305
|
+
description: 'Email address of the contact',
|
|
234
306
|
},
|
|
235
307
|
{
|
|
236
308
|
displayName: 'Last Name',
|
|
@@ -402,7 +474,7 @@ class ZohoDesk {
|
|
|
402
474
|
async execute() {
|
|
403
475
|
const items = this.getInputData();
|
|
404
476
|
const returnData = [];
|
|
405
|
-
const
|
|
477
|
+
const parseLegacyCustomFieldsJson = (raw, itemIndex) => {
|
|
406
478
|
if (!raw)
|
|
407
479
|
return {};
|
|
408
480
|
if (typeof raw === 'object' && raw !== null) {
|
|
@@ -425,6 +497,23 @@ class ZohoDesk {
|
|
|
425
497
|
throw new n8n_workflow_1.NodeOperationError(this.getNode(), new Error(`Invalid Custom Fields (JSON): ${error.message}`), { itemIndex });
|
|
426
498
|
}
|
|
427
499
|
};
|
|
500
|
+
const parseCustomFieldEntries = (raw) => {
|
|
501
|
+
if (!raw || typeof raw !== 'object') {
|
|
502
|
+
return {};
|
|
503
|
+
}
|
|
504
|
+
const fieldRows = Array.isArray(raw.field)
|
|
505
|
+
? raw.field
|
|
506
|
+
: [];
|
|
507
|
+
const customFields = {};
|
|
508
|
+
for (const row of fieldRows) {
|
|
509
|
+
const fieldName = String(row.fieldName ?? '').trim();
|
|
510
|
+
if (!fieldName) {
|
|
511
|
+
continue;
|
|
512
|
+
}
|
|
513
|
+
customFields[fieldName] = row.fieldValue ?? '';
|
|
514
|
+
}
|
|
515
|
+
return customFields;
|
|
516
|
+
};
|
|
428
517
|
for (let i = 0; i < items.length; i++) {
|
|
429
518
|
const resource = this.getNodeParameter('resource', i);
|
|
430
519
|
const operation = this.getNodeParameter('operation', i);
|
|
@@ -462,8 +551,9 @@ class ZohoDesk {
|
|
|
462
551
|
const departmentId = this.getNodeParameter('departmentId', i);
|
|
463
552
|
const description = this.getNodeParameter('description', i, '');
|
|
464
553
|
const additionalFields = this.getNodeParameter('additionalFields', i, {});
|
|
465
|
-
const { cfTaskId, customFieldsJson, ...otherAdditionalFields } = additionalFields;
|
|
466
|
-
const
|
|
554
|
+
const { cfTaskId, customFieldsList, customFieldsJson, ...otherAdditionalFields } = additionalFields;
|
|
555
|
+
const legacyCustomFields = parseLegacyCustomFieldsJson(customFieldsJson, i);
|
|
556
|
+
const listCustomFields = parseCustomFieldEntries(customFieldsList);
|
|
467
557
|
const body = {
|
|
468
558
|
subject,
|
|
469
559
|
email,
|
|
@@ -471,7 +561,8 @@ class ZohoDesk {
|
|
|
471
561
|
...otherAdditionalFields,
|
|
472
562
|
};
|
|
473
563
|
const customFields = {
|
|
474
|
-
...
|
|
564
|
+
...legacyCustomFields,
|
|
565
|
+
...listCustomFields,
|
|
475
566
|
};
|
|
476
567
|
if (cfTaskId) {
|
|
477
568
|
customFields.cf_task_id = cfTaskId;
|
|
@@ -482,6 +573,10 @@ class ZohoDesk {
|
|
|
482
573
|
if (description) {
|
|
483
574
|
body.description = description;
|
|
484
575
|
}
|
|
576
|
+
if (body.contactId && typeof body.contactId === 'string' && body.contactId.includes('@')) {
|
|
577
|
+
const contact = await GenericFunctions_1.getContactByEmail.call(this, body.contactId, i);
|
|
578
|
+
body.contactId = contact.id;
|
|
579
|
+
}
|
|
485
580
|
const responseData = await GenericFunctions_1.zohoDeskApiRequest.call(this, 'POST', '/tickets', body, {}, i);
|
|
486
581
|
returnData.push({ json: responseData });
|
|
487
582
|
}
|
|
@@ -489,13 +584,15 @@ class ZohoDesk {
|
|
|
489
584
|
const ticketId = this.getNodeParameter('ticketId', i);
|
|
490
585
|
const description = this.getNodeParameter('description', i, '');
|
|
491
586
|
const additionalFields = this.getNodeParameter('additionalFields', i, {});
|
|
492
|
-
const { cfTaskId, customFieldsJson, ...otherAdditionalFields } = additionalFields;
|
|
493
|
-
const
|
|
587
|
+
const { cfTaskId, customFieldsList, customFieldsJson, ...otherAdditionalFields } = additionalFields;
|
|
588
|
+
const legacyCustomFields = parseLegacyCustomFieldsJson(customFieldsJson, i);
|
|
589
|
+
const listCustomFields = parseCustomFieldEntries(customFieldsList);
|
|
494
590
|
const body = {
|
|
495
591
|
...otherAdditionalFields,
|
|
496
592
|
};
|
|
497
593
|
const customFields = {
|
|
498
|
-
...
|
|
594
|
+
...legacyCustomFields,
|
|
595
|
+
...listCustomFields,
|
|
499
596
|
};
|
|
500
597
|
if (cfTaskId) {
|
|
501
598
|
customFields.cf_task_id = cfTaskId;
|
|
@@ -506,6 +603,10 @@ class ZohoDesk {
|
|
|
506
603
|
if (description) {
|
|
507
604
|
body.description = description;
|
|
508
605
|
}
|
|
606
|
+
if (body.contactId && typeof body.contactId === 'string' && body.contactId.includes('@')) {
|
|
607
|
+
const contact = await GenericFunctions_1.getContactByEmail.call(this, body.contactId, i);
|
|
608
|
+
body.contactId = contact.id;
|
|
609
|
+
}
|
|
509
610
|
const responseData = await GenericFunctions_1.zohoDeskApiRequest.call(this, 'PATCH', `/tickets/${ticketId}`, body, {}, i);
|
|
510
611
|
returnData.push({ json: responseData });
|
|
511
612
|
}
|
|
@@ -513,8 +614,25 @@ class ZohoDesk {
|
|
|
513
614
|
// ==================== CONTACT OPERATIONS ====================
|
|
514
615
|
if (resource === 'contact') {
|
|
515
616
|
if (operation === 'get') {
|
|
516
|
-
const
|
|
517
|
-
|
|
617
|
+
const by = this.getNodeParameter('by', i, 'id');
|
|
618
|
+
let identifier = '';
|
|
619
|
+
let isEmail = by === 'email';
|
|
620
|
+
if (by === 'email') {
|
|
621
|
+
identifier = this.getNodeParameter('contactEmail', i);
|
|
622
|
+
}
|
|
623
|
+
else {
|
|
624
|
+
identifier = this.getNodeParameter('contactId', i);
|
|
625
|
+
if (identifier.includes('@')) {
|
|
626
|
+
isEmail = true;
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
let responseData;
|
|
630
|
+
if (isEmail) {
|
|
631
|
+
responseData = await GenericFunctions_1.getContactByEmail.call(this, identifier, i);
|
|
632
|
+
}
|
|
633
|
+
else {
|
|
634
|
+
responseData = (await GenericFunctions_1.zohoDeskApiRequest.call(this, 'GET', `/contacts/${identifier}`, {}, {}, i));
|
|
635
|
+
}
|
|
518
636
|
returnData.push({ json: responseData });
|
|
519
637
|
}
|
|
520
638
|
if (operation === 'getAll') {
|
|
@@ -549,9 +667,25 @@ class ZohoDesk {
|
|
|
549
667
|
returnData.push({ json: responseData });
|
|
550
668
|
}
|
|
551
669
|
if (operation === 'update') {
|
|
552
|
-
const
|
|
670
|
+
const by = this.getNodeParameter('by', i, 'id');
|
|
671
|
+
let identifier = '';
|
|
672
|
+
let isEmail = by === 'email';
|
|
673
|
+
if (by === 'email') {
|
|
674
|
+
identifier = this.getNodeParameter('contactEmail', i);
|
|
675
|
+
}
|
|
676
|
+
else {
|
|
677
|
+
identifier = this.getNodeParameter('contactId', i);
|
|
678
|
+
if (identifier.includes('@')) {
|
|
679
|
+
isEmail = true;
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
let targetContactId = identifier;
|
|
683
|
+
if (isEmail) {
|
|
684
|
+
const contact = await GenericFunctions_1.getContactByEmail.call(this, identifier, i);
|
|
685
|
+
targetContactId = contact.id;
|
|
686
|
+
}
|
|
553
687
|
const additionalFields = this.getNodeParameter('additionalFields', i, {});
|
|
554
|
-
const responseData = await GenericFunctions_1.zohoDeskApiRequest.call(this, 'PATCH', `/contacts/${
|
|
688
|
+
const responseData = await GenericFunctions_1.zohoDeskApiRequest.call(this, 'PATCH', `/contacts/${targetContactId}`, additionalFields, {}, i);
|
|
555
689
|
returnData.push({ json: responseData });
|
|
556
690
|
}
|
|
557
691
|
if (operation === 'search') {
|