mirror of
https://github.com/zenstackhq/zenstack
synced 2026-05-24 10:08:55 +00:00
204 lines
6.1 KiB
TypeScript
204 lines
6.1 KiB
TypeScript
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
import {
|
|
useInfiniteQuery,
|
|
useMutation,
|
|
useQuery,
|
|
useQueryClient,
|
|
type MutateFunction,
|
|
type QueryClient,
|
|
type UseInfiniteQueryOptions,
|
|
type UseMutationOptions,
|
|
type UseQueryOptions,
|
|
} from '@tanstack/react-query';
|
|
import { createContext } from 'react';
|
|
import {
|
|
DEFAULT_QUERY_ENDPOINT,
|
|
FetchFn,
|
|
QUERY_KEY_PREFIX,
|
|
fetcher,
|
|
makeUrl,
|
|
marshal,
|
|
type APIContext,
|
|
} from './common';
|
|
|
|
/**
|
|
* Context for configuring react hooks.
|
|
*/
|
|
export const RequestHandlerContext = createContext<APIContext>({
|
|
endpoint: DEFAULT_QUERY_ENDPOINT,
|
|
fetch: undefined,
|
|
});
|
|
|
|
/**
|
|
* Context provider.
|
|
*/
|
|
export const Provider = RequestHandlerContext.Provider;
|
|
|
|
/**
|
|
* Creates a react-query query.
|
|
*
|
|
* @param model The name of the model under query.
|
|
* @param url The request URL.
|
|
* @param args The request args object, URL-encoded and appended as "?q=" parameter
|
|
* @param options The react-query options object
|
|
* @returns useQuery hook
|
|
*/
|
|
export function query<R>(model: string, url: string, args?: unknown, options?: UseQueryOptions<R>, fetch?: FetchFn) {
|
|
const reqUrl = makeUrl(url, args);
|
|
return useQuery<R>({
|
|
queryKey: [QUERY_KEY_PREFIX + model, url, args],
|
|
queryFn: () => fetcher<R, false>(reqUrl, undefined, fetch, false),
|
|
...options,
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Creates a react-query infinite query.
|
|
*
|
|
* @param model The name of the model under query.
|
|
* @param url The request URL.
|
|
* @param args The initial request args object, URL-encoded and appended as "?q=" parameter
|
|
* @param options The react-query infinite query options object
|
|
* @returns useInfiniteQuery hook
|
|
*/
|
|
export function infiniteQuery<R>(
|
|
model: string,
|
|
url: string,
|
|
args?: unknown,
|
|
options?: UseInfiniteQueryOptions<R>,
|
|
fetch?: FetchFn
|
|
) {
|
|
return useInfiniteQuery<R>({
|
|
queryKey: [QUERY_KEY_PREFIX + model, url, args],
|
|
queryFn: ({ pageParam }) => {
|
|
return fetcher<R, false>(makeUrl(url, pageParam ?? args), undefined, fetch, false);
|
|
},
|
|
...options,
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Creates a POST mutation with react-query.
|
|
*
|
|
* @param model The name of the model under mutation.
|
|
* @param url The request URL.
|
|
* @param options The react-query options.
|
|
* @param invalidateQueries Whether to invalidate queries after mutation.
|
|
* @returns useMutation hooks
|
|
*/
|
|
export function postMutation<T, R = any, C extends boolean = boolean, Result = C extends true ? R | undefined : R>(
|
|
model: string,
|
|
url: string,
|
|
options?: Omit<UseMutationOptions<Result, unknown, T>, 'mutationFn'>,
|
|
fetch?: FetchFn,
|
|
invalidateQueries = true,
|
|
checkReadBack?: C
|
|
) {
|
|
const queryClient = useQueryClient();
|
|
const mutationFn = (data: any) =>
|
|
fetcher<R, C>(
|
|
url,
|
|
{
|
|
method: 'POST',
|
|
headers: {
|
|
'content-type': 'application/json',
|
|
},
|
|
body: marshal(data),
|
|
},
|
|
fetch,
|
|
checkReadBack
|
|
) as Promise<Result>;
|
|
|
|
const finalOptions = mergeOptions<T, Result>(model, options, invalidateQueries, mutationFn, queryClient);
|
|
const mutation = useMutation<Result, unknown, T>(finalOptions);
|
|
return mutation;
|
|
}
|
|
|
|
/**
|
|
* Creates a PUT mutation with react-query.
|
|
*
|
|
* @param model The name of the model under mutation.
|
|
* @param url The request URL.
|
|
* @param options The react-query options.
|
|
* @param invalidateQueries Whether to invalidate queries after mutation.
|
|
* @returns useMutation hooks
|
|
*/
|
|
export function putMutation<T, R = any, C extends boolean = boolean, Result = C extends true ? R | undefined : R>(
|
|
model: string,
|
|
url: string,
|
|
options?: Omit<UseMutationOptions<Result, unknown, T>, 'mutationFn'>,
|
|
fetch?: FetchFn,
|
|
invalidateQueries = true,
|
|
checkReadBack?: C
|
|
) {
|
|
const queryClient = useQueryClient();
|
|
const mutationFn = (data: any) =>
|
|
fetcher<R, C>(
|
|
url,
|
|
{
|
|
method: 'PUT',
|
|
headers: {
|
|
'content-type': 'application/json',
|
|
},
|
|
body: marshal(data),
|
|
},
|
|
fetch,
|
|
checkReadBack
|
|
) as Promise<Result>;
|
|
|
|
const finalOptions = mergeOptions<T, Result>(model, options, invalidateQueries, mutationFn, queryClient);
|
|
const mutation = useMutation<Result, unknown, T>(finalOptions);
|
|
return mutation;
|
|
}
|
|
|
|
/**
|
|
* Creates a DELETE mutation with react-query.
|
|
*
|
|
* @param model The name of the model under mutation.
|
|
* @param url The request URL.
|
|
* @param options The react-query options.
|
|
* @param invalidateQueries Whether to invalidate queries after mutation.
|
|
* @returns useMutation hooks
|
|
*/
|
|
export function deleteMutation<T, R = any, C extends boolean = boolean, Result = C extends true ? R | undefined : R>(
|
|
model: string,
|
|
url: string,
|
|
options?: Omit<UseMutationOptions<Result, unknown, T>, 'mutationFn'>,
|
|
fetch?: FetchFn,
|
|
invalidateQueries = true,
|
|
checkReadBack?: C
|
|
) {
|
|
const queryClient = useQueryClient();
|
|
const mutationFn = (data: any) =>
|
|
fetcher<R, C>(
|
|
makeUrl(url, data),
|
|
{
|
|
method: 'DELETE',
|
|
},
|
|
fetch,
|
|
checkReadBack
|
|
) as Promise<Result>;
|
|
|
|
const finalOptions = mergeOptions<T, Result>(model, options, invalidateQueries, mutationFn, queryClient);
|
|
const mutation = useMutation<Result, unknown, T>(finalOptions);
|
|
return mutation;
|
|
}
|
|
|
|
function mergeOptions<T, R = any>(
|
|
model: string,
|
|
options: Omit<UseMutationOptions<R, unknown, T, unknown>, 'mutationFn'> | undefined,
|
|
invalidateQueries: boolean,
|
|
mutationFn: MutateFunction<R, unknown, T>,
|
|
queryClient: QueryClient
|
|
): UseMutationOptions<R, unknown, T, unknown> {
|
|
const result = { ...options, mutationFn };
|
|
if (options?.onSuccess || invalidateQueries) {
|
|
result.onSuccess = (...args) => {
|
|
if (invalidateQueries) {
|
|
queryClient.invalidateQueries([QUERY_KEY_PREFIX + model]);
|
|
}
|
|
return options?.onSuccess?.(...args);
|
|
};
|
|
}
|
|
return result;
|
|
}
|