Merge pull request #42 from ryanxcharles/feature/command-line-2
WIP: More command-line stuff
This commit is contained in:
commit
3a91581f31
4 changed files with 364 additions and 128 deletions
200
API.js
Normal file
200
API.js
Normal file
|
|
@ -0,0 +1,200 @@
|
||||||
|
var imports = require('soop').imports();
|
||||||
|
|
||||||
|
var API = function(opts) {
|
||||||
|
this._init(opts);
|
||||||
|
};
|
||||||
|
|
||||||
|
API.prototype._init = function(opts) {
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
opts = opts || {};
|
||||||
|
self.opts = opts;
|
||||||
|
|
||||||
|
var Wallet = require('soop').load('./js/models/core/Wallet', {
|
||||||
|
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 || 3,
|
||||||
|
totalCopayers: opts.totalCopayers || 5,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var walletConfig = opts.walletConfig || config;
|
||||||
|
var walletOpts = opts.walletOpts || {};
|
||||||
|
|
||||||
|
self.wallet = self.opts.wallet || Wallet.factory.create(walletConfig, walletOpts);
|
||||||
|
};
|
||||||
|
|
||||||
|
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["_cmd_" + command] == 'function') {
|
||||||
|
var f = API.prototype[command];
|
||||||
|
if (f.argTypes[f.argTypes.length-1][1] == 'function')
|
||||||
|
return self["_cmd_" + command].apply(self, args.concat([callback]));
|
||||||
|
else
|
||||||
|
return callback(null, self["_cmd_" + command].apply(self, args));
|
||||||
|
};
|
||||||
|
|
||||||
|
return callback(new Error('invalid command'));
|
||||||
|
};
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
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 = decorate('echo', [
|
||||||
|
['str', 'string'],
|
||||||
|
['callback', 'function']
|
||||||
|
]);
|
||||||
|
|
||||||
|
API.prototype._cmd_echoNumber = function(num, callback) {
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
return callback(null, num);
|
||||||
|
};
|
||||||
|
|
||||||
|
API.prototype.echoNumber = decorate('echoNumber', [
|
||||||
|
['num', 'number'],
|
||||||
|
['callback', 'function']
|
||||||
|
]);
|
||||||
|
|
||||||
|
API.prototype._cmd_echoObject = function(obj, callback) {
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
return callback(null, obj);
|
||||||
|
};
|
||||||
|
|
||||||
|
API.prototype.echoObject = decorate('echoObject', [
|
||||||
|
['obj', 'object'],
|
||||||
|
['callback', 'function']
|
||||||
|
]);
|
||||||
|
|
||||||
|
/*
|
||||||
|
API.prototype.getBalance = function(callback) {
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
return callback(null, self.wallet.getBalance([]));
|
||||||
|
};
|
||||||
|
|
||||||
|
API.prototype.getBalance.argTypes =
|
||||||
|
[
|
||||||
|
['callback', 'function']
|
||||||
|
];
|
||||||
|
*/
|
||||||
|
|
||||||
|
API.prototype._cmd_getArgTypes = function(command, callback) {
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
if (command[0] == '_' || typeof API.prototype[command] != 'function')
|
||||||
|
return callback(new Error('Invalid command'));
|
||||||
|
|
||||||
|
var argTypes = API.prototype[command].argTypes;
|
||||||
|
|
||||||
|
return callback(null, argTypes);
|
||||||
|
};
|
||||||
|
|
||||||
|
API.prototype.getArgTypes = decorate('getArgTypes', [
|
||||||
|
['command', 'string'],
|
||||||
|
['callback', 'function']
|
||||||
|
]);
|
||||||
|
|
||||||
|
API.prototype._cmd_getCommands = function(callback) {
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
var fs = [];
|
||||||
|
|
||||||
|
for (var i in API.prototype) {
|
||||||
|
var f = API.prototype[i];
|
||||||
|
if (typeof f == 'function' && i[0] != "_")
|
||||||
|
fs.push(i);
|
||||||
|
};
|
||||||
|
|
||||||
|
return callback(null, fs);
|
||||||
|
};
|
||||||
|
|
||||||
|
API.prototype.getCommands = decorate('getCommands', [
|
||||||
|
['callback', 'function']
|
||||||
|
]);
|
||||||
|
|
||||||
|
API.prototype._cmd_getPublicKeyRingId = function(callback) {
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
return callback(null, self.wallet.publicKeyRing.walletId);
|
||||||
|
};
|
||||||
|
|
||||||
|
API.prototype.getPublicKeyRingId = decorate('getPublicKeyRingId', [
|
||||||
|
['callback', 'function']
|
||||||
|
]);
|
||||||
|
|
||||||
|
API.prototype._cmd_help = function(callback) {
|
||||||
|
this._cmd_getCommands.apply(this, arguments);
|
||||||
|
};
|
||||||
|
|
||||||
|
API.prototype.help = decorate('help', [
|
||||||
|
['callback', 'function']
|
||||||
|
]);
|
||||||
|
|
||||||
|
module.exports = require('soop')(API);
|
||||||
118
bin/Copay.js
118
bin/Copay.js
|
|
@ -1,118 +0,0 @@
|
||||||
var imports = require('soop').imports();
|
|
||||||
var PublicKeyRing = imports.PublicKeyRing || require('../js/models/core/PublicKeyRing');
|
|
||||||
var Wallet = imports.Wallet || require('../js/models/core/Wallet');
|
|
||||||
|
|
||||||
var Copay = function(opts) {
|
|
||||||
this._init(opts);
|
|
||||||
};
|
|
||||||
|
|
||||||
Copay.prototype._init = function(opts) {
|
|
||||||
var self = this;
|
|
||||||
|
|
||||||
opts = opts ? opts : {};
|
|
||||||
|
|
||||||
self.optsList = [
|
|
||||||
'publicKeyRing',
|
|
||||||
'wallet'
|
|
||||||
];
|
|
||||||
|
|
||||||
self.opts = {};
|
|
||||||
for (var a in self.optsList) {
|
|
||||||
if (opts.hasOwnProperty(a))
|
|
||||||
self.opts[a] = opts[a];
|
|
||||||
}
|
|
||||||
|
|
||||||
self.publicKeyRing = self.opts.publicKeyRing ? self.opts.publicKeyRing : new PublicKeyRing();
|
|
||||||
self.wallet = self.opts.wallet ? self.opts.wallet : new Wallet();
|
|
||||||
};
|
|
||||||
|
|
||||||
Copay.prototype._command = function(command, args, callback) {
|
|
||||||
var self = this;
|
|
||||||
|
|
||||||
if (command[0] == "_")
|
|
||||||
return callback(new Error('invalid command'));
|
|
||||||
|
|
||||||
if (typeof self[command] == 'function') {
|
|
||||||
var f = Copay.prototype[command];
|
|
||||||
if (f.argTypes[f.argTypes.length-1][1] == 'function')
|
|
||||||
return self[command].apply(self, args.concat([callback]));
|
|
||||||
else
|
|
||||||
return callback(null, self[command].apply(self, args));
|
|
||||||
};
|
|
||||||
|
|
||||||
return callback(new Error('invalid command'));
|
|
||||||
};
|
|
||||||
|
|
||||||
Copay._checkArgTypes = function(command, args) {
|
|
||||||
var f = Copay.prototype[command];
|
|
||||||
for (var i in args) {
|
|
||||||
if (typeof args[i] != f.argTypes[i][1])
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
|
|
||||||
function checkArgs(name, args) {
|
|
||||||
if (!Copay._checkArgTypes(name, args))
|
|
||||||
throw new Error('Invalid arguments');
|
|
||||||
}
|
|
||||||
|
|
||||||
Copay.prototype.echo = function(str, callback) {
|
|
||||||
var self = this;
|
|
||||||
checkArgs('echo', arguments);
|
|
||||||
|
|
||||||
return callback(null, str);
|
|
||||||
};
|
|
||||||
|
|
||||||
Copay.prototype.echo.argTypes =
|
|
||||||
[
|
|
||||||
['str', 'string'],
|
|
||||||
['callback', 'function']
|
|
||||||
];
|
|
||||||
|
|
||||||
Copay.prototype.getBalance = function(callback) {
|
|
||||||
var self = this;
|
|
||||||
checkArgs('getBalance', arguments);
|
|
||||||
|
|
||||||
return callback(null, self.wallet.getBalance([]));
|
|
||||||
};
|
|
||||||
|
|
||||||
Copay.prototype.getBalance.argTypes =
|
|
||||||
[
|
|
||||||
['callback', 'function']
|
|
||||||
];
|
|
||||||
|
|
||||||
Copay.prototype.getCommands = function(callback) {
|
|
||||||
var self = this;
|
|
||||||
checkArgs('getCommands', arguments);
|
|
||||||
|
|
||||||
var fs = [];
|
|
||||||
|
|
||||||
for (var i in Copay.prototype) {
|
|
||||||
var f = Copay.prototype[i];
|
|
||||||
if (typeof f == 'function' && i[0] != "_")
|
|
||||||
fs.push(i);
|
|
||||||
};
|
|
||||||
|
|
||||||
return callback(null, fs);
|
|
||||||
};
|
|
||||||
|
|
||||||
Copay.prototype.getCommands.argTypes =
|
|
||||||
[
|
|
||||||
['callback', 'function']
|
|
||||||
];
|
|
||||||
|
|
||||||
Copay.prototype.getPublicKeyRingId = function(callback) {
|
|
||||||
var self = this;
|
|
||||||
checkArgs('getPublicKeyRingId', arguments);
|
|
||||||
|
|
||||||
return callback(null, self.publicKeyRing.id);
|
|
||||||
};
|
|
||||||
|
|
||||||
Copay.prototype.getPublicKeyRingId.argTypes =
|
|
||||||
[
|
|
||||||
['callback', 'function']
|
|
||||||
];
|
|
||||||
|
|
||||||
|
|
||||||
module.exports = require('soop')(Copay);
|
|
||||||
41
bin/copay
41
bin/copay
|
|
@ -1,30 +1,51 @@
|
||||||
#!/usr/bin/env node
|
#!/usr/bin/env node
|
||||||
var Copay = require('./Copay.js');
|
var API = require('../API.js');
|
||||||
var commander = require('commander');
|
var commander = require('commander');
|
||||||
|
|
||||||
var main = function() {
|
var main = function() {
|
||||||
|
|
||||||
commander
|
commander
|
||||||
.version("0.0.1")
|
.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('-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('-d, --daemon', 'Run as daemon accepting RPC commands')
|
||||||
.option('--rpcport [port]', 'RPC port [18332]', Number, 18332)
|
.option('--rpcport [port]', 'RPC port [18332]', Number, 18332)
|
||||||
.option('--rpcuser [user]', 'RPC user [user]', String, 'user')
|
.option('--rpcuser [user]', 'RPC user [user]', String, 'user')
|
||||||
.option('--rpcpass [password]', 'RPC password [pass]', String, 'pass')
|
.option('--rpcpass [password]', 'RPC password [pass]', String, 'pass')
|
||||||
.parse(process.argv);
|
.parse(process.argv);
|
||||||
|
|
||||||
var copay = new Copay(commander);
|
var api = new API(commander);
|
||||||
|
|
||||||
var args = commander.args;
|
var args = commander.args;
|
||||||
|
|
||||||
copay._command(args[0], args.slice(1), function(err, result) {
|
try {
|
||||||
if (err)
|
var command = args[0];
|
||||||
return console.log("" + err);
|
var commandArgs = args.slice(1);
|
||||||
|
|
||||||
console.log(JSON.stringify(result, null, 2));
|
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) {
|
||||||
|
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);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if (require.main === module) {
|
if (require.main === module) {
|
||||||
|
|
|
||||||
133
test/test.API.js
Normal file
133
test/test.API.js
Normal file
|
|
@ -0,0 +1,133 @@
|
||||||
|
'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');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
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.echoNumber(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.echoObject(obj, function(err, result) {
|
||||||
|
result.test.should.equal(obj.test);
|
||||||
|
(typeof result).should.equal('object');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
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();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
Loading…
Add table
Add a link
Reference in a new issue