mirror of
https://github.com/Unleash/unleash.git
synced 2025-09-19 17:52:45 +02:00
Add use change request search hook for UI. (#10664)
Adds a use change request search hook. The hook (and tests) are based closely on the `useFeatureSearch` files. I will wire them up to the table in an upcoming PR. Also: fixes the orval schema to use numbers for offset and limit instead of strings (enterprise pr incoming). Plus: updates a variable usage in the use feature search hook.
This commit is contained in:
parent
af0b3529b7
commit
a519cb84f5
@ -0,0 +1,76 @@
|
|||||||
|
import type { FC } from 'react';
|
||||||
|
import { render, screen } from '@testing-library/react';
|
||||||
|
import '@testing-library/jest-dom';
|
||||||
|
import { testServerRoute, testServerSetup } from 'utils/testServer';
|
||||||
|
import { useChangeRequestSearch } from './useChangeRequestSearch.ts';
|
||||||
|
import { useSWRConfig } from 'swr';
|
||||||
|
|
||||||
|
const server = testServerSetup();
|
||||||
|
|
||||||
|
const TestComponent: FC<{ params: { createdBy?: string; limit?: number } }> = ({
|
||||||
|
params,
|
||||||
|
}) => {
|
||||||
|
const { loading, error, changeRequests, total, refetch } =
|
||||||
|
useChangeRequestSearch(params);
|
||||||
|
const { cache } = useSWRConfig();
|
||||||
|
if (loading) {
|
||||||
|
return <div>Loading...</div>;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
return <div>Error: {error}</div>;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<button type='button' onClick={refetch}>
|
||||||
|
refetch
|
||||||
|
</button>
|
||||||
|
<div>
|
||||||
|
Change Requests:{' '}
|
||||||
|
{changeRequests.map((cr) => cr.title).join(', ')}
|
||||||
|
</div>
|
||||||
|
<div>Total: {total}</div>
|
||||||
|
<div>Cache: {[...cache.keys()]}</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
describe('useChangeRequestSearch', () => {
|
||||||
|
test('should overwrite cache total with 0 if the next result has 0 values', async () => {
|
||||||
|
const createdBy = '789';
|
||||||
|
const url = `/api/admin/search/change-requests?createdBy=${createdBy}`;
|
||||||
|
testServerRoute(server, url, {
|
||||||
|
changeRequests: [
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
title: 'Change Request 1',
|
||||||
|
createdAt: '2024-01-01T00:00:00Z',
|
||||||
|
createdBy: { id: 789, username: 'testuser' },
|
||||||
|
environment: 'production',
|
||||||
|
project: 'test-project',
|
||||||
|
features: ['feature1'],
|
||||||
|
segments: [],
|
||||||
|
state: 'Draft',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
total: 1,
|
||||||
|
});
|
||||||
|
|
||||||
|
const { rerender } = render(<TestComponent params={{ createdBy }} />);
|
||||||
|
|
||||||
|
await screen.findByText(/Total: 1/);
|
||||||
|
|
||||||
|
testServerRoute(server, url, {
|
||||||
|
changeRequests: [],
|
||||||
|
total: 0,
|
||||||
|
});
|
||||||
|
|
||||||
|
// force fetch
|
||||||
|
const button = await screen.findByRole('button', { name: 'refetch' });
|
||||||
|
button.click();
|
||||||
|
|
||||||
|
rerender(<TestComponent params={{ createdBy }} />);
|
||||||
|
await screen.findByText(/Total: 0/);
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,127 @@
|
|||||||
|
import useSWR, { type SWRConfiguration } from 'swr';
|
||||||
|
import { useCallback, useEffect } from 'react';
|
||||||
|
import { formatApiPath } from 'utils/formatPath';
|
||||||
|
import handleErrorResponses from '../httpErrorResponseHandler.js';
|
||||||
|
import type {
|
||||||
|
SearchChangeRequestsParams,
|
||||||
|
ChangeRequestSearchResponseSchema,
|
||||||
|
} from 'openapi';
|
||||||
|
import { useClearSWRCache } from 'hooks/useClearSWRCache';
|
||||||
|
|
||||||
|
type UseChangeRequestSearchOutput = {
|
||||||
|
loading: boolean;
|
||||||
|
initialLoad: boolean;
|
||||||
|
error: string;
|
||||||
|
refetch: () => void;
|
||||||
|
} & ChangeRequestSearchResponseSchema;
|
||||||
|
|
||||||
|
type CacheValue = {
|
||||||
|
total: number;
|
||||||
|
initialLoad: boolean;
|
||||||
|
[key: string]: number | boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
type InternalCache = Record<string, CacheValue>;
|
||||||
|
|
||||||
|
const fallbackData: ChangeRequestSearchResponseSchema = {
|
||||||
|
changeRequests: [],
|
||||||
|
total: 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
const SWR_CACHE_SIZE = 10;
|
||||||
|
const PATH = 'api/admin/search/change-requests?';
|
||||||
|
|
||||||
|
const createChangeRequestSearch = () => {
|
||||||
|
const internalCache: InternalCache = {};
|
||||||
|
|
||||||
|
const initCache = (id: string) => {
|
||||||
|
internalCache[id] = {
|
||||||
|
total: 0,
|
||||||
|
initialLoad: true,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const set = (id: string, key: string, value: number | boolean) => {
|
||||||
|
if (!internalCache[id]) {
|
||||||
|
initCache(id);
|
||||||
|
}
|
||||||
|
internalCache[id][key] = value;
|
||||||
|
};
|
||||||
|
|
||||||
|
const get = (id: string) => {
|
||||||
|
if (!internalCache[id]) {
|
||||||
|
initCache(id);
|
||||||
|
}
|
||||||
|
return internalCache[id];
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
params: SearchChangeRequestsParams,
|
||||||
|
options: SWRConfiguration = {},
|
||||||
|
cachePrefix: string = '',
|
||||||
|
): UseChangeRequestSearchOutput => {
|
||||||
|
const { KEY, fetcher } = getChangeRequestSearchFetcher(params);
|
||||||
|
const swrKey = `${cachePrefix}${KEY}`;
|
||||||
|
const cacheId = 'global';
|
||||||
|
useClearSWRCache(swrKey, PATH, SWR_CACHE_SIZE);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
initCache(cacheId);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const { data, error, mutate, isLoading } =
|
||||||
|
useSWR<ChangeRequestSearchResponseSchema>(swrKey, fetcher, options);
|
||||||
|
|
||||||
|
const refetch = useCallback(() => {
|
||||||
|
mutate();
|
||||||
|
}, [mutate]);
|
||||||
|
|
||||||
|
const cacheValues = get(cacheId);
|
||||||
|
|
||||||
|
if (data?.total !== undefined) {
|
||||||
|
set(cacheId, 'total', data.total);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isLoading && cacheValues.initialLoad) {
|
||||||
|
set(cacheId, 'initialLoad', false);
|
||||||
|
}
|
||||||
|
|
||||||
|
const returnData = data || fallbackData;
|
||||||
|
return {
|
||||||
|
...returnData,
|
||||||
|
loading: isLoading,
|
||||||
|
error,
|
||||||
|
refetch,
|
||||||
|
total: cacheValues.total,
|
||||||
|
initialLoad: isLoading && cacheValues.initialLoad,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const DEFAULT_PAGE_LIMIT = 25;
|
||||||
|
|
||||||
|
const getChangeRequestSearchFetcher = (params: SearchChangeRequestsParams) => {
|
||||||
|
const urlSearchParams = new URLSearchParams(
|
||||||
|
Array.from(
|
||||||
|
Object.entries(params)
|
||||||
|
.filter(([_, value]) => !!value)
|
||||||
|
.map(([key, value]) => [key, value.toString()]),
|
||||||
|
),
|
||||||
|
).toString();
|
||||||
|
const KEY = `${PATH}${urlSearchParams}`;
|
||||||
|
const fetcher = () => {
|
||||||
|
const path = formatApiPath(KEY);
|
||||||
|
return fetch(path, {
|
||||||
|
method: 'GET',
|
||||||
|
})
|
||||||
|
.then(handleErrorResponses('Change request search'))
|
||||||
|
.then((res) => res.json());
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
fetcher,
|
||||||
|
KEY,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useChangeRequestSearch = createChangeRequestSearch();
|
@ -63,7 +63,7 @@ const createFeatureSearch = () => {
|
|||||||
useClearSWRCache(swrKey, PATH, SWR_CACHE_SIZE);
|
useClearSWRCache(swrKey, PATH, SWR_CACHE_SIZE);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
initCache(params.project || '');
|
initCache(cacheId);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const { data, error, mutate, isLoading } = useSWR<SearchFeaturesSchema>(
|
const { data, error, mutate, isLoading } = useSWR<SearchFeaturesSchema>(
|
||||||
|
@ -16,9 +16,9 @@ export type SearchChangeRequestsParams = {
|
|||||||
/**
|
/**
|
||||||
* The number of change requests to skip when returning a page. By default it is set to 0.
|
* The number of change requests to skip when returning a page. By default it is set to 0.
|
||||||
*/
|
*/
|
||||||
offset?: string;
|
offset?: number;
|
||||||
/**
|
/**
|
||||||
* The number of change requests to return in a page. By default it is set to 50. The maximum is 1000.
|
* The number of change requests to return in a page. By default it is set to 50. The maximum is 1000.
|
||||||
*/
|
*/
|
||||||
limit?: string;
|
limit?: number;
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user