Chore/cron fixes (#37626)

* Fix query to fetch cron jobs resulting in duplicate if job previous run succeeded, but latest failed

* Fix integration tabs child label

* Add a key to the rows in the data grid.

* Fix the small loading state when clicking the create cron job button.

* Wrap try catch around getDatabaseCronJob when validating cron job name

---------

Co-authored-by: Ivan Vasilov <vasilov.ivan@gmail.com>
This commit is contained in:
Joshen LimandIvan Vasilov authored and GitHub committed 2025-08-01 15:00:42 -02:30
1 parent 5e3518ffd6
commit 310d2d575f
5 files changed
+48 -24

No files matched your search

@@ -203,6 +203,7 @@ export const CreateCronJobSheet = ({
const { project } = useProjectContext()
const { data: org } = useSelectedOrganizationQuery()
const [searchQuery] = useQueryState('search', parseAsString.withDefault(''))
const [isLoadingGetCronJob, setIsLoadingGetCronJob] = useState(false)
const isEditing = !!selectedCronJob?.jobname
const [showEnableExtensionModal, setShowEnableExtensionModal] = useState(false)
@@ -215,7 +216,8 @@ export const CreateCronJobSheet = ({
const pgNetExtensionInstalled = pgNetExtension?.installed_version != undefined
const { mutate: sendEvent } = useSendEventMutation()
const { mutate: upsertCronJob, isLoading } = useDatabaseCronJobCreateMutation()
const { mutate: upsertCronJob, isLoading: isUpserting } = useDatabaseCronJobCreateMutation()
const isLoading = isLoadingGetCronJob || isUpserting
const canToggleExtensions = useCheckPermissions(
PermissionAction.TENANT_SQL_ADMIN_WRITE,
@@ -306,18 +308,25 @@ export const CreateCronJobSheet = ({
if (!project) return console.error('Project is required')
if (!isEditing) {
const checkExistingJob = await getDatabaseCronJob({
projectRef: project.ref,
connectionString: project.connectionString,
name,
})
const nameExists = !!checkExistingJob
if (nameExists) {
return form.setError('name', {
type: 'manual',
message: 'A cron job with this name already exists',
try {
setIsLoadingGetCronJob(true)
const checkExistingJob = await getDatabaseCronJob({
projectRef: project.ref,
connectionString: project.connectionString,
name,
})
const nameExists = !!checkExistingJob
if (nameExists) {
return form.setError('name', {
type: 'manual',
message: 'A cron job with this name already exists',
})
}
} catch (error: any) {
toast.error(`Failed to validate cron job name: ${error.message}`)
} finally {
setIsLoadingGetCronJob(false)
}
}
@@ -369,6 +378,7 @@ export const CreateCronJobSheet = ({
},
}
)
setIsLoadingGetCronJob(false)
}
return (
@@ -193,9 +193,10 @@ export const CronjobsTab = () => {
}}
onScroll={handleScroll}
renderers={{
renderRow(_, props) {
renderRow(key, props) {
return (
<Row
key={props.row.jobid}
{...props}
onClick={(e) => {
const { jobid, jobname } = props.row
@@ -1,21 +1,20 @@
import { useRouter } from 'next/router'
import { PropsWithChildren, useEffect, useRef, useState } from 'react'
import { IntegrationDefinition } from 'components/interfaces/Integrations/Landing/Integrations.constants'
import { useInstalledIntegrations } from 'components/interfaces/Integrations/Landing/useInstalledIntegrations'
import { Header } from 'components/layouts/Integrations/header'
import ProjectLayout from 'components/layouts/ProjectLayout/ProjectLayout'
import AlertError from 'components/ui/AlertError'
import { ProductMenu } from 'components/ui/ProductMenu'
import { ProductMenuGroup } from 'components/ui/ProductMenu/ProductMenu.types'
import ProductMenuItem from 'components/ui/ProductMenu/ProductMenuItem'
import { useScroll } from 'framer-motion'
import { useSelectedProject } from 'hooks/misc/useSelectedProject'
import { useSelectedProject, useSelectedProjectQuery } from 'hooks/misc/useSelectedProject'
import { withAuth } from 'hooks/misc/withAuth'
import { useFlag } from 'hooks/ui/useFlag'
import { IntegrationTabs } from './tabs'
import { Menu, Separator } from 'ui'
import ProductMenuItem from 'components/ui/ProductMenu/ProductMenuItem'
import { GenericSkeletonLoader } from 'ui-patterns'
import AlertError from 'components/ui/AlertError'
import { IntegrationTabs } from './tabs'
/**
* Layout component for the Integrations section
@@ -156,7 +155,7 @@ const IntegrationTopHeaderLayout = ({ ...props }: PropsWithChildren) => {
const IntegrationsLayoutSide = ({ ...props }: PropsWithChildren) => {
const router = useRouter()
const page = router.pathname.split('/')[4]
const project = useSelectedProject()
const { data: project } = useSelectedProjectQuery()
const {
installedIntegrations: integrations,
@@ -97,7 +97,9 @@ export const IntegrationTabs = ({ scroll, isSticky }: IntegrationTabsProps) => {
>
<NavMenuItem active={true} className="flex items-center gap-2">
{tab.childIcon}
<Link href={`${tabUrl}/${childId}`}>
<Link
href={`${tabUrl}/${childId}${childLabel ? `?child-label=${childLabel}` : ''}`}
>
{childLabel ? childLabel : childId}
</Link>
</NavMenuItem>
@@ -22,6 +22,7 @@ export type CronJob = {
status: string
}
// [Joshen] Just to call out that I had AI help me with this, so please let me know if this can be optimized
const getCronJobSql = ({ searchTerm, page }: { searchTerm?: string; page: number }) =>
`
WITH latest_runs AS (
@@ -31,6 +32,17 @@ WITH latest_runs AS (
MAX(start_time) AS latest_run
FROM cron.job_run_details
GROUP BY jobid, status
), most_recent_runs AS (
SELECT
jobid,
status,
latest_run
FROM latest_runs lr1
WHERE latest_run = (
SELECT MAX(latest_run)
FROM latest_runs lr2
WHERE lr2.jobid = lr1.jobid
)
)
SELECT
job.jobid,
@@ -38,13 +50,13 @@ SELECT
job.schedule,
job.command,
job.active,
lr.latest_run,
lr.status
mr.latest_run,
mr.status
FROM
cron.job job
LEFT JOIN latest_runs lr ON job.jobid = lr.jobid
${!!searchTerm ? `WHERE job.jobname ILIKE '%${searchTerm}%'` : ''}
LEFT JOIN most_recent_runs mr ON job.jobid = mr.jobid
ORDER BY job.jobid
${!!searchTerm ? `WHERE job.jobname ILIKE '%${searchTerm}%'` : ''}
LIMIT ${CRON_JOBS_PAGE_LIMIT}
OFFSET ${page * CRON_JOBS_PAGE_LIMIT};
`.trim()