Files
supabase/apps/studio/components/interfaces/Integrations/CronJobs/CronJobsTab.useCronJobsData.ts

152 lines
5.3 KiB
TypeScript

import { keepPreviousData } from '@tanstack/react-query'
import { useMemo } from 'react'
import { CRON_JOBS_THRESHOLD } from './CronJobsTab.constants'
import type { ConnectionVars } from '@/data/common.types'
import { useCronJobsCountEstimateQuery } from '@/data/database-cron-jobs/database-cron-jobs-count-estimate-query'
import { useCronJobsCountQuery } from '@/data/database-cron-jobs/database-cron-jobs-count-query'
import {
CronJob,
useCronJobsInfiniteQuery,
} from '@/data/database-cron-jobs/database-cron-jobs-infinite-query'
import { useCronJobsMinimalInfiniteQuery } from '@/data/database-cron-jobs/database-cron-jobs-minimal-infinite-query'
import { COST_THRESHOLD_ERROR } from '@/data/sql/execute-sql-query'
import type { ResponseError } from '@/types'
// =============================================================================
// Input types
// =============================================================================
type UseCronJobsDataParams = ConnectionVars & {
searchQuery: string | null
}
// =============================================================================
// Shared state types (available in all states)
// =============================================================================
interface CronJobsGridState {
rows: Array<CronJob>
queryCost?: number
isSuccess: boolean
isLoading: boolean
error: ResponseError | null
isRefetching: boolean
isFetchingNextPage: boolean
hasNextPage: boolean
isMinimal: boolean
refetch: () => void
fetchNextPage: () => void
}
interface CronJobsCountState {
value: number | undefined
isEstimate: boolean
isLoading: boolean
}
// =============================================================================
// Result type
// =============================================================================
interface UseCronJobsDataResult {
/** State for the cron jobs grid */
grid: CronJobsGridState
/** State for the cron jobs count (exact or estimate) */
count: CronJobsCountState
}
export function useCronJobsData({
projectRef,
connectionString,
searchQuery,
}: UseCronJobsDataParams): UseCronJobsDataResult {
const isProjectReady = !!projectRef
const {
data: cronJobsData,
error: cronJobsError,
isSuccess: isCronJobsSuccess,
isLoading: isCronJobsLoading,
isRefetching: isCronJobsRefetching,
isFetchingNextPage,
hasNextPage = false,
refetch: refetchCronJobs,
fetchNextPage,
} = useCronJobsInfiniteQuery(
{ projectRef, connectionString, searchTerm: searchQuery ?? undefined },
{
placeholderData: Boolean(searchQuery) ? keepPreviousData : undefined,
staleTime: Infinity,
}
)
const useMinimalQuery = cronJobsError?.message === COST_THRESHOLD_ERROR
const {
data: cronJobsMinimalData,
error: cronJobsMinimalError,
isSuccess: isCronJobsMinimalSuccess,
isLoading: isCronJobsMinimalLoading,
isRefetching: isCronJobsMinimalRefetching,
isFetchingNextPage: isFetchingNextPageMinimal,
hasNextPage: hasNextPageMinimal = false,
refetch: refetchCronJobsMinimal,
fetchNextPage: fetchNextPageMinimal,
} = useCronJobsMinimalInfiniteQuery(
{ projectRef, connectionString, searchTerm: searchQuery ?? undefined },
{
placeholderData: Boolean(searchQuery) ? keepPreviousData : undefined,
staleTime: Infinity,
enabled: useMinimalQuery,
}
)
const cronJobs = useMemo(() => {
if (useMinimalQuery) {
return cronJobsMinimalData?.pages.flatMap((page) => page) ?? []
} else {
return cronJobsData?.pages.flatMap((page) => page) ?? []
}
}, [useMinimalQuery, cronJobsData?.pages, cronJobsMinimalData?.pages])
// Fetch count - gated on cron.job table size
// Always fetch the estimate first (it's fast since it uses pg_stat)
const {
data: estimatedCount,
isPending: isLoadingEstimatedCount,
isError: isEstimatedCountError,
} = useCronJobsCountEstimateQuery({ projectRef, connectionString }, { enabled: isProjectReady })
const hasLargeCronJobsTable =
typeof estimatedCount === 'number' && estimatedCount > CRON_JOBS_THRESHOLD
// Exact count is enabled when the cron.job table is small enough
const isCountQueryEnabled =
isProjectReady && !isLoadingEstimatedCount && !isEstimatedCountError && !hasLargeCronJobsTable
const { data: exactCount, isPending: isLoadingExactCount } = useCronJobsCountQuery(
{ projectRef, connectionString },
{ enabled: isCountQueryEnabled }
)
return {
grid: {
rows: cronJobs,
queryCost: cronJobsError?.metadata?.cost,
error: useMinimalQuery ? cronJobsMinimalError : cronJobsError,
isSuccess: useMinimalQuery ? isCronJobsMinimalSuccess : isCronJobsSuccess,
isLoading: useMinimalQuery ? isCronJobsMinimalLoading : isCronJobsLoading,
isRefetching: useMinimalQuery ? isCronJobsMinimalRefetching : isCronJobsRefetching,
isFetchingNextPage: useMinimalQuery ? isFetchingNextPageMinimal : isFetchingNextPage,
hasNextPage: useMinimalQuery ? hasNextPageMinimal : hasNextPage,
isMinimal: useMinimalQuery,
refetch: useMinimalQuery ? refetchCronJobsMinimal : refetchCronJobs,
fetchNextPage: useMinimalQuery ? fetchNextPageMinimal : fetchNextPage,
},
count: {
value: isCountQueryEnabled ? exactCount : estimatedCount,
isEstimate: !isCountQueryEnabled,
isLoading: isCountQueryEnabled ? isLoadingExactCount : isLoadingEstimatedCount,
},
}
}