@pipedream/zendesk 0.6.0 → 0.7.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.
- package/README.md +92 -17
- package/actions/create-ticket/create-ticket.mjs +2 -2
- package/actions/delete-ticket/delete-ticket.mjs +2 -2
- package/actions/get-ticket-info/get-ticket-info.mjs +40 -0
- package/actions/list-tickets/list-tickets.mjs +67 -0
- package/actions/search-tickets/search-tickets.mjs +74 -0
- package/actions/update-ticket/update-ticket.mjs +2 -2
- package/common/constants.mjs +206 -0
- package/package.json +2 -2
- package/sources/common/ticket.mjs +22 -0
- package/sources/common/webhook.mjs +13 -1
- package/sources/new-ticket/new-ticket.mjs +1 -1
- package/sources/ticket-added-to-view/ticket-added-to-view.mjs +1 -1
- package/sources/ticket-closed/ticket-closed.mjs +1 -1
- package/sources/ticket-pended/ticket-pended.mjs +1 -1
- package/sources/ticket-solved/ticket-solved.mjs +1 -1
- package/sources/ticket-updated/ticket-updated.mjs +1 -1
- package/zendesk.app.mjs +94 -0
package/README.md
CHANGED
|
@@ -1,19 +1,94 @@
|
|
|
1
1
|
# Overview
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
3
|
+
The Zendesk API enables seamless integration of Zendesk's customer service platform with your existing business processes and third-party applications. By leveraging this API with Pipedream, you can automate ticket tracking, sync customer data, escalate issues, and streamline communication across multiple channels. This can significantly increase efficiency, accelerate response times, and enhance the overall customer experience. Automations can range from simple notifications to complex workflows involving data transformation and multi-step actions across various services.
|
|
4
|
+
|
|
5
|
+
# Example Use Cases
|
|
6
|
+
|
|
7
|
+
**Ticket Management Automation**
|
|
8
|
+
Automatically create Zendesk tickets from emails, chat messages, or form submissions captured in other apps like Gmail or Slack. Use Pipedream to parse the incoming information and create a ticket in Zendesk with the appropriate tags, priorities, and assignments.
|
|
9
|
+
|
|
10
|
+
**Customer Feedback Loop**
|
|
11
|
+
After a ticket is resolved, trigger a workflow to send a follow-up survey using a platform like Typeform. Record responses back in Zendesk to ensure customer feedback influences service quality. An automated workflow could tag the ticket with the feedback score or add notes for support agents.
|
|
12
|
+
|
|
13
|
+
**Real-Time Notifications for Critical Issues**
|
|
14
|
+
Set up a Pipedream workflow that monitors Zendesk for tickets with 'Urgent' priority or specific keywords and sends instant notifications to a dedicated Slack channel or via SMS through Twilio. This ensures that critical issues are promptly addressed by support teams.
|
|
15
|
+
|
|
16
|
+
# Getting Started
|
|
17
|
+
|
|
18
|
+
First, log in to your [Pipedream workspace](https://pipedream.com), then connect Zendesk either through a step or trigger in a workflow, or directly from the Connected Accounts page in Pipedream.
|
|
19
|
+
|
|
20
|
+
You'll first be prompted to enter your Zendesk subdomain. You can find this in the URL after logging into Zendesk.
|
|
21
|
+
|
|
22
|
+
The subdomain is the portion of the URL *before* `zendesk.com`.
|
|
23
|
+
|
|
24
|
+
For example, if the subdomain is `pipedream1903`, that's what you would enter in Pipedream.
|
|
25
|
+
|
|
26
|
+

|
|
27
|
+
|
|
28
|
+
Next, you'll be prompted to connect your Zendesk account. Zendesk will ask if you'd like to grant Pipedream permission to perform actions on your account; accept these permissions to continue.
|
|
29
|
+
|
|
30
|
+
And that's it! You can now automate Zendesk actions from within Pipedream workflows.
|
|
31
|
+
|
|
32
|
+
# Troubleshooting
|
|
33
|
+
|
|
34
|
+
## Status Codes
|
|
35
|
+
Responses may have the status codes described in the following sections.
|
|
36
|
+
|
|
37
|
+
### 200 range
|
|
38
|
+
The request was successful. The status is 200 for successful GET and PUT requests, 201 for most POST requests, and 204 for DELETE requests.
|
|
39
|
+
|
|
40
|
+
### 400 range
|
|
41
|
+
The request was not successful. The content type of the response may be text/plain for API-level error messages such as trying to call the API without SSL. The content type is application/json for business-level error messages because the response includes a JSON object with information about the error:
|
|
42
|
+
|
|
43
|
+
```json
|
|
44
|
+
{
|
|
45
|
+
"details": {
|
|
46
|
+
"value": [
|
|
47
|
+
{
|
|
48
|
+
"type": "blank",
|
|
49
|
+
"description": "can't be blank"
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
"type": "invalid",
|
|
53
|
+
"description": " is not properly formatted"
|
|
54
|
+
}
|
|
55
|
+
]
|
|
56
|
+
},
|
|
57
|
+
"description": "RecordValidation errors",
|
|
58
|
+
"error": "RecordInvalid"
|
|
59
|
+
}
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
If you see a response from a known endpoint that looks like plain text, you probably made a syntax error in your request. This type of response commonly occurs when making a request to a nonexistent Zendesk Support instance.
|
|
63
|
+
|
|
64
|
+
### **403**
|
|
65
|
+
|
|
66
|
+
A 403 response means the server has determined the user or the account doesn’t have the required permissions to use the API.
|
|
67
|
+
|
|
68
|
+
### **409**
|
|
69
|
+
|
|
70
|
+
A 409 response indicates a conflict with the resource you're trying to create or update.
|
|
71
|
+
|
|
72
|
+
409 errors typically occur when two or more requests try to create or change the same resource simultaneously. While Zendesk APIs can handle concurrent requests, requests shouldn't change the same resource at the same time. To avoid 409 errors, serialize requests when possible. If you receive a 409 error, you can retry your request after resolving the conflict.
|
|
73
|
+
|
|
74
|
+
The Zendesk Ticketing API provides specific parameters to prevent conflicts when updating tickets. For more information, see Protecting against ticket update collisions.
|
|
75
|
+
|
|
76
|
+
### **422 Unprocessable Entity**
|
|
77
|
+
|
|
78
|
+
A 422 response means that the content type and the syntax of the request entity are correct, but the content itself is not processable by the server. This is usually due to the request entity not being relevant to the resource that it's trying to create or update. Example: Trying to close a ticket that's already closed.
|
|
79
|
+
|
|
80
|
+
### **429**
|
|
81
|
+
|
|
82
|
+
A 429 error indicates that a usage limit has been exceeded. See the [Zendesk Rate limits](https://developer.zendesk.com/api-reference/introduction/rate-limits/).
|
|
83
|
+
|
|
84
|
+
### **500 range**
|
|
85
|
+
|
|
86
|
+
If you ever experience responses with status codes in the 500 range, the Zendesk API may be experiencing internal issues or having a scheduled maintenance during which you might receive a 503 Service Unavailable status code.
|
|
87
|
+
|
|
88
|
+
A 503 response with a `Retry-After` header indicates a database timeout or deadlock. You can retry your request after the number of seconds specified in the `Retry-After` header.
|
|
89
|
+
|
|
90
|
+
If the 503 response doesn't have a Retry-After header, Zendesk Support may be experiencing internal issues or undergoing scheduled maintenance. In such cases, check `@zendeskops` and our status page for any known issues.
|
|
91
|
+
|
|
92
|
+
When building an API client, we recommend treating any 500 status codes as a warning or temporary state. However, if the status persists and if Zendesk doesn't have a publicly announced maintenance or service disruption, contact the [Zendesk Customer Support](https://support.zendesk.com/hc/en-us/articles/360026614173).
|
|
93
|
+
|
|
94
|
+
If submitting a ticket to Zendesk, provide the `X-Zendesk-Request-Id` header included in the HTTP response. This helps the Support team track down the request in the logs more quickly.
|
|
@@ -3,9 +3,9 @@ import app from "../../zendesk.app.mjs";
|
|
|
3
3
|
export default {
|
|
4
4
|
key: "zendesk-create-ticket",
|
|
5
5
|
name: "Create Ticket",
|
|
6
|
-
description: "Creates a ticket. [See the
|
|
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.
|
|
8
|
+
version: "0.1.3",
|
|
9
9
|
props: {
|
|
10
10
|
app,
|
|
11
11
|
ticketCommentBody: {
|
|
@@ -3,9 +3,9 @@ import app from "../../zendesk.app.mjs";
|
|
|
3
3
|
export default {
|
|
4
4
|
key: "zendesk-delete-ticket",
|
|
5
5
|
name: "Delete Ticket",
|
|
6
|
-
description: "Deletes a ticket. [See the
|
|
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.
|
|
8
|
+
version: "0.1.3",
|
|
9
9
|
props: {
|
|
10
10
|
app,
|
|
11
11
|
ticketId: {
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import app from "../../zendesk.app.mjs";
|
|
2
|
+
|
|
3
|
+
export default {
|
|
4
|
+
key: "zendesk-get-ticket-info",
|
|
5
|
+
name: "Get Ticket Info",
|
|
6
|
+
description: "Retrieves information about a specific ticket. [See the documentation](https://developer.zendesk.com/api-reference/ticketing/tickets/tickets/#show-ticket).",
|
|
7
|
+
type: "action",
|
|
8
|
+
version: "0.0.1",
|
|
9
|
+
props: {
|
|
10
|
+
app,
|
|
11
|
+
ticketId: {
|
|
12
|
+
propDefinition: [
|
|
13
|
+
app,
|
|
14
|
+
"ticketId",
|
|
15
|
+
],
|
|
16
|
+
},
|
|
17
|
+
customSubdomain: {
|
|
18
|
+
propDefinition: [
|
|
19
|
+
app,
|
|
20
|
+
"customSubdomain",
|
|
21
|
+
],
|
|
22
|
+
},
|
|
23
|
+
},
|
|
24
|
+
async run({ $: step }) {
|
|
25
|
+
const {
|
|
26
|
+
ticketId,
|
|
27
|
+
customSubdomain,
|
|
28
|
+
} = this;
|
|
29
|
+
|
|
30
|
+
const response = await this.app.getTicketInfo({
|
|
31
|
+
step,
|
|
32
|
+
ticketId,
|
|
33
|
+
customSubdomain,
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
step.export("$summary", `Successfully retrieved ticket with ID ${response.ticket.id}`);
|
|
37
|
+
|
|
38
|
+
return response;
|
|
39
|
+
},
|
|
40
|
+
};
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import app from "../../zendesk.app.mjs";
|
|
2
|
+
|
|
3
|
+
export default {
|
|
4
|
+
key: "zendesk-list-tickets",
|
|
5
|
+
name: "List Tickets",
|
|
6
|
+
description: "Retrieves a list of tickets. [See the documentation](https://developer.zendesk.com/api-reference/ticketing/tickets/tickets/#list-tickets).",
|
|
7
|
+
type: "action",
|
|
8
|
+
version: "0.0.1",
|
|
9
|
+
props: {
|
|
10
|
+
app,
|
|
11
|
+
sortBy: {
|
|
12
|
+
propDefinition: [
|
|
13
|
+
app,
|
|
14
|
+
"sortBy",
|
|
15
|
+
],
|
|
16
|
+
},
|
|
17
|
+
sortOrder: {
|
|
18
|
+
propDefinition: [
|
|
19
|
+
app,
|
|
20
|
+
"sortOrder",
|
|
21
|
+
],
|
|
22
|
+
},
|
|
23
|
+
limit: {
|
|
24
|
+
propDefinition: [
|
|
25
|
+
app,
|
|
26
|
+
"limit",
|
|
27
|
+
],
|
|
28
|
+
},
|
|
29
|
+
customSubdomain: {
|
|
30
|
+
propDefinition: [
|
|
31
|
+
app,
|
|
32
|
+
"customSubdomain",
|
|
33
|
+
],
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
async run({ $: step }) {
|
|
37
|
+
const {
|
|
38
|
+
sortBy,
|
|
39
|
+
sortOrder,
|
|
40
|
+
limit,
|
|
41
|
+
customSubdomain,
|
|
42
|
+
} = this;
|
|
43
|
+
|
|
44
|
+
const results = this.app.paginate({
|
|
45
|
+
fn: this.app.listTickets,
|
|
46
|
+
args: {
|
|
47
|
+
step,
|
|
48
|
+
customSubdomain,
|
|
49
|
+
params: {
|
|
50
|
+
sort_by: sortBy,
|
|
51
|
+
sort_order: sortOrder,
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
resourceKey: "tickets",
|
|
55
|
+
max: limit,
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
const tickets = [];
|
|
59
|
+
for await (const ticket of results) {
|
|
60
|
+
tickets.push(ticket);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
step.export("$summary", `Successfully retrieved ${tickets.length} tickets`);
|
|
64
|
+
|
|
65
|
+
return tickets;
|
|
66
|
+
},
|
|
67
|
+
};
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import app from "../../zendesk.app.mjs";
|
|
2
|
+
|
|
3
|
+
export default {
|
|
4
|
+
key: "zendesk-search-tickets",
|
|
5
|
+
name: "Search Tickets",
|
|
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
|
+
type: "action",
|
|
8
|
+
version: "0.0.1",
|
|
9
|
+
props: {
|
|
10
|
+
app,
|
|
11
|
+
query: {
|
|
12
|
+
type: "string",
|
|
13
|
+
label: "Search Query",
|
|
14
|
+
description: "The search query to find tickets. You can use Zendesk's search syntax. Example: `type:ticket status:open priority:high`. [See the documentation](https://developer.zendesk.com/documentation/ticketing/using-the-zendesk-api/searching-with-the-zendesk-api/)",
|
|
15
|
+
},
|
|
16
|
+
sortBy: {
|
|
17
|
+
propDefinition: [
|
|
18
|
+
app,
|
|
19
|
+
"sortBy",
|
|
20
|
+
],
|
|
21
|
+
},
|
|
22
|
+
sortOrder: {
|
|
23
|
+
propDefinition: [
|
|
24
|
+
app,
|
|
25
|
+
"sortOrder",
|
|
26
|
+
],
|
|
27
|
+
},
|
|
28
|
+
limit: {
|
|
29
|
+
propDefinition: [
|
|
30
|
+
app,
|
|
31
|
+
"limit",
|
|
32
|
+
],
|
|
33
|
+
},
|
|
34
|
+
customSubdomain: {
|
|
35
|
+
propDefinition: [
|
|
36
|
+
app,
|
|
37
|
+
"customSubdomain",
|
|
38
|
+
],
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
async run({ $: step }) {
|
|
42
|
+
const {
|
|
43
|
+
query,
|
|
44
|
+
sortBy,
|
|
45
|
+
sortOrder,
|
|
46
|
+
limit,
|
|
47
|
+
customSubdomain,
|
|
48
|
+
} = this;
|
|
49
|
+
|
|
50
|
+
const results = this.app.paginate({
|
|
51
|
+
fn: this.app.searchTickets,
|
|
52
|
+
args: {
|
|
53
|
+
step,
|
|
54
|
+
customSubdomain,
|
|
55
|
+
params: {
|
|
56
|
+
query,
|
|
57
|
+
sort_by: sortBy,
|
|
58
|
+
sort_order: sortOrder,
|
|
59
|
+
},
|
|
60
|
+
},
|
|
61
|
+
resourceKey: "results",
|
|
62
|
+
max: limit,
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
const tickets = [];
|
|
66
|
+
for await (const ticket of results) {
|
|
67
|
+
tickets.push(ticket);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
step.export("$summary", `Successfully found ${tickets.length} tickets matching the search query`);
|
|
71
|
+
|
|
72
|
+
return tickets;
|
|
73
|
+
},
|
|
74
|
+
};
|
|
@@ -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. [See the
|
|
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.
|
|
8
|
+
version: "0.1.3",
|
|
9
9
|
props: {
|
|
10
10
|
app,
|
|
11
11
|
ticketId: {
|
package/common/constants.mjs
CHANGED
|
@@ -31,6 +31,210 @@ const TICKET_STATUS_OPTIONS = {
|
|
|
31
31
|
CLOSED: "closed",
|
|
32
32
|
};
|
|
33
33
|
|
|
34
|
+
const TICKET_FIELD_OPTIONS = [
|
|
35
|
+
{
|
|
36
|
+
label: "Current User Details",
|
|
37
|
+
value: "{{current_user.details}}",
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
label: "Current User Email",
|
|
41
|
+
value: "{{current_user.email}}",
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
label: "Current User External ID",
|
|
45
|
+
value: "{{current_user.external_id}}",
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
label: "Current User First Name",
|
|
49
|
+
value: "{{current_user.first_name}}",
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
label: "Current User Language",
|
|
53
|
+
value: "{{current_user.language}}",
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
label: "Current User Name",
|
|
57
|
+
value: "{{current_user.name}}",
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
label: "Current User Notes",
|
|
61
|
+
value: "{{current_user.notes}}",
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
label: "Current User Organization Details",
|
|
65
|
+
value: "{{current_user.organization.details}}",
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
label: "Current User Organization Name",
|
|
69
|
+
value: "{{current_user.organization.name}}",
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
label: "Current User Organization Notes",
|
|
73
|
+
value: "{{current_user.organization.notes}}",
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
label: "Current User Phone",
|
|
77
|
+
value: "{{current_user.phone}}",
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
label: "Satisfaction Current Comment",
|
|
81
|
+
value: "{{satisfaction.current_comment",
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
label: "Satisfaction Current Rating",
|
|
85
|
+
value: "{{satisfaction.current_rating}}",
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
label: "Ticket Account",
|
|
89
|
+
value: "{{ticket.account}}",
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
label: "Ticket Assignee Email",
|
|
93
|
+
value: "{{ticket.assignee.email}}",
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
label: "Ticket Assignee First Name",
|
|
97
|
+
value: "{{ticket.assignee.first_name}}",
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
label: "Ticket Assignee Last Name",
|
|
101
|
+
value: "{{ticket.assignee.last_name}}",
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
label: "Ticket Name",
|
|
105
|
+
value: "{{ticket.name}}",
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
label: "Ticket Brand Name",
|
|
109
|
+
value: "{{ticket.brand.name}}",
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
label: "Ticket CC Names",
|
|
113
|
+
value: "{{ticket.cc_names}}",
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
label: "Ticket CSS",
|
|
117
|
+
value: "{{ticket.css}}",
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
label: "Ticket Current Holiday Name",
|
|
121
|
+
value: "{{ticket.current_holiday_name}}",
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
label: "Ticket Description",
|
|
125
|
+
value: "{{ticket.description}}",
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
label: "Ticket Due Date",
|
|
129
|
+
value: "{{ticket.due_date}}",
|
|
130
|
+
},
|
|
131
|
+
{
|
|
132
|
+
label: "Ticket External ID",
|
|
133
|
+
value: "{{ticket.external_id}}",
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
label: "Ticket Group Name",
|
|
137
|
+
value: "{{ticket.group.name}}",
|
|
138
|
+
},
|
|
139
|
+
{
|
|
140
|
+
label: "Ticket Latest Comment HTML",
|
|
141
|
+
value: "{{ticket.latest_comment_html}}",
|
|
142
|
+
},
|
|
143
|
+
{
|
|
144
|
+
label: "Ticket Latest Public Comment HTML",
|
|
145
|
+
value: "{{ticket.latest_public_comment_html}}",
|
|
146
|
+
},
|
|
147
|
+
{
|
|
148
|
+
label: "Ticket Organization Details",
|
|
149
|
+
value: "{{ticket.organization.details}}",
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
label: "Ticket Organization External ID",
|
|
153
|
+
value: "{{ticket.organization.external_id}}",
|
|
154
|
+
},
|
|
155
|
+
{
|
|
156
|
+
label: "Ticket Organization Name",
|
|
157
|
+
value: "{{ticket.organization.name}}",
|
|
158
|
+
},
|
|
159
|
+
{
|
|
160
|
+
label: "Ticket Organization Notes",
|
|
161
|
+
value: "{{ticket.organization.notes}}",
|
|
162
|
+
},
|
|
163
|
+
{
|
|
164
|
+
label: "Ticket Priority",
|
|
165
|
+
value: "{{ticket.priority}}",
|
|
166
|
+
},
|
|
167
|
+
{
|
|
168
|
+
label: "Ticket Requester Details",
|
|
169
|
+
value: "{{ticket.requester.details}}",
|
|
170
|
+
},
|
|
171
|
+
{
|
|
172
|
+
label: "Ticket Requester Email",
|
|
173
|
+
value: "{{ticket.requester.email}}",
|
|
174
|
+
},
|
|
175
|
+
{
|
|
176
|
+
label: "Ticket Requester External ID",
|
|
177
|
+
value: "{{ticket.requester.external_id}}",
|
|
178
|
+
},
|
|
179
|
+
{
|
|
180
|
+
label: "Ticket Requester First Name",
|
|
181
|
+
value: "{{ticket.requester.first_name}}",
|
|
182
|
+
},
|
|
183
|
+
{
|
|
184
|
+
label: "Ticket Requester Language",
|
|
185
|
+
value: "{{ticket.requester.language}}",
|
|
186
|
+
},
|
|
187
|
+
{
|
|
188
|
+
label: "Ticket Requester Last Name",
|
|
189
|
+
value: "{{ticket.requester.last_name}}",
|
|
190
|
+
},
|
|
191
|
+
{
|
|
192
|
+
label: "Ticket Requester Name",
|
|
193
|
+
value: "{{ticket.requester.name}}",
|
|
194
|
+
},
|
|
195
|
+
{
|
|
196
|
+
label: "Ticket Requester Phone",
|
|
197
|
+
value: "{{ticket.requester.phone}}",
|
|
198
|
+
},
|
|
199
|
+
{
|
|
200
|
+
label: "Ticket Status",
|
|
201
|
+
value: "{{ticket.status}}",
|
|
202
|
+
},
|
|
203
|
+
{
|
|
204
|
+
label: "Ticket Tags",
|
|
205
|
+
value: "{{ticket.tags}}",
|
|
206
|
+
},
|
|
207
|
+
{
|
|
208
|
+
label: "Ticket Title",
|
|
209
|
+
value: "{{ticket.title}}",
|
|
210
|
+
},
|
|
211
|
+
{
|
|
212
|
+
label: "Ticket Type",
|
|
213
|
+
value: "{{ticket.ticket_type}}",
|
|
214
|
+
},
|
|
215
|
+
{
|
|
216
|
+
label: "Ticket URL",
|
|
217
|
+
value: "{{ticket.url}}",
|
|
218
|
+
},
|
|
219
|
+
{
|
|
220
|
+
label: "Ticket Via",
|
|
221
|
+
value: "{{ticket.via}}",
|
|
222
|
+
},
|
|
223
|
+
];
|
|
224
|
+
|
|
225
|
+
const SORT_BY_OPTIONS = [
|
|
226
|
+
"assignee",
|
|
227
|
+
"assignee.name",
|
|
228
|
+
"created_at",
|
|
229
|
+
"group",
|
|
230
|
+
"id",
|
|
231
|
+
"requester",
|
|
232
|
+
"requester.name",
|
|
233
|
+
"status",
|
|
234
|
+
"subject",
|
|
235
|
+
"updated_at",
|
|
236
|
+
];
|
|
237
|
+
|
|
34
238
|
export default {
|
|
35
239
|
SUBDOMAIN_PLACEHOLDER,
|
|
36
240
|
BASE_URL,
|
|
@@ -50,4 +254,6 @@ export default {
|
|
|
50
254
|
DEFAULT_TIMEOUT,
|
|
51
255
|
TICKET_PRIORITY_OPTIONS,
|
|
52
256
|
TICKET_STATUS_OPTIONS,
|
|
257
|
+
TICKET_FIELD_OPTIONS,
|
|
258
|
+
SORT_BY_OPTIONS,
|
|
53
259
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pipedream/zendesk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"description": "Pipedream Zendesk Components",
|
|
5
5
|
"main": "zendesk.app.mjs",
|
|
6
6
|
"keywords": [
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
"access": "public"
|
|
15
15
|
},
|
|
16
16
|
"dependencies": {
|
|
17
|
-
"@pipedream/platform": "^0.
|
|
17
|
+
"@pipedream/platform": "^3.0.3",
|
|
18
18
|
"crypto": "^1.0.1"
|
|
19
19
|
}
|
|
20
20
|
}
|
|
@@ -4,7 +4,29 @@ export default {
|
|
|
4
4
|
...common,
|
|
5
5
|
methods: {
|
|
6
6
|
...common.methods,
|
|
7
|
+
convertToCamelCase(str) {
|
|
8
|
+
return str.replace(/(?:^\w|[A-Z]|\b\w)/g, function(match, index) {
|
|
9
|
+
return index === 0
|
|
10
|
+
? match.toLowerCase()
|
|
11
|
+
: match.toUpperCase();
|
|
12
|
+
}).replace(/\s+/g, "");
|
|
13
|
+
},
|
|
7
14
|
getTriggerPayload() {
|
|
15
|
+
if (this.jsonBody) {
|
|
16
|
+
return JSON.parse(this.jsonBody);
|
|
17
|
+
}
|
|
18
|
+
if (this.fields?.length) {
|
|
19
|
+
const payload = {
|
|
20
|
+
ticketId: "{{ticket.id}}",
|
|
21
|
+
createdAt: "{{ticket.created_at_with_timestamp}}",
|
|
22
|
+
updatedAt: "{{ticket.updated_at_with_timestamp}}",
|
|
23
|
+
};
|
|
24
|
+
for (const field of this.fields) {
|
|
25
|
+
const key = this.convertToCamelCase(field.label);
|
|
26
|
+
payload[key] = field.value;
|
|
27
|
+
}
|
|
28
|
+
return payload;
|
|
29
|
+
}
|
|
8
30
|
return {
|
|
9
31
|
ticketId: "{{ticket.id}}",
|
|
10
32
|
title: "{{ticket.title}}",
|
|
@@ -19,6 +19,18 @@ export default {
|
|
|
19
19
|
"customSubdomain",
|
|
20
20
|
],
|
|
21
21
|
},
|
|
22
|
+
fields: {
|
|
23
|
+
propDefinition: [
|
|
24
|
+
app,
|
|
25
|
+
"fields",
|
|
26
|
+
],
|
|
27
|
+
},
|
|
28
|
+
jsonBody: {
|
|
29
|
+
type: "string",
|
|
30
|
+
label: "JSON Body",
|
|
31
|
+
description: "Custom JSON Body of the incoming payload. Setting `jsonBody` will overwrite the `fields` prop",
|
|
32
|
+
optional: true,
|
|
33
|
+
},
|
|
22
34
|
},
|
|
23
35
|
hooks: {
|
|
24
36
|
async activate() {
|
|
@@ -234,7 +246,7 @@ export default {
|
|
|
234
246
|
|
|
235
247
|
this.$emit(payload, {
|
|
236
248
|
id,
|
|
237
|
-
summary: payload.title,
|
|
249
|
+
summary: payload.title || payload.ticketId,
|
|
238
250
|
ts,
|
|
239
251
|
});
|
|
240
252
|
},
|
|
@@ -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.
|
|
8
|
+
version: "0.0.3",
|
|
9
9
|
type: "source",
|
|
10
10
|
dedupe: "unique",
|
|
11
11
|
props: {
|
package/zendesk.app.mjs
CHANGED
|
@@ -100,6 +100,25 @@ export default {
|
|
|
100
100
|
};
|
|
101
101
|
},
|
|
102
102
|
},
|
|
103
|
+
fields: {
|
|
104
|
+
type: "string[]",
|
|
105
|
+
label: "Fields",
|
|
106
|
+
description: "Ticket fields to be included in the incoming webhook payload",
|
|
107
|
+
withLabel: true,
|
|
108
|
+
optional: true,
|
|
109
|
+
async options() {
|
|
110
|
+
// placehoders reference - https://support.zendesk.com/hc/en-us/articles/4408886858138
|
|
111
|
+
const { ticket_fields: customFields } = await this.listTicketFields();
|
|
112
|
+
const fields = customFields.reverse().map(({
|
|
113
|
+
id, title,
|
|
114
|
+
}) => ({
|
|
115
|
+
label: title,
|
|
116
|
+
value: `{{ticket.ticket_field_${id}}}`,
|
|
117
|
+
}));
|
|
118
|
+
fields.push(...constants.TICKET_FIELD_OPTIONS);
|
|
119
|
+
return fields;
|
|
120
|
+
},
|
|
121
|
+
},
|
|
103
122
|
ticketCommentBody: {
|
|
104
123
|
type: "string",
|
|
105
124
|
label: "Comment body",
|
|
@@ -125,6 +144,30 @@ export default {
|
|
|
125
144
|
optional: true,
|
|
126
145
|
options: Object.values(constants.TICKET_STATUS_OPTIONS),
|
|
127
146
|
},
|
|
147
|
+
sortBy: {
|
|
148
|
+
type: "string",
|
|
149
|
+
label: "Sort By",
|
|
150
|
+
description: "Field to sort tickets by",
|
|
151
|
+
optional: true,
|
|
152
|
+
options: constants.SORT_BY_OPTIONS,
|
|
153
|
+
},
|
|
154
|
+
sortOrder: {
|
|
155
|
+
type: "string",
|
|
156
|
+
label: "Sort Order",
|
|
157
|
+
description: "Sort order (ascending or descending)",
|
|
158
|
+
optional: true,
|
|
159
|
+
options: [
|
|
160
|
+
"asc",
|
|
161
|
+
"desc",
|
|
162
|
+
],
|
|
163
|
+
},
|
|
164
|
+
limit: {
|
|
165
|
+
type: "integer",
|
|
166
|
+
label: "Limit",
|
|
167
|
+
description: "Maximum number of tickets to return",
|
|
168
|
+
optional: true,
|
|
169
|
+
default: 100,
|
|
170
|
+
},
|
|
128
171
|
customSubdomain: {
|
|
129
172
|
type: "string",
|
|
130
173
|
label: "Custom Subdomain",
|
|
@@ -162,6 +205,20 @@ export default {
|
|
|
162
205
|
};
|
|
163
206
|
return axios(step, config);
|
|
164
207
|
},
|
|
208
|
+
getTicketInfo({
|
|
209
|
+
ticketId, ...args
|
|
210
|
+
} = {}) {
|
|
211
|
+
return this.makeRequest({
|
|
212
|
+
path: `/tickets/${ticketId}`,
|
|
213
|
+
...args,
|
|
214
|
+
});
|
|
215
|
+
},
|
|
216
|
+
searchTickets(args = {}) {
|
|
217
|
+
return this.makeRequest({
|
|
218
|
+
path: "/search",
|
|
219
|
+
...args,
|
|
220
|
+
});
|
|
221
|
+
},
|
|
165
222
|
create(args = {}) {
|
|
166
223
|
return this.makeRequest({
|
|
167
224
|
method: "post",
|
|
@@ -206,5 +263,42 @@ export default {
|
|
|
206
263
|
...args,
|
|
207
264
|
});
|
|
208
265
|
},
|
|
266
|
+
listTicketFields(args = {}) {
|
|
267
|
+
return this.makeRequest({
|
|
268
|
+
path: "/ticket_fields",
|
|
269
|
+
...args,
|
|
270
|
+
});
|
|
271
|
+
},
|
|
272
|
+
async *paginate({
|
|
273
|
+
fn, args, resourceKey, max,
|
|
274
|
+
}) {
|
|
275
|
+
args = {
|
|
276
|
+
...args,
|
|
277
|
+
params: {
|
|
278
|
+
...args?.params,
|
|
279
|
+
per_page: constants.DEFAULT_LIMIT,
|
|
280
|
+
page: 1,
|
|
281
|
+
},
|
|
282
|
+
};
|
|
283
|
+
let hasMore = true;
|
|
284
|
+
let count = 0;
|
|
285
|
+
while (hasMore) {
|
|
286
|
+
const response = await fn(args);
|
|
287
|
+
const items = resourceKey
|
|
288
|
+
? response[resourceKey]
|
|
289
|
+
: response;
|
|
290
|
+
if (!items?.length) {
|
|
291
|
+
return;
|
|
292
|
+
}
|
|
293
|
+
for (const item of items) {
|
|
294
|
+
yield item;
|
|
295
|
+
if (max && ++count >= max) {
|
|
296
|
+
return;
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
hasMore = !!response.next_page;
|
|
300
|
+
args.params.page += 1;
|
|
301
|
+
}
|
|
302
|
+
},
|
|
209
303
|
},
|
|
210
304
|
};
|