@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.
@@ -80,16 +80,10 @@ class TestomatioPipe {
80
80
  shouldRetry: error => {
81
81
  if (!error.response)
82
82
  return false;
83
- switch (error.response?.status) {
84
- case 400: // Bad request (probably wrong API key)
85
- case 404: // Test not matched
86
- case 429: // Rate limit exceeded
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 I AM roles
109
- if (!s3Config.credentials.sessionToken) {
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@testomatio/reporter",
3
- "version": "2.6.2",
3
+ "version": "2.6.4-beta-acl",
4
4
  "description": "Testomatio Reporter Client",
5
5
  "engines": {
6
6
  "node": ">=18"
@@ -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
- switch (error.response?.status) {
91
- case 400: // Bad request (probably wrong API key)
92
- case 404: // Test not matched
93
- case 429: // Rate limit exceeded
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 I AM roles
122
- if (!s3Config.credentials.sessionToken) {
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(APP_PREFIX, 'Upload failed:', e.message, '\nConfig:\n', this.getMaskedConfig());
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