1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-10-09 11:14:29 +02:00
unleash.unleash/frontend/src/hooks/api/getters/useFeatureSearch/useFeatureSearch.ts
Thomas Heartman a519cb84f5
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.
2025-09-12 12:16:58 +00:00

128 lines
3.3 KiB
TypeScript

import useSWR, { type SWRConfiguration } from 'swr';
import { useCallback, useEffect } from 'react';
import { formatApiPath } from 'utils/formatPath';
import handleErrorResponses from '../httpErrorResponseHandler.js';
import type { SearchFeaturesParams, SearchFeaturesSchema } from 'openapi';
import { useClearSWRCache } from 'hooks/useClearSWRCache';
type UseFeatureSearchOutput = {
loading: boolean;
initialLoad: boolean;
error: string;
refetch: () => void;
} & SearchFeaturesSchema;
type CacheValue = {
total: number;
initialLoad: boolean;
[key: string]: number | boolean;
};
type InternalCache = Record<string, CacheValue>;
const fallbackData: SearchFeaturesSchema = {
features: [],
total: 0,
};
const SWR_CACHE_SIZE = 10;
const PATH = 'api/admin/search/features?';
const createFeatureSearch = () => {
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: SearchFeaturesParams,
options: SWRConfiguration = {},
cachePrefix: string = '',
): UseFeatureSearchOutput => {
const { KEY, fetcher } = getFeatureSearchFetcher(params);
const swrKey = `${cachePrefix}${KEY}`;
const cacheId = params.project || '';
useClearSWRCache(swrKey, PATH, SWR_CACHE_SIZE);
useEffect(() => {
initCache(cacheId);
}, []);
const { data, error, mutate, isLoading } = useSWR<SearchFeaturesSchema>(
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 getFeatureSearchFetcher = (params: SearchFeaturesParams) => {
const urlSearchParams = new URLSearchParams(
Array.from(
Object.entries(params)
.filter(([_, value]) => !!value)
.map(([key, value]) => [key, value.toString()]), // TODO: parsing non-string parameters
),
).toString();
const KEY = `${PATH}${urlSearchParams}`;
const fetcher = () => {
const path = formatApiPath(KEY);
return fetch(path, {
method: 'GET',
})
.then(handleErrorResponses('Feature search'))
.then((res) => res.json());
};
return {
fetcher,
KEY,
};
};
export const useFeatureSearch = createFeatureSearch();