mirror of
https://github.com/Unleash/unleash.git
synced 2025-06-18 01:18:23 +02:00
feat: do not cache old search and filter results (#5600)
This commit is contained in:
parent
e02c252636
commit
e88beff2b2
@ -0,0 +1,67 @@
|
|||||||
|
import React, { FC } from 'react';
|
||||||
|
import { render, screen } from '@testing-library/react';
|
||||||
|
import '@testing-library/jest-dom';
|
||||||
|
import { testServerRoute, testServerSetup } from 'utils/testServer';
|
||||||
|
import { useFeatureSearch } from './useFeatureSearch';
|
||||||
|
import { useSWRConfig } from 'swr';
|
||||||
|
|
||||||
|
const server = testServerSetup();
|
||||||
|
|
||||||
|
const TestComponent: FC<{ params: { project: string } }> = ({ params }) => {
|
||||||
|
const { loading, error, features, total } = useFeatureSearch(params);
|
||||||
|
const { cache } = useSWRConfig();
|
||||||
|
|
||||||
|
if (loading) {
|
||||||
|
return <div>Loading...</div>;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
return <div>Error: {error}</div>;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<div>Features: {features.map((f) => f.name).join(', ')}</div>
|
||||||
|
<div>Total: {total}</div>
|
||||||
|
<div>Cache: {[...cache.keys()]}</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
describe('useFeatureSearch', () => {
|
||||||
|
test('should return features after loading', async () => {
|
||||||
|
testServerRoute(server, '/api/admin/search/features?project=project1', {
|
||||||
|
features: [{ name: 'Feature1' }, { name: 'Feature2' }],
|
||||||
|
total: 2,
|
||||||
|
});
|
||||||
|
|
||||||
|
render(<TestComponent params={{ project: 'project1' }} />);
|
||||||
|
|
||||||
|
await screen.findByText(/Loading.../);
|
||||||
|
|
||||||
|
await screen.findByText(/Features:/);
|
||||||
|
await screen.findByText(/Total:/);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should keep only latest cache entry', async () => {
|
||||||
|
testServerRoute(server, '/api/admin/search/features?project=project1', {
|
||||||
|
features: [{ name: 'Feature1' }],
|
||||||
|
total: 1,
|
||||||
|
});
|
||||||
|
render(<TestComponent params={{ project: 'project1' }} />);
|
||||||
|
await screen.findByText(/Features:/);
|
||||||
|
await screen.findByText(
|
||||||
|
'Cache: api/admin/search/features?project=project1',
|
||||||
|
);
|
||||||
|
|
||||||
|
testServerRoute(server, '/api/admin/search/features?project=project2', {
|
||||||
|
features: [{ name: 'Feature2' }],
|
||||||
|
total: 1,
|
||||||
|
});
|
||||||
|
render(<TestComponent params={{ project: 'project2' }} />);
|
||||||
|
await screen.findByText(/Features:/);
|
||||||
|
await screen.findByText(
|
||||||
|
'Cache: api/admin/search/features?project=project2',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
@ -1,4 +1,4 @@
|
|||||||
import useSWR, { SWRConfiguration } from 'swr';
|
import useSWR, { SWRConfiguration, useSWRConfig } from 'swr';
|
||||||
import { useCallback, useEffect } from 'react';
|
import { useCallback, useEffect } from 'react';
|
||||||
import { formatApiPath } from 'utils/formatPath';
|
import { formatApiPath } from 'utils/formatPath';
|
||||||
import handleErrorResponses from '../httpErrorResponseHandler';
|
import handleErrorResponses from '../httpErrorResponseHandler';
|
||||||
@ -24,6 +24,21 @@ const fallbackData: SearchFeaturesSchema = {
|
|||||||
total: 0,
|
total: 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const PREFIX_KEY = 'api/admin/search/features?';
|
||||||
|
|
||||||
|
/**
|
||||||
|
With dynamic search and filter parameters we want to prevent cache from growing extensively.
|
||||||
|
We only keep the latest cache key `currentKey` and remove all other entries identified
|
||||||
|
by the `clearPrefix`
|
||||||
|
*/
|
||||||
|
const useClearSWRCache = (currentKey: string, clearPrefix: string) => {
|
||||||
|
const { cache } = useSWRConfig();
|
||||||
|
const keys = [...cache.keys()];
|
||||||
|
keys.filter((key) => key !== currentKey && key.startsWith(clearPrefix)).map(
|
||||||
|
(key) => cache.delete(key),
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
const createFeatureSearch = () => {
|
const createFeatureSearch = () => {
|
||||||
const internalCache: InternalCache = {};
|
const internalCache: InternalCache = {};
|
||||||
|
|
||||||
@ -54,6 +69,7 @@ const createFeatureSearch = () => {
|
|||||||
): UseFeatureSearchOutput => {
|
): UseFeatureSearchOutput => {
|
||||||
const { KEY, fetcher } = getFeatureSearchFetcher(params);
|
const { KEY, fetcher } = getFeatureSearchFetcher(params);
|
||||||
const cacheId = params.project || '';
|
const cacheId = params.project || '';
|
||||||
|
useClearSWRCache(KEY, PREFIX_KEY);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
initCache(params.project || '');
|
initCache(params.project || '');
|
||||||
@ -103,7 +119,7 @@ const getFeatureSearchFetcher = (params: SearchFeaturesParams) => {
|
|||||||
.map(([key, value]) => [key, value.toString()]), // TODO: parsing non-string parameters
|
.map(([key, value]) => [key, value.toString()]), // TODO: parsing non-string parameters
|
||||||
),
|
),
|
||||||
).toString();
|
).toString();
|
||||||
const KEY = `api/admin/search/features?${urlSearchParams}`;
|
const KEY = `${PREFIX_KEY}${urlSearchParams}`;
|
||||||
const fetcher = () => {
|
const fetcher = () => {
|
||||||
const path = formatApiPath(KEY);
|
const path = formatApiPath(KEY);
|
||||||
return fetch(path, {
|
return fetch(path, {
|
||||||
|
Loading…
Reference in New Issue
Block a user