Skip to content

Commit 6955e71

Browse files
renan628Fryunimarcospassos
authored
Implement a defaut while miss cache provider (#115)
Co-authored-by: Luiz Ferraz <[email protected]> Co-authored-by: Marcos Passos <[email protected]>
1 parent 73a01bf commit 6955e71

File tree

3 files changed

+121
-1
lines changed

3 files changed

+121
-1
lines changed

src/defaultWhileMiss.ts

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import {CacheLoader, CacheProvider} from './cacheProvider';
2+
3+
export type Configuration<K, V> = {
4+
provider: CacheProvider<K, V>,
5+
defaultValue: V,
6+
/**
7+
* Handler for background revalidation errors
8+
*/
9+
errorHandler?: (error: Error) => void,
10+
};
11+
12+
export class DefaultWhileMissCache<K, V> implements CacheProvider<K, V> {
13+
private readonly provider: Configuration<K, V>['provider'];
14+
15+
private readonly defaultValue: V;
16+
17+
private readonly errorHandler: (error: Error) => void;
18+
19+
public constructor(config: Configuration<K, V>) {
20+
this.provider = config.provider;
21+
this.defaultValue = config.defaultValue;
22+
this.errorHandler = config.errorHandler ?? ((): void => { /* noop */ });
23+
}
24+
25+
public get(key: K, loader: CacheLoader<K, V>): Promise<V> {
26+
return this.provider.get(key, innerKey => {
27+
loader(innerKey).catch(this.errorHandler);
28+
29+
return Promise.resolve(this.defaultValue);
30+
});
31+
}
32+
33+
public set(key: K, value: V): Promise<void> {
34+
return this.provider.set(key, value);
35+
}
36+
37+
public delete(key: K): Promise<void> {
38+
return this.provider.delete(key);
39+
}
40+
}

src/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,5 @@ export * from './staleWhileRevalidate';
99
export * from './timestampedCacheEntry';
1010
export * from './errorResilient';
1111
export * from './sharedInFlight';
12-
export {NoopCache} from './noop';
12+
export * from './noop';
13+
export * from './defaultWhileMiss';

test/defaultWhileMiss.test.ts

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
import {DefaultWhileMissCache, NoopCache} from '../src';
2+
3+
describe('A cache provider that returns a default value while loading in the background', () => {
4+
it('should return a default value calling the loader the background', async () => {
5+
const provider = new NoopCache();
6+
7+
jest.spyOn(provider, 'get');
8+
9+
const loader = jest.fn().mockResolvedValue('loaderValue');
10+
11+
const cache = new DefaultWhileMissCache({
12+
provider: provider,
13+
defaultValue: 'defaultValue',
14+
});
15+
16+
await expect(cache.get('key', loader)).resolves.toBe('defaultValue');
17+
18+
expect(provider.get).toHaveBeenCalledWith('key', expect.any(Function));
19+
20+
expect(loader).toHaveBeenCalledWith('key');
21+
});
22+
23+
it('should return a default value handling a loading error in background', async () => {
24+
const provider = new NoopCache();
25+
26+
jest.spyOn(provider, 'get');
27+
28+
const error = new Error('Some error.');
29+
30+
const loader = jest.fn().mockRejectedValue(error);
31+
32+
const errorHandler = jest.fn();
33+
34+
const cache = new DefaultWhileMissCache({
35+
provider: provider,
36+
defaultValue: 'defaultValue',
37+
errorHandler: errorHandler,
38+
});
39+
40+
await expect(cache.get('key', loader)).resolves.toBe('defaultValue');
41+
42+
expect(provider.get).toHaveBeenCalledWith('key', expect.any(Function));
43+
44+
expect(loader).toHaveBeenCalledWith('key');
45+
46+
expect(errorHandler).toHaveBeenCalledTimes(1);
47+
expect(errorHandler).toHaveBeenCalledWith(error);
48+
});
49+
50+
it('should delegate setting a value to the underlying provider', async () => {
51+
const provider = new NoopCache();
52+
53+
jest.spyOn(provider, 'set');
54+
55+
const cache = new DefaultWhileMissCache({
56+
provider: provider,
57+
defaultValue: 'defaultValue',
58+
});
59+
60+
await cache.set('key', 'value');
61+
62+
expect(provider.set).toHaveBeenCalledWith('key', 'value');
63+
});
64+
65+
it('should delegate deleting a value to the underlying provider', async () => {
66+
const provider = new NoopCache();
67+
68+
jest.spyOn(provider, 'delete');
69+
70+
const cache = new DefaultWhileMissCache({
71+
provider: provider,
72+
defaultValue: 'defaultValue',
73+
});
74+
75+
await cache.delete('key');
76+
77+
expect(provider.delete).toHaveBeenCalledWith('key');
78+
});
79+
});

0 commit comments

Comments
 (0)