Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Adding Caching Adapter, allows caching of _Role and _User queries (fixes
- Loading branch information
Showing
18 changed files
with
526 additions
and
134 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
var CacheController = require('../src/Controllers/CacheController.js').default; | ||
|
||
describe('CacheController', function() { | ||
var FakeCacheAdapter; | ||
var FakeAppID = 'foo'; | ||
var KEY = 'hello'; | ||
|
||
beforeEach(() => { | ||
FakeCacheAdapter = { | ||
get: () => Promise.resolve(null), | ||
put: jasmine.createSpy('put'), | ||
del: jasmine.createSpy('del'), | ||
clear: jasmine.createSpy('clear') | ||
} | ||
|
||
spyOn(FakeCacheAdapter, 'get').and.callThrough(); | ||
}); | ||
|
||
|
||
it('should expose role and user caches', (done) => { | ||
var cache = new CacheController(FakeCacheAdapter, FakeAppID); | ||
|
||
expect(cache.role).not.toEqual(null); | ||
expect(cache.role.get).not.toEqual(null); | ||
expect(cache.user).not.toEqual(null); | ||
expect(cache.user.get).not.toEqual(null); | ||
|
||
done(); | ||
}); | ||
|
||
|
||
['role', 'user'].forEach((cacheName) => { | ||
it('should prefix ' + cacheName + ' cache', () => { | ||
var cache = new CacheController(FakeCacheAdapter, FakeAppID)[cacheName]; | ||
|
||
cache.put(KEY, 'world'); | ||
var firstPut = FakeCacheAdapter.put.calls.first(); | ||
expect(firstPut.args[0]).toEqual([FakeAppID, cacheName, KEY].join(':')); | ||
|
||
cache.get(KEY); | ||
var firstGet = FakeCacheAdapter.get.calls.first(); | ||
expect(firstGet.args[0]).toEqual([FakeAppID, cacheName, KEY].join(':')); | ||
|
||
cache.del(KEY); | ||
var firstDel = FakeCacheAdapter.del.calls.first(); | ||
expect(firstDel.args[0]).toEqual([FakeAppID, cacheName, KEY].join(':')); | ||
}); | ||
}); | ||
|
||
it('should clear the entire cache', () => { | ||
var cache = new CacheController(FakeCacheAdapter, FakeAppID); | ||
|
||
cache.clear(); | ||
expect(FakeCacheAdapter.clear.calls.count()).toEqual(1); | ||
|
||
cache.user.clear(); | ||
expect(FakeCacheAdapter.clear.calls.count()).toEqual(2); | ||
|
||
cache.role.clear(); | ||
expect(FakeCacheAdapter.clear.calls.count()).toEqual(3); | ||
}); | ||
|
||
it('should handle cache rejections', (done) => { | ||
|
||
FakeCacheAdapter.get = () => Promise.reject(); | ||
|
||
var cache = new CacheController(FakeCacheAdapter, FakeAppID); | ||
|
||
cache.get('foo').then(done, () => { | ||
fail('Promise should not be rejected.'); | ||
}); | ||
}); | ||
|
||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
const InMemoryCache = require('../src/Adapters/Cache/InMemoryCache').default; | ||
|
||
|
||
describe('InMemoryCache', function() { | ||
var BASE_TTL = { | ||
ttl: 10 | ||
}; | ||
var NO_EXPIRE_TTL = { | ||
ttl: NaN | ||
}; | ||
var KEY = 'hello'; | ||
var KEY_2 = KEY + '_2'; | ||
|
||
var VALUE = 'world'; | ||
|
||
|
||
function wait(sleep) { | ||
return new Promise(function(resolve, reject) { | ||
setTimeout(resolve, sleep); | ||
}) | ||
} | ||
|
||
it('should destroy a expire items in the cache', (done) => { | ||
var cache = new InMemoryCache(BASE_TTL); | ||
|
||
cache.put(KEY, VALUE); | ||
|
||
var value = cache.get(KEY); | ||
expect(value).toEqual(VALUE); | ||
|
||
wait(BASE_TTL.ttl * 5).then(() => { | ||
value = cache.get(KEY) | ||
expect(value).toEqual(null); | ||
done(); | ||
}); | ||
}); | ||
|
||
it('should delete items', (done) => { | ||
var cache = new InMemoryCache(NO_EXPIRE_TTL); | ||
cache.put(KEY, VALUE); | ||
cache.put(KEY_2, VALUE); | ||
expect(cache.get(KEY)).toEqual(VALUE); | ||
expect(cache.get(KEY_2)).toEqual(VALUE); | ||
|
||
cache.del(KEY); | ||
expect(cache.get(KEY)).toEqual(null); | ||
expect(cache.get(KEY_2)).toEqual(VALUE); | ||
|
||
cache.del(KEY_2); | ||
expect(cache.get(KEY)).toEqual(null); | ||
expect(cache.get(KEY_2)).toEqual(null); | ||
done(); | ||
}); | ||
|
||
it('should clear all items', (done) => { | ||
var cache = new InMemoryCache(NO_EXPIRE_TTL); | ||
cache.put(KEY, VALUE); | ||
cache.put(KEY_2, VALUE); | ||
|
||
expect(cache.get(KEY)).toEqual(VALUE); | ||
expect(cache.get(KEY_2)).toEqual(VALUE); | ||
cache.clear(); | ||
|
||
expect(cache.get(KEY)).toEqual(null); | ||
expect(cache.get(KEY_2)).toEqual(null); | ||
done(); | ||
}); | ||
|
||
it('should deafult TTL to 5 seconds', () => { | ||
var cache = new InMemoryCache({}); | ||
expect(cache.ttl).toEqual(5 * 1000); | ||
}); | ||
|
||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
var InMemoryCacheAdapter = require('../src/Adapters/Cache/InMemoryCacheAdapter').default; | ||
|
||
describe('InMemoryCacheAdapter', function() { | ||
var KEY = 'hello'; | ||
var VALUE = 'world'; | ||
|
||
function wait(sleep) { | ||
return new Promise(function(resolve, reject) { | ||
setTimeout(resolve, sleep); | ||
}) | ||
} | ||
|
||
it('should expose promisifyed methods', (done) => { | ||
var cache = new InMemoryCacheAdapter({ | ||
ttl: NaN | ||
}); | ||
|
||
var noop = () => {}; | ||
|
||
// Verify all methods return promises. | ||
Promise.all([ | ||
cache.put(KEY, VALUE), | ||
cache.del(KEY), | ||
cache.get(KEY), | ||
cache.clear() | ||
]).then(() => { | ||
done(); | ||
}); | ||
}); | ||
|
||
it('should get/set/clear', (done) => { | ||
var cache = new InMemoryCacheAdapter({ | ||
ttl: NaN | ||
}); | ||
|
||
cache.put(KEY, VALUE) | ||
.then(() => cache.get(KEY)) | ||
.then((value) => expect(value).toEqual(VALUE)) | ||
.then(() => cache.clear()) | ||
.then(() => cache.get(KEY)) | ||
.then((value) => expect(value).toEqual(null)) | ||
.then(done); | ||
}); | ||
|
||
it('should expire after ttl', (done) => { | ||
var cache = new InMemoryCacheAdapter({ | ||
ttl: 10 | ||
}); | ||
|
||
cache.put(KEY, VALUE) | ||
.then(() => cache.get(KEY)) | ||
.then((value) => expect(value).toEqual(VALUE)) | ||
.then(wait.bind(null, 50)) | ||
.then(() => cache.get(KEY)) | ||
.then((value) => expect(value).toEqual(null)) | ||
.then(done); | ||
}) | ||
|
||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
export class CacheAdapter { | ||
/** | ||
* Get a value in the cache | ||
* @param key Cache key to get | ||
* @return Promise that will eventually resolve to the value in the cache. | ||
*/ | ||
get(key) {} | ||
|
||
/** | ||
* Set a value in the cache | ||
* @param key Cache key to set | ||
* @param value Value to set the key | ||
* @param ttl Optional TTL | ||
*/ | ||
put(key, value, ttl) {} | ||
|
||
/** | ||
* Remove a value from the cache. | ||
* @param key Cache key to remove | ||
*/ | ||
del(key) {} | ||
|
||
/** | ||
* Empty a cache | ||
*/ | ||
clear() {} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
const DEFAULT_CACHE_TTL = 5 * 1000; | ||
|
||
|
||
export class InMemoryCache { | ||
constructor({ | ||
ttl = DEFAULT_CACHE_TTL | ||
}) { | ||
this.ttl = ttl; | ||
this.cache = Object.create(null); | ||
} | ||
|
||
get(key) { | ||
let record = this.cache[key]; | ||
if (record == null) { | ||
return null; | ||
} | ||
|
||
// Has Record and isnt expired | ||
if (isNaN(record.expire) || record.expire >= Date.now()) { | ||
return record.value; | ||
} | ||
|
||
// Record has expired | ||
delete this.cache[key]; | ||
return null; | ||
} | ||
|
||
put(key, value, ttl = this.ttl) { | ||
if (ttl < 0 || isNaN(ttl)) { | ||
ttl = NaN; | ||
} | ||
|
||
var record = { | ||
value: value, | ||
expire: ttl + Date.now() | ||
} | ||
|
||
if (!isNaN(record.expire)) { | ||
record.timeout = setTimeout(() => { | ||
this.del(key); | ||
}, ttl); | ||
} | ||
|
||
this.cache[key] = record; | ||
} | ||
|
||
del(key) { | ||
var record = this.cache[key]; | ||
if (record == null) { | ||
return; | ||
} | ||
|
||
if (record.timeout) { | ||
clearTimeout(record.timeout); | ||
} | ||
|
||
delete this.cache[key]; | ||
} | ||
|
||
clear() { | ||
this.cache = Object.create(null); | ||
} | ||
|
||
} | ||
|
||
export default InMemoryCache; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
import {InMemoryCache} from './InMemoryCache'; | ||
|
||
export class InMemoryCacheAdapter { | ||
|
||
constructor(ctx) { | ||
this.cache = new InMemoryCache(ctx) | ||
} | ||
|
||
get(key) { | ||
return new Promise((resolve, reject) => { | ||
let record = this.cache.get(key); | ||
if (record == null) { | ||
return resolve(null); | ||
} | ||
|
||
return resolve(JSON.parse(record)); | ||
}) | ||
} | ||
|
||
put(key, value, ttl) { | ||
this.cache.put(key, JSON.stringify(value), ttl); | ||
return Promise.resolve(); | ||
} | ||
|
||
del(key) { | ||
this.cache.del(key); | ||
return Promise.resolve(); | ||
} | ||
|
||
clear() { | ||
this.cache.clear(); | ||
return Promise.resolve(); | ||
} | ||
} | ||
|
||
export default InMemoryCacheAdapter; |
Oops, something went wrong.