@rbaileysr/zephyr-managed-api 1.2.0 → 1.2.8
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 +200 -863
- package/dist/README.md +200 -863
- package/dist/groups/Private/PrivateAttachments.d.ts +697 -0
- package/dist/groups/Private/PrivateAttachments.d.ts.map +1 -0
- package/dist/groups/Private/PrivateAttachments.js +2109 -0
- package/dist/groups/Private/PrivateAuthentication.d.ts +29 -0
- package/dist/groups/Private/PrivateAuthentication.d.ts.map +1 -0
- package/dist/groups/Private/PrivateAuthentication.js +24 -0
- package/dist/groups/Private/PrivateBase.d.ts +32 -0
- package/dist/groups/Private/PrivateBase.d.ts.map +1 -0
- package/dist/groups/Private/PrivateBase.js +77 -0
- package/dist/groups/Private/PrivateComments.d.ts +149 -0
- package/dist/groups/Private/PrivateComments.d.ts.map +1 -0
- package/dist/groups/Private/PrivateComments.js +493 -0
- package/dist/groups/Private/PrivateCustomFields.d.ts +54 -0
- package/dist/groups/Private/PrivateCustomFields.d.ts.map +1 -0
- package/dist/groups/Private/PrivateCustomFields.js +150 -0
- package/dist/groups/Private/PrivateVersions.d.ts +38 -0
- package/dist/groups/Private/PrivateVersions.d.ts.map +1 -0
- package/dist/groups/Private/PrivateVersions.js +99 -0
- package/dist/groups/Private.d.ts +144 -176
- package/dist/groups/Private.d.ts.map +1 -1
- package/dist/groups/Private.js +181 -673
- package/dist/package.json +1 -1
- package/dist/types.d.ts +339 -3
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/groups/Private.js
CHANGED
|
@@ -1,441 +1,64 @@
|
|
|
1
1
|
/*!
|
|
2
2
|
* Copyright Adaptavist 2025 (c) All rights reserved
|
|
3
3
|
*/
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
4
|
+
import { PrivateBase } from './Private/PrivateBase';
|
|
5
|
+
import { PrivateComments } from './Private/PrivateComments';
|
|
6
|
+
import { PrivateCustomFields } from './Private/PrivateCustomFields';
|
|
7
|
+
import { PrivateVersions } from './Private/PrivateVersions';
|
|
8
|
+
import { PrivateAttachments } from './Private/PrivateAttachments';
|
|
9
|
+
import { PrivateAuthentication } from './Private/PrivateAuthentication';
|
|
6
10
|
/**
|
|
7
11
|
* Private API group for accessing Zephyr's private/unofficial endpoints
|
|
8
12
|
*
|
|
9
13
|
* ⚠️ WARNING: These methods use private APIs that are not officially supported.
|
|
10
14
|
* They may change or break at any time without notice.
|
|
11
15
|
*/
|
|
12
|
-
export class PrivateGroup {
|
|
16
|
+
export class PrivateGroup extends PrivateBase {
|
|
13
17
|
constructor(apiConnection) {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
this.
|
|
18
|
-
this.
|
|
19
|
-
|
|
20
|
-
this.testCaseGroup = new TestCaseGroup(apiConnection);
|
|
21
|
-
}
|
|
18
|
+
super(apiConnection);
|
|
19
|
+
this.Authentication = new PrivateAuthentication(apiConnection);
|
|
20
|
+
this.Comments = new PrivateComments(apiConnection);
|
|
21
|
+
this.CustomFields = new PrivateCustomFields(apiConnection);
|
|
22
|
+
this.Versions = new PrivateVersions(apiConnection);
|
|
23
|
+
this.Attachments = new PrivateAttachments(apiConnection);
|
|
22
24
|
}
|
|
23
25
|
/**
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
* Retrieves a short-lived JWT token (15 minutes) from Jira that is required
|
|
27
|
-
* for private Zephyr API endpoints. This token may need to be refreshed
|
|
28
|
-
* during long operations.
|
|
29
|
-
*
|
|
30
|
-
* ⚠️ WARNING: This uses a private Jira endpoint and may change without notice.
|
|
31
|
-
*
|
|
32
|
-
* @param userEmail - Jira user email address
|
|
33
|
-
* @param apiToken - Jira API token
|
|
34
|
-
* @param jiraInstanceUrl - Full Jira instance URL (e.g., 'https://your-instance.atlassian.net')
|
|
35
|
-
* @returns The Context JWT token string
|
|
36
|
-
* @throws {UnauthorizedError} If authentication fails
|
|
37
|
-
* @throws {UnexpectedError} If the JWT cannot be extracted from the response
|
|
26
|
+
* @deprecated Use Private.Authentication.getContextJwt() instead
|
|
38
27
|
*/
|
|
39
|
-
async getContextJwt(
|
|
40
|
-
|
|
41
|
-
// Create Basic Auth header
|
|
42
|
-
const credentials = btoa(`${userEmail}:${apiToken}`);
|
|
43
|
-
const headers = {
|
|
44
|
-
'Authorization': `Basic ${credentials}`,
|
|
45
|
-
'Content-Type': 'application/json',
|
|
46
|
-
};
|
|
47
|
-
try {
|
|
48
|
-
const response = await fetch(url, {
|
|
49
|
-
method: 'POST',
|
|
50
|
-
headers,
|
|
51
|
-
});
|
|
52
|
-
if (!response.ok) {
|
|
53
|
-
if (response.status === 401) {
|
|
54
|
-
throw new UnauthorizedError('Failed to authenticate with Jira. Please check your email and API token.');
|
|
55
|
-
}
|
|
56
|
-
throw new ServerError(`Failed to retrieve Context JWT. Status: ${response.status}`, response.status, response.statusText);
|
|
57
|
-
}
|
|
58
|
-
const responseText = await response.text();
|
|
59
|
-
// Extract contextJwt using regex (as per documentation)
|
|
60
|
-
const match = responseText.match(/"contextJwt":"([^"]+)"/);
|
|
61
|
-
if (!match || !match[1]) {
|
|
62
|
-
// Retry once if JWT is missing (as per documentation)
|
|
63
|
-
const retryResponse = await fetch(url, {
|
|
64
|
-
method: 'POST',
|
|
65
|
-
headers,
|
|
66
|
-
});
|
|
67
|
-
if (!retryResponse.ok) {
|
|
68
|
-
throw new ServerError(`Failed to retrieve Context JWT on retry. Status: ${retryResponse.status}`, retryResponse.status, retryResponse.statusText);
|
|
69
|
-
}
|
|
70
|
-
const retryText = await retryResponse.text();
|
|
71
|
-
const retryMatch = retryText.match(/"contextJwt":"([^"]+)"/);
|
|
72
|
-
if (!retryMatch || !retryMatch[1]) {
|
|
73
|
-
throw new UnexpectedError('Context JWT not found in response after retry. This may be a Jira bug or API change.', { responseText: retryText });
|
|
74
|
-
}
|
|
75
|
-
return retryMatch[1];
|
|
76
|
-
}
|
|
77
|
-
return match[1];
|
|
78
|
-
}
|
|
79
|
-
catch (error) {
|
|
80
|
-
if (error instanceof UnauthorizedError ||
|
|
81
|
-
error instanceof ServerError ||
|
|
82
|
-
error instanceof UnexpectedError) {
|
|
83
|
-
throw error;
|
|
84
|
-
}
|
|
85
|
-
throw new UnexpectedError('Unexpected error while retrieving Context JWT', error);
|
|
86
|
-
}
|
|
28
|
+
async getContextJwt(credentials) {
|
|
29
|
+
return this.Authentication.getContextJwt(credentials);
|
|
87
30
|
}
|
|
88
31
|
/**
|
|
89
|
-
*
|
|
90
|
-
*
|
|
91
|
-
* Creates a custom field for the specified entity type (test case, test plan, test run, or test step).
|
|
92
|
-
*
|
|
93
|
-
* ⚠️ WARNING: This uses a private Zephyr API endpoint that is not officially supported.
|
|
94
|
-
* The endpoint may change or be removed at any time without notice.
|
|
95
|
-
*
|
|
96
|
-
* @param userEmail - Jira user email address
|
|
97
|
-
* @param apiToken - Jira API token
|
|
98
|
-
* @param jiraInstanceUrl - Full Jira instance URL (e.g., 'https://your-instance.atlassian.net')
|
|
99
|
-
* @param category - Entity type for the custom field (TEST_CASE, TEST_PLAN, TEST_RUN, or TEST_STEP)
|
|
100
|
-
* @param request - Custom field creation request
|
|
101
|
-
* @returns The created custom field response
|
|
102
|
-
* @throws {BadRequestError} If the request is invalid
|
|
103
|
-
* @throws {UnauthorizedError} If authentication fails
|
|
104
|
-
* @throws {ForbiddenError} If the user doesn't have permission
|
|
105
|
-
* @throws {ServerError} If the server returns an error
|
|
32
|
+
* @deprecated Use Private.CustomFields.createCustomField() instead
|
|
106
33
|
*/
|
|
107
|
-
async createCustomField(
|
|
108
|
-
|
|
109
|
-
const contextJwt = await this.getContextJwt(userEmail, apiToken, jiraInstanceUrl);
|
|
110
|
-
// Determine endpoint based on category
|
|
111
|
-
const endpointMap = {
|
|
112
|
-
TEST_CASE: 'customfield/testcase',
|
|
113
|
-
TEST_PLAN: 'customfield/testplan',
|
|
114
|
-
TEST_RUN: 'customfield/testrun',
|
|
115
|
-
TEST_STEP: 'customfield/teststep',
|
|
116
|
-
};
|
|
117
|
-
const url = `${this.privateApiBaseUrl}/${endpointMap[category]}`;
|
|
118
|
-
const headers = {
|
|
119
|
-
'Content-Type': 'application/json',
|
|
120
|
-
'authorization': `JWT ${contextJwt}`,
|
|
121
|
-
'jira-project-id': String(request.projectId),
|
|
122
|
-
};
|
|
123
|
-
try {
|
|
124
|
-
const response = await fetch(url, {
|
|
125
|
-
method: 'POST',
|
|
126
|
-
headers,
|
|
127
|
-
body: JSON.stringify(request),
|
|
128
|
-
});
|
|
129
|
-
if (!response.ok) {
|
|
130
|
-
if (response.status === 400) {
|
|
131
|
-
const errorText = await response.text().catch(() => 'Bad Request');
|
|
132
|
-
throw new BadRequestError(`Failed to create custom field: ${errorText}`, response.statusText);
|
|
133
|
-
}
|
|
134
|
-
if (response.status === 401) {
|
|
135
|
-
throw new UnauthorizedError('Authentication failed. Please check your credentials.');
|
|
136
|
-
}
|
|
137
|
-
if (response.status === 403) {
|
|
138
|
-
throw new ForbiddenError('You do not have permission to create custom fields in this project.');
|
|
139
|
-
}
|
|
140
|
-
if (response.status === 404) {
|
|
141
|
-
throw new NotFoundError('Project or resource not found.');
|
|
142
|
-
}
|
|
143
|
-
throw new ServerError(`Failed to create custom field. Status: ${response.status}`, response.status, response.statusText);
|
|
144
|
-
}
|
|
145
|
-
return await response.json();
|
|
146
|
-
}
|
|
147
|
-
catch (error) {
|
|
148
|
-
if (error instanceof BadRequestError ||
|
|
149
|
-
error instanceof UnauthorizedError ||
|
|
150
|
-
error instanceof ForbiddenError ||
|
|
151
|
-
error instanceof NotFoundError ||
|
|
152
|
-
error instanceof ServerError) {
|
|
153
|
-
throw error;
|
|
154
|
-
}
|
|
155
|
-
throw new UnexpectedError('Unexpected error while creating custom field', error);
|
|
156
|
-
}
|
|
34
|
+
async createCustomField(credentials, category, request) {
|
|
35
|
+
return this.CustomFields.createCustomField(credentials, category, request);
|
|
157
36
|
}
|
|
158
37
|
/**
|
|
159
|
-
*
|
|
160
|
-
*
|
|
161
|
-
* Creates a new version of an existing test case. Note that when a new version
|
|
162
|
-
* is created, the testCaseId changes for that test case.
|
|
163
|
-
*
|
|
164
|
-
* ⚠️ WARNING: This uses a private Zephyr API endpoint that is not officially supported.
|
|
165
|
-
* The endpoint may change or be removed at any time without notice.
|
|
166
|
-
*
|
|
167
|
-
* @param userEmail - Jira user email address
|
|
168
|
-
* @param apiToken - Jira API token
|
|
169
|
-
* @param jiraInstanceUrl - Full Jira instance URL (e.g., 'https://your-instance.atlassian.net')
|
|
170
|
-
* @param request - Test case version creation request
|
|
171
|
-
* @param request.testCaseKey - Test case key (e.g., 'PROJ-T1'). The numeric ID will be looked up automatically if Zephyr Connector is available.
|
|
172
|
-
* @param request.projectId - Jira project ID (numeric, not the project key)
|
|
173
|
-
* @returns The created version response with id and key
|
|
174
|
-
* @throws {BadRequestError} If the request is invalid
|
|
175
|
-
* @throws {UnauthorizedError} If authentication fails
|
|
176
|
-
* @throws {ForbiddenError} If the user doesn't have permission
|
|
177
|
-
* @throws {NotFoundError} If the test case is not found
|
|
178
|
-
* @throws {ServerError} If the server returns an error (including 409 Conflict if version already exists)
|
|
179
|
-
* @throws {UnexpectedError} If test case ID cannot be looked up from key and Zephyr Connector is not available
|
|
38
|
+
* @deprecated Use Private.CustomFields.getCustomFields() instead
|
|
180
39
|
*/
|
|
181
|
-
async
|
|
182
|
-
|
|
183
|
-
let testCaseId;
|
|
184
|
-
if (this.testCaseGroup) {
|
|
185
|
-
try {
|
|
186
|
-
const testCase = await this.testCaseGroup.getTestCase({ testCaseKey: request.testCaseKey });
|
|
187
|
-
if (!testCase) {
|
|
188
|
-
throw new NotFoundError(`Test case with key '${request.testCaseKey}' not found.`);
|
|
189
|
-
}
|
|
190
|
-
testCaseId = testCase.id;
|
|
191
|
-
}
|
|
192
|
-
catch (error) {
|
|
193
|
-
if (error instanceof NotFoundError) {
|
|
194
|
-
throw new NotFoundError(`Test case with key '${request.testCaseKey}' not found.`);
|
|
195
|
-
}
|
|
196
|
-
throw new UnexpectedError(`Failed to look up test case ID from key '${request.testCaseKey}'. Ensure Zephyr Connector is configured.`, error);
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
else {
|
|
200
|
-
throw new UnexpectedError('Cannot look up test case ID from key. This method requires Zephyr Connector to be configured. Use createZephyrApi() with an API Connection.');
|
|
201
|
-
}
|
|
202
|
-
// Get Context JWT
|
|
203
|
-
const contextJwt = await this.getContextJwt(userEmail, apiToken, jiraInstanceUrl);
|
|
204
|
-
const url = `${this.privateApiBaseUrl}/testcase/${testCaseId}/newversion`;
|
|
205
|
-
const headers = {
|
|
206
|
-
'Content-Type': 'application/json',
|
|
207
|
-
'authorization': `JWT ${contextJwt}`,
|
|
208
|
-
'jira-project-id': String(request.projectId),
|
|
209
|
-
};
|
|
210
|
-
try {
|
|
211
|
-
const response = await fetch(url, {
|
|
212
|
-
method: 'POST',
|
|
213
|
-
headers,
|
|
214
|
-
body: JSON.stringify({}), // Empty body as per documentation
|
|
215
|
-
});
|
|
216
|
-
if (!response.ok) {
|
|
217
|
-
if (response.status === 400) {
|
|
218
|
-
const errorText = await response.text().catch(() => 'Bad Request');
|
|
219
|
-
throw new BadRequestError(`Failed to create test case version: ${errorText}`, response.statusText);
|
|
220
|
-
}
|
|
221
|
-
if (response.status === 401) {
|
|
222
|
-
throw new UnauthorizedError('Authentication failed. Please check your credentials.');
|
|
223
|
-
}
|
|
224
|
-
if (response.status === 403) {
|
|
225
|
-
throw new ForbiddenError('You do not have permission to create test case versions in this project.');
|
|
226
|
-
}
|
|
227
|
-
if (response.status === 404) {
|
|
228
|
-
throw new NotFoundError('Test case not found.');
|
|
229
|
-
}
|
|
230
|
-
if (response.status === 409) {
|
|
231
|
-
throw new ServerError('Conflict: A new version already exists for this test case.', response.status, response.statusText);
|
|
232
|
-
}
|
|
233
|
-
throw new ServerError(`Failed to create test case version. Status: ${response.status}`, response.status, response.statusText);
|
|
234
|
-
}
|
|
235
|
-
return await response.json();
|
|
236
|
-
}
|
|
237
|
-
catch (error) {
|
|
238
|
-
if (error instanceof BadRequestError ||
|
|
239
|
-
error instanceof UnauthorizedError ||
|
|
240
|
-
error instanceof ForbiddenError ||
|
|
241
|
-
error instanceof NotFoundError ||
|
|
242
|
-
error instanceof ServerError ||
|
|
243
|
-
error instanceof UnexpectedError) {
|
|
244
|
-
throw error;
|
|
245
|
-
}
|
|
246
|
-
throw new UnexpectedError('Unexpected error while creating test case version', error);
|
|
247
|
-
}
|
|
40
|
+
async getCustomFields(credentials, category, request) {
|
|
41
|
+
return this.CustomFields.getCustomFields(credentials, category, request);
|
|
248
42
|
}
|
|
249
43
|
/**
|
|
250
|
-
*
|
|
251
|
-
*
|
|
252
|
-
* Retrieves S3 upload credentials and configuration needed to upload an attachment.
|
|
253
|
-
*
|
|
254
|
-
* ⚠️ WARNING: This uses a private Zephyr API endpoint that is not officially supported.
|
|
255
|
-
* The endpoint may change or be removed at any time without notice.
|
|
256
|
-
*
|
|
257
|
-
* @param contextJwt - Jira Context JWT token
|
|
258
|
-
* @returns Upload details including S3 bucket URL, credentials, and policy
|
|
259
|
-
* @throws {UnauthorizedError} If authentication fails
|
|
260
|
-
* @throws {ServerError} If the server returns an error
|
|
44
|
+
* @deprecated Use Private.Versions.createTestCaseVersion() instead
|
|
261
45
|
*/
|
|
262
|
-
async
|
|
263
|
-
|
|
264
|
-
const headers = {
|
|
265
|
-
'Authorization': `JWT ${contextJwt}`,
|
|
266
|
-
'Accept': 'application/json',
|
|
267
|
-
};
|
|
268
|
-
try {
|
|
269
|
-
const response = await fetch(url, {
|
|
270
|
-
method: 'GET',
|
|
271
|
-
headers,
|
|
272
|
-
});
|
|
273
|
-
if (!response.ok) {
|
|
274
|
-
if (response.status === 401) {
|
|
275
|
-
throw new UnauthorizedError('Authentication failed. Please check your credentials.');
|
|
276
|
-
}
|
|
277
|
-
throw new ServerError(`Failed to get upload details. Status: ${response.status}`, response.status, response.statusText);
|
|
278
|
-
}
|
|
279
|
-
return await response.json();
|
|
280
|
-
}
|
|
281
|
-
catch (error) {
|
|
282
|
-
if (error instanceof UnauthorizedError || error instanceof ServerError) {
|
|
283
|
-
throw error;
|
|
284
|
-
}
|
|
285
|
-
throw new UnexpectedError('Unexpected error while getting upload details', error);
|
|
286
|
-
}
|
|
46
|
+
async createTestCaseVersion(credentials, request) {
|
|
47
|
+
return this.Versions.createTestCaseVersion(credentials, request);
|
|
287
48
|
}
|
|
288
49
|
/**
|
|
289
|
-
*
|
|
50
|
+
* Get comments for a test case using private API
|
|
290
51
|
*
|
|
291
|
-
*
|
|
292
|
-
*
|
|
293
|
-
* @param upload - Upload details from getUploadDetails
|
|
294
|
-
* @param projectId - Jira project ID
|
|
295
|
-
* @param testCaseId - Numeric test case ID
|
|
296
|
-
* @param userAccountId - Atlassian account ID
|
|
297
|
-
* @param file - File to upload (Blob or ArrayBuffer)
|
|
298
|
-
* @param fileName - Name of the file
|
|
299
|
-
* @returns S3 upload information
|
|
300
|
-
* @throws {UnexpectedError} If upload fails
|
|
301
|
-
*/
|
|
302
|
-
async uploadToS3(upload, projectId, testCaseId, userAccountId, file, fileName) {
|
|
303
|
-
const bucketUrl = upload.bucketUrl;
|
|
304
|
-
const keyPrefix = upload.keyPrefix;
|
|
305
|
-
const credential = upload.credential;
|
|
306
|
-
const date = upload.date;
|
|
307
|
-
const policy = upload.policy;
|
|
308
|
-
const signature = upload.signature;
|
|
309
|
-
// Generate UUID for file
|
|
310
|
-
const fileUuid = crypto.randomUUID();
|
|
311
|
-
const s3Key = `${keyPrefix}/project/${projectId}/testcase/${testCaseId}/${fileUuid}`;
|
|
312
|
-
// Determine MIME type
|
|
313
|
-
const mimeType = this.getMimeType(fileName);
|
|
314
|
-
// Get file size
|
|
315
|
-
const fileSize = file instanceof Blob ? file.size : file.byteLength;
|
|
316
|
-
// Create form data
|
|
317
|
-
const formData = new FormData();
|
|
318
|
-
formData.append('key', s3Key);
|
|
319
|
-
formData.append('acl', 'private');
|
|
320
|
-
formData.append('Policy', policy);
|
|
321
|
-
formData.append('X-Amz-Credential', credential);
|
|
322
|
-
formData.append('X-Amz-Algorithm', 'AWS4-HMAC-SHA256');
|
|
323
|
-
formData.append('X-Amz-Date', date);
|
|
324
|
-
formData.append('X-Amz-Signature', signature);
|
|
325
|
-
formData.append('X-Amz-Meta-user-account-id', userAccountId);
|
|
326
|
-
formData.append('X-Amz-Meta-name', fileName);
|
|
327
|
-
formData.append('Content-Type', mimeType);
|
|
328
|
-
// Add file
|
|
329
|
-
if (file instanceof Blob) {
|
|
330
|
-
formData.append('file', file, fileName);
|
|
331
|
-
}
|
|
332
|
-
else {
|
|
333
|
-
formData.append('file', new Blob([file], { type: mimeType }), fileName);
|
|
334
|
-
}
|
|
335
|
-
try {
|
|
336
|
-
const response = await fetch(bucketUrl, {
|
|
337
|
-
method: 'POST',
|
|
338
|
-
body: formData,
|
|
339
|
-
});
|
|
340
|
-
if (!response.ok && response.status !== 200 && response.status !== 201 && response.status !== 204) {
|
|
341
|
-
const errorText = await response.text().catch(() => 'Upload failed');
|
|
342
|
-
throw new UnexpectedError(`Failed to upload file to S3: ${errorText}`);
|
|
343
|
-
}
|
|
344
|
-
return {
|
|
345
|
-
s3Key,
|
|
346
|
-
fileName,
|
|
347
|
-
mimeType,
|
|
348
|
-
fileSize,
|
|
349
|
-
};
|
|
350
|
-
}
|
|
351
|
-
catch (error) {
|
|
352
|
-
if (error instanceof UnexpectedError) {
|
|
353
|
-
throw error;
|
|
354
|
-
}
|
|
355
|
-
throw new UnexpectedError('Unexpected error while uploading file to S3', error);
|
|
356
|
-
}
|
|
357
|
-
}
|
|
358
|
-
/**
|
|
359
|
-
* Save attachment metadata
|
|
360
|
-
*
|
|
361
|
-
* Saves metadata for an uploaded attachment.
|
|
362
|
-
*
|
|
363
|
-
* @param contextJwt - Jira Context JWT token
|
|
364
|
-
* @param projectId - Jira project ID
|
|
365
|
-
* @param testCaseId - Numeric test case ID
|
|
366
|
-
* @param userAccountId - Atlassian account ID
|
|
367
|
-
* @param s3Key - S3 key from upload
|
|
368
|
-
* @param fileName - File name
|
|
369
|
-
* @param mimeType - MIME type
|
|
370
|
-
* @param fileSize - File size in bytes
|
|
371
|
-
* @returns Attachment metadata response
|
|
372
|
-
* @throws {BadRequestError} If the request is invalid
|
|
373
|
-
* @throws {UnauthorizedError} If authentication fails
|
|
374
|
-
* @throws {ServerError} If the server returns an error
|
|
375
|
-
*/
|
|
376
|
-
async saveAttachmentMetadata(contextJwt, projectId, testCaseId, userAccountId, s3Key, fileName, mimeType, fileSize) {
|
|
377
|
-
const url = 'https://app.tm4j.smartbear.com/backend/rest/tests/2.0/attachment/metadata';
|
|
378
|
-
const headers = {
|
|
379
|
-
'Authorization': `JWT ${contextJwt}`,
|
|
380
|
-
'jira-project-id': String(projectId),
|
|
381
|
-
'Content-Type': 'application/json',
|
|
382
|
-
'Accept': 'application/json',
|
|
383
|
-
};
|
|
384
|
-
const createdOn = new Date().toISOString();
|
|
385
|
-
const payload = {
|
|
386
|
-
createdOn,
|
|
387
|
-
mimeType,
|
|
388
|
-
name: fileName,
|
|
389
|
-
s3Key,
|
|
390
|
-
size: fileSize,
|
|
391
|
-
testCaseId,
|
|
392
|
-
userAccountId,
|
|
393
|
-
};
|
|
394
|
-
try {
|
|
395
|
-
const response = await fetch(url, {
|
|
396
|
-
method: 'POST',
|
|
397
|
-
headers,
|
|
398
|
-
body: JSON.stringify(payload),
|
|
399
|
-
});
|
|
400
|
-
if (!response.ok) {
|
|
401
|
-
if (response.status === 400) {
|
|
402
|
-
const errorText = await response.text().catch(() => 'Bad Request');
|
|
403
|
-
throw new BadRequestError(`Failed to save attachment metadata: ${errorText}`, response.statusText);
|
|
404
|
-
}
|
|
405
|
-
if (response.status === 401) {
|
|
406
|
-
throw new UnauthorizedError('Authentication failed. Please check your credentials.');
|
|
407
|
-
}
|
|
408
|
-
throw new ServerError(`Failed to save attachment metadata. Status: ${response.status}`, response.status, response.statusText);
|
|
409
|
-
}
|
|
410
|
-
return await response.json();
|
|
411
|
-
}
|
|
412
|
-
catch (error) {
|
|
413
|
-
if (error instanceof BadRequestError ||
|
|
414
|
-
error instanceof UnauthorizedError ||
|
|
415
|
-
error instanceof ServerError) {
|
|
416
|
-
throw error;
|
|
417
|
-
}
|
|
418
|
-
throw new UnexpectedError('Unexpected error while saving attachment metadata', error);
|
|
419
|
-
}
|
|
420
|
-
}
|
|
421
|
-
/**
|
|
422
|
-
* Create a comment on a test case using private API
|
|
423
|
-
*
|
|
424
|
-
* Adds a comment to an existing test case. The comment will be associated with
|
|
425
|
-
* the specified user account ID.
|
|
52
|
+
* Retrieves all comments associated with a test case.
|
|
426
53
|
*
|
|
427
54
|
* ⚠️ WARNING: This uses a private Zephyr API endpoint that is not officially supported.
|
|
428
55
|
* The endpoint may change or be removed at any time without notice.
|
|
429
56
|
*
|
|
430
|
-
* @param
|
|
431
|
-
* @param
|
|
432
|
-
* @param jiraInstanceUrl - Full Jira instance URL (e.g., 'https://your-instance.atlassian.net')
|
|
433
|
-
* @param request - Comment creation request
|
|
57
|
+
* @param credentials - Private API credentials
|
|
58
|
+
* @param request - Get comments request
|
|
434
59
|
* @param request.testCaseKey - Test case key (e.g., 'PROJ-T1'). The numeric ID will be looked up automatically if Zephyr Connector is available.
|
|
435
60
|
* @param request.projectId - Jira project ID (numeric, not the project key)
|
|
436
|
-
* @
|
|
437
|
-
* @param request.createdBy - Atlassian account ID of the user creating the comment (e.g., '5d6fdc98dc6e480dbc021aae')
|
|
438
|
-
* @returns Comment creation response
|
|
61
|
+
* @returns Array of comments
|
|
439
62
|
* @throws {BadRequestError} If the request is invalid
|
|
440
63
|
* @throws {UnauthorizedError} If authentication fails
|
|
441
64
|
* @throws {ForbiddenError} If the user doesn't have permission
|
|
@@ -443,301 +66,186 @@ export class PrivateGroup {
|
|
|
443
66
|
* @throws {ServerError} If the server returns an error
|
|
444
67
|
* @throws {UnexpectedError} If test case ID cannot be looked up from key and Zephyr Connector is not available
|
|
445
68
|
*/
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
const testCase = await this.testCaseGroup.getTestCase({ testCaseKey: request.testCaseKey });
|
|
452
|
-
if (!testCase) {
|
|
453
|
-
throw new NotFoundError(`Test case with key '${request.testCaseKey}' not found.`);
|
|
454
|
-
}
|
|
455
|
-
testCaseId = testCase.id;
|
|
456
|
-
}
|
|
457
|
-
catch (error) {
|
|
458
|
-
if (error instanceof NotFoundError) {
|
|
459
|
-
throw new NotFoundError(`Test case with key '${request.testCaseKey}' not found.`);
|
|
460
|
-
}
|
|
461
|
-
throw new UnexpectedError(`Failed to look up test case ID from key '${request.testCaseKey}'. Ensure Zephyr Connector is configured.`, error);
|
|
462
|
-
}
|
|
463
|
-
}
|
|
464
|
-
else {
|
|
465
|
-
throw new UnexpectedError('Cannot look up test case ID from key. This method requires Zephyr Connector to be configured. Use createZephyrApi() with an API Connection.');
|
|
466
|
-
}
|
|
467
|
-
// Get Context JWT
|
|
468
|
-
const contextJwt = await this.getContextJwt(userEmail, apiToken, jiraInstanceUrl);
|
|
469
|
-
const url = `${this.privateApiBaseUrl}/testcase/${testCaseId}/comments`;
|
|
470
|
-
const headers = {
|
|
471
|
-
'Content-Type': 'application/json',
|
|
472
|
-
'authorization': `JWT ${contextJwt}`,
|
|
473
|
-
'jira-project-id': String(request.projectId),
|
|
474
|
-
};
|
|
475
|
-
const payload = {
|
|
476
|
-
body: request.body,
|
|
477
|
-
createdBy: request.createdBy,
|
|
478
|
-
};
|
|
479
|
-
try {
|
|
480
|
-
const response = await fetch(url, {
|
|
481
|
-
method: 'POST',
|
|
482
|
-
headers,
|
|
483
|
-
body: JSON.stringify(payload),
|
|
484
|
-
});
|
|
485
|
-
if (!response.ok) {
|
|
486
|
-
if (response.status === 400) {
|
|
487
|
-
const errorText = await response.text().catch(() => 'Bad Request');
|
|
488
|
-
throw new BadRequestError(`Failed to create test case comment: ${errorText}`, response.statusText);
|
|
489
|
-
}
|
|
490
|
-
if (response.status === 401) {
|
|
491
|
-
throw new UnauthorizedError('Authentication failed. Please check your credentials.');
|
|
492
|
-
}
|
|
493
|
-
if (response.status === 403) {
|
|
494
|
-
throw new ForbiddenError('You do not have permission to create comments on this test case.');
|
|
495
|
-
}
|
|
496
|
-
if (response.status === 404) {
|
|
497
|
-
throw new NotFoundError('Test case not found.');
|
|
498
|
-
}
|
|
499
|
-
throw new ServerError(`Failed to create test case comment. Status: ${response.status}`, response.status, response.statusText);
|
|
500
|
-
}
|
|
501
|
-
return await response.json();
|
|
502
|
-
}
|
|
503
|
-
catch (error) {
|
|
504
|
-
if (error instanceof BadRequestError ||
|
|
505
|
-
error instanceof UnauthorizedError ||
|
|
506
|
-
error instanceof ForbiddenError ||
|
|
507
|
-
error instanceof NotFoundError ||
|
|
508
|
-
error instanceof ServerError ||
|
|
509
|
-
error instanceof UnexpectedError) {
|
|
510
|
-
throw error;
|
|
511
|
-
}
|
|
512
|
-
throw new UnexpectedError('Unexpected error while creating test case comment', error);
|
|
513
|
-
}
|
|
69
|
+
/**
|
|
70
|
+
* @deprecated Use Private.Comments.getTestCaseComments() instead
|
|
71
|
+
*/
|
|
72
|
+
async getTestCaseComments(credentials, request) {
|
|
73
|
+
return this.Comments.getTestCaseComments(credentials, request);
|
|
514
74
|
}
|
|
515
75
|
/**
|
|
516
|
-
* Get
|
|
76
|
+
* Get comments for a test cycle using private API
|
|
517
77
|
*
|
|
518
|
-
* Retrieves all
|
|
519
|
-
* for downloading the attachments.
|
|
78
|
+
* Retrieves all comments associated with a test cycle (test run).
|
|
520
79
|
*
|
|
521
80
|
* ⚠️ WARNING: This uses a private Zephyr API endpoint that is not officially supported.
|
|
522
81
|
* The endpoint may change or be removed at any time without notice.
|
|
523
82
|
*
|
|
524
|
-
* @param
|
|
525
|
-
* @param
|
|
526
|
-
* @param
|
|
527
|
-
* @param request - Get attachments request
|
|
528
|
-
* @param request.testCaseKey - Test case key (e.g., 'PROJ-T1'). The numeric ID will be looked up automatically if Zephyr Connector is available.
|
|
83
|
+
* @param credentials - Private API credentials
|
|
84
|
+
* @param request - Get comments request
|
|
85
|
+
* @param request.testCycleId - Test cycle ID (numeric)
|
|
529
86
|
* @param request.projectId - Jira project ID (numeric, not the project key)
|
|
530
|
-
* @returns
|
|
87
|
+
* @returns Array of comments
|
|
531
88
|
* @throws {BadRequestError} If the request is invalid
|
|
532
89
|
* @throws {UnauthorizedError} If authentication fails
|
|
533
90
|
* @throws {ForbiddenError} If the user doesn't have permission
|
|
534
|
-
* @throws {NotFoundError} If the test
|
|
91
|
+
* @throws {NotFoundError} If the test cycle is not found
|
|
535
92
|
* @throws {ServerError} If the server returns an error
|
|
536
|
-
* @throws {UnexpectedError} If test case ID cannot be looked up from key and Zephyr Connector is not available
|
|
537
93
|
*/
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
const testCase = await this.testCaseGroup.getTestCase({ testCaseKey: request.testCaseKey });
|
|
544
|
-
if (!testCase) {
|
|
545
|
-
throw new NotFoundError(`Test case with key '${request.testCaseKey}' not found.`);
|
|
546
|
-
}
|
|
547
|
-
testCaseId = testCase.id;
|
|
548
|
-
}
|
|
549
|
-
catch (error) {
|
|
550
|
-
if (error instanceof NotFoundError) {
|
|
551
|
-
throw new NotFoundError(`Test case with key '${request.testCaseKey}' not found.`);
|
|
552
|
-
}
|
|
553
|
-
throw new UnexpectedError(`Failed to look up test case ID from key '${request.testCaseKey}'. Ensure Zephyr Connector is configured.`, error);
|
|
554
|
-
}
|
|
555
|
-
}
|
|
556
|
-
else {
|
|
557
|
-
throw new UnexpectedError('Cannot look up test case ID from key. This method requires Zephyr Connector to be configured. Use createZephyrApi() with an API Connection.');
|
|
558
|
-
}
|
|
559
|
-
// Get Context JWT
|
|
560
|
-
const contextJwt = await this.getContextJwt(userEmail, apiToken, jiraInstanceUrl);
|
|
561
|
-
const url = `${this.privateApiBaseUrl}/testcase/${testCaseId}?fields=attachments`;
|
|
562
|
-
const headers = {
|
|
563
|
-
'accept': 'application/json',
|
|
564
|
-
'authorization': `JWT ${contextJwt}`,
|
|
565
|
-
'jira-project-id': String(request.projectId),
|
|
566
|
-
};
|
|
567
|
-
try {
|
|
568
|
-
const response = await fetch(url, {
|
|
569
|
-
method: 'GET',
|
|
570
|
-
headers,
|
|
571
|
-
});
|
|
572
|
-
if (!response.ok) {
|
|
573
|
-
if (response.status === 400) {
|
|
574
|
-
const errorText = await response.text().catch(() => 'Bad Request');
|
|
575
|
-
throw new BadRequestError(`Failed to get test case attachments: ${errorText}`, response.statusText);
|
|
576
|
-
}
|
|
577
|
-
if (response.status === 401) {
|
|
578
|
-
throw new UnauthorizedError('Authentication failed. Please check your credentials.');
|
|
579
|
-
}
|
|
580
|
-
if (response.status === 403) {
|
|
581
|
-
throw new ForbiddenError('You do not have permission to view attachments for this test case.');
|
|
582
|
-
}
|
|
583
|
-
if (response.status === 404) {
|
|
584
|
-
throw new NotFoundError('Test case not found.');
|
|
585
|
-
}
|
|
586
|
-
throw new ServerError(`Failed to get test case attachments. Status: ${response.status}`, response.status, response.statusText);
|
|
587
|
-
}
|
|
588
|
-
return await response.json();
|
|
589
|
-
}
|
|
590
|
-
catch (error) {
|
|
591
|
-
if (error instanceof BadRequestError ||
|
|
592
|
-
error instanceof UnauthorizedError ||
|
|
593
|
-
error instanceof ForbiddenError ||
|
|
594
|
-
error instanceof NotFoundError ||
|
|
595
|
-
error instanceof ServerError ||
|
|
596
|
-
error instanceof UnexpectedError) {
|
|
597
|
-
throw error;
|
|
598
|
-
}
|
|
599
|
-
throw new UnexpectedError('Unexpected error while getting test case attachments', error);
|
|
600
|
-
}
|
|
94
|
+
/**
|
|
95
|
+
* @deprecated Use Private.Comments.getTestCycleComments() instead
|
|
96
|
+
*/
|
|
97
|
+
async getTestCycleComments(credentials, request) {
|
|
98
|
+
return this.Comments.getTestCycleComments(credentials, request);
|
|
601
99
|
}
|
|
602
100
|
/**
|
|
603
|
-
*
|
|
101
|
+
* Get comments for a test plan using private API
|
|
604
102
|
*
|
|
605
|
-
*
|
|
606
|
-
* 1. Gets fresh attachment details (including a fresh signed S3 URL)
|
|
607
|
-
* 2. Downloads the file from the signed URL
|
|
608
|
-
* 3. Returns the file as a Blob
|
|
103
|
+
* Retrieves all comments associated with a test plan.
|
|
609
104
|
*
|
|
610
|
-
* ⚠️ WARNING: This uses private Zephyr API
|
|
611
|
-
* The
|
|
105
|
+
* ⚠️ WARNING: This uses a private Zephyr API endpoint that is not officially supported.
|
|
106
|
+
* The endpoint may change or be removed at any time without notice.
|
|
612
107
|
*
|
|
613
|
-
* @param
|
|
614
|
-
* @param
|
|
615
|
-
* @param
|
|
616
|
-
* @param request - Download attachment request
|
|
617
|
-
* @param request.testCaseKey - Test case key (e.g., 'PROJ-T1'). The numeric ID will be looked up automatically if Zephyr Connector is available.
|
|
108
|
+
* @param credentials - Private API credentials
|
|
109
|
+
* @param request - Get comments request
|
|
110
|
+
* @param request.testPlanId - Test plan ID (numeric)
|
|
618
111
|
* @param request.projectId - Jira project ID (numeric, not the project key)
|
|
619
|
-
* @
|
|
620
|
-
* @returns Blob containing the attachment file data
|
|
112
|
+
* @returns Array of comments
|
|
621
113
|
* @throws {BadRequestError} If the request is invalid
|
|
622
114
|
* @throws {UnauthorizedError} If authentication fails
|
|
623
115
|
* @throws {ForbiddenError} If the user doesn't have permission
|
|
624
|
-
* @throws {NotFoundError} If the test
|
|
116
|
+
* @throws {NotFoundError} If the test plan is not found
|
|
625
117
|
* @throws {ServerError} If the server returns an error
|
|
626
|
-
* @throws {UnexpectedError} If test case ID cannot be looked up from key and Zephyr Connector is not available, or if download fails
|
|
627
118
|
*/
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
});
|
|
634
|
-
// Step 2: Find the requested attachment
|
|
635
|
-
const attachment = attachmentsResponse.attachments.find((att) => att.id === request.attachmentId);
|
|
636
|
-
if (!attachment) {
|
|
637
|
-
throw new NotFoundError(`Attachment with ID '${request.attachmentId}' not found for test case '${request.testCaseKey}'.`);
|
|
638
|
-
}
|
|
639
|
-
// Step 3: Download the file from the signed S3 URL
|
|
640
|
-
try {
|
|
641
|
-
const downloadResponse = await fetch(attachment.url, {
|
|
642
|
-
method: 'GET',
|
|
643
|
-
});
|
|
644
|
-
if (!downloadResponse.ok) {
|
|
645
|
-
if (downloadResponse.status === 403) {
|
|
646
|
-
throw new ForbiddenError('Failed to download attachment. The signed URL may have expired. Please try again.');
|
|
647
|
-
}
|
|
648
|
-
if (downloadResponse.status === 404) {
|
|
649
|
-
throw new NotFoundError('Attachment file not found on S3.');
|
|
650
|
-
}
|
|
651
|
-
throw new ServerError(`Failed to download attachment. Status: ${downloadResponse.status}`, downloadResponse.status, downloadResponse.statusText);
|
|
652
|
-
}
|
|
653
|
-
// Return as Blob
|
|
654
|
-
return await downloadResponse.blob();
|
|
655
|
-
}
|
|
656
|
-
catch (error) {
|
|
657
|
-
if (error instanceof ForbiddenError ||
|
|
658
|
-
error instanceof NotFoundError ||
|
|
659
|
-
error instanceof ServerError) {
|
|
660
|
-
throw error;
|
|
661
|
-
}
|
|
662
|
-
throw new UnexpectedError('Unexpected error while downloading attachment', error);
|
|
663
|
-
}
|
|
119
|
+
/**
|
|
120
|
+
* @deprecated Use Private.Comments.getTestPlanComments() instead
|
|
121
|
+
*/
|
|
122
|
+
async getTestPlanComments(credentials, request) {
|
|
123
|
+
return this.Comments.getTestPlanComments(credentials, request);
|
|
664
124
|
}
|
|
665
125
|
/**
|
|
666
|
-
*
|
|
126
|
+
* @deprecated Use Private.Comments.createTestCaseComment() instead
|
|
667
127
|
*/
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
const extension = fileName.split('.').pop()?.toLowerCase();
|
|
671
|
-
const mimeTypes = {
|
|
672
|
-
png: 'image/png',
|
|
673
|
-
jpg: 'image/jpeg',
|
|
674
|
-
jpeg: 'image/jpeg',
|
|
675
|
-
gif: 'image/gif',
|
|
676
|
-
pdf: 'application/pdf',
|
|
677
|
-
txt: 'text/plain',
|
|
678
|
-
csv: 'text/csv',
|
|
679
|
-
json: 'application/json',
|
|
680
|
-
xml: 'application/xml',
|
|
681
|
-
zip: 'application/zip',
|
|
682
|
-
};
|
|
683
|
-
return mimeTypes[extension || ''] || 'application/octet-stream';
|
|
128
|
+
async createTestCaseComment(credentials, request) {
|
|
129
|
+
return this.Comments.createTestCaseComment(credentials, request);
|
|
684
130
|
}
|
|
685
131
|
/**
|
|
686
|
-
*
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
*
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
* @
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
* @
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
* @
|
|
711
|
-
|
|
132
|
+
* @deprecated Use Private.Comments.createTestCycleComment() instead
|
|
133
|
+
*/
|
|
134
|
+
async createTestCycleComment(credentials, request) {
|
|
135
|
+
return this.Comments.createTestCycleComment(credentials, request);
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* @deprecated Use Private.Comments.createTestPlanComment() instead
|
|
139
|
+
*/
|
|
140
|
+
async createTestPlanComment(credentials, request) {
|
|
141
|
+
return this.Comments.createTestPlanComment(credentials, request);
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* @deprecated Use Private.Attachments.getTestCaseAttachments() instead
|
|
145
|
+
*/
|
|
146
|
+
async getTestCaseAttachments(credentials, request) {
|
|
147
|
+
return this.Attachments.getTestCaseAttachments(credentials, request);
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* @deprecated Use Private.Attachments.downloadAttachment() instead
|
|
151
|
+
*/
|
|
152
|
+
async downloadAttachment(credentials, request) {
|
|
153
|
+
return this.Attachments.downloadAttachment(credentials, request);
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* @deprecated Use Private.Attachments.createAttachment() instead
|
|
157
|
+
*/
|
|
158
|
+
async createAttachment(credentials, request) {
|
|
159
|
+
return this.Attachments.createAttachment(credentials, request);
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* @deprecated Use Private.Attachments.getTestCycleAttachments() instead
|
|
163
|
+
*/
|
|
164
|
+
async getTestCycleAttachments(credentials, request) {
|
|
165
|
+
return this.Attachments.getTestCycleAttachments(credentials, request);
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* @deprecated Use Private.Attachments.downloadTestCycleAttachment() instead
|
|
169
|
+
*/
|
|
170
|
+
async downloadTestCycleAttachment(credentials, request) {
|
|
171
|
+
return this.Attachments.downloadTestCycleAttachment(credentials, request);
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* @deprecated Use Private.Attachments.createTestCycleAttachment() instead
|
|
175
|
+
*/
|
|
176
|
+
async createTestCycleAttachment(credentials, request) {
|
|
177
|
+
return this.Attachments.createTestCycleAttachment(credentials, request);
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* @deprecated Use Private.Attachments.getTestPlanAttachments() instead
|
|
181
|
+
*/
|
|
182
|
+
async getTestPlanAttachments(credentials, request) {
|
|
183
|
+
return this.Attachments.getTestPlanAttachments(credentials, request);
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* @deprecated Use Private.Attachments.downloadTestPlanAttachment() instead
|
|
187
|
+
*/
|
|
188
|
+
async downloadTestPlanAttachment(credentials, request) {
|
|
189
|
+
return this.Attachments.downloadTestPlanAttachment(credentials, request);
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* @deprecated Use Private.Attachments.createTestPlanAttachment() instead
|
|
193
|
+
*/
|
|
194
|
+
async createTestPlanAttachment(credentials, request) {
|
|
195
|
+
return this.Attachments.createTestPlanAttachment(credentials, request);
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* @deprecated Use Private.Attachments.getTestStepAttachments() instead
|
|
199
|
+
*/
|
|
200
|
+
async getTestStepAttachments(credentials, request) {
|
|
201
|
+
return this.Attachments.getTestStepAttachments(credentials, request);
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* @deprecated Use Private.Attachments.downloadTestStepAttachment() instead
|
|
205
|
+
*/
|
|
206
|
+
async downloadTestStepAttachment(credentials, request) {
|
|
207
|
+
return this.Attachments.downloadTestStepAttachment(credentials, request);
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* @deprecated Use Private.Attachments.createTestStepAttachment() instead
|
|
211
|
+
*/
|
|
212
|
+
async createTestStepAttachment(credentials, request) {
|
|
213
|
+
return this.Attachments.createTestStepAttachment(credentials, request);
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* @deprecated Use Private.Attachments.getTestExecutionAttachments() instead
|
|
217
|
+
*/
|
|
218
|
+
async getTestExecutionAttachments(credentials, request) {
|
|
219
|
+
return this.Attachments.getTestExecutionAttachments(credentials, request);
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* @deprecated Use Private.Attachments.downloadTestExecutionAttachment() instead
|
|
223
|
+
*/
|
|
224
|
+
async downloadTestExecutionAttachment(credentials, request) {
|
|
225
|
+
return this.Attachments.downloadTestExecutionAttachment(credentials, request);
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* @deprecated Use Private.Attachments.createTestExecutionAttachment() instead
|
|
229
|
+
*/
|
|
230
|
+
async createTestExecutionAttachment(credentials, request) {
|
|
231
|
+
return this.Attachments.createTestExecutionAttachment(credentials, request);
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* @deprecated Use Private.Attachments.getTestExecutionStepAttachments() instead
|
|
235
|
+
*/
|
|
236
|
+
async getTestExecutionStepAttachments(credentials, request) {
|
|
237
|
+
return this.Attachments.getTestExecutionStepAttachments(credentials, request);
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* @deprecated Use Private.Attachments.downloadTestExecutionStepAttachment() instead
|
|
241
|
+
*/
|
|
242
|
+
async downloadTestExecutionStepAttachment(credentials, request) {
|
|
243
|
+
return this.Attachments.downloadTestExecutionStepAttachment(credentials, request);
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* @deprecated Use Private.Attachments.createTestExecutionStepAttachment() instead
|
|
712
247
|
*/
|
|
713
|
-
async
|
|
714
|
-
|
|
715
|
-
let testCaseId;
|
|
716
|
-
if (this.testCaseGroup) {
|
|
717
|
-
try {
|
|
718
|
-
const testCase = await this.testCaseGroup.getTestCase({ testCaseKey: request.testCaseKey });
|
|
719
|
-
if (!testCase) {
|
|
720
|
-
throw new NotFoundError(`Test case with key '${request.testCaseKey}' not found.`);
|
|
721
|
-
}
|
|
722
|
-
testCaseId = testCase.id;
|
|
723
|
-
}
|
|
724
|
-
catch (error) {
|
|
725
|
-
if (error instanceof NotFoundError) {
|
|
726
|
-
throw new NotFoundError(`Test case with key '${request.testCaseKey}' not found.`);
|
|
727
|
-
}
|
|
728
|
-
throw new UnexpectedError(`Failed to look up test case ID from key '${request.testCaseKey}'. Ensure Zephyr Connector is configured.`, error);
|
|
729
|
-
}
|
|
730
|
-
}
|
|
731
|
-
else {
|
|
732
|
-
throw new UnexpectedError('Cannot look up test case ID from key. This method requires Zephyr Connector to be configured. Use createZephyrApi() with an API Connection.');
|
|
733
|
-
}
|
|
734
|
-
// Get Context JWT
|
|
735
|
-
const contextJwt = await this.getContextJwt(userEmail, apiToken, jiraInstanceUrl);
|
|
736
|
-
// Step 1: Get upload details
|
|
737
|
-
const uploadDetails = await this.getUploadDetails(contextJwt);
|
|
738
|
-
// Step 2: Upload to S3
|
|
739
|
-
const s3Info = await this.uploadToS3(uploadDetails, request.projectId, testCaseId, request.userAccountId, request.file, request.fileName);
|
|
740
|
-
// Step 3: Save metadata
|
|
741
|
-
return await this.saveAttachmentMetadata(contextJwt, request.projectId, testCaseId, request.userAccountId, s3Info.s3Key, s3Info.fileName, s3Info.mimeType, s3Info.fileSize);
|
|
248
|
+
async createTestExecutionStepAttachment(credentials, request) {
|
|
249
|
+
return this.Attachments.createTestExecutionStepAttachment(credentials, request);
|
|
742
250
|
}
|
|
743
251
|
}
|