@trg-admin/n8n-nodes-zoho-desk 0.1.23 → 0.1.24
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.
package/README.md
CHANGED
|
@@ -33,15 +33,19 @@ When you create the credential in n8n, the default scope is:
|
|
|
33
33
|
|
|
34
34
|
```
|
|
35
35
|
ZohoCRM.modules.ALL Desk.tickets.ALL Desk.contacts.ALL Desk.search.READ
|
|
36
|
+
Desk.tasks.ALL Desk.basic.READ Desk.fields.READ Desk.settings.READ
|
|
37
|
+
Desk.webhooks.CREATE Desk.webhooks.READ Desk.webhooks.UPDATE Desk.webhooks.DELETE
|
|
36
38
|
```
|
|
37
39
|
|
|
38
40
|
**Available Zoho Desk Scopes:**
|
|
39
41
|
- `Desk.tickets.ALL` - Full access to tickets
|
|
40
42
|
- `Desk.contacts.ALL` - Full access to contacts
|
|
41
43
|
- `Desk.search.READ` - Search across modules
|
|
44
|
+
- `Desk.fields.READ` - Read field definitions (required for the Classification / Category / Sub-Category and Channel dropdowns)
|
|
42
45
|
- `Desk.settings.ALL` - Access to settings and configurations
|
|
43
46
|
- `Desk.tasks.ALL` - Task management
|
|
44
47
|
- `Desk.basic.READ` - Read-only access to basic information
|
|
48
|
+
- `Desk.webhooks.*` - Webhook subscription management (required by the Zoho Desk Trigger node)
|
|
45
49
|
|
|
46
50
|
You can customize the scope field in the credential to add or remove permissions as needed.
|
|
47
51
|
|
|
@@ -110,7 +110,7 @@ class ZohoCRMDeskOAuth2Api {
|
|
|
110
110
|
displayName: 'Scope',
|
|
111
111
|
name: 'scope',
|
|
112
112
|
type: 'string',
|
|
113
|
-
default: 'ZohoCRM.modules.ALL Desk.tickets.ALL Desk.contacts.ALL Desk.search.READ Desk.tasks.ALL Desk.basic.READ Desk.settings.READ Desk.webhooks.CREATE Desk.webhooks.READ Desk.webhooks.UPDATE Desk.webhooks.DELETE',
|
|
113
|
+
default: 'ZohoCRM.modules.ALL Desk.tickets.ALL Desk.contacts.ALL Desk.search.READ Desk.tasks.ALL Desk.basic.READ Desk.fields.READ Desk.settings.READ Desk.webhooks.CREATE Desk.webhooks.READ Desk.webhooks.UPDATE Desk.webhooks.DELETE',
|
|
114
114
|
required: true,
|
|
115
115
|
description: 'Space-separated OAuth scopes. Default includes both CRM and Desk permissions. Add or remove scopes as needed (e.g., Desk.settings.ALL, Desk.tasks.ALL).',
|
|
116
116
|
},
|
|
@@ -1,4 +1,23 @@
|
|
|
1
1
|
import type { IExecuteFunctions, INodeExecutionData, INodeType, INodeTypeDescription, ILoadOptionsFunctions } from 'n8n-workflow';
|
|
2
|
+
/**
|
|
3
|
+
* Load a ticket taxonomy picklist (classification / category / subCategory) for a dropdown.
|
|
4
|
+
*
|
|
5
|
+
* Zoho Desk has no /classifications, /categories or /subcategories endpoints — ticket
|
|
6
|
+
* taxonomy is exposed through the ticket module's organization field definitions.
|
|
7
|
+
*
|
|
8
|
+
* The ticket payload takes the taxonomy *name string* (there is no classificationId /
|
|
9
|
+
* categoryId / subCategoryId on a ticket), so the option value is the value itself.
|
|
10
|
+
*
|
|
11
|
+
* ponytail: the three levels are listed independently rather than cascaded. Whether a
|
|
12
|
+
* subCategory must be mapped under the chosen category is configured per-tenant (field
|
|
13
|
+
* dependencies) and is not documented as an API rule, so we list all values and let the
|
|
14
|
+
* API answer. Upgrade to GET /layouts/{layoutId}/fields/{fieldId}/dependentValues if users
|
|
15
|
+
* hit rejections in tenants that do enforce the mapping.
|
|
16
|
+
*/
|
|
17
|
+
export declare function loadTaxonomyOptions(context: ILoadOptionsFunctions, apiName: string): Promise<Array<{
|
|
18
|
+
name: string;
|
|
19
|
+
value: string;
|
|
20
|
+
}>>;
|
|
2
21
|
export declare class ZohoDesk implements INodeType {
|
|
3
22
|
description: INodeTypeDescription;
|
|
4
23
|
methods: {
|
|
@@ -11,6 +30,18 @@ export declare class ZohoDesk implements INodeType {
|
|
|
11
30
|
name: string;
|
|
12
31
|
value: string;
|
|
13
32
|
}[]>;
|
|
33
|
+
getClassifications(this: ILoadOptionsFunctions): Promise<{
|
|
34
|
+
name: string;
|
|
35
|
+
value: string;
|
|
36
|
+
}[]>;
|
|
37
|
+
getCategories(this: ILoadOptionsFunctions): Promise<{
|
|
38
|
+
name: string;
|
|
39
|
+
value: string;
|
|
40
|
+
}[]>;
|
|
41
|
+
getSubCategories(this: ILoadOptionsFunctions): Promise<{
|
|
42
|
+
name: string;
|
|
43
|
+
value: string;
|
|
44
|
+
}[]>;
|
|
14
45
|
};
|
|
15
46
|
};
|
|
16
47
|
execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]>;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.ZohoDesk = void 0;
|
|
4
|
+
exports.loadTaxonomyOptions = loadTaxonomyOptions;
|
|
4
5
|
const n8n_workflow_1 = require("n8n-workflow");
|
|
5
6
|
const GenericFunctions_1 = require("./GenericFunctions");
|
|
6
7
|
function cleanCommaSeparated(value) {
|
|
@@ -13,6 +14,56 @@ function cleanCommaSeparated(value) {
|
|
|
13
14
|
.filter(Boolean)
|
|
14
15
|
.join(',');
|
|
15
16
|
}
|
|
17
|
+
/**
|
|
18
|
+
* Load a ticket taxonomy picklist (classification / category / subCategory) for a dropdown.
|
|
19
|
+
*
|
|
20
|
+
* Zoho Desk has no /classifications, /categories or /subcategories endpoints — ticket
|
|
21
|
+
* taxonomy is exposed through the ticket module's organization field definitions.
|
|
22
|
+
*
|
|
23
|
+
* The ticket payload takes the taxonomy *name string* (there is no classificationId /
|
|
24
|
+
* categoryId / subCategoryId on a ticket), so the option value is the value itself.
|
|
25
|
+
*
|
|
26
|
+
* ponytail: the three levels are listed independently rather than cascaded. Whether a
|
|
27
|
+
* subCategory must be mapped under the chosen category is configured per-tenant (field
|
|
28
|
+
* dependencies) and is not documented as an API rule, so we list all values and let the
|
|
29
|
+
* API answer. Upgrade to GET /layouts/{layoutId}/fields/{fieldId}/dependentValues if users
|
|
30
|
+
* hit rejections in tenants that do enforce the mapping.
|
|
31
|
+
*/
|
|
32
|
+
async function loadTaxonomyOptions(context, apiName) {
|
|
33
|
+
// Taxonomy is department-scoped; only top-level ticket parameters carry a department.
|
|
34
|
+
const departmentId = context.getCurrentNodeParameter('departmentId');
|
|
35
|
+
const qs = { module: 'tickets', apiNames: apiName };
|
|
36
|
+
if (departmentId) {
|
|
37
|
+
qs.departmentId = String(departmentId);
|
|
38
|
+
}
|
|
39
|
+
try {
|
|
40
|
+
const responseData = await GenericFunctions_1.zohoDeskApiRequest.call(context, 'GET', '/organizationFields', {}, qs, 0);
|
|
41
|
+
const fields = Array.isArray(responseData?.data)
|
|
42
|
+
? responseData.data
|
|
43
|
+
: Array.isArray(responseData)
|
|
44
|
+
? responseData
|
|
45
|
+
: [];
|
|
46
|
+
const field = fields.find((f) => String(f.apiName || '').toLowerCase() === apiName.toLowerCase());
|
|
47
|
+
// Field definitions carry picklist values under either key depending on tenant.
|
|
48
|
+
const rawValues = (field?.pickListValues ?? field?.allowedValues ?? []);
|
|
49
|
+
const seen = new Set();
|
|
50
|
+
const options = [];
|
|
51
|
+
for (const entry of rawValues) {
|
|
52
|
+
const value = typeof entry === 'string'
|
|
53
|
+
? entry
|
|
54
|
+
: (entry.value || entry.displayValue || '');
|
|
55
|
+
if (!value || seen.has(value)) {
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
seen.add(value);
|
|
59
|
+
options.push({ name: value, value });
|
|
60
|
+
}
|
|
61
|
+
return options.sort((a, b) => a.name.localeCompare(b.name));
|
|
62
|
+
}
|
|
63
|
+
catch (error) {
|
|
64
|
+
throw new Error(`Failed to load "${apiName}" values from the ticket layout. Error: ${error.message}`);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
16
67
|
class ZohoDesk {
|
|
17
68
|
constructor() {
|
|
18
69
|
this.description = {
|
|
@@ -190,6 +241,39 @@ class ZohoDesk {
|
|
|
190
241
|
default: 'Email',
|
|
191
242
|
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>.',
|
|
192
243
|
},
|
|
244
|
+
{
|
|
245
|
+
displayName: 'Classification',
|
|
246
|
+
name: 'classification',
|
|
247
|
+
type: 'options',
|
|
248
|
+
typeOptions: {
|
|
249
|
+
loadOptionsMethod: 'getClassifications',
|
|
250
|
+
loadOptionsDependsOn: ['organizationId', 'departmentId'],
|
|
251
|
+
},
|
|
252
|
+
default: '',
|
|
253
|
+
description: 'Top-level ticket classification, as configured on the department\'s ticket layout. Choose from the list, or specify a value using an <a href="https://docs.n8n.io/code-examples/expressions/">expression</a>.',
|
|
254
|
+
},
|
|
255
|
+
{
|
|
256
|
+
displayName: 'Category',
|
|
257
|
+
name: 'category',
|
|
258
|
+
type: 'options',
|
|
259
|
+
typeOptions: {
|
|
260
|
+
loadOptionsMethod: 'getCategories',
|
|
261
|
+
loadOptionsDependsOn: ['organizationId', 'departmentId'],
|
|
262
|
+
},
|
|
263
|
+
default: '',
|
|
264
|
+
description: 'Ticket category, as configured on the department\'s ticket layout. Choose from the list, or specify a value using an <a href="https://docs.n8n.io/code-examples/expressions/">expression</a>.',
|
|
265
|
+
},
|
|
266
|
+
{
|
|
267
|
+
displayName: 'Sub-Category',
|
|
268
|
+
name: 'subCategory',
|
|
269
|
+
type: 'options',
|
|
270
|
+
typeOptions: {
|
|
271
|
+
loadOptionsMethod: 'getSubCategories',
|
|
272
|
+
loadOptionsDependsOn: ['organizationId', 'departmentId'],
|
|
273
|
+
},
|
|
274
|
+
default: '',
|
|
275
|
+
description: 'Ticket sub-category, as configured on the department\'s ticket layout. Choose from the list, or specify a value using an <a href="https://docs.n8n.io/code-examples/expressions/">expression</a>.',
|
|
276
|
+
},
|
|
193
277
|
{
|
|
194
278
|
displayName: 'Contact ID',
|
|
195
279
|
name: 'contactId',
|
|
@@ -875,6 +959,15 @@ class ZohoDesk {
|
|
|
875
959
|
}
|
|
876
960
|
return fallbackChannels;
|
|
877
961
|
},
|
|
962
|
+
async getClassifications() {
|
|
963
|
+
return loadTaxonomyOptions(this, 'classification');
|
|
964
|
+
},
|
|
965
|
+
async getCategories() {
|
|
966
|
+
return loadTaxonomyOptions(this, 'category');
|
|
967
|
+
},
|
|
968
|
+
async getSubCategories() {
|
|
969
|
+
return loadTaxonomyOptions(this, 'subCategory');
|
|
970
|
+
},
|
|
878
971
|
},
|
|
879
972
|
};
|
|
880
973
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@trg-admin/n8n-nodes-zoho-desk",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.24",
|
|
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",
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
"copy-assets": "cp nodes/ZohoDesk/zohodesk.png dist/nodes/ZohoDesk/",
|
|
11
11
|
"lint": "echo 'No linter configured'",
|
|
12
12
|
"typecheck": "tsc --noEmit",
|
|
13
|
-
"test": "node scripts.validate.js",
|
|
13
|
+
"test": "node scripts.validate.js && node test/Taxonomy.test.js",
|
|
14
14
|
"prepublishOnly": "npm run typecheck && npm run build"
|
|
15
15
|
},
|
|
16
16
|
"files": [
|