From 98c43e23a0af134dee41c50252506678c025cb23 Mon Sep 17 00:00:00 2001 From: Richard Walker Date: Mon, 12 Mar 2018 17:15:01 +0100 Subject: [PATCH] feat: add bundlingComplete helper method This method simplifies the process of checking whether bundling is complete. --- README.md | 35 +++++++++++++++++++ lib/main.js | 17 +++++++++ test/unit/main.test.js | 78 ++++++++++++++++++++++++++++++++++-------- 3 files changed, 115 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index fcc669f..156c5c9 100644 --- a/README.md +++ b/README.md @@ -110,6 +110,13 @@ endpoint. GET http:///bundle/.js ``` +To make some of this a little easier, 2 helper methods exist on the client: + +* `bundleURL` - calculates the url location for a bundle based on a given array of asset hashes. +* `bundlingComplete` - determines if bundling is complete for a given array of asset hashes. + +See API documentation below for more information. + ## Installation ```bash @@ -377,6 +384,34 @@ const { uri, id2 } = await client.publishAssets('podlet2', ['/path/to/file.js']) const url = await client.bundleURL([id1, id2]); ``` +### bundlingComplete(feedhashes, options) + +Calculates whether a bundling for the given `feedHashes` has been completed. The rules for this method are as follows: + +* If `feedHashes` is an empty array, this method resolves to `true` as no bundle needs to be built. +* Otherwise, if `feedHashes` is not an empty array then a bundle url will be computed and a request made to check if the file exists on the server. + +* `hashes` - `string[]` - array of asset feed content hashes as returned by `client.publishAssets` +* `options` - `object` + * `options.prefix` - `string` url prefix to use when building bundle url. Defaults to `${client.buildServerUri}/bundle/` which is the location on the asset server that a bundle can be located. Overwrite this if you use a CDN and need to point to that. + * `options.type` - `string` (`js`|`css`) - file type. Defaults to `js` + +`return` - `Promise` - resolves to a boolean representing whether the bundling process for the given `feedHashes` is considered to be complete. + +**Example** + +```js +// publish instructions +await client.publishInstructions('layout', 'js', ['podlet1', 'podlet2']); + +// publish necessary assets +const { uri, id1 } = await client.publishAssets('podlet1', ['/path/to/file.js']); +const { uri, id2 } = await client.publishAssets('podlet2', ['/path/to/file.js']); + +// calculate the url of the finished bundle +const isComplete = await client.bundlingComplete([id1, id2]); +``` + ## Transpilers Since [asset-pipe][asset-pipe] is built on [browserify][browserify] under the diff --git a/lib/main.js b/lib/main.js index 19b3df8..b3d4c5c 100644 --- a/lib/main.js +++ b/lib/main.js @@ -69,6 +69,15 @@ function post(options) { }); } +function get(uri) { + return new Promise((resolve, reject) => { + request(uri, (error, response, body) => { + if (error) return reject(error); + resolve({ response, body }); + }); + }); +} + module.exports = class Client { constructor({ serverId, buildServerUri, minify, sourceMaps } = {}) { assert( @@ -370,6 +379,7 @@ module.exports = class Client { } async bundleURL(feedHashes, options = {}) { + if (feedHashes.length === 0) return null; const { type, prefix } = { prefix: url.resolve(this.buildServerUri, '/bundle/'), type: 'js', @@ -379,4 +389,11 @@ module.exports = class Client { const filename = this.bundleFilename(hash, type); return url.resolve(prefix, filename); } + + async bundlingComplete(feedHashes, options = {}) { + const uri = await this.bundleURL(feedHashes, options); + if (!uri) return true; + const { response } = await get(uri); + return response.statusCode >= 200 && response.statusCode < 300; + } }; diff --git a/test/unit/main.test.js b/test/unit/main.test.js index 124f321..b903edc 100644 --- a/test/unit/main.test.js +++ b/test/unit/main.test.js @@ -6,23 +6,26 @@ const buildServerUri = 'http://server.com:3000'; function createRequestMock(error, response, body) { const { PassThrough } = require('stream'); - return { - post(options, callback) { - const resolve = () => { - if (error) { - return callback(error); - } - callback(null, response, body); - }; - if (options.body) { - return resolve(); + const mockRequest = (url, callback) => { + if (error) return callback(error); + callback(null, response, body); + }; + mockRequest.post = (options, callback) => { + const resolve = () => { + if (error) { + return callback(error); } - const destination = new PassThrough(); - destination.on('data', () => {}); - destination.on('end', resolve); - return destination; - }, + callback(null, response, body); + }; + if (options.body) { + return resolve(); + } + const destination = new PassThrough(); + destination.on('data', () => {}); + destination.on('end', resolve); + return destination; }; + return mockRequest; } function createJsWriterMock() { @@ -365,3 +368,48 @@ test('bundleURL(hashes, options) - js with prefix', async () => { 'http://server/fb8e20fc2e4c3f248c60c39bd652f3c1347298bb977b8b4d5903b85055620603.js' ); }); + +test('bundleURL(hashes, options) - empty array returns null', async () => { + expect.assertions(1); + const client = new Client({ buildServerUri }); + const url = await client.bundleURL([]); + expect(url).toBe(null); +}); + +test('bundlingComplete(hashes, options) - empty hashes array returns true', async () => { + expect.assertions(1); + const client = new Client({ buildServerUri }); + const isComplete = await client.bundlingComplete([]); + expect(isComplete).toBe(true); +}); + +test('bundlingComplete(hashes, options) - empty hashes array returns true', async () => { + expect.assertions(1); + jest.resetModules(); + jest.doMock('request', () => createRequestMock(null, { statusCode: 200 })); + Client = require('../../'); + const client = new Client({ buildServerUri }); + const isComplete = await client.bundlingComplete([]); + expect(isComplete).toBe(true); +}); + +test('bundlingComplete(hashes, options) - bundle does not exist returns false', async () => { + expect.assertions(1); + jest.resetModules(); + jest.doMock('request', () => createRequestMock(null, { statusCode: 404 })); + Client = require('../../'); + const client = new Client({ buildServerUri }); + const isComplete = await client.bundlingComplete(['a12sd2s1a1a1323a']); + expect(isComplete).toBe(false); +}); + +test('bundlingComplete(hashes, options) - request throws', async () => { + expect.assertions(1); + jest.resetModules(); + jest.doMock('request', () => createRequestMock(new Error('fake error'))); + Client = require('../../'); + const client = new Client({ buildServerUri }); + await expect(client.bundlingComplete(['a12sd2s1a1a1323a'])).rejects.toEqual( + new Error('fake error') + ); +});