mirror of https://github.com/buster-so/buster.git
query prefetching
This commit is contained in:
parent
ca9b816c5a
commit
c57b5ce40e
|
@ -93,7 +93,7 @@ describe('Chat Query Hooks', () => {
|
|||
expect(requests.getListChats).toHaveBeenCalledWith({
|
||||
admin_view: false,
|
||||
page_token: 0,
|
||||
page_size: 3000,
|
||||
page_size: 3500,
|
||||
search: 'test'
|
||||
});
|
||||
});
|
||||
|
|
|
@ -37,14 +37,14 @@ export const useGetListChats = (
|
|||
filters?: Omit<Parameters<typeof getListChats>[0], 'page_token' | 'page_size'>
|
||||
) => {
|
||||
const filtersCompiled: Parameters<typeof getListChats>[0] = useMemo(
|
||||
() => ({ admin_view: false, page_token: 0, page_size: 3000, ...filters }),
|
||||
() => ({ admin_view: false, page_token: 0, page_size: 3500, ...filters }),
|
||||
[filters]
|
||||
);
|
||||
|
||||
const queryFn = useMemoizedFn(() => getListChats(filtersCompiled));
|
||||
|
||||
return useQuery({
|
||||
...chatQueryKeys.chatsGetList(filters),
|
||||
...chatQueryKeys.chatsGetList(filtersCompiled),
|
||||
queryFn
|
||||
});
|
||||
};
|
||||
|
@ -67,14 +67,14 @@ export const useGetListLogs = (
|
|||
filters?: Omit<Parameters<typeof getListLogs>[0], 'page_token' | 'page_size'>
|
||||
) => {
|
||||
const filtersCompiled: Parameters<typeof getListLogs>[0] = useMemo(
|
||||
() => ({ page_token: 0, page_size: 3000, ...filters }),
|
||||
() => ({ page_token: 0, page_size: 3500, ...filters }),
|
||||
[filters]
|
||||
);
|
||||
|
||||
const queryFn = useMemoizedFn(() => getListLogs(filtersCompiled));
|
||||
|
||||
return useQuery({
|
||||
...chatQueryKeys.logsGetList(filters),
|
||||
...chatQueryKeys.logsGetList(filtersCompiled),
|
||||
queryFn
|
||||
});
|
||||
};
|
||||
|
|
|
@ -44,7 +44,7 @@ describe('Chat API Requests', () => {
|
|||
|
||||
// Verify the API was called with correct parameters
|
||||
expect(mainApi.get).toHaveBeenCalledWith('/chats', {
|
||||
params: { page_token: 0, page_size: 3000 }
|
||||
params: { page_token: 0, page_size: 3500 }
|
||||
});
|
||||
|
||||
// Verify the result matches the mock data
|
||||
|
|
|
@ -9,7 +9,7 @@ export const getListChats = async (params?: {
|
|||
page_token: number;
|
||||
page_size: number;
|
||||
}): Promise<BusterChatListItem[]> => {
|
||||
const { page_token = 0, page_size = 3000 } = params || {};
|
||||
const { page_token = 0, page_size = 3500 } = params || {};
|
||||
return mainApi
|
||||
.get<BusterChatListItem[]>(`${CHATS_BASE}`, {
|
||||
params: { page_token, page_size }
|
||||
|
@ -20,7 +20,7 @@ export const getListChats = async (params?: {
|
|||
export const getListLogs = async (
|
||||
params?: Parameters<typeof getListChats>[0]
|
||||
): Promise<BusterChatListItem[]> => {
|
||||
const { page_token = 0, page_size = 3000 } = params || {};
|
||||
const { page_token = 0, page_size = 3500 } = params || {};
|
||||
return mainApi
|
||||
.get<BusterChatListItem[]>(`/logs`, {
|
||||
params: { page_token, page_size }
|
||||
|
|
|
@ -1,4 +1,10 @@
|
|||
import { useMutation, useQuery, useQueryClient, UseQueryOptions } from '@tanstack/react-query';
|
||||
import {
|
||||
QueryClient,
|
||||
useMutation,
|
||||
useQuery,
|
||||
useQueryClient,
|
||||
UseQueryOptions
|
||||
} from '@tanstack/react-query';
|
||||
import { collectionQueryKeys } from '@/api/query_keys/collection';
|
||||
import {
|
||||
collectionsGetList,
|
||||
|
@ -19,25 +25,45 @@ import { useBusterAssetsContextSelector } from '@/context/Assets/BusterAssetsPro
|
|||
import { create } from 'mutative';
|
||||
import type { BusterCollection } from '@/api/asset_interfaces/collection';
|
||||
import { RustApiError } from '../errors';
|
||||
import { isQueryStale } from '@/lib';
|
||||
|
||||
export const useGetCollectionsList = (
|
||||
filters: Omit<Parameters<typeof collectionsGetList>[0], 'page' | 'page_size'>,
|
||||
filters: Omit<Parameters<typeof collectionsGetList>[0], 'page_token' | 'page_size'>,
|
||||
options?: Omit<
|
||||
UseQueryOptions<Awaited<ReturnType<typeof collectionsGetList>>, RustApiError>,
|
||||
'queryKey' | 'queryFn' | 'initialData'
|
||||
>
|
||||
) => {
|
||||
const payload = useMemo(() => {
|
||||
return { page: 0, page_size: 3000, ...filters };
|
||||
return { ...filters, page_token: 0, page_size: 3500 };
|
||||
}, [filters]);
|
||||
|
||||
return useQuery({
|
||||
...collectionQueryKeys.collectionsGetList(filters),
|
||||
...collectionQueryKeys.collectionsGetList(payload),
|
||||
queryFn: () => collectionsGetList(payload),
|
||||
...options
|
||||
});
|
||||
};
|
||||
|
||||
export const prefetchGetCollectionsList = async (
|
||||
queryClient: QueryClient,
|
||||
params?: Parameters<typeof collectionsGetList>[0]
|
||||
) => {
|
||||
const options = collectionQueryKeys.collectionsGetList(params);
|
||||
const isStale = isQueryStale(options, queryClient);
|
||||
if (!isStale) return queryClient;
|
||||
|
||||
const lastQueryKey = options.queryKey[options.queryKey.length - 1];
|
||||
const compiledParams = lastQueryKey as Parameters<typeof collectionsGetList>[0];
|
||||
|
||||
await queryClient.prefetchQuery({
|
||||
...options,
|
||||
queryFn: () => collectionsGetList(compiledParams)
|
||||
});
|
||||
|
||||
return queryClient;
|
||||
};
|
||||
|
||||
const useFetchCollection = () => {
|
||||
const getAssetPassword = useBusterAssetsContextSelector((state) => state.getAssetPassword);
|
||||
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import { ShareRole } from '@/api/asset_interfaces';
|
||||
import type { BusterCollection, BusterCollectionListItem } from '@/api/asset_interfaces/collection';
|
||||
import {
|
||||
ShareDeleteRequest,
|
||||
|
@ -10,7 +9,7 @@ import type { ShareAssetType } from '@/api/asset_interfaces';
|
|||
|
||||
export const collectionsGetList = async (params: {
|
||||
/** Current page number (1-based indexing) */
|
||||
page: number;
|
||||
page_token: number;
|
||||
/** Number of items to display per page */
|
||||
page_size: number;
|
||||
/** When true, returns only collections shared with the current user */
|
||||
|
|
|
@ -1,4 +1,10 @@
|
|||
import { useMutation, useQuery, useQueryClient, UseQueryOptions } from '@tanstack/react-query';
|
||||
import {
|
||||
QueryClient,
|
||||
useMutation,
|
||||
useQuery,
|
||||
useQueryClient,
|
||||
UseQueryOptions
|
||||
} from '@tanstack/react-query';
|
||||
import {
|
||||
dashboardsGetList,
|
||||
dashboardsCreateDashboard,
|
||||
|
@ -37,6 +43,7 @@ import { useGetLatestMetricVersionMemoized } from '../metrics';
|
|||
import { useBusterAssetsContextSelector } from '@/context/Assets/BusterAssetsProvider';
|
||||
import last from 'lodash/last';
|
||||
import { createDashboardFullConfirmModal } from './confirmModals';
|
||||
import { isQueryStale } from '@/lib';
|
||||
|
||||
export const useGetDashboard = <TData = BusterDashboardResponse>(
|
||||
{
|
||||
|
@ -700,13 +707,29 @@ export const useGetDashboardsList = (
|
|||
return {
|
||||
...params,
|
||||
page_token: 0,
|
||||
page_size: 3000
|
||||
page_size: 3500
|
||||
};
|
||||
}, [params]);
|
||||
|
||||
return useQuery({
|
||||
...dashboardQueryKeys.dashboardGetList(params),
|
||||
...dashboardQueryKeys.dashboardGetList(filters),
|
||||
queryFn: () => dashboardsGetList(filters),
|
||||
...options
|
||||
});
|
||||
};
|
||||
|
||||
export const prefetchGetDashboardsList = async (
|
||||
queryClient: QueryClient,
|
||||
params?: Parameters<typeof dashboardsGetList>[0]
|
||||
) => {
|
||||
const options = dashboardQueryKeys.dashboardGetList(params);
|
||||
const isStale = isQueryStale(options, queryClient);
|
||||
if (!isStale) return queryClient;
|
||||
|
||||
const lastQueryKey = options.queryKey[options.queryKey.length - 1];
|
||||
const compiledParams = lastQueryKey as Parameters<typeof dashboardsGetList>[0];
|
||||
|
||||
await queryClient.prefetchQuery({ ...options, queryFn: () => dashboardsGetList(compiledParams) });
|
||||
|
||||
return queryClient;
|
||||
};
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
import { useMemo } from 'react';
|
||||
import { listMetrics } from './requests';
|
||||
import { useQuery, UseQueryOptions } from '@tanstack/react-query';
|
||||
import { QueryClient, useQuery, UseQueryOptions } from '@tanstack/react-query';
|
||||
import { useMemoizedFn } from '@/hooks';
|
||||
import { metricsQueryKeys } from '@/api/query_keys/metric';
|
||||
import { RustApiError } from '../errors';
|
||||
import { isQueryStale } from '@/lib';
|
||||
|
||||
export const useGetMetricsList = (
|
||||
params: Omit<Parameters<typeof listMetrics>[0], 'page_token' | 'page_size'>,
|
||||
|
@ -13,16 +14,36 @@ export const useGetMetricsList = (
|
|||
>
|
||||
) => {
|
||||
const compiledParams: Parameters<typeof listMetrics>[0] = useMemo(
|
||||
() => ({ ...params, page_token: 0, page_size: 3500 }),
|
||||
() => ({ status: [], ...params, page_token: 0, page_size: 3500 }),
|
||||
[params]
|
||||
);
|
||||
|
||||
const queryFn = useMemoizedFn(() => listMetrics(compiledParams));
|
||||
|
||||
return useQuery({
|
||||
...metricsQueryKeys.metricsGetList(params),
|
||||
...metricsQueryKeys.metricsGetList(compiledParams),
|
||||
queryFn,
|
||||
select: options?.select,
|
||||
...options
|
||||
});
|
||||
};
|
||||
|
||||
export const prefetchGetMetricsList = async (
|
||||
queryClient: QueryClient,
|
||||
params?: Parameters<typeof listMetrics>[0]
|
||||
) => {
|
||||
const options = metricsQueryKeys.metricsGetList(params);
|
||||
const isStale = isQueryStale(options, queryClient);
|
||||
if (!isStale) return queryClient;
|
||||
|
||||
const lastQueryKey = options.queryKey[options.queryKey.length - 1];
|
||||
const compiledParams = lastQueryKey as Parameters<typeof listMetrics>[0];
|
||||
|
||||
await queryClient.prefetchQuery({
|
||||
...options,
|
||||
queryFn: async () => {
|
||||
return await listMetrics(compiledParams);
|
||||
}
|
||||
});
|
||||
return queryClient;
|
||||
};
|
||||
|
|
|
@ -7,10 +7,10 @@ import { useMemoizedFn } from '@/hooks';
|
|||
import { useBusterNotifications } from '@/context/BusterNotifications';
|
||||
|
||||
export const useGetTermsList = (
|
||||
params?: Omit<Parameters<typeof getTermsList>[0], 'page' | 'page_size'>
|
||||
params?: Omit<Parameters<typeof getTermsList>[0], 'page_token' | 'page_size'>
|
||||
) => {
|
||||
const compiledParams: Parameters<typeof getTermsList>[0] = useMemo(
|
||||
() => ({ page: 0, page_size: 3000, ...params }),
|
||||
() => ({ page_token: 0, page_size: 3500, ...params }),
|
||||
[params]
|
||||
);
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@ import { BusterTerm, BusterTermListItem } from '@/api/asset_interfaces/terms';
|
|||
|
||||
export const getTermsList = async (params: {
|
||||
/** The page number to retrieve */
|
||||
page: number;
|
||||
page_token: number;
|
||||
/** The number of items per page */
|
||||
page_size: number;
|
||||
}) => {
|
||||
|
|
|
@ -29,7 +29,7 @@ const chatsGetList = (
|
|||
filters?: Omit<Parameters<typeof getListChats>[0], 'page_token' | 'page_size'>
|
||||
) =>
|
||||
queryOptions<BusterChatListItem[]>({
|
||||
queryKey: ['chats', 'list', filters || {}] as const,
|
||||
queryKey: ['chats', 'list', filters || { page_token: 0, page_size: 3500 }] as const,
|
||||
staleTime: 60 * 1000, // 1 minute
|
||||
initialData: [],
|
||||
initialDataUpdatedAt: 0
|
||||
|
@ -47,7 +47,7 @@ const logsGetList = (
|
|||
filters?: Omit<Parameters<typeof getListLogs>[0], 'page_token' | 'page_size'>
|
||||
) =>
|
||||
queryOptions<BusterChatListItem[]>({
|
||||
queryKey: ['logs', 'list', filters || {}] as const,
|
||||
queryKey: ['logs', 'list', filters || { page_token: 0, page_size: 3500 }] as const,
|
||||
staleTime: 60 * 1000, // 1 minute
|
||||
initialData: [],
|
||||
initialDataUpdatedAt: 0
|
||||
|
|
|
@ -3,10 +3,10 @@ import type { BusterCollectionListItem, BusterCollection } from '@/api/asset_int
|
|||
import { collectionsGetList as collectionsGetListRequest } from '@/api/buster_rest/collections/requests';
|
||||
|
||||
const collectionsGetList = (
|
||||
filters?: Omit<Parameters<typeof collectionsGetListRequest>[0], 'page' | 'page_size'>
|
||||
filters?: Omit<Parameters<typeof collectionsGetListRequest>[0], 'page_token' | 'page_size'>
|
||||
) =>
|
||||
queryOptions<BusterCollectionListItem[]>({
|
||||
queryKey: ['collections', 'list', filters || {}] as const,
|
||||
queryKey: ['collections', 'list', filters || { page_token: 0, page_size: 3500 }] as const,
|
||||
staleTime: 60 * 1000,
|
||||
initialData: [],
|
||||
initialDataUpdatedAt: 0
|
||||
|
|
|
@ -9,7 +9,7 @@ const dashboardGetList = (
|
|||
filters?: Omit<Parameters<typeof dashboardsGetList>[0], 'page_token' | 'page_size'>
|
||||
) =>
|
||||
queryOptions<BusterDashboardListItem[]>({
|
||||
queryKey: ['dashboard', 'list', filters || {}] as const,
|
||||
queryKey: ['dashboard', 'list', filters || { page_token: 0, page_size: 3500 }] as const,
|
||||
initialData: [],
|
||||
staleTime: 60 * 1000,
|
||||
initialDataUpdatedAt: 0
|
||||
|
|
|
@ -17,7 +17,11 @@ export const metricsGetList = (
|
|||
filters?: Omit<Parameters<typeof listMetrics>[0], 'page_token' | 'page_size'>
|
||||
) =>
|
||||
queryOptions<BusterMetricListItem[]>({
|
||||
queryKey: ['metrics', 'list', filters || { page_token: 0, page_size: 3500 }] as const,
|
||||
queryKey: [
|
||||
'metrics',
|
||||
'list',
|
||||
filters || { status: [], page_token: 0, page_size: 3500 }
|
||||
] as const,
|
||||
initialData: [],
|
||||
initialDataUpdatedAt: 0
|
||||
});
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import { Dropdown, type DropdownItem, type DropdownProps } from '@/components/ui/dropdown';
|
||||
import { AppTooltip } from '@/components/ui/tooltip';
|
||||
import { useAppLayoutContextSelector } from '@/context/BusterAppLayout';
|
||||
import { BusterRoutes, createBusterRoute } from '@/routes/busterRoutes';
|
||||
import { useMemoizedFn } from '@/hooks';
|
||||
|
|
|
@ -6,6 +6,10 @@ import { BusterRoutes, createBusterRoute } from '@/routes';
|
|||
import { BusterAppRoutes } from '@/routes/busterRoutes/busterAppRoutes';
|
||||
import { useAsyncEffect } from '@/hooks';
|
||||
import { timeout } from '@/lib';
|
||||
import { prefetchGetMetricsList } from '@/api/buster_rest/metrics';
|
||||
import { QueryClient, useQueryClient } from '@tanstack/react-query';
|
||||
import { prefetchGetDashboardsList } from '@/api/buster_rest/dashboards';
|
||||
import { prefetchGetCollectionsList } from '@/api/buster_rest/collections';
|
||||
|
||||
const HIGH_PRIORITY_ROUTES = [
|
||||
BusterRoutes.APP_HOME,
|
||||
|
@ -25,14 +29,25 @@ const LOW_PRIORITY_ROUTES = [
|
|||
BusterRoutes.APP_CHAT_ID_METRIC_ID_CHART
|
||||
];
|
||||
|
||||
const LOW_PRIORITY_PREFETCH: ((queryClient: QueryClient) => Promise<QueryClient>)[] = [
|
||||
(queryClient) => prefetchGetMetricsList(queryClient),
|
||||
(queryClient) => prefetchGetDashboardsList(queryClient),
|
||||
(queryClient) => prefetchGetCollectionsList(queryClient)
|
||||
];
|
||||
|
||||
export const RoutePrefetcher: React.FC<{}> = React.memo(() => {
|
||||
const router = useRouter();
|
||||
const queryClient = useQueryClient();
|
||||
const debounceTimerRef = useRef<NodeJS.Timeout | null>(null);
|
||||
const isPreFetchedHighPriorityRef = useRef(false);
|
||||
const isPreFetchedLowPriorityRef = useRef(false);
|
||||
|
||||
useAsyncEffect(async () => {
|
||||
const prefetchRoutes = (routes: BusterRoutes[], priority: 'high' | 'low') => {
|
||||
const prefetchRoutes = (
|
||||
routes: BusterRoutes[],
|
||||
prefetchFns: typeof LOW_PRIORITY_PREFETCH,
|
||||
priority: 'high' | 'low'
|
||||
) => {
|
||||
if (priority === 'high' && isPreFetchedHighPriorityRef.current) return;
|
||||
if (priority === 'low' && isPreFetchedLowPriorityRef.current) return;
|
||||
|
||||
|
@ -41,6 +56,10 @@ export const RoutePrefetcher: React.FC<{}> = React.memo(() => {
|
|||
router.prefetch(path);
|
||||
});
|
||||
|
||||
prefetchFns.forEach((prefetchFn) => {
|
||||
prefetchFn(queryClient);
|
||||
});
|
||||
|
||||
if (priority === 'high') {
|
||||
isPreFetchedHighPriorityRef.current = true;
|
||||
} else {
|
||||
|
@ -49,7 +68,7 @@ export const RoutePrefetcher: React.FC<{}> = React.memo(() => {
|
|||
};
|
||||
|
||||
if (!isPreFetchedHighPriorityRef.current) {
|
||||
prefetchRoutes(HIGH_PRIORITY_ROUTES, 'high');
|
||||
prefetchRoutes(HIGH_PRIORITY_ROUTES, [], 'high');
|
||||
}
|
||||
|
||||
// Wait for page load
|
||||
|
@ -72,7 +91,7 @@ export const RoutePrefetcher: React.FC<{}> = React.memo(() => {
|
|||
|
||||
// Set a new debounce timer - will trigger if no network activity for 1500ms
|
||||
debounceTimerRef.current = setTimeout(() => {
|
||||
prefetchRoutes(LOW_PRIORITY_ROUTES, 'low');
|
||||
prefetchRoutes(LOW_PRIORITY_ROUTES, LOW_PRIORITY_PREFETCH, 'low');
|
||||
observer.disconnect();
|
||||
}, 1000);
|
||||
});
|
||||
|
@ -82,7 +101,7 @@ export const RoutePrefetcher: React.FC<{}> = React.memo(() => {
|
|||
|
||||
// Fallback - ensure prefetch happens even if network is already quiet
|
||||
fallbackTimer = setTimeout(() => {
|
||||
prefetchRoutes(LOW_PRIORITY_ROUTES, 'low');
|
||||
prefetchRoutes(LOW_PRIORITY_ROUTES, LOW_PRIORITY_PREFETCH, 'low');
|
||||
observer.disconnect();
|
||||
}, 3000);
|
||||
} catch (error) {
|
||||
|
@ -92,7 +111,7 @@ export const RoutePrefetcher: React.FC<{}> = React.memo(() => {
|
|||
clearTimeout(debounceTimerRef.current);
|
||||
}
|
||||
// Still prefetch low priority routes as fallback
|
||||
prefetchRoutes(LOW_PRIORITY_ROUTES, 'low');
|
||||
prefetchRoutes(LOW_PRIORITY_ROUTES, LOW_PRIORITY_PREFETCH, 'low');
|
||||
}
|
||||
|
||||
return () => {
|
||||
|
|
|
@ -10,7 +10,7 @@ import { collectionsGetList } from '@/api/buster_rest/collections/requests';
|
|||
export const CollectionListController: React.FC = () => {
|
||||
const [openNewCollectionModal, setOpenNewCollectionModal] = useState(false);
|
||||
const [collectionListFilters, setCollectionListFilters] = useState<
|
||||
Omit<Parameters<typeof collectionsGetList>[0], 'page' | 'page_size'>
|
||||
Omit<Parameters<typeof collectionsGetList>[0], 'page_token' | 'page_size'>
|
||||
>({});
|
||||
|
||||
const { data: collectionsList, isFetched: isCollectionListFetched } =
|
||||
|
|
|
@ -16,7 +16,10 @@ import { BusterCollectionListItem } from '@/api/asset_interfaces/collection';
|
|||
import { useGetCollection } from '@/api/buster_rest/collections';
|
||||
import { collectionsGetList } from '@/api/buster_rest/collections/requests';
|
||||
|
||||
type CollectionListFilters = Omit<Parameters<typeof collectionsGetList>[0], 'page' | 'page_size'>;
|
||||
type CollectionListFilters = Omit<
|
||||
Parameters<typeof collectionsGetList>[0],
|
||||
'page_token' | 'page_size'
|
||||
>;
|
||||
type SetCollectionListFilters = (filters: CollectionListFilters) => void;
|
||||
|
||||
export const CollectionListHeader: React.FC<{
|
||||
|
|
|
@ -13,3 +13,4 @@ export * from './columnFormatter';
|
|||
export * from './colors';
|
||||
export * from './regression';
|
||||
export * from './canvas';
|
||||
export * from './query';
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
import { RustApiError } from '@/api/buster_rest/errors';
|
||||
import { QueryClient, queryOptions } from '@tanstack/react-query';
|
||||
|
||||
export const isQueryStale = (
|
||||
options: ReturnType<typeof queryOptions<any, RustApiError, any>>,
|
||||
queryClient: QueryClient
|
||||
): boolean => {
|
||||
const queryState = queryClient.getQueryState(options.queryKey);
|
||||
const updatedAt = queryState?.dataUpdatedAt;
|
||||
const staleTime =
|
||||
(options.staleTime as number) ||
|
||||
(queryClient.getDefaultOptions().queries?.staleTime as number) ||
|
||||
0;
|
||||
const isStale = updatedAt ? Date.now() - updatedAt > staleTime : true;
|
||||
|
||||
return isStale;
|
||||
};
|
Loading…
Reference in New Issue