Skip to content

Commit

Permalink
fix: aborted requests should not clear its cache afterwards if previo…
Browse files Browse the repository at this point in the history
…us request was cached (#922)
  • Loading branch information
Wykks committed Oct 22, 2024
1 parent a7a4e31 commit de11ef5
Show file tree
Hide file tree
Showing 2 changed files with 61 additions and 7 deletions.
25 changes: 18 additions & 7 deletions src/interceptors/response.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,15 @@ export function defaultResponseInterceptor(axios: AxiosCacheInstance): ResponseI
*
* Also update the waiting list for this key by rejecting it.
*/
const rejectResponse = async (responseId: string, config: CacheRequestConfig) => {
const rejectResponse = async (
responseId: string,
config: CacheRequestConfig,
clearCache: boolean
) => {
// Updates the cache to empty to prevent infinite loading state
await axios.storage.remove(responseId, config);
if (clearCache) {
await axios.storage.remove(responseId, config);
}

// Rejects the deferred, if present
const deferred = axios.waiting.get(responseId);
Expand Down Expand Up @@ -116,7 +122,7 @@ export function defaultResponseInterceptor(axios: AxiosCacheInstance): ResponseI
!cache.data &&
!(await testCachePredicate(response, cacheConfig.cachePredicate))
) {
await rejectResponse(response.id, config);
await rejectResponse(response.id, config, true);

if (__ACI_DEV__) {
axios.debug({
Expand Down Expand Up @@ -154,7 +160,7 @@ export function defaultResponseInterceptor(axios: AxiosCacheInstance): ResponseI

// Cache should not be used
if (expirationTime === 'dont cache') {
await rejectResponse(response.id, config);
await rejectResponse(response.id, config, true);

if (__ACI_DEV__) {
axios.debug({
Expand Down Expand Up @@ -276,7 +282,7 @@ export function defaultResponseInterceptor(axios: AxiosCacheInstance): ResponseI
}

// Rejects all other requests waiting for this response
await rejectResponse(id, config);
await rejectResponse(id, config, true);

throw error;
}
Expand All @@ -297,7 +303,12 @@ export function defaultResponseInterceptor(axios: AxiosCacheInstance): ResponseI
}

// Rejects all other requests waiting for this response
await rejectResponse(id, config);
await rejectResponse(
id,
config,
// Do not clear cache if this request is cached, but the request was cancelled before returning the cached response
error.code !== 'ERR_CANCELED' || (error.code === 'ERR_CANCELED' && cache.state !== 'cached')
);

throw error;
}
Expand Down Expand Up @@ -381,7 +392,7 @@ export function defaultResponseInterceptor(axios: AxiosCacheInstance): ResponseI
}

// Rejects all other requests waiting for this response
await rejectResponse(id, config);
await rejectResponse(id, config, true);

throw error;
};
Expand Down
43 changes: 43 additions & 0 deletions test/interceptors/response.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -361,4 +361,47 @@ describe('Response Interceptor', () => {
assert.equal(storage.state, 'cached');
assert.equal(storage.data?.data, true);
});

// https://github.com/arthurfiorette/axios-cache-interceptor/issues/922
it('Aborted requests should preserve non-stale valid cache entries', async () => {
const instance = Axios.create({});
const axios = setupCache(instance, {});

const id = '1';

const cache = {
data: true,
headers: {},
status: 200,
statusText: 'Ok'
};

// Cache request
axios.storage.set(id, {
state: 'cached',
ttl: 5000,
createdAt: Date.now(),
data: cache
});

// First request cancelled immediately
const controller = new AbortController();
const cancelled = axios.get('http://unknown.url.lan:1234', { id, signal: controller.signal });
controller.abort();
try {
await cancelled;
assert.fail('should have thrown an error');
} catch (error: any) {
assert.equal(error.code, 'ERR_CANCELED');
}

// Second request cancelled after a macrotask
const controller2 = new AbortController();
const promise = axios.get('http://unknown.url.lan:1234', { id, signal: controller2.signal });
// Wait for eventual request to be sent
await new Promise((res) => setTimeout(res));
controller2.abort();
const response = await promise;
assert.ok(response.cached);
});
});

0 comments on commit de11ef5

Please sign in to comment.