mirror of
https://github.com/Unleash/unleash.git
synced 2024-12-22 19:07:54 +01:00
refactor: paginated data hook (#6333)
This commit is contained in:
parent
1633722877
commit
d1e93228a3
@ -22,8 +22,8 @@ import { LinkCell } from 'component/common/Table/cells/LinkCell/LinkCell';
|
||||
import { ProjectApplicationSchema } from '../../../openapi';
|
||||
import mapValues from 'lodash.mapvalues';
|
||||
import {
|
||||
useProjectApplications,
|
||||
DEFAULT_PAGE_LIMIT,
|
||||
useProjectApplications,
|
||||
} from 'hooks/api/getters/useProjectApplications/useProjectApplications';
|
||||
import { StringArrayCell } from 'component/common/Table/cells/StringArrayCell';
|
||||
import { SdkCell } from './SdkCell';
|
||||
@ -51,12 +51,11 @@ export const ProjectApplications = () => {
|
||||
applications = [],
|
||||
total,
|
||||
loading,
|
||||
refetch: refetchApplications,
|
||||
} = useProjectApplications(
|
||||
projectId,
|
||||
mapValues(encodeQueryParams(stateConfig, tableState), (value) =>
|
||||
value ? `${value}` : undefined,
|
||||
),
|
||||
projectId,
|
||||
);
|
||||
|
||||
const setSearchValue = (query = '') => {
|
||||
|
@ -1,62 +1,10 @@
|
||||
import useSWR, { mutate, SWRConfiguration } from 'swr';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { formatApiPath } from 'utils/formatPath';
|
||||
import handleErrorResponses from '../httpErrorResponseHandler';
|
||||
import { ApplicationsSchema, GetApplicationsParams } from '../../../../openapi';
|
||||
import { useClearSWRCache } from '../../../useClearSWRCache';
|
||||
import { ApplicationsSchema } from '../../../../openapi';
|
||||
import { createPaginatedHook } from '../usePaginatedData/usePaginatedData';
|
||||
|
||||
interface IUseApplicationsOutput extends ApplicationsSchema {
|
||||
refetchApplications: () => void;
|
||||
loading: boolean;
|
||||
error?: Error;
|
||||
}
|
||||
|
||||
const PREFIX_KEY = 'api/admin/metrics/applications?';
|
||||
|
||||
const useApplications = (
|
||||
params: GetApplicationsParams = {},
|
||||
options: SWRConfiguration = {},
|
||||
): IUseApplicationsOutput => {
|
||||
const urlSearchParams = new URLSearchParams(
|
||||
Array.from(
|
||||
Object.entries(params)
|
||||
.filter(([_, value]) => !!value)
|
||||
.map(([key, value]) => [key, value.toString()]),
|
||||
),
|
||||
).toString();
|
||||
|
||||
const KEY = `${PREFIX_KEY}${urlSearchParams}`;
|
||||
useClearSWRCache(KEY, PREFIX_KEY);
|
||||
|
||||
const fetcher = async () => {
|
||||
return fetch(formatApiPath(KEY), {
|
||||
method: 'GET',
|
||||
})
|
||||
.then(handleErrorResponses('Applications data'))
|
||||
.then((res) => res.json());
|
||||
};
|
||||
|
||||
const { data, error } = useSWR(KEY, fetcher, {
|
||||
...options,
|
||||
});
|
||||
|
||||
const [loading, setLoading] = useState(!error && !data);
|
||||
|
||||
const refetchApplications = () => {
|
||||
mutate(KEY);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
setLoading(!error && !data);
|
||||
}, [data, error]);
|
||||
|
||||
return {
|
||||
applications: data?.applications || [],
|
||||
total: data?.total || 0,
|
||||
error,
|
||||
loading,
|
||||
refetchApplications,
|
||||
};
|
||||
};
|
||||
const prefixKey = 'api/admin/metrics/applications?';
|
||||
const useApplications = createPaginatedHook<ApplicationsSchema>(
|
||||
{ applications: [], total: 0 },
|
||||
prefixKey,
|
||||
);
|
||||
|
||||
export default useApplications;
|
||||
|
@ -0,0 +1,40 @@
|
||||
import { testServerRoute, testServerSetup } from 'utils/testServer';
|
||||
import { render } from 'utils/testRenderer';
|
||||
import { screen } from '@testing-library/react';
|
||||
import { createPaginatedHook } from './usePaginatedData';
|
||||
import { FC } from 'react';
|
||||
import { http, HttpResponse } from 'msw';
|
||||
|
||||
const server = testServerSetup();
|
||||
|
||||
const usePaginatedData = createPaginatedHook<{ total: number; items: string }>(
|
||||
{ total: 0, items: 'default' },
|
||||
'/api/project/my-project?',
|
||||
);
|
||||
|
||||
const TestComponent: FC<{ query: string }> = ({ query }) => {
|
||||
const { items, total } = usePaginatedData({ query });
|
||||
|
||||
return (
|
||||
<span>
|
||||
{items} ({total})
|
||||
</span>
|
||||
);
|
||||
};
|
||||
|
||||
test('Pass query params to server and return total', async () => {
|
||||
testServerRoute(server, '/api/admin/ui-config', {});
|
||||
server.use(
|
||||
http.get('/api/project/my-project', ({ request }) => {
|
||||
const url = new URL(request.url);
|
||||
return HttpResponse.json({
|
||||
items: `result${url.searchParams.get('query')}`,
|
||||
total: 10,
|
||||
});
|
||||
}),
|
||||
);
|
||||
render(<TestComponent query='value' />);
|
||||
|
||||
await screen.findByText('default (0)');
|
||||
const element = await screen.findByText('resultvalue (10)');
|
||||
});
|
@ -0,0 +1,54 @@
|
||||
import useSWR, { SWRConfiguration } from 'swr';
|
||||
import { formatApiPath } from 'utils/formatPath';
|
||||
import handleErrorResponses from '../httpErrorResponseHandler';
|
||||
import { useClearSWRCache } from '../../../useClearSWRCache';
|
||||
|
||||
type GenericSearchOutput<T> = {
|
||||
loading: boolean;
|
||||
initialLoad: boolean;
|
||||
error: string;
|
||||
total: number;
|
||||
} & T;
|
||||
|
||||
export function createPaginatedHook<T extends { total?: number }>(
|
||||
customFallbackData: T,
|
||||
defaultPrefixKey = '',
|
||||
) {
|
||||
return (
|
||||
params: Record<string, any> = {},
|
||||
dynamicPrefixKey: string = '',
|
||||
options: SWRConfiguration = {},
|
||||
): GenericSearchOutput<T> => {
|
||||
const urlSearchParams = new URLSearchParams(
|
||||
Array.from(
|
||||
Object.entries(params)
|
||||
.filter(([_, value]) => !!value)
|
||||
.map(([key, value]) => [key, value.toString()]),
|
||||
),
|
||||
).toString();
|
||||
|
||||
const prefix = dynamicPrefixKey || defaultPrefixKey;
|
||||
const KEY = `${prefix}${urlSearchParams}`;
|
||||
useClearSWRCache(KEY, prefix);
|
||||
|
||||
const fetcher = async () => {
|
||||
return fetch(formatApiPath(KEY), {
|
||||
method: 'GET',
|
||||
})
|
||||
.then(handleErrorResponses('Paginated data'))
|
||||
.then((res) => res.json());
|
||||
};
|
||||
|
||||
const { data, error, isLoading } = useSWR(KEY, fetcher, {
|
||||
...options,
|
||||
});
|
||||
|
||||
const returnData = data || customFallbackData;
|
||||
return {
|
||||
...returnData,
|
||||
total: data?.total || 0,
|
||||
error,
|
||||
loading: isLoading,
|
||||
};
|
||||
};
|
||||
}
|
@ -1,84 +1,18 @@
|
||||
import useSWR, { SWRConfiguration } from 'swr';
|
||||
import { useCallback } from 'react';
|
||||
import { formatApiPath } from 'utils/formatPath';
|
||||
import handleErrorResponses from '../httpErrorResponseHandler';
|
||||
import {
|
||||
GetProjectApplicationsParams,
|
||||
ProjectApplicationsSchema,
|
||||
} from 'openapi';
|
||||
import { useClearSWRCache } from 'hooks/useClearSWRCache';
|
||||
import { ProjectApplicationsSchema } from 'openapi';
|
||||
import { createPaginatedHook } from '../usePaginatedData/usePaginatedData';
|
||||
|
||||
type UseProjectApplicationsOutput = {
|
||||
loading: boolean;
|
||||
error: string;
|
||||
refetch: () => void;
|
||||
} & ProjectApplicationsSchema;
|
||||
|
||||
const fallbackData: ProjectApplicationsSchema = {
|
||||
applications: [],
|
||||
total: 0,
|
||||
};
|
||||
export const DEFAULT_PAGE_LIMIT = 25;
|
||||
|
||||
const getPrefixKey = (projectId: string) => {
|
||||
return `api/admin/projects/${projectId}/applications?`;
|
||||
};
|
||||
const useParameterizedProjectApplications =
|
||||
createPaginatedHook<ProjectApplicationsSchema>({
|
||||
applications: [],
|
||||
total: 0,
|
||||
});
|
||||
|
||||
const createProjectApplications = () => {
|
||||
return (
|
||||
projectId: string,
|
||||
params: GetProjectApplicationsParams,
|
||||
options: SWRConfiguration = {},
|
||||
): UseProjectApplicationsOutput => {
|
||||
const { KEY, fetcher } = getProjectApplicationsFetcher(
|
||||
projectId,
|
||||
params,
|
||||
);
|
||||
|
||||
const { data, error, mutate, isLoading } =
|
||||
useSWR<ProjectApplicationsSchema>(KEY, fetcher, options);
|
||||
|
||||
const refetch = useCallback(() => {
|
||||
mutate();
|
||||
}, [mutate]);
|
||||
|
||||
const returnData = data || fallbackData;
|
||||
return {
|
||||
...returnData,
|
||||
loading: isLoading,
|
||||
error,
|
||||
refetch,
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
export const DEFAULT_PAGE_LIMIT = 25;
|
||||
|
||||
export const useProjectApplications = createProjectApplications();
|
||||
|
||||
const getProjectApplicationsFetcher = (
|
||||
export const useProjectApplications = (
|
||||
params: Record<string, any>,
|
||||
projectId: string,
|
||||
params: GetProjectApplicationsParams,
|
||||
) => {
|
||||
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 = `${getPrefixKey(projectId)}${urlSearchParams}`;
|
||||
useClearSWRCache(KEY, getPrefixKey(projectId));
|
||||
const fetcher = () => {
|
||||
const path = formatApiPath(KEY);
|
||||
return fetch(path, {
|
||||
method: 'GET',
|
||||
})
|
||||
.then(handleErrorResponses('Feature search'))
|
||||
.then((res) => res.json());
|
||||
};
|
||||
|
||||
return {
|
||||
fetcher,
|
||||
KEY,
|
||||
};
|
||||
};
|
||||
) => useParameterizedProjectApplications(params, getPrefixKey(projectId));
|
||||
|
Loading…
Reference in New Issue
Block a user