Skip to content

Commit

Permalink
Implement a defaut while miss cache provider (#115)
Browse files Browse the repository at this point in the history
Co-authored-by: Luiz Ferraz <[email protected]>
Co-authored-by: Marcos Passos <[email protected]>
  • Loading branch information
3 people authored May 16, 2024
1 parent 73a01bf commit 6955e71
Show file tree
Hide file tree
Showing 3 changed files with 121 additions and 1 deletion.
40 changes: 40 additions & 0 deletions src/defaultWhileMiss.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import {CacheLoader, CacheProvider} from './cacheProvider';

export type Configuration<K, V> = {
provider: CacheProvider<K, V>,
defaultValue: V,
/**
* Handler for background revalidation errors
*/
errorHandler?: (error: Error) => void,
};

export class DefaultWhileMissCache<K, V> implements CacheProvider<K, V> {
private readonly provider: Configuration<K, V>['provider'];

private readonly defaultValue: V;

private readonly errorHandler: (error: Error) => void;

public constructor(config: Configuration<K, V>) {
this.provider = config.provider;
this.defaultValue = config.defaultValue;
this.errorHandler = config.errorHandler ?? ((): void => { /* noop */ });
}

public get(key: K, loader: CacheLoader<K, V>): Promise<V> {
return this.provider.get(key, innerKey => {
loader(innerKey).catch(this.errorHandler);

return Promise.resolve(this.defaultValue);
});
}

public set(key: K, value: V): Promise<void> {
return this.provider.set(key, value);
}

public delete(key: K): Promise<void> {
return this.provider.delete(key);
}
}
3 changes: 2 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,5 @@ export * from './staleWhileRevalidate';
export * from './timestampedCacheEntry';
export * from './errorResilient';
export * from './sharedInFlight';
export {NoopCache} from './noop';
export * from './noop';
export * from './defaultWhileMiss';
79 changes: 79 additions & 0 deletions test/defaultWhileMiss.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import {DefaultWhileMissCache, NoopCache} from '../src';

describe('A cache provider that returns a default value while loading in the background', () => {
it('should return a default value calling the loader the background', async () => {
const provider = new NoopCache();

jest.spyOn(provider, 'get');

const loader = jest.fn().mockResolvedValue('loaderValue');

const cache = new DefaultWhileMissCache({
provider: provider,
defaultValue: 'defaultValue',
});

await expect(cache.get('key', loader)).resolves.toBe('defaultValue');

expect(provider.get).toHaveBeenCalledWith('key', expect.any(Function));

expect(loader).toHaveBeenCalledWith('key');
});

it('should return a default value handling a loading error in background', async () => {
const provider = new NoopCache();

jest.spyOn(provider, 'get');

const error = new Error('Some error.');

const loader = jest.fn().mockRejectedValue(error);

const errorHandler = jest.fn();

const cache = new DefaultWhileMissCache({
provider: provider,
defaultValue: 'defaultValue',
errorHandler: errorHandler,
});

await expect(cache.get('key', loader)).resolves.toBe('defaultValue');

expect(provider.get).toHaveBeenCalledWith('key', expect.any(Function));

expect(loader).toHaveBeenCalledWith('key');

expect(errorHandler).toHaveBeenCalledTimes(1);
expect(errorHandler).toHaveBeenCalledWith(error);
});

it('should delegate setting a value to the underlying provider', async () => {
const provider = new NoopCache();

jest.spyOn(provider, 'set');

const cache = new DefaultWhileMissCache({
provider: provider,
defaultValue: 'defaultValue',
});

await cache.set('key', 'value');

expect(provider.set).toHaveBeenCalledWith('key', 'value');
});

it('should delegate deleting a value to the underlying provider', async () => {
const provider = new NoopCache();

jest.spyOn(provider, 'delete');

const cache = new DefaultWhileMissCache({
provider: provider,
defaultValue: 'defaultValue',
});

await cache.delete('key');

expect(provider.delete).toHaveBeenCalledWith('key');
});
});

0 comments on commit 6955e71

Please sign in to comment.