@pipedream/freshdesk 0.3.0 → 0.4.0

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.
Files changed (39) hide show
  1. package/actions/add-note-to-ticket/add-note-to-ticket.mjs +104 -0
  2. package/actions/add-ticket-tags/add-ticket-tags.mjs +1 -1
  3. package/actions/assign-ticket-to-agent/assign-ticket-to-agent.mjs +1 -1
  4. package/actions/assign-ticket-to-group/assign-ticket-to-group.mjs +1 -1
  5. package/actions/close-ticket/close-ticket.mjs +1 -1
  6. package/actions/create-agent/create-agent.mjs +103 -0
  7. package/actions/create-company/create-company.mjs +1 -1
  8. package/actions/create-contact/create-contact.mjs +1 -1
  9. package/actions/create-solution-article/create-solution-article.mjs +72 -0
  10. package/actions/create-ticket/create-ticket.mjs +1 -1
  11. package/actions/create-ticket-field/create-ticket-field.mjs +109 -0
  12. package/actions/delete-solution-article/delete-solution-article.mjs +44 -0
  13. package/actions/get-contact/get-contact.mjs +26 -0
  14. package/actions/get-solution-article/get-solution-article.mjs +44 -0
  15. package/actions/get-ticket/get-ticket.mjs +1 -1
  16. package/actions/list-agents/list-agents.mjs +66 -0
  17. package/actions/list-all-tickets/list-all-tickets.mjs +1 -1
  18. package/actions/list-category-folders/list-category-folders.mjs +28 -0
  19. package/actions/list-folder-articles/list-folder-articles.mjs +47 -0
  20. package/actions/list-solution-categories/list-solution-categories.mjs +21 -0
  21. package/actions/list-ticket-fields/list-ticket-fields.mjs +32 -0
  22. package/actions/remove-ticket-tags/remove-ticket-tags.mjs +1 -1
  23. package/actions/set-ticket-priority/set-ticket-priority.mjs +1 -1
  24. package/actions/set-ticket-status/set-ticket-status.mjs +1 -1
  25. package/actions/set-ticket-tags/set-ticket-tags.mjs +1 -1
  26. package/actions/update-agent/update-agent.mjs +112 -0
  27. package/actions/update-contact/update-contact.mjs +64 -0
  28. package/actions/update-solution-article/update-solution-article.mjs +84 -0
  29. package/actions/update-ticket/update-ticket.mjs +2 -3
  30. package/actions/update-ticket-field/update-ticket-field.mjs +106 -0
  31. package/common/constants.mjs +38 -0
  32. package/common/utils.mjs +27 -0
  33. package/freshdesk.app.mjs +330 -6
  34. package/package.json +1 -1
  35. package/sources/common/polling.mjs +63 -0
  36. package/sources/contact-updated/contact-updated.mjs +28 -0
  37. package/sources/new-contact/new-contact.mjs +18 -40
  38. package/sources/new-ticket/new-ticket.mjs +18 -43
  39. package/sources/ticket-updated/ticket-updated.mjs +28 -0
package/freshdesk.app.mjs CHANGED
@@ -19,7 +19,6 @@ export default {
19
19
  }));
20
20
  },
21
21
  },
22
-
23
22
  ticketId: {
24
23
  type: "integer",
25
24
  label: "Ticket ID",
@@ -79,7 +78,6 @@ export default {
79
78
  }));
80
79
  },
81
80
  },
82
-
83
81
  ticketStatus: {
84
82
  type: "integer",
85
83
  label: "Status",
@@ -116,6 +114,138 @@ export default {
116
114
  }));
117
115
  },
118
116
  },
