@pipedream/jira_service_desk 0.2.0 → 1.0.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/actions/create-comment-on-request/create-comment-on-request.mjs +1 -1
- package/actions/create-incident/create-incident.mjs +107 -0
- package/actions/create-request/create-request.mjs +1 -1
- package/actions/get-current-user/get-current-user.mjs +28 -0
- package/actions/get-request/get-request.mjs +75 -0
- package/actions/get-request-status/get-request-status.mjs +62 -0
- package/actions/list-cloud-id-options/list-cloud-id-options.mjs +1 -1
- package/actions/list-my-requests/list-my-requests.mjs +85 -0
- package/actions/list-request-transitions/list-request-transitions.mjs +61 -0
- package/actions/list-sites/list-sites.mjs +28 -0
- package/actions/transition-request/transition-request.mjs +67 -0
- package/actions/update-issue-fields/update-issue-fields.mjs +64 -0
- package/common/constants.mjs +20 -0
- package/jira_service_desk.app.mjs +156 -10
- package/package.json +1 -1
- package/sources/new-request-created/new-request-created.mjs +1 -1
- package/sources/request-status-updated/request-status-updated.mjs +1 -1
|
@@ -4,7 +4,7 @@ export default {
|
|
|
4
4
|
key: "jira_service_desk-create-comment-on-request",
|
|
5
5
|
name: "Create Comment on Request",
|
|
6
6
|
description: "Create a comment on a customer request. [See the documentation](https://developer.atlassian.com/cloud/jira/service-desk/rest/api-group-request/#api-rest-servicedeskapi-request-issueidorkey-comment-post)",
|
|
7
|
-
version: "0.0.
|
|
7
|
+
version: "0.0.4",
|
|
8
8
|
annotations: {
|
|
9
9
|
destructiveHint: false,
|
|
10
10
|
openWorldHint: true,
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import app from "../../jira_service_desk.app.mjs";
|
|
2
|
+
|
|
3
|
+
export default {
|
|
4
|
+
key: "jira_service_desk-create-incident",
|
|
5
|
+
name: "Create Incident",
|
|
6
|
+
description:
|
|
7
|
+
"Creates a new incident request in a Jira Service Desk."
|
|
8
|
+
+ " Auto-discovers the best-matching request type (prefers types named 'incident' or 'problem') and, if `serviceDeskId` is omitted, auto-discovers it from the user's existing requests."
|
|
9
|
+
+ " Use **List Sites** first to obtain the required `cloudId`."
|
|
10
|
+
+ " `serviceDeskId` is optional — if not provided, the tool calls **List My Requests** to discover it automatically."
|
|
11
|
+
+ " Returns the new request including its `issueKey` and `issueId`."
|
|
12
|
+
+ " [See the documentation](https://developer.atlassian.com/cloud/jira/service-desk/rest/api-group-request/#api-rest-servicedeskapi-request-post)",
|
|
13
|
+
version: "0.0.2",
|
|
14
|
+
type: "action",
|
|
15
|
+
annotations: {
|
|
16
|
+
destructiveHint: false,
|
|
17
|
+
openWorldHint: true,
|
|
18
|
+
readOnlyHint: false,
|
|
19
|
+
},
|
|
20
|
+
props: {
|
|
21
|
+
app,
|
|
22
|
+
cloudId: {
|
|
23
|
+
propDefinition: [
|
|
24
|
+
app,
|
|
25
|
+
"cloudId",
|
|
26
|
+
],
|
|
27
|
+
},
|
|
28
|
+
serviceDeskId: {
|
|
29
|
+
type: "string",
|
|
30
|
+
label: "Service Desk ID",
|
|
31
|
+
description: "The numeric ID of the service desk (e.g. `2`). If omitted, the tool auto-discovers it from your existing requests. You can also find it in your Jira Service Management project settings.",
|
|
32
|
+
optional: true,
|
|
33
|
+
},
|
|
34
|
+
summary: {
|
|
35
|
+
type: "string",
|
|
36
|
+
label: "Summary",
|
|
37
|
+
description: "The title/summary of the incident.",
|
|
38
|
+
},
|
|
39
|
+
description: {
|
|
40
|
+
type: "string",
|
|
41
|
+
label: "Description",
|
|
42
|
+
description: "A detailed description of the incident.",
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
async run({ $ }) {
|
|
46
|
+
// Auto-discover serviceDeskId if not provided: extract from existing requests
|
|
47
|
+
let serviceDeskId = this.serviceDeskId;
|
|
48
|
+
if (!serviceDeskId) {
|
|
49
|
+
const { results: existingRequests } = await this.app.listMyRequests({
|
|
50
|
+
$,
|
|
51
|
+
cloudId: this.cloudId,
|
|
52
|
+
});
|
|
53
|
+
if (!existingRequests?.length) {
|
|
54
|
+
throw new Error("No existing requests found to auto-discover serviceDeskId. Please provide serviceDeskId explicitly.");
|
|
55
|
+
}
|
|
56
|
+
const uniqueIds = [
|
|
57
|
+
...new Set(existingRequests.map((r) => r.serviceDeskId)),
|
|
58
|
+
];
|
|
59
|
+
if (uniqueIds.length > 1) {
|
|
60
|
+
throw new Error(
|
|
61
|
+
`Multiple service desks found (${uniqueIds.join(", ")}). Please provide serviceDeskId explicitly.`,
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
serviceDeskId = uniqueIds[0];
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Auto-discover the best-matching request type for an incident.
|
|
68
|
+
// Prefers types named "incident" or "problem"; falls back to first available.
|
|
69
|
+
const requestTypes = await this.app.getRequestTypes({
|
|
70
|
+
$,
|
|
71
|
+
cloudId: this.cloudId,
|
|
72
|
+
serviceDeskId,
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
if (!requestTypes?.length) {
|
|
76
|
+
throw new Error(`No request types found in service desk ${serviceDeskId}.`);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const INCIDENT_KEYWORDS = [
|
|
80
|
+
"incident",
|
|
81
|
+
"problem",
|
|
82
|
+
"system problem",
|
|
83
|
+
"issue",
|
|
84
|
+
];
|
|
85
|
+
|
|
86
|
+
const incidentType = requestTypes.find(({ name }) =>
|
|
87
|
+
INCIDENT_KEYWORDS.some((kw) => name?.toLowerCase().includes(kw))) || requestTypes[0];
|
|
88
|
+
|
|
89
|
+
const requestFieldValues = {
|
|
90
|
+
summary: this.summary,
|
|
91
|
+
description: this.description,
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
const response = await this.app.createCustomerRequest({
|
|
95
|
+
$,
|
|
96
|
+
cloudId: this.cloudId,
|
|
97
|
+
data: {
|
|
98
|
+
serviceDeskId,
|
|
99
|
+
requestTypeId: incidentType.id,
|
|
100
|
+
requestFieldValues,
|
|
101
|
+
},
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
$.export("$summary", `Created ${incidentType.name} ${response.issueKey}: ${this.summary}`);
|
|
105
|
+
return response;
|
|
106
|
+
},
|
|
107
|
+
};
|
|
@@ -5,7 +5,7 @@ export default {
|
|
|
5
5
|
name: "Create Request",
|
|
6
6
|
description:
|
|
7
7
|
"Creates a new customer request. [See the documentation](https://docs.atlassian.com/jira-servicedesk/REST/3.6.2/#servicedeskapi/request-createCustomerRequest)",
|
|
8
|
-
version: "0.1.
|
|
8
|
+
version: "0.1.3",
|
|
9
9
|
annotations: {
|
|
10
10
|
destructiveHint: false,
|
|
11
11
|
openWorldHint: true,
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import app from "../../jira_service_desk.app.mjs";
|
|
2
|
+
|
|
3
|
+
export default {
|
|
4
|
+
key: "jira_service_desk-get-current-user",
|
|
5
|
+
name: "Get Current User",
|
|
6
|
+
description:
|
|
7
|
+
"Returns the authenticated user's `account_id`, `display_name`, and `email` from Atlassian."
|
|
8
|
+
+ " Use this to identify who is logged in, or to filter requests by the current user's `account_id`."
|
|
9
|
+
+ " No `cloudId` required — this uses the Atlassian Identity API directly."
|
|
10
|
+
+ " [See the documentation](https://developer.atlassian.com/cloud/jira/platform/oauth-2-3lo-apps/)",
|
|
11
|
+
version: "0.0.2",
|
|
12
|
+
type: "action",
|
|
13
|
+
annotations: {
|
|
14
|
+
destructiveHint: false,
|
|
15
|
+
openWorldHint: true,
|
|
16
|
+
readOnlyHint: true,
|
|
17
|
+
},
|
|
18
|
+
props: {
|
|
19
|
+
app,
|
|
20
|
+
},
|
|
21
|
+
async run({ $ }) {
|
|
22
|
+
const user = await this.app.getCurrentUser({
|
|
23
|
+
$,
|
|
24
|
+
});
|
|
25
|
+
$.export("$summary", `Current user: ${user.name || user.account_id}`);
|
|
26
|
+
return user;
|
|
27
|
+
},
|
|
28
|
+
};
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import app from "../../jira_service_desk.app.mjs";
|
|
2
|
+
|
|
3
|
+
export default {
|
|
4
|
+
key: "jira_service_desk-get-request",
|
|
5
|
+
name: "Get Request",
|
|
6
|
+
description:
|
|
7
|
+
"Fetches the full details of a Jira Service Desk request including its field values and comment thread in a single response."
|
|
8
|
+
+ " Comments are paginated automatically up to `maxResults`; the summary says so when the thread was truncated."
|
|
9
|
+
+ " Use this to summarize a ticket without needing follow-up calls."
|
|
10
|
+
+ " Use **List Sites** first to obtain the required `cloudId`."
|
|
11
|
+
+ " Use **List My Requests** to find the `issueKey` of a request (e.g. `IT-42`)."
|
|
12
|
+
+ " [See the documentation](https://developer.atlassian.com/cloud/jira/service-desk/rest/api-group-request/#api-rest-servicedeskapi-request-issueidorkey-get)",
|
|
13
|
+
version: "0.1.0",
|
|
14
|
+
type: "action",
|
|
15
|
+
annotations: {
|
|
16
|
+
destructiveHint: false,
|
|
17
|
+
openWorldHint: true,
|
|
18
|
+
readOnlyHint: true,
|
|
19
|
+
},
|
|
20
|
+
props: {
|
|
21
|
+
app,
|
|
22
|
+
cloudId: {
|
|
23
|
+
propDefinition: [
|
|
24
|
+
app,
|
|
25
|
+
"cloudId",
|
|
26
|
+
],
|
|
27
|
+
},
|
|
28
|
+
issueIdOrKey: {
|
|
29
|
+
propDefinition: [
|
|
30
|
+
app,
|
|
31
|
+
"issueIdOrKey",
|
|
32
|
+
],
|
|
33
|
+
},
|
|
34
|
+
maxResults: {
|
|
35
|
+
propDefinition: [
|
|
36
|
+
app,
|
|
37
|
+
"maxResults",
|
|
38
|
+
],
|
|
39
|
+
label: "Max Comments",
|
|
40
|
+
description: "Maximum number of comments to return across all pages (1-1000).",
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
async run({ $ }) {
|
|
44
|
+
const [
|
|
45
|
+
request,
|
|
46
|
+
{
|
|
47
|
+
results: comments, hasMore,
|
|
48
|
+
},
|
|
49
|
+
] = await Promise.all([
|
|
50
|
+
this.app.getRequest({
|
|
51
|
+
$,
|
|
52
|
+
cloudId: this.cloudId,
|
|
53
|
+
issueIdOrKey: this.issueIdOrKey,
|
|
54
|
+
}),
|
|
55
|
+
this.app.getRequestComments({
|
|
56
|
+
$,
|
|
57
|
+
cloudId: this.cloudId,
|
|
58
|
+
issueIdOrKey: this.issueIdOrKey,
|
|
59
|
+
maxResults: this.maxResults,
|
|
60
|
+
}),
|
|
61
|
+
]);
|
|
62
|
+
|
|
63
|
+
const summary = request.requestFieldValues?.find?.(({ fieldId }) => fieldId === "summary")?.value
|
|
64
|
+
|| this.issueIdOrKey;
|
|
65
|
+
|
|
66
|
+
$.export("$summary", `Request ${request.issueKey}: ${summary} (${comments.length}${hasMore
|
|
67
|
+
? "+"
|
|
68
|
+
: ""} comment(s))`);
|
|
69
|
+
return {
|
|
70
|
+
...request,
|
|
71
|
+
comments,
|
|
72
|
+
commentsTruncated: hasMore,
|
|
73
|
+
};
|
|
74
|
+
},
|
|
75
|
+
};
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import app from "../../jira_service_desk.app.mjs";
|
|
2
|
+
|
|
3
|
+
export default {
|
|
4
|
+
key: "jira_service_desk-get-request-status",
|
|
5
|
+
name: "Get Request Status",
|
|
6
|
+
description:
|
|
7
|
+
"Returns the status history of a Jira Service Desk request: the current status plus previous states with timestamps."
|
|
8
|
+
+ " The history is paginated automatically up to `maxResults`."
|
|
9
|
+
+ " Returns `{ statuses, truncated }`, newest first, where `truncated` is `true` when more entries remained unfetched."
|
|
10
|
+
+ " Use this to understand how a request has progressed through the workflow."
|
|
11
|
+
+ " Use **List Sites** first to obtain the required `cloudId`."
|
|
12
|
+
+ " Use **List My Requests** to find the `issueKey` (e.g. `IT-42`)."
|
|
13
|
+
+ " [See the documentation](https://developer.atlassian.com/cloud/jira/service-desk/rest/api-group-request/#api-rest-servicedeskapi-request-issueidorkey-status-get)",
|
|
14
|
+
version: "1.0.0",
|
|
15
|
+
type: "action",
|
|
16
|
+
annotations: {
|
|
17
|
+
destructiveHint: false,
|
|
18
|
+
openWorldHint: true,
|
|
19
|
+
readOnlyHint: true,
|
|
20
|
+
},
|
|
21
|
+
props: {
|
|
22
|
+
app,
|
|
23
|
+
cloudId: {
|
|
24
|
+
propDefinition: [
|
|
25
|
+
app,
|
|
26
|
+
"cloudId",
|
|
27
|
+
],
|
|
28
|
+
},
|
|
29
|
+
issueIdOrKey: {
|
|
30
|
+
propDefinition: [
|
|
31
|
+
app,
|
|
32
|
+
"issueIdOrKey",
|
|
33
|
+
],
|
|
34
|
+
},
|
|
35
|
+
maxResults: {
|
|
36
|
+
propDefinition: [
|
|
37
|
+
app,
|
|
38
|
+
"maxResults",
|
|
39
|
+
],
|
|
40
|
+
label: "Max Entries",
|
|
41
|
+
description: "Maximum number of status history entries to return across all pages (1-1000).",
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
async run({ $ }) {
|
|
45
|
+
const {
|
|
46
|
+
results: statuses, hasMore,
|
|
47
|
+
} = await this.app.getRequestStatus({
|
|
48
|
+
$,
|
|
49
|
+
cloudId: this.cloudId,
|
|
50
|
+
issueIdOrKey: this.issueIdOrKey,
|
|
51
|
+
maxResults: this.maxResults,
|
|
52
|
+
});
|
|
53
|
+
const current = statuses?.[0]?.status || "unknown";
|
|
54
|
+
$.export("$summary", `Status history for ${this.issueIdOrKey}: current status is "${current}" (${statuses.length}${hasMore
|
|
55
|
+
? "+"
|
|
56
|
+
: ""} entries)`);
|
|
57
|
+
return {
|
|
58
|
+
statuses,
|
|
59
|
+
truncated: hasMore,
|
|
60
|
+
};
|
|
61
|
+
},
|
|
62
|
+
};
|
|
@@ -4,7 +4,7 @@ export default {
|
|
|
4
4
|
key: "jira_service_desk-list-cloud-id-options",
|
|
5
5
|
name: "List Cloud ID Options",
|
|
6
6
|
description: "Retrieves available options for the Cloud ID field.",
|
|
7
|
-
version: "0.0.
|
|
7
|
+
version: "0.0.3",
|
|
8
8
|
type: "action",
|
|
9
9
|
annotations: {
|
|
10
10
|
destructiveHint: false,
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import app from "../../jira_service_desk.app.mjs";
|
|
2
|
+
|
|
3
|
+
export default {
|
|
4
|
+
key: "jira_service_desk-list-my-requests",
|
|
5
|
+
name: "List My Requests",
|
|
6
|
+
description:
|
|
7
|
+
"Lists Jira Service Desk requests owned or participated in by the current user."
|
|
8
|
+
+ " Defaults to open requests owned by the current user."
|
|
9
|
+
+ " Results are paginated automatically up to `maxResults`."
|
|
10
|
+
+ " Returns `{ requests, truncated }`, where `truncated` is `true` when more requests remained unfetched."
|
|
11
|
+
+ " Use **List Sites** first to obtain the required `cloudId`."
|
|
12
|
+
+ " Each result includes `issueKey`, `issueId`, and request field values (summary, status)."
|
|
13
|
+
+ " `requestStatus`: `OPEN_REQUESTS` (default), `CLOSED_REQUESTS`, or `ALL_REQUESTS`."
|
|
14
|
+
+ " `requestOwnership`: `OWNED_REQUESTS` (default) or `PARTICIPATED_REQUESTS`."
|
|
15
|
+
+ " [See the documentation](https://developer.atlassian.com/cloud/jira/service-desk/rest/api-group-request/#api-rest-servicedeskapi-request-get)",
|
|
16
|
+
version: "1.0.0",
|
|
17
|
+
type: "action",
|
|
18
|
+
annotations: {
|
|
19
|
+
destructiveHint: false,
|
|
20
|
+
openWorldHint: true,
|
|
21
|
+
readOnlyHint: true,
|
|
22
|
+
},
|
|
23
|
+
props: {
|
|
24
|
+
app,
|
|
25
|
+
cloudId: {
|
|
26
|
+
propDefinition: [
|
|
27
|
+
app,
|
|
28
|
+
"cloudId",
|
|
29
|
+
],
|
|
30
|
+
},
|
|
31
|
+
serviceDeskId: {
|
|
32
|
+
type: "string",
|
|
33
|
+
label: "Service Desk ID",
|
|
34
|
+
description: "Filter results to a specific service desk. Omit to return requests from all service desks.",
|
|
35
|
+
optional: true,
|
|
36
|
+
},
|
|
37
|
+
requestStatus: {
|
|
38
|
+
type: "string",
|
|
39
|
+
label: "Request Status",
|
|
40
|
+
description: "Filter by request status. `OPEN_REQUESTS` (default), `CLOSED_REQUESTS`, or `ALL_REQUESTS`.",
|
|
41
|
+
options: [
|
|
42
|
+
"OPEN_REQUESTS",
|
|
43
|
+
"CLOSED_REQUESTS",
|
|
44
|
+
"ALL_REQUESTS",
|
|
45
|
+
],
|
|
46
|
+
optional: true,
|
|
47
|
+
},
|
|
48
|
+
requestOwnership: {
|
|
49
|
+
type: "string",
|
|
50
|
+
label: "Request Ownership",
|
|
51
|
+
description: "Filter by ownership. `OWNED_REQUESTS` (default) returns requests created by the current user. `PARTICIPATED_REQUESTS` includes requests the user commented on.",
|
|
52
|
+
options: [
|
|
53
|
+
"OWNED_REQUESTS",
|
|
54
|
+
"PARTICIPATED_REQUESTS",
|
|
55
|
+
],
|
|
56
|
+
optional: true,
|
|
57
|
+
},
|
|
58
|
+
maxResults: {
|
|
59
|
+
propDefinition: [
|
|
60
|
+
app,
|
|
61
|
+
"maxResults",
|
|
62
|
+
],
|
|
63
|
+
description: "Maximum number of requests to return across all pages (1-1000).",
|
|
64
|
+
},
|
|
65
|
+
},
|
|
66
|
+
async run({ $ }) {
|
|
67
|
+
const {
|
|
68
|
+
results: requests, hasMore,
|
|
69
|
+
} = await this.app.listMyRequests({
|
|
70
|
+
$,
|
|
71
|
+
cloudId: this.cloudId,
|
|
72
|
+
serviceDeskId: this.serviceDeskId,
|
|
73
|
+
requestStatus: this.requestStatus,
|
|
74
|
+
requestOwnership: this.requestOwnership,
|
|
75
|
+
maxResults: this.maxResults,
|
|
76
|
+
});
|
|
77
|
+
$.export("$summary", `Found ${requests.length} request(s)${hasMore
|
|
78
|
+
? ", truncated at Max Results; raise it to fetch more"
|
|
79
|
+
: ""}`);
|
|
80
|
+
return {
|
|
81
|
+
requests,
|
|
82
|
+
truncated: hasMore,
|
|
83
|
+
};
|
|
84
|
+
},
|
|
85
|
+
};
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import app from "../../jira_service_desk.app.mjs";
|
|
2
|
+
|
|
3
|
+
export default {
|
|
4
|
+
key: "jira_service_desk-list-request-transitions",
|
|
5
|
+
name: "List Request Transitions",
|
|
6
|
+
description:
|
|
7
|
+
"Lists the available workflow transitions for a Jira Service Desk request, returning each transition's `id` and `name`."
|
|
8
|
+
+ " Transitions are paginated automatically up to `maxResults`."
|
|
9
|
+
+ " Returns `{ transitions, truncated }`, where `truncated` is `true` when more transitions remained unfetched."
|
|
10
|
+
+ " Call this before **Transition Request** to obtain valid `transitionId` values."
|
|
11
|
+
+ " Use **List Sites** first to obtain the required `cloudId`."
|
|
12
|
+
+ " Use **List My Requests** or **Get Request** to find the `issueKey` (e.g. `IT-42`)."
|
|
13
|
+
+ " [See the documentation](https://developer.atlassian.com/cloud/jira/service-desk/rest/api-group-request/#api-rest-servicedeskapi-request-issueidorkey-transition-get)",
|
|
14
|
+
version: "1.0.0",
|
|
15
|
+
type: "action",
|
|
16
|
+
annotations: {
|
|
17
|
+
destructiveHint: false,
|
|
18
|
+
openWorldHint: true,
|
|
19
|
+
readOnlyHint: true,
|
|
20
|
+
},
|
|
21
|
+
props: {
|
|
22
|
+
app,
|
|
23
|
+
cloudId: {
|
|
24
|
+
propDefinition: [
|
|
25
|
+
app,
|
|
26
|
+
"cloudId",
|
|
27
|
+
],
|
|
28
|
+
},
|
|
29
|
+
issueIdOrKey: {
|
|
30
|
+
propDefinition: [
|
|
31
|
+
app,
|
|
32
|
+
"issueIdOrKey",
|
|
33
|
+
],
|
|
34
|
+
},
|
|
35
|
+
maxResults: {
|
|
36
|
+
propDefinition: [
|
|
37
|
+
app,
|
|
38
|
+
"maxResults",
|
|
39
|
+
],
|
|
40
|
+
label: "Max Transitions",
|
|
41
|
+
description: "Maximum number of transitions to return across all pages (1-1000).",
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
async run({ $ }) {
|
|
45
|
+
const {
|
|
46
|
+
results: transitions, hasMore,
|
|
47
|
+
} = await this.app.getRequestTransitions({
|
|
48
|
+
$,
|
|
49
|
+
cloudId: this.cloudId,
|
|
50
|
+
issueIdOrKey: this.issueIdOrKey,
|
|
51
|
+
maxResults: this.maxResults,
|
|
52
|
+
});
|
|
53
|
+
$.export("$summary", `Found ${transitions.length}${hasMore
|
|
54
|
+
? "+"
|
|
55
|
+
: ""} available transition(s) for ${this.issueIdOrKey}`);
|
|
56
|
+
return {
|
|
57
|
+
transitions,
|
|
58
|
+
truncated: hasMore,
|
|
59
|
+
};
|
|
60
|
+
},
|
|
61
|
+
};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import app from "../../jira_service_desk.app.mjs";
|
|
2
|
+
|
|
3
|
+
export default {
|
|
4
|
+
key: "jira_service_desk-list-sites",
|
|
5
|
+
name: "List Sites",
|
|
6
|
+
description:
|
|
7
|
+
"Returns all Atlassian cloud sites accessible to the authenticated user."
|
|
8
|
+
+ " **Call this tool first** to obtain the `cloudId` (returned as `id`) required by every other Jira Service Desk tool."
|
|
9
|
+
+ " Each site includes its `id` (cloudId), `name`, and `url`."
|
|
10
|
+
+ " [See the documentation](https://developer.atlassian.com/cloud/jira/platform/oauth-2-3lo-apps/#3-1-get-the-cloudid-for-your-site)",
|
|
11
|
+
version: "0.0.2",
|
|
12
|
+
type: "action",
|
|
13
|
+
annotations: {
|
|
14
|
+
destructiveHint: false,
|
|
15
|
+
openWorldHint: true,
|
|
16
|
+
readOnlyHint: true,
|
|
17
|
+
},
|
|
18
|
+
props: {
|
|
19
|
+
app,
|
|
20
|
+
},
|
|
21
|
+
async run({ $ }) {
|
|
22
|
+
const sites = await this.app.getSites({
|
|
23
|
+
$,
|
|
24
|
+
});
|
|
25
|
+
$.export("$summary", `Found ${sites?.length ?? 0} accessible Atlassian site(s)`);
|
|
26
|
+
return sites;
|
|
27
|
+
},
|
|
28
|
+
};
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import app from "../../jira_service_desk.app.mjs";
|
|
2
|
+
|
|
3
|
+
export default {
|
|
4
|
+
key: "jira_service_desk-transition-request",
|
|
5
|
+
name: "Transition Request",
|
|
6
|
+
description:
|
|
7
|
+
"Transitions a Jira Service Desk request to a new workflow status."
|
|
8
|
+
+ " Use **List Request Transitions** first to get valid `transitionId` values for the request."
|
|
9
|
+
+ " Use **List Sites** to obtain the required `cloudId`."
|
|
10
|
+
+ " Use **List My Requests** or **Get Request** to find the `issueKey` (e.g. `IT-42`)."
|
|
11
|
+
+ " Optionally include a comment to explain the transition."
|
|
12
|
+
+ " [See the documentation](https://developer.atlassian.com/cloud/jira/service-desk/rest/api-group-request/#api-rest-servicedeskapi-request-issueidorkey-transition-post)",
|
|
13
|
+
version: "0.0.2",
|
|
14
|
+
type: "action",
|
|
15
|
+
annotations: {
|
|
16
|
+
destructiveHint: false,
|
|
17
|
+
openWorldHint: true,
|
|
18
|
+
readOnlyHint: false,
|
|
19
|
+
},
|
|
20
|
+
props: {
|
|
21
|
+
app,
|
|
22
|
+
cloudId: {
|
|
23
|
+
propDefinition: [
|
|
24
|
+
app,
|
|
25
|
+
"cloudId",
|
|
26
|
+
],
|
|
27
|
+
},
|
|
28
|
+
issueIdOrKey: {
|
|
29
|
+
propDefinition: [
|
|
30
|
+
app,
|
|
31
|
+
"issueIdOrKey",
|
|
32
|
+
],
|
|
33
|
+
},
|
|
34
|
+
transitionId: {
|
|
35
|
+
type: "string",
|
|
36
|
+
label: "Transition ID",
|
|
37
|
+
description: "The ID of the transition to perform. Use **List Request Transitions** to retrieve available transition IDs and names (in its `transitions` array).",
|
|
38
|
+
},
|
|
39
|
+
additionalComment: {
|
|
40
|
+
type: "string",
|
|
41
|
+
label: "Additional Comment",
|
|
42
|
+
description: "Optional comment to add when performing the transition.",
|
|
43
|
+
optional: true,
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
async run({ $ }) {
|
|
47
|
+
const body = {
|
|
48
|
+
id: this.transitionId,
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
if (this.additionalComment) {
|
|
52
|
+
body.additionalComment = {
|
|
53
|
+
body: this.additionalComment,
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const response = await this.app.transitionRequest({
|
|
58
|
+
$,
|
|
59
|
+
cloudId: this.cloudId,
|
|
60
|
+
issueIdOrKey: this.issueIdOrKey,
|
|
61
|
+
data: body,
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
$.export("$summary", `Transitioned request ${this.issueIdOrKey} using transition ID ${this.transitionId}`);
|
|
65
|
+
return response;
|
|
66
|
+
},
|
|
67
|
+
};
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import app from "../../jira_service_desk.app.mjs";
|
|
2
|
+
|
|
3
|
+
export default {
|
|
4
|
+
key: "jira_service_desk-update-issue-fields",
|
|
5
|
+
name: "Update Issue Fields",
|
|
6
|
+
description:
|
|
7
|
+
"Updates fields on an existing Jira Service Desk request via the Jira platform API."
|
|
8
|
+
+ " Use this to change the summary, priority, description, or other fields after a request has been created."
|
|
9
|
+
+ " Use **List Sites** first to obtain the required `cloudId`."
|
|
10
|
+
+ " Use **List My Requests** or **Get Request** to find the `issueKey` (e.g. `IT-42`)."
|
|
11
|
+
+ " `fields` is a JSON object of field name-value pairs."
|
|
12
|
+
+ " Example: `{\"summary\": \"Updated title\", \"priority\": {\"name\": \"High\"}}`."
|
|
13
|
+
+ " [See the documentation](https://developer.atlassian.com/cloud/jira/platform/rest/v3/api-group-issues/#api-rest-api-3-issue-issueidorkey-put)",
|
|
14
|
+
version: "0.0.2",
|
|
15
|
+
type: "action",
|
|
16
|
+
annotations: {
|
|
17
|
+
destructiveHint: false,
|
|
18
|
+
openWorldHint: true,
|
|
19
|
+
readOnlyHint: false,
|
|
20
|
+
},
|
|
21
|
+
props: {
|
|
22
|
+
app,
|
|
23
|
+
cloudId: {
|
|
24
|
+
propDefinition: [
|
|
25
|
+
app,
|
|
26
|
+
"cloudId",
|
|
27
|
+
],
|
|
28
|
+
},
|
|
29
|
+
issueIdOrKey: {
|
|
30
|
+
propDefinition: [
|
|
31
|
+
app,
|
|
32
|
+
"issueIdOrKey",
|
|
33
|
+
],
|
|
34
|
+
},
|
|
35
|
+
fields: {
|
|
36
|
+
type: "string",
|
|
37
|
+
label: "Fields",
|
|
38
|
+
description: "JSON object of Jira field name-value pairs to update."
|
|
39
|
+
+ " Example: `{\"summary\": \"New title\", \"priority\": {\"name\": \"High\"}}`."
|
|
40
|
+
+ " Use **Get Request** to see current field values.",
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
async run({ $ }) {
|
|
44
|
+
const fields = JSON.parse(this.fields);
|
|
45
|
+
if (fields === null || typeof fields !== "object" || Array.isArray(fields)) {
|
|
46
|
+
throw new Error("`fields` must be a JSON object (e.g. `{\"summary\": \"New title\"}`), not an array or primitive.");
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
await this.app.updateIssueFields({
|
|
50
|
+
$,
|
|
51
|
+
cloudId: this.cloudId,
|
|
52
|
+
issueIdOrKey: this.issueIdOrKey,
|
|
53
|
+
data: {
|
|
54
|
+
fields,
|
|
55
|
+
},
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
$.export("$summary", `Updated fields on issue ${this.issueIdOrKey}`);
|
|
59
|
+
return {
|
|
60
|
+
issueIdOrKey: this.issueIdOrKey,
|
|
61
|
+
updatedFields: Object.keys(fields),
|
|
62
|
+
};
|
|
63
|
+
},
|
|
64
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// Jira Service Management caps the page size of each paginated `servicedeskapi`
|
|
2
|
+
// resource server-side, and the cap differs per resource (verified live:
|
|
3
|
+
// `/request` clamps to 100, `/request/{id}/comment` clamps to 50). Atlassian
|
|
4
|
+
// documents the cap as an implementation detail that may change, so `_paginate`
|
|
5
|
+
// requests this page size and then keys off the returned `isLastPage` and
|
|
6
|
+
// `size` rather than assuming the page it asked for is the page it got.
|
|
7
|
+
// https://developer.atlassian.com/cloud/jira/service-desk/rest/intro/#pagination
|
|
8
|
+
const PAGE_SIZE = 100;
|
|
9
|
+
|
|
10
|
+
// Ceiling on how many items a single action run will collect across pages.
|
|
11
|
+
const MAX_RESULTS_DEFAULT = 100;
|
|
12
|
+
const MAX_RESULTS_MIN = 1;
|
|
13
|
+
const MAX_RESULTS_MAX = 1000;
|
|
14
|
+
|
|
15
|
+
export default {
|
|
16
|
+
PAGE_SIZE,
|
|
17
|
+
MAX_RESULTS_DEFAULT,
|
|
18
|
+
MAX_RESULTS_MIN,
|
|
19
|
+
MAX_RESULTS_MAX,
|
|
20
|
+
};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { axios } from "@pipedream/platform";
|
|
2
|
+
import constants from "./common/constants.mjs";
|
|
2
3
|
|
|
3
4
|
export default {
|
|
4
5
|
type: "app",
|
|
@@ -72,6 +73,20 @@ export default {
|
|
|
72
73
|
}));
|
|
73
74
|
},
|
|
74
75
|
},
|
|
76
|
+
issueIdOrKey: {
|
|
77
|
+
type: "string",
|
|
78
|
+
label: "Issue ID or Key",
|
|
79
|
+
description: "The ID or key of the Jira Service Desk request (e.g. `IT-42` or `10001`). Use **List My Requests** to find the `issueKey` of a request (in its `requests` array).",
|
|
80
|
+
},
|
|
81
|
+
maxResults: {
|
|
82
|
+
type: "integer",
|
|
83
|
+
label: "Max Results",
|
|
84
|
+
description: `Maximum number of items to return across all pages (${constants.MAX_RESULTS_MIN}-${constants.MAX_RESULTS_MAX}).`,
|
|
85
|
+
optional: true,
|
|
86
|
+
default: constants.MAX_RESULTS_DEFAULT,
|
|
87
|
+
min: constants.MAX_RESULTS_MIN,
|
|
88
|
+
max: constants.MAX_RESULTS_MAX,
|
|
89
|
+
},
|
|
75
90
|
},
|
|
76
91
|
methods: {
|
|
77
92
|
_baseUrl() {
|
|
@@ -89,24 +104,76 @@ export default {
|
|
|
89
104
|
},
|
|
90
105
|
});
|
|
91
106
|
},
|
|
92
|
-
|
|
107
|
+
/**
|
|
108
|
+
* Walks a paginated `servicedeskapi` collection until the API reports
|
|
109
|
+
* `isLastPage`, or until `maxResults` items have been collected.
|
|
110
|
+
*
|
|
111
|
+
* @returns {Promise<{ results: object[], hasMore: boolean }>} the collected
|
|
112
|
+
* items and whether the API still had more to give when collection stopped.
|
|
113
|
+
*/
|
|
114
|
+
async _paginate({
|
|
115
|
+
$, path, params, maxResults = constants.MAX_RESULTS_DEFAULT,
|
|
116
|
+
}) {
|
|
117
|
+
const results = [];
|
|
118
|
+
let start = 0;
|
|
119
|
+
let isLastPage = false;
|
|
120
|
+
|
|
121
|
+
while (results.length < maxResults) {
|
|
122
|
+
const response = await this._makeRequest({
|
|
123
|
+
$,
|
|
124
|
+
path,
|
|
125
|
+
params: {
|
|
126
|
+
...params,
|
|
127
|
+
start,
|
|
128
|
+
limit: Math.min(maxResults - results.length, constants.PAGE_SIZE),
|
|
129
|
+
},
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
const values = response?.values;
|
|
133
|
+
if (!values?.length) {
|
|
134
|
+
isLastPage = true;
|
|
135
|
+
break;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
results.push(...values);
|
|
139
|
+
isLastPage = Boolean(response.isLastPage);
|
|
140
|
+
if (isLastPage) {
|
|
141
|
+
break;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// The API may cap a page below the requested `limit`, so advance by what
|
|
145
|
+
// was actually returned instead of by the requested page size.
|
|
146
|
+
start += response.size || values.length;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
return {
|
|
150
|
+
results: results.slice(0, maxResults),
|
|
151
|
+
hasMore: !isLastPage || results.length > maxResults,
|
|
152
|
+
};
|
|
153
|
+
},
|
|
154
|
+
async getSites({ $ } = {}) {
|
|
93
155
|
return this._makeRequest({
|
|
156
|
+
$,
|
|
94
157
|
path: "/oauth/token/accessible-resources",
|
|
95
158
|
});
|
|
96
159
|
},
|
|
97
|
-
async getServiceDesks({
|
|
98
|
-
|
|
160
|
+
async getServiceDesks({
|
|
161
|
+
$, cloudId,
|
|
162
|
+
}) {
|
|
163
|
+
const { results } = await this._paginate({
|
|
164
|
+
$,
|
|
99
165
|
path: `/ex/jira/${cloudId}/rest/servicedeskapi/servicedesk`,
|
|
100
166
|
});
|
|
101
|
-
return
|
|
167
|
+
return results;
|
|
102
168
|
},
|
|
103
169
|
async getRequestTypes({
|
|
104
|
-
cloudId, serviceDeskId,
|
|
170
|
+
$, cloudId, serviceDeskId,
|
|
105
171
|
}) {
|
|
106
|
-
const
|
|
172
|
+
const { results } = await this._paginate({
|
|
173
|
+
$,
|
|
107
174
|
path: `/ex/jira/${cloudId}/rest/servicedeskapi/servicedesk/${serviceDeskId}/requesttype`,
|
|
108
175
|
});
|
|
109
|
-
return
|
|
176
|
+
return results;
|
|
110
177
|
},
|
|
111
178
|
async getRequestTypeFields({
|
|
112
179
|
cloudId, serviceDeskId, requestTypeId,
|
|
@@ -116,11 +183,14 @@ export default {
|
|
|
116
183
|
});
|
|
117
184
|
return response.requestTypeFields;
|
|
118
185
|
},
|
|
119
|
-
async getCustomerRequests({
|
|
120
|
-
|
|
186
|
+
async getCustomerRequests({
|
|
187
|
+
$, cloudId,
|
|
188
|
+
}) {
|
|
189
|
+
const { results } = await this._paginate({
|
|
190
|
+
$,
|
|
121
191
|
path: `/ex/jira/${cloudId}/rest/servicedeskapi/request`,
|
|
122
192
|
});
|
|
123
|
-
return
|
|
193
|
+
return results;
|
|
124
194
|
},
|
|
125
195
|
async createCustomerRequest({
|
|
126
196
|
cloudId, ...opts
|
|
@@ -140,5 +210,81 @@ export default {
|
|
|
140
210
|
path: `/ex/jira/${cloudId}/rest/servicedeskapi/request/${requestId}/comment`,
|
|
141
211
|
});
|
|
142
212
|
},
|
|
213
|
+
async getCurrentUser({ $ } = {}) {
|
|
214
|
+
return this._makeRequest({
|
|
215
|
+
$,
|
|
216
|
+
path: "/me",
|
|
217
|
+
});
|
|
218
|
+
},
|
|
219
|
+
async listMyRequests({
|
|
220
|
+
$, cloudId, serviceDeskId, requestStatus, requestOwnership, maxResults,
|
|
221
|
+
}) {
|
|
222
|
+
const params = {
|
|
223
|
+
requestStatus: requestStatus || "OPEN_REQUESTS",
|
|
224
|
+
requestOwnership: requestOwnership || "OWNED_REQUESTS",
|
|
225
|
+
};
|
|
226
|
+
if (serviceDeskId) {
|
|
227
|
+
params.serviceDeskId = serviceDeskId;
|
|
228
|
+
}
|
|
229
|
+
return this._paginate({
|
|
230
|
+
$,
|
|
231
|
+
path: `/ex/jira/${cloudId}/rest/servicedeskapi/request`,
|
|
232
|
+
params,
|
|
233
|
+
maxResults,
|
|
234
|
+
});
|
|
235
|
+
},
|
|
236
|
+
async getRequest({
|
|
237
|
+
$, cloudId, issueIdOrKey,
|
|
238
|
+
}) {
|
|
239
|
+
return this._makeRequest({
|
|
240
|
+
$,
|
|
241
|
+
path: `/ex/jira/${cloudId}/rest/servicedeskapi/request/${issueIdOrKey}`,
|
|
242
|
+
});
|
|
243
|
+
},
|
|
244
|
+
async getRequestComments({
|
|
245
|
+
$, cloudId, issueIdOrKey, maxResults,
|
|
246
|
+
}) {
|
|
247
|
+
return this._paginate({
|
|
248
|
+
$,
|
|
249
|
+
path: `/ex/jira/${cloudId}/rest/servicedeskapi/request/${issueIdOrKey}/comment`,
|
|
250
|
+
maxResults,
|
|
251
|
+
});
|
|
252
|
+
},
|
|
253
|
+
async getRequestStatus({
|
|
254
|
+
$, cloudId, issueIdOrKey, maxResults,
|
|
255
|
+
}) {
|
|
256
|
+
return this._paginate({
|
|
257
|
+
$,
|
|
258
|
+
path: `/ex/jira/${cloudId}/rest/servicedeskapi/request/${issueIdOrKey}/status`,
|
|
259
|
+
maxResults,
|
|
260
|
+
});
|
|
261
|
+
},
|
|
262
|
+
async getRequestTransitions({
|
|
263
|
+
$, cloudId, issueIdOrKey, maxResults,
|
|
264
|
+
}) {
|
|
265
|
+
return this._paginate({
|
|
266
|
+
$,
|
|
267
|
+
path: `/ex/jira/${cloudId}/rest/servicedeskapi/request/${issueIdOrKey}/transition`,
|
|
268
|
+
maxResults,
|
|
269
|
+
});
|
|
270
|
+
},
|
|
271
|
+
async transitionRequest({
|
|
272
|
+
cloudId, issueIdOrKey, ...opts
|
|
273
|
+
}) {
|
|
274
|
+
return this._makeRequest({
|
|
275
|
+
...opts,
|
|
276
|
+
method: "POST",
|
|
277
|
+
path: `/ex/jira/${cloudId}/rest/servicedeskapi/request/${issueIdOrKey}/transition`,
|
|
278
|
+
});
|
|
279
|
+
},
|
|
280
|
+
async updateIssueFields({
|
|
281
|
+
cloudId, issueIdOrKey, ...opts
|
|
282
|
+
}) {
|
|
283
|
+
return this._makeRequest({
|
|
284
|
+
...opts,
|
|
285
|
+
method: "PUT",
|
|
286
|
+
path: `/ex/jira/${cloudId}/rest/api/3/issue/${issueIdOrKey}`,
|
|
287
|
+
});
|
|
288
|
+
},
|
|
143
289
|
},
|
|
144
290
|
};
|
package/package.json
CHANGED
|
@@ -6,7 +6,7 @@ export default {
|
|
|
6
6
|
name: "New Request Created",
|
|
7
7
|
description:
|
|
8
8
|
"Emit new event when a customer request is created. [See the documentation](https://developer.atlassian.com/cloud/jira/service-desk/rest/#api-rest-servicedeskapi-request-get)",
|
|
9
|
-
version: "0.0.
|
|
9
|
+
version: "0.0.3",
|
|
10
10
|
type: "source",
|
|
11
11
|
dedupe: "unique",
|
|
12
12
|
methods: {
|
|
@@ -6,7 +6,7 @@ export default {
|
|
|
6
6
|
name: "Request Status Updated",
|
|
7
7
|
description:
|
|
8
8
|
"Emit new event when a customer request is updated. [See the documentation](https://developer.atlassian.com/cloud/jira/service-desk/rest/#api-rest-servicedeskapi-request-get)",
|
|
9
|
-
version: "0.0.
|
|
9
|
+
version: "0.0.3",
|
|
10
10
|
type: "source",
|
|
11
11
|
dedupe: "unique",
|
|
12
12
|
methods: {
|