scratch-storage/src/WebHelper.js
p3nny cc503d5d94 fix: allow asset url function to indicate skipping
If there are multiple web sources, the url function may want to indicate
whether the current asset is available in its source, based on the asset.
While sources are already filtered by asset type, there may be different
properties that determine if a source is applicable.
In our case, we use a special prefix on some projects to indicate a special
source (like a special S3 location). The source will always be the first
source, so we can be sure the prefixed projects are always loaded from the
right source. But if the prefix is missing the url function should be able
to indicate a skip by returning false. Throwing an error is not handled and
results in a failure of the app.
2018-06-07 14:37:28 +02:00

96 lines
3.3 KiB
JavaScript

const nets = require('nets');
const Asset = require('./Asset');
const Helper = require('./Helper');
/**
* @typedef {function} UrlFunction - A function which computes a URL from asset information.
* @param {Asset} - The asset for which the URL should be computed.
* @returns {string} - The URL for the asset.
*/
class WebHelper extends Helper {
constructor (parent) {
super(parent);
/**
* @type {Array.<SourceRecord>}
* @typedef {object} SourceRecord
* @property {Array.<string>} types - The types of asset provided by this source, from AssetType's name field.
* @property {UrlFunction} urlFunction - A function which computes a URL from an Asset.
*/
this.sources = [];
}
/**
* Register a web-based source for assets. Sources will be checked in order of registration.
* @param {Array.<AssetType>} types - The types of asset provided by this source.
* @param {UrlFunction} urlFunction - A function which computes a URL from an Asset.
*/
addSource (types, urlFunction) {
this.sources.push({
types: types.map(assetType => assetType.name),
urlFunction: urlFunction
});
}
/**
* Fetch an asset but don't process dependencies.
* @param {AssetType} assetType - The type of asset to fetch.
* @param {string} assetId - The ID of the asset to fetch: a project ID, MD5, etc.
* @param {DataFormat} dataFormat - The file format / file extension of the asset to fetch: PNG, JPG, etc.
* @return {Promise.<Asset>} A promise for the contents of the asset.
*/
load (assetType, assetId, dataFormat) {
/** @type {Array.<{url:string, result:*}>} List of URLs attempted & errors encountered. */
const errors = [];
const sources = this.sources.slice();
const asset = new Asset(assetType, assetId, dataFormat);
let sourceIndex = 0;
return new Promise((fulfill, reject) => {
const tryNextSource = () => {
/** @type {UrlFunction} */
let urlFunction;
while (sourceIndex < sources.length) {
const source = sources[sourceIndex];
++sourceIndex;
if (source.types.indexOf(assetType.name) >= 0) {
urlFunction = source.urlFunction;
break;
}
}
if (urlFunction) {
const url = urlFunction(asset);
if (url === false) {
tryNextSource();
return;
}
nets({url: url}, (err, resp, body) => {
// body is a Buffer
if (err || Math.floor(resp.statusCode / 100) !== 2) {
tryNextSource();
} else {
asset.setData(body, dataFormat);
fulfill(asset);
}
});
} else if (errors.length > 0) {
reject(errors);
} else {
fulfill(null); // no sources matching asset
}
};
tryNextSource();
});
}
}
module.exports = WebHelper;