redis-dataloader
Advanced tools
Comparing version 0.3.0 to 0.4.1
@@ -1,3 +0,1 @@ | ||
'use strict'; | ||
const gulp = require('gulp'); | ||
@@ -4,0 +2,0 @@ const mocha = require('gulp-mocha'); |
267
index.js
@@ -1,150 +0,165 @@ | ||
'use strict'; | ||
const _ = require('lodash'); | ||
const Q = require('q'); | ||
const Promise = require('bluebird'); | ||
const DataLoader = require('dataloader'); | ||
const stringify = require('json-stable-stringify'); | ||
module.exports = fig => { | ||
const redis = fig.redis; | ||
const redis = fig.redis; | ||
const parse = (resp, opt) => Q.Promise((resolve, reject) => { | ||
try { | ||
if(resp === '' || resp === null) { | ||
resolve(resp); | ||
} | ||
else if(opt.deserialize) { | ||
resolve(opt.deserialize(resp)); | ||
} | ||
else { | ||
resolve(JSON.parse(resp)); | ||
} | ||
const parse = (resp, opt) => | ||
new Promise((resolve, reject) => { | ||
try { | ||
if (resp === '' || resp === null) { | ||
resolve(resp); | ||
} else if (opt.deserialize) { | ||
resolve(opt.deserialize(resp)); | ||
} else { | ||
resolve(JSON.parse(resp)); | ||
} | ||
catch(err) { | ||
reject(err); | ||
} | ||
} catch (err) { | ||
reject(err); | ||
} | ||
}); | ||
const toString = (val, opt) => { | ||
if(val === null) { | ||
return Q(''); | ||
} | ||
else if(opt.serialize) { | ||
return Q(opt.serialize(val)); | ||
} | ||
else if(_.isObject(val)) { | ||
return Q(JSON.stringify(val)); | ||
} | ||
else { | ||
return Q.reject(new Error('Must be Object or Null')); | ||
} | ||
}; | ||
const toString = (val, opt) => { | ||
if (val === null) { | ||
return Promise.resolve(''); | ||
} else if (opt.serialize) { | ||
return Promise.resolve(opt.serialize(val)); | ||
} else if (_.isObject(val)) { | ||
return Promise.resolve(JSON.stringify(val)); | ||
} else { | ||
return Promise.reject(new Error('Must be Object or Null')); | ||
} | ||
}; | ||
const makeKey = (keySpace, key) => `${keySpace}:${key}`; | ||
const makeKey = (keySpace, key, cacheKeyFn) => | ||
`${keySpace}:${cacheKeyFn(key)}`; | ||
const rSetAndGet = (keySpace, key, rawVal, opt) => toString(rawVal, opt) | ||
.then(val => Q.Promise((resolve, reject) => { | ||
const fullKey = makeKey(keySpace, key); | ||
const multi = redis.multi(); | ||
multi.set(fullKey, val); | ||
if(opt.expire) { | ||
const rSetAndGet = (keySpace, key, rawVal, opt) => | ||
toString(rawVal, opt).then( | ||
val => | ||
new Promise((resolve, reject) => { | ||
const fullKey = makeKey(keySpace, key, opt.cacheKeyFn); | ||
const multi = redis.multi(); | ||
multi.set(fullKey, val); | ||
if (opt.expire) { | ||
multi.expire(fullKey, opt.expire); | ||
} | ||
multi.get(fullKey); | ||
multi.exec((err, replies) => err ? | ||
reject(err) : parse(_.last(replies), opt).then(resolve) | ||
); | ||
})); | ||
} | ||
multi.get(fullKey); | ||
multi.exec( | ||
(err, replies) => | ||
err ? reject(err) : parse(_.last(replies), opt).then(resolve) | ||
); | ||
}) | ||
); | ||
const rGet = (keySpace, key, opt) => Q.Promise( | ||
(resolve, reject) => redis.get( | ||
makeKey(keySpace, key), | ||
(err, result) => err ? reject(err) : parse(result, opt).then(resolve) | ||
) | ||
const rGet = (keySpace, key, opt) => | ||
new Promise((resolve, reject) => | ||
redis.get( | ||
makeKey(keySpace, key, opt.cacheKeyFn), | ||
(err, result) => (err ? reject(err) : parse(result, opt).then(resolve)) | ||
) | ||
); | ||
const rMGet = (keySpace, keys, opt) => Q.Promise( | ||
(resolve, reject) => redis.mget( | ||
_.map(keys, k => makeKey(keySpace, k)), | ||
(err, results) => err ? | ||
reject(err) : | ||
Q.all(_.map(results, r => parse(r, opt))).then(resolve) | ||
) | ||
const rMGet = (keySpace, keys, opt) => | ||
new Promise((resolve, reject) => | ||
redis.mget( | ||
_.map(keys, k => makeKey(keySpace, k, opt.cacheKeyFn)), | ||
(err, results) => | ||
err | ||
? reject(err) | ||
: Promise.map(results, r => parse(r, opt)).then(resolve) | ||
) | ||
); | ||
const rDel = (keySpace, key) => Q.Promise((resolve, reject) => redis.del( | ||
makeKey(keySpace, key), (err, resp) => err ? reject(err) : resolve(resp) | ||
)); | ||
const rDel = (keySpace, key, opt) => | ||
new Promise((resolve, reject) => | ||
redis.del( | ||
makeKey(keySpace, key, opt.cacheKeyFn), | ||
(err, resp) => (err ? reject(err) : resolve(resp)) | ||
) | ||
); | ||
return class RedisDataLoader { | ||
constructor(ks, userLoader, opt) { | ||
const customOptions = ['expire', 'serialize', 'deserialize']; | ||
this.opt = _.pick(opt, customOptions) || {}; | ||
this.keySpace = ks; | ||
this.loader = new DataLoader( | ||
keys => rMGet(this.keySpace, keys, this.opt) | ||
.then(results => Q.all(_.map( | ||
results, | ||
(v, i) => { | ||
if(v === '') { | ||
return Q(null); | ||
} | ||
else if(v === null) { | ||
return userLoader.load(keys[i]) | ||
.then(resp => rSetAndGet( | ||
this.keySpace, keys[i], resp, this.opt | ||
)) | ||
.then(r => r === '' ? null : r); | ||
} | ||
else { | ||
return Q(v); | ||
} | ||
} | ||
))), | ||
_.omit(opt, customOptions) | ||
); | ||
} | ||
return class RedisDataLoader { | ||
constructor(ks, userLoader, opt) { | ||
const customOptions = [ | ||
'expire', | ||
'serialize', | ||
'deserialize', | ||
'cacheKeyFn', | ||
]; | ||
this.opt = _.pick(opt, customOptions) || {}; | ||
load(key) { | ||
return key ? | ||
Q(this.loader.load(key)) : | ||
Q.reject(new TypeError('key parameter is required')); | ||
} | ||
this.opt.cacheKeyFn = | ||
this.opt.cacheKeyFn || (k => (_.isObject(k) ? stringify(k) : k)); | ||
loadMany(keys) { | ||
return keys ? | ||
Q(this.loader.loadMany(keys)) : | ||
Q.reject(new TypeError('keys parameter is required')); | ||
} | ||
this.keySpace = ks; | ||
this.loader = new DataLoader( | ||
keys => | ||
rMGet( | ||
this.keySpace, | ||
_.map(keys, this.opt.cacheKeyFn), | ||
this.opt | ||
).then(results => | ||
Promise.map(results, (v, i) => { | ||
if (v === '') { | ||
return Promise.resolve(null); | ||
} else if (v === null) { | ||
return userLoader | ||
.load(keys[i]) | ||
.then(resp => | ||
rSetAndGet(this.keySpace, keys[i], resp, this.opt) | ||
) | ||
.then(r => (r === '' ? null : r)); | ||
} else { | ||
return Promise.resolve(v); | ||
} | ||
}) | ||
), | ||
_.chain(opt) | ||
.omit(customOptions) | ||
.extend({ cacheKeyFn: this.opt.cacheKeyFn }) | ||
.value() | ||
); | ||
} | ||
prime(key, val) { | ||
if(!key) { | ||
return Q.reject(new TypeError('key parameter is required')); | ||
} | ||
else if(val === undefined) { | ||
return Q.reject(new TypeError('value parameter is required')); | ||
} | ||
else { | ||
return rSetAndGet(this.keySpace, key, val, this.opt) | ||
.then(r => { | ||
this.loader.clear(key) | ||
.prime(key, r === '' ? null : r); | ||
}); | ||
} | ||
} | ||
load(key) { | ||
return key | ||
? Promise.resolve(this.loader.load(key)) | ||
: Promise.reject(new TypeError('key parameter is required')); | ||
} | ||
clear(key) { | ||
return key ? | ||
rDel(this.keySpace, key).then(() => this.loader.clear(key)) : | ||
Q.reject(new TypeError('key parameter is required')); | ||
} | ||
loadMany(keys) { | ||
return keys | ||
? Promise.resolve(this.loader.loadMany(keys)) | ||
: Promise.reject(new TypeError('keys parameter is required')); | ||
} | ||
clearAllLocal() { | ||
return Q(this.loader.clearAll()); | ||
} | ||
prime(key, val) { | ||
if (!key) { | ||
return Promise.reject(new TypeError('key parameter is required')); | ||
} else if (val === undefined) { | ||
return Promise.reject(new TypeError('value parameter is required')); | ||
} else { | ||
return rSetAndGet(this.keySpace, key, val, this.opt).then(r => { | ||
this.loader.clear(key).prime(key, r === '' ? null : r); | ||
}); | ||
} | ||
} | ||
clearLocal(key) { | ||
return Q(this.loader.clear(key)); | ||
} | ||
}; | ||
clear(key) { | ||
return key | ||
? rDel(this.keySpace, key, this.opt).then(() => this.loader.clear(key)) | ||
: Promise.reject(new TypeError('key parameter is required')); | ||
} | ||
clearAllLocal() { | ||
return Promise.resolve(this.loader.clearAll()); | ||
} | ||
clearLocal(key) { | ||
return Promise.resolve(this.loader.clear(key)); | ||
} | ||
}; | ||
}; | ||
{ | ||
"name": "redis-dataloader", | ||
"version": "0.3.0", | ||
"version": "0.4.1", | ||
"description": "DataLoader Using Redis as a Cache", | ||
@@ -27,8 +27,10 @@ "main": "index.js", | ||
"dependencies": { | ||
"bluebird": "^3.5.0", | ||
"dataloader": "^1.2.0", | ||
"lodash": "^4.17.2", | ||
"q": "^1.4.1" | ||
"json-stable-stringify": "^1.0.1", | ||
"lodash": "^4.17.2" | ||
}, | ||
"devDependencies": { | ||
"chai": "^3.5.0", | ||
"chai-as-promised": "^7.1.1", | ||
"gulp": "^3.9.1", | ||
@@ -35,0 +37,0 @@ "gulp-mocha": "^3.0.1", |
@@ -93,2 +93,3 @@ # Redis Dataloader | ||
By default, the "cacheKeyFn" will serialize objects and arrays using [json-stable-stringify](https://github.com/substack/json-stable-stringify) and allow all other values to pass through unchanged. | ||
@@ -95,0 +96,0 @@ ### Caching |
532
test.js
@@ -1,6 +0,6 @@ | ||
'use strict'; | ||
const _ = require('lodash'); | ||
const Q = require('q'); | ||
const expect = require('chai').expect; | ||
const Promise = require('bluebird'); | ||
const chai = require('chai'); | ||
chai.use(require('chai-as-promised')); | ||
const { expect } = chai; | ||
const sinon = require('sinon'); | ||
@@ -12,286 +12,320 @@ const redis = require('redis').createClient(); | ||
describe('redis-dataloader', () => { | ||
beforeEach(done => { | ||
const rDel = key => Q.Promise((resolve, reject) => redis.del( | ||
key, (err, resp) => err ? reject(err) : resolve(resp) | ||
)); | ||
beforeEach(() => { | ||
const rDel = key => | ||
new Promise((resolve, reject) => | ||
redis.del(key, (err, resp) => (err ? reject(err) : resolve(resp))) | ||
); | ||
this.rSet = (k, v) => Q.Promise((resolve, reject) => redis.set( | ||
k, v, (err, resp) => err ? reject(err) : resolve(resp) | ||
)); | ||
this.rSet = (k, v) => | ||
new Promise((resolve, reject) => | ||
redis.set(k, v, (err, resp) => (err ? reject(err) : resolve(resp))) | ||
); | ||
this.keySpace = 'key-space'; | ||
this.data = { | ||
json: { foo: 'bar' }, | ||
'null': null | ||
}; | ||
this.rGet = k => | ||
new Promise((resolve, reject) => { | ||
redis.get(k, (err, resp) => (err ? rejecte(err) : resolve(resp))); | ||
}); | ||
this.stubs = {}; | ||
this.keySpace = 'key-space'; | ||
this.data = { | ||
json: { foo: 'bar' }, | ||
null: null, | ||
}; | ||
this.loadFn = sinon.stub(); | ||
this.stubs = {}; | ||
_.each(this.data, (v, k) => { | ||
this.loadFn.withArgs(k).returns(Q(v)); | ||
}); | ||
this.loadFn = sinon.stub(); | ||
this.userLoader = () => new DataLoader( | ||
keys => Q.all(_.map(keys, this.loadFn)), | ||
{ cache: false } | ||
); | ||
_.each(this.data, (v, k) => { | ||
this.loadFn.withArgs(k).returns(Promise.resolve(v)); | ||
}); | ||
Q.all(_.map(_.keys(this.data), k => rDel(`${this.keySpace}:${k}`))) | ||
.then(() => { | ||
this.loader = new RedisDataLoader(this.keySpace, this.userLoader()); | ||
this.loadFn | ||
.withArgs(sinon.match({ a: 1, b: 2 })) | ||
.returns(Promise.resolve({ bar: 'baz' })); | ||
this.noCacheLoader = new RedisDataLoader( | ||
this.keySpace, | ||
this.userLoader(), | ||
{ cache: false } | ||
); | ||
this.loadFn | ||
.withArgs(sinon.match([1, 2])) | ||
.returns(Promise.resolve({ ball: 'bat' })); | ||
done(); | ||
}).done(); | ||
this.userLoader = () => | ||
new DataLoader(keys => Promise.map(keys, this.loadFn), { | ||
cache: false, | ||
}); | ||
return Promise.map( | ||
_.keys(this.data).concat(['{"a":1,"b":2}', '[1,2]']), | ||
k => rDel(`${this.keySpace}:${k}`) | ||
).then(() => { | ||
this.loader = new RedisDataLoader(this.keySpace, this.userLoader()); | ||
this.noCacheLoader = new RedisDataLoader( | ||
this.keySpace, | ||
this.userLoader(), | ||
{ cache: false } | ||
); | ||
}); | ||
}); | ||
afterEach(() => _.each(this.stubs, s => s.restore())); | ||
afterEach(() => { | ||
_.each(this.stubs, s => s.restore()); | ||
}); | ||
describe('load', () => { | ||
it('should load json value', done => { | ||
this.loader.load('json').then(data => { | ||
expect(data).to.deep.equal(this.data.json); | ||
done(); | ||
}).done(); | ||
}); | ||
describe('load', () => { | ||
it('should load json value', () => | ||
this.loader.load('json').then(data => { | ||
expect(data).to.deep.equal(this.data.json); | ||
})); | ||
it('should require key', done => { | ||
this.loader.load().catch(err => { | ||
expect(err).to.be.instanceof(TypeError); | ||
done(); | ||
}).done(); | ||
}); | ||
it('should allow for object key', () => | ||
this.loader | ||
.load({ a: 1, b: 2 }) | ||
.then(data => { | ||
expect(data).to.deep.equal({ bar: 'baz' }); | ||
return this.rGet(`${this.keySpace}:{"a":1,"b":2}`); | ||
}) | ||
.then(data => { | ||
expect(JSON.parse(data)).to.deep.equal({ bar: 'baz' }); | ||
})); | ||
it('should use local cache on second load', done => { | ||
this.stubs.redisMGet = sinon.stub(redis, 'mget', (keys, cb) => { | ||
cb(null, [JSON.stringify(this.data.json)]); | ||
}); | ||
it('should ignore key order on object key', () => | ||
this.loader | ||
.load({ b: 2, a: 1 }) | ||
.then(data => { | ||
expect(data).to.deep.equal({ bar: 'baz' }); | ||
return this.rGet(`${this.keySpace}:{"a":1,"b":2}`); | ||
}) | ||
.then(data => { | ||
expect(JSON.parse(data)).to.deep.equal({ bar: 'baz' }); | ||
})); | ||
this.loader.load('json') | ||
.then(data => { | ||
expect(this.loadFn.callCount).to.equal(0); | ||
expect(this.stubs.redisMGet.callCount).to.equal(1); | ||
return this.loader.load('json'); | ||
}) | ||
.then(data => { | ||
expect(this.loadFn.callCount).to.equal(0); | ||
expect(this.stubs.redisMGet.callCount).to.equal(1); | ||
done(); | ||
}).done(); | ||
}); | ||
it('should handle key that is array', () => | ||
this.loader | ||
.load([1, 2]) | ||
.then(data => { | ||
expect(data).to.deep.equal({ ball: 'bat' }); | ||
return this.rGet(`${this.keySpace}:[1,2]`); | ||
}) | ||
.then(data => { | ||
expect(JSON.parse(data)).to.deep.equal({ ball: 'bat' }); | ||
})); | ||
it('should not use in memory cache if option is passed', done => { | ||
this.stubs.redisMGet = sinon.stub(redis, 'mget', (keys, cb) => { | ||
cb(null, [JSON.stringify(this.data.json)]); | ||
}); | ||
it('should require key', () => | ||
expect(this.loader.load()).to.be.rejectedWith(TypeError)); | ||
this.noCacheLoader.load('json') | ||
.then(data => { | ||
expect(this.loadFn.callCount).to.equal(0); | ||
expect(this.stubs.redisMGet.callCount).to.equal(1); | ||
return this.noCacheLoader.load('json'); | ||
}) | ||
.then(data => { | ||
expect(this.loadFn.callCount).to.equal(0); | ||
expect(this.stubs.redisMGet.callCount).to.equal(2); | ||
done(); | ||
}).done(); | ||
}); | ||
it('should use local cache on second load', () => { | ||
this.stubs.redisMGet = sinon.stub(redis, 'mget', (keys, cb) => { | ||
cb(null, [JSON.stringify(this.data.json)]); | ||
}); | ||
it('should load null values', done => { | ||
this.loader.load('null') | ||
.then(data => { | ||
expect(data).to.be.null; | ||
return this.loader.load('null'); | ||
}) | ||
.then(data => { | ||
expect(data).to.be.null; | ||
done(); | ||
}).done(); | ||
return this.loader | ||
.load('json') | ||
.then(data => { | ||
expect(this.loadFn.callCount).to.equal(0); | ||
expect(this.stubs.redisMGet.callCount).to.equal(1); | ||
return this.loader.load('json'); | ||
}) | ||
.then(data => { | ||
expect(this.loadFn.callCount).to.equal(0); | ||
expect(this.stubs.redisMGet.callCount).to.equal(1); | ||
}); | ||
}); | ||
it('should handle redis cacheing of null values', done => { | ||
this.noCacheLoader.load('null') | ||
.then(data => { | ||
expect(data).to.be.null; | ||
return this.noCacheLoader.load('null'); | ||
}) | ||
.then(data => { | ||
expect(data).to.be.null; | ||
done(); | ||
}).done(); | ||
it('should not use in memory cache if option is passed', () => { | ||
this.stubs.redisMGet = sinon.stub(redis, 'mget', (keys, cb) => { | ||
cb(null, [JSON.stringify(this.data.json)]); | ||
}); | ||
return this.noCacheLoader | ||
.load('json') | ||
.then(data => { | ||
expect(this.loadFn.callCount).to.equal(0); | ||
expect(this.stubs.redisMGet.callCount).to.equal(1); | ||
return this.noCacheLoader.load('json'); | ||
}) | ||
.then(data => { | ||
expect(this.loadFn.callCount).to.equal(0); | ||
expect(this.stubs.redisMGet.callCount).to.equal(2); | ||
}); | ||
}); | ||
it('should handle redis key expiration if set', done => { | ||
const loader = new RedisDataLoader( | ||
this.keySpace, | ||
this.userLoader(), | ||
{ cache: false, expire: 1 } | ||
); | ||
it('should load null values', () => | ||
this.loader | ||
.load('null') | ||
.then(data => { | ||
expect(data).to.be.null; | ||
return this.loader.load('null'); | ||
}) | ||
.then(data => { | ||
expect(data).to.be.null; | ||
})); | ||
loader.load('json') | ||
.then(data => { | ||
expect(data).to.deep.equal(this.data.json); | ||
setTimeout(() => { | ||
loader.load('json') | ||
.then(data => { | ||
expect(data).to.deep.equal(this.data.json); | ||
expect(this.loadFn.callCount).to.equal(2); | ||
done(); | ||
}).done(); | ||
}, 1100); | ||
}).done(); | ||
}); | ||
it('should handle redis cacheing of null values', () => | ||
this.noCacheLoader | ||
.load('null') | ||
.then(data => { | ||
expect(data).to.be.null; | ||
return this.noCacheLoader.load('null'); | ||
}) | ||
.then(data => { | ||
expect(data).to.be.null; | ||
})); | ||
it('should handle custom serialize and deserialize method', done => { | ||
const loader = new RedisDataLoader( | ||
this.keySpace, | ||
this.userLoader(), | ||
{ | ||
serialize: v => 100, | ||
deserialize: v => new Date(Number(v)) | ||
} | ||
); | ||
it('should handle redis key expiration if set', done => { | ||
const loader = new RedisDataLoader(this.keySpace, this.userLoader(), { | ||
cache: false, | ||
expire: 1, | ||
}); | ||
loader.load('json') | ||
.then(data => { | ||
expect(data).to.be.instanceof(Date); | ||
expect(data.getTime()).to.equal(100); | ||
loader | ||
.load('json') | ||
.then(data => { | ||
expect(data).to.deep.equal(this.data.json); | ||
setTimeout(() => { | ||
loader | ||
.load('json') | ||
.then(data => { | ||
expect(data).to.deep.equal(this.data.json); | ||
expect(this.loadFn.callCount).to.equal(2); | ||
done(); | ||
}).done(); | ||
}); | ||
}) | ||
.done(); | ||
}, 1100); | ||
}) | ||
.catch(done) | ||
.done(); | ||
}); | ||
describe('loadMany', () => { | ||
it('should load multiple keys', done => { | ||
this.loader.loadMany(['json', 'null']) | ||
.then(results => { | ||
expect(results).to.deep.equal([this.data.json, this.data.null]); | ||
done(); | ||
}).done(); | ||
}); | ||
it('should handle custom serialize and deserialize method', () => { | ||
const loader = new RedisDataLoader(this.keySpace, this.userLoader(), { | ||
serialize: v => 100, | ||
deserialize: v => new Date(Number(v)), | ||
}); | ||
it('should handle empty array', done => { | ||
this.loader.loadMany([]) | ||
.then(results => { | ||
expect(results).to.deep.equal([]); | ||
done(); | ||
}).done(); | ||
}); | ||
it('should require array', done => { | ||
this.loader.loadMany().catch(err => { | ||
expect(err).to.be.instanceof(TypeError); | ||
done(); | ||
}).done(); | ||
}); | ||
return loader.load('json').then(data => { | ||
expect(data).to.be.instanceof(Date); | ||
expect(data.getTime()).to.equal(100); | ||
}); | ||
}); | ||
}); | ||
describe('prime', () => { | ||
it('should set cache', done => { | ||
this.loader.prime('json', { new: 'value' }) | ||
.then(() => this.loader.load('json')) | ||
.then(data => { | ||
expect(data).to.deep.equal({ new: 'value' }); | ||
done(); | ||
}).done(); | ||
}); | ||
describe('loadMany', () => { | ||
it('should load multiple keys', () => | ||
this.loader.loadMany(['json', 'null']).then(results => { | ||
expect(results).to.deep.equal([this.data.json, this.data.null]); | ||
})); | ||
it('should handle primeing without local cache', done => { | ||
this.noCacheLoader.prime('json', { new: 'value' }) | ||
.then(() => this.noCacheLoader.load('json')) | ||
.then(data => { | ||
expect(data).to.deep.equal({ new: 'value' }); | ||
done(); | ||
}).done(); | ||
}); | ||
it('should handle object key', () => | ||
this.loader.loadMany([{ a: 1, b: 2 }]).then(results => { | ||
expect(results).to.deep.equal([{ bar: 'baz' }]); | ||
})); | ||
it('should require key', done => { | ||
this.loader.prime(undefined, { new: 'value' }) | ||
.catch(err => { | ||
expect(err).to.be.instanceof(TypeError); | ||
done(); | ||
}).done(); | ||
}); | ||
it('should handle empty array', () => | ||
this.loader.loadMany([]).then(results => { | ||
expect(results).to.deep.equal([]); | ||
})); | ||
it('should require value', done => { | ||
this.loader.prime('json').catch(err => { | ||
expect(err).to.be.instanceof(TypeError); | ||
done(); | ||
}).done(); | ||
}); | ||
it('should require array', () => | ||
expect(this.loader.loadMany()).to.be.rejectedWith(TypeError)); | ||
}); | ||
it('should allow null for value', done => { | ||
this.loader.prime('json', null) | ||
.then(() => this.loader.load('json')) | ||
.then(data => { | ||
expect(data).to.be.null; | ||
done(); | ||
}).done(); | ||
}); | ||
}); | ||
describe('prime', () => { | ||
it('should set cache', () => | ||
this.loader | ||
.prime('json', { new: 'value' }) | ||
.then(() => this.loader.load('json')) | ||
.then(data => { | ||
expect(data).to.deep.equal({ new: 'value' }); | ||
})); | ||
describe('clear', () => { | ||
it('should clear cache', done => { | ||
this.loader.load('json') | ||
.then(() => this.loader.clear('json')) | ||
.then(() => this.loader.load('json')) | ||
.then(data => { | ||
expect(data).to.deep.equal(this.data.json); | ||
expect(this.loadFn.callCount).to.equal(2); | ||
done(); | ||
}).done(); | ||
}); | ||
it('should handle object key', () => | ||
this.loader | ||
.prime({ a: 1, b: 2 }, { new: 'val' }) | ||
.then(() => this.loader.load({ a: 1, b: 2 })) | ||
.then(data => { | ||
expect(data).to.deep.equal({ new: 'val' }); | ||
})); | ||
it('should require a key', done => { | ||
this.loader.clear() | ||
.catch(err => { | ||
expect(err).to.be.instanceof(TypeError); | ||
done(); | ||
}).done(); | ||
}); | ||
}); | ||
it('should handle primeing without local cache', () => | ||
this.noCacheLoader | ||
.prime('json', { new: 'value' }) | ||
.then(() => this.noCacheLoader.load('json')) | ||
.then(data => { | ||
expect(data).to.deep.equal({ new: 'value' }); | ||
})); | ||
describe('clearAllLocal', () => { | ||
it('should clear all local in-memory cache', done => { | ||
this.loader.loadMany(['json', 'null']) | ||
.then(() => this.loader.clearAllLocal()) | ||
.then(() => this.rSet( | ||
`${this.keySpace}:json`, JSON.stringify({ new: 'valeo' }) | ||
)) | ||
.then(() => this.rSet( | ||
`${this.keySpace}:null`, JSON.stringify({ foo: 'bar' }) | ||
)) | ||
.then(() => this.loader.loadMany(['null', 'json'])) | ||
.then(data => { | ||
expect(data).to.deep.equal([{ foo: 'bar' }, { new: 'valeo' }]); | ||
done(); | ||
}).done(); | ||
}); | ||
}); | ||
it('should require key', () => | ||
expect(this.loader.prime(undefined, { new: 'value' })).to.be.rejectedWith( | ||
TypeError | ||
)); | ||
describe('clearLocal', () => { | ||
it('should clear local cache for a specific key', done => { | ||
this.loader.loadMany(['json', 'null']) | ||
.then(() => this.loader.clearLocal('json')) | ||
.then(() => this.rSet( | ||
`${this.keySpace}:json`, JSON.stringify({ new: 'valeo' }) | ||
)) | ||
.then(() => this.rSet( | ||
`${this.keySpace}:null`, JSON.stringify({ foo: 'bar' }) | ||
)) | ||
.then(() => this.loader.loadMany(['null', 'json'])) | ||
.then(data => { | ||
expect(data).to.deep.equal([null, { new: 'valeo' }]); | ||
done(); | ||
}).done(); | ||
}); | ||
}); | ||
it('should require value', () => | ||
expect(this.loader.prime('json')).to.be.rejectedWith(TypeError)); | ||
it('should allow null for value', () => | ||
this.loader | ||
.prime('json', null) | ||
.then(() => this.loader.load('json')) | ||
.then(data => { | ||
expect(data).to.be.null; | ||
})); | ||
}); | ||
describe('clear', () => { | ||
it('should clear cache', () => | ||
this.loader | ||
.load('json') | ||
.then(() => this.loader.clear('json')) | ||
.then(() => this.loader.load('json')) | ||
.then(data => { | ||
expect(data).to.deep.equal(this.data.json); | ||
expect(this.loadFn.callCount).to.equal(2); | ||
})); | ||
it('should handle object key', () => | ||
this.loader | ||
.load({ a: 1, b: 2 }) | ||
.then(() => this.loader.clear({ a: 1, b: 2 })) | ||
.then(() => this.loader.load({ a: 1, b: 2 })) | ||
.then(data => { | ||
expect(data).to.deep.equal({ bar: 'baz' }); | ||
expect(this.loadFn.callCount).to.equal(2); | ||
})); | ||
it('should require a key', () => | ||
expect(this.loader.clear()).to.be.rejectedWith(TypeError)); | ||
}); | ||
describe('clearAllLocal', () => { | ||
it('should clear all local in-memory cache', () => | ||
this.loader | ||
.loadMany(['json', 'null']) | ||
.then(() => this.loader.clearAllLocal()) | ||
.then(() => | ||
this.rSet(`${this.keySpace}:json`, JSON.stringify({ new: 'valeo' })) | ||
) | ||
.then(() => | ||
this.rSet(`${this.keySpace}:null`, JSON.stringify({ foo: 'bar' })) | ||
) | ||
.then(() => this.loader.loadMany(['null', 'json'])) | ||
.then(data => { | ||
expect(data).to.deep.equal([{ foo: 'bar' }, { new: 'valeo' }]); | ||
})); | ||
}); | ||
describe('clearLocal', () => { | ||
it('should clear local cache for a specific key', () => | ||
this.loader | ||
.loadMany(['json', 'null']) | ||
.then(() => this.loader.clearLocal('json')) | ||
.then(() => | ||
this.rSet(`${this.keySpace}:json`, JSON.stringify({ new: 'valeo' })) | ||
) | ||
.then(() => | ||
this.rSet(`${this.keySpace}:null`, JSON.stringify({ foo: 'bar' })) | ||
) | ||
.then(() => this.loader.loadMany(['null', 'json'])) | ||
.then(data => { | ||
expect(data).to.deep.equal([null, { new: 'valeo' }]); | ||
})); | ||
}); | ||
}); |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
22867
6901
9
440
112
4
6
1
+ Addedbluebird@^3.5.0
+ Addedjson-stable-stringify@^1.0.1
+ Addedbluebird@3.7.2(transitive)
+ Addedcall-bind@1.0.8(transitive)
+ Addedcall-bind-apply-helpers@1.0.1(transitive)
+ Addedcall-bound@1.0.3(transitive)
+ Addeddefine-data-property@1.1.4(transitive)
+ Addeddunder-proto@1.0.1(transitive)
+ Addedes-define-property@1.0.1(transitive)
+ Addedes-errors@1.3.0(transitive)
+ Addedes-object-atoms@1.0.0(transitive)
+ Addedfunction-bind@1.1.2(transitive)
+ Addedget-intrinsic@1.2.6(transitive)
+ Addedgopd@1.2.0(transitive)
+ Addedhas-property-descriptors@1.0.2(transitive)
+ Addedhas-symbols@1.1.0(transitive)
+ Addedhasown@2.0.2(transitive)
+ Addedisarray@2.0.5(transitive)
+ Addedjson-stable-stringify@1.2.1(transitive)
+ Addedjsonify@0.0.1(transitive)
+ Addedmath-intrinsics@1.1.0(transitive)
+ Addedobject-keys@1.1.1(transitive)
+ Addedset-function-length@1.2.2(transitive)
- Removedq@^1.4.1
- Removedq@1.5.1(transitive)