mirror of
https://github.com/arthurfiorette/axios-cache-interceptor.git
synced 2025-12-08 17:36:16 +00:00
feat: buildKeyGenerator and ids with req.data by default
This commit is contained in:
parent
fa2c6e3204
commit
9379fce100
@ -18,7 +18,11 @@ See more about storages [here](pages/storages).
|
||||
|
||||
The function used to create different keys for each request. Defaults to a function that
|
||||
priorizes the id, and if not specified, a string is generated using the `method`,
|
||||
`baseURL`, `params`, and `url`.
|
||||
`baseURL`, `params`, `data` and `url`.
|
||||
|
||||
The
|
||||
[default](https://github.com/arthurfiorette/axios-cache-interceptor/blob/main/src/util/key-generator.ts)
|
||||
id generation can clarify this idea.
|
||||
|
||||
## `waiting`
|
||||
|
||||
|
||||
@ -36,3 +36,33 @@ console.log('Cache 2:', await Axios.storage.get(id2));
|
||||
The
|
||||
[default](https://github.com/arthurfiorette/axios-cache-interceptor/blob/main/src/util/key-generator.ts)
|
||||
id generation can clarify this idea.
|
||||
|
||||
## Joining requests
|
||||
|
||||
Everything that is used to treat two requests as same or not, is done by the `generateKey`
|
||||
property.
|
||||
|
||||
By default, it uses the `method`, `baseURL`, `params`, `data` and `url` properties from
|
||||
the request object into an hashcode generated by the `object-code` library.
|
||||
|
||||
You can make two "different" requests share the same cache with this property.
|
||||
|
||||
An example:
|
||||
|
||||
```js #runkit
|
||||
const axios = require('axios');
|
||||
const { setupCache, buildKeyGenerator } = require('axios-cache-interceptor');
|
||||
|
||||
const generator = buildKeyGenerator(({ headers }) => {
|
||||
// In this imaginary example, two requests will
|
||||
// be treated as the same if their x-cache-server header is the same.
|
||||
|
||||
// The result of this function, being a object or not, will be
|
||||
// hashed by `object-code` library.
|
||||
return headers?.['x-cache-server'] || 'not-set';
|
||||
});
|
||||
|
||||
const axios = mockAxios({
|
||||
generateKey: keyGenerator
|
||||
});
|
||||
```
|
||||
|
||||
@ -47,7 +47,8 @@
|
||||
"homepage": "https://axios-cache-interceptor.js.org",
|
||||
"dependencies": {
|
||||
"cache-parser": "^1.1.2",
|
||||
"fast-defer": "^1.1.3"
|
||||
"fast-defer": "^1.1.3",
|
||||
"object-code": "^1.0.1"
|
||||
},
|
||||
"resolutions": {
|
||||
"colors": "1.4.0"
|
||||
|
||||
@ -1,41 +1,70 @@
|
||||
import type { Method } from 'axios';
|
||||
import { code } from 'object-code';
|
||||
import type { CacheRequestConfig } from '../cache/axios';
|
||||
import type { KeyGenerator } from './types';
|
||||
|
||||
// Remove first and last '/' char, if present
|
||||
const SLASHES_REGEX = /^\/|\/$/g;
|
||||
|
||||
const stringifyObject = (obj?: unknown) =>
|
||||
obj !== undefined
|
||||
? JSON.stringify(obj, obj === null ? undefined : Object.keys(obj as object).sort())
|
||||
: '{}';
|
||||
/**
|
||||
* Builds an generator that received the {@link CacheRequestConfig} and should return a
|
||||
* string id for it.
|
||||
*/
|
||||
export function buildKeyGenerator<R = unknown, D = unknown>(
|
||||
hash: false,
|
||||
generator: KeyGenerator
|
||||
): KeyGenerator<R, D>;
|
||||
|
||||
export const defaultKeyGenerator: KeyGenerator = ({
|
||||
baseURL = '',
|
||||
url = '',
|
||||
method = 'get',
|
||||
params,
|
||||
data,
|
||||
id
|
||||
}) => {
|
||||
if (id) {
|
||||
return id;
|
||||
/**
|
||||
* Builds an generator that received the {@link CacheRequestConfig} and has it's return
|
||||
* value hashed by {@link code}.
|
||||
*
|
||||
* ### You can return an object that is hashed into an unique number, example:
|
||||
*
|
||||
* ```js
|
||||
* // This generator will return a hash code.
|
||||
* // The code will only be the same if url, method and data are the same.
|
||||
* const generator = buildKeyGenerator(true, ({ url, method, data }) => ({
|
||||
* url,
|
||||
* method,
|
||||
* data
|
||||
* }));
|
||||
* ```
|
||||
*/
|
||||
export function buildKeyGenerator<R = unknown, D = unknown>(
|
||||
hash: true,
|
||||
generator: (options: CacheRequestConfig<R, D>) => unknown
|
||||
): KeyGenerator<R, D>;
|
||||
|
||||
export function buildKeyGenerator<R = unknown, D = unknown>(
|
||||
hash: boolean,
|
||||
generator: (options: CacheRequestConfig<R, D>) => unknown
|
||||
): KeyGenerator<R, D> {
|
||||
return (request) => {
|
||||
if (request.id) {
|
||||
return request.id;
|
||||
}
|
||||
|
||||
// Remove trailing slashes
|
||||
request.baseURL && (request.baseURL = request.baseURL.replace(SLASHES_REGEX, ''));
|
||||
request.url && (request.url = request.url.replace(SLASHES_REGEX, ''));
|
||||
|
||||
// lowercase method
|
||||
request.method && (request.method = request.method.toLowerCase() as Method);
|
||||
|
||||
const result = generator(request) as string;
|
||||
return hash ? code(result).toString() : result;
|
||||
};
|
||||
}
|
||||
|
||||
export const defaultKeyGenerator = buildKeyGenerator(
|
||||
true,
|
||||
({ baseURL = '', url = '', method = 'get', params, data }) => {
|
||||
return {
|
||||
url: baseURL + (baseURL && url ? '/' : '') + url,
|
||||
method,
|
||||
params: params as unknown,
|
||||
data
|
||||
};
|
||||
}
|
||||
|
||||
// Remove trailing slashes
|
||||
baseURL = baseURL.replace(SLASHES_REGEX, '');
|
||||
url = url.replace(SLASHES_REGEX, '');
|
||||
|
||||
return `${
|
||||
// method
|
||||
method.toLowerCase()
|
||||
}::${
|
||||
// complete url
|
||||
baseURL + (baseURL && url ? '/' : '') + url
|
||||
}::${
|
||||
// query
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
||||
stringifyObject(params)
|
||||
}::${
|
||||
// request body
|
||||
stringifyObject(data)
|
||||
}`;
|
||||
};
|
||||
);
|
||||
|
||||
@ -29,7 +29,7 @@ export type CachePredicateObject<R = unknown, D = unknown> = {
|
||||
};
|
||||
|
||||
/** A simple function that receives a cache request config and should return a string id for it. */
|
||||
export type KeyGenerator = <R = unknown, D = unknown>(
|
||||
export type KeyGenerator<R = unknown, D = unknown> = (
|
||||
options: CacheRequestConfig<R, D>
|
||||
) => string;
|
||||
|
||||
|
||||
@ -234,4 +234,24 @@ describe('tests cache predicate object', () => {
|
||||
|
||||
expect(result).toBeDefined();
|
||||
});
|
||||
|
||||
it('request have id no matter what', async () => {
|
||||
const axios = mockAxios({
|
||||
methods: ['post'] // only post
|
||||
});
|
||||
|
||||
const req1 = await axios.post('url', { a: 1 });
|
||||
const req2 = await axios.post('url', { a: 1 });
|
||||
|
||||
const req3 = await axios.get('url-2');
|
||||
|
||||
expect(req1.id).toBeDefined();
|
||||
expect(req1.cached).toBe(false);
|
||||
|
||||
expect(req2.id).toBeDefined();
|
||||
expect(req2.cached).toBe(true);
|
||||
|
||||
expect(req3.id).toBeDefined();
|
||||
expect(req3.cached).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import { defaultKeyGenerator } from '../../src/util/key-generator';
|
||||
import { buildKeyGenerator, defaultKeyGenerator } from '../../src/util/key-generator';
|
||||
import { mockAxios } from '../mocks/axios';
|
||||
|
||||
describe('tests key generation', () => {
|
||||
it('should generate different key for and id', () => {
|
||||
@ -89,14 +90,6 @@ describe('tests key generation', () => {
|
||||
});
|
||||
|
||||
it('tests argument replacement', () => {
|
||||
const key = defaultKeyGenerator({
|
||||
baseURL: 'http://example.com',
|
||||
url: '',
|
||||
params: { a: 1, b: 2 }
|
||||
});
|
||||
|
||||
expect(key).toBe('get::http://example.com::{"a":1,"b":2}::{}');
|
||||
|
||||
const groups = [
|
||||
['http://example.com', '/http://example.com'],
|
||||
['http://example.com', '/http://example.com/'],
|
||||
@ -127,7 +120,7 @@ describe('tests key generation', () => {
|
||||
defaultKeyGenerator({ ...def, data: undefined })
|
||||
];
|
||||
|
||||
expect(dataProps).toStrictEqual([...new Set(dataProps)]);
|
||||
expect(new Set(dataProps).size).toBe(dataProps.length);
|
||||
|
||||
const paramsProps = [
|
||||
defaultKeyGenerator({ ...def, params: 23 }),
|
||||
@ -135,10 +128,74 @@ describe('tests key generation', () => {
|
||||
defaultKeyGenerator({ ...def, params: -453 }),
|
||||
defaultKeyGenerator({ ...def, params: 'string' }),
|
||||
defaultKeyGenerator({ ...def, params: new Date() }),
|
||||
defaultKeyGenerator({ ...def, params: Symbol() }),
|
||||
defaultKeyGenerator({ ...def, params: null }),
|
||||
defaultKeyGenerator({ ...def, params: undefined })
|
||||
];
|
||||
|
||||
expect(paramsProps).toStrictEqual([...new Set(paramsProps)]);
|
||||
expect(new Set(paramsProps).size).toBe(paramsProps.length);
|
||||
});
|
||||
|
||||
it('tests buildKeyGenerator & hash: false', async () => {
|
||||
const keyGenerator = buildKeyGenerator(false, ({ headers }) => {
|
||||
return headers?.['x-req-header'] || 'not-set';
|
||||
});
|
||||
|
||||
const axios = mockAxios({ generateKey: keyGenerator });
|
||||
|
||||
const { id } = await axios.get('random-url', {
|
||||
data: Math.random(),
|
||||
headers: {
|
||||
'x-req-header': 'my-custom-id'
|
||||
}
|
||||
});
|
||||
|
||||
const { id: id2 } = await axios.get('other-url', {
|
||||
data: Math.random() * 2,
|
||||
headers: {
|
||||
'x-req-header': 'my-custom-id'
|
||||
}
|
||||
});
|
||||
|
||||
const { id: id3 } = await axios.get('other-url', {
|
||||
data: Math.random() * 2
|
||||
});
|
||||
|
||||
expect(id).toBe('my-custom-id');
|
||||
expect(id).toBe(id2);
|
||||
expect(id3).toBe('not-set');
|
||||
});
|
||||
|
||||
it('tests buildKeyGenerator & hash: true', async () => {
|
||||
const keyGenerator = buildKeyGenerator(true, ({ headers }) => {
|
||||
return headers?.['x-req-header'] || 'not-set';
|
||||
});
|
||||
|
||||
const axios = mockAxios({ generateKey: keyGenerator });
|
||||
|
||||
const { id } = await axios.get('random-url', {
|
||||
data: Math.random(),
|
||||
headers: {
|
||||
'x-req-header': 'my-custom-id'
|
||||
}
|
||||
});
|
||||
|
||||
const { id: id2 } = await axios.get('other-url', {
|
||||
data: Math.random() * 2,
|
||||
headers: {
|
||||
'x-req-header': 'my-custom-id'
|
||||
}
|
||||
});
|
||||
|
||||
const { id: id3 } = await axios.get('other-url', {
|
||||
data: Math.random() * 2
|
||||
});
|
||||
|
||||
expect(id).toBe(id2);
|
||||
expect(id).not.toBe('my-custom-id'); // hashed value
|
||||
|
||||
expect(id3).not.toBe(id);
|
||||
expect(id3).not.toBe(id2);
|
||||
expect(id3).not.toBe('not-set');
|
||||
});
|
||||
});
|
||||
|
||||
@ -1878,6 +1878,7 @@ __metadata:
|
||||
eslint-plugin-prettier: ^4.0.0
|
||||
fast-defer: ^1.1.3
|
||||
jest: ^27.4.7
|
||||
object-code: ^1.0.1
|
||||
prettier: ^2.5.1
|
||||
prettier-plugin-jsdoc: ^0.3.30
|
||||
prettier-plugin-organize-imports: ^2.3.4
|
||||
@ -4859,6 +4860,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"object-code@npm:^1.0.1":
|
||||
version: 1.0.1
|
||||
resolution: "object-code@npm:1.0.1"
|
||||
checksum: 0a914caf04b28c0baa6b7423a16475a7f6bcbdbda79dc0035e8977462abe82549b33c8135c850f06359234cd1ea0504eab3743905994dbae2ea465ff737a55de
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"once@npm:^1.3.0":
|
||||
version: 1.4.0
|
||||
resolution: "once@npm:1.4.0"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user