From ebe2a769c965e2ac5866fc2207e936d72eb658c2 Mon Sep 17 00:00:00 2001 From: Daniel Gasienica Date: Wed, 14 Mar 2018 18:59:58 -0400 Subject: [PATCH] Add `Attachment.migrateDataToFileSystem` --- js/modules/types/attachment.js | 3 + .../attachment/migrate_data_to_file_system.js | 37 +++++++++ test/modules/types/attachment_test.js | 78 +++++++++++++++++++ 3 files changed, 118 insertions(+) create mode 100644 js/modules/types/attachment/migrate_data_to_file_system.js diff --git a/js/modules/types/attachment.js b/js/modules/types/attachment.js index 74c46bf12..c1266d318 100644 --- a/js/modules/types/attachment.js +++ b/js/modules/types/attachment.js @@ -3,6 +3,7 @@ const isString = require('lodash/isString'); const MIME = require('./mime'); const { arrayBufferToBlob, blobToArrayBuffer, dataURLToBlob } = require('blob-util'); const { autoOrientImage } = require('../auto_orient_image'); +const { migrateDataToFileSystem } = require('./attachment/migrate_data_to_file_system'); // // Incoming message attachment fields // { @@ -107,3 +108,5 @@ exports.removeSchemaVersion = (attachment) => { delete attachmentWithoutSchemaVersion.schemaVersion; return attachmentWithoutSchemaVersion; }; + +exports.migrateDataToFileSystem = migrateDataToFileSystem; diff --git a/js/modules/types/attachment/migrate_data_to_file_system.js b/js/modules/types/attachment/migrate_data_to_file_system.js new file mode 100644 index 000000000..7a7a4db82 --- /dev/null +++ b/js/modules/types/attachment/migrate_data_to_file_system.js @@ -0,0 +1,37 @@ +const isArrayBuffer = require('lodash/isArrayBuffer'); +const isFunction = require('lodash/isFunction'); +const isUndefined = require('lodash/isUndefined'); + + +// type Context :: { +// writeAttachmentData :: ArrayBuffer -> Promise (IO Path) +// } +// +// migrateDataToFileSystem :: Attachment -> +// Context -> +// Promise Attachment +exports.migrateDataToFileSystem = async (attachment, { writeAttachmentData } = {}) => { + if (!isFunction(writeAttachmentData)) { + throw new TypeError('`writeAttachmentData` must be a function'); + } + + const { data } = attachment; + const hasData = !isUndefined(data); + const shouldSkipSchemaUpgrade = !hasData; + if (shouldSkipSchemaUpgrade) { + console.log('WARNING: `attachment.data` is `undefined`'); + return attachment; + } + + const isValidData = isArrayBuffer(data); + if (!isValidData) { + throw new TypeError('Expected `attachment.data` to be an array buffer;' + + ` got: ${typeof attachment.data}`); + } + + const path = await writeAttachmentData(data); + + const attachmentWithoutData = Object.assign({}, attachment, { path }); + delete attachmentWithoutData.data; + return attachmentWithoutData; +}; diff --git a/test/modules/types/attachment_test.js b/test/modules/types/attachment_test.js index bf4b8f6a4..8d7fe6484 100644 --- a/test/modules/types/attachment_test.js +++ b/test/modules/types/attachment_test.js @@ -1,5 +1,6 @@ require('mocha-testcheck').install(); +const stringToArrayBuffer = require('string-to-arraybuffer'); const { assert } = require('chai'); const Attachment = require('../../../js/modules/types/attachment'); @@ -101,4 +102,81 @@ describe('Attachment', () => { assert.deepEqual(actual, expected); }); }); + + describe('migrateDataToFileSystem', () => { + it('should write data to disk and store relative path to it', async () => { + const input = { + contentType: 'image/jpeg', + data: stringToArrayBuffer('Above us only sky'), + fileName: 'foo.jpg', + size: 1111, + }; + + const expected = { + contentType: 'image/jpeg', + path: 'abc/abcdefgh123456789', + fileName: 'foo.jpg', + size: 1111, + }; + + const expectedAttachmentData = stringToArrayBuffer('Above us only sky'); + const writeAttachmentData = async (attachmentData) => { + assert.deepEqual(attachmentData, expectedAttachmentData); + return 'abc/abcdefgh123456789'; + }; + + const actual = await Attachment.migrateDataToFileSystem( + input, + { writeAttachmentData } + ); + assert.deepEqual(actual, expected); + }); + + it('should skip over (invalid) attachments without data', async () => { + const input = { + contentType: 'image/jpeg', + fileName: 'foo.jpg', + size: 1111, + }; + + const expected = { + contentType: 'image/jpeg', + fileName: 'foo.jpg', + size: 1111, + }; + + const writeAttachmentData = async () => + 'abc/abcdefgh123456789'; + + const actual = await Attachment.migrateDataToFileSystem( + input, + { writeAttachmentData } + ); + assert.deepEqual(actual, expected); + }); + + it('should throw error if data is not valid', async () => { + const input = { + contentType: 'image/jpeg', + data: 42, + fileName: 'foo.jpg', + size: 1111, + }; + + const writeAttachmentData = async () => + 'abc/abcdefgh123456789'; + + try { + await Attachment.migrateDataToFileSystem(input, { writeAttachmentData }); + } catch (error) { + assert.strictEqual( + error.message, + 'Expected `attachment.data` to be an array buffer; got: number' + ); + return; + } + + assert.fail('Unreachable'); + }); + }); });