@pipedream/jira_service_desk 0.3.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.
@@ -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.3",
7
+ version: "0.0.4",
8
8
  annotations: {
9
9
  destructiveHint: false,
10
10
  openWorldHint: true,
@@ -10,7 +10,7 @@ export default {
10
10
  + " `serviceDeskId` is optional — if not provided, the tool calls **List My Requests** to discover it automatically."
11
11
  + " Returns the new request including its `issueKey` and `issueId`."
12
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.1",
13
+ version: "0.0.2",
14
14
  type: "action",
15
15
  annotations: {
16
16
  destructiveHint: false,
@@ -43,10 +43,10 @@ export default {
43
43
  },
44
44
  },
45
45
  async run({ $ }) {
46
- // Auto-discover serviceDeskId if not provided extract from existing requests
46
+ // Auto-discover serviceDeskId if not provided: extract from existing requests
47
47
  let serviceDeskId = this.serviceDeskId;
48
48
  if (!serviceDeskId) {
49
- const existingRequests = await this.app.listMyRequests({
49
+ const { results: existingRequests } = await this.app.listMyRequests({
50
50
  $,
51
51
  cloudId: this.cloudId,
52
52
  });
@@ -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.2",
8
+ version: "0.1.3",
9
9
  annotations: {
10
10
  destructiveHint: false,
11
11
  openWorldHint: true,
@@ -8,7 +8,7 @@ export default {
8
8
  + " Use this to identify who is logged in, or to filter requests by the current user's `account_id`."
9
9
  + " No `cloudId` required — this uses the Atlassian Identity API directly."
10
10
  + " [See the documentation](https://developer.atlassian.com/cloud/jira/platform/oauth-2-3lo-apps/)",
11
- version: "0.0.1",
11
+ version: "0.0.2",
12
12
  type: "action",
13
13
  annotations: {
14
14
  destructiveHint: false,
@@ -4,12 +4,13 @@ export default {
4
4
  key: "jira_service_desk-get-request",
5
5
  name: "Get Request",
6
6
  description:
7
- "Fetches the full details of a Jira Service Desk request including its field values and complete comment thread in a single response."
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."
8
9
  + " Use this to summarize a ticket without needing follow-up calls."
9
10
  + " Use **List Sites** first to obtain the required `cloudId`."
10
11
  + " Use **List My Requests** to find the `issueKey` of a request (e.g. `IT-42`)."
11
12
  + " [See the documentation](https://developer.atlassian.com/cloud/jira/service-desk/rest/api-group-request/#api-rest-servicedeskapi-request-issueidorkey-get)",
12
- version: "0.0.1",
13
+ version: "0.1.0",
13
14
  type: "action",
14
15
  annotations: {
15
16
  destructiveHint: false,
@@ -30,11 +31,21 @@ export default {
30
31
  "issueIdOrKey",
31
32
  ],
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
+ },
33
42
  },
34
43
  async run({ $ }) {
35
44
  const [
36
45
  request,
37
- comments,
46
+ {
47
+ results: comments, hasMore,
48
+ },
38
49
  ] = await Promise.all([
39
50
  this.app.getRequest({
40
51
  $,
@@ -45,16 +56,20 @@ export default {
45
56
  $,
46
57
  cloudId: this.cloudId,
47
58
  issueIdOrKey: this.issueIdOrKey,
59
+ maxResults: this.maxResults,
48
60
  }),
49
61
  ]);
50
62
 
51
63
  const summary = request.requestFieldValues?.find?.(({ fieldId }) => fieldId === "summary")?.value
52
64
  || this.issueIdOrKey;
53
65
 
54
- $.export("$summary", `Request ${request.issueKey}: ${summary} (${comments?.length ?? 0} comment(s))`);
66
+ $.export("$summary", `Request ${request.issueKey}: ${summary} (${comments.length}${hasMore
67
+ ? "+"
68
+ : ""} comment(s))`);
55
69
  return {
56
70
  ...request,
57
71
  comments,
72
+ commentsTruncated: hasMore,
58
73
  };
59
74
  },
60
75
  };
@@ -4,12 +4,14 @@ export default {
4
4
  key: "jira_service_desk-get-request-status",
5
5
  name: "Get Request Status",
6
6
  description:
7
- "Returns the status history of a Jira Service Desk request the current status plus all previous states with timestamps."
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."
8
10
  + " Use this to understand how a request has progressed through the workflow."
9
11
  + " Use **List Sites** first to obtain the required `cloudId`."
10
12
  + " Use **List My Requests** to find the `issueKey` (e.g. `IT-42`)."
11
13
  + " [See the documentation](https://developer.atlassian.com/cloud/jira/service-desk/rest/api-group-request/#api-rest-servicedeskapi-request-issueidorkey-status-get)",
12
- version: "0.0.1",
14
+ version: "1.0.0",
13
15
  type: "action",
14
16
  annotations: {
15
17
  destructiveHint: false,
@@ -30,15 +32,31 @@ export default {
30
32
  "issueIdOrKey",
31
33
  ],
32
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
+ },
33
43
  },
34
44
  async run({ $ }) {
35
- const statuses = await this.app.getRequestStatus({
45
+ const {
46
+ results: statuses, hasMore,
47
+ } = await this.app.getRequestStatus({
36
48
  $,
37
49
  cloudId: this.cloudId,
38
50
  issueIdOrKey: this.issueIdOrKey,
51
+ maxResults: this.maxResults,
39
52
  });
40
53
  const current = statuses?.[0]?.status || "unknown";
41
- $.export("$summary", `Status history for ${this.issueIdOrKey}: current status is "${current}" (${statuses?.length ?? 0} entries)`);
42
- return statuses;
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
+ };
43
61
  },
44
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.2",
7
+ version: "0.0.3",
8
8
  type: "action",
9
9
  annotations: {
10
10
  destructiveHint: false,
@@ -6,12 +6,14 @@ export default {
6
6
  description:
7
7
  "Lists Jira Service Desk requests owned or participated in by the current user."
8
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."
9
11
  + " Use **List Sites** first to obtain the required `cloudId`."
10
12
  + " Each result includes `issueKey`, `issueId`, and request field values (summary, status)."
11
13
  + " `requestStatus`: `OPEN_REQUESTS` (default), `CLOSED_REQUESTS`, or `ALL_REQUESTS`."
12
14
  + " `requestOwnership`: `OWNED_REQUESTS` (default) or `PARTICIPATED_REQUESTS`."
13
15
  + " [See the documentation](https://developer.atlassian.com/cloud/jira/service-desk/rest/api-group-request/#api-rest-servicedeskapi-request-get)",
14
- version: "0.0.1",
16
+ version: "1.0.0",
15
17
  type: "action",
16
18
  annotations: {
17
19
  destructiveHint: false,
@@ -53,16 +55,31 @@ export default {
53
55
  ],
54
56
  optional: true,
55
57
  },
58
+ maxResults: {
59
+ propDefinition: [
60
+ app,
61
+ "maxResults",
62
+ ],
63
+ description: "Maximum number of requests to return across all pages (1-1000).",
64
+ },
56
65
  },
57
66
  async run({ $ }) {
58
- const requests = await this.app.listMyRequests({
67
+ const {
68
+ results: requests, hasMore,
69
+ } = await this.app.listMyRequests({
59
70
  $,
60
71
  cloudId: this.cloudId,
61
72
  serviceDeskId: this.serviceDeskId,
62
73
  requestStatus: this.requestStatus,
63
74
  requestOwnership: this.requestOwnership,
75
+ maxResults: this.maxResults,
64
76
  });
65
- $.export("$summary", `Found ${requests?.length ?? 0} request(s)`);
66
- return requests;
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
+ };
67
84
  },
68
85
  };
@@ -4,12 +4,14 @@ export default {
4
4
  key: "jira_service_desk-list-request-transitions",
5
5
  name: "List Request Transitions",
6
6
  description:
7
- "Lists the available workflow transitions for a Jira Service Desk request returns each transition's `id` and `name`."
8
- + " **Call this before `Transition Request`** to obtain valid `transitionId` values."
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."
9
11
  + " Use **List Sites** first to obtain the required `cloudId`."
10
12
  + " Use **List My Requests** or **Get Request** to find the `issueKey` (e.g. `IT-42`)."
11
13
  + " [See the documentation](https://developer.atlassian.com/cloud/jira/service-desk/rest/api-group-request/#api-rest-servicedeskapi-request-issueidorkey-transition-get)",
12
- version: "0.0.1",
14
+ version: "1.0.0",
13
15
  type: "action",
14
16
  annotations: {
15
17
  destructiveHint: false,
@@ -30,14 +32,30 @@ export default {
30
32
  "issueIdOrKey",
31
33
  ],
32
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
+ },
33
43
  },
34
44
  async run({ $ }) {
35
- const transitions = await this.app.getRequestTransitions({
45
+ const {
46
+ results: transitions, hasMore,
47
+ } = await this.app.getRequestTransitions({
36
48
  $,
37
49
  cloudId: this.cloudId,
38
50
  issueIdOrKey: this.issueIdOrKey,
51
+ maxResults: this.maxResults,
39
52
  });
40
- $.export("$summary", `Found ${transitions?.length ?? 0} available transition(s) for ${this.issueIdOrKey}`);
41
- return transitions;
53
+ $.export("$summary", `Found ${transitions.length}${hasMore
54
+ ? "+"
55
+ : ""} available transition(s) for ${this.issueIdOrKey}`);
56
+ return {
57
+ transitions,
58
+ truncated: hasMore,
59
+ };
42
60
  },
43
61
  };
@@ -8,7 +8,7 @@ export default {
8
8
  + " **Call this tool first** to obtain the `cloudId` (returned as `id`) required by every other Jira Service Desk tool."
9
9
  + " Each site includes its `id` (cloudId), `name`, and `url`."
10
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.1",
11
+ version: "0.0.2",
12
12
  type: "action",
13
13
  annotations: {
14
14
  destructiveHint: false,
@@ -10,7 +10,7 @@ export default {
10
10
  + " Use **List My Requests** or **Get Request** to find the `issueKey` (e.g. `IT-42`)."
11
11
  + " Optionally include a comment to explain the transition."
12
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.1",
13
+ version: "0.0.2",
14
14
  type: "action",
15
15
  annotations: {
16
16
  destructiveHint: false,
@@ -34,7 +34,7 @@ export default {
34
34
  transitionId: {
35
35
  type: "string",
36
36
  label: "Transition ID",
37
- description: "The ID of the transition to perform. Use **List Request Transitions** to retrieve available transition IDs and names.",
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
38
  },
39
39
  additionalComment: {
40
40
  type: "string",
@@ -11,7 +11,7 @@ export default {
11
11
  + " `fields` is a JSON object of field name-value pairs."
12
12
  + " Example: `{\"summary\": \"Updated title\", \"priority\": {\"name\": \"High\"}}`."
13
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.1",
14
+ version: "0.0.2",
15
15
  type: "action",
16
16
  annotations: {
17
17
  destructiveHint: false,
@@ -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",
@@ -75,7 +76,16 @@ export default {
75
76
  issueIdOrKey: {
76
77
  type: "string",
77
78
  label: "Issue ID or Key",
78
- 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.",
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,
79
89
  },
80
90
  },
81
91
  methods: {
@@ -94,26 +104,76 @@ export default {
94
104
  },
95
105
  });
96
106
  },
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
+ },
97
154
  async getSites({ $ } = {}) {
98
155
  return this._makeRequest({
99
156
  $,
100
157
  path: "/oauth/token/accessible-resources",
101
158
  });
102
159
  },
103
- async getServiceDesks({ cloudId }) {
104
- const response = await this._makeRequest({
160
+ async getServiceDesks({
161
+ $, cloudId,
162
+ }) {
163
+ const { results } = await this._paginate({
164
+ $,
105
165
  path: `/ex/jira/${cloudId}/rest/servicedeskapi/servicedesk`,
106
166
  });
107
- return response.values;
167
+ return results;
108
168
  },
109
169
  async getRequestTypes({
110
170
  $, cloudId, serviceDeskId,
111
171
  }) {
112
- const response = await this._makeRequest({
172
+ const { results } = await this._paginate({
113
173
  $,
114
174
  path: `/ex/jira/${cloudId}/rest/servicedeskapi/servicedesk/${serviceDeskId}/requesttype`,
115
175
  });
116
- return response.values;
176
+ return results;
117
177
  },
118
178
  async getRequestTypeFields({
119
179
  cloudId, serviceDeskId, requestTypeId,
@@ -123,11 +183,14 @@ export default {
123
183
  });
124
184
  return response.requestTypeFields;
125
185
  },
126
- async getCustomerRequests({ cloudId }) {
127
- const response = await this._makeRequest({
186
+ async getCustomerRequests({
187
+ $, cloudId,
188
+ }) {
189
+ const { results } = await this._paginate({
190
+ $,
128
191
  path: `/ex/jira/${cloudId}/rest/servicedeskapi/request`,
129
192
  });
130
- return response.values;
193
+ return results;
131
194
  },
132
195
  async createCustomerRequest({
133
196
  cloudId, ...opts
@@ -154,19 +217,21 @@ export default {
154
217
  });
155
218
  },
156
219
  async listMyRequests({
157
- $, cloudId, serviceDeskId, requestStatus, requestOwnership,
220
+ $, cloudId, serviceDeskId, requestStatus, requestOwnership, maxResults,
158
221
  }) {
159
222
  const params = {
160
223
  requestStatus: requestStatus || "OPEN_REQUESTS",
161
224
  requestOwnership: requestOwnership || "OWNED_REQUESTS",
162
225
  };
163
- if (serviceDeskId) params.serviceDeskId = serviceDeskId;
164
- const response = await this._makeRequest({
226
+ if (serviceDeskId) {
227
+ params.serviceDeskId = serviceDeskId;
228
+ }
229
+ return this._paginate({
165
230
  $,
166
231
  path: `/ex/jira/${cloudId}/rest/servicedeskapi/request`,
167
232
  params,
233
+ maxResults,
168
234
  });
169
- return response.values;
170
235
  },
171
236
  async getRequest({
172
237
  $, cloudId, issueIdOrKey,
@@ -177,31 +242,31 @@ export default {
177
242
  });
178
243
  },
179
244
  async getRequestComments({
180
- $, cloudId, issueIdOrKey,
245
+ $, cloudId, issueIdOrKey, maxResults,
181
246
  }) {
182
- const response = await this._makeRequest({
247
+ return this._paginate({
183
248
  $,
184
249
  path: `/ex/jira/${cloudId}/rest/servicedeskapi/request/${issueIdOrKey}/comment`,
250
+ maxResults,
185
251
  });
186
- return response.values;
187
252
  },
188
253
  async getRequestStatus({
189
- $, cloudId, issueIdOrKey,
254
+ $, cloudId, issueIdOrKey, maxResults,
190
255
  }) {
191
- const response = await this._makeRequest({
256
+ return this._paginate({
192
257
  $,
193
258
  path: `/ex/jira/${cloudId}/rest/servicedeskapi/request/${issueIdOrKey}/status`,
259
+ maxResults,
194
260
  });
195
- return response.values;
196
261
  },
197
262
  async getRequestTransitions({
198
- $, cloudId, issueIdOrKey,
263
+ $, cloudId, issueIdOrKey, maxResults,
199
264
  }) {
200
- const response = await this._makeRequest({
265
+ return this._paginate({
201
266
  $,
202
267
  path: `/ex/jira/${cloudId}/rest/servicedeskapi/request/${issueIdOrKey}/transition`,
268
+ maxResults,
203
269
  });
204
- return response.values;
205
270
  },
206
271
  async transitionRequest({
207
272
  cloudId, issueIdOrKey, ...opts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pipedream/jira_service_desk",
3
- "version": "0.3.0",
3
+ "version": "1.0.0",
4
4
  "description": "Pipedream Jira Service Desk Components",
5
5
  "main": "jira_service_desk.app.mjs",
6
6
  "keywords": [
@@ -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.2",
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.2",
9
+ version: "0.0.3",
10
10
  type: "source",
11
11
  dedupe: "unique",
12
12
  methods: {