diff --git a/lib/jitsu/api/apps.js b/lib/jitsu/api/apps.js index 4cc9ccf..7766441 100644 --- a/lib/jitsu/api/apps.js +++ b/lib/jitsu/api/apps.js @@ -46,6 +46,18 @@ Apps.prototype.create = function (app, callback) { }); }; +// +// ### function view (name, callback) +// #### @name {string} Name of the application to view +// #### @callback {function} Continuation to pass control to when complete +// Views the application specified by `name`. +// +Apps.prototype.view = function (name, callback) { + this._request('GET', ['apps', jitsu.config.username, name], callback, function (res, result) { + callback(null, result.app); + }); +}; + // // ### function update (name, attrs, callback) // #### @name {string} Name of the application to update @@ -54,8 +66,8 @@ Apps.prototype.create = function (app, callback) { // Updates the application with `name` with the specified attributes in `attrs` // Apps.prototype.update = function (name, attrs, callback) { - this._request('PUT', ['apps', name], attrs, callback, function (res, result) { - + this._request('PUT', ['apps', jitsu.config.username, name], attrs, callback, function (res, result) { + callback(); }); }; diff --git a/lib/jitsu/commands/apps.js b/lib/jitsu/commands/apps.js index 19bc851..cf83405 100644 --- a/lib/jitsu/commands/apps.js +++ b/lib/jitsu/commands/apps.js @@ -5,11 +5,49 @@ * */ -var winston = require('winston'), +var eyes = require('eyes'), + winston = require('winston'), jitsu = require('jitsu'); var apps = exports; +// +// ### function list (callback) +// #### @name {string} **optional** Name of the application to create +// #### @callback {function} Continuation to pass control to when complete. +// Creates an application for the package.json in the current directory +// using `name` if supplied and falling back to `package.name`. +// +apps.create = function (name, callback) { + jitsu.utils.readPackage(process.cwd(), function (err, pkg) { + if (!callback) { + callback = name; + name = null; + } + + pkg.name = name || pkg.name; + jitsu.apps.list(function (err, apps) { + var existing = apps.filter(function (a) { return a.name === pkg.name }); + if (existing.length > 0) { + return winston.warn('Cannot create duplicate application ' + pkg.name.magenta); + } + + // + // TODO (indexzero): Configure this default value in nodejitsu APIs + // + pkg.state = 'stopped'; + winston.info('Validating package.json for ' + pkg.name.magenta); + jitsu.prompt.addProperties(pkg, ['subdomain'], function (updated) { + winston.info('Creating app ' + pkg.name.magenta); + jitsu.apps.create(updated, function (err, res, result) { + winston.silly('Done creating app ' + pkg.name.magenta); + return err ? callback(err) : callback(); + }); + }); + }); + }); +}; + // // ### function list (callback) // #### @callback {function} Continuation to pass control to when complete. @@ -40,41 +78,38 @@ apps.list = function (callback) { }; // -// ### function list (callback) -// #### @name {string} **optional** Name of the application to create +// ### function view (name, callback) +// #### @name {string} **optional** Name of the application to view // #### @callback {function} Continuation to pass control to when complete. -// Creates an application for the package.json in the current directory -// using `name` if supplied and falling back to `package.name`. +// Views the application with the specfied `name` for the authenticated user. +// If no name is supplied this will view the application in the current directory. // -apps.create = function (name, callback) { - jitsu.utils.readPackage(process.cwd(), function (err, pkg) { - if (!callback) { - callback = name; - name = null; - } - - pkg.name = name || pkg.name; - jitsu.apps.list(function (err, apps) { - var existing = apps.filter(function (a) { return a.name === pkg.name }); - if (existing.length > 0) { - return winston.warn('Cannot create duplicate application ' + pkg.name.magenta); +apps.view = function (name, callback) { + if (!callback) { + callback = name; + return jitsu.utils.readPackage(process.cwd(), function (err, package) { + name = package.name; + executeView(); + }); + } + + + function executeView() { + jitsu.apps.view(name, function (err, app) { + if (err) { + return callback(err); } // - // TODO (indexzero): Configure this default value in nodejitsu APIs + // TODO (indexzero): Better object inspection // - pkg.state = 'stopped'; - winston.info('Validating package.json for ' + pkg.name.magenta); - jitsu.prompt.addProperties(pkg, ['subdomain'], function (updated) { - winston.info('Creating app ' + pkg.name.magenta); - jitsu.apps.create(updated, function (err, res, result) { - winston.silly('Done creating app ' + pkg.name.magenta); - return err ? callback(err) : callback(); - }); - }); + eyes.inspect(app); + callback(); }); - }); -}; + } + + executeView(); +} // // ### function list (callback) @@ -85,9 +120,33 @@ apps.create = function (name, callback) { apps.update = function (name, callback) { if (!callback) { callback = name; + name = null; } - + winston.silly('Reading package.json in ' + process.cwd()); + jitsu.utils.readPackage(process.cwd(), function (err, pkg) { + if (err) { + return callback(err); + } + + name = name || pkg.name; + jitsu.apps.view(name, function (err, app) { + if (err) { + return callback(err); + } + + var diff = jitsu.utils.objectDiff(app, pkg); + if (!diff) { + winston.warn('No changes found to your package.json for ' + name.magenta); + return callback(); + } + + winston.info('Updating application ' + name.magenta + ' with:'); + eyes.inspect(diff); + + jitsu.apps.update(name, diff, callback); + }); + }); }; // diff --git a/lib/jitsu/commands/snapshots.js b/lib/jitsu/commands/snapshots.js index c6366fa..437831d 100644 --- a/lib/jitsu/commands/snapshots.js +++ b/lib/jitsu/commands/snapshots.js @@ -28,18 +28,28 @@ snapshots.list = function (name, callback) { function executeList () { jitsu.snapshots.list(name, function (err, snapshots) { - var rows = [['filename', 'created', 'md5']], - colors = ['underline', 'yellow', 'grey']; + if (err) { + return callback(err); + } + + if (snapshots && snapshots.length > 0) { + var rows = [['filename', 'created', 'md5']], + colors = ['underline', 'yellow', 'grey']; - snapshots.forEach(function (snap) { - rows.push([ - snap.filename, - jitsu.utils.snapshotTime(snap.filename), - snap.md5 - ]); - }); + snapshots.forEach(function (snap) { + rows.push([ + snap.filename, + jitsu.utils.snapshotTime(snap.filename), + snap.md5 + ]); + }); - jitsu.log.logRows('data', rows, colors); + jitsu.log.logRows('data', rows, colors); + } + else { + winston.warn('No snapshots for application ' + name.magenta); + } + callback(); }); } diff --git a/lib/jitsu/utils/index.js b/lib/jitsu/utils/index.js index 48c948f..572633a 100644 --- a/lib/jitsu/utils/index.js +++ b/lib/jitsu/utils/index.js @@ -7,6 +7,7 @@ var jitsu = require('jitsu'), util = require('util'), + eyes = require('eyes'), spawn = require('child_process').spawn, fs = require('fs'), path = require('path'); @@ -105,4 +106,83 @@ utils.createPackage = function (dir, callback) { callback(null, pkg, tarball); }); }); +}; + +// +// ### function missingKeys (source, target) +// #### @source {Array} List of keys for the current object +// #### @target {Array} List of keys for the new object +// Returns the complement of the intersection of the two arrays. +// +// e.g. [1,2,3,5], [1,2,3,4,5] => [4] +// +utils.missingKeys = function (source, target) { + var missing = []; + + source.forEach(function (key) { + if (target.indexOf(key) === -1) { + missing.push(key); + } + }); + + return missing; +}; + +// +// ### function objectDiff (current, update, level) +// #### @current {Object} Current representation of the object. +// #### @update {Object} Updated representation of the object. +// #### @level {Number} Level in the object we are diffing. +// Returns an incremental diff of the `current` object +// against the updated representation `update` +// +// e.g. { foo: 1, bar: 2 }, { foo: 2, bar: 2 } => { foo: 2 } +// +utils.objectDiff = function (current, update, level) { + var ckeys = Object.keys(current), + ukeys = Object.keys(update), + diff = {}; + + // + // Ignore changes on the first level of the object. + // + level = level || 0; + if (level > 0) { + utils.missingKeys(ckeys, ukeys).forEach(function (key) { + diff[key] = undefined; + }); + } + + ukeys.forEach(function (key) { + var nested; + + if (!current[key]) { + diff[key] = update[key]; + } + else if (Array.isArray(update[key])) { + if (update[key].length !== current[key].length) { + diff[key] = update[key]; + } + else { + for (var i = 0; i < update[key]; i++) { + if (current[key].indexOf(update[key][i]) === -1) { + diff[key] = update[key]; + break; + } + } + } + } + else if (typeof update[key] === 'object') { + if ((nested = utils.objectDiff(current[key], update[key], level + 1))) { + diff[key] = update[key]; + } + } + else { + if (current[key] !== update[key]) { + diff[key] = update[key]; + } + } + }); + + return Object.keys(diff).length > 0 ? diff : null; }; \ No newline at end of file