mirror of https://github.com/buster-so/buster.git
create report requests on web
This commit is contained in:
parent
729468d5cd
commit
b031cec0c4
|
@ -0,0 +1,3 @@
|
|||
// Export all reports-related functionality
|
||||
export * from './requests';
|
||||
export * from './queryRequests';
|
|
@ -0,0 +1,163 @@
|
|||
import {
|
||||
QueryClient,
|
||||
type UseQueryOptions,
|
||||
useMutation,
|
||||
useQuery,
|
||||
useQueryClient
|
||||
} from '@tanstack/react-query';
|
||||
import { create } from 'mutative';
|
||||
import { useMemoizedFn } from '@/hooks';
|
||||
import { queryKeys } from '@/api/query_keys';
|
||||
import type { RustApiError } from '../errors';
|
||||
import type {
|
||||
GetReportsListResponse,
|
||||
GetReportIndividualResponse,
|
||||
UpdateReportRequest,
|
||||
UpdateReportResponse
|
||||
} from '@buster/server-shared/reports';
|
||||
import {
|
||||
getReportsList,
|
||||
getReportsList_server,
|
||||
getReportById,
|
||||
getReportById_server,
|
||||
updateReport
|
||||
} from './requests';
|
||||
|
||||
/**
|
||||
* Hook to get a list of reports
|
||||
*/
|
||||
export const useGetReportsList = (params?: Parameters<typeof getReportsList>[0]) => {
|
||||
const queryFn = useMemoizedFn(() => {
|
||||
return getReportsList(params);
|
||||
});
|
||||
|
||||
const res = useQuery({
|
||||
...queryKeys.reportsGetList(params),
|
||||
queryFn
|
||||
});
|
||||
|
||||
return {
|
||||
...res,
|
||||
data: res.data || {
|
||||
data: [],
|
||||
pagination: { page: 1, page_size: 5000, total: 0, total_pages: 0 }
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Prefetch function for reports list (server-side)
|
||||
*/
|
||||
export const prefetchGetReportsList = async (
|
||||
params?: Parameters<typeof getReportsList>[0],
|
||||
queryClientProp?: QueryClient
|
||||
) => {
|
||||
const queryClient = queryClientProp || new QueryClient();
|
||||
|
||||
await queryClient.prefetchQuery({
|
||||
...queryKeys.reportsGetList(params),
|
||||
queryFn: () => getReportsList_server(params)
|
||||
});
|
||||
|
||||
return queryClient;
|
||||
};
|
||||
|
||||
/**
|
||||
* Hook to get an individual report by ID
|
||||
*/
|
||||
export const useGetReportById = (
|
||||
reportId: string,
|
||||
options?: Omit<UseQueryOptions<GetReportIndividualResponse, RustApiError>, 'queryKey' | 'queryFn'>
|
||||
) => {
|
||||
const queryFn = useMemoizedFn(() => {
|
||||
return getReportById(reportId);
|
||||
});
|
||||
|
||||
return useQuery({
|
||||
...queryKeys.reportsGetById(reportId),
|
||||
queryFn,
|
||||
enabled: !!reportId,
|
||||
...options
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Prefetch function for individual report (server-side)
|
||||
*/
|
||||
export const prefetchGetReportById = async (reportId: string, queryClientProp?: QueryClient) => {
|
||||
const queryClient = queryClientProp || new QueryClient();
|
||||
|
||||
await queryClient.prefetchQuery({
|
||||
...queryKeys.reportsGetById(reportId),
|
||||
queryFn: () => getReportById_server(reportId)
|
||||
});
|
||||
|
||||
return queryClient;
|
||||
};
|
||||
|
||||
/**
|
||||
* Hook to update a report
|
||||
*/
|
||||
export const useUpdateReport = () => {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation<
|
||||
UpdateReportResponse,
|
||||
RustApiError,
|
||||
{ reportId: string; data: UpdateReportRequest },
|
||||
{ previousReport?: GetReportIndividualResponse }
|
||||
>({
|
||||
mutationFn: ({ reportId, data }) => updateReport(reportId, data),
|
||||
onMutate: async ({ reportId, data }) => {
|
||||
// Cancel any outgoing refetches
|
||||
await queryClient.cancelQueries({
|
||||
queryKey: queryKeys.reportsGetById(reportId).queryKey
|
||||
});
|
||||
|
||||
// Snapshot the previous value
|
||||
const previousReport = queryClient.getQueryData<GetReportIndividualResponse>(
|
||||
queryKeys.reportsGetById(reportId).queryKey
|
||||
);
|
||||
|
||||
// Optimistically update the individual report
|
||||
if (previousReport) {
|
||||
queryClient.setQueryData(
|
||||
queryKeys.reportsGetById(reportId).queryKey,
|
||||
create(previousReport, (draft) => {
|
||||
if (data.name !== undefined) draft.name = data.name;
|
||||
if (data.description !== undefined) draft.description = data.description;
|
||||
if (data.publicly_accessible !== undefined)
|
||||
draft.publicly_accessible = data.publicly_accessible;
|
||||
if (data.content !== undefined) draft.content = data.content;
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
// Return context with previous values
|
||||
return { previousReport };
|
||||
},
|
||||
onError: (_err, { reportId }, context) => {
|
||||
// If the mutation fails, use the context to roll back
|
||||
if (context?.previousReport) {
|
||||
queryClient.setQueryData(
|
||||
queryKeys.reportsGetById(reportId).queryKey,
|
||||
context.previousReport
|
||||
);
|
||||
}
|
||||
},
|
||||
onSuccess: (data, { reportId, data: updateData }) => {
|
||||
// Update the individual report cache with server response
|
||||
queryClient.setQueryData(queryKeys.reportsGetById(reportId).queryKey, data);
|
||||
|
||||
const nameChanged = updateData.name !== undefined && updateData.name !== data.name;
|
||||
|
||||
// Invalidate the list cache to ensure it's fresh
|
||||
if (nameChanged) {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: queryKeys.reportsGetList().queryKey,
|
||||
refetchType: 'all'
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
|
@ -0,0 +1,55 @@
|
|||
import { mainApiV2 } from '../instances';
|
||||
import { serverFetch } from '@/api/createServerInstance';
|
||||
import { BASE_URL_V2 } from '../config';
|
||||
import type {
|
||||
GetReportsListRequest,
|
||||
GetReportsListResponse,
|
||||
GetReportIndividualResponse,
|
||||
UpdateReportRequest,
|
||||
UpdateReportResponse
|
||||
} from '@buster/server-shared/reports';
|
||||
|
||||
/**
|
||||
* Get a list of reports with optional filters
|
||||
*/
|
||||
export const getReportsList = async (params?: GetReportsListRequest) => {
|
||||
const { page = 1, page_size = 5000, ...allParams } = params || {};
|
||||
return mainApiV2
|
||||
.get<GetReportsListResponse>('/reports', { params: { page, page_size, ...allParams } })
|
||||
.then((res) => res.data);
|
||||
};
|
||||
|
||||
/**
|
||||
* Server-side version of getReportsList
|
||||
*/
|
||||
export const getReportsList_server = async (params?: Parameters<typeof getReportsList>[0]) => {
|
||||
const { page = 1, page_size = 5000, ...allParams } = params || {};
|
||||
return await serverFetch<GetReportsListResponse>('/reports', {
|
||||
baseURL: BASE_URL_V2,
|
||||
params: { page, page_size, ...allParams }
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Get an individual report by ID
|
||||
*/
|
||||
export const getReportById = async (reportId: string) => {
|
||||
return mainApiV2.get<GetReportIndividualResponse>(`/reports/${reportId}`).then((res) => res.data);
|
||||
};
|
||||
|
||||
/**
|
||||
* Server-side version of getReportById
|
||||
*/
|
||||
export const getReportById_server = async (reportId: string) => {
|
||||
return await serverFetch<GetReportIndividualResponse>(`/reports/${reportId}`, {
|
||||
baseURL: BASE_URL_V2,
|
||||
method: 'GET'
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Update a report
|
||||
*/
|
||||
export const updateReport = async (reportId: string, data: UpdateReportRequest) => {
|
||||
return mainApiV2.put<UpdateReportResponse>(`/reports/${reportId}`, data).then((res) => res.data);
|
||||
};
|
|
@ -12,6 +12,7 @@ import { userQueryKeys } from './users';
|
|||
import { securityQueryKeys } from './security';
|
||||
import { slackQueryKeys } from './slack';
|
||||
import { dictionariesQueryKeys } from './dictionaries';
|
||||
import { reportsQueryKeys } from './reports';
|
||||
|
||||
export const queryKeys = {
|
||||
...datasetQueryKeys,
|
||||
|
@ -27,5 +28,6 @@ export const queryKeys = {
|
|||
...permissionGroupQueryKeys,
|
||||
...securityQueryKeys,
|
||||
...slackQueryKeys,
|
||||
...dictionariesQueryKeys
|
||||
...dictionariesQueryKeys,
|
||||
...reportsQueryKeys
|
||||
};
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
import { queryOptions } from '@tanstack/react-query';
|
||||
import type {
|
||||
GetReportsListResponse,
|
||||
GetReportIndividualResponse,
|
||||
GetReportsListRequest
|
||||
} from '@buster/server-shared/reports';
|
||||
|
||||
const reportsGetList = (filters?: GetReportsListRequest) =>
|
||||
queryOptions<GetReportsListResponse>({
|
||||
queryKey: ['reports', 'list', filters || { page: 1, page_size: 5000 }] as const,
|
||||
staleTime: 10 * 1000, // 10 seconds
|
||||
initialData: { data: [], pagination: { page: 1, page_size: 5000, total: 0, total_pages: 0 } },
|
||||
initialDataUpdatedAt: 0
|
||||
});
|
||||
|
||||
const reportsGetById = (reportId: string) =>
|
||||
queryOptions<GetReportIndividualResponse>({
|
||||
queryKey: ['reports', 'get', reportId] as const,
|
||||
staleTime: 60 * 1000 // 60 seconds
|
||||
});
|
||||
|
||||
export const reportsQueryKeys = {
|
||||
reportsGetList,
|
||||
reportsGetById
|
||||
};
|
|
@ -42,7 +42,8 @@ export const persistOptions: PersistQueryClientProviderProps['persistOptions'] =
|
|||
maxAge: PERSIST_TIME,
|
||||
dehydrateOptions: {
|
||||
shouldDehydrateQuery: (query) => {
|
||||
const isList = query.queryKey[1] === 'list';
|
||||
const isList =
|
||||
query.queryKey[1] === 'list' || query.queryKey[query.queryKey.length - 1] === 'list';
|
||||
return isList || ALL_PERSISTED_QUERIES.includes(query.queryHash);
|
||||
}
|
||||
},
|
||||
|
|
Loading…
Reference in New Issue