1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-05-17 01:17:29 +02:00

feat: paginated hook for applications list (#6315)

This commit is contained in:
Mateusz Kwasniewski 2024-02-22 14:37:08 +01:00 committed by GitHub
parent fb63f21d8a
commit edbd71ac15
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 61 additions and 34 deletions

View File

@ -13,12 +13,18 @@ import { IconCell } from 'component/common/Table/cells/IconCell/IconCell';
import { LinkCell } from 'component/common/Table/cells/LinkCell/LinkCell';
import { ApplicationUsageCell } from './ApplicationUsageCell/ApplicationUsageCell';
import { ApplicationSchema } from '../../../openapi';
import { NumberParam, StringParam, withDefault } from 'use-query-params';
import {
encodeQueryParams,
NumberParam,
StringParam,
withDefault,
} from 'use-query-params';
import { DEFAULT_PAGE_LIMIT } from 'hooks/api/getters/useProjectApplications/useProjectApplications';
import { usePersistentTableState } from 'hooks/usePersistentTableState';
import { createColumnHelper, useReactTable } from '@tanstack/react-table';
import { withTableState } from 'utils/withTableState';
import useLoading from 'hooks/useLoading';
import mapValues from 'lodash.mapvalues';
const renderNoApplications = () => (
<>
@ -42,9 +48,6 @@ const renderNoApplications = () => (
const columnHelper = createColumnHelper<ApplicationSchema>();
export const PaginatedApplicationList = () => {
const { applications: data, loading } = useApplications();
const total = 1000;
const stateConfig = {
offset: withDefault(NumberParam, 0),
limit: withDefault(NumberParam, DEFAULT_PAGE_LIMIT),
@ -56,11 +59,21 @@ export const PaginatedApplicationList = () => {
`applications-table`,
stateConfig,
);
const {
applications: data,
total,
loading,
} = useApplications(
mapValues(encodeQueryParams(stateConfig, tableState), (value) =>
value ? `${value}` : undefined,
),
);
const columns = useMemo(
() => [
columnHelper.accessor('icon', {
id: 'Icon',
header: () => '',
cell: ({
row: {
original: { icon },
@ -74,6 +87,7 @@ export const PaginatedApplicationList = () => {
}
/>
),
enableSorting: false,
}),
columnHelper.accessor('appName', {
header: 'Name',
@ -93,6 +107,7 @@ export const PaginatedApplicationList = () => {
columnHelper.accessor('usage', {
header: 'Project(environment)',
meta: { width: '50%' },
enableSorting: false,
cell: ({
row: { original },
}: {

View File

@ -13,12 +13,11 @@ import { ConditionallyRender } from 'component/common/ConditionallyRender/Condit
import styles from '../../strategies.module.scss';
import { TogglesLinkList } from 'component/strategies/TogglesLinkList/TogglesLinkList';
import { IStrategy, IStrategyParameter } from 'interfaces/strategy';
import { IApplication } from 'interfaces/application';
import { FeatureSchema } from 'openapi';
import { ApplicationSchema, FeatureSchema } from 'openapi';
interface IStrategyDetailsProps {
strategy: IStrategy;
applications: IApplication[];
applications: ApplicationSchema[];
toggles: FeatureSchema[];
}

View File

@ -1,40 +1,49 @@
import useSWR, { mutate, SWRConfiguration } from 'swr';
import { useState, useEffect } from 'react';
import { useEffect, useState } from 'react';
import { formatApiPath } from 'utils/formatPath';
import handleErrorResponses from '../httpErrorResponseHandler';
import { IApplication } from 'interfaces/application';
import { ApplicationsSchema, GetApplicationsParams } from '../../../../openapi';
import { useClearSWRCache } from '../../../useClearSWRCache';
const path = formatApiPath('api/admin/metrics/applications');
interface IUseApplicationsOutput {
applications: IApplication[];
interface IUseApplicationsOutput extends ApplicationsSchema {
refetchApplications: () => void;
loading: boolean;
error?: Error;
APPLICATIONS_CACHE_KEY: string;
}
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(path, {
return fetch(formatApiPath(KEY), {
method: 'GET',
})
.then(handleErrorResponses('Applications data'))
.then((res) => res.json());
};
const APPLICATIONS_CACHE_KEY = 'api/admin/metrics/applications';
const { data, error } = useSWR(APPLICATIONS_CACHE_KEY, fetcher, {
const { data, error } = useSWR(KEY, fetcher, {
...options,
});
const [loading, setLoading] = useState(!error && !data);
const refetchApplications = () => {
mutate(APPLICATIONS_CACHE_KEY);
mutate(KEY);
};
useEffect(() => {
@ -43,10 +52,10 @@ const useApplications = (
return {
applications: data?.applications || [],
total: data?.total || 0,
error,
loading,
refetchApplications,
APPLICATIONS_CACHE_KEY,
};
};

View File

@ -1,8 +1,9 @@
import useSWR, { SWRConfiguration, useSWRConfig } from 'swr';
import useSWR, { SWRConfiguration } from 'swr';
import { useCallback, useEffect } from 'react';
import { formatApiPath } from 'utils/formatPath';
import handleErrorResponses from '../httpErrorResponseHandler';
import { SearchFeaturesParams, SearchFeaturesSchema } from 'openapi';
import { useClearSWRCache } from 'hooks/useClearSWRCache';
type UseFeatureSearchOutput = {
loading: boolean;
@ -26,19 +27,6 @@ const fallbackData: SearchFeaturesSchema = {
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 internalCache: InternalCache = {};

View File

@ -6,6 +6,7 @@ import {
GetProjectApplicationsParams,
ProjectApplicationsSchema,
} from 'openapi';
import { useClearSWRCache } from 'hooks/useClearSWRCache';
type UseProjectApplicationsOutput = {
loading: boolean;
@ -66,6 +67,7 @@ const getProjectApplicationsFetcher = (
),
).toString();
const KEY = `${getPrefixKey(projectId)}${urlSearchParams}`;
useClearSWRCache(KEY, getPrefixKey(projectId));
const fetcher = () => {
const path = formatApiPath(KEY);
return fetch(path, {

View File

@ -0,0 +1,14 @@
import { useSWRConfig } from 'swr';
/**
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`
*/
export 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),
);
};