UNPKG

ra-core

Version:

Core components of react-admin, a frontend Framework for building admin applications on top of REST services, using ES6, React

163 lines (148 loc) 4.95 kB
import { useEffect, useMemo } from 'react'; import { QueryObserverResult, useQuery, UseQueryOptions, } from '@tanstack/react-query'; import useAuthProvider from './useAuthProvider'; import useLogoutIfAccessDenied from './useLogoutIfAccessDenied'; import { useEvent } from '../util'; const emptyParams = {}; /** * Hook for getting user permissions * * Calls the authProvider.getPermissions() method using react-query. * If the authProvider returns a rejected promise, returns empty permissions. * * The return value updates according to the request state: * * - start: { isPending: true } * - success: { permissions: [any], isPending: false } * - error: { error: [error from provider], isPending: false } * * Useful to enable features based on user permissions * * @param {Object} params Any params you want to pass to the authProvider * * @returns The current auth check state. Destructure as { permissions, error, isPending, refetch }. * * @example * import { usePermissions } from 'react-admin'; * * const PostDetail = () => { * const { isPending, permissions } = usePermissions(); * if (!isPending && permissions == 'editor') { * return <PostEdit /> * } else { * return <PostShow /> * } * }; */ const usePermissions = <PermissionsType = any, ErrorType = Error>( params = emptyParams, queryParams: UsePermissionsOptions<PermissionsType, ErrorType> = { staleTime: 5 * 60 * 1000, } ): UsePermissionsResult<PermissionsType, ErrorType> => { const authProvider = useAuthProvider(); const logoutIfAccessDenied = useLogoutIfAccessDenied(); const { onSuccess, onError, onSettled, ...queryOptions } = queryParams ?? {}; const queryResult = useQuery<PermissionsType, ErrorType>({ queryKey: ['auth', 'getPermissions', params], queryFn: async ({ signal }) => { if (!authProvider || !authProvider.getPermissions) { return []; } const permissions = await authProvider.getPermissions({ ...params, signal, }); return permissions ?? null; }, ...queryOptions, }); const onSuccessEvent = useEvent(onSuccess ?? noop); const onSettledEvent = useEvent(onSettled ?? noop); const onErrorEvent = useEvent( onError ?? ((error: ErrorType) => { if (process.env.NODE_ENV === 'development') { console.error(error); } logoutIfAccessDenied(error); }) ); useEffect(() => { if (queryResult.data === undefined || queryResult.isFetching) return; onSuccessEvent(queryResult.data); }, [onSuccessEvent, queryResult.data, queryResult.isFetching]); useEffect(() => { if (queryResult.error == null || queryResult.isFetching) return; onErrorEvent(queryResult.error); }, [onErrorEvent, queryResult.error, queryResult.isFetching]); useEffect(() => { if (queryResult.status === 'pending' || queryResult.isFetching) return; onSettledEvent(queryResult.data, queryResult.error); }, [ onSettledEvent, queryResult.data, queryResult.error, queryResult.status, queryResult.isFetching, ]); const result = useMemo( () => ({ ...queryResult, permissions: queryResult.data, }), [queryResult] ); return !authProvider || !authProvider.getPermissions ? (fakeQueryResult as UsePermissionsResult<PermissionsType, ErrorType>) : result; }; export default usePermissions; export interface UsePermissionsOptions<PermissionsType = any, ErrorType = Error> extends Omit< UseQueryOptions<PermissionsType, ErrorType>, 'queryKey' | 'queryFn' > { onSuccess?: (data: PermissionsType) => void; onError?: (err: ErrorType) => void; onSettled?: (data?: PermissionsType, error?: ErrorType | null) => void; } export type UsePermissionsResult< PermissionsType = any, ErrorType = Error, > = QueryObserverResult<PermissionsType, ErrorType> & { permissions: PermissionsType | undefined; }; const noop = () => {}; const fakeQueryResult = { permissions: undefined, data: undefined, dataUpdatedAt: 0, error: null, errorUpdatedAt: 0, errorUpdateCount: 0, failureCount: 0, failureReason: null, fetchStatus: 'idle', isError: false, isInitialLoading: false, isLoading: false, isLoadingError: false, isFetched: true, isFetchedAfterMount: true, isFetching: false, isPaused: false, isPlaceholderData: false, isPending: false, isRefetchError: false, isRefetching: false, isStale: false, isSuccess: true, status: 'success', refetch: () => Promise.resolve(fakeQueryResult), };