diff --git a/packages/composer-connector-hlfv1/lib/hlfconnectionmanager.js b/packages/composer-connector-hlfv1/lib/hlfconnectionmanager.js index e3e6bbd6f1..9ce7b194a1 100644 --- a/packages/composer-connector-hlfv1/lib/hlfconnectionmanager.js +++ b/packages/composer-connector-hlfv1/lib/hlfconnectionmanager.js @@ -438,7 +438,8 @@ class HLFConnectionManager extends ConnectionManager { }); LOG.exit(method); } catch (error) { - let newError = `Failed to import identity. ${error}`; + let newError = new Error(`Failed to import identity. ${error}`); + newError.cause = error; LOG.error(method, newError); throw newError; } diff --git a/packages/composer-wallet-filesystem/lib/filesystemwallet.js b/packages/composer-wallet-filesystem/lib/filesystemwallet.js index c05b276bcf..0e9bf66d2e 100644 --- a/packages/composer-wallet-filesystem/lib/filesystemwallet.js +++ b/packages/composer-wallet-filesystem/lib/filesystemwallet.js @@ -17,11 +17,10 @@ const Wallet = require('composer-common').Wallet; const nodefs = require('fs'); const path = require('path'); -const util = require('util'); const composerUtil = require('composer-common').Util; const mkdirp = require('mkdirp'); const IdCard = require('composer-common').IdCard; -const rimraf = util.promisify(require('rimraf')); +const rimraf = require('rimraf'); /** * String based key-val store, A 'client-data' directory is created under the storePath option (or ~/.composer) * @private @@ -48,12 +47,19 @@ class FileSystemWallet extends Wallet{ * @return {boolean} true if directory, false otherwise */ _isDirectory(name){ - return this._stat(this._path(name)) - .then(status=>{ - return status.isDirectory(); + + return new Promise((resolve,reject)=>{ + this.fs.stat(this._path(name),(err,status)=>{ + if (err){ + resolve(false); + }else { + resolve(status.isDirectory()); + } + }); }); } + /** * @param {Object} options Configuration options * @param {Object} options.storePath The root directory where this wallet can put things @@ -62,18 +68,14 @@ class FileSystemWallet extends Wallet{ */ constructor(options){ super(); + let root = options.storePath || path.resolve(composerUtil.homeDirectory(),'.composer'); - this.storePath = path.join(root,options.namePrefix); + let prefix = options.namePrefix || ''; + this.storePath = path.join(root,prefix); this.fs = options.fs || nodefs; mkdirp.sync(this.storePath,{fs:this.fs}); - this._readFile = util.promisify(this.fs.readFile); - this._writeFile = util.promisify(this.fs.writeFile); - this._stat = util.promisify(this.fs.stat); - this._readdir = util.promisify(this.fs.readdir); - this._unlink = util.promisify(this.fs.unlink); - this.rimrafOptions = Object.assign({}, this.fs); this.rimrafOptions.disableGlob = true; } @@ -86,34 +88,40 @@ class FileSystemWallet extends Wallet{ * error. */ listNames() { - return this._readdir(this.storePath) - .then((result)=>{ - return result; - }); + return new Promise((resolve,reject)=>{ + try { + resolve(this.fs.readdirSync(this.storePath)); + } catch (err){ + reject(err); + } + }); } /** - * Check to see if the named credentials are in - * the wallet. + * Check to see if the named keys is in the wallet. + * * @abstract * @param {string} name The name of the credentials. * @return {Promise} A promise that is resolved with - * a boolean; true if the named credentials are in the + * a boolean; true if the named key is in the * wallet, false otherwise. */ contains(name) { if (!name) { return Promise.reject(new Error('Name must be specified')); } - return this._stat(this._path(name)) - .then(()=>{ - return true; - }) - .catch((error)=>{ - return false; - }); + + return new Promise((resolve,reject)=>{ + this.fs.stat(this._path(name),(err)=>{ + if (err){ + resolve(false); + }else { + resolve(true); + } + }); + }); } @@ -124,28 +132,23 @@ class FileSystemWallet extends Wallet{ * @return {Promise} A promise that is resolved with * the named credentials, or rejected with an error. */ - get(name) { + async get(name) { if (!name) { return Promise.reject(new Error('Name must be specified')); } - return this._isDirectory(this._path(name)) - .then((dir)=>{ - if(dir){ - return IdCard.fromDirectory(this._path(name), this.fs) - .then((card)=>{ - return card.toArchive({ type: 'nodebuffer' }); - }); - } else { - return this._readFile(this._path(name),'utf8') - .then((result)=>{ - if (result.startsWith('BASE64')){ - return Buffer.from(result.replace('BASE64::',''),'base64'); - } - return result; - }); + + let dir = await this._isDirectory(this._path(name)); + if(dir){ + let card = await IdCard.fromDirectory(this._path(name), this.fs); + return card.toArchive({ type: 'nodebuffer' }); + } else { + let result = this.fs.readFileSync(this._path(name),'utf8'); + if (result.startsWith('BASE64')){ + return Buffer.from(result.replace('BASE64::',''),'base64'); } + return result; + } - }); } /** @@ -166,17 +169,31 @@ class FileSystemWallet extends Wallet{ return card.toDirectory(this._path(name)); } else if (value instanceof Buffer){ - // base 64 encode the buffer and write it as a string. - return this._writeFile(this._path(name),'BASE64::'+value.toString('base64')); + return new Promise((resolve,reject)=>{ + this.fs.writeFile(this._path(name),'BASE64::'+value.toString('base64'),(err)=>{ + if (err){ + reject(err); + } else { + resolve(); + } + }); + }); + } else if (value instanceof String || typeof value === 'string'){ - return this._writeFile(this._path(name),value); + return new Promise((resolve,reject)=>{ + this.fs.writeFile(this._path(name),value,(err)=>{ + if (err){ + reject(err); + } else { + resolve(); + } + }); + }); } else { return Promise.reject(new Error('Unkown type being stored')); } } - - /** * Remove existing credentials from the wallet. * @abstract @@ -184,28 +201,25 @@ class FileSystemWallet extends Wallet{ * @return {Promise} A promise that is resolved when * complete, or rejected with an error. */ - remove(name) { + async remove(name) { if (!name) { return Promise.reject(new Error('Name must be specified')); } - return this.contains(name).then( - (result)=>{ - if (result){ - return this._isDirectory(name) - .then((dir)=>{ - if(dir){ - return rimraf(this._path(name),this.rimrafOptions); - }else { - return this._unlink(this._path(name)); - } - }).then(()=>{return true;}); - } else { - return false; - } - } - ); + let result = await this.contains(name); + if (result){ + let dir = await this._isDirectory(name); + + if(dir){ + rimraf.sync(this._path(name),this.rimrafOptions); + }else { + this.fs.unlinkSync(this._path(name)); + } + return true; + } else { + return false; + } } /** diff --git a/packages/composer-wallet-filesystem/test/config.js b/packages/composer-wallet-filesystem/test/config.js index 77caefaa13..51174362b6 100644 --- a/packages/composer-wallet-filesystem/test/config.js +++ b/packages/composer-wallet-filesystem/test/config.js @@ -22,12 +22,12 @@ const path= require('path'); module.exports.getStore = require('../index.js').getStore; module.exports.wrongConfigs = [ { c: null, text: 'Cannot read property' }, - { c: {}, text: 'Path must be a string' }, - { c: { storePath : '/nothere' }, text: 'Path'} + { c: { storePath : {} }, text: 'Path must be a string' }, + { c: { storePath : '/nothere' }, text: 'permission denied'} ]; module.exports.correctConfigs=[ - { c: null, text: 'Need configuration' }, - { c: { storePath : '/tmp' }, text: 'custom location'} + { c: {}, text: 'Default Locations' }, + { c: { storePath : '/tmp/filestemwallet' }, text: 'custom location'} ]; module.exports.clean=async ()=>{ await rimraf('/tmp/filestemwallet'); diff --git a/packages/composer-wallet-filesystem/test/wallet.js b/packages/composer-wallet-filesystem/test/wallet.js index 6548097c2b..28cd729675 100644 --- a/packages/composer-wallet-filesystem/test/wallet.js +++ b/packages/composer-wallet-filesystem/test/wallet.js @@ -20,6 +20,7 @@ const expect = chai.expect; chai.use(require('chai-as-promised')); chai.use(require('chai-things')); const sinon = require('sinon'); +const fs = require('fs'); const CLOUD_CONFIG = require('./config'); const cloneDeep = require('lodash').cloneDeep; @@ -32,9 +33,13 @@ describe('Composer wallet implementation', function () { describe('Wrong Config settings', function () { CLOUD_CONFIG.wrongConfigs.forEach((cfg) => { - it('should fail to create with faulty config', function () { + it(`should fail to create with faulty config: \"${cfg.text}\"`, function () { (function () { - CLOUD_CONFIG.getStore(cfg.c); + try { + CLOUD_CONFIG.getStore(cfg.c); + } catch (err){ + throw err; + } }).should.throw(Error, cfg.text); }); @@ -51,7 +56,7 @@ describe('Composer wallet implementation', function () { let wallet; beforeEach(async () => { sandbox = sinon.sandbox.create(); - let config = cfg; + let config = cfg.c; config.namePrefix = 'testing'; await CLOUD_CONFIG.clean(); @@ -64,6 +69,10 @@ describe('Composer wallet implementation', function () { }); describe('#listNames', async function () { + it('should be rejected if an error', async function () { + sandbox.stub(fs,'readdirSync').throws(new Error('Error')); + return wallet.listNames().should.be.rejectedWith(/Error/); + }); it('should return empty list for nothing present', async function () { let result = await wallet.listNames(); return expect(result).to.be.an('array').that.is.empty; @@ -73,6 +82,7 @@ describe('Composer wallet implementation', function () { await wallet.put('Batman-Reloaded', 'It\'s not who I am underneath, but what I do that defines me'); let result = await wallet.listNames(); + let expected = ['Batman-Original','Batman-Reloaded']; expect(result).to.be.an('array'); expect(result.length).to.equal(2); @@ -189,6 +199,18 @@ describe('Composer wallet implementation', function () { let Umbrella = class Umbrella { }; return wallet.put('ThePenguin', new Umbrella()).should.be.rejectedWith('Unkown type being stored'); }); + + it('should return error if unable to write to the filesystem (for string values)', async function(){ + sandbox.stub(fs,'writeFile').callsArgWith(2,new Error('Alfred says no')); + return wallet.put('Batman','I only work in black and sometimes very, very dark grey.').should.be.rejectedWith('Alfred says no'); + }); + + it('should return error if unable to write to the filesystem (for buffer values)', async function () { + sandbox.stub(fs,'writeFile').callsArgWith(2,new Error('Alfred says no')); + // Creates a Buffer containing [0x1, 0x2, 0x3]. + const buffer = Buffer.from([1, 2, 3]); + return wallet.put('Batman', buffer).should.be.rejectedWith('Alfred says no'); + }); }); });