mirror of
https://github.com/arthurfiorette/axios-cache-interceptor.git
synced 2025-12-08 17:36:16 +00:00
feat: cache updater function for multiple keys. (#304)
* feat: implemented general cache updater function * test: added tests * docs: included cache update warning
This commit is contained in:
parent
24ff867288
commit
f0cd5d1af2
@ -88,6 +88,10 @@ with the request id.
|
||||
|
||||
Here's an example with some basic login:
|
||||
|
||||
Using a function instead of an object is supported but not recommended, as it's better to
|
||||
just consume the response normally and write your own code after it. But it`s here in case
|
||||
you need it.
|
||||
|
||||
```ts
|
||||
// Some requests id's
|
||||
let profileInfoId;
|
||||
@ -112,6 +116,7 @@ axios.post<{ auth: { user: User } }>(
|
||||
|
||||
cachedValue.data = data;
|
||||
|
||||
// This returned value will be returned in next calls to the cache.
|
||||
return cachedValue;
|
||||
}
|
||||
}
|
||||
|
||||
6
src/cache/cache.ts
vendored
6
src/cache/cache.ts
vendored
@ -60,9 +60,13 @@ export type CacheProperties<R = unknown, D = unknown> = {
|
||||
*
|
||||
* The id used is the same as the id on `CacheRequestConfig['id']`, auto-generated or not.
|
||||
*
|
||||
* **Using a function instead of an object is supported but not recommended, as it's
|
||||
* better to just consume the response normally and write your own code after it. But
|
||||
* it`s here in case you need it.**
|
||||
*
|
||||
* @default {{}}
|
||||
*/
|
||||
update: Record<string, CacheUpdater<R, D>>;
|
||||
update: CacheUpdater<R, D>;
|
||||
|
||||
/**
|
||||
* If the request should handle `ETag` and `If-None-Match` support. Use a string to
|
||||
|
||||
@ -35,13 +35,6 @@ export type KeyGenerator<R = unknown, D = unknown> = (
|
||||
|
||||
export type MaybePromise<T> = T | Promise<T> | PromiseLike<T>;
|
||||
|
||||
export type CacheUpdater<R, D> =
|
||||
| 'delete'
|
||||
| ((
|
||||
cached: Exclude<StorageValue, LoadingStorageValue>,
|
||||
response: CacheAxiosResponse<R, D>
|
||||
) => MaybePromise<CachedStorageValue | 'delete' | 'ignore'>);
|
||||
|
||||
/**
|
||||
* You can use a `number` to ensure an max time (in seconds) that the cache can be reused.
|
||||
*
|
||||
@ -58,3 +51,29 @@ export type StaleIfErrorPredicate<R, D> =
|
||||
cache: LoadingStorageValue & { previous: 'stale' },
|
||||
error: Record<string, unknown>
|
||||
) => MaybePromise<number | boolean>);
|
||||
|
||||
export type CacheUpdaterFn<R, D> = (
|
||||
response: CacheAxiosResponse<R, D>
|
||||
) => MaybePromise<void>;
|
||||
|
||||
/**
|
||||
* A record for a custom cache updater for each specified request id.
|
||||
*
|
||||
* `delete` -> Deletes the request cache `predicate()` -> Determines if the cache can be
|
||||
* reused, deleted or modified.
|
||||
*/
|
||||
export type CacheUpdaterRecord<R, D> = {
|
||||
[requestId: string]:
|
||||
| 'delete'
|
||||
| ((
|
||||
cached: Exclude<StorageValue, LoadingStorageValue>,
|
||||
response: CacheAxiosResponse<R, D>
|
||||
) => MaybePromise<CachedStorageValue | 'delete' | 'ignore'>);
|
||||
};
|
||||
|
||||
/**
|
||||
* Updates any specified request cache by applying the response for this network call.
|
||||
*
|
||||
* You can use a function to implement your own cache updater function.
|
||||
*/
|
||||
export type CacheUpdater<R, D> = CacheUpdaterFn<R, D> | CacheUpdaterRecord<R, D>;
|
||||
|
||||
@ -3,12 +3,17 @@ import type { AxiosStorage } from '../storage/types';
|
||||
import type { CacheUpdater } from './types';
|
||||
|
||||
/** Function to update all caches, from CacheProperties.update, with the new data. */
|
||||
export async function updateCache<T, D>(
|
||||
export async function updateCache<R, D>(
|
||||
storage: AxiosStorage,
|
||||
data: CacheAxiosResponse<T, D>,
|
||||
entries: Record<string, CacheUpdater<T, D>>
|
||||
data: CacheAxiosResponse<R, D>,
|
||||
cacheUpdater: CacheUpdater<R, D>
|
||||
): Promise<void> {
|
||||
for (const [cacheKey, updater] of Object.entries(entries)) {
|
||||
// Global cache update function.
|
||||
if (typeof cacheUpdater === `function`) {
|
||||
return cacheUpdater(data);
|
||||
}
|
||||
|
||||
for (const [cacheKey, updater] of Object.entries(cacheUpdater)) {
|
||||
if (updater === 'delete') {
|
||||
await storage.remove(cacheKey, data.config);
|
||||
continue;
|
||||
|
||||
@ -2,87 +2,105 @@ import type { CachedStorageValue } from '../../src/storage/types';
|
||||
import { defaultKeyGenerator } from '../../src/util/key-generator';
|
||||
import { mockAxios } from '../mocks/axios';
|
||||
|
||||
const cacheKey = defaultKeyGenerator({ url: 'https://example.com/' });
|
||||
const cachedValue: CachedStorageValue = {
|
||||
const CACHE_KEY = defaultKeyGenerator({ url: 'https://example.com/' });
|
||||
const CACHED_VALUE: CachedStorageValue = Object.freeze({
|
||||
createdAt: Date.now(),
|
||||
state: 'cached',
|
||||
ttl: Number.MAX_SAFE_INTEGER, // never expires
|
||||
data: {
|
||||
data: 'value',
|
||||
headers: {},
|
||||
status: 200,
|
||||
statusText: '200 OK'
|
||||
}
|
||||
};
|
||||
data: { data: 'value', headers: {}, status: 200, statusText: '200 OK' }
|
||||
});
|
||||
|
||||
describe('Tests update-cache', () => {
|
||||
it('tests for delete key', async () => {
|
||||
it('tests for delete key with CacheUpdaterFn', async () => {
|
||||
const axios = mockAxios({});
|
||||
await axios.storage.set(cacheKey, cachedValue);
|
||||
await axios.storage.set(CACHE_KEY, CACHED_VALUE);
|
||||
|
||||
await axios.get('other-key', {
|
||||
cache: { update: { [cacheKey]: 'delete' } }
|
||||
cache: { update: () => axios.storage.remove(CACHE_KEY) }
|
||||
});
|
||||
|
||||
const cacheValue1 = await axios.storage.get(cacheKey);
|
||||
const cacheValue1 = await axios.storage.get(CACHE_KEY);
|
||||
expect(cacheValue1).toStrictEqual({ state: 'empty' });
|
||||
|
||||
//
|
||||
|
||||
await axios.storage.set(cacheKey, cachedValue);
|
||||
await axios.storage.set(CACHE_KEY, CACHED_VALUE);
|
||||
|
||||
await axios.get('other-key2', {
|
||||
cache: { update: () => axios.storage.remove(CACHE_KEY) }
|
||||
});
|
||||
|
||||
const cacheValue3 = await axios.storage.get(CACHE_KEY);
|
||||
expect(cacheValue3).toStrictEqual({ state: 'empty' });
|
||||
});
|
||||
|
||||
it('tests for delete key', async () => {
|
||||
const axios = mockAxios({});
|
||||
await axios.storage.set(CACHE_KEY, CACHED_VALUE);
|
||||
|
||||
await axios.get('other-key', {
|
||||
cache: { update: { [CACHE_KEY]: 'delete' } }
|
||||
});
|
||||
|
||||
const cacheValue1 = await axios.storage.get(CACHE_KEY);
|
||||
expect(cacheValue1).toStrictEqual({ state: 'empty' });
|
||||
|
||||
//
|
||||
|
||||
await axios.storage.set(CACHE_KEY, CACHED_VALUE);
|
||||
|
||||
await axios.get('other-key2', {
|
||||
cache: {
|
||||
update: {
|
||||
[cacheKey]: () => 'delete'
|
||||
[CACHE_KEY]: () => 'delete'
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const cacheValue2 = await axios.storage.get(cacheKey);
|
||||
const cacheValue2 = await axios.storage.get(CACHE_KEY);
|
||||
expect(cacheValue2).toStrictEqual({ state: 'empty' });
|
||||
|
||||
//
|
||||
|
||||
await axios.storage.set(cacheKey, cachedValue);
|
||||
await axios.storage.set(CACHE_KEY, CACHED_VALUE);
|
||||
|
||||
await axios.get('other-key3', {
|
||||
cache: { update: { [cacheKey]: () => Promise.resolve('delete') } }
|
||||
cache: { update: { [CACHE_KEY]: () => Promise.resolve('delete') } }
|
||||
});
|
||||
|
||||
const cacheValue3 = await axios.storage.get(cacheKey);
|
||||
const cacheValue3 = await axios.storage.get(CACHE_KEY);
|
||||
expect(cacheValue3).toStrictEqual({ state: 'empty' });
|
||||
});
|
||||
|
||||
it('tests for ignore key', async () => {
|
||||
const axios = mockAxios({});
|
||||
await axios.storage.set(cacheKey, cachedValue);
|
||||
await axios.storage.set(CACHE_KEY, CACHED_VALUE);
|
||||
|
||||
await axios.get('other-key', {
|
||||
cache: { update: { [cacheKey]: () => 'ignore' } }
|
||||
cache: { update: { [CACHE_KEY]: () => 'ignore' } }
|
||||
});
|
||||
|
||||
const cacheValue = await axios.storage.get(cacheKey);
|
||||
expect(cacheValue).toStrictEqual(cachedValue);
|
||||
const cacheValue = await axios.storage.get(CACHE_KEY);
|
||||
expect(cacheValue).toStrictEqual(CACHED_VALUE);
|
||||
|
||||
//
|
||||
|
||||
await axios.get('other-key2', {
|
||||
cache: { update: { [cacheKey]: async () => Promise.resolve('ignore') } }
|
||||
cache: { update: { [CACHE_KEY]: async () => Promise.resolve('ignore') } }
|
||||
});
|
||||
|
||||
const cacheValue2 = await axios.storage.get(cacheKey);
|
||||
expect(cacheValue2).toStrictEqual(cachedValue);
|
||||
const cacheValue2 = await axios.storage.get(CACHE_KEY);
|
||||
expect(cacheValue2).toStrictEqual(CACHED_VALUE);
|
||||
});
|
||||
|
||||
it('tests for new cached storage value', async () => {
|
||||
const axios = mockAxios({});
|
||||
await axios.storage.set(cacheKey, cachedValue);
|
||||
await axios.storage.set(CACHE_KEY, CACHED_VALUE);
|
||||
|
||||
await axios.get('other-key', {
|
||||
cache: {
|
||||
update: {
|
||||
[cacheKey]: (cached) => {
|
||||
[CACHE_KEY]: (cached) => {
|
||||
if (cached.state !== 'cached') {
|
||||
return 'ignore';
|
||||
}
|
||||
@ -96,28 +114,28 @@ describe('Tests update-cache', () => {
|
||||
}
|
||||
});
|
||||
|
||||
const cacheValue = await axios.storage.get(cacheKey);
|
||||
expect(cacheValue).not.toStrictEqual(cachedValue);
|
||||
const cacheValue = await axios.storage.get(CACHE_KEY);
|
||||
expect(cacheValue).not.toStrictEqual(CACHED_VALUE);
|
||||
expect(cacheValue.data?.data).toBe(1);
|
||||
});
|
||||
|
||||
it('tests updateCache with key is loading', async () => {
|
||||
const axios = mockAxios({});
|
||||
await axios.storage.set(cacheKey, { state: 'loading', previous: 'empty' });
|
||||
await axios.storage.set(CACHE_KEY, { state: 'loading', previous: 'empty' });
|
||||
|
||||
const handler = jest.fn();
|
||||
|
||||
await axios.get('other-key', {
|
||||
cache: {
|
||||
update: {
|
||||
[cacheKey]: handler
|
||||
[CACHE_KEY]: handler
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
expect(handler).not.toHaveBeenCalled();
|
||||
|
||||
const cacheValue = await axios.storage.get(cacheKey);
|
||||
const cacheValue = await axios.storage.get(CACHE_KEY);
|
||||
expect(cacheValue.state).toBe('loading');
|
||||
});
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user