From cba3487988018874f41abb032e1a5762e923713e Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Mon, 14 Apr 2014 23:01:56 -0300 Subject: [PATCH 1/9] improve error handling - print error rather than crash --- bin/Copay.js | 26 ++++++++++++++++++-------- bin/copay | 16 ++++++++++------ 2 files changed, 28 insertions(+), 14 deletions(-) diff --git a/bin/Copay.js b/bin/Copay.js index b8239e4bf..2dde15ddf 100644 --- a/bin/Copay.js +++ b/bin/Copay.js @@ -1,6 +1,6 @@ var imports = require('soop').imports(); var PublicKeyRing = imports.PublicKeyRing || require('../js/models/core/PublicKeyRing'); -var Wallet = imports.Wallet || require('../js/models/core/Wallet'); +//var Wallet = imports.Wallet || require('../js/models/core/Wallet'); var Copay = function(opts) { this._init(opts); @@ -23,7 +23,7 @@ Copay.prototype._init = function(opts) { } self.publicKeyRing = self.opts.publicKeyRing ? self.opts.publicKeyRing : new PublicKeyRing(); - self.wallet = self.opts.wallet ? self.opts.wallet : new Wallet(); + //self.wallet = self.opts.wallet ? self.opts.wallet : new Wallet(); }; Copay.prototype._command = function(command, args, callback) { @@ -57,9 +57,9 @@ function checkArgs(name, args) { throw new Error('Invalid arguments'); } -Copay.prototype.echo = function(str, callback) { +Copay.prototype.echo = function echo(str, callback) { var self = this; - checkArgs('echo', arguments); + checkArgs(arguments.callee.name, arguments); return callback(null, str); }; @@ -70,6 +70,7 @@ Copay.prototype.echo.argTypes = ['callback', 'function'] ]; +/* Copay.prototype.getBalance = function(callback) { var self = this; checkArgs('getBalance', arguments); @@ -81,10 +82,11 @@ Copay.prototype.getBalance.argTypes = [ ['callback', 'function'] ]; +*/ -Copay.prototype.getCommands = function(callback) { +Copay.prototype.getCommands = function getCommands(callback) { var self = this; - checkArgs('getCommands', arguments); + checkArgs(arguments.callee.name, arguments); var fs = []; @@ -102,9 +104,9 @@ Copay.prototype.getCommands.argTypes = ['callback', 'function'] ]; -Copay.prototype.getPublicKeyRingId = function(callback) { +Copay.prototype.getPublicKeyRingId = function getPublicKeyRingId(callback) { var self = this; - checkArgs('getPublicKeyRingId', arguments); + checkArgs(arguments.callee.name, arguments); return callback(null, self.publicKeyRing.id); }; @@ -114,5 +116,13 @@ Copay.prototype.getPublicKeyRingId.argTypes = ['callback', 'function'] ]; +Copay.prototype.help = function help(callback) { + this.getCommands.apply(this, arguments); +}; + +Copay.prototype.help.argTypes = + [ + ['callback', 'function'] + ]; module.exports = require('soop')(Copay); diff --git a/bin/copay b/bin/copay index 57ca91cbb..937c4cd1e 100755 --- a/bin/copay +++ b/bin/copay @@ -19,12 +19,16 @@ var main = function() { var args = commander.args; - copay._command(args[0], args.slice(1), function(err, result) { - if (err) - return console.log("" + err); - - console.log(JSON.stringify(result, null, 2)); - }); + try { + copay._command(args[0], args.slice(1), function(err, result) { + if (err) + return console.log("" + err); + + console.log(JSON.stringify(result, null, 2)); + }); + } catch (err) { + console.log("" + err); + } }; if (require.main === module) { From 4773b6577aae8bde40a122274e96ca4d7f80b16e Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Tue, 15 Apr 2014 09:19:07 -0300 Subject: [PATCH 2/9] add getArgTypes command Which allows you to see what arguments you need to pass to a function. --- bin/Copay.js | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/bin/Copay.js b/bin/Copay.js index 2dde15ddf..763e68626 100644 --- a/bin/Copay.js +++ b/bin/Copay.js @@ -84,6 +84,24 @@ Copay.prototype.getBalance.argTypes = ]; */ +Copay.prototype.getArgTypes = function getArgTypes(command, callback) { + var self = this; + checkArgs(arguments.callee.name, arguments); + + if (command[0] == '_' || typeof Copay.prototype[command] != 'function') + return callback(new Error('Invalid command')); + + var argTypes = Copay.prototype[command].argTypes; + + return callback(null, argTypes); +}; + +Copay.prototype.getArgTypes.argTypes = + [ + ['command', 'string'], + ['callback', 'function'] + ]; + Copay.prototype.getCommands = function getCommands(callback) { var self = this; checkArgs(arguments.callee.name, arguments); From dd2e8c585e175ee6f1b3024d802cf9f9a9b2589c Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Tue, 15 Apr 2014 10:50:54 -0300 Subject: [PATCH 3/9] move Copay.js -> API.js, and handle case of no command better * API is a more appropriate name for what this is. It is intended to be the interface used by external apps. * The case where you run "copay" with no command is now handled better. --- bin/Copay.js => API.js | 54 +++++++++++++++++++++--------------------- bin/copay | 10 ++++---- 2 files changed, 32 insertions(+), 32 deletions(-) rename bin/Copay.js => API.js (62%) diff --git a/bin/Copay.js b/API.js similarity index 62% rename from bin/Copay.js rename to API.js index 763e68626..e44129553 100644 --- a/bin/Copay.js +++ b/API.js @@ -1,12 +1,12 @@ var imports = require('soop').imports(); -var PublicKeyRing = imports.PublicKeyRing || require('../js/models/core/PublicKeyRing'); +var PublicKeyRing = imports.PublicKeyRing || require('./js/models/core/PublicKeyRing'); //var Wallet = imports.Wallet || require('../js/models/core/Wallet'); -var Copay = function(opts) { +var API = function(opts) { this._init(opts); }; -Copay.prototype._init = function(opts) { +API.prototype._init = function(opts) { var self = this; opts = opts ? opts : {}; @@ -26,14 +26,14 @@ Copay.prototype._init = function(opts) { //self.wallet = self.opts.wallet ? self.opts.wallet : new Wallet(); }; -Copay.prototype._command = function(command, args, callback) { +API.prototype._command = function(command, args, callback) { var self = this; - - if (command[0] == "_") + + if (!command || command[0] == "_") return callback(new Error('invalid command')); if (typeof self[command] == 'function') { - var f = Copay.prototype[command]; + var f = API.prototype[command]; if (f.argTypes[f.argTypes.length-1][1] == 'function') return self[command].apply(self, args.concat([callback])); else @@ -43,8 +43,8 @@ Copay.prototype._command = function(command, args, callback) { return callback(new Error('invalid command')); }; -Copay._checkArgTypes = function(command, args) { - var f = Copay.prototype[command]; +API._checkArgTypes = function(command, args) { + var f = API.prototype[command]; for (var i in args) { if (typeof args[i] != f.argTypes[i][1]) return false; @@ -53,63 +53,63 @@ Copay._checkArgTypes = function(command, args) { }; function checkArgs(name, args) { - if (!Copay._checkArgTypes(name, args)) + if (!API._checkArgTypes(name, args)) throw new Error('Invalid arguments'); } -Copay.prototype.echo = function echo(str, callback) { +API.prototype.echo = function echo(str, callback) { var self = this; checkArgs(arguments.callee.name, arguments); return callback(null, str); }; -Copay.prototype.echo.argTypes = +API.prototype.echo.argTypes = [ ['str', 'string'], ['callback', 'function'] ]; /* -Copay.prototype.getBalance = function(callback) { +API.prototype.getBalance = function(callback) { var self = this; checkArgs('getBalance', arguments); return callback(null, self.wallet.getBalance([])); }; -Copay.prototype.getBalance.argTypes = +API.prototype.getBalance.argTypes = [ ['callback', 'function'] ]; */ -Copay.prototype.getArgTypes = function getArgTypes(command, callback) { +API.prototype.getArgTypes = function getArgTypes(command, callback) { var self = this; checkArgs(arguments.callee.name, arguments); - if (command[0] == '_' || typeof Copay.prototype[command] != 'function') + if (command[0] == '_' || typeof API.prototype[command] != 'function') return callback(new Error('Invalid command')); - var argTypes = Copay.prototype[command].argTypes; + var argTypes = API.prototype[command].argTypes; return callback(null, argTypes); }; -Copay.prototype.getArgTypes.argTypes = +API.prototype.getArgTypes.argTypes = [ ['command', 'string'], ['callback', 'function'] ]; -Copay.prototype.getCommands = function getCommands(callback) { +API.prototype.getCommands = function getCommands(callback) { var self = this; checkArgs(arguments.callee.name, arguments); var fs = []; - for (var i in Copay.prototype) { - var f = Copay.prototype[i]; + for (var i in API.prototype) { + var f = API.prototype[i]; if (typeof f == 'function' && i[0] != "_") fs.push(i); }; @@ -117,30 +117,30 @@ Copay.prototype.getCommands = function getCommands(callback) { return callback(null, fs); }; -Copay.prototype.getCommands.argTypes = +API.prototype.getCommands.argTypes = [ ['callback', 'function'] ]; -Copay.prototype.getPublicKeyRingId = function getPublicKeyRingId(callback) { +API.prototype.getPublicKeyRingId = function getPublicKeyRingId(callback) { var self = this; checkArgs(arguments.callee.name, arguments); return callback(null, self.publicKeyRing.id); }; -Copay.prototype.getPublicKeyRingId.argTypes = +API.prototype.getPublicKeyRingId.argTypes = [ ['callback', 'function'] ]; -Copay.prototype.help = function help(callback) { +API.prototype.help = function help(callback) { this.getCommands.apply(this, arguments); }; -Copay.prototype.help.argTypes = +API.prototype.help.argTypes = [ ['callback', 'function'] ]; -module.exports = require('soop')(Copay); +module.exports = require('soop')(API); diff --git a/bin/copay b/bin/copay index 937c4cd1e..dced11f5f 100755 --- a/bin/copay +++ b/bin/copay @@ -1,26 +1,26 @@ #!/usr/bin/env node -var Copay = require('./Copay.js'); +var API = require('../API.js'); var commander = require('commander'); var main = function() { commander .version("0.0.1") - .option('-f, --file [file]', 'AES encrypted data file', 'copay.json.aes') + .option('-f, --file [file]', 'AES encrypted data file', 'api.json.aes') .option('-p, --pass [passphrase]', 'AES wallet passphrase') - .option('-c, --client', 'Issue command over RPC to copay daemon') + .option('-c, --client', 'Issue command over RPC to api daemon') .option('-d, --daemon', 'Run as daemon accepting RPC commands') .option('--rpcport [port]', 'RPC port [18332]', Number, 18332) .option('--rpcuser [user]', 'RPC user [user]', String, 'user') .option('--rpcpass [password]', 'RPC password [pass]', String, 'pass') .parse(process.argv); - var copay = new Copay(commander); + var api = new API(commander); var args = commander.args; try { - copay._command(args[0], args.slice(1), function(err, result) { + api._command(args[0], args.slice(1), function(err, result) { if (err) return console.log("" + err); From 5cc25e3e59e01f366e378aa5c6b00ff92fcd7de5 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Tue, 15 Apr 2014 12:07:04 -0300 Subject: [PATCH 4/9] added type coercion for command-line All inputs from the command are strings, but sometimes an argument needs to be something other than a string. This adds basic type coercion, so you can input objects and numbers as well. --- API.js | 61 ++++++++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 51 insertions(+), 10 deletions(-) diff --git a/API.js b/API.js index e44129553..43bdb0da0 100644 --- a/API.js +++ b/API.js @@ -26,12 +26,39 @@ API.prototype._init = function(opts) { //self.wallet = self.opts.wallet ? self.opts.wallet : new Wallet(); }; +API._coerceArgTypes = function(args, argTypes) { + for (var i in args) { + var arg = args[i]; + var argType = argTypes[i][1]; + if (typeof arg == 'string') { + switch (argType) { + case 'object': + args[i] = JSON.parse(arg); + break; + case 'number': + args[i] = Number(arg); + break; + } + } + } + + return args; +}; + API.prototype._command = function(command, args, callback) { var self = this; if (!command || command[0] == "_") return callback(new Error('invalid command')); + if (!API._checkArgTypes(command, args)) { + var argTypes = API.prototype[command].argTypes; + API._coerceArgTypes(args, argTypes) + if (!API._checkArgTypes(command, args)) { + throw new Error('Invalid arguments'); + } + } + if (typeof self[command] == 'function') { var f = API.prototype[command]; if (f.argTypes[f.argTypes.length-1][1] == 'function') @@ -52,14 +79,8 @@ API._checkArgTypes = function(command, args) { return true; }; -function checkArgs(name, args) { - if (!API._checkArgTypes(name, args)) - throw new Error('Invalid arguments'); -} - API.prototype.echo = function echo(str, callback) { var self = this; - checkArgs(arguments.callee.name, arguments); return callback(null, str); }; @@ -70,10 +91,33 @@ API.prototype.echo.argTypes = ['callback', 'function'] ]; +API.prototype.echoNumber = function echoNumber(num, callback) { + var self = this; + + return callback(null, num); +}; + +API.prototype.echoNumber.argTypes = + [ + ['num', 'number'], + ['callback', 'function'] + ]; + +API.prototype.echoObject = function echoNumber(obj, callback) { + var self = this; + + return callback(null, obj); +}; + +API.prototype.echoObject.argTypes = + [ + ['obj', 'object'], + ['callback', 'function'] + ]; + /* API.prototype.getBalance = function(callback) { var self = this; - checkArgs('getBalance', arguments); return callback(null, self.wallet.getBalance([])); }; @@ -86,7 +130,6 @@ API.prototype.getBalance.argTypes = API.prototype.getArgTypes = function getArgTypes(command, callback) { var self = this; - checkArgs(arguments.callee.name, arguments); if (command[0] == '_' || typeof API.prototype[command] != 'function') return callback(new Error('Invalid command')); @@ -104,7 +147,6 @@ API.prototype.getArgTypes.argTypes = API.prototype.getCommands = function getCommands(callback) { var self = this; - checkArgs(arguments.callee.name, arguments); var fs = []; @@ -124,7 +166,6 @@ API.prototype.getCommands.argTypes = API.prototype.getPublicKeyRingId = function getPublicKeyRingId(callback) { var self = this; - checkArgs(arguments.callee.name, arguments); return callback(null, self.publicKeyRing.id); }; From 9f39da9ff8b45be696ecffcc221877fef8620024 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Tue, 15 Apr 2014 12:21:24 -0300 Subject: [PATCH 5/9] check for present of callback in args correctly --- API.js | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/API.js b/API.js index 43bdb0da0..c56c66128 100644 --- a/API.js +++ b/API.js @@ -54,9 +54,8 @@ API.prototype._command = function(command, args, callback) { if (!API._checkArgTypes(command, args)) { var argTypes = API.prototype[command].argTypes; API._coerceArgTypes(args, argTypes) - if (!API._checkArgTypes(command, args)) { + if (!API._checkArgTypes(command, args)) throw new Error('Invalid arguments'); - } } if (typeof self[command] == 'function') { @@ -72,6 +71,14 @@ API.prototype._command = function(command, args, callback) { API._checkArgTypes = function(command, args) { var f = API.prototype[command]; + + if (f.argTypes.length != args.length) { + + //if the function doesn't have a callback + if (!(f.argTypes.length == args.length + 1 && f.argTypes[f.argTypes.length-1][1] == 'function')) + return false; + } + for (var i in args) { if (typeof args[i] != f.argTypes[i][1]) return false; From 01ca3763d813c76b0b7b535fe8795e1fafb45a86 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Tue, 15 Apr 2014 12:53:06 -0300 Subject: [PATCH 6/9] add some basic tests for the structure of the API --- test/test.API.js | 49 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 test/test.API.js diff --git a/test/test.API.js b/test/test.API.js new file mode 100644 index 000000000..5a334bc32 --- /dev/null +++ b/test/test.API.js @@ -0,0 +1,49 @@ +'use strict'; + +var chai = chai || require('chai'); +var should = chai.should(); +var API = API || require('../API'); + +describe('API', function() { + + it('should have a command called "echo"', function() { + var api = new API(); + should.exist(api.echo); + }); + + it('should have argTypes for every command', function() { + for (var i in API.prototype) { + var f = API.prototype[i]; + if (i[0] != '_' && typeof f == 'function') { + f.argTypes.length.should.be.greaterThan(0); + } + }; + }) + + it('should throw an error for all commands when called with wrong number of arguments', function() { + var api = new API(); + for (var i in API.prototype) { + var f = API.prototype[i]; + if (i[0] != '_' && typeof f == 'function') { + var a = new Array(); + for (var j = 0; j <= f.argTypes.length + 1; j++) { + a.push(0); + } + (function() { + api[i].apply(api, a); + }).should.throw(); + } + }; + }); + + it('should have a callback in the arguments on every command', function() { + for (var i in API.prototype) { + var f = API.prototype[i]; + if (i[0] != '_' && typeof f == 'function') { + f.argTypes[f.argTypes.length-1][0].should.equal('callback'); + f.argTypes[f.argTypes.length-1][1].should.equal('function'); + } + } + }); + +}); From c1ff96ab1bb0231200f90e395f0f126df6a6fcd6 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Tue, 15 Apr 2014 14:50:14 -0300 Subject: [PATCH 7/9] add basic integration with wallet ...and further tests and stuff --- API.js | 33 ++++++++++++++++++--------------- test/test.API.js | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+), 15 deletions(-) diff --git a/API.js b/API.js index c56c66128..46163885d 100644 --- a/API.js +++ b/API.js @@ -1,6 +1,4 @@ var imports = require('soop').imports(); -var PublicKeyRing = imports.PublicKeyRing || require('./js/models/core/PublicKeyRing'); -//var Wallet = imports.Wallet || require('../js/models/core/Wallet'); var API = function(opts) { this._init(opts); @@ -10,20 +8,25 @@ API.prototype._init = function(opts) { var self = this; opts = opts ? opts : {}; + self.opts = opts; - self.optsList = [ - 'publicKeyRing', - 'wallet' - ]; - - self.opts = {}; - for (var a in self.optsList) { - if (opts.hasOwnProperty(a)) - self.opts[a] = opts[a]; - } + var Wallet = require('soop').load('./js/models/core/Wallet', { + Storage: opts.Storage ? opts.Storage : require('./test/FakeStorage'), + Network: opts.Network ? opts.Network : require('./js/models/Network/WebRTC'), + Blockchain: opts.Blockchain ? opts.Blockchain : require('./js/models/Blockchain/Insight') + }); - self.publicKeyRing = self.opts.publicKeyRing ? self.opts.publicKeyRing : new PublicKeyRing(); - //self.wallet = self.opts.wallet ? self.opts.wallet : new Wallet(); + var config = { + wallet: { + requiredCopayers: opts.requiredCopayers ? opts.requiredCopayers : 3, + totalCopayers: opts.totalCopayers ? opts.totalCopayers : 5, + } + }; + + var walletConfig = opts.walletConfig ? opts.walletConfig : config; + var walletOpts = opts.walletOpts ? opts.walletOpts : {}; + + self.wallet = self.opts.wallet ? self.opts.wallet : Wallet.factory.create(walletConfig, walletOpts); }; API._coerceArgTypes = function(args, argTypes) { @@ -174,7 +177,7 @@ API.prototype.getCommands.argTypes = API.prototype.getPublicKeyRingId = function getPublicKeyRingId(callback) { var self = this; - return callback(null, self.publicKeyRing.id); + return callback(null, self.wallet.publicKeyRing.walletId); }; API.prototype.getPublicKeyRingId.argTypes = diff --git a/test/test.API.js b/test/test.API.js index 5a334bc32..02541570f 100644 --- a/test/test.API.js +++ b/test/test.API.js @@ -46,4 +46,39 @@ describe('API', function() { } }); + describe('#echo', function() { + it('should echo a string', function(done) { + var api = new API(); + var str = 'mystr'; + api.echo(str, function(err, result) { + result.should.equal(str); + done(); + }); + }); + }); + + describe('#echoNumber', function() { + it('should echo a number', function(done) { + var api = new API(); + var num = 500; + api.echo(num, function(err, result) { + result.should.equal(num); + (typeof result).should.equal('number'); + done(); + }); + }); + }); + + describe('#echoObject', function() { + it('should echo an object', function(done) { + var api = new API(); + var obj = {test:'test'}; + api.echo(obj, function(err, result) { + result.test.should.equal(obj.test); + (typeof result).should.equal('object'); + done(); + }); + }); + }); + }); From 34f30e88ea76ffc21492f8a5bbc005a2de1ee1e7 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Tue, 15 Apr 2014 15:12:37 -0300 Subject: [PATCH 8/9] convert from ? notation to more concise || notation --- API.js | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/API.js b/API.js index 46163885d..9e0cd8c35 100644 --- a/API.js +++ b/API.js @@ -7,26 +7,26 @@ var API = function(opts) { API.prototype._init = function(opts) { var self = this; - opts = opts ? opts : {}; + opts = opts || {}; self.opts = opts; var Wallet = require('soop').load('./js/models/core/Wallet', { - Storage: opts.Storage ? opts.Storage : require('./test/FakeStorage'), - Network: opts.Network ? opts.Network : require('./js/models/Network/WebRTC'), - Blockchain: opts.Blockchain ? opts.Blockchain : require('./js/models/Blockchain/Insight') + Storage: opts.Storage || require('./test/FakeStorage'), + Network: opts.Network || require('./js/models/Network/WebRTC'), + Blockchain: opts.Blockchain || require('./js/models/Blockchain/Insight') }); var config = { wallet: { - requiredCopayers: opts.requiredCopayers ? opts.requiredCopayers : 3, - totalCopayers: opts.totalCopayers ? opts.totalCopayers : 5, + requiredCopayers: opts.requiredCopayers || 3, + totalCopayers: opts.totalCopayers || 5, } }; - var walletConfig = opts.walletConfig ? opts.walletConfig : config; - var walletOpts = opts.walletOpts ? opts.walletOpts : {}; + var walletConfig = opts.walletConfig || config; + var walletOpts = opts.walletOpts || {}; - self.wallet = self.opts.wallet ? self.opts.wallet : Wallet.factory.create(walletConfig, walletOpts); + self.wallet = self.opts.wallet || Wallet.factory.create(walletConfig, walletOpts); }; API._coerceArgTypes = function(args, argTypes) { From 4241287887aad083741ae34af0f65225686c10ec Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Tue, 15 Apr 2014 16:53:23 -0300 Subject: [PATCH 9/9] use decorator --- API.js | 69 +++++++++++++++++++++++++----------------------- bin/copay | 25 +++++++++++++++--- test/test.API.js | 53 +++++++++++++++++++++++++++++++++++-- 3 files changed, 108 insertions(+), 39 deletions(-) diff --git a/API.js b/API.js index 9e0cd8c35..45f280848 100644 --- a/API.js +++ b/API.js @@ -58,15 +58,15 @@ API.prototype._command = function(command, args, callback) { var argTypes = API.prototype[command].argTypes; API._coerceArgTypes(args, argTypes) if (!API._checkArgTypes(command, args)) - throw new Error('Invalid arguments'); + throw new Error('invalid arguments'); } - if (typeof self[command] == 'function') { + if (typeof self["_cmd_" + command] == 'function') { var f = API.prototype[command]; if (f.argTypes[f.argTypes.length-1][1] == 'function') - return self[command].apply(self, args.concat([callback])); + return self["_cmd_" + command].apply(self, args.concat([callback])); else - return callback(null, self[command].apply(self, args)); + return callback(null, self["_cmd_" + command].apply(self, args)); }; return callback(new Error('invalid command')); @@ -89,41 +89,48 @@ API._checkArgTypes = function(command, args) { return true; }; -API.prototype.echo = function echo(str, callback) { +function decorate(command, argTypes) { + var d = function() { + API.prototype._command.call(this, command, Array.prototype.slice.call(arguments, 0)); + }; + + d.argTypes = argTypes; + + return d; +}; + +API.prototype._cmd_echo = function(str, callback) { var self = this; return callback(null, str); }; -API.prototype.echo.argTypes = - [ +API.prototype.echo = decorate('echo', [ ['str', 'string'], ['callback', 'function'] - ]; + ]); -API.prototype.echoNumber = function echoNumber(num, callback) { +API.prototype._cmd_echoNumber = function(num, callback) { var self = this; return callback(null, num); }; -API.prototype.echoNumber.argTypes = - [ +API.prototype.echoNumber = decorate('echoNumber', [ ['num', 'number'], ['callback', 'function'] - ]; + ]); -API.prototype.echoObject = function echoNumber(obj, callback) { +API.prototype._cmd_echoObject = function(obj, callback) { var self = this; return callback(null, obj); }; -API.prototype.echoObject.argTypes = - [ +API.prototype.echoObject = decorate('echoObject', [ ['obj', 'object'], ['callback', 'function'] - ]; + ]); /* API.prototype.getBalance = function(callback) { @@ -138,7 +145,7 @@ API.prototype.getBalance.argTypes = ]; */ -API.prototype.getArgTypes = function getArgTypes(command, callback) { +API.prototype._cmd_getArgTypes = function(command, callback) { var self = this; if (command[0] == '_' || typeof API.prototype[command] != 'function') @@ -149,13 +156,12 @@ API.prototype.getArgTypes = function getArgTypes(command, callback) { return callback(null, argTypes); }; -API.prototype.getArgTypes.argTypes = - [ +API.prototype.getArgTypes = decorate('getArgTypes', [ ['command', 'string'], ['callback', 'function'] - ]; + ]); -API.prototype.getCommands = function getCommands(callback) { +API.prototype._cmd_getCommands = function(callback) { var self = this; var fs = []; @@ -169,29 +175,26 @@ API.prototype.getCommands = function getCommands(callback) { return callback(null, fs); }; -API.prototype.getCommands.argTypes = - [ +API.prototype.getCommands = decorate('getCommands', [ ['callback', 'function'] - ]; + ]); -API.prototype.getPublicKeyRingId = function getPublicKeyRingId(callback) { +API.prototype._cmd_getPublicKeyRingId = function(callback) { var self = this; return callback(null, self.wallet.publicKeyRing.walletId); }; -API.prototype.getPublicKeyRingId.argTypes = - [ +API.prototype.getPublicKeyRingId = decorate('getPublicKeyRingId', [ ['callback', 'function'] - ]; + ]); -API.prototype.help = function help(callback) { - this.getCommands.apply(this, arguments); +API.prototype._cmd_help = function(callback) { + this._cmd_getCommands.apply(this, arguments); }; -API.prototype.help.argTypes = - [ +API.prototype.help = decorate('help', [ ['callback', 'function'] - ]; + ]); module.exports = require('soop')(API); diff --git a/bin/copay b/bin/copay index dced11f5f..2721da0e2 100755 --- a/bin/copay +++ b/bin/copay @@ -20,14 +20,31 @@ var main = function() { var args = commander.args; try { - api._command(args[0], args.slice(1), function(err, result) { + var command = args[0]; + var commandArgs = args.slice(1); + + if (command[0] == '_' || typeof api[command] != 'function') + throw new Error('invalid command'); + + api[command].apply(api, commandArgs.concat([function(err, result) { if (err) return console.log("" + err); console.log(JSON.stringify(result, null, 2)); - }); - } catch (err) { - console.log("" + err); + }])); + } catch(err) { + if (err.toString() == 'Error: invalid command') { + console.log("" + err); + } + else if (err.toString() == 'Error: invalid arguments') { + console.log("" + err); + console.log('Arguments for ' + command + ':') + api.getArgTypes(command, function(err, result) { + console.log(JSON.stringify(result, null, 2)); + }); + } + else + throw (err); } }; diff --git a/test/test.API.js b/test/test.API.js index 02541570f..5bf1edf28 100644 --- a/test/test.API.js +++ b/test/test.API.js @@ -61,7 +61,7 @@ describe('API', function() { it('should echo a number', function(done) { var api = new API(); var num = 500; - api.echo(num, function(err, result) { + api.echoNumber(num, function(err, result) { result.should.equal(num); (typeof result).should.equal('number'); done(); @@ -73,7 +73,7 @@ describe('API', function() { it('should echo an object', function(done) { var api = new API(); var obj = {test:'test'}; - api.echo(obj, function(err, result) { + api.echoObject(obj, function(err, result) { result.test.should.equal(obj.test); (typeof result).should.equal('object'); done(); @@ -81,4 +81,53 @@ describe('API', function() { }); }); + describe('#getArgTypes', function() { + it('should get the argTypes of echo', function(done) { + var api = new API(); + api.getArgTypes('echo', function(err, result) { + result[0][1].should.equal('string'); + done(); + }); + }); + }); + + describe('#getCommands', function() { + it('should get all the commands', function(done) { + var api = new API(); + var n = 0; + + for (var i in api) + if (i[0] != '_' && typeof api[i] == 'function') + n++; + + api.getCommands(function(err, result) { + result.length.should.equal(n); + done(); + }); + }); + }); + + describe('#getPublicKeyRingId', function() { + it('should get a public key ring ID', function(done) { + var api = new API(); + api.getPublicKeyRingId(function(err, result) { + result.length.should.be.greaterThan(5); + done(); + }); + }); + }); + + describe('#help', function() { + it('should call _cmd_getCommands', function(done) { + var api = new API(); + api._cmd_getCommands = function(callback) { + (typeof arguments[0]).should.equal('function'); + callback(null, ['item']); + } + api.help(function(err, result) { + result[0].should.equal('item'); + done(); + }); + }); + }); });