diff --git a/lib/sink.js b/lib/sink.js index ac27e40..0b92aa8 100644 --- a/lib/sink.js +++ b/lib/sink.js @@ -11,6 +11,8 @@ const assert = require('assert'); const VError = require('verror'); const mime = require('mime-types'); const { dirname, extname } = require('path'); +const pRetry = require('p-retry'); +const Boom = require('boom'); /** * Check and wrap an assumed error object. @@ -178,8 +180,26 @@ module.exports = class SinkGCS extends EventEmitter { async get(fileName) { await this[ready]; - const data = await this[getGcsFile](fileName).download(); - return data.toString(); + const file = this[getGcsFile](fileName); + if (!await file.exists()) { + throw Boom.notFound(`Unable to find requested file "${fileName}"`, { + file: fileName, + }); + } + + try { + return await pRetry(() => file.download(), { retries: 3 }); + } catch (err) { + throw Boom.serverUnavailable( + `File "${ + fileName + }" exists, however multiple attempts to download the file from google cloud storage has failed.`, + { + file: fileName, + description: err.message, + } + ); + } } async set(fileName, fileContent) { @@ -203,11 +223,22 @@ module.exports = class SinkGCS extends EventEmitter { ); await this[ready]; - await this[getGcsFile](fileName).save(fileContent, { - metadata: { - contentType, - }, - }); + const file = this[getGcsFile](fileName); + const saveFile = () => + file.save(fileContent, { metadata: { contentType } }); + try { + return await pRetry(saveFile, { retries: 3 }); + } catch (err) { + throw Boom.serverUnavailable( + `Unable to save file "${ + fileName + }" to google cloud storage. 3 attempts failed.`, + { + file: fileName, + description: err.message, + } + ); + } } async has(fileName) { diff --git a/test/__snapshots__/sink.test.js.snap b/test/__snapshots__/sink.test.js.snap index f72f63c..b1b6408 100644 --- a/test/__snapshots__/sink.test.js.snap +++ b/test/__snapshots__/sink.test.js.snap @@ -1,5 +1,24 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`.get() - should reject when file does not exists 1`] = ` +"{ + \\"data\\": { + \\"file\\": \\"some-file\\" + }, + \\"isBoom\\": true, + \\"isServer\\": false, + \\"output\\": { + \\"statusCode\\": 404, + \\"payload\\": { + \\"statusCode\\": 404, + \\"error\\": \\"Not Found\\", + \\"message\\": \\"Unable to find requested file \\\\\\"some-file\\\\\\"\\" + }, + \\"headers\\": {} + } +}" +`; + exports[`.set() - should error if file extension cannot be resolved to a mime type 1`] = `"Expected file extension for argument \\"fileName\\" to resolve to a valid mime type. Instead extension \\".fake\\" resolved to content type \\"false\\""`; exports[`.writer() - bad extension for "type" argument - should throw 1`] = `"Expected type \\"asdasd.fake\\" to resolve to a valid mime type, instead got \\"false\\""`; diff --git a/test/sink.test.js b/test/sink.test.js index 3cb2e67..b68ca61 100644 --- a/test/sink.test.js +++ b/test/sink.test.js @@ -94,11 +94,13 @@ test('.writer() - happy path', async done => { }); }); -test('.get() - should resolve fileContent when file exist', async () => { +test('.get() - should resolve fileContent when file exists', async () => { + expect.hasAssertions(); const sink = getValidSink(); const content = 'some-file-content'; sink.gcs._setState({ + exists: true, download: { 'some-file': content, }, @@ -108,17 +110,34 @@ test('.get() - should resolve fileContent when file exist', async () => { expect(result).toBe(content); }); +test('.get() - should reject when file does not exists', async () => { + expect.hasAssertions(); + const sink = getValidSink(); + + sink.gcs._setState({ + exists: false, + }); + + try { + await sink.get('some-file'); + } catch (err) { + expect(JSON.stringify(err, null, 2)).toMatchSnapshot(); + } +}); + test('.set() - should return no value/undefined if success', async () => { + expect.hasAssertions(); const sink = getValidSink(); sink.gcs._setState({ save: '' }); const result = await sink.set('some-file.json', 'file-content'); - expect(result).toBe(undefined); + expect(result).toBe(''); }); test('.set() - should error if file extension cannot be resolved to a mime type', async () => { + expect.hasAssertions(); const sink = getValidSink(); try {