From 8e390ea64e873022b38816d816e3a409396ace63 Mon Sep 17 00:00:00 2001 From: stromboul Date: Tue, 1 Aug 2017 11:19:06 -0400 Subject: [PATCH] - Re-applied Kfardanesh modification for Multi-directorty support on most recent fork of master. - Added additional tests - Slight modifications in the functionality of the Getall and Keys functions to correctly support a different directory supplied as parameter --- lib/storage.js | 99 +++++++++++++++++++++++++++++++++++++------ lib/utils.js | 47 +++++++++++++++----- tests/storage.spec.js | 87 +++++++++++++++++++++++++++++++++++++ tests/utils.spec.js | 44 ++++++++++++++++++- 4 files changed, 250 insertions(+), 27 deletions(-) diff --git a/lib/storage.js b/lib/storage.js index 8d996a7..5302702 100644 --- a/lib/storage.js +++ b/lib/storage.js @@ -38,6 +38,46 @@ const RWlock = require('rwlock'); const utils = require('./utils'); const lock = new RWlock(); +/** + * @summary Set data path + * @function + * @public + * + * @description + * Pass the directory you want to call the functions from. + * If no directory is passed, returns to default 'storage' + * + * @param {String} directory - directory + * + * @example + * const storage = require('electron-json-storage'); + * + * storage.setDataPath('foo'); + */ +exports.setDataPath = function(dir = ""){ + utils.setUserDataPath(dir); +}; + +/** + * @summary Get data path + * @function + * @public + * + * @description + * Returns the current directory being used. + * + * @param {String} directory - directory + * + * @example + * const storage = require('electron-json-storage'); + * + * let dataPath = storage.getDataPath(); + * console.log(dataPath); + */ +exports.getDataPath = function(){ + return utils.getUserDataPath(); +}; + /** * @summary Read user data * @function @@ -64,9 +104,13 @@ const lock = new RWlock(); * console.log(data); * }); */ -exports.get = function(key, callback) { +exports.get = function(key, directory, callback) { + if(arguments.length === 2){ + callback = directory; + directory = ""; + } async.waterfall([ - async.asyncify(_.partial(utils.getFileName, key)), + async.asyncify(_.partial(utils.getFileName, key, directory)), function(fileName, callback) { fs.readFile(fileName, { encoding: 'utf8' @@ -122,7 +166,6 @@ exports.getMany = function(keys, callback) { if (error) { return callback(error); } - return callback(null, _.set(reducer, key, data)); }); }, callback); @@ -136,6 +179,7 @@ exports.getMany = function(keys, callback) { * @description * This function returns an empty object if there is no data to be read. * + * @param {Function} directory - optional parameter, to get data in a different folder than the current user data * @param {Function} callback - callback (error, data) * * @example @@ -147,13 +191,17 @@ exports.getMany = function(keys, callback) { * console.log(data); * }); */ -exports.getAll = function(callback) { +exports.getAll = function(directory, callback) { + if(arguments.length === 1){ + callback = directory; + directory = ""; + } async.waterfall([ - exports.keys, + _.partial(exports.keys, directory), function(keys, callback) { async.reduce(keys, {}, function(reducer, key, callback) { async.waterfall([ - _.partial(exports.get, key), + _.partial(exports.get, key, directory), function(contents, callback) { return callback(null, _.set(reducer, key, contents)); } @@ -251,6 +299,7 @@ exports.has = function(key, callback) { * @function * @public * + * @param {Function} directory - optional parameter, to get keys of a different folder than the current user data * @param {Function} callback - callback (error, keys) * * @example @@ -264,18 +313,29 @@ exports.has = function(key, callback) { * } * }); */ -exports.keys = function(callback) { +exports.keys = function(directory, callback) { + + if(arguments.length === 1){ + callback = directory; + directory = ""; + } async.waterfall([ - async.asyncify(utils.getUserDataPath), - function(userDataPath, callback) { - mkdirp(userDataPath, function(error) { + function (done) { + if (directory !== "") { + return done(null, directory); + } else { + return done(null, utils.getUserDataPath()); + } + }, + function (userDataPath, callback) { + mkdirp(userDataPath, function (error) { return callback(error, userDataPath); }); }, fs.readdir, function(keys, callback) { callback(null, _.map(_.reject(keys, function(key) { - return _.includes([ '.DS_Store' ], key); + return _.includes([ '.DS_Store' ], key) || path.extname(key) !== '.json'; }), function(key) { return path.basename(decodeURIComponent(key), '.json'); })); @@ -310,7 +370,7 @@ exports.remove = function(key, callback) { }; /** - * @summary Clear all stored data + * @summary Clear all stored data in current user data path * @function * @public * @@ -323,8 +383,19 @@ exports.remove = function(key, callback) { * if (error) throw error; * }); */ -exports.clear = function(callback) { - const userData = utils.getUserDataPath(); +exports.clear = function(directory, callback) { + if(arguments.length === 1){ + callback = directory; + directory = ""; + } + + let userData = ""; + if (directory !== "") { + userData = directory; + } else { + userData = utils.getUserDataPath(); + } + const jsonFiles = path.join(userData, '*.json'); rimraf(jsonFiles, callback); }; diff --git a/lib/utils.js b/lib/utils.js index b6b365d..1334e54 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -27,6 +27,31 @@ const _ = require('lodash'); const path = require('path'); const electron = require('electron'); +const app = electron.app || electron.remote.app; + +const defaultBasePath = path.join(app.getPath('userData'), 'storage'); +let currentDirectory = defaultBasePath; + +/** + * @summary Set default user data directory path + * @function + * @public + * + * @example + * utils.getUserDataPath('newDirectory'); + */ +exports.setUserDataPath = function (directory = "") { + if (directory === "") { + currentDirectory = defaultBasePath; + } else { + if (path.isAbsolute(directory)) { + currentDirectory = path.normalize(directory); + } else { + throw new Error('Not an absolute directory'); + } + } + +}; /** * @summary Get user data directory path @@ -35,18 +60,13 @@ const electron = require('electron'); * * @returns {Strings} user data path * + * @example * let userDataPath = utils.getUserDataPath(); * console.log(userDataPath); */ - exports.getUserDataPath = function() { - const app = electron.app || (electron.remote && electron.remote.app); - - if (app) { - return path.join(app.getPath('userData'), 'storage'); - } else { - return '/tmp/storage'; - } - }; +exports.getUserDataPath = function() { + return currentDirectory; +}; /** * @summary Get storage file name for a key @@ -60,7 +80,7 @@ const electron = require('electron'); * let fileName = utils.getFileName('foo'); * console.log(fileName); */ -exports.getFileName = function(key) { +exports.getFileName = function(key, directory = "") { if (!key) { throw new Error('Missing key'); } @@ -78,5 +98,10 @@ exports.getFileName = function(key) { // See: https://en.wikipedia.org/wiki/Filename#Reserved%5Fcharacters%5Fand%5Fwords const escapedFileName = encodeURIComponent(keyFileName); - return path.join(exports.getUserDataPath(), escapedFileName); + if (directory === "") { + return path.join(exports.getUserDataPath(), escapedFileName); + } else { + return path.join(directory, escapedFileName); + } + }; diff --git a/tests/storage.spec.js b/tests/storage.spec.js index 2f2d5fe..19eef5a 100644 --- a/tests/storage.spec.js +++ b/tests/storage.spec.js @@ -290,6 +290,27 @@ describe('Electron JSON Storage', function() { describe('.getAll()', function() { + const newpath = path.join(utils.getUserDataPath(), "newfolder"); + + beforeEach(function(done) { + async.waterfall( [ + async.asyncify(_.partial(utils.setUserDataPath, newpath)), + function (err,done) { + storage.clear("", done); + }, + async.asyncify(_.partial(utils.setUserDataPath,"")), + function (err,done) { + storage.clear("", done); + }, + ], done); + }); + + afterEach(function (done) { + async.waterfall( [ + async.asyncify(_.partial(utils.setUserDataPath)) + ], done); + }); + describe('given the user data path does not exist', function() { beforeEach(function(done) { @@ -348,6 +369,64 @@ describe('Electron JSON Storage', function() { }); + describe('given many stored keys in multiple directory', function() { + + beforeEach(function(done) { + async.parallel([ + _.partial(storage.set, 'foo', { name: 'foo' }), + _.partial(storage.set, 'bar', { name: 'bar' }), + ], done); + }); + + it('should return only the keys stored in the current user directory', function(done) { + + async.waterfall([ + async.asyncify(_.partial(utils.setUserDataPath, newpath)), + function(err, done) { + storage.set('baz', { name: 'baz' }, done); + }, + function (done) { + storage.getAll(function (error, data) { + m.chai.expect(error).to.not.exist; + m.chai.expect(data).to.deep.equal({ + baz: { name: 'baz' } + }); + done(); + }); + } + ], done); + }); + }); + + describe('given many stored keys in multiple directory', function() { + + beforeEach(function(done) { + async.parallel([ + _.partial(storage.set, 'foo', { name: 'foo' }), + _.partial(storage.set, 'bar', { name: 'bar' }), + ], done); + }); + + it('should return only the keys stored in the specified directory', function(done) { + + async.waterfall([ + async.asyncify(_.partial(utils.setUserDataPath, newpath)), + function(err, done) { + storage.set('baz', { name: 'baz' }, done); + }, + async.asyncify(_.partial(utils.setUserDataPath)), + function (err, done) { + storage.getAll(newpath, function (error, data) { + m.chai.expect(error).to.not.exist; + m.chai.expect(data).to.deep.equal({ + baz: { name: 'baz' } + }); + done(); + }); + } + ], done); + }); + }); }); describe('.set()', function() { @@ -579,6 +658,14 @@ describe('Electron JSON Storage', function() { describe('.keys()', function() { + beforeEach(function () { + utils.setUserDataPath(); + }); + + afterEach(function () { + utils.setUserDataPath(); + }); + describe('given a file name with colons', function() { beforeEach(function(done) { diff --git a/tests/utils.spec.js b/tests/utils.spec.js index 7838640..2993e37 100644 --- a/tests/utils.spec.js +++ b/tests/utils.spec.js @@ -27,6 +27,7 @@ const m = require('mochainon'); const path = require('path'); const utils = require('../lib/utils'); +const electron = require('electron'); describe('Utils', function() { @@ -38,10 +39,43 @@ describe('Utils', function() { m.chai.expect(path.isAbsolute(utils.getUserDataPath())).to.be.true; }); + it('should equal the dirname of the path returned by getFileName()', function() { + const fileName = utils.getFileName('foo'); + const userDataPath = utils.getUserDataPath(); + m.chai.expect(path.dirname(fileName)).to.equal(userDataPath); + }); + + }); + + describe('.setUserDataPath()', function() { + + beforeEach(function() { + utils.setUserDataPath(); + }); + + it('should reset to default path with default argument', function() { + const app = electron.app || electron.remote.app; + utils.setUserDataPath(); + m.chai.expect(utils.getUserDataPath()).to.equal(path.join(app.getPath('userData'), 'storage')); + }); + + it('should change the path used correctly', function() { + const newpath = path.join(utils.getUserDataPath(), "foo/bar"); + utils.setUserDataPath(newpath); + m.chai.expect(utils.getUserDataPath()).to.equal(newpath); + }); + + it('should throw if path is not absolute', function() { + m.chai.expect(function() { + utils.setUserDataPath("testpath/storage"); + }).to.throw('Not an absolute directory'); + }); + it('should equal the dirname of the path returned by getFileName()', function() { + const newpath = path.join(utils.getUserDataPath(), "foo/bar"); + utils.setUserDataPath(newpath); const fileName = utils.getFileName('foo'); - const userDataPath = utils.getUserDataPath(); - m.chai.expect(path.dirname(fileName)).to.equal(userDataPath); + m.chai.expect(path.dirname(fileName)).to.equal(newpath); }); }); @@ -91,6 +125,12 @@ describe('Utils', function() { m.chai.expect(path.basename(fileName)).to.equal('foo%3Fbar%3Abaz.json'); }); + it('should work with a different directory than the user', function() { + const newpath = path.join(utils.getUserDataPath(), "newpath"); + const fileName = utils.getFileName('foo?bar:baz', newpath); + m.chai.expect(path.dirname(fileName)).to.equal(newpath); + }); + }); });