Use API introspection.
This commit is contained in:
parent
84e6228f90
commit
061d8dc94f
@ -30,7 +30,7 @@
|
||||
"l33teral": "^2.0.4",
|
||||
"lodash": "^2.4.1",
|
||||
"mkdirp": "^0.5.0",
|
||||
"nomnom": "^1.6.2",
|
||||
"multiline": "^0.3.4",
|
||||
"ws": "^0.4.31",
|
||||
"xdg": "^0.1.1"
|
||||
},
|
||||
|
@ -3,8 +3,9 @@
|
||||
//====================================================================
|
||||
|
||||
var _ = require('lodash');
|
||||
var nomnom = require('nomnom')();
|
||||
var Promise = require('bluebird');
|
||||
var multiline = require('multiline');
|
||||
var chalk = require('chalk');
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
@ -14,15 +15,6 @@ var Xo = require('./xo');
|
||||
|
||||
//====================================================================
|
||||
|
||||
//`nomnom.print()` version which does not use `process.exit()`.
|
||||
nomnom.print = function (str, code) {
|
||||
throw ''+ str;
|
||||
|
||||
// TODO: handles code.
|
||||
};
|
||||
|
||||
//====================================================================
|
||||
|
||||
var connect = function () {
|
||||
return config.load().bind({}).then(function (config) {
|
||||
if (!config.server)
|
||||
@ -37,7 +29,7 @@ var connect = function () {
|
||||
|
||||
this.xo = new Xo(config.server);
|
||||
|
||||
return this.xo.send('session.signInWithToken', {
|
||||
return this.xo.call('session.signInWithToken', {
|
||||
token: config.token,
|
||||
});
|
||||
}).then(function () {
|
||||
@ -45,188 +37,114 @@ var connect = function () {
|
||||
}).bind();
|
||||
};
|
||||
|
||||
var wrap = function (val) {
|
||||
return function () {
|
||||
return val;
|
||||
};
|
||||
};
|
||||
|
||||
//====================================================================
|
||||
|
||||
module.exports = function (argv) {
|
||||
var command;
|
||||
var commandName;
|
||||
var commandOpts;
|
||||
|
||||
var registerCommand = function (name, def, fn) {
|
||||
var cmdParser = nomnom.command(name);
|
||||
|
||||
if (def.description)
|
||||
{
|
||||
cmdParser.help(def.description);
|
||||
}
|
||||
|
||||
if (def.args)
|
||||
{
|
||||
var interactive = process.stdin.isTTY;
|
||||
|
||||
_.each(def.args, function (def, name) {
|
||||
cmdParser.option(name, {
|
||||
abbr: def.abbr,
|
||||
flag: (def.type === 'boolean'),
|
||||
help: def.description,
|
||||
|
||||
// Do not mark options as required if the program is run in
|
||||
// interactive mode, they will be asked.
|
||||
required: !interactive && def.required,
|
||||
});
|
||||
});
|
||||
|
||||
fn = (function (fn, args) {
|
||||
return function (opts) {
|
||||
var prompts = [];
|
||||
|
||||
_.each(args, function (def, name) {
|
||||
if (!(name in opts))
|
||||
{
|
||||
prompts.push({
|
||||
name: name,
|
||||
message: def.prompt || def.description || name,
|
||||
type: def.promptType || 'input',
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
if (prompts.length)
|
||||
{
|
||||
return prompt(prompts).then(function (answers) {
|
||||
return fn(_.extend(opts, answers));
|
||||
});
|
||||
}
|
||||
|
||||
return fn(opts);
|
||||
};
|
||||
})(fn, def.args);
|
||||
}
|
||||
|
||||
cmdParser.callback(function (opts) {
|
||||
command = fn;
|
||||
commandName = name;
|
||||
commandOpts = opts;
|
||||
});
|
||||
};
|
||||
|
||||
registerCommand('add-server', {
|
||||
description: 'adds a new Xen server',
|
||||
args: {
|
||||
host: {
|
||||
description: 'host of the server',
|
||||
required: true,
|
||||
},
|
||||
username: {
|
||||
description: 'username to use to connect',
|
||||
required: true,
|
||||
},
|
||||
password: {
|
||||
description: 'password to use to connect',
|
||||
promptType: 'password',
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
}, function (opts) {
|
||||
return connect().then(function (connection) {
|
||||
return connection.send('server.add', {
|
||||
host: opts.host,
|
||||
username: opts.username,
|
||||
password: opts.password,
|
||||
});
|
||||
}).return('ok');
|
||||
});
|
||||
|
||||
registerCommand('register', {
|
||||
description: 'registers the XO instance',
|
||||
args: {
|
||||
host: {
|
||||
description: 'host/ip optionally followed by `:port` if not 80',
|
||||
required: true,
|
||||
},
|
||||
email: {
|
||||
description: 'email to use to connect',
|
||||
required: true,
|
||||
},
|
||||
password: {
|
||||
description: 'password to use to connect',
|
||||
promptType: 'password',
|
||||
required: true,
|
||||
},
|
||||
}
|
||||
}, function (opts) {
|
||||
return Promise.bind({opts: opts || {}}).then(function () {
|
||||
this.xo = new Xo(opts.host);
|
||||
|
||||
return this.xo.send('session.signInWithPassword', {
|
||||
email: this.opts.email,
|
||||
password: this.opts.password,
|
||||
});
|
||||
}).then(function (user) {
|
||||
console.log('Successfully logged with', user.email);
|
||||
|
||||
return this.xo.send('token.create');
|
||||
}).then(function (token) {
|
||||
console.log('Token created:', token);
|
||||
|
||||
return config.set({
|
||||
server: this.opts.host,
|
||||
token: token,
|
||||
});
|
||||
}).bind();
|
||||
});
|
||||
|
||||
registerCommand('list-commands', {}, function () {
|
||||
return connect().then(function (xo) {
|
||||
return xo.send('system.listMethods').then(JSON.stringify);
|
||||
});
|
||||
});
|
||||
registerCommand('show-command', {
|
||||
args: {
|
||||
name: {
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
}, function (opts) {
|
||||
return connect().then(function (xo) {
|
||||
return xo.send('system.methodSignature', {
|
||||
name: opts.name
|
||||
}).then(console.log);
|
||||
});
|
||||
});
|
||||
|
||||
registerCommand('whoami', {
|
||||
description: 'displays information about the current user',
|
||||
}, function () {
|
||||
return connect().then(function (xo) {
|
||||
return xo.send('session.getUser');
|
||||
}).then(function (user) {
|
||||
if (user)
|
||||
{
|
||||
console.log('You are signed in as', user.email);
|
||||
console.log('Your global permission is', user.permission);
|
||||
}
|
||||
else
|
||||
{
|
||||
console.log('Your are not signed in.');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// TODO: handle global `--config FILE` option.
|
||||
var opts = nomnom
|
||||
.option('version', {
|
||||
flag: true,
|
||||
help: 'prints the current version of xo-cli',
|
||||
})
|
||||
.parse(argv)
|
||||
;
|
||||
|
||||
if (opts.version)
|
||||
{
|
||||
return 'xo-cli version '+ require('../package').version;
|
||||
exports = module.exports = function (args) {
|
||||
if (!args || !args.length) {
|
||||
return help();
|
||||
}
|
||||
|
||||
// Executes the selected command.
|
||||
return command(commandOpts);
|
||||
var fnName = args[0].replace(/^--|-\w/g, function (match) {
|
||||
if (match === '--')
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
return match[1].toUpperCase();
|
||||
});
|
||||
if (fnName in exports) {
|
||||
return exports[fnName](args.slice(1));
|
||||
}
|
||||
|
||||
return exports.call(args);
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
var help = exports.help = wrap(multiline.stripIndent(function () {/*
|
||||
Usage:
|
||||
|
||||
xo-cli --register [<XO-Server URL>] [<username>] [<password>]
|
||||
Registers the XO instance to use.
|
||||
|
||||
xo-cli --list-commands [--json]
|
||||
Returns the list of available commands on the current XO instance.
|
||||
|
||||
xo-cli <command> [<name>=<value>]...
|
||||
Executes a command on the current XO instance.
|
||||
*/}));
|
||||
|
||||
exports.version = wrap('xo-cli v'+ require('../package').version);
|
||||
|
||||
exports.register = function (args) {
|
||||
};
|
||||
|
||||
exports.listCommands = function (args) {
|
||||
return connect().then(function (xo) {
|
||||
return xo.call('system.getMethodsInfo');
|
||||
}).then(function (methods) {
|
||||
if (args.indexOf('--json') !== -1)
|
||||
{
|
||||
return methods;
|
||||
}
|
||||
|
||||
methods = _.pairs(methods);
|
||||
methods.sort(function (a, b) {
|
||||
a = a[0];
|
||||
b = b[0];
|
||||
if (a < b) {
|
||||
return -1;
|
||||
}
|
||||
return +(a > b);
|
||||
});
|
||||
|
||||
var str = [];
|
||||
methods.forEach(function (method) {
|
||||
var name = method[0];
|
||||
var info = method[1];
|
||||
str.push(chalk.bold.blue(name));
|
||||
_.each(info.params || [], function (info, name) {
|
||||
str.push(' ');
|
||||
if (info.optional) {
|
||||
str.push('[');
|
||||
}
|
||||
str.push(name, '=<', info.type || 'unknown', '>');
|
||||
if (info.optional) {
|
||||
str.push(']');
|
||||
}
|
||||
});
|
||||
str.push('\n');
|
||||
if (info.description) {
|
||||
str.push(' ', info.description, '\n');
|
||||
}
|
||||
});
|
||||
return str.join('');
|
||||
});
|
||||
};
|
||||
|
||||
var PARAM_RE = /^([^=]+)=(.*)$/;
|
||||
exports.call = function (args) {
|
||||
if (!args.length) {
|
||||
throw 'missing command name';
|
||||
}
|
||||
|
||||
var method = args.shift();
|
||||
var params = {};
|
||||
args.forEach(function (arg) {
|
||||
var matches;
|
||||
if (!(matches = arg.match(PARAM_RE))) {
|
||||
throw 'invalid arg: '+arg;
|
||||
}
|
||||
params[matches[1]] = matches[2];
|
||||
});
|
||||
|
||||
return connect().then(function (xo) {
|
||||
return xo.call(method, params);
|
||||
});
|
||||
};
|
||||
|
@ -14,6 +14,12 @@ var WebSocket = (this && 'WebSocket' in this) ? this.WebSocket : require('ws');
|
||||
|
||||
//====================================================================
|
||||
|
||||
var notConnected = function () {
|
||||
throw new Error('not connected');
|
||||
};
|
||||
|
||||
//====================================================================
|
||||
|
||||
var Xo = function (url) {
|
||||
this._url = url;
|
||||
|
||||
@ -102,9 +108,7 @@ _.extend(Xo.prototype, {
|
||||
|
||||
socket.on('close', function () {
|
||||
// Closes accesses.
|
||||
this.send = function () {
|
||||
throw new Error('not connected');
|
||||
};
|
||||
this.send = notConnected;
|
||||
|
||||
// Fails all waiting requests.
|
||||
_.each(this._deferreds, function (deferred) {
|
||||
@ -121,7 +125,7 @@ _.extend(Xo.prototype, {
|
||||
return deferred.promise;
|
||||
},
|
||||
|
||||
send: function (method, params) {
|
||||
call: function (method, params) {
|
||||
return this.connect().then(function () {
|
||||
var socket = this._socket;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user