Skip to content

Commit

Permalink
Remove top level libVersion support, remove historic installed collec…
Browse files Browse the repository at this point in the history
…tions, redirect v1 requests to v2 endpoint.
  • Loading branch information
triblondon committed Dec 26, 2015
1 parent 7330d50 commit 66385a1
Show file tree
Hide file tree
Showing 9 changed files with 132 additions and 283 deletions.
2 changes: 0 additions & 2 deletions .gitignore
@@ -1,8 +1,6 @@
/node_modules
/npm-debug.log
/newrelic_agent.log
/polyfills/__repo
/polyfills/__versions
/polyfills/__dist
/polyfills/aliases.json
/test/results
Expand Down
2 changes: 0 additions & 2 deletions .npmignore
@@ -1,7 +1,5 @@
/node_modules
/npm-debug.log
/polyfills/__repo
/polyfills/__versions
/test/results
/.env.json
/.haikro-cache
Expand Down
7 changes: 0 additions & 7 deletions Gruntfile.js
Expand Up @@ -13,8 +13,6 @@ module.exports = function(grunt) {

grunt.initConfig({
"clean": {
repo: ['polyfills/__repo'],
versions: ['polyfills/__versions'],
dist: ['polyfills/__dist']
},
"simplemocha": {
Expand Down Expand Up @@ -125,17 +123,12 @@ module.exports = function(grunt) {

grunt.registerTask("build", [
"clean",
"installcollections",
"buildsources",
"clean:repo",
"clean:versions"
]);

grunt.registerTask("devbuild", [
"clean",
"buildsources",
"clean:repo",
"clean:versions"
]);

grunt.registerTask('dev', [
Expand Down
26 changes: 4 additions & 22 deletions docs/api.html
Expand Up @@ -6,17 +6,16 @@

<h1>API reference</h1>

{{#ifEq apiversion 1}}
<p>This version of the API is deprecated. It is guaranteed to be available until December 31, 2015, after which requests may be redirected to v2 in a manner that is not guaranteed to be backwards compatible. Please upgrade to <a href='/'>the latest version</a>.</p>
{{/ifEq}}
{{#ifEq apiversion 2}}
<h2>Migrating from v1</h2>

<aside>This describes the API version, not the project version you find in the package.json file.</aside>

<ul>
<li><code>libVersion</code> is no longer supported. We no longer support using an older version of the polyfill collection with the current version of the service. To use an older polyfill library, use the version of the project that contains the library in the state you want it (not possible if using cdn.polyfill.io)</li>
<li><code>gated</code> is no longer supported. Use <code>flags</code> instead.</li>
<li>There are changes to the Node API. See the README for details.</li>
</ul>
{{/ifEq}}

<h2>GET /v{{apiversion}}/polyfill<var>:minify</var>.<var>:type</var></h2>

<p>Fetch a polyfill bundle.</p>
Expand Down Expand Up @@ -66,23 +65,6 @@ <h2>GET /v{{apiversion}}/polyfill<var>:minify</var>.<var>:type</var></h2>
<td>Querystring</td>
<td>Name of JavaScript function to call after polyfills are loaded. Must match the PCRE expression <code>^[\w\.]+$</code> otherwise will have no effect. Note that this feature differs from normal JSONp in that nothing is passed to the function - it is simply a way of triggering your own code when the polyfills have loaded, intended to allow the polyfill service to be more easily loaded asyncronously with <code>async</code> and <code>defer</code> attributes.</td>
</tr>
{{#ifEq apiversion 1}}
<tr>
<td><code>libVersion</code></td>
<td>Querystring</td>
<td>
<p>Version of the polyfill collection to use. Accepts any valid <a href='https://semver.org'>semver</a> expression. This is useful if you wish to synronise releases in the polyfill service with your own release process, to ensure that incompatibilities cannot cause errors in your applications without warning.</p>
{{#hasVersions}}
<p>The following versions are available:</p>
<p class='versionlist'>{{#versions}}<code>{{.}}</code>{{/versions}}</p>
<p>If not specified, the latest version of the library will be used.</p>
{{/hasVersions}}
{{^hasVersions}}
<p><em>Historic polyfill collections are not installed. To install run <code>grunt installcollections</code>. Only the `latest` collection is available.</code></em></p>
{{/hasVersions}}
</td>
</tr>
{{/ifEq}}
<tr>
<td><code>unknown</code></td>
<td>Querystring</td>
Expand Down
20 changes: 4 additions & 16 deletions lib/index.js
Expand Up @@ -12,11 +12,11 @@ require('useragent/features');
"use strict";

function listAllPolyfills() {
return sources.latest.listPolyfills();
return sources.getCollection().listPolyfills();
}

function describePolyfill(featureName) {
return sources.latest.getPolyfill(featureName);
return sources.getCollection().getPolyfill(featureName);
}

function getOptions(opts) {
Expand All @@ -38,8 +38,7 @@ function getPolyfills(options) {
var ua, sourceslib, aliasResolver;

options = getOptions(options);

sourceslib = sources.getCollection(options.libVersion);
sourceslib = sources.getCollection();
ua = new UA(options.uaString);

aliasResolver = new AliasResolver([
Expand Down Expand Up @@ -93,7 +92,7 @@ function getPolyfillString(options) {
lf = options.minify ? '' : '\n';

try {
sourceslib = sources.getCollection(options.libVersion);
sourceslib = sources.getCollection();
} catch(e) {
if (e.message === 'No matching version found') {
sourceslib = null;
Expand All @@ -111,13 +110,6 @@ function getPolyfillString(options) {
explainerComment.push('Version range for polyfill support in this family is: ' + ua.getBaseline());
}
return {};
} else if (!sourceslib) {
explainerComment = [
'Cannot find a polyfill collection to satisfy version: '+options.libVersion+'.',
'Available versions: '+sources.listVersions().join(', ')
];
return {};

} else {
if (options.minify) {
explainerComment = ['Rerun without minification for verbose metadata'];
Expand All @@ -127,7 +119,6 @@ function getPolyfillString(options) {
'',
'UA detected: ' + uaDebugName,
'Features requested: ' + Object.keys(options.features),
'Library version: ' + sourceslib.getVersion(),
''
];
}
Expand Down Expand Up @@ -217,7 +208,4 @@ module.exports = {
getPolyfills: getPolyfills,
getPolyfillString: getPolyfillString,
normalizeUserAgent: UA.normalize,

// Backwards compatibility
getAllPolyfills: listAllPolyfills,
};
57 changes: 17 additions & 40 deletions lib/sources.js
Expand Up @@ -5,62 +5,47 @@ var denodeify = require('denodeify');
var readFile = denodeify(fs.readFile);
var memoize = require('lodash').memoize;

var versions;
var semverVersions;
var configuredAliases;
var features = {};
var features = [];
var polyfillCount = 0;
var polyfillsPath = path.join(__dirname, '../polyfills/__dist');

// Discover and index all the available polyfills (synchronously so that the index is available for the first request)
try {
configuredAliases = require(path.join(__dirname, '../polyfills/__dist/aliases.json'));
versions = fs.readdirSync(polyfillsPath);
semverVersions = versions.filter(function(ver) {
return ver.match(/^v?\d+\.\d+\.\d+$/);
});
versions.forEach(function(version) {
if (fs.lstatSync(path.join(polyfillsPath, version)).isDirectory()) {
features[version] = fs.readdirSync(path.join(polyfillsPath, version)).map(function(fileName) {
return fileName.replace(/\.json$/i, '');
});
polyfillCount += features[version].length;
}
features = fs.readdirSync(polyfillsPath).filter(function(fileName) {
return (fileName.match(/\.json$/) && fileName !== 'aliases.json');
}).map(function(fileName) {
return fileName.replace(/\.json$/i, '');
});
polyfillCount = features.length;
console.log("Found "+polyfillCount+" polyfills");
} catch(e) {
throw Error("No polyfill sources found. Run `grunt buildsources` to build them");
}

function Collection(version) {
this.version = version || 'latest';
function Collection() {
this.cache = {};
if (this.version !== 'latest') {
this.version = semver.maxSatisfying(semverVersions, version);
}
if (versions.indexOf(this.version) === -1) throw new Error('No matching version found');
}

Collection.prototype.polyfillExistsSync = function(featureName) {
return (features[this.version].indexOf(featureName) !== -1);
return (features.indexOf(featureName) !== -1);
};

Collection.prototype.listPolyfills = function() {
return Promise.resolve(features[this.version]);
return Promise.resolve(features);
};

Collection.prototype.getPolyfill = function(featureName) {

// TODO: Should this reject if the polyfill doesn't exist? Makes the main module logic a bit more complex
if (features[this.version].indexOf(featureName) !== -1) {
if (this.version === 'latest' && this.cache[featureName]) {
if (features.indexOf(featureName) !== -1) {
if (this.cache[featureName]) {
return Promise.resolve(this.cache[featureName]);
}
return readFile(path.join(polyfillsPath, this.version, featureName+'.json'), 'utf-8').then(function(str) {
return readFile(path.join(polyfillsPath, featureName+'.json'), 'utf-8').then(function(str) {
var feature = JSON.parse(str);
if (this.version === 'latest') {
this.cache[featureName] = feature;
}
this.cache[featureName] = feature;
return feature;
}.bind(this));
} else {
Expand All @@ -69,19 +54,11 @@ Collection.prototype.getPolyfill = function(featureName) {
};

Collection.prototype.getConfigAliases = function(featureName) {
return Promise.resolve(configuredAliases[this.version][featureName]);
};

Collection.prototype.getVersion = function() {
return this.version;
return Promise.resolve(configuredAliases[featureName]);
};

module.exports = {
getCollection: memoize(function(version) {
return new Collection(version);
}),
listVersions: function() {
return semverVersions.map(function(ver) { return ver.replace(/^v/, ''); });
}
getCollection: memoize(function() {
return new Collection();
})
};
module.exports.latest = module.exports.getCollection('latest');
39 changes: 20 additions & 19 deletions service/index.js
Expand Up @@ -140,27 +140,32 @@ app.get(/^\/__health$/, function(req, res) {

/* API endpoints */

app.get(/^\/v([12])\/polyfill(\.\w+)(\.\w+)?/, function(req, res) {
app.get(/^\/v1\/(.*)/, function(req, res) {

var qs = Object.keys(req.query).reduce(function(out, key) {
if (key !== 'libVersion' && key !== 'gated') {
out.push(key+'='+encodeURIComponent(req.query[key]));
}
return out;
}, []).join('&');
var redirPath = '/v2/' + req.params[0] + (qs.length ? '?'+qs : '');

res.status(301);
res.set('Location', redirPath);
res.set('Deprecation-Notice', 'API version 1 has been decommissioned - see the body of this response for more information.');
res.send('API version 1 has been decommissioned. Your request is being redirected to v2. The `libVersion` and `gated` query string parameters are no longer supported and if present have been removed from your request.\n\nA deprecation period for v1 existed between August and December 2015, during which time v1 requests were honoured but a deprecation warning was added to output.');
});

app.get(/^\/v2\/polyfill(\.\w+)(\.\w+)?/, function(req, res) {
metrics.meter('hits').mark();
var respTimeTimer = metrics.timer('respTime').start();
var apiVersion = parseInt(req.params[0], 10);
var firstParameter = req.params[1].toLowerCase();
var firstParameter = req.params[0].toLowerCase();
var minified = firstParameter === '.min';
var fileExtension = req.params[2] ? req.params[2].toLowerCase() : firstParameter;
var fileExtension = req.params[1] ? req.params[1].toLowerCase() : firstParameter;
var uaString = (typeof req.query.ua === 'string' && req.query.ua) || req.header('user-agent');
var flags = (typeof req.query.flags === 'string') ? req.query.flags.split(',') : [];
var warnings = [];

if (apiVersion === 1) {
warnings.push('API Version 1 is deprecated: please consider upgrading to v2. API v1 will be closed after December 31, 2015, at which time v1 requests will be mapped internally to v2 requests and will be subject to potentially breaking changes. See https://cdn.polyfill.io/v2/docs/api for details.');
}

// Backwards compatibility
if (req.query.gated && apiVersion < 2) {
flags.push('gated');
warnings.push('The `gated` query parameter is deprecated and is not supported in API v2. Set `flags=gated` instead.');
}

// Currently don't support CSS
if (fileExtension !== '.js') {
res.status(404);
Expand All @@ -180,10 +185,6 @@ app.get(/^\/v([12])\/polyfill(\.\w+)(\.\w+)?/, function(req, res) {
features: polyfills.get(),
minify: minified
};
if (req.query.libVersion && apiVersion === 1) {
warnings.push('The `libVersion` query parameter is deprecated and switching library version at runtime is not supported in API v2. To use an older version of the polyfill library, consider running your own version of the service at the version that you want.');
params.libVersion = req.query.libVersion;
}
if (req.query.unknown) {
params.unknown = req.query.unknown;
}
Expand Down Expand Up @@ -213,7 +214,7 @@ app.get(/^\/v([12])\/polyfill(\.\w+)(\.\w+)?/, function(req, res) {

});

app.get("/v[12]/normalizeUa", function(req, res, next) {
app.get("/v2/normalizeUa", function(req, res, next) {

if (req.query.ua) {
res.status(200);
Expand Down

0 comments on commit 66385a1

Please sign in to comment.