Merge pull request #70 from ryanxcharles/feature/command-line-3

Command Line Interface #3
This commit is contained in:
Manuel Aráoz 2014-04-16 21:19:32 -03:00
commit 4e4dd249a8
10 changed files with 295 additions and 53 deletions

22
API.js
View file

@ -10,23 +10,13 @@ API.prototype._init = function(opts) {
opts = opts || {};
self.opts = opts;
var Wallet = require('soop').load('./js/models/core/Wallet', {
var WalletFactory = require('soop').load('./js/models/core/WalletFactory', {
Storage: opts.Storage || require('./test/mocks/FakeStorage'),
Network: opts.Network || require('./js/models/network/WebRTC'),
Network: opts.Network || require('./js/models/network/Base'),
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);
this.walletFactory = new WalletFactory(opts);
};
API._coerceArgTypes = function(args, argTypes) {
@ -179,13 +169,13 @@ API.prototype.getCommands = decorate('getCommands', [
['callback', 'function']
]);
API.prototype._cmd_getPublicKeyRingId = function(callback) {
API.prototype._cmd_getWalletIds = function(callback) {
var self = this;
return callback(null, self.wallet.publicKeyRing.walletId);
return callback(null, self.walletFactory.getWalletIds());
};
API.prototype.getPublicKeyRingId = decorate('getPublicKeyRingId', [
API.prototype.getWalletIds = decorate('getWalletIds', [
['callback', 'function']
]);

View file

@ -18,11 +18,10 @@ var main = function() {
var api = new API(commander);
var args = commander.args;
var command = args[0];
var commandArgs = args.slice(1);
try {
var command = args[0];
var commandArgs = args.slice(1);
if (command[0] == '_' || typeof api[command] != 'function')
throw new Error('invalid command');

View file

@ -110,7 +110,6 @@ WalletFactory.prototype.create = function(opts) {
opts.totalCopayers = totalCopayers;
var w = new Wallet(opts);
w.store();
this.addWalletId(w.id);
return w;
};
@ -156,28 +155,11 @@ WalletFactory.prototype.openRemote = function(peedId) {
};
WalletFactory.prototype.getWalletIds = function() {
var ids = this.storage.getGlobal('walletIds');
return ids || [];
return this.storage.getWalletIds();
}
WalletFactory.prototype._delWalletId = function(walletId) {
var ids = this.getWalletIds();
var index = ids.indexOf(walletId);
if (index === -1) return;
ids.splice(index, 1); // removes walletId
this.storage.setGlobal('walletIds', ids);
};
WalletFactory.prototype.remove = function(walletId) {
WalletFactory._delWalletId(walletId);
// TODO remove wallet contents, not only the id (Wallet.remove?)
};
WalletFactory.prototype.addWalletId = function(walletId) {
var ids = this.getWalletIds();
if (ids.indexOf(walletId) !== -1) return;
ids.push(walletId);
this.storage.setGlobal('walletIds', ids);
// TODO remove wallet contents
};

View file

@ -17,6 +17,9 @@ Storage.prototype.set = function(walletId,v) {
Storage.prototype.remove = function(walletId, k) {
};
Storage.prototype.getWalletIds = function() {
};
// remove all values
Storage.prototype.clearAll = function() {
};

102
js/models/storage/File.js Normal file
View file

@ -0,0 +1,102 @@
'use strict';
var imports = require('soop').imports();
var fs = imports.fs || require('fs');
function Storage(opts) {
opts = opts || {};
this.data = {};
}
Storage.prototype.load = function(walletId, callback) {
fs.readFile(walletId, function(err, data) {
if (err) return callback(err);
try {
this.data[walletId] = JSON.parse(data);
} catch (err) {
if (callback)
return callback(err);
}
if (callback)
return callback(null);
});
};
Storage.prototype.save = function(walletId, callback) {
var data = JSON.stringify(this.data[walletId]);
//TODO: update to use a queue to ensure that saves are made sequentially
fs.writeFile(walletId, data, function(err) {
if (callback)
return callback(err);
});
};
Storage.prototype._read = function(k) {
var split = k.split('::');
var walletId = split[0];
var key = split[1];
return this.data[walletId][key];
};
Storage.prototype._write = function(k, v, callback) {
var split = k.split('::');
var walletId = split[0];
var key = split[1];
if (!this.data[walletId])
this.data[walletId] = {};
this.data[walletId][key] = v;
this.save(walletId, callback);
};
// get value by key
Storage.prototype.getGlobal = function(k) {
return this._read(k);
};
// set value for key
Storage.prototype.setGlobal = function(k, v, callback) {
this._write(k, v, callback);
};
// remove value for key
Storage.prototype.removeGlobal = function(k, callback) {
var split = k.split('::');
var walletId = split[0];
var key = split[1];
delete this.data[walletId][key];
this.save(walletId, callback);
};
Storage.prototype._key = function(walletId, k) {
return walletId + '::' + k;
};
// get value by key
Storage.prototype.get = function(walletId, k) {
return this.getGlobal(this._key(walletId, k));
};
// set value for key
Storage.prototype.set = function(walletId, k, v, callback) {
this.setGlobal(this._key(walletId,k), v, callback);
};
// remove value for key
Storage.prototype.remove = function(walletId, k, callback) {
this.removeGlobal(this._key(walletId, k), callback);
};
Storage.prototype.getWalletIds = function() {
return [];
};
// remove all values
Storage.prototype.clearAll = function(callback) {
this.data = {};
this.save(callback);
};
module.exports = require('soop')(Storage);

View file

@ -52,6 +52,21 @@ Storage.prototype.remove = function(walletId, k) {
this.removeGlobal(this._key(walletId,k));
};
Storage.prototype.getWalletIds = function() {
var walletIds = [];
for (var i = 0; i < localStorage.length; i++) {
var key = localStorage.key(i);
var split = key.split('::');
if (split.length == 2) {
var walletId = split[0];
walletIds.push(walletId);
}
}
return walletIds;
};
// remove all values
Storage.prototype.clearAll = function() {
localStorage.clear();

View file

@ -35,6 +35,7 @@
"uglifyify": "~1.2.3",
"soop": "~0.1.5",
"bitcore": "git://github.com/maraoz/bitcore.git#5e636f6b9c7f8e629b1a502025556e886c3b75e1",
"chai": "~1.9.1"
"chai": "~1.9.1",
"sinon": "~1.9.1"
}
}

View file

@ -23,4 +23,8 @@ FakeStorage.prototype.clear = function() {
delete this['storage'];
}
FakeStorage.prototype.getWalletIds = function() {
return [];
};
module.exports = require('soop')(FakeStorage);

View file

@ -4,11 +4,13 @@ var chai = chai || require('chai');
var should = chai.should();
var copay = copay || require('../copay');
var API = API || copay.API;
var Storage = Storage || require('../test/mocks/FakeStorage');
describe('API', function() {
it('should have a command called "echo"', function() {
var api = new API();
var api = new API({Storage: Storage});
should.exist(api.echo);
});
@ -22,7 +24,7 @@ describe('API', function() {
})
it('should throw an error for all commands when called with wrong number of arguments', function() {
var api = new API();
var api = new API({Storage: Storage});
for (var i in API.prototype) {
var f = API.prototype[i];
if (i[0] != '_' && typeof f == 'function') {
@ -49,7 +51,7 @@ describe('API', function() {
describe('#echo', function() {
it('should echo a string', function(done) {
var api = new API();
var api = new API({Storage: Storage});
var str = 'mystr';
api.echo(str, function(err, result) {
result.should.equal(str);
@ -60,7 +62,7 @@ describe('API', function() {
describe('#echoNumber', function() {
it('should echo a number', function(done) {
var api = new API();
var api = new API({Storage: Storage});
var num = 500;
api.echoNumber(num, function(err, result) {
result.should.equal(num);
@ -72,7 +74,7 @@ describe('API', function() {
describe('#echoObject', function() {
it('should echo an object', function(done) {
var api = new API();
var api = new API({Storage: Storage});
var obj = {test:'test'};
api.echoObject(obj, function(err, result) {
result.test.should.equal(obj.test);
@ -84,7 +86,7 @@ describe('API', function() {
describe('#getArgTypes', function() {
it('should get the argTypes of echo', function(done) {
var api = new API();
var api = new API({Storage: Storage});
api.getArgTypes('echo', function(err, result) {
result[0][1].should.equal('string');
done();
@ -94,7 +96,7 @@ describe('API', function() {
describe('#getCommands', function() {
it('should get all the commands', function(done) {
var api = new API();
var api = new API({Storage: Storage});
var n = 0;
for (var i in api)
@ -108,11 +110,11 @@ describe('API', function() {
});
});
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);
describe('#getWalletIds', function() {
it('should get the wallet ids', function(done) {
var api = new API({Storage: Storage});
api.getWalletIds(function(err, result) {
result.length.should.be.greaterThan(-1);
done();
});
});
@ -120,7 +122,7 @@ describe('API', function() {
describe('#help', function() {
it('should call _cmd_getCommands', function(done) {
var api = new API();
var api = new API({Storage: Storage});
api._cmd_getCommands = function(callback) {
(typeof arguments[0]).should.equal('function');
callback(null, ['item']);

144
test/test.storage.File.js Normal file
View file

@ -0,0 +1,144 @@
'use strict';
var chai = chai || require('chai');
var should = chai.should();
var Storage = Storage || require('../js/models/storage/File.js');
var sinon = sinon || require('sinon');
describe('Storage/File', function() {
it('should exist', function() {
should.exist(Storage);
});
describe('#load', function(done) {
it('should call fs.readFile', function(done) {
var fs = {}
fs.readFile = function(filename, callback) {
filename.should.equal('myfilename');
callback();
};
var Storage = require('soop').load('../js/models/storage/File.js', {fs: fs});
var storage = new Storage({password: 'password'});
storage.load('myfilename', function(err) {
done();
});
});
});
describe('#save', function(done) {
it('should call fs.writeFile', function(done) {
var fs = {}
fs.writeFile = function(filename, data, callback) {
filename.should.equal('myfilename');
callback();
};
var Storage = require('soop').load('../js/models/storage/File.js', {fs: fs});
var storage = new Storage({password: 'password'});
storage.save('myfilename', function(err) {
done();
});
});
});
describe('#_read', function() {
it('should return the value of a key', function() {
var storage = new Storage();
storage.data = {'walletId':{'test':'data'}};
storage._read('walletId::test').should.equal('data');
});
});
describe('#_write', function() {
it('should save the value of a key and then run save', function(done) {
var storage = new Storage();
storage.save = function(walletId, callback) {
storage.data[walletId]['key'].should.equal('value');
callback();
};
storage._write('walletId::key', 'value', function() {
done();
});
});
});
describe('#getGlobal', function() {
it('should call storage._read', function() {
var storage = new Storage();
storage.data = {'walletId':{'test':'test'}};
storage._read = sinon.spy();
storage.getGlobal('walletId::test');
storage._read.calledOnce.should.equal(true);
});
});
describe('#setGlobal', function() {
it('should store a global key', function(done) {
var storage = new Storage();
storage.save = function(walletId, callback) {
storage.data[walletId]['key'].should.equal('value');
callback();
};
storage.setGlobal('walletId::key', 'value', function() {
done();
});
});
});
describe('#removeGlobal', function() {
it('should remove a global key', function(done) {
var storage = new Storage();
storage.data = {'walletId':{'key':'value'}};
storage.save = function(walletId, callback) {
should.not.exist(storage.data[walletId]['key']);
callback();
};
storage.removeGlobal('walletId::key', function() {
done();
});
});
});
describe('#_key', function() {
it('should merge the wallet id and item key', function() {
var storage = new Storage();
storage._key('wallet', 'key').should.equal('wallet::key');
});
});
describe('#get', function() {
it('should call getGlobal with the correct key', function() {
var storage = new Storage();
storage.getGlobal = sinon.spy();
storage.get('wallet', 'key');
storage.getGlobal.calledOnce.should.equal(true);
storage.getGlobal.calledWith('wallet::key').should.equal(true);
});
});
describe('#set', function() {
it('should call setGlobal with the correct key', function() {
var storage = new Storage();
storage.setGlobal = sinon.spy();
storage.set('wallet', 'key');
storage.setGlobal.calledOnce.should.equal(true);
storage.setGlobal.calledWith('wallet::key').should.equal(true);
});
});
describe('#remove', function() {
it('should call removeGlobal with the correct key', function() {
var storage = new Storage();
storage.removeGlobal = sinon.spy();
storage.remove('wallet', 'key');
storage.removeGlobal.calledOnce.should.equal(true);
storage.removeGlobal.calledWith('wallet::key').should.equal(true);
});
});
describe('#clearAll', function() {
it('should set data to {}', function() {
});
});
});