-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implement a defaut while miss cache provider (#115)
Co-authored-by: Luiz Ferraz <[email protected]> Co-authored-by: Marcos Passos <[email protected]>
- Loading branch information
1 parent
73a01bf
commit 6955e71
Showing
3 changed files
with
121 additions
and
1 deletion.
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,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); | ||
} | ||
} |
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,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'); | ||
}); | ||
}); |