feat(ajax): add an option to initialize the cache interceptors even when useCache is turned off in the global options

This commit is contained in:
Martin Pool 2022-04-26 16:48:33 +02:00 committed by Thijs Louisse
parent a28686ee72
commit 56af96f1da
6 changed files with 64 additions and 18 deletions

View file

@ -0,0 +1,5 @@
---
'@lion/ajax': minor
---
Add an option "addCaching" to the Ajax config, in order to add cache interceptors when useCache is turned off. In this situation, all requests are cached proactively.

View file

@ -23,11 +23,12 @@ export class Ajax {
*/ */
constructor(config = {}) { constructor(config = {}) {
/** /**
* @type {Partial<AjaxConfig>} * @type {AjaxConfig}
* @private * @private
*/ */
this.__config = { this.__config = {
addAcceptLanguage: true, addAcceptLanguage: true,
addCaching: false,
xsrfCookieName: 'XSRF-TOKEN', xsrfCookieName: 'XSRF-TOKEN',
xsrfHeaderName: 'X-XSRF-TOKEN', xsrfHeaderName: 'X-XSRF-TOKEN',
jsonPrefix: '', jsonPrefix: '',
@ -53,7 +54,7 @@ export class Ajax {
} }
const { cacheOptions } = this.__config; const { cacheOptions } = this.__config;
if (cacheOptions?.useCache) { if (cacheOptions.useCache || this.__config.addCaching) {
const { cacheRequestInterceptor, cacheResponseInterceptor } = createCacheInterceptors( const { cacheRequestInterceptor, cacheResponseInterceptor } = createCacheInterceptors(
cacheOptions.getCacheIdentifier, cacheOptions.getCacheIdentifier,
cacheOptions, cacheOptions,
@ -65,7 +66,7 @@ export class Ajax {
/** /**
* Configures the Ajax instance * Configures the Ajax instance
* @param {Partial<AjaxConfig>} config configuration for the Ajax instance * @param {AjaxConfig} config configuration for the Ajax instance
*/ */
set options(config) { set options(config) {
this.__config = config; this.__config = config;

View file

@ -10,6 +10,15 @@ import {
isCurrentSessionId, isCurrentSessionId,
} from '../cacheManager.js'; } from '../cacheManager.js';
/**
* Tests whether the request method is supported according to the `cacheOptions`
* @param {ValidatedCacheOptions} cacheOptions
* @param {string} method
* @returns {boolean}
*/
const isMethodSupported = (cacheOptions, method) =>
cacheOptions.methods.includes(method.toLowerCase());
/** /**
* Request interceptor to return relevant cached requests * Request interceptor to return relevant cached requests
* @param {function(): string} getCacheId used to invalidate cache if identifier is changed * @param {function(): string} getCacheId used to invalidate cache if identifier is changed
@ -36,9 +45,8 @@ const createCacheRequestInterceptor =
} }
const requestId = cacheOptions.requestIdFunction(request); const requestId = cacheOptions.requestIdFunction(request);
const isMethodSupported = cacheOptions.methods.includes(request.method.toLowerCase());
if (!isMethodSupported) { if (!isMethodSupported(cacheOptions, request.method)) {
invalidateMatchingCache(requestId, cacheOptions); invalidateMatchingCache(requestId, cacheOptions);
return request; return request;
} }
@ -81,12 +89,9 @@ const createCacheResponseInterceptor =
...response.request.cacheOptions, ...response.request.cacheOptions,
}); });
if (!response.fromCache && isMethodSupported(cacheOptions, response.request.method)) {
const requestId = cacheOptions.requestIdFunction(response.request); const requestId = cacheOptions.requestIdFunction(response.request);
const isAlreadyFromCache = !!response.fromCache;
const isCacheActive = cacheOptions.useCache;
const isMethodSupported = cacheOptions.methods.includes(response.request?.method.toLowerCase());
if (!isAlreadyFromCache && isCacheActive && isMethodSupported) {
if (isCurrentSessionId(response.request.cacheSessionId)) { if (isCurrentSessionId(response.request.cacheSessionId)) {
// Cache the response // Cache the response
ajaxCache.set(requestId, response.clone()); ajaxCache.set(requestId, response.clone());
@ -95,6 +100,7 @@ const createCacheResponseInterceptor =
// Mark the pending request as resolved // Mark the pending request as resolved
pendingRequestStore.resolve(requestId); pendingRequestStore.resolve(requestId);
} }
return response; return response;
}; };

View file

@ -43,6 +43,7 @@ describe('Ajax', () => {
}; };
const expected = { const expected = {
addAcceptLanguage: true, addAcceptLanguage: true,
addCaching: false,
xsrfCookieName: 'XSRF-TOKEN', xsrfCookieName: 'XSRF-TOKEN',
xsrfHeaderName: 'X-XSRF-TOKEN', xsrfHeaderName: 'X-XSRF-TOKEN',
jsonPrefix: ")]}',", jsonPrefix: ")]}',",
@ -303,6 +304,44 @@ describe('Ajax', () => {
getCacheIdentifier = () => String(cacheId); getCacheIdentifier = () => String(cacheId);
}); });
it('does not add cache interceptors when useCache is turned off', () => {
const customAjax = new Ajax({
cacheOptions: {
maxAge: 100,
getCacheIdentifier,
},
});
expect(customAjax._requestInterceptors.length).to.equal(2);
expect(customAjax._responseInterceptors.length).to.equal(0);
});
it('adds cache interceptors when useCache is turned on', () => {
const customAjax = new Ajax({
cacheOptions: {
useCache: true,
maxAge: 100,
getCacheIdentifier,
},
});
expect(customAjax._requestInterceptors.length).to.equal(3);
expect(customAjax._responseInterceptors.length).to.equal(1);
});
it('adds cache interceptors when addCaching is turned on', () => {
const customAjax = new Ajax({
addCaching: true,
cacheOptions: {
maxAge: 100,
getCacheIdentifier,
},
});
expect(customAjax._requestInterceptors.length).to.equal(3);
expect(customAjax._responseInterceptors.length).to.equal(1);
});
describe('caching interceptors', async () => { describe('caching interceptors', async () => {
/** /**
* @type {Ajax} * @type {Ajax}

View file

@ -150,14 +150,12 @@ describe('cache interceptors', () => {
expect(fetchStub.callCount).to.equal(1); expect(fetchStub.callCount).to.equal(1);
}); });
// TODO: Check if this is the behaviour we want it('all calls are cached proactively', async () => {
it('all calls with non-default `maxAge` are cached proactively', async () => {
// Given // Given
newCacheId(); newCacheId();
addCacheInterceptors(ajax, { addCacheInterceptors(ajax, {
useCache: false, useCache: false,
maxAge: 100,
}); });
// When // When
@ -169,11 +167,7 @@ describe('cache interceptors', () => {
expect(fetchStub.callCount).to.equal(1); expect(fetchStub.callCount).to.equal(1);
// When // When
await ajax.fetch('/test', { await ajax.fetch('/test');
cacheOptions: {
useCache: true,
},
});
// Then // Then
expect(fetchStub.callCount).to.equal(2); expect(fetchStub.callCount).to.equal(2);

View file

@ -12,6 +12,7 @@ export interface LionRequestInit extends Omit<RequestInit, 'body'> {
export interface AjaxConfig { export interface AjaxConfig {
addAcceptLanguage: boolean; addAcceptLanguage: boolean;
addCaching: boolean;
xsrfCookieName: string | null; xsrfCookieName: string | null;
xsrfHeaderName: string | null; xsrfHeaderName: string | null;
cacheOptions: CacheOptionsWithIdentifier; cacheOptions: CacheOptionsWithIdentifier;