@pipedream/zendesk 0.8.1 → 0.8.3

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.
@@ -5,7 +5,7 @@ export default {
5
5
  name: "Add Ticket Tags",
6
6
  description: "Add tags to a ticket (appends to existing tags). [See the documentation](https://developer.zendesk.com/api-reference/ticketing/ticket-management/tags/#add-tags).",
7
7
  type: "action",
8
- version: "0.0.2",
8
+ version: "0.0.4",
9
9
  props: {
10
10
  app,
11
11
  ticketId: {
@@ -5,7 +5,7 @@ export default {
5
5
  name: "Create Ticket",
6
6
  description: "Creates a ticket. [See the documentation](https://developer.zendesk.com/api-reference/ticketing/tickets/tickets/#create-ticket).",
7
7
  type: "action",
8
- version: "0.1.6",
8
+ version: "0.1.8",
9
9
  props: {
10
10
  app,
11
11
  ticketCommentBody: {
@@ -5,7 +5,7 @@ export default {
5
5
  name: "Delete Ticket",
6
6
  description: "Deletes a ticket. [See the documentation](https://developer.zendesk.com/api-reference/ticketing/tickets/tickets/#delete-ticket).",
7
7
  type: "action",
8
- version: "0.1.6",
8
+ version: "0.1.8",
9
9
  props: {
10
10
  app,
11
11
  ticketId: {
@@ -5,7 +5,7 @@ export default {
5
5
  name: "Get Ticket Info",
6
6
  description: "Retrieves information about a specific ticket. [See the documentation](https://developer.zendesk.com/api-reference/ticketing/tickets/tickets/#show-ticket).",
7
7
  type: "action",
8
- version: "0.0.4",
8
+ version: "0.0.6",
9
9
  props: {
10
10
  app,
11
11
  ticketId: {
@@ -4,7 +4,7 @@ export default {
4
4
  key: "zendesk-get-user-info",
5
5
  name: "Get User Info",
6
6
  description: "Retrieves information about a specific user. [See the documentation](https://developer.zendesk.com/api-reference/ticketing/users/users/#show-user).",
7
- version: "0.0.1",
7
+ version: "0.0.3",
8
8
  type: "action",
9
9
  props: {
10
10
  zendesk,
@@ -4,7 +4,7 @@ export default {
4
4
  key: "zendesk-list-locales",
5
5
  name: "List Locales",
6
6
  description: "Retrieves all locales. [See the documentation](https://developer.zendesk.com/api-reference/ticketing/account-configuration/locales/).",
7
- version: "0.0.1",
7
+ version: "0.0.3",
8
8
  type: "action",
9
9
  props: {
10
10
  zendesk,
@@ -4,7 +4,7 @@ export default {
4
4
  key: "zendesk-list-macros",
5
5
  name: "List Macros",
6
6
  description: "Retrieves all macros. [See the documentation](https://developer.zendesk.com/api-reference/ticketing/business-rules/macros/#list-macros).",
7
- version: "0.0.1",
7
+ version: "0.0.3",
8
8
  type: "action",
9
9
  props: {
10
10
  zendesk,
@@ -4,7 +4,7 @@ export default {
4
4
  key: "zendesk-list-ticket-comments",
5
5
  name: "List Ticket Comments",
6
6
  description: "Retrieves all comments for a specific ticket. [See the documentation](https://developer.zendesk.com/api-reference/ticketing/tickets/ticket_comments/#list-comments).",
7
- version: "0.0.1",
7
+ version: "0.0.3",
8
8
  type: "action",
9
9
  props: {
10
10
  zendesk,
@@ -5,7 +5,7 @@ export default {
5
5
  name: "List Tickets",
6
6
  description: "Retrieves a list of tickets. [See the documentation](https://developer.zendesk.com/api-reference/ticketing/tickets/tickets/#list-tickets).",
7
7
  type: "action",
8
- version: "0.0.4",
8
+ version: "0.0.6",
9
9
  props: {
10
10
  app,
11
11
  sortBy: {
@@ -5,7 +5,7 @@ export default {
5
5
  name: "Remove Ticket Tags",
6
6
  description: "Remove specific tags from a ticket. [See the documentation](https://developer.zendesk.com/api-reference/ticketing/ticket-management/tags/#remove-tags).",
7
7
  type: "action",
8
- version: "0.0.2",
8
+ version: "0.0.4",
9
9
  props: {
10
10
  app,
11
11
  ticketId: {
@@ -5,7 +5,7 @@ export default {
5
5
  name: "Search Tickets",
6
6
  description: "Searches for tickets using Zendesk's search API. [See the documentation](https://developer.zendesk.com/api-reference/ticketing/ticket-management/search/#search-tickets).",
7
7
  type: "action",
8
- version: "0.0.5",
8
+ version: "0.0.7",
9
9
  props: {
10
10
  app,
11
11
  query: {
@@ -5,7 +5,7 @@ export default {
5
5
  name: "Set Ticket Tags",
6
6
  description: "Set tags on a ticket (replaces all existing tags). [See the documentation](https://developer.zendesk.com/api-reference/ticketing/ticket-management/tags/#set-tags).",
7
7
  type: "action",
8
- version: "0.0.2",
8
+ version: "0.0.4",
9
9
  props: {
10
10
  app,
11
11
  ticketId: {
@@ -3,9 +3,9 @@ import app from "../../zendesk.app.mjs";
3
3
  export default {
4
4
  key: "zendesk-update-ticket",
5
5
  name: "Update Ticket",
6
- description: "Updates a ticket and optionally manages tags. [See the documentation](https://developer.zendesk.com/api-reference/ticketing/tickets/tickets/#update-ticket).",
6
+ description: "Updates a ticket. [See the documentation](https://developer.zendesk.com/api-reference/ticketing/tickets/tickets/#update-ticket).",
7
7
  type: "action",
8
- version: "0.1.6",
8
+ version: "0.2.1",
9
9
  props: {
10
10
  app,
11
11
  ticketId: {
@@ -56,6 +56,12 @@ export default {
56
56
  "customSubdomain",
57
57
  ],
58
58
  },
59
+ attachments: {
60
+ propDefinition: [
61
+ app,
62
+ "attachments",
63
+ ],
64
+ },
59
65
  ticketTags: {
60
66
  propDefinition: [
61
67
  app,
@@ -83,6 +89,18 @@ export default {
83
89
  optional: true,
84
90
  default: "set",
85
91
  },
92
+ assigneeId: {
93
+ propDefinition: [
94
+ app,
95
+ "assigneeId",
96
+ ],
97
+ },
98
+ assigneeEmail: {
99
+ propDefinition: [
100
+ app,
101
+ "assigneeEmail",
102
+ ],
103
+ },
86
104
  },
87
105
  methods: {
88
106
  updateTicket({
@@ -104,8 +122,11 @@ export default {
104
122
  ticketStatus,
105
123
  ticketCommentPublic,
106
124
  customSubdomain,
125
+ attachments,
107
126
  ticketTags,
108
127
  tagAction,
128
+ assigneeId,
129
+ assigneeEmail,
109
130
  } = this;
110
131
 
111
132
  const ticketComment = ticketCommentBodyIsHTML
@@ -118,20 +139,68 @@ export default {
118
139
 
119
140
  ticketComment.public = ticketCommentPublic;
120
141
 
142
+ // Upload attachments if provided
143
+ if (attachments && attachments.length > 0) {
144
+ try {
145
+ const uploadTokens = await this.app.uploadFiles({
146
+ attachments,
147
+ customSubdomain,
148
+ step,
149
+ });
150
+
151
+ if (uploadTokens.length > 0) {
152
+ ticketComment.uploads = uploadTokens;
153
+ }
154
+ } catch (error) {
155
+ step.export("$summary", `Failed to upload attachments: ${error.message}`);
156
+ throw error;
157
+ }
158
+ }
159
+
160
+ // Build ticket data object
161
+ const ticketData = {
162
+ comment: ticketComment,
163
+ priority: ticketPriority,
164
+ subject: ticketSubject,
165
+ status: ticketStatus,
166
+ };
167
+
168
+ // Add assignee fields if provided
169
+ if (assigneeId) {
170
+ ticketData.assignee_id = assigneeId;
171
+ }
172
+ if (assigneeEmail) {
173
+ ticketData.assignee_email = assigneeEmail;
174
+ }
175
+
121
176
  const response = await this.updateTicket({
122
177
  step,
123
178
  ticketId,
124
179
  customSubdomain,
125
180
  data: {
126
- ticket: {
127
- comment: ticketComment,
128
- priority: ticketPriority,
129
- subject: ticketSubject,
130
- status: ticketStatus,
131
- },
181
+ ticket: ticketData,
132
182
  },
133
183
  });
134
184
 
185
+ const attachmentCount = ticketComment.uploads?.length || 0;
186
+ const assigneeUpdated = assigneeId || assigneeEmail;
187
+
188
+ let summary = `Successfully updated ticket with ID ${response.ticket.id}`;
189
+
190
+ const updates = [];
191
+ if (attachmentCount > 0) {
192
+ updates.push(`${attachmentCount} attachment(s)`);
193
+ }
194
+ if (assigneeUpdated) {
195
+ updates.push("assignee");
196
+ }
197
+
198
+ if (updates.length > 0) {
199
+ summary += ` with ${updates.join(" and ")}`;
200
+ }
201
+
202
+ step.export("$summary", summary);
203
+
135
204
  // Handle tag operations if tags are provided
136
205
  if (ticketTags && ticketTags.length > 0) {
137
206
  let tagResponse;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pipedream/zendesk",
3
- "version": "0.8.1",
3
+ "version": "0.8.3",
4
4
  "description": "Pipedream Zendesk Components",
5
5
  "main": "zendesk.app.mjs",
6
6
  "keywords": [
@@ -14,7 +14,8 @@
14
14
  "access": "public"
15
15
  },
16
16
  "dependencies": {
17
- "@pipedream/platform": "^3.0.3",
18
- "crypto": "^1.0.1"
17
+ "@pipedream/platform": "^3.1.0",
18
+ "crypto": "^1.0.1",
19
+ "path": "^0.12.7"
19
20
  }
20
21
  }
@@ -6,7 +6,7 @@ export default {
6
6
  name: "Locale Updated",
7
7
  type: "source",
8
8
  description: "Emit new event when a locale has been updated",
9
- version: "0.0.1",
9
+ version: "0.0.3",
10
10
  dedupe: "unique",
11
11
  async run() {
12
12
  const lastTs = this._getLastTs();
@@ -6,7 +6,7 @@ export default {
6
6
  key: "zendesk-new-ticket",
7
7
  type: "source",
8
8
  description: "Emit new event when a ticket is created",
9
- version: "0.2.6",
9
+ version: "0.2.8",
10
10
  dedupe: "unique",
11
11
  methods: {
12
12
  ...common.methods,
@@ -7,7 +7,7 @@ export default {
7
7
  key: "zendesk-new-ticket-comment-added",
8
8
  type: "source",
9
9
  description: "Emit new event when a ticket comment has been added",
10
- version: "0.0.1",
10
+ version: "0.0.3",
11
11
  dedupe: "unique",
12
12
  props: {
13
13
  app,
@@ -5,7 +5,7 @@ export default {
5
5
  key: "zendesk-ticket-added-to-view",
6
6
  name: "New Ticket Added to View (Instant)",
7
7
  description: "Emit new event when a ticket is added to the specified view",
8
- version: "0.0.6",
8
+ version: "0.0.8",
9
9
  type: "source",
10
10
  dedupe: "unique",
11
11
  props: {
@@ -6,7 +6,7 @@ export default {
6
6
  key: "zendesk-ticket-closed",
7
7
  type: "source",
8
8
  description: "Emit new event when a ticket has changed to closed status",
9
- version: "0.2.6",
9
+ version: "0.2.8",
10
10
  dedupe: "unique",
11
11
  methods: {
12
12
  ...common.methods,
@@ -6,7 +6,7 @@ export default {
6
6
  key: "zendesk-ticket-pended",
7
7
  type: "source",
8
8
  description: "Emit new event when a ticket has changed to pending status",
9
- version: "0.2.6",
9
+ version: "0.2.8",
10
10
  dedupe: "unique",
11
11
  methods: {
12
12
  ...common.methods,
@@ -6,7 +6,7 @@ export default {
6
6
  key: "zendesk-ticket-solved",
7
7
  type: "source",
8
8
  description: "Emit new event when a ticket has changed to solved status",
9
- version: "0.2.6",
9
+ version: "0.2.8",
10
10
  dedupe: "unique",
11
11
  methods: {
12
12
  ...common.methods,
@@ -6,7 +6,7 @@ export default {
6
6
  key: "zendesk-ticket-updated",
7
7
  type: "source",
8
8
  description: "Emit new event when a ticket has been updated",
9
- version: "0.2.6",
9
+ version: "0.2.8",
10
10
  dedupe: "unique",
11
11
  methods: {
12
12
  ...common.methods,
package/zendesk.app.mjs CHANGED
@@ -1,5 +1,7 @@
1
1
  import { axios } from "@pipedream/platform";
2
2
  import constants from "./common/constants.mjs";
3
+ import { getFileStreamAndMetadata } from "@pipedream/platform";
4
+ import path from "path";
3
5
 
4
6
  export default {
5
7
  type: "app",
@@ -260,12 +262,56 @@ export default {
260
262
  description: "For Enterprise Zendesk accounts: optionally specify the subdomain to use. This will override the subdomain that was provided when connecting your Zendesk account to Pipedream. For example, if you Zendesk URL is https://examplehelp.zendesk.com, your subdomain is `examplehelp`",
261
263
  optional: true,
262
264
  },
265
+ attachments: {
266
+ type: "string[]",
267
+ label: "Attachments",
268
+ description: "File paths or URLs to attach to the ticket. Multiple files can be attached.",
269
+ optional: true,
270
+ },
263
271
  ticketTags: {
264
272
  type: "string[]",
265
273
  label: "Tags",
266
274
  description: "Array of tags to apply to the ticket. These will replace any existing tags on the ticket.",
267
275
  optional: true,
268
276
  },
277
+ assigneeId: {
278
+ type: "string",
279
+ label: "Assignee ID",
280
+ description: "The ID of the agent to assign the ticket to",
281
+ optional: true,
282
+ async options({ prevContext }) {
283
+ const { afterCursor } = prevContext;
284
+
285
+ const {
286
+ users,
287
+ meta,
288
+ } = await this.listUsers({
289
+ params: {
290
+ [constants.PAGE_SIZE_PARAM]: constants.DEFAULT_LIMIT,
291
+ [constants.PAGE_AFTER_PARAM]: afterCursor,
292
+ role: "agent",
293
+ },
294
+ });
295
+
296
+ return {
297
+ context: {
298
+ afterCursor: meta.after_cursor,
299
+ },
300
+ options: users.map(({
301
+ id, name,
302
+ }) => ({
303
+ label: name,
304
+ value: id,
305
+ })),
306
+ };
307
+ },
308
+ },
309
+ assigneeEmail: {
310
+ type: "string",
311
+ label: "Assignee Email",
312
+ description: "The email address of the agent to assign the ticket to",
313
+ optional: true,
314
+ },
269
315
  },
270
316
  methods: {
271
317
  getUrl(path, customSubdomain) {
@@ -369,6 +415,94 @@ export default {
369
415
  ...args,
370
416
  });
371
417
  },
418
+ streamToBuffer(stream) {
419
+ return new Promise((resolve, reject) => {
420
+ const chunks = [];
421
+ stream.on("data", (chunk) => chunks.push(chunk));
422
+ stream.on("end", () => resolve(Buffer.concat(chunks)));
423
+ stream.on("error", reject);
424
+ });
425
+ },
426
+ /**
427
+ * Upload a single file (local path or http(s) URL) to Zendesk Uploads API.
428
+ * @param {Object} params
429
+ * @param {string} params.filePath - Local filesystem path or http(s) URL.
430
+ * @param {string} [params.filename] - Optional filename override for the upload.
431
+ * @param {string} [params.customSubdomain]
432
+ * @param {*} [params.step]
433
+ */
434
+ async uploadFile({
435
+ filePath, filename, customSubdomain, step,
436
+ }) {
437
+ if (!filePath || typeof filePath !== "string") {
438
+ throw new Error("uploadFile: 'filePath' (string) is required");
439
+ }
440
+
441
+ const {
442
+ stream, metadata,
443
+ } = await getFileStreamAndMetadata(filePath);
444
+ const fileBinary = await this.streamToBuffer(stream);
445
+
446
+ if (!filename) {
447
+ filename = path.basename(filePath);
448
+ }
449
+
450
+ return this.makeRequest({
451
+ step,
452
+ method: "post",
453
+ path: `/uploads?filename=${encodeURIComponent(filename)}`,
454
+ customSubdomain,
455
+ headers: {
456
+ "Content-Type": metadata.contentType,
457
+ "Content-Length": metadata.size,
458
+ "Accept": "application/json",
459
+ },
460
+ data: Buffer.from(fileBinary, "binary"),
461
+ });
462
+ },
463
+ async uploadFiles({
464
+ attachments, customSubdomain, step,
465
+ } = {}) {
466
+ if (!attachments || !attachments.length) {
467
+ return [];
468
+ }
469
+ const files = attachments
470
+ .map((a) => (typeof a === "string"
471
+ ? a.trim()
472
+ : a))
473
+ .filter(Boolean);
474
+
475
+ const settled = await Promise.allSettled(
476
+ files.map((attachment) =>
477
+ this.uploadFile({
478
+ filePath: attachment,
479
+ customSubdomain,
480
+ step,
481
+ })),
482
+ );
483
+
484
+ const tokens = [];
485
+ const errors = [];
486
+ settled.forEach((res, i) => {
487
+ const attachment = files[i];
488
+ if (res.status === "fulfilled") {
489
+ const token = res.value?.upload?.token;
490
+ if (!token) {
491
+ errors.push(`Upload API returned no token for ${attachment}`);
492
+ } else {
493
+ tokens.push(token);
494
+ }
495
+ } else {
496
+ const reason = res.reason?.message || String(res.reason || "Unknown error");
497
+ errors.push(`${attachment}: ${reason}`);
498
+ }
499
+ });
500
+
501
+ if (errors.length) {
502
+ throw new Error(`Failed to upload ${errors.length}/${files.length} attachment(s): ${errors.join("; ")}`);
503
+ }
504
+ return tokens;
505
+ },
372
506
  listTicketComments({
373
507
  ticketId, ...args
374
508
  } = {}) {