diff --git a/.jshintrc b/.jshintrc index 08096ef..2ca6b93 100644 --- a/.jshintrc +++ b/.jshintrc @@ -28,5 +28,6 @@ "white": false, "eqnull": true, "esnext": true, - "unused": true + "unused": true, + "node": true } diff --git a/README.md b/README.md index 0213324..6dcc3e7 100644 --- a/README.md +++ b/README.md @@ -172,6 +172,10 @@ The maximum number of recent revisions to keep in Redis. *Default:* `10` +### revisionData + +Metadata about the revision being uploaded. (normally provided by a plugin like [ember-cli-deploy-revision-data][6]) + ## Activation As well as uploading a file to Redis, *ember-cli-deploy-redis* has the ability to mark a revision of a deployed file as `current`. This is most commonly used in the [lightning method of deployment][1] whereby an index.html file is pushed to Redis and then served to the user by a web server. The web server could be configured to return any existing revision of the index.html file as requested by a query parameter. However, the revision marked as the currently `active` revision would be returned if no query paramter is present. For more detailed information on this method of deployment please refer to the [ember-cli-deploy-lightning-pack README][1]. diff --git a/index.js b/index.js index 7cdde62..b698dc6 100644 --- a/index.js +++ b/index.js @@ -55,6 +55,10 @@ module.exports = { var redisLib = context._redisLib; return new Redis(redisOptions, redisLib); + }, + + revisionData: function(context) { + return context.revisionData; } }, configure: function(/* context */) { @@ -69,7 +73,7 @@ module.exports = { this.log('config ok', { verbose: true }); }, - upload: function(/* context */) { + upload: function(context) { var redisDeployClient = this.readConfig('redisDeployClient'); var revisionKey = this.readConfig('revisionKey'); var distDir = this.readConfig('distDir'); @@ -80,7 +84,7 @@ module.exports = { this.log('Uploading `' + filePath + '`', { verbose: true }); return this._readFileContents(filePath) - .then(redisDeployClient.upload.bind(redisDeployClient, keyPrefix, revisionKey)) + .then(redisDeployClient.upload.bind(redisDeployClient, keyPrefix, revisionKey, this.readConfig('revisionData'))) .then(this._uploadSuccessMessage.bind(this)) .then(function(key) { return { redisKey: key }; diff --git a/lib/redis.js b/lib/redis.js index 6032acf..ef78c92 100644 --- a/lib/redis.js +++ b/lib/redis.js @@ -2,6 +2,7 @@ var CoreObject = require('core-object'); var Promise = require('ember-cli/lib/ext/promise'); module.exports = CoreObject.extend({ + init: function(options, lib) { var redisOptions = options; var redisLib = lib; @@ -39,12 +40,21 @@ module.exports = CoreObject.extend({ var keyPrefix = args.shift(); var value = args.pop(); var revisionKey = args[0] || 'default'; + var revisionData = args[1]; var redisKey = keyPrefix + ':' + revisionKey; var maxEntries = this._maxRecentUploads; + var _this = this; return Promise.resolve() .then(this._uploadIfKeyDoesNotExist.bind(this, redisKey, value)) + .then(function() { + if (revisionData) { + return _this._uploadRevisionData(keyPrefix, revisionKey, revisionData); + } else { + return Promise.resolve(); + } + }) .then(this._updateRecentUploadsList.bind(this, keyPrefix, revisionKey)) .then(this._trimRecentUploadsList.bind(this, keyPrefix, maxEntries)) .then(function() { @@ -60,17 +70,25 @@ module.exports = CoreObject.extend({ }, fetchRevisions: function(keyPrefix) { - return Promise.hash({ - revisions: this._listRevisions(keyPrefix), - current: this.activeRevision(keyPrefix) + var _this = this; + return this._listRevisions(keyPrefix).then(function(revisions) { + return Promise.hash({ + revisions: Promise.resolve(revisions), + current: _this.activeRevision(keyPrefix), + revisionData: _this._revisionData(keyPrefix, revisions) + }); }).then(function(results) { - return results.revisions.map(function(revision) { - return { - revision: revision, - active: revision === results.current - }; - }); + return results.revisions.map(function(revision, i) { + var hash = { + revision: revision, + active: revision === results.current, + }; + if (results.revisionData) { + hash.revisionData = results.revisionData[i]; + } + return hash; }); + }); }, activeRevision: function(keyPrefix) { @@ -78,6 +96,24 @@ module.exports = CoreObject.extend({ return this._client.get(currentKey); }, + _revisionData: function(keyPrefix, revisions) { + if (revisions.length === 0) { + return Promise.resolve(); + } + var dataKeys = revisions.map(function(revision) { + return keyPrefix + ':revision-data:' + revision; + }); + + return this._client.mget(dataKeys).then(function(data) { + if (!data) { + return Promise.resolve(); + } + return data.map(function(d) { + return JSON.parse(d); + }); + }); + }, + _listRevisions: function(keyPrefix) { var client = this._client; var listKey = keyPrefix + ':revisions'; @@ -116,7 +152,7 @@ module.exports = CoreObject.extend({ }); }, reject - ); + ); }); }, @@ -138,6 +174,15 @@ module.exports = CoreObject.extend({ }); }, + _uploadRevisionData: function(keyPrefix, revisionKey, revisionData) { + var client = this._client; + var redisKey = keyPrefix + ':revision-data:' + revisionKey; + return Promise.resolve() + .then(function() { + return client.set(redisKey, JSON.stringify(revisionData)); + }); + }, + _updateRecentUploadsList: function(keyPrefix, revisionKey) { var client = this._client; var score = new Date().getTime(); @@ -161,6 +206,7 @@ module.exports = CoreObject.extend({ revisions.forEach(function(revision) { if (revision !== current) { client.del(keyPrefix + ":" + revision); + client.del(keyPrefix + ":revision-data:" + revision); client.zrem(listKey, revision); } }); diff --git a/tests/helpers/fake-redis-client.js b/tests/helpers/fake-redis-client.js index d58a859..3213078 100644 --- a/tests/helpers/fake-redis-client.js +++ b/tests/helpers/fake-redis-client.js @@ -1,3 +1,5 @@ +/* jshint node: true */ +var Promise = require('ember-cli/lib/ext/promise'); var CoreObject = require('core-object'); module.exports = CoreObject.extend({ @@ -21,6 +23,9 @@ module.exports = CoreObject.extend({ zrange: function() { }, zrevrange: function() { - return this.recentRevisions; + return Promise.resolve(this.recentRevisions); + }, + mget: function() { + return Promise.resolve(); } }); diff --git a/tests/unit/index-nodetest.js b/tests/unit/index-nodetest.js index 7c493bc..9941356 100644 --- a/tests/unit/index-nodetest.js +++ b/tests/unit/index-nodetest.js @@ -1,3 +1,5 @@ +/* jshint node: true */ +/* jshint jasmine: true */ 'use strict'; var Promise = require('ember-cli/lib/ext/promise'); diff --git a/tests/unit/lib/redis-nodetest.js b/tests/unit/lib/redis-nodetest.js index 028f78d..4aae41a 100644 --- a/tests/unit/lib/redis-nodetest.js +++ b/tests/unit/lib/redis-nodetest.js @@ -1,3 +1,5 @@ +/* jshint node: true */ +/* jshint jasmine: true */ 'use strict'; var FakeRedis = require('../../helpers/fake-redis-lib'); @@ -81,7 +83,7 @@ describe('redis', function() { }); }); - it('trims the list of recent uploads and removes the index key', function() { + it('trims the list of recent uploads and removes the index key and the revisionData', function() { var finalUploads = ['3','4','5','6','7','8','9','10','11','key:12']; var redis = new Redis({}, new FakeRedis(FakeClient.extend({ @@ -89,7 +91,7 @@ describe('redis', function() { return Promise.resolve(null); }, del: function(key) { - assert(key === 'key:1' || key === 'key:2'); + assert(key === 'key:1' || key === 'key:2' || key === 'key:revision-data:1' || key === 'key:revision-data:2'); }, zrange: function() { return this.recentRevisions.slice(0,2); @@ -339,5 +341,38 @@ describe('redis', function() { ); }); }); + + it('retrieves revisionData', function() { + var redis = new Redis({}, new FakeRedis(FakeClient.extend({ + get: function() { + }, + mget: function(keys) { + return Promise.resolve(['{"revisionKey":"a","timestamp":"2016-03-13T14:25:40.563Z","scm":{"sha":"9101968710f18a6720c48bf032fd82efd5743b7d","email":"mattia@mail.com","name":"Mattia Gheda","timestamp":"2015-12-22T12:44:48.000Z","branch":"master"}}']); + } + }))); + + redis._client.recentRevisions = ['a']; + + var promise = redis.fetchRevisions('key-prefix'); + return assert.isFulfilled(promise) + .then(function(result) { + assert.deepEqual(result, [ + { + revision: 'a', + active: false, + revisionData: { + revisionKey: 'a', + timestamp: '2016-03-13T14:25:40.563Z', + scm: + { sha: '9101968710f18a6720c48bf032fd82efd5743b7d', + email: 'mattia@mail.com', + name: 'Mattia Gheda', + timestamp: '2015-12-22T12:44:48.000Z', + branch: 'master' } + } + } + ]); + }); + }); }); });