@testomatio/reporter 2.6.2 → 2.6.4-beta-acl
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/lib/pipe/testomatio.js +21 -10
- package/lib/uploader.js +7 -4
- package/package.json +1 -1
- package/src/pipe/testomatio.js +20 -10
- package/src/uploader.js +14 -5
package/lib/pipe/testomatio.js
CHANGED
|
@@ -80,16 +80,10 @@ class TestomatioPipe {
|
|
|
80
80
|
shouldRetry: error => {
|
|
81
81
|
if (!error.response)
|
|
82
82
|
return false;
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
case 500: // Internal server error
|
|
88
|
-
return false;
|
|
89
|
-
default:
|
|
90
|
-
break;
|
|
91
|
-
}
|
|
92
|
-
return error.response?.status >= 401; // Retry on 401+ and 5xx
|
|
83
|
+
// no need to retry on 4xx errors, because they caused by user mistake, thus retrying will not help
|
|
84
|
+
// 500 could also be related to both user or server mistake, but decided not to retry for now
|
|
85
|
+
// this code code be changed to retry 500 too if needed
|
|
86
|
+
return error.response?.status >= 501; // Retry only on server errors
|
|
93
87
|
},
|
|
94
88
|
},
|
|
95
89
|
});
|
|
@@ -270,6 +264,8 @@ class TestomatioPipe {
|
|
|
270
264
|
const errorText = err.response?.data?.message || err.message;
|
|
271
265
|
debug('Error creating run', err);
|
|
272
266
|
console.log(errorText || err);
|
|
267
|
+
if (err.response?.status === 403)
|
|
268
|
+
this.#disablePipe();
|
|
273
269
|
if (!this.apiKey)
|
|
274
270
|
console.error('Testomat.io API key is not set');
|
|
275
271
|
if (!this.apiKey?.startsWith('tstmt'))
|
|
@@ -318,6 +314,8 @@ class TestomatioPipe {
|
|
|
318
314
|
maxContentLength: Infinity,
|
|
319
315
|
})
|
|
320
316
|
.catch(err => {
|
|
317
|
+
if (err.response?.status === 403)
|
|
318
|
+
this.#disablePipe();
|
|
321
319
|
this.requestFailures++;
|
|
322
320
|
this.notReportedTestsCount++;
|
|
323
321
|
if (err.response) {
|
|
@@ -369,6 +367,8 @@ class TestomatioPipe {
|
|
|
369
367
|
maxContentLength: Infinity,
|
|
370
368
|
})
|
|
371
369
|
.catch(err => {
|
|
370
|
+
if (err.response?.status === 403)
|
|
371
|
+
this.#disablePipe();
|
|
372
372
|
this.requestFailures++;
|
|
373
373
|
this.notReportedTestsCount += testsToSend.length;
|
|
374
374
|
if (err.response) {
|
|
@@ -478,6 +478,17 @@ class TestomatioPipe {
|
|
|
478
478
|
}
|
|
479
479
|
debug('Run finished');
|
|
480
480
|
}
|
|
481
|
+
#disablePipe() {
|
|
482
|
+
this.isEnabled = false;
|
|
483
|
+
this.apiKey = null;
|
|
484
|
+
// clear interval function, otherwise the proccess will continue indefinitely
|
|
485
|
+
if (this.batch.intervalFunction) {
|
|
486
|
+
clearInterval(this.batch.intervalFunction);
|
|
487
|
+
this.batch.intervalFunction = null;
|
|
488
|
+
this.batch.isEnabled = false;
|
|
489
|
+
}
|
|
490
|
+
this.batch.tests = [];
|
|
491
|
+
}
|
|
481
492
|
#logFailedResponse(error) {
|
|
482
493
|
let responseBody = stringify(error.response?.data ?? error.response ?? error, { pretty: true });
|
|
483
494
|
if (!responseBody)
|
package/lib/uploader.js
CHANGED
|
@@ -40,6 +40,7 @@ class S3Uploader {
|
|
|
40
40
|
'TESTOMATIO_DISABLE_ARTIFACTS',
|
|
41
41
|
'TESTOMATIO_PRIVATE_ARTIFACTS',
|
|
42
42
|
'TESTOMATIO_ARTIFACT_MAX_SIZE_MB',
|
|
43
|
+
'TESTOMATIO_S3_NO_ACL',
|
|
43
44
|
];
|
|
44
45
|
}
|
|
45
46
|
resetConfig() {
|
|
@@ -91,7 +92,7 @@ class S3Uploader {
|
|
|
91
92
|
* @returns
|
|
92
93
|
*/
|
|
93
94
|
async #uploadToS3(Body, Key, file) {
|
|
94
|
-
const { S3_BUCKET, TESTOMATIO_PRIVATE_ARTIFACTS } = this.getConfig();
|
|
95
|
+
const { S3_BUCKET, TESTOMATIO_PRIVATE_ARTIFACTS, TESTOMATIO_S3_NO_ACL } = this.getConfig();
|
|
95
96
|
const ACL = TESTOMATIO_PRIVATE_ARTIFACTS ? 'private' : 'public-read';
|
|
96
97
|
if (!S3_BUCKET || !Body) {
|
|
97
98
|
console.log(constants_js_1.APP_PREFIX, picocolors_1.default.bold(picocolors_1.default.red(`Failed uploading '${Key}'. Please check S3 credentials`)), this.getMaskedConfig());
|
|
@@ -105,8 +106,10 @@ class S3Uploader {
|
|
|
105
106
|
Key,
|
|
106
107
|
Body,
|
|
107
108
|
};
|
|
108
|
-
// disable ACL for
|
|
109
|
-
|
|
109
|
+
// disable ACL for IAM roles or GCS (GCS doesn't support x-amz-acl header)
|
|
110
|
+
// to precent potential issues, forece setting TESTOMATIO_S3_NO_ACL env var too in such case
|
|
111
|
+
const isGCS = s3Config.endpoint && s3Config.endpoint.includes('storage.googleapis.com');
|
|
112
|
+
if (!s3Config.credentials.sessionToken && !TESTOMATIO_S3_NO_ACL && !isGCS) {
|
|
110
113
|
params.ACL = ACL;
|
|
111
114
|
}
|
|
112
115
|
try {
|
|
@@ -119,7 +122,7 @@ class S3Uploader {
|
|
|
119
122
|
catch (e) {
|
|
120
123
|
this.failedUploads.push({ path: file.path, size: file.size });
|
|
121
124
|
debug('S3 uploading error:', e);
|
|
122
|
-
console.log(constants_js_1.APP_PREFIX, 'Upload failed:', e.message, '\nConfig:\n', this.getMaskedConfig());
|
|
125
|
+
console.log(constants_js_1.APP_PREFIX, 'Upload failed:', e.message, `\nFile:\n ${file.path}, size: ${(0, filesize_1.filesize)(file.size || 0)}`, '\nConfig:\n', this.getMaskedConfig());
|
|
123
126
|
}
|
|
124
127
|
}
|
|
125
128
|
/**
|
package/package.json
CHANGED
package/src/pipe/testomatio.js
CHANGED
|
@@ -87,16 +87,10 @@ class TestomatioPipe {
|
|
|
87
87
|
httpMethodsToRetry: ['GET', 'PUT', 'HEAD', 'OPTIONS', 'DELETE', 'POST'],
|
|
88
88
|
shouldRetry: error => {
|
|
89
89
|
if (!error.response) return false;
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
case 500: // Internal server error
|
|
95
|
-
return false;
|
|
96
|
-
default:
|
|
97
|
-
break;
|
|
98
|
-
}
|
|
99
|
-
return error.response?.status >= 401; // Retry on 401+ and 5xx
|
|
90
|
+
// no need to retry on 4xx errors, because they caused by user mistake, thus retrying will not help
|
|
91
|
+
// 500 could also be related to both user or server mistake, but decided not to retry for now
|
|
92
|
+
// this code code be changed to retry 500 too if needed
|
|
93
|
+
return error.response?.status >= 501; // Retry only on server errors
|
|
100
94
|
},
|
|
101
95
|
},
|
|
102
96
|
});
|
|
@@ -300,6 +294,7 @@ class TestomatioPipe {
|
|
|
300
294
|
const errorText = err.response?.data?.message || err.message;
|
|
301
295
|
debug('Error creating run', err);
|
|
302
296
|
console.log(errorText || err);
|
|
297
|
+
if (err.response?.status === 403) this.#disablePipe();
|
|
303
298
|
if (!this.apiKey) console.error('Testomat.io API key is not set');
|
|
304
299
|
if (!this.apiKey?.startsWith('tstmt')) console.error('Testomat.io API key is invalid');
|
|
305
300
|
|
|
@@ -353,6 +348,7 @@ class TestomatioPipe {
|
|
|
353
348
|
maxContentLength: Infinity,
|
|
354
349
|
})
|
|
355
350
|
.catch(err => {
|
|
351
|
+
if (err.response?.status === 403) this.#disablePipe();
|
|
356
352
|
this.requestFailures++;
|
|
357
353
|
this.notReportedTestsCount++;
|
|
358
354
|
if (err.response) {
|
|
@@ -403,6 +399,7 @@ class TestomatioPipe {
|
|
|
403
399
|
maxContentLength: Infinity,
|
|
404
400
|
})
|
|
405
401
|
.catch(err => {
|
|
402
|
+
if (err.response?.status === 403) this.#disablePipe();
|
|
406
403
|
this.requestFailures++;
|
|
407
404
|
this.notReportedTestsCount += testsToSend.length;
|
|
408
405
|
if (err.response) {
|
|
@@ -526,6 +523,19 @@ class TestomatioPipe {
|
|
|
526
523
|
debug('Run finished');
|
|
527
524
|
}
|
|
528
525
|
|
|
526
|
+
#disablePipe() {
|
|
527
|
+
this.isEnabled = false;
|
|
528
|
+
this.apiKey = null;
|
|
529
|
+
|
|
530
|
+
// clear interval function, otherwise the proccess will continue indefinitely
|
|
531
|
+
if (this.batch.intervalFunction) {
|
|
532
|
+
clearInterval(this.batch.intervalFunction);
|
|
533
|
+
this.batch.intervalFunction = null;
|
|
534
|
+
this.batch.isEnabled = false;
|
|
535
|
+
}
|
|
536
|
+
this.batch.tests = [];
|
|
537
|
+
}
|
|
538
|
+
|
|
529
539
|
#logFailedResponse(error) {
|
|
530
540
|
let responseBody = stringify(error.response?.data ?? error.response ?? error, { pretty: true });
|
|
531
541
|
if (!responseBody) responseBody = '<empty>';
|
package/src/uploader.js
CHANGED
|
@@ -38,6 +38,7 @@ export class S3Uploader {
|
|
|
38
38
|
'TESTOMATIO_DISABLE_ARTIFACTS',
|
|
39
39
|
'TESTOMATIO_PRIVATE_ARTIFACTS',
|
|
40
40
|
'TESTOMATIO_ARTIFACT_MAX_SIZE_MB',
|
|
41
|
+
'TESTOMATIO_S3_NO_ACL',
|
|
41
42
|
];
|
|
42
43
|
}
|
|
43
44
|
|
|
@@ -97,7 +98,7 @@ export class S3Uploader {
|
|
|
97
98
|
* @returns
|
|
98
99
|
*/
|
|
99
100
|
async #uploadToS3(Body, Key, file) {
|
|
100
|
-
const { S3_BUCKET, TESTOMATIO_PRIVATE_ARTIFACTS } = this.getConfig();
|
|
101
|
+
const { S3_BUCKET, TESTOMATIO_PRIVATE_ARTIFACTS, TESTOMATIO_S3_NO_ACL } = this.getConfig();
|
|
101
102
|
const ACL = TESTOMATIO_PRIVATE_ARTIFACTS ? 'private' : 'public-read';
|
|
102
103
|
|
|
103
104
|
if (!S3_BUCKET || !Body) {
|
|
@@ -118,14 +119,15 @@ export class S3Uploader {
|
|
|
118
119
|
Key,
|
|
119
120
|
Body,
|
|
120
121
|
};
|
|
121
|
-
// disable ACL for
|
|
122
|
-
|
|
122
|
+
// disable ACL for IAM roles or GCS (GCS doesn't support x-amz-acl header)
|
|
123
|
+
// to precent potential issues, forece setting TESTOMATIO_S3_NO_ACL env var too in such case
|
|
124
|
+
const isGCS = s3Config.endpoint && s3Config.endpoint.includes('storage.googleapis.com');
|
|
125
|
+
if (!s3Config.credentials.sessionToken && !TESTOMATIO_S3_NO_ACL && !isGCS) {
|
|
123
126
|
params.ACL = ACL;
|
|
124
127
|
}
|
|
125
128
|
|
|
126
129
|
try {
|
|
127
130
|
const upload = new Upload({ client: s3, params });
|
|
128
|
-
|
|
129
131
|
const link = await this.getS3LocationLink(upload);
|
|
130
132
|
this.successfulUploads.push({ path: file.path, size: file.size, link });
|
|
131
133
|
debug(`📤 Uploaded artifact. File: ${file.path}, size: ${prettyBytes(file.size || 0)}, link: ${link}`);
|
|
@@ -133,7 +135,14 @@ export class S3Uploader {
|
|
|
133
135
|
} catch (e) {
|
|
134
136
|
this.failedUploads.push({ path: file.path, size: file.size });
|
|
135
137
|
debug('S3 uploading error:', e);
|
|
136
|
-
console.log(
|
|
138
|
+
console.log(
|
|
139
|
+
APP_PREFIX,
|
|
140
|
+
'Upload failed:',
|
|
141
|
+
e.message,
|
|
142
|
+
`\nFile:\n ${file.path}, size: ${prettyBytes(file.size || 0)}`,
|
|
143
|
+
'\nConfig:\n',
|
|
144
|
+
this.getMaskedConfig(),
|
|
145
|
+
);
|
|
137
146
|
}
|
|
138
147
|
}
|
|
139
148
|
|