diff --git a/api-server/server/utils/in-memory-cache.js b/api-server/server/utils/in-memory-cache.js new file mode 100644 index 0000000000..3239c88649 --- /dev/null +++ b/api-server/server/utils/in-memory-cache.js @@ -0,0 +1,34 @@ +function isPromiseLike(thing) { + return !!thing && typeof thing.then === 'function'; +} + +function InMemoryCache(initialValue) { + const cacheKey = Symbol('cacheKey'); + const cache = new Map(); + + if (typeof initialValue !== 'undefined') { + cache.set(cacheKey, initialValue); + } + + return { + get() { + const value = cache.get(cacheKey); + return typeof value !== 'undefined' ? value : null; + }, + async update(fn) { + const maybePromisedValue = fn(); + if (isPromiseLike(maybePromisedValue)) { + return maybePromisedValue.then(value => cache.set(cacheKey, value)); + } else { + const value = maybePromisedValue; + cache.set(cacheKey, value); + return null; + } + }, + clear() { + return cache.delete(cacheKey); + } + }; +} + +export default InMemoryCache; diff --git a/api-server/server/utils/in-memory-cache.test.js b/api-server/server/utils/in-memory-cache.test.js new file mode 100644 index 0000000000..443e96a222 --- /dev/null +++ b/api-server/server/utils/in-memory-cache.test.js @@ -0,0 +1,49 @@ +/* global describe expect it beforeEach */ +import inMemoryCache from './in-memory-cache'; + +describe('InMemoryCache', () => { + const theAnswer = 42; + const before = 'before'; + const after = 'after'; + const emptyCacheValue = null; + + describe('get', () => { + it('returns null for an empty cache', () => { + const cache = inMemoryCache(); + expect(cache.get()).toBe(emptyCacheValue); + }); + + it('returns an initial value', () => { + const cache = inMemoryCache(theAnswer); + expect(cache.get()).toBe(theAnswer); + }); + }); + + describe('update', () => { + it('updates the cached value', () => { + const cache = inMemoryCache(before); + cache.update(() => after); + expect(cache.get()).toBe(after); + }); + + it('can handle promises correctly', done => { + const cache = inMemoryCache(before); + cache.update(() => new Promise(resolve => resolve(after))); + // because async + setImmediate(() => { + expect(cache.get()).toBe(after); + done(); + }); + }); + }); + + describe('clear', () => { + it('clears the cache', () => { + expect.assertions(2); + const cache = inMemoryCache(theAnswer); + expect(cache.get()).toBe(theAnswer); + cache.clear(); + expect(cache.get()).toBe(emptyCacheValue); + }); + }); +});