@trg-admin/n8n-nodes-zoho-desk 0.1.21 → 0.1.23

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.
@@ -3,6 +3,16 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.ZohoDesk = void 0;
4
4
  const n8n_workflow_1 = require("n8n-workflow");
5
5
  const GenericFunctions_1 = require("./GenericFunctions");
6
+ function cleanCommaSeparated(value) {
7
+ if (typeof value !== 'string') {
8
+ return '';
9
+ }
10
+ return value
11
+ .split(',')
12
+ .map((s) => s.trim())
13
+ .filter(Boolean)
14
+ .join(',');
15
+ }
6
16
  class ZohoDesk {
7
17
  constructor() {
8
18
  this.description = {
@@ -43,6 +53,21 @@ class ZohoDesk {
43
53
  { name: 'Ticket', value: 'ticket' },
44
54
  { name: 'Contact', value: 'contact' },
45
55
  { name: 'Comment', value: 'comment' },
56
+ { name: 'Thread', value: 'thread' },
57
+ ],
58
+ },
59
+ // Thread Operations
60
+ {
61
+ displayName: 'Operation',
62
+ name: 'operation',
63
+ type: 'options',
64
+ noDataExpression: true,
65
+ displayOptions: { show: { resource: ['thread'] } },
66
+ default: 'getAll',
67
+ options: [
68
+ { name: 'Get', value: 'get', action: 'Get a thread from a ticket' },
69
+ { name: 'Get Many', value: 'getAll', action: 'Get all threads from a ticket' },
70
+ { name: 'Send Reply', value: 'sendReply', action: 'Send a reply to a ticket' },
46
71
  ],
47
72
  },
48
73
  // Ticket Operations
@@ -593,6 +618,155 @@ class ZohoDesk {
593
618
  displayOptions: { show: { resource: ['comment'], operation: ['getAll'] } },
594
619
  description: 'Max number of results to return',
595
620
  },
621
+ // ================== Thread Resource Parameters ==================
622
+ {
623
+ displayName: 'Ticket ID',
624
+ name: 'ticketId',
625
+ type: 'string',
626
+ default: '',
627
+ required: true,
628
+ displayOptions: { show: { resource: ['thread'], operation: ['get', 'getAll', 'sendReply'] } },
629
+ description: 'The unique ID of the ticket',
630
+ },
631
+ {
632
+ displayName: 'Thread ID',
633
+ name: 'threadId',
634
+ type: 'string',
635
+ default: '',
636
+ required: true,
637
+ displayOptions: { show: { resource: ['thread'], operation: ['get'] } },
638
+ description: 'The unique ID of the thread',
639
+ },
640
+ {
641
+ displayName: 'Content',
642
+ name: 'content',
643
+ type: 'string',
644
+ typeOptions: { alwaysOpenEditWindow: true },
645
+ default: '',
646
+ required: true,
647
+ displayOptions: { show: { resource: ['thread'], operation: ['sendReply'] } },
648
+ description: 'Content / body of the reply',
649
+ },
650
+ {
651
+ displayName: 'Content Type',
652
+ name: 'contentType',
653
+ type: 'options',
654
+ options: [
655
+ { name: 'HTML', value: 'html' },
656
+ { name: 'Plain Text', value: 'plainText' },
657
+ ],
658
+ default: 'html',
659
+ displayOptions: { show: { resource: ['thread'], operation: ['sendReply'] } },
660
+ description: 'Format of the reply content',
661
+ },
662
+ {
663
+ displayName: 'Additional Fields',
664
+ name: 'additionalFields',
665
+ type: 'collection',
666
+ placeholder: 'Add Field',
667
+ default: {},
668
+ displayOptions: { show: { resource: ['thread'], operation: ['sendReply'] } },
669
+ options: [
670
+ {
671
+ displayName: 'BCC',
672
+ name: 'bcc',
673
+ type: 'string',
674
+ default: '',
675
+ placeholder: '[EMAIL_REDACTED], [EMAIL_REDACTED]',
676
+ description: 'Blind carbon copy recipient email addresses. Multiple addresses can be separated with commas.',
677
+ },
678
+ {
679
+ displayName: 'CC',
680
+ name: 'cc',
681
+ type: 'string',
682
+ default: '',
683
+ placeholder: '[EMAIL_REDACTED], [EMAIL_REDACTED]',
684
+ description: 'Carbon copy recipient email addresses. Multiple addresses can be separated with commas.',
685
+ },
686
+ {
687
+ displayName: 'Channel',
688
+ name: 'channel',
689
+ type: 'options',
690
+ options: [
691
+ { name: 'Email', value: 'EMAIL' },
692
+ { name: 'Phone', value: 'PHONE' },
693
+ ],
694
+ default: 'EMAIL',
695
+ description: 'Channel through which the reply is sent',
696
+ },
697
+ {
698
+ displayName: 'From Email Address',
699
+ name: 'fromEmailAddress',
700
+ type: 'string',
701
+ default: '',
702
+ description: 'Sender email address. Must be a verified "From Address" configured in Zoho Desk Setup.',
703
+ },
704
+ {
705
+ displayName: 'Is Forward',
706
+ name: 'isForward',
707
+ type: 'boolean',
708
+ default: false,
709
+ description: 'Whether this reply is a forwarded message',
710
+ },
711
+ {
712
+ displayName: 'To',
713
+ name: 'to',
714
+ type: 'string',
715
+ default: '',
716
+ placeholder: '[EMAIL_REDACTED], [EMAIL_REDACTED]',
717
+ description: 'Recipient email address(es). Multiple addresses can be separated with commas.',
718
+ },
719
+ ],
720
+ },
721
+ {
722
+ displayName: 'Return All',
723
+ name: 'returnAll',
724
+ type: 'boolean',
725
+ default: false,
726
+ displayOptions: { show: { resource: ['thread'], operation: ['getAll'] } },
727
+ description: 'Whether to return all results or limit to specified number',
728
+ },
729
+ {
730
+ displayName: 'Limit',
731
+ name: 'limit',
732
+ type: 'number',
733
+ typeOptions: { minValue: 1, maxValue: 100 },
734
+ default: 50,
735
+ displayOptions: {
736
+ show: {
737
+ resource: ['thread'],
738
+ operation: ['getAll'],
739
+ returnAll: [false],
740
+ },
741
+ },
742
+ description: 'Max number of results to return (1-100)',
743
+ },
744
+ {
745
+ displayName: 'Options',
746
+ name: 'options',
747
+ type: 'collection',
748
+ placeholder: 'Add Option',
749
+ default: {},
750
+ displayOptions: { show: { resource: ['thread'], operation: ['get', 'getAll'] } },
751
+ options: [
752
+ {
753
+ displayName: 'From (Offset)',
754
+ name: 'from',
755
+ type: 'number',
756
+ typeOptions: { minValue: 0 },
757
+ default: 0,
758
+ description: 'Index number from which the threads should be fetched (getAll only)',
759
+ },
760
+ {
761
+ displayName: 'Include',
762
+ name: 'include',
763
+ type: 'string',
764
+ default: '',
765
+ placeholder: 'canReply, draft, portalHtmlUrl',
766
+ description: 'Additional information to include in response (e.g. canReply, draft, portalHtmlUrl). Multiple values can be separated with commas.',
767
+ },
768
+ ],
769
+ },
596
770
  ],
597
771
  };
