From bf47a984c32f44ab8bc31e36e64e4bdefea76017 Mon Sep 17 00:00:00 2001 From: Anudeep Date: Sat, 26 Oct 2024 23:48:13 +0530 Subject: [PATCH] chore: compress attachments (#257) * chore: compress attachments * chore: bump version --- package-lock.json | 4 +- package.json | 2 +- src/beats/beats.attachments.js | 46 ++++++++++++++++++- test/beats.spec.js | 4 +- .../failed-tests-with-attachments.json | 4 ++ 5 files changed, 53 insertions(+), 7 deletions(-) diff --git a/package-lock.json b/package-lock.json index 5add1f9..7a61937 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "testbeats", - "version": "2.1.5", + "version": "2.1.6", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "testbeats", - "version": "2.1.5", + "version": "2.1.6", "license": "ISC", "dependencies": { "async-retry": "^1.3.3", diff --git a/package.json b/package.json index e566e38..e67e758 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "testbeats", - "version": "2.1.5", + "version": "2.1.6", "description": "Publish test results to Microsoft Teams, Google Chat, Slack and InfluxDB", "main": "src/index.js", "types": "./src/index.d.ts", diff --git a/src/beats/beats.attachments.js b/src/beats/beats.attachments.js index ce1b2c0..6479194 100644 --- a/src/beats/beats.attachments.js +++ b/src/beats/beats.attachments.js @@ -1,6 +1,9 @@ const fs = require('fs'); const path = require('path'); +const zlib = require('zlib'); +const stream = require('stream'); const FormData = require('form-data-lite'); +const ml = require('mime-lite') const TestResult = require('test-results-parser/src/models/TestResult'); const { BeatsApi } = require('./beats.api'); const logger = require('../utils/logger'); @@ -23,12 +26,14 @@ class BeatsAttachments { this.test_run_id = test_run_id; this.failed_test_cases = []; this.attachments = []; + this.compressed_attachment_paths = []; } async upload() { this.#setAllFailedTestCases(); this.#setAttachments(); await this.#uploadAttachments(); + this.#deleteCompressedAttachments(); } #setAllFailedTestCases() { @@ -67,17 +72,22 @@ class BeatsAttachments { form.append('test_run_id', this.test_run_id); const file_images = [] for (const attachment of attachments_subset) { - const attachment_path = this.#getAttachmentFilePath(attachment); + let attachment_path = this.#getAttachmentFilePath(attachment); if (!attachment_path) { logger.warn(`⚠️ Unable to find attachment ${attachment.path}`); continue; } + attachment_path = await this.#compressAttachment(attachment_path); const stats = fs.statSync(attachment_path); if (stats.size > MAX_ATTACHMENT_SIZE) { logger.warn(`⚠️ Attachment ${attachment.path} is too big (${stats.size} bytes). Allowed size is ${MAX_ATTACHMENT_SIZE} bytes.`); continue; } - form.append('images', fs.readFileSync(attachment_path), { filename: path.basename(attachment_path), filepath: attachment_path }); + form.append('images', fs.readFileSync(attachment_path), { + filename: path.basename(attachment_path), + filepath: attachment_path, + contentType: ml.getType(attachment.path), + }); file_images.push({ file_name: attachment.name, file_path: attachment.path, @@ -123,7 +133,39 @@ class BeatsAttachments { return null; } + /** + * + * @param {string} attachment_path + */ + #compressAttachment(attachment_path) { + return new Promise((resolve, _) => { + console.log(attachment_path) + if (attachment_path.endsWith('.br') || attachment_path.endsWith('.gz') || attachment_path.endsWith('.zst') || attachment_path.endsWith('.zip') || attachment_path.endsWith('.7z') || attachment_path.endsWith('.png') || attachment_path.endsWith('.jpg') || attachment_path.endsWith('.jpeg') || attachment_path.endsWith('.svg') || attachment_path.endsWith('.gif') || attachment_path.endsWith('.webp')) { + resolve(attachment_path); + return; + } + const read_stream = fs.createReadStream(attachment_path); + const br = zlib.createBrotliCompress(); + const compressed_file_path = attachment_path + '.br'; + const write_stream = fs.createWriteStream(compressed_file_path); + stream.pipeline(read_stream, br, write_stream, (err) => { + if (err) { + resolve(attachment_path); + return; + } + this.compressed_attachment_paths.push(compressed_file_path); + resolve(compressed_file_path); + return; + }); + }); + } + + #deleteCompressedAttachments() { + for (const attachment_path of this.compressed_attachment_paths) { + fs.unlinkSync(attachment_path); + } + } } diff --git a/test/beats.spec.js b/test/beats.spec.js index 0812381..7527dae 100644 --- a/test/beats.spec.js +++ b/test/beats.spec.js @@ -78,7 +78,7 @@ describe('TestBeats', () => { assert.equal(mock.getInteraction(id4).exercised, true); }); - it('should send results with attachments to beats', async () => { + it('should send results with image attachments to beats', async () => { const id1 = mock.addInteraction('post test results to beats'); const id2 = mock.addInteraction('get test results from beats'); const id3 = mock.addInteraction('upload attachments'); @@ -114,7 +114,7 @@ describe('TestBeats', () => { assert.equal(mock.getInteraction(id5).exercised, true); }); - it('should send results with attachments from cucumber to beats', async () => { + it('should send results with file attachments from cucumber to beats', async () => { const id1 = mock.addInteraction('post test results to beats'); const id2 = mock.addInteraction('get test results from beats'); const id3 = mock.addInteraction('upload attachments'); diff --git a/test/data/cucumber/failed-tests-with-attachments.json b/test/data/cucumber/failed-tests-with-attachments.json index 1f858e8..c84f6fa 100644 --- a/test/data/cucumber/failed-tests-with-attachments.json +++ b/test/data/cucumber/failed-tests-with-attachments.json @@ -91,6 +91,10 @@ { "data": "eyJyZXEiOnsidXJsIjoiaHR0cHM6Ly9yZXFyZXMuaW4vYXBpL3VzZXJzIiwibWV0aG9kIjoiR0VUIn0sInJlcyI6eyJzdGF0dXMiOjIwMCwiYm9keSI6eyJwYWdlIjoxLCJwZXJfcGFnZSI6NiwidG90YWwiOjEyLCJ0b3RhbF9wYWdlcyI6MiwiZGF0YSI6W3siaWQiOjEsImVtYWlsIjoiZ2VvcmdlLmJsdXRoQHJlcXJlcy5pbiIsImZpcnN0X25hbWUiOiJHZW9yZ2UiLCJsYXN0X25hbWUiOiJCbHV0aCIsImF2YXRhciI6Imh0dHBzOi8vcmVxcmVzLmluL2ltZy9mYWNlcy8xLWltYWdlLmpwZyJ9LHsiaWQiOjIsImVtYWlsIjoiamFuZXQud2VhdmVyQHJlcXJlcy5pbiIsImZpcnN0X25hbWUiOiJKYW5ldCIsImxhc3RfbmFtZSI6IldlYXZlciIsImF2YXRhciI6Imh0dHBzOi8vcmVxcmVzLmluL2ltZy9mYWNlcy8yLWltYWdlLmpwZyJ9LHsiaWQiOjMsImVtYWlsIjoiZW1tYS53b25nQHJlcXJlcy5pbiIsImZpcnN0X25hbWUiOiJFbW1hIiwibGFzdF9uYW1lIjoiV29uZyIsImF2YXRhciI6Imh0dHBzOi8vcmVxcmVzLmluL2ltZy9mYWNlcy8zLWltYWdlLmpwZyJ9LHsiaWQiOjQsImVtYWlsIjoiZXZlLmhvbHRAcmVxcmVzLmluIiwiZmlyc3RfbmFtZSI6IkV2ZSIsImxhc3RfbmFtZSI6IkhvbHQiLCJhdmF0YXIiOiJodHRwczovL3JlcXJlcy5pbi9pbWcvZmFjZXMvNC1pbWFnZS5qcGcifSx7ImlkIjo1LCJlbWFpbCI6ImNoYXJsZXMubW9ycmlzQHJlcXJlcy5pbiIsImZpcnN0X25hbWUiOiJDaGFybGVzIiwibGFzdF9uYW1lIjoiTW9ycmlzIiwiYXZhdGFyIjoiaHR0cHM6Ly9yZXFyZXMuaW4vaW1nL2ZhY2VzLzUtaW1hZ2UuanBnIn0seyJpZCI6NiwiZW1haWwiOiJ0cmFjZXkucmFtb3NAcmVxcmVzLmluIiwiZmlyc3RfbmFtZSI6IlRyYWNleSIsImxhc3RfbmFtZSI6IlJhbW9zIiwiYXZhdGFyIjoiaHR0cHM6Ly9yZXFyZXMuaW4vaW1nL2ZhY2VzLzYtaW1hZ2UuanBnIn1dLCJzdXBwb3J0Ijp7InVybCI6Imh0dHBzOi8vcmVxcmVzLmluLyNzdXBwb3J0LWhlYWRpbmciLCJ0ZXh0IjoiVG8ga2VlcCBSZXFSZXMgZnJlZSwgY29udHJpYnV0aW9ucyB0b3dhcmRzIHNlcnZlciBjb3N0cyBhcmUgYXBwcmVjaWF0ZWQhIn19LCJoZWFkZXJzIjp7ImRhdGUiOiJTdW4sIDAxIFNlcCAyMDI0IDEzOjA2OjI4IEdNVCIsImNvbnRlbnQtdHlwZSI6ImFwcGxpY2F0aW9uL2pzb247IGNoYXJzZXQ9dXRmLTgiLCJjb250ZW50LWxlbmd0aCI6Ijk5NiIsImNvbm5lY3Rpb24iOiJrZWVwLWFsaXZlIiwicmVwb3J0LXRvIjoie1wiZ3JvdXBcIjpcImhlcm9rdS1uZWxcIixcIm1heF9hZ2VcIjozNjAwLFwiZW5kcG9pbnRzXCI6W3tcInVybFwiOlwiaHR0cHM6Ly9uZWwuaGVyb2t1LmNvbS9yZXBvcnRzP3RzPTE3MjQ2ODA2NDImc2lkPWM0Yzk3MjVmLTFhYjAtNDRkOC04MjBmLTQzMGRmMjcxOGUxMSZzPWJIR2RiVXoxc3ZkUUtqaFFHeHpRc2FqZElET0FlR2xGNFlJTUJlQno4JTJGRSUzRFwifV19IiwicmVwb3J0aW5nLWVuZHBvaW50cyI6Imhlcm9rdS1uZWw9aHR0cHM6Ly9uZWwuaGVyb2t1LmNvbS9yZXBvcnRzP3RzPTE3MjQ2ODA2NDImc2lkPWM0Yzk3MjVmLTFhYjAtNDRkOC04MjBmLTQzMGRmMjcxOGUxMSZzPWJIR2RiVXoxc3ZkUUtqaFFHeHpRc2FqZElET0FlR2xGNFlJTUJlQno4JTJGRSUzRCIsIm5lbCI6IntcInJlcG9ydF90b1wiOlwiaGVyb2t1LW5lbFwiLFwibWF4X2FnZVwiOjM2MDAsXCJzdWNjZXNzX2ZyYWN0aW9uXCI6MC4wMDUsXCJmYWlsdXJlX2ZyYWN0aW9uXCI6MC4wNSxcInJlc3BvbnNlX2hlYWRlcnNcIjpbXCJWaWFcIl19IiwieC1wb3dlcmVkLWJ5IjoiRXhwcmVzcyIsImFjY2Vzcy1jb250cm9sLWFsbG93LW9yaWdpbiI6IioiLCJldGFnIjoiVy9cIjNlNC0yUkxYdnI1d1RnOVlRNmFIOTVDa1lvRk51TzhcIiIsInZpYSI6IjEuMSB2ZWd1ciIsImNhY2hlLWNvbnRyb2wiOiJtYXgtYWdlPTE0NDAwIiwiY2YtY2FjaGUtc3RhdHVzIjoiSElUIiwiYWdlIjoiNTAxIiwiYWNjZXB0LXJhbmdlcyI6ImJ5dGVzIiwic2VydmVyIjoiY2xvdWRmbGFyZSIsImNmLXJheSI6IjhiYzU3YTUwOGQ4ZDJlODUtSFlEIn19fQ==", "mime_type": "application/json" + }, + { + "data": "dGVzdC9kYXRhL2N1Y3VtYmVyL2ZhaWxlZC10ZXN0cy13aXRoLWF0dGFjaG1lbnRzLmpzb24=", + "mime_type": "application/json" } ] }