@@ -1,20 +1,33 @@
'use strict';

var parts = ['Apps', 'Users', 'Keys', 'Snapshots', 'Databases', 'Logs', 'Client'];

parts.forEach(function (k) {
parts.forEach(function forEach(k) {
exports[k] = require('./client/' + k.toLowerCase())[k];
})
});

exports.createClient = function (options) {
//
// ### function createClient(options)
// #### @options {Object} options for the clients
// Generates a new API client.
//
exports.createClient = function createClient(options) {
var client = {};
parts.forEach(function (k) {
client[k.toLowerCase()] = new exports[k](options);
client[k.toLowerCase()].on('debug::request', debug);
client[k.toLowerCase()].on('debug::response', debug);
});
function debug (arguments) {

parts.forEach(function generate(k) {
var endpoint = k.toLowerCase();

client[endpoint] = new exports[k](options);

if (options.debug) {
console.log(arguments);
client[endpoint].on('debug::request', debug);
client[endpoint].on('debug::response', debug);
}
});

function debug(args) {
console.log(args);
}

return client;
}
};
@@ -1,3 +1,5 @@
'use strict';

/*
* app.js: Client for the Nodejitsu apps API.
*
@@ -16,12 +18,83 @@ var util = require('util'),
// with Nodejitsu's Apps API
//
var Apps = exports.Apps = function (options) {
this.clouds = {};
this.datacenters = {};

Client.call(this, options);
};

// Inherit from Client base object
util.inherits(Apps, Client);

//
// ### @private function request
// #### @options {Object}
// #### @callback {function} Continuation to call if errors occur.
// Overrites the default request logic to make it aware of datacenter locations
//
Apps.prototype.request = function (options, callback) {
var self = this,
flow = [];

// we don't need to have any datacenter information for these types of calls
if (options.remoteUri || !options.app || !options.method || options.method === 'GET') {
return Client.prototype.request.apply(this, arguments);
}

//
// We don't have any datacenter data by default as it's only needed for
// starting or stopping the application.
//
if (!Object.keys(this.datacenters)) flow.push(function (done) {
self.endpoints(function endpoints(err, datacenters) {
if (err) return done(err);

self.datacenters = datacenters;
done();
});
});

//
// Make sure that we have this app in our cloud cache so we know in which
// datacenter it is.
//
if (!(options.app in this.clouds)) flow.push(function (done) {
self.list(function apps(err, applications) {
if (err) return done(err);

self.clouds = applications.reduce(function reduce(memo, app) {
memo[app] = app.cloud;
}, {});

done();
});
});

//
// Upgrade the remoteUri to the correct location of the datacenter or we will
// not be able to communicate correctly with it.
//
flow.push(function (done) {
var cloud = self.clouds[options.app];

options.remoteUri = self.datacenters[cloud.provider][cloud.datacenter];
Client.prototype.request.call(self, options, callback);
});

var completed = 0;
(function iterate() {
flow[completed](function next(err) {
if (err) {
callback(err);
} else {
completed++;
if (completed !== flow.length) iterate();
}
});
}());
};

//
// ### function list (username, callback)
// #### @callback {function} Continuation to pass control to when complete
@@ -33,8 +106,10 @@ Apps.prototype.list = function (username, callback) {
username = this.options.get('username');
}

this.request('GET', ['apps', username], callback, function (res, result) {
callback(null, result.apps || res.statusCode);
this.request({ uri: ['apps', username] }, function (err, result, res) {
if (err) return callback(err);

callback(err, result.apps);
});
};

@@ -47,9 +122,7 @@ Apps.prototype.list = function (username, callback) {
Apps.prototype.create = function (app, callback) {
var appName = defaultUser.call(this, app.name);

this.request('POST', ['apps', appName], app, callback, function (res, result) {
callback(null, result || res.statusCode);
});
this.request({ method: 'POST', uri: ['apps', appName], body: app }, callback);
};

//
@@ -62,8 +135,10 @@ Apps.prototype.view = function (appName, callback) {
appName = defaultUser.call(this, appName);
var argv = ['apps'].concat(appName.split('/'));

this.request('GET', argv, callback, function (res, result) {
callback(null, result.app || res.statusCode);
this.request({ uri: argv }, function (err, result, res) {
if (err) return callback(err);

callback(err, result.app);
});
};

@@ -78,9 +153,7 @@ Apps.prototype.update = function (appName, attrs, callback) {
appName = defaultUser.call(this, appName);
var argv = ['apps'].concat(appName.split('/'));

this.request('PUT', argv, attrs, callback, function (res, result) {
callback(null, result || res.statusCode);
});
this.request({ method: 'PUT', uri: argv, body: attrs }, callback);
};

//
@@ -93,9 +166,7 @@ Apps.prototype.destroy = function (appName, callback) {
appName = defaultUser.call(this, appName);
var argv = ['apps'].concat(appName.split('/'));

this.request('DELETE', argv, callback, function (res, result) {
callback(null, result || res.statusCode);
});
this.request({ method: 'DELETE', uri: argv }, callback);
};

//
@@ -108,9 +179,7 @@ Apps.prototype.start = function (appName, callback) {
appName = defaultUser.call(this, appName);
var argv = ['apps'].concat(appName.split('/')).concat('start');

this.request('POST', argv, callback, function (res, result) {
callback(null, result || res.statusCode);
});
this.request({ method: 'POST', uri: argv }, callback);
};

//
@@ -123,9 +192,7 @@ Apps.prototype.restart = function (appName, callback) {
appName = defaultUser.call(this, appName);
var argv = ['apps'].concat(appName.split('/')).concat('restart');

this.request('POST', argv, callback, function (res, result) {
callback(null, result || res.statusCode);
});
this.request({ method: 'POST', uri: argv }, callback);
};

//
@@ -138,9 +205,7 @@ Apps.prototype.stop = function (appName, callback) {
appName = defaultUser.call(this, appName);
var argv = ['apps'].concat(appName.split('/')).concat('stop');

this.request('POST', argv, callback, function (res, result) {
callback(null, result || res.statusCode);
});
this.request({ method: 'POST', uri: argv }, callback);
};

//
@@ -154,9 +219,7 @@ Apps.prototype.available = function (app, callback) {
var appName = defaultUser.call(this, app.name),
argv = ['apps'].concat(appName.split('/')).concat('available');

this.request('POST', argv, app, callback, function (res, result) {
callback(null, result || res.statusCode);
});
this.request({ method: 'POST', uri: argv, body: app }, callback);
};

//
@@ -167,10 +230,37 @@ Apps.prototype.available = function (app, callback) {
// Runs `app` on `drones` drones.
//
Apps.prototype.setDrones = function (appName, drones, callback) {
defaultUser.call(this, appName);
var argv = ['apps'].concat(appName.split('/')).concat('drones');
appName = defaultUser.call(this, appName);
var argv = ['apps'].concat(appName.split('/')).concat('cloud');

this.request({ method: 'POST', uri: argv, body: [{ drones: drones }] }, callback);
};

//
// ### function datacenter(appName, cloud, callback)
// #### @appName {String} Name of the application
// #### @cloud {Object} Cloud specification
// #### @callback {Function} Continuation to pass control to when complete
// Deploy the given application in a new datacenter.
//
Apps.prototype.datacenter = function (appName, cloud, callback) {
appName = defaultUser.call(this, appName);
var argv = ['apps'].concat(appName.split('/')).concat('cloud');

if (!Array.isArray(cloud)) cloud = [cloud];

this.request({ method: 'POST', uri: argv, body: cloud }, callback);
};

//
// ### function endpoints(callback)
// #### @callback {function} Continuation to respond to when complete.
// Retrieves a list of currenlty active datacenters and providers
//
Apps.prototype.endpoints = function (callback) {
this.request({ uri: ['endpoints'] }, function (err, result) {
if (err) return callback(err);

this.request('POST', argv, { drones: drones }, callback, function (res, result) {
callback(null, result || res.statusCode);
callback(err, result.endpoints);
});
};