117
+ contactId: {
118
+ type: "string",
119
+ label: "Contact ID",
120
+ description: "The ID of a contact",
121
+ async options({
122
+ companyId, page,
123
+ }) {
124
+ const contacts = await this.getContacts({
125
+ params: {
126
+ company_id: companyId,
127
+ page: page + 1,
128
+ },
129
+ });
130
+ return contacts
131
+ .map(({
132
+ id, name, email,
133
+ }) => ({
134
+ label: name || email,
135
+ value: id,
136
+ }));
137
+ },
138
+ },
139
+ ticketFieldId: {
140
+ type: "string",
141
+ label: "Ticket Field ID",
142
+ description: "The ID of a ticket field",
143
+ async options({ page }) {
144
+ const fields = await this.listTicketFields({
145
+ params: {
146
+ page: page + 1,
147
+ },
148
+ });
149
+ return fields.map(({
150
+ id, label,
151
+ }) => ({
152
+ label: label || id,
153
+ value: id,
154
+ }));
155
+ },
156
+ },
157
+ skillIds: {
158
+ type: "string[]",
159
+ label: "Skill IDs",
160
+ description: "Array of skill IDs",
161
+ optional: true,
162
+ async options({ page }) {
163
+ const skills = await this.listSkills({
164
+ params: {
165
+ page: page + 1,
166
+ },
167
+ });
168
+ return skills.map(({
169
+ id, name,
170
+ }) => ({
171
+ label: name || id,
172
+ value: id,
173
+ }));
174
+ },
175
+ },
176
+ roleIds: {
177
+ type: "string[]",
178
+ label: "Role IDs",
179
+ description: "Array of role IDs",
180
+ optional: true,
181
+ async options() {
182
+ const roles = await this.listRoles();
183
+ return roles.map(({
184
+ id, name,
185
+ }) => ({
186
+ label: name || id,
187
+ value: id,
188
+ }));
189
+ },
190
+ },
191
+ categoryId: {
192
+ type: "integer",
193
+ label: "Category ID",
194
+ description: "The ID of a category",
195
+ async options() {
196
+ const categories = await this.listSolutionCategories();
197
+ return categories.map(({
198
+ id, name,
199
+ }) => ({
200
+ label: name || id,
201
+ value: id,
202
+ }));
203
+ },
204
+ },
205
+ folderId: {
206
+ type: "integer",
207
+ label: "Folder ID",
208
+ description: "The ID of a folder",
209
+ async options({ categoryId }) {
210
+ const folders = await this.listCategoryFolders({
211
+ categoryId,
212
+ });
213
+ return folders.map(({
214
+ id, name,
215
+ }) => ({
216
+ label: name || id,
217
+ value: id,
218
+ }));
219
+ },
220
+ },
221
+ articleId: {
222
+ type: "integer",
223
+ label: "Article ID",
224
+ description: "The ID of an article",
225
+ async options({
226
+ page, folderId,
227
+ }) {
228
+ const articles = await this.listFolderArticles({
229
+ folderId,
230
+ params: {
231
+ page: page + 1,
232
+ },
233
+ });
234
+ return articles.map(({
235
+ id, title,
236
+ }) => ({
237
+ label: title || id,
238
+ value: id,
239
+ }));
240
+ },
241
+ },
242
+ maxResults: {
243
+ type: "integer",
244
+ label: "Max Results",
245
+ description: "The maximum number of results to return",
246
+ default: 100,
247
+ optional: true,
248
+ },
119
249
  ticketTags: {
120
250
  type: "string[]",
121
251
  label: "Tags",
@@ -210,6 +340,14 @@ export default {
210
340
  ...args,
211
341
  });
212
342
  },