598
772
  this.methods = {
@@ -762,52 +936,86 @@ class ZohoDesk {
762
936
  const returnAll = this.getNodeParameter('returnAll', i, false);
763
937
  const options = this.getNodeParameter('options', i, {});
764
938
  const qs = {};
939
+ const cleanCommaSeparated = (value) => {
940
+ if (typeof value !== 'string') {
941
+ return '';
942
+ }
943
+ return value
944
+ .split(',')
945
+ .map((s) => s.trim())
946
+ .filter(Boolean)
947
+ .join(',');
948
+ };
765
949
  // Department filtering: options.departmentIds, options.departmentId, or legacy parameter
766
950
  const topLevelDepartmentId = this.getNodeParameter('departmentId', i, '');
767
951
  if (options.departmentIds) {
768
- qs.departmentIds = options.departmentIds;
952
+ const cleaned = cleanCommaSeparated(options.departmentIds);
953
+ if (cleaned)
954
+ qs.departmentIds = cleaned;
769
955
  }
770
956
  else if (options.departmentId) {
771
- qs.departmentId = options.departmentId;
957
+ const cleaned = String(options.departmentId).trim();
958
+ if (cleaned)
959
+ qs.departmentId = cleaned;
772
960
  }
773
961
  else if (topLevelDepartmentId) {
774
- qs.departmentId = topLevelDepartmentId;
962
+ const cleaned = String(topLevelDepartmentId).trim();
963
+ if (cleaned)
964
+ qs.departmentId = cleaned;
775
965
  }
776
966
  if (options.viewId) {
777
- qs.viewId = options.viewId;
967
+ const cleaned = String(options.viewId).trim();
968
+ if (cleaned)
969
+ qs.viewId = cleaned;
778
970
  }
779
971
  if (options.assignee) {
780
- qs.assignee = options.assignee;
972
+ const cleaned = cleanCommaSeparated(options.assignee);
973
+ if (cleaned)
974
+ qs.assignee = cleaned;
781
975
  }
782
976
  if (options.teamIds) {
783
- qs.teamIds = options.teamIds;
977
+ const cleaned = cleanCommaSeparated(options.teamIds);
978
+ if (cleaned)
979
+ qs.teamIds = cleaned;
784
980
  }
785
981
  if (options.channel) {
786
- qs.channel = options.channel;
982
+ const cleaned = cleanCommaSeparated(options.channel);
983
+ if (cleaned)
984
+ qs.channel = cleaned;
787
985
  }
788
986
  if (options.status) {
789
- qs.status = options.status;
987
+ const cleaned = cleanCommaSeparated(options.status);
988
+ if (cleaned)
989
+ qs.status = cleaned;
790
990
  }
791
991
  if (options.priority) {
792
- qs.priority = options.priority;
992
+ const cleaned = cleanCommaSeparated(options.priority);
993
+ if (cleaned)
994
+ qs.priority = cleaned;
793
995
  }
794
996
  if (options.sortBy) {
795
- qs.sortBy = options.sortBy;
997
+ const cleaned = String(options.sortBy).trim();
998
+ if (cleaned)
999
+ qs.sortBy = cleaned;
796
1000
  }
797
1001
  if (options.receivedInDays) {
798
1002
  qs.receivedInDays = options.receivedInDays;
799
1003
  }
800
1004
  if (options.fields) {
801
- qs.fields = options.fields;
1005
+ const cleaned = cleanCommaSeparated(options.fields);
1006
+ if (cleaned)
1007
+ qs.fields = cleaned;
802
1008
  }
803
1009
  if (options.include) {
804
1010
  if (Array.isArray(options.include)) {
805
1011
  if (options.include.length > 0) {
806
- qs.include = options.include.join(',');
1012
+ qs.include = options.include.map((s) => String(s).trim()).filter(Boolean).join(',');
807
1013
  }
808
1014
  }
809
- else if (typeof options.include === 'string' && options.include.trim()) {
810
- qs.include = options.include.trim();
1015
+ else if (typeof options.include === 'string') {
1016
+ const cleaned = cleanCommaSeparated(options.include);
1017
+ if (cleaned)
1018
+ qs.include = cleaned;
811
1019
  }
812
1020
  }
813
1021
  if (returnAll) {
@@ -1015,6 +1223,85 @@ class ZohoDesk {
1015
1223
  }
1016
1224
  }
1017
1225
  }
1226
+ // ==================== THREAD OPERATIONS ====================
1227
+ if (resource === 'thread') {
1228
+ if (operation === 'get') {
1229
+ const ticketId = this.getNodeParameter('ticketId', i);
1230
+ const threadId = this.getNodeParameter('threadId', i);
1231
+ const options = this.getNodeParameter('options', i, {});
1232
+ const qs = {};
1233
+ if (options.include) {
1234
+ const clean = cleanCommaSeparated(options.include);
1235
+ if (clean)
1236
+ qs.include = clean;
1237
+ }
1238
+ const responseData = await GenericFunctions_1.zohoDeskApiRequest.call(this, 'GET', `/tickets/${ticketId}/threads/${threadId}`, {}, qs, i);
1239
+ returnData.push({ json: responseData });
1240
+ }
1241
+ if (operation === 'getAll') {
1242
+ const ticketId = this.getNodeParameter('ticketId', i);
1243
+ const returnAll = this.getNodeParameter('returnAll', i, false);
1244
+ const options = this.getNodeParameter('options', i, {});
1245
+ const qs = {};
1246
+ if (options.include) {
1247
+ const clean = cleanCommaSeparated(options.include);
1248
+ if (clean)
1249
+ qs.include = clean;
1250
+ }
1251
+ if (returnAll) {
1252
+ const responseData = await GenericFunctions_1.zohoDeskApiRequestAllItems.call(this, 'GET', `/tickets/${ticketId}/threads`, {}, qs, i);
1253
+ for (const row of responseData) {
1254
+ returnData.push({ json: row });
1255
+ }
1256
+ }
1257
+ else {
1258
+ const limit = this.getNodeParameter('limit', i, 50);
1259
+ qs.limit = limit;
1260
+ if (options.from !== undefined && options.from !== null && options.from !== '') {
1261
+ qs.from = options.from;
1262
+ }
1263
+ const responseData = await GenericFunctions_1.zohoDeskApiRequest.call(this, 'GET', `/tickets/${ticketId}/threads`, {}, qs, i);
1264
+ const data = Array.isArray(responseData.data)
1265
+ ? responseData.data
1266
+ : Array.isArray(responseData)
1267
+ ? responseData
1268
+ : [responseData];
1269
+ for (const row of data) {
1270
+ returnData.push({ json: row });
1271
+ }
1272
+ }
1273
+ }
1274
+ if (operation === 'sendReply') {
1275
+ const ticketId = this.getNodeParameter('ticketId', i);
1276
+ const content = this.getNodeParameter('content', i);
1277
+ const contentType = this.getNodeParameter('contentType', i, 'html');
1278
+ const additionalFields = this.getNodeParameter('additionalFields', i, {});
1279
+ const body = {
1280
+ content,
1281
+ contentType,
1282
+ };
1283
+ if (additionalFields.channel) {
1284
+ body.channel = additionalFields.channel;
1285
+ }
1286
+ if (additionalFields.fromEmailAddress) {
1287
+ body.fromEmailAddress = String(additionalFields.fromEmailAddress).trim();
1288
+ }
1289
+ if (additionalFields.to) {
1290
+ body.to = cleanCommaSeparated(additionalFields.to);
1291
+ }
1292
+ if (additionalFields.cc) {
1293
+ body.cc = cleanCommaSeparated(additionalFields.cc).split(',').filter(Boolean);
1294
+ }
1295
+ if (additionalFields.bcc) {
1296
+ body.bcc = cleanCommaSeparated(additionalFields.bcc).split(',').filter(Boolean);
1297
+ }
1298
+ if (additionalFields.isForward !== undefined) {
1299
+ body.isForward = additionalFields.isForward;
1300
+ }
1301
+ const responseData = await GenericFunctions_1.zohoDeskApiRequest.call(this, 'POST', `/tickets/${ticketId}/sendReply`, body, {}, i);
1302
+ returnData.push({ json: responseData });
1303
+ }
1304
+ }
1018
1305
  }
1019
1306
  catch (error) {
1020
1307
  if (this.continueOnFail()) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@trg-admin/n8n-nodes-zoho-desk",
3
- "version": "0.1.21",
3
+ "version": "0.1.23",
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",