feat: allow ttl to be defined based on the response

This commit is contained in:
arthurfiorette 2022-01-03 10:00:39 -03:00
parent 4c58316165
commit 1e87549eb6
No known key found for this signature in database
GPG Key ID: 9D190CD53C53C555
4 changed files with 84 additions and 7 deletions

View File

@ -531,10 +531,13 @@ You can override the request id used by this property.
### request.cache.ttl
The time that the request will remain in cache. Some custom storage implementations may
not respect 100% the time.
The time until the cached value is expired in milliseconds.
When using `interpretHeader`, this value is ignored.
If a function is used, it will receive the complete response and waits to return a TTL
value
When using `interpretHeader: true`, this value will only be used if the interpreter can't
determine their TTL value to override this
### request.cache.interpretHeader

6
src/cache/cache.ts vendored
View File

@ -11,14 +11,14 @@ export type CacheProperties = {
/**
* The time until the cached value is expired in milliseconds.
*
* If a function is used, it will receive the complete response and waits to return a TTL value
*
* When using `interpretHeader: true`, this value will only be used if the interpreter
* can't determine their TTL value to override this
*
* **Note**: a custom storage implementation may not respect this.
*
* @default 1000 * 60 * 5 // 5 Minutes
*/
ttl: number;
ttl: number | (<R, D>(response: CacheAxiosResponse<R, D>) => number | Promise<number>);
/**
* If this interceptor should configure the cache from the request cache header When

View File

@ -88,6 +88,10 @@ export class CacheResponseInterceptor<R, D>
const data = setupCacheData(response, cache.data);
if (typeof ttl === 'function') {
ttl = await ttl(response);
}
const newCache: CachedStorageValue = {
state: 'cached',
ttl,

View File

@ -1,5 +1,5 @@
import { Header } from '../../src/util/headers';
import { mockAxios } from '../mocks/axios';
import { mockAxios, XMockRandom } from '../mocks/axios';
describe('test request interceptor', () => {
it('tests cache predicate integration', async () => {
@ -79,4 +79,74 @@ describe('test request interceptor', () => {
expect(cache.state).toBe('cached');
expect(cache.ttl).toBe(defaultTtl);
});
it('tests ttl with functions', async () => {
const axios = mockAxios();
const id = 'my-id';
// first request (cached by tll)
await axios.get('url', {
id,
cache: {
ttl: (resp) => {
expect(resp.cached).toBe(false);
expect(resp.config).toBeDefined();
expect(resp.headers[XMockRandom]).not.toBeNaN();
expect(resp.status).toBe(200);
expect(resp.statusText).toBe('200 OK');
expect(resp.data).toBeTruthy();
return 100;
}
}
});
const cache1 = await axios.storage.get(id);
expect(cache1.state).toBe('cached');
expect(cache1.ttl).toBe(100);
// Second request (cached by ttl)
const ttl = jest.fn().mockReturnValue(200);
await axios.get('url', {
id,
cache: { ttl }
});
const cache2 = await axios.storage.get(id);
expect(cache2.state).toBe('cached');
expect(cache2.ttl).toBe(100);
expect(ttl).not.toHaveBeenCalled();
// Force invalidation
await axios.storage.remove(id);
});
it('tests async ttl function', async () => {
const axios = mockAxios();
// A lot of promises and callbacks
const { id } = await axios.get('url', {
cache: {
ttl: async () => {
await 0;
return new Promise((res) => {
setTimeout(() => {
process.nextTick(() => {
res(173);
});
}, 50);
});
}
}
});
const cache = await axios.storage.get(id);
expect(cache.state).toBe('cached');
expect(cache.ttl).toBe(173);
});
});