343
+ async getContact({
344
+ contactId, ...args
345
+ }) {
346
+ return this._makeRequest({
347
+ url: `/contacts/${contactId}`,
348
+ ...args,
349
+ });
350
+ },
213
351
  async getContacts(args) {
214
352
  return this._makeRequest({
215
353
  url: "/contacts",
@@ -223,6 +361,15 @@ export default {
223
361
  ...args,
224
362
  });
225
363
  },
364
+ async updateContact({
365
+ contactId, ...args
366
+ }) {
367
+ return this._makeRequest({
368
+ url: `/contacts/${contactId}`,
369
+ method: "put",
370
+ ...args,
371
+ });
372
+ },
226
373
  async createTicket(args) {
227
374
  return this._makeRequest({
228
375
  url: "/tickets",
@@ -250,6 +397,119 @@ export default {
250
397
  ...args,
251
398
  });
252
399
  },
400
+ async listTicketFields(args) {
401
+ return this._makeRequest({
402
+ url: "/ticket_fields",
403
+ ...args,
404
+ });
405
+ },
406
+ async createTicketField(args) {
407
+ return this._makeRequest({
408
+ url: "/admin/ticket_fields",
409
+ method: "post",
410
+ ...args,
411
+ });
412
+ },
413
+ async updateTicketField({
414
+ ticketFieldId, ...args
415
+ }) {
416
+ return this._makeRequest({
417
+ url: `/admin/ticket_fields/${ticketFieldId}`,
418
+ method: "put",
419
+ ...args,
420
+ });
421
+ },
422
+ async listAgents(args) {
423
+ return this._makeRequest({
424
+ url: "/agents",
425
+ ...args,
426
+ });
427
+ },
428
+ async createAgent(args) {
429
+ return this._makeRequest({
430
+ url: "/agents",
431
+ method: "post",
432
+ ...args,
433
+ });
434
+ },
435
+ async updateAgent({
436
+ agentId, ...args
437
+ }) {
438
+ return this._makeRequest({
439
+ url: `/agents/${agentId}`,
440
+ method: "put",
441
+ ...args,
442
+ });
443
+ },
444
+ async listSkills(args) {
445
+ return this._makeRequest({
446
+ url: "/admin/skills",
447
+ ...args,
448
+ });
449
+ },
450
+ async listRoles(args) {
451
+ return this._makeRequest({
452
+ url: "/roles",
453
+ ...args,
454
+ });
455
+ },
456
+ async listSolutionCategories(args) {
457
+ return this._makeRequest({
458
+ url: "/solutions/categories",
459
+ ...args,
460
+ });
461
+ },
462
+ async listCategoryFolders({
463
+ categoryId, ...args
464
+ }) {
465
+ return this._makeRequest({
466
+ url: `/solutions/categories/${categoryId}/folders`,
467
+ ...args,
468
+ });
469
+ },
470
+ async listFolderArticles({
471
+ folderId, ...args
472
+ }) {
473
+ return this._makeRequest({
474
+ url: `/solutions/folders/${folderId}/articles`,
475
+ ...args,
476
+ });
477
+ },
478
+ async getArticle({
479
+ articleId, ...args
480
+ }) {
481
+ return this._makeRequest({
482
+ url: `/solutions/articles/${articleId}`,
483
+ ...args,
484
+ });
485
+ },
486
+ async createArticle({
487
+ folderId, ...args
488
+ }) {
489
+ return this._makeRequest({
490
+ url: `/solutions/folders/${folderId}/articles`,
491
+ method: "post",
492
+ ...args,
493
+ });
494
+ },
495
+ async updateArticle({
496
+ articleId, ...args
497
+ }) {
498
+ return this._makeRequest({
499
+ url: `/solutions/articles/${articleId}`,
500
+ method: "put",
501
+ ...args,
502
+ });
503
+ },
504
+ async deleteArticle({
505
+ articleId, ...args
506
+ }) {
507
+ return this._makeRequest({
508
+ url: `/solutions/articles/${articleId}`,
509
+ method: "delete",
510
+ ...args,
511
+ });
512
+ },
253
513
  async listTickets(args) {
254
514
  return this._makeRequest({
255
515
  url: "/tickets",
@@ -257,10 +517,17 @@ export default {
257
517
  });
258
518
  },
259
519
  async getTicketName(ticketId) {
260
- const ticket = await this.getTicket({
261
- ticketId,
262
- });
263
- return ticket.subject;
520
+ try {
521
+ const ticket = await this.getTicket({
522
+ ticketId,
523
+ });
524
+ return ticket.subject;
525
+ } catch (error) {
526
+ if (error.response?.status === 404) {
527
+ return null;
528
+ }
529
+ throw error;
530
+ }
264
531
  },
265
532
  parseIfJSONString(input) {
266
533
  if (typeof input === "string") {
@@ -272,6 +539,29 @@ export default {
272
539
  }
273
540
  return input;
274
541
  },
542
+ /**
543
+ * Add a note to a Freshdesk ticket
544
+ * @param {Object} options - The options object
545
+ * @param {number} options.ticketId - The ID of the ticket to add the note to
546
+ * @param {Object} options.data - The note data object
547
+ * @param {string} options.data.body - Content of the note in HTML format
548
+ * @param {boolean} [options.data.private=false] - Whether the note is private
549
+ * @param {boolean} [options.data.incoming] - Whether the note is incoming
550
+ * @param {number} [options.data.user_id] - ID of the user creating the note
551
+ * @param {string[]} [options.data.notify_emails] - Array of email addresses to notify
552
+ * @param {...*} args - Additional arguments passed to _makeRequest
553
+ * @returns {Promise<Object>} The API response containing the created note
554
+ */
555
+ async addNoteToTicket({
556
+ ticketId, data, ...args
557
+ }) {
558
+ return this._makeRequest({
559
+ url: `/tickets/${ticketId}/notes`,
560
+ method: "post",
561
+ data,
562
+ ...args,
563
+ });
564
+ },
275
565
  /**
276
566
  * Set tags on a ticket (replaces all existing tags)
277
567
  * @param {object} args - Arguments object
@@ -345,5 +635,39 @@ export default {
345
635
  ...args,
346
636
  });
347
637
  },
638
+ async *paginate({
639
+ fn, args, max,
640
+ }) {
641
+ args = {
642
+ ...args,
643
+ params: {
644
+ ...args?.params,
645
+ page: 1,
646
+ per_page: 100,
647
+ },
648
+ };
649
+ let total, count = 0;
650
+ do {
651
+ const results = await fn(args);
652
+ total = results?.length;
653
+ if (!total) {
654
+ return;
655
+ }
656
+ for (const result of results) {
657
+ yield result;
658
+ if (max && ++count >= max) {
659
+ return;
660
+ }
661
+ }
662
+ args.params.page += 1;
663
+ } while (total === args.params.per_page);
664
+ },
665
+ async getPaginatedResources(opts) {
666
+ const results = [];
667
+ for await (const result of this.paginate(opts)) {
668
+ results.push(result);
669
+ }
670
+ return results;
671
+ },
348
672
  },
349
673
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pipedream/freshdesk",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "description": "Pipedream Freshdesk Components",
5
5
  "main": "freshdesk.app.mjs",
6
6
  "keywords": [
@@ -0,0 +1,63 @@
1
+ import freshdesk from "../../freshdesk.app.mjs";
2
+ import { DEFAULT_POLLING_SOURCE_TIMER_INTERVAL } from "@pipedream/platform";
3
+ import moment from "moment";
4
+
5
+ export default {
6
+ props: {
7
+ freshdesk,
8
+ timer: {
9
+ type: "$.interface.timer",
10
+ default: {
11
+ intervalSeconds: DEFAULT_POLLING_SOURCE_TIMER_INTERVAL,
12
+ },
13
+ },
14
+ db: "$.service.db",
15
+ },
16
+ methods: {
17
+ getResourceFn() {
18
+ throw new Error("getResourceFn is not implemented");
19
+ },
20
+ getTsField() {
21
+ throw new Error("getTsField is not implemented");
22
+ },
23
+ generateMeta() {
24
+ throw new Error("generateMeta is not implemented");
25
+ },
26
+ },
27
+ async run() {
28
+ const data = [];
29
+ let lastDateChecked = this.freshdesk.getLastDateChecked(this.db);
30
+ if (!lastDateChecked) {
31
+ lastDateChecked = new Date().toISOString();
32
+ this.freshdesk.setLastDateChecked(this.db, lastDateChecked);
33
+ }
34
+ let maxTs = lastDateChecked;
35
+
36
+ const resourceFn = this.getResourceFn();
37
+ const tsField = this.getTsField();
38
+
39
+ const formatedDate = lastDateChecked.substr(
40
+ 0,
41
+ (lastDateChecked + "T").indexOf("T"),
42
+ );
43
+ const results = await resourceFn({
44
+ query: `"${tsField}:>'${formatedDate}'"`,
45
+ page: 1,
46
+ });
47
+ for await (const result of results) {
48
+ data.push(result);
49
+ }
50
+
51
+ data &&
52
+ data.reverse().forEach((item) => {
53
+ if (moment(item[tsField]).isAfter(lastDateChecked)) {
54
+ if (moment(item[tsField]).isAfter(maxTs)) {
55
+ maxTs = item[tsField];
56
+ }
57
+ this.$emit(item, this.generateMeta(item));
58
+ }
59
+ });
60
+
61
+ this.freshdesk.setLastDateChecked(this.db, maxTs);
62
+ },
63
+ };
@@ -0,0 +1,28 @@
1
+ import common from "../common/polling.mjs";
2
+
3
+ export default {
4
+ ...common,
5
+ key: "freshdesk-contact-updated",
6
+ name: "Contact Updated",
7
+ description: "Emit new event when a contact is updated. [See the documentation](https://developers.freshdesk.com/api/#filter_contacts)",
8
+ version: "0.0.1",
9
+ type: "source",
10
+ dedupe: "unique",
11
+ methods: {
12
+ ...common.methods,
13
+ getResourceFn() {
14
+ return this.freshdesk.filterContacts;
15
+ },
16
+ getTsField() {
17
+ return "updated_at";
18
+ },
19
+ generateMeta(item) {
20
+ const ts = Date.parse(item.updated_at);
21
+ return {
22
+ id: `${item.id}-${ts}`,
23
+ summary: `Contact Updated (ID: ${item.id})`,
24
+ ts,
25
+ };
26
+ },
27
+ },
28
+ };
@@ -1,49 +1,27 @@
1
- import freshdesk from "../../freshdesk.app.mjs";
2
- import moment from "moment";
3
- import { DEFAULT_POLLING_SOURCE_TIMER_INTERVAL } from "@pipedream/platform";
1
+ import common from "../common/polling.mjs";
4
2
 
5
3
  export default {
4
+ ...common,
6
5
  key: "freshdesk-new-contact",
7
6
  name: "New Contact Created",
8
7
  description: "Emit new event when a contact is created. [See the documentation](https://developers.freshdesk.com/api/#filter_contacts)",
9
- version: "0.0.6",
8
+ version: "0.0.8",
10
9
  type: "source",
11
- props: {
12
- freshdesk,
13
- timer: {
14
- type: "$.interface.timer",
15
- default: {
16
- intervalSeconds: DEFAULT_POLLING_SOURCE_TIMER_INTERVAL,
17
- },
18
- },
19
- db: "$.service.db",
20
- },
21
10
  dedupe: "unique",
22
- async run() {
23
- const data = [];
24
- let lastDateChecked = this.freshdesk.getLastDateChecked(this.db);
25
- if (!lastDateChecked) {
26
- lastDateChecked = new Date().toISOString();
27
- this.freshdesk.setLastDateChecked(this.db, lastDateChecked);
28
- }
29
- const formatedDate = lastDateChecked.substr(0, (lastDateChecked + "T").indexOf("T"));
30
- const contacts = await this.freshdesk.filterContacts({
31
- query: `"created_at:>'${formatedDate}'"`,
32
- page: 1,
33
- });
34
- for await (const contact of contacts) {
35
- data.push(contact);
36
- }
37
- data && data.reverse().forEach((contact) => {
38
- this.freshdesk.setLastDateChecked(this.db, contact.created_at);
39
- if (moment(contact.created_at).isAfter(lastDateChecked)) {
40
- this.$emit(contact,
41
- {
42
- id: contact.id,
43
- summary: `New Contact: "${contact.name}"`,
44
- ts: Date.parse(contact.created_at),
45
- });
46
- }
47
- });
11
+ methods: {
12
+ ...common.methods,
13
+ getResourceFn() {
14
+ return this.freshdesk.filterContacts;
15
+ },
16
+ getTsField() {
17
+ return "created_at";
18
+ },
19
+ generateMeta(item) {
20
+ return {
21
+ id: item.id,
22
+ summary: `New Contact: "${item.name}"`,
23
+ ts: Date.parse(item.created_at),
24
+ };
25
+ },
48
26
  },
49
27
  };
@@ -1,52 +1,27 @@
1
- import freshdesk from "../../freshdesk.app.mjs";
2
- import moment from "moment";
3
- import { DEFAULT_POLLING_SOURCE_TIMER_INTERVAL } from "@pipedream/platform";
1
+ import common from "../common/polling.mjs";
4
2
 
5
3
  export default {
4
+ ...common,
6
5
  key: "freshdesk-new-ticket",
7
6
  name: "New Ticket Created",
8
7
  description: "Emit new event when a ticket is created. [See the documentation](https://developers.freshdesk.com/api/#filter_tickets)",
9
- version: "0.0.6",
8
+ version: "0.0.8",
10
9
  type: "source",
11
- props: {
12
- freshdesk,
13
- timer: {
14
- type: "$.interface.timer",
15
- default: {
16
- intervalSeconds: DEFAULT_POLLING_SOURCE_TIMER_INTERVAL,
17
- },
18
- },
19
- db: "$.service.db",
20
- },
21
10
  dedupe: "unique",
22
- async run() {
23
- const data = [];
24
- let lastDateChecked = this.freshdesk.getLastDateChecked(this.db);
25
- if (!lastDateChecked) {
26
- lastDateChecked = new Date().toISOString();
27
- this.freshdesk.setLastDateChecked(this.db, lastDateChecked);
28
- }
29
- const formatedDate = lastDateChecked.substr(
30
- 0,
31
- (lastDateChecked + "T").indexOf("T"),
32
- );
33
- const tickets = await this.freshdesk.filterTickets({
34
- query: `"created_at:>'${formatedDate}'"`,
35
- page: 1,
36
- });
37
- for await (const ticket of tickets) {
38
- data.push(ticket);
39
- }
40
- data &&
41
- data.reverse().forEach((ticket) => {
42
- this.freshdesk.setLastDateChecked(this.db, ticket.created_at);
43
- if (moment(ticket.created_at).isAfter(lastDateChecked)) {
44
- this.$emit(ticket, {
45
- id: ticket.id,
46
- summary: `New Ticket (ID: ${ticket.id})`,
47
- ts: Date.parse(ticket.created_at),
48
- });
49
- }
50
- });
11
+ methods: {
12
+ ...common.methods,
13
+ getResourceFn() {
14
+ return this.freshdesk.filterTickets;
15
+ },
16
+ getTsField() {
17
+ return "created_at";
18
+ },
19
+ generateMeta(item) {
20
+ return {
21
+ id: item.id,
22
+ summary: `New Ticket (ID: ${item.id})`,
23
+ ts: Date.parse(item.created_at),
24
+ };
25
+ },
51
26
  },
52
27
  };
@@ -0,0 +1,28 @@
1
+ import common from "../common/polling.mjs";
2
+
3
+ export default {
4
+ ...common,
5
+ key: "freshdesk-ticket-updated",
6
+ name: "Ticket Updated",
7
+ description: "Emit new event when a ticket is updated. [See the documentation](https://developers.freshdesk.com/api/#filter_tickets)",
8
+ version: "0.0.1",
9
+ type: "source",
10
+ dedupe: "unique",
11
+ methods: {
12
+ ...common.methods,
13
+ getResourceFn() {
14
+ return this.freshdesk.filterTickets;
15
+ },
16
+ getTsField() {
17
+ return "updated_at";
18
+ },
19
+ generateMeta(item) {
20
+ const ts = Date.parse(item.updated_at);
21
+ return {
22
+ id: `${item.id}-${ts}`,
23
+ summary: `Ticket Updated (ID: ${item.id})`,
24
+ ts,
25
+ };
26
+ },
27
+ },
28
+ };