From eb4c5924a3b74ff7980d833fff4fbe830526a147 Mon Sep 17 00:00:00 2001 From: Gabe Isman Date: Tue, 22 Mar 2016 11:09:54 -0700 Subject: [PATCH] Add getAll contract and tests, implementation for redis. --- .gitignore | 3 +- lib/backend.js | 82 +++++++++++++++++++++++++------------------- lib/redis-backend.js | 67 ++++++++++++++++++++++++------------ test/backendtests.js | 58 +++++++++++++++++++++++++++++++ test/runner.js | 17 +++++---- 5 files changed, 162 insertions(+), 65 deletions(-) create mode 100644 test/backendtests.js diff --git a/.gitignore b/.gitignore index 71e6f99..e053845 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ node_modules/* .DS_Store *~ -.idea/ \ No newline at end of file +.idea/ +.tags* diff --git a/lib/backend.js b/lib/backend.js index 914e3e0..3d75075 100644 --- a/lib/backend.js +++ b/lib/backend.js @@ -1,78 +1,88 @@ /** - Backend Interface. - - Implement this API for providing a backend for the acl module. + Backend Interface. + + Implement this API for providing a backend for the acl module. */ var contract = require('./contract'); var Backend = { - /** + /** Begins a transaction. */ begin : function(){ // returns a transaction object }, - + /** Ends a transaction (and executes it) */ end : function(transaction, cb){ - contract(arguments).params('object', 'function').end(); + contract(arguments).params('object', 'function').end(); // Execute transaction }, - + /** Cleans the whole storage. */ clean : function(cb){ contract(arguments).params('function').end(); }, - + /** Gets the contents at the bucket's key. */ get : function(bucket, key, cb){ - contract(arguments) - .params('string', 'string|number', 'function') - .end(); + contract(arguments) + .params('string', 'string|number', 'function') + .end(); }, - /** - Returns the union of the values in the given keys. - */ + /** + Gets the contents of the specified keys and returns them in the same order + passed. + */ + getAll : function(bucket, keys, cb){ + contract(arguments) + .params('string', 'array', 'function') + .end(); + }, + + /** + Returns the union of the values in the given keys. + */ union : function(bucket, keys, cb){ contract(arguments) - .params('string', 'array', 'function') - .end(); - }, - + .params('string', 'array', 'function') + .end(); + }, + /** - Adds values to a given key inside a bucket. - */ - add : function(transaction, bucket, key, values){ - contract(arguments) - .params('object', 'string', 'string|number','string|array|number') + Adds values to a given key inside a bucket. + */ + add : function(transaction, bucket, key, values){ + contract(arguments) + .params('object', 'string', 'string|number','string|array|number') .end(); - }, - + }, + /** Delete the given key(s) at the bucket */ del : function(transaction, bucket, keys){ - contract(arguments) - .params('object', 'string', 'string|array') - .end(); + contract(arguments) + .params('object', 'string', 'string|array') + .end(); }, - - /** - Removes values from a given key inside a bucket. - */ - remove : function(transaction, bucket, key, values){ - contract(arguments) - .params('object', 'string', 'string|number','string|array|number') + + /** + Removes values from a given key inside a bucket. + */ + remove : function(transaction, bucket, key, values){ + contract(arguments) + .params('object', 'string', 'string|number','string|array|number') .end(); - }, + }, } exports = module.exports = Backend; diff --git a/lib/redis-backend.js b/lib/redis-backend.js index d488911..666c0db 100644 --- a/lib/redis-backend.js +++ b/lib/redis-backend.js @@ -1,11 +1,12 @@ /** Redis Backend. - + Implementation of the storage backend using Redis */ "use strict"; var contract = require('./contract'); +var _ = require('lodash'); function noop(){}; @@ -15,14 +16,14 @@ function RedisBackend(redis, prefix){ } RedisBackend.prototype = { - - /** + + /** Begins a transaction */ begin : function(){ return this.redis.multi(); }, - + /** Ends a transaction (and executes it) */ @@ -30,7 +31,7 @@ RedisBackend.prototype = { contract(arguments).params('object', 'function').end(); transaction.exec(function(){cb()}); }, - + /** Cleans the whole storage. */ @@ -45,7 +46,7 @@ RedisBackend.prototype = { } }); }, - + /** Gets the contents at the bucket's key. */ @@ -55,10 +56,32 @@ RedisBackend.prototype = { .end(); key = this.bucketKey(bucket, key); - + this.redis.smembers(key, cb); }, - + + /** + Gets an array of the contents of the specified keys. + */ + getAll : function(bucket, keys, cb){ + contract(arguments) + .params('string', 'array', 'function') + .end(); + + var redisKeys = this.bucketKey(bucket, keys); + var batch = this.redis.batch(); + var self = this; + + redisKeys.map(function(key) { + batch.smembers(key, self.redis.print); + }); + + batch.exec(function(err, replies) { + var result = Array.isArray(replies) ? _.zipObject(keys, replies) : replies; + cb(err, result); + }); + }, + /** Returns the union of the values in the given keys. */ @@ -66,11 +89,11 @@ RedisBackend.prototype = { contract(arguments) .params('string', 'array', 'function') .end(); - + keys = this.bucketKey(bucket, keys); this.redis.sunion(keys, cb); }, - + /** Adds values to a given key inside a bucket. */ @@ -78,9 +101,9 @@ RedisBackend.prototype = { contract(arguments) .params('object', 'string', 'string|number','string|array|number') .end(); - + key = this.bucketKey(bucket, key); - + if (Array.isArray(values)){ values.forEach(function(value){ transaction.sadd(key, value); @@ -89,7 +112,7 @@ RedisBackend.prototype = { transaction.sadd(key, values); } }, - + /** Delete the given key(s) at the bucket */ @@ -97,18 +120,18 @@ RedisBackend.prototype = { contract(arguments) .params('object', 'string', 'string|array') .end(); - + var self = this; - + keys = Array.isArray(keys) ? keys : [keys] - + keys = keys.map(function(key){ return self.bucketKey(bucket, key); }); - + transaction.del(keys); }, - + /** Removes values from a given key inside a bucket. */ @@ -116,9 +139,9 @@ RedisBackend.prototype = { contract(arguments) .params('object', 'string', 'string|number','string|array|number') .end(); - + key = this.bucketKey(bucket, key); - + if (Array.isArray(values)){ values.forEach(function(value){ transaction.srem(key, value); @@ -127,11 +150,11 @@ RedisBackend.prototype = { transaction.srem(key, values); } }, - + // // Private methods // - + bucketKey : function(bucket, keys){ var self = this; if(Array.isArray(keys)){ diff --git a/test/backendtests.js b/test/backendtests.js new file mode 100644 index 0000000..8498215 --- /dev/null +++ b/test/backendtests.js @@ -0,0 +1,58 @@ +var chai = require('chai'); +var assert = chai.assert; +var expect = chai.expect; +var _ = require('lodash'); + +var testData = { + key1: ["1", "2", "3"], + key2: ["4", "5", "6"], + key3: ["7", "8", "9"] +}; +var bucket = 'test-bucket'; + +exports.getAll = function() { + describe('getAll', function() { + before(function(done) { + var backend = this.backend; + if (!backend.getAll) { + this.skip(); + } + + backend.clean(function() { + var transaction = backend.begin(); + Object.keys(testData).forEach(function(key) { + backend.add(transaction, bucket, key, testData[key]); + }); + backend.end(transaction, done); + }); + }); + + after(function(done) { + this.backend.clean(done); + }); + + it('should respond with an appropriate map', function(done) { + this.backend.getAll(bucket, Object.keys(testData), function(err, result) { + expect(err).to.be.null; + expect(result).to.be.eql(testData); + done(); + }); + }); + + it('should get only the specified keys', function(done) { + this.backend.getAll(bucket, ['key1'], function(err, result) { + expect(err).to.be.null; + expect(result).to.be.eql(_.pick(testData, 'key1')); + done(); + }); + }); + + it('should be order-independent', function(done) { + this.backend.getAll(bucket, Object.keys(testData).reverse(), function(err, result) { + expect(err).to.be.null; + expect(result).to.be.eql(testData); + done(); + }); + }); + }); +}; diff --git a/test/runner.js b/test/runner.js index 7eb6f2a..3092900 100644 --- a/test/runner.js +++ b/test/runner.js @@ -1,5 +1,6 @@ var Acl = require('../') , tests = require('./tests') + , backendTests = require('./backendtests'); describe('MongoDB - Default', function () { before(function (done) { @@ -43,22 +44,22 @@ describe('Redis', function () { password: null } , Redis = require('redis') - - + + var redis = Redis.createClient(options.port, options.host, {no_ready_check: true} ) function start(){ self.backend = new Acl.redisBackend(redis) done() } - + if (options.password) { redis.auth(options.password, start) } else { start() } }) - + run() }) @@ -68,7 +69,7 @@ describe('Memory', function () { var self = this self.backend = new Acl.memoryBackend() }) - + run() }) @@ -76,4 +77,8 @@ function run() { Object.keys(tests).forEach(function (test) { tests[test]() }) -} \ No newline at end of file + + Object.keys(backendTests).forEach(function (test) { + backendTests[test]() + }); +}