Skip to content

Commit

Permalink
feat: suupport for async CacheUpdater
Browse files Browse the repository at this point in the history
  • Loading branch information
arthurfiorette committed Jan 4, 2022
1 parent ce2f597 commit cfbd601
Show file tree
Hide file tree
Showing 7 changed files with 213 additions and 102 deletions.
15 changes: 7 additions & 8 deletions src/util/cache-predicate.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import type { AxiosResponse } from 'axios';
import type { CacheProperties } from '..';
import type { CacheAxiosResponse, CacheProperties } from '..';
import type { CachePredicateObject } from './types';

/** Returns true if the response should be cached */
export function shouldCacheResponse<R>(
response: AxiosResponse<R>,
export function shouldCacheResponse<R, D>(
response: CacheAxiosResponse<R, D>,
{ cachePredicate }: CacheProperties
) {
if (typeof cachePredicate === 'function') {
Expand All @@ -14,9 +13,9 @@ export function shouldCacheResponse<R>(
return isCachePredicateValid(response, cachePredicate);
}

export function isCachePredicateValid<R>(
response: AxiosResponse<R>,
{ statusCheck, containsHeaders, responseMatch }: CachePredicateObject
export function isCachePredicateValid<R, D>(
response: CacheAxiosResponse<R, D>,
{ statusCheck, containsHeaders, responseMatch }: CachePredicateObject<R, D>
): boolean {
if (statusCheck) {
if (typeof statusCheck === 'function') {
Expand Down Expand Up @@ -60,7 +59,7 @@ export function isCachePredicateValid<R>(
}
}

if (responseMatch && !responseMatch(response.data)) {
if (responseMatch && !responseMatch(response)) {
return false;
}

Expand Down
31 changes: 23 additions & 8 deletions src/util/types.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
import type { AxiosResponse } from 'axios';
import type { CacheRequestConfig } from '../cache/axios';
import type { CacheAxiosResponse, CacheRequestConfig } from '../cache/axios';
import type {
CachedStorageValue,
LoadingStorageValue,
StorageValue
} from '../storage/types';

export type CachePredicate =
| CachePredicateObject
| (<R>(response: AxiosResponse<R>) => boolean);
export type CachePredicate<R = any, D = any> =
| CachePredicateObject<R, D>
| (<R, D>(response: CacheAxiosResponse<R, D>) => boolean);

export type CachePredicateObject = {
export type CachePredicateObject<R = any, D = any> = {
/**
* The status predicate, if a tuple is returned, the first and seconds value means the
* interval (inclusive) accepted. Can also be a function.
Expand All @@ -19,8 +23,19 @@ export type CachePredicateObject = {
containsHeaders?: Record<string, true | string | ((header: string) => boolean)>;

/** Check if the desired response matches this predicate. */
responseMatch?: <T = any>(res: T | undefined) => boolean;
responseMatch?: (res: CacheAxiosResponse<R, D>) => boolean;
};

/** A simple function that receives a cache request config and should return a string id for it. */
export type KeyGenerator = <R>(options: CacheRequestConfig<R>) => string;
export type KeyGenerator = <R = any, D = any>(
options: CacheRequestConfig<R, D>
) => string;

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'>);
32 changes: 12 additions & 20 deletions src/util/update-cache.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,12 @@
import type {
AxiosStorage,
CachedStorageValue,
LoadingStorageValue,
StorageValue
} from '../storage/types';

export type CacheUpdater =
| 'delete'
| ((
cached: Exclude<StorageValue, LoadingStorageValue>,
newData: any
) => CachedStorageValue | void);
import type { AxiosStorage } from '..';
import type { CacheAxiosResponse } from '../cache/axios';
import type { CacheUpdater } from './types';

/** Function to update all caches, from CacheProperties.update, with the new data. */
export async function updateCache<T = any>(
export async function updateCache<T, D>(
storage: AxiosStorage,
data: T,
entries: Record<string, CacheUpdater>
data: CacheAxiosResponse<T, D>,
entries: Record<string, CacheUpdater<T, D>>
): Promise<void> {
for (const cacheKey in entries) {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
Expand All @@ -30,16 +20,18 @@ export async function updateCache<T = any>(
const oldValue = await storage.get(cacheKey);

if (oldValue.state === 'loading') {
throw new Error('cannot update the cache while loading');
continue;
}

const newValue = value(oldValue, data);
const newValue = await value(oldValue, data);

if (newValue === undefined) {
if (newValue === 'delete') {
await storage.remove(cacheKey);
continue;
}

await storage.set(cacheKey, newValue);
if (newValue !== 'ignore') {
await storage.set(cacheKey, newValue);
}
}
}
42 changes: 40 additions & 2 deletions test/util/cache-predicate.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import type { CachedStorageValue } from '../../src';
import { isCachePredicateValid } from '../../src/util/cache-predicate';
import { mockAxios } from '../mocks/axios';
import { createResponse } from '../utils';

describe('tests cache predicate object', () => {
Expand Down Expand Up @@ -94,14 +96,50 @@ describe('tests cache predicate object', () => {
});

const testStrict = isCachePredicateValid(response, {
responseMatch: (data: any) => data && data.a === true && data.b === 1
responseMatch: ({ data }) => data && data.a === true && data.b === 1
});

const testError = isCachePredicateValid(response, {
responseMatch: (data: any) => data && (data.a !== true || data.b !== 1)
responseMatch: ({ data }) => data && (data.a !== true || data.b !== 1)
});

expect(testStrict).toBeTruthy();
expect(testError).toBeFalsy();
});

it('tests generics and typescript types', () => {
() => {
const axios = mockAxios();
axios.get<{ a: boolean; b: number }>('/', {
cache: {
ttl: ({ data }) => {
return data.b;
},
cachePredicate: {
responseMatch: ({ data }) => {
return data.a;
}
},
update: {
id: (
_,
{ data: { a, b }, headers, status, statusText }
): CachedStorageValue => {
return {
state: 'cached',
ttl: Infinity,
createdAt: Date.now(),
data: {
headers,
status,
statusText,
data: { a, b }
}
};
}
}
}
});
};
});
});
26 changes: 26 additions & 0 deletions test/util/key-generator.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,4 +87,30 @@ describe('tests key generation', () => {

expect(keyABOrder).toBe(keyBAOrder);
});

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/'],
['http://example.com/', '/http://example.com'],
['http://example.com/', '/http://example.com/']
];

for (const [first, second] of groups) {
expect(defaultKeyGenerator({ url: first })).toBe(
defaultKeyGenerator({ url: second })
);
expect(defaultKeyGenerator({ baseURL: first })).toBe(
defaultKeyGenerator({ baseURL: second })
);
}
});
});

0 comments on commit cfbd601

Please sign in to comment.