qase-javascript-commons 2.4.10 → 2.4.11
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/changelog.md +6 -0
- package/dist/client/clientV1.d.ts +21 -0
- package/dist/client/clientV1.js +85 -2
- package/package.json +18 -17
- package/tsconfig.build.json +3 -1
package/changelog.md
CHANGED
|
@@ -21,6 +21,27 @@ export declare class ClientV1 implements IClient {
|
|
|
21
21
|
private getErrorMessage;
|
|
22
22
|
uploadAttachment(attachment: Attachment): Promise<string>;
|
|
23
23
|
protected uploadAttachments(attachments: Attachment[]): Promise<string[]>;
|
|
24
|
+
/**
|
|
25
|
+
* Upload attachment with retry logic for 429 errors
|
|
26
|
+
* @param project Project code
|
|
27
|
+
* @param data Attachment data
|
|
28
|
+
* @param attachmentName Attachment name for logging
|
|
29
|
+
* @param maxRetries Maximum number of retry attempts
|
|
30
|
+
* @param initialDelay Initial delay in milliseconds
|
|
31
|
+
* @returns Promise with upload response
|
|
32
|
+
*/
|
|
33
|
+
private uploadAttachmentWithRetry;
|
|
34
|
+
/**
|
|
35
|
+
* Extract Retry-After header value from response or return null
|
|
36
|
+
* @param error Axios error
|
|
37
|
+
* @returns Retry-After value in milliseconds or null
|
|
38
|
+
*/
|
|
39
|
+
private getRetryAfter;
|
|
40
|
+
/**
|
|
41
|
+
* Delay execution for specified milliseconds
|
|
42
|
+
* @param ms Milliseconds to delay
|
|
43
|
+
*/
|
|
44
|
+
private delay;
|
|
24
45
|
private prepareAttachmentData;
|
|
25
46
|
private getEnvironmentId;
|
|
26
47
|
private prepareRunObject;
|
package/dist/client/clientV1.js
CHANGED
|
@@ -145,11 +145,15 @@ class ClientV1 {
|
|
|
145
145
|
return [];
|
|
146
146
|
}
|
|
147
147
|
const uploadedHashes = [];
|
|
148
|
-
for (
|
|
148
|
+
for (let i = 0; i < attachments.length; i++) {
|
|
149
|
+
const attachment = attachments[i];
|
|
150
|
+
if (!attachment) {
|
|
151
|
+
continue;
|
|
152
|
+
}
|
|
149
153
|
try {
|
|
150
154
|
this.logger.logDebug(`Uploading attachment: ${attachment.file_path ?? attachment.file_name}`);
|
|
151
155
|
const data = this.prepareAttachmentData(attachment);
|
|
152
|
-
const response = await this.
|
|
156
|
+
const response = await this.uploadAttachmentWithRetry(this.config.project, [data], attachment.file_path ?? attachment.file_name);
|
|
153
157
|
const hash = response.data.result?.[0]?.hash;
|
|
154
158
|
if (hash) {
|
|
155
159
|
uploadedHashes.push(hash);
|
|
@@ -158,9 +162,88 @@ class ClientV1 {
|
|
|
158
162
|
catch (error) {
|
|
159
163
|
this.logger.logError('Cannot upload attachment:', error);
|
|
160
164
|
}
|
|
165
|
+
// Add delay between requests to avoid rate limiting
|
|
166
|
+
// Skip delay after the last attachment
|
|
167
|
+
if (i < attachments.length - 1) {
|
|
168
|
+
await this.delay(100); // 100ms delay between requests
|
|
169
|
+
}
|
|
161
170
|
}
|
|
162
171
|
return uploadedHashes;
|
|
163
172
|
}
|
|
173
|
+
/**
|
|
174
|
+
* Upload attachment with retry logic for 429 errors
|
|
175
|
+
* @param project Project code
|
|
176
|
+
* @param data Attachment data
|
|
177
|
+
* @param attachmentName Attachment name for logging
|
|
178
|
+
* @param maxRetries Maximum number of retry attempts
|
|
179
|
+
* @param initialDelay Initial delay in milliseconds
|
|
180
|
+
* @returns Promise with upload response
|
|
181
|
+
*/
|
|
182
|
+
async uploadAttachmentWithRetry(project, data, attachmentName, maxRetries = 5, initialDelay = 1000) {
|
|
183
|
+
let lastError;
|
|
184
|
+
let delay = initialDelay;
|
|
185
|
+
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
186
|
+
try {
|
|
187
|
+
return await this.attachmentClient.uploadAttachment(project, data);
|
|
188
|
+
}
|
|
189
|
+
catch (error) {
|
|
190
|
+
lastError = error;
|
|
191
|
+
// Check if it's a 429 error (Too Many Requests)
|
|
192
|
+
if ((0, is_axios_error_1.isAxiosError)(error)) {
|
|
193
|
+
if (error.response?.status === 429) {
|
|
194
|
+
if (attempt < maxRetries) {
|
|
195
|
+
const retryAfter = this.getRetryAfter(error);
|
|
196
|
+
const waitTime = retryAfter ?? delay;
|
|
197
|
+
this.logger.logDebug(`Rate limit exceeded (429) for attachment "${attachmentName}". ` +
|
|
198
|
+
`Retrying in ${waitTime}ms (attempt ${attempt + 1}/${maxRetries})`);
|
|
199
|
+
await this.delay(waitTime);
|
|
200
|
+
// Exponential backoff: double the delay for next attempt
|
|
201
|
+
delay = Math.min(delay * 2, 30000); // Cap at 30 seconds
|
|
202
|
+
}
|
|
203
|
+
else {
|
|
204
|
+
this.logger.logError(`Failed to upload attachment "${attachmentName}" after ${maxRetries} retries due to rate limiting`);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
else {
|
|
208
|
+
// For non-429 errors, throw immediately
|
|
209
|
+
throw error;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
else {
|
|
213
|
+
// For non-Axios errors, throw immediately
|
|
214
|
+
throw error;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
// If we exhausted all retries, throw the last error
|
|
219
|
+
throw lastError;
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* Extract Retry-After header value from response or return null
|
|
223
|
+
* @param error Axios error
|
|
224
|
+
* @returns Retry-After value in milliseconds or null
|
|
225
|
+
*/
|
|
226
|
+
getRetryAfter(error) {
|
|
227
|
+
const headers = error.response?.headers;
|
|
228
|
+
if (!headers) {
|
|
229
|
+
return null;
|
|
230
|
+
}
|
|
231
|
+
const retryAfterHeader = headers['retry-after'];
|
|
232
|
+
if (retryAfterHeader && typeof retryAfterHeader === 'string') {
|
|
233
|
+
const retryAfterSeconds = parseInt(retryAfterHeader, 10);
|
|
234
|
+
if (!isNaN(retryAfterSeconds)) {
|
|
235
|
+
return retryAfterSeconds * 1000; // Convert to milliseconds
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
return null;
|
|
239
|
+
}
|
|
240
|
+
/**
|
|
241
|
+
* Delay execution for specified milliseconds
|
|
242
|
+
* @param ms Milliseconds to delay
|
|
243
|
+
*/
|
|
244
|
+
delay(ms) {
|
|
245
|
+
return new Promise(resolve => setTimeout(resolve, ms));
|
|
246
|
+
}
|
|
164
247
|
prepareAttachmentData(attachment) {
|
|
165
248
|
if (attachment.file_path) {
|
|
166
249
|
return {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "qase-javascript-commons",
|
|
3
|
-
"version": "2.4.
|
|
3
|
+
"version": "2.4.11",
|
|
4
4
|
"description": "Qase JS Reporters",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -24,31 +24,32 @@
|
|
|
24
24
|
"author": "Qase Team <support@qase.io>",
|
|
25
25
|
"license": "Apache-2.0",
|
|
26
26
|
"dependencies": {
|
|
27
|
-
"ajv": "^8.
|
|
27
|
+
"ajv": "^8.17.1",
|
|
28
28
|
"async-mutex": "~0.5.0",
|
|
29
29
|
"chalk": "^4.1.2",
|
|
30
|
-
"env-schema": "^5.2.
|
|
31
|
-
"form-data": "^4.0.
|
|
30
|
+
"env-schema": "^5.2.1",
|
|
31
|
+
"form-data": "^4.0.5",
|
|
32
32
|
"lodash.get": "^4.4.2",
|
|
33
33
|
"lodash.merge": "^4.6.2",
|
|
34
34
|
"lodash.mergewith": "^4.6.2",
|
|
35
|
-
"mime-types": "^2.1.
|
|
35
|
+
"mime-types": "^2.1.35",
|
|
36
36
|
"qase-api-client": "~1.1.0",
|
|
37
|
-
"qase-api-v2-client": "~1.0.
|
|
37
|
+
"qase-api-v2-client": "~1.0.3",
|
|
38
38
|
"strip-ansi": "^6.0.1",
|
|
39
|
-
"uuid": "^9.0.
|
|
39
|
+
"uuid": "^9.0.1"
|
|
40
40
|
},
|
|
41
41
|
"devDependencies": {
|
|
42
|
-
"@jest/globals": "^29.
|
|
43
|
-
"@types/jest": "^29.5.
|
|
44
|
-
"@types/lodash.get": "^4.4.
|
|
45
|
-
"@types/lodash.merge": "^4.6.
|
|
46
|
-
"@types/lodash.mergewith": "^4.6.
|
|
42
|
+
"@jest/globals": "^29.7.0",
|
|
43
|
+
"@types/jest": "^29.5.14",
|
|
44
|
+
"@types/lodash.get": "^4.4.9",
|
|
45
|
+
"@types/lodash.merge": "^4.6.9",
|
|
46
|
+
"@types/lodash.mergewith": "^4.6.9",
|
|
47
47
|
"@types/mime-types": "^2.1.4",
|
|
48
|
-
"@types/
|
|
49
|
-
"@types/
|
|
50
|
-
"
|
|
51
|
-
"
|
|
52
|
-
"
|
|
48
|
+
"@types/minimatch": "^6.0.0",
|
|
49
|
+
"@types/node": "^20.19.25",
|
|
50
|
+
"@types/uuid": "^9.0.8",
|
|
51
|
+
"axios": "^1.13.2",
|
|
52
|
+
"jest": "^29.7.0",
|
|
53
|
+
"ts-jest": "^29.4.5"
|
|
53
54
|
}
|
|
54
55
|
}
|