From 965333fbc4d9c71f73e9986710b49507562816be Mon Sep 17 00:00:00 2001 From: Aditya Choudhari Date: Fri, 20 Dec 2024 19:55:20 -0500 Subject: [PATCH 1/2] fix: Optimize release list query and use lazy loading for release cells --- apps/webservice/package.json | 1 + .../deployment-resource-drawer/TableRow.tsx | 56 ++++++- .../deployments/ReleaseEnvironmentCell.tsx | 118 ++++++++++++++ .../[systemSlug]/deployments/TableCells.tsx | 150 ++++++++++-------- .../deployments/TableDeployments.tsx | 118 ++------------ .../releases/DeploymentPageContent.tsx | 117 +++----------- packages/api/src/router/release.ts | 102 +++++------- packages/validators/src/jobs/index.ts | 1 + pnpm-lock.yaml | 40 +++-- 9 files changed, 353 insertions(+), 350 deletions(-) create mode 100644 apps/webservice/src/app/[workspaceSlug]/(app)/systems/[systemSlug]/deployments/ReleaseEnvironmentCell.tsx diff --git a/apps/webservice/package.json b/apps/webservice/package.json index d7db3e465..f83ce475a 100644 --- a/apps/webservice/package.json +++ b/apps/webservice/package.json @@ -72,6 +72,7 @@ "react-dom": "catalog:react18", "react-grid-layout": "^1.4.4", "react-hook-form": "^7.51.4", + "react-intersection-observer": "^9.14.0", "react-use": "^17.5.0", "react-use-websocket": "^4.10.1", "reactflow": "^11.11.3", diff --git a/apps/webservice/src/app/[workspaceSlug]/(app)/_components/deployment-resource-drawer/TableRow.tsx b/apps/webservice/src/app/[workspaceSlug]/(app)/_components/deployment-resource-drawer/TableRow.tsx index 41fa7a00c..97d520270 100644 --- a/apps/webservice/src/app/[workspaceSlug]/(app)/_components/deployment-resource-drawer/TableRow.tsx +++ b/apps/webservice/src/app/[workspaceSlug]/(app)/_components/deployment-resource-drawer/TableRow.tsx @@ -1,6 +1,6 @@ import type { RouterOutputs } from "@ctrlplane/api"; import type * as SCHEMA from "@ctrlplane/db/schema"; -import type { JobStatus } from "@ctrlplane/validators/jobs"; +import type { JobCondition, JobStatus } from "@ctrlplane/validators/jobs"; import { useState } from "react"; import { useParams, useRouter } from "next/navigation"; import { IconChevronRight, IconDots } from "@tabler/icons-react"; @@ -14,9 +14,15 @@ import { CollapsibleTrigger, } from "@ctrlplane/ui/collapsible"; import { TableCell, TableRow } from "@ctrlplane/ui/table"; -import { ReservedMetadataKey } from "@ctrlplane/validators/conditions"; -import { JobStatusReadable } from "@ctrlplane/validators/jobs"; +import { + ColumnOperator, + ComparisonOperator, + FilterType, + ReservedMetadataKey, +} from "@ctrlplane/validators/conditions"; +import { JobFilterType, JobStatusReadable } from "@ctrlplane/validators/jobs"; +import { api } from "~/trpc/react"; import { JobDropdownMenu } from "../../systems/[systemSlug]/deployments/[deploymentSlug]/releases/[versionId]/JobDropdownMenu"; import { DeployButton } from "../../systems/[systemSlug]/deployments/DeployButton"; import { JobLinksCell } from "../job-table/JobLinksCell"; @@ -258,9 +264,43 @@ export const ReleaseRows: React.FC = ({ deployment, resource, }) => { + const { workspaceSlug } = useParams<{ workspaceSlug: string }>(); + const { data: workspace } = api.workspace.bySlug.useQuery(workspaceSlug); + const [open, setOpen] = useState(false); - const { releaseJobTriggers } = release; - const hasOtherReleaseJobTriggers = releaseJobTriggers.length > 1; + + const isSameRelease: JobCondition = { + type: JobFilterType.Release, + operator: ColumnOperator.Equals, + value: release.id, + }; + + const isSameResource: JobCondition = { + type: JobFilterType.JobResource, + operator: ColumnOperator.Equals, + value: resource.id, + }; + + const isSameEnvironment: JobCondition = { + type: JobFilterType.Environment, + operator: ColumnOperator.Equals, + value: environment.id, + }; + + const filter: JobCondition = { + type: FilterType.Comparison, + operator: ComparisonOperator.And, + conditions: [isSameRelease, isSameResource, isSameEnvironment], + }; + + const { data: releaseJobTriggers } = + api.job.config.byWorkspaceId.list.useQuery( + { workspaceId: workspace?.id ?? "", filter }, + { enabled: workspace != null }, + ); + + const hasOtherReleaseJobTriggers = + releaseJobTriggers != null && releaseJobTriggers.length > 1; return ( @@ -270,14 +310,14 @@ export const ReleaseRows: React.FC = ({ environment={environment} deployment={deployment} resource={resource} - releaseJobTrigger={releaseJobTriggers[0]} + releaseJobTrigger={releaseJobTriggers?.[0]} isExpandable={hasOtherReleaseJobTriggers} isExpanded={open} - key={releaseJobTriggers[0]?.id ?? `${release.id}-parent`} + key={releaseJobTriggers?.[0]?.id ?? `${release.id}-parent`} /> <> - {releaseJobTriggers.map((trigger, idx) => { + {releaseJobTriggers?.map((trigger, idx) => { if (idx === 0) return null; return ( = ({ + environment, + deployment, + release, + blockedEnv, +}) => { + const { workspaceSlug, systemSlug } = useParams<{ + workspaceSlug: string; + systemSlug: string; + }>(); + + const { data: statuses, isLoading } = + api.release.status.byEnvironmentId.useQuery( + { releaseId: release.id, environmentId: environment.id }, + { refetchInterval: 2_000 }, + ); + + const { setReleaseChannelId } = useReleaseChannelDrawer(); + + if (isLoading) + return

Loading...

; + + const hasResources = environment.resources.length > 0; + const isAlreadyDeployed = statuses != null && statuses.length > 0; + + const hasJobAgent = deployment.jobAgentId != null; + const isBlockedByReleaseChannel = blockedEnv != null; + + const showRelease = isAlreadyDeployed; + const showDeployButton = + !isAlreadyDeployed && + hasJobAgent && + hasResources && + !isBlockedByReleaseChannel; + + return ( + <> + {showRelease && ( + s.job.status)} + /> + )} + + {showDeployButton && ( + + )} + + {!isAlreadyDeployed && ( +
+ {isBlockedByReleaseChannel && ( + <> + Blocked by{" "} + + + )} + {!isBlockedByReleaseChannel && !hasJobAgent && "No job agent"} + {!isBlockedByReleaseChannel && + hasJobAgent && + !hasResources && + "No resources"} +
+ )} + + ); +}; + +export const LazyReleaseEnvironmentCell: React.FC< + ReleaseEnvironmentCellProps +> = (props) => { + const { ref, inView } = useInView(); + + return ( +
+ {!inView &&

Loading...

} + {inView && } +
+ ); +}; diff --git a/apps/webservice/src/app/[workspaceSlug]/(app)/systems/[systemSlug]/deployments/TableCells.tsx b/apps/webservice/src/app/[workspaceSlug]/(app)/systems/[systemSlug]/deployments/TableCells.tsx index 87a6e3832..e33fc8757 100644 --- a/apps/webservice/src/app/[workspaceSlug]/(app)/systems/[systemSlug]/deployments/TableCells.tsx +++ b/apps/webservice/src/app/[workspaceSlug]/(app)/systems/[systemSlug]/deployments/TableCells.tsx @@ -1,9 +1,4 @@ -import type { - Deployment, - Job, - ReleaseJobTrigger, - Resource, -} from "@ctrlplane/db/schema"; +import type { JobCondition, JobStatusType } from "@ctrlplane/validators/jobs"; import Link from "next/link"; import { IconCalendarTime, @@ -12,7 +7,6 @@ import { IconCircleX, IconClock, IconExclamationMark, - IconHistoryToggle, IconLoader2, IconSettingsX, } from "@tabler/icons-react"; @@ -33,18 +27,26 @@ import { TooltipProvider, TooltipTrigger, } from "@ctrlplane/ui/tooltip"; -import { activeStatus, JobStatus } from "@ctrlplane/validators/jobs"; +import { + ColumnOperator, + ComparisonOperator, + FilterType, +} from "@ctrlplane/validators/conditions"; +import { + activeStatusType, + JobFilterType, + JobStatus, +} from "@ctrlplane/validators/jobs"; +import { api } from "~/trpc/react"; import { DeploymentBarChart } from "./DeploymentBarChart"; import { ReleaseDropdownMenu } from "./ReleaseDropdownMenu"; import { getStatusColor, statusColor } from "./status-color"; const ReleaseIcon: React.FC<{ - releaseJobTriggers: Array; -}> = ({ releaseJobTriggers }) => { - const statues = releaseJobTriggers.map((s) => s.job?.status); - - const inProgress = statues.some((s) => s === JobStatus.InProgress); + statuses: JobStatusType[]; +}> = ({ statuses }) => { + const inProgress = statuses.some((s) => s === JobStatus.InProgress); if (inProgress) return (
@@ -52,7 +54,7 @@ const ReleaseIcon: React.FC<{
); - const hasAnyFailed = statues.some((s) => s === JobStatus.Failure); + const hasAnyFailed = statuses.some((s) => s === JobStatus.Failure); if (hasAnyFailed) return (
@@ -60,22 +62,22 @@ const ReleaseIcon: React.FC<{
); - if (statues.some((s) => s === JobStatus.InvalidJobAgent)) + if (statuses.some((s) => s === JobStatus.InvalidJobAgent)) return (
); - const allPending = statues.every((s) => s === JobStatus.Pending); + const allPending = statuses.every((s) => s === JobStatus.Pending); if (allPending) return (
- +
); - const isComplete = statues.every((s) => s === JobStatus.Completed); + const isComplete = statuses.every((s) => s === JobStatus.Completed); if (isComplete) return (
@@ -83,7 +85,7 @@ const ReleaseIcon: React.FC<{
); - const isRollingOut = statues.some((s) => s === JobStatus.Completed); + const isRollingOut = statuses.some((s) => s === JobStatus.Completed); if (isRollingOut) return (
@@ -91,15 +93,7 @@ const ReleaseIcon: React.FC<{
); - const waiting = statues.some((s) => s == null); - if (waiting) - return ( -
- -
- ); - - const isCancelled = statues.some((s) => s === JobStatus.Cancelled); + const isCancelled = statuses.some((s) => s === JobStatus.Cancelled); if (isCancelled) return (
@@ -120,29 +114,50 @@ export const Release: React.FC<{ releaseId: string; environment: { id: string; name: string }; deployedAt: Date; - releaseJobTriggers: Array< - ReleaseJobTrigger & { - job: Job; - resource: Resource; - deployment?: Deployment | null; - } - >; workspaceSlug: string; systemSlug: string; deploymentSlug: string; + statuses: JobStatusType[]; }> = (props) => { const { name, deployedAt, - releaseJobTriggers, releaseId, version, environment, workspaceSlug, systemSlug, deploymentSlug, + statuses, } = props; + const isSameRelease: JobCondition = { + type: JobFilterType.Release, + operator: ColumnOperator.Equals, + value: releaseId, + }; + + const isSameEnvironment: JobCondition = { + type: JobFilterType.Environment, + operator: ColumnOperator.Equals, + value: environment.id, + }; + + const filter: JobCondition = { + type: FilterType.Comparison, + operator: ComparisonOperator.And, + conditions: [isSameRelease, isSameEnvironment], + }; + + const workspaceId = api.workspace.bySlug.useQuery(workspaceSlug); + + const releaseJobTriggersQ = api.job.config.byWorkspaceId.list.useQuery( + { workspaceId: workspaceId.data?.id ?? "", filter }, + { enabled: isPresent(workspaceId.data?.id) }, + ); + + const releaseJobTriggers = releaseJobTriggersQ.data ?? []; + const latestJobsByResource = _.chain(releaseJobTriggers) .groupBy((r) => r.resource.id) .mapValues((triggers) => @@ -165,21 +180,15 @@ export const Release: React.FC<{ [d.job, d.resource, d.job.message].every(isPresent), ); - const firstReleaseJobTrigger = releaseJobTriggers.at(0); - - const isReleaseActive = releaseJobTriggers.some((d) => - activeStatus.includes(d.job.status as JobStatus), - ); - return (
- +
@@ -202,36 +211,43 @@ export const Release: React.FC<{ -
- - {configuredWithMessages.length > 0 && ( - - {configuredWithMessages.map((d) => ( -
-
- - {d.resource.name} -
- {d.job.message != null && d.job.message !== "" && ( -
- {capitalCase(d.job.status)} — {d.job.message} + {releaseJobTriggersQ.isLoading && ( +
+ +
+ )} + {!releaseJobTriggersQ.isLoading && ( +
+ + {configuredWithMessages.length > 0 && ( + + {configuredWithMessages.map((d) => ( +
+
+ + {d.resource.name}
- )} -
- ))} -
- )} -
+ {d.job.message != null && d.job.message !== "" && ( +
+ {capitalCase(d.job.status)} — {d.job.message} +
+ )} +
+ ))} + + )} +
+ )} activeStatusType.includes(s))} />
); diff --git a/apps/webservice/src/app/[workspaceSlug]/(app)/systems/[systemSlug]/deployments/TableDeployments.tsx b/apps/webservice/src/app/[workspaceSlug]/(app)/systems/[systemSlug]/deployments/TableDeployments.tsx index 60bcb3ff9..761d46838 100644 --- a/apps/webservice/src/app/[workspaceSlug]/(app)/systems/[systemSlug]/deployments/TableDeployments.tsx +++ b/apps/webservice/src/app/[workspaceSlug]/(app)/systems/[systemSlug]/deployments/TableDeployments.tsx @@ -1,22 +1,15 @@ +"use client"; + import type { RouterOutputs } from "@ctrlplane/api"; import type { Deployment, Workspace } from "@ctrlplane/db/schema"; -import type { JobCondition } from "@ctrlplane/validators/jobs"; import Link from "next/link"; import { IconCircleFilled } from "@tabler/icons-react"; -import { isPresent } from "ts-is-present"; import { cn } from "@ctrlplane/ui"; import { Badge } from "@ctrlplane/ui/badge"; -import { - ColumnOperator, - ComparisonOperator, - FilterType, -} from "@ctrlplane/validators/conditions"; -import { JobFilterType } from "@ctrlplane/validators/jobs"; import { DeploymentOptionsDropdown } from "~/app/[workspaceSlug]/(app)/_components/DeploymentOptionsDropdown"; -import { api } from "~/trpc/server"; -import { Release } from "./TableCells"; +import { LazyReleaseEnvironmentCell } from "./ReleaseEnvironmentCell"; type Environment = RouterOutputs["environment"]["bySystemId"][number]; @@ -67,92 +60,6 @@ const EnvIcon: React.FC<{ ); }; -const ReleaseCell: React.FC<{ - workspace: Workspace; - systemSlug: string; - environment: Environment; - release: { - id: string; - name: string; - version: string; - createdAt: Date; - } | null; - deployment: Deployment; -}> = async ({ - release, - environment: env, - deployment, - workspace, - systemSlug, -}) => { - const isSameDeployment: JobCondition = { - type: JobFilterType.Deployment, - operator: ColumnOperator.Equals, - value: deployment.id, - }; - - const isSameEnvironment: JobCondition = { - type: JobFilterType.Environment, - operator: ColumnOperator.Equals, - value: env.id, - }; - - const isSameRelease: JobCondition = { - type: JobFilterType.Release, - operator: ColumnOperator.Equals, - value: release?.id ?? "", - }; - - const filter: JobCondition = { - type: FilterType.Comparison, - operator: ComparisonOperator.And, - conditions: [isSameDeployment, isSameEnvironment, isSameRelease], - }; - - const releaseJobTriggers = - release != null - ? await api.job.config.byWorkspaceId.list({ - workspaceId: workspace.id, - filter, - }) - : []; - const hasResources = env.resources.length > 0; - const hasRelease = release != null; - const jc = releaseJobTriggers - .filter( - (releaseJobTrigger) => - isPresent(releaseJobTrigger.environmentId) && - isPresent(releaseJobTrigger.releaseId) && - isPresent(releaseJobTrigger.resource.id), - ) - .map((releaseJobTrigger) => ({ ...releaseJobTrigger })); - return ( - <> - {hasRelease && hasResources && ( - - )} - - {!hasResources && hasRelease && ( -
No resources
- )} - - {!hasRelease && ( -
No release
- )} - - ); -}; - const DeploymentTable: React.FC<{ workspace: Workspace; systemSlug: string; @@ -228,13 +135,18 @@ const DeploymentTable: React.FC<{ idx === 0 && "border-t", )} > - + {release != null && ( + + )} + {release == null && ( +
+ No release +
+ )} ); })} diff --git a/apps/webservice/src/app/[workspaceSlug]/(app)/systems/[systemSlug]/deployments/[deploymentSlug]/releases/DeploymentPageContent.tsx b/apps/webservice/src/app/[workspaceSlug]/(app)/systems/[systemSlug]/deployments/[deploymentSlug]/releases/DeploymentPageContent.tsx index dc7779ce3..56a776abf 100644 --- a/apps/webservice/src/app/[workspaceSlug]/(app)/systems/[systemSlug]/deployments/[deploymentSlug]/releases/DeploymentPageContent.tsx +++ b/apps/webservice/src/app/[workspaceSlug]/(app)/systems/[systemSlug]/deployments/[deploymentSlug]/releases/DeploymentPageContent.tsx @@ -30,8 +30,7 @@ import { ReleaseConditionBadge } from "~/app/[workspaceSlug]/(app)/_components/r import { ReleaseConditionDialog } from "~/app/[workspaceSlug]/(app)/_components/release-condition/ReleaseConditionDialog"; import { useReleaseFilter } from "~/app/[workspaceSlug]/(app)/_components/release-condition/useReleaseFilter"; import { api } from "~/trpc/react"; -import { DeployButton } from "../../DeployButton"; -import { Release } from "../../TableCells"; +import { LazyReleaseEnvironmentCell } from "../../ReleaseEnvironmentCell"; import { EnvironmentColumnSelector, useEnvironmentColumnSelector, @@ -40,9 +39,10 @@ import { JobHistoryPopover } from "./JobHistoryPopover"; import { ReleaseDistributionGraphPopover } from "./ReleaseDistributionPopover"; type Environment = RouterOutputs["environment"]["bySystemId"][number]; +type Deployment = NonNullable; type DeploymentPageContentProps = { - deployment: schema.Deployment & { releaseChannels: schema.ReleaseChannel[] }; + deployment: Deployment; environments: Environment[]; releaseChannel: schema.ReleaseChannel | null; }; @@ -241,99 +241,26 @@ export const DeploymentPageContent: React.FC = ({
- {selectedEnvironments.map((env) => { - const environmentReleaseReleaseJobTriggers = - release.releaseJobTriggers.filter( - (t) => t.environmentId === env.id, - ); - - const hasResources = env.resources.length > 0; - const isAlreadyDeployed = - environmentReleaseReleaseJobTriggers.length > 0; - const hasJobAgent = deployment.jobAgentId != null; - const blockedEnv = blockedEnvByRelease.data?.find( - (be) => - be.releaseId === release.id && - be.environmentId === env.id, - ); - const isBlockedByReleaseChannel = blockedEnv != null; - - const showRelease = isAlreadyDeployed; - const showDeployButton = - !isAlreadyDeployed && - hasJobAgent && - hasResources && - !isBlockedByReleaseChannel; - - return ( - ( + e.stopPropagation()} + key={env.id} + > + b.environmentId === env.id, )} - onClick={(e) => e.stopPropagation()} - key={env.id} - > -
- {showRelease && ( - - )} - - {showDeployButton && ( - - )} - - {!isAlreadyDeployed && ( -
- {isBlockedByReleaseChannel && ( - <> - Blocked by{" "} - - - )} - {!isBlockedByReleaseChannel && - !hasJobAgent && - "No job agent"} - {!isBlockedByReleaseChannel && - hasJobAgent && - !hasResources && - "No resources"} -
- )} -
-
- ); - })} + /> +
+ ))} ))} diff --git a/packages/api/src/router/release.ts b/packages/api/src/router/release.ts index 2a6610bb2..0a81be3ba 100644 --- a/packages/api/src/router/release.ts +++ b/packages/api/src/router/release.ts @@ -8,7 +8,6 @@ import { desc, eq, inArray, - isNull, takeFirst, takeFirstOrNull, } from "@ctrlplane/db"; @@ -21,15 +20,12 @@ import { environmentPolicyReleaseChannel, environmentReleaseChannel, job, - jobMetadata, release, releaseChannel, releaseDependency, - releaseJobMatchesCondition, releaseJobTrigger, releaseMatchesCondition, releaseMetadata, - resource, } from "@ctrlplane/db/schema"; import { cancelOldReleaseJobTriggersOnJobDispatch, @@ -70,26 +66,19 @@ export const releaseRouter = createTRPCRouter({ ctx.db, input.filter, ); - const checks = [deploymentIdCheck, releaseConditionCheck].filter( - isPresent, - ); + const checks = and( + ...[deploymentIdCheck, releaseConditionCheck].filter(isPresent), + )!; - const getItems = async () => { - const items = await ctx.db + const getItems = async () => + ctx.db .select() .from(release) .leftJoin( releaseDependency, eq(release.id, releaseDependency.releaseId), ) - .where( - and( - ...[ - eq(release.deploymentId, input.deploymentId), - releaseMatchesCondition(ctx.db, input.filter), - ].filter(isPresent), - ), - ) + .where(checks) .orderBy(desc(release.createdAt), desc(release.version)) .limit(input.limit) .offset(input.offset) @@ -105,57 +94,12 @@ export const releaseRouter = createTRPCRouter({ .value(), ); - const jobTriggers = await ctx.db - .select() - .from(releaseJobTrigger) - .innerJoin(job, eq(releaseJobTrigger.jobId, job.id)) - .leftJoin(jobMetadata, eq(jobMetadata.jobId, job.id)) - .innerJoin(release, eq(releaseJobTrigger.releaseId, release.id)) - .innerJoin( - resource, - and( - eq(releaseJobTrigger.resourceId, resource.id), - isNull(resource.deletedAt), - releaseJobMatchesCondition(ctx.db, input.jobFilter), - ), - ) - .where( - inArray( - releaseJobTrigger.releaseId, - items.map((r) => r.id), - ), - ) - .orderBy(desc(releaseJobTrigger.createdAt)) - .then((data) => - _.chain(data) - .groupBy((j) => j.release_job_trigger.id) - .map((j) => ({ - ...j[0]!.release_job_trigger, - job: { - ...j[0]!.job, - metadata: _.chain(j) - .map((j) => j.job_metadata) - .filter(isPresent) - .uniqBy((j) => j.id) - .value(), - }, - resource: j[0]!.resource, - })) - .value(), - ); - - return items.map((r) => ({ - ...r, - releaseJobTriggers: jobTriggers.filter((j) => j.releaseId === r.id), - })); - }; - const total = ctx.db .select({ count: count().mapWith(Number), }) .from(release) - .where(and(...checks)) + .where(checks) .then(takeFirst) .then((t) => t.count); @@ -377,5 +321,37 @@ export const releaseRouter = createTRPCRouter({ return Promise.all(blockedEnvsPromises).then((r) => r.filter(isPresent)); }), + status: createTRPCRouter({ + byEnvironmentId: protectedProcedure + .input( + z.object({ + releaseId: z.string().uuid(), + environmentId: z.string().uuid(), + }), + ) + .meta({ + authorizationCheck: ({ canUser, input }) => + canUser.perform(Permission.ReleaseGet).on({ + type: "release", + id: input.releaseId, + }), + }) + .query(async ({ input }) => { + const statuses = await db + .selectDistinctOn([job.status]) + .from(job) + .innerJoin(releaseJobTrigger, eq(job.id, releaseJobTrigger.jobId)) + .orderBy(job.status, desc(job.createdAt)) + .where( + and( + eq(releaseJobTrigger.releaseId, input.releaseId), + eq(releaseJobTrigger.environmentId, input.environmentId), + ), + ); + + return statuses; + }), + }), + metadataKeys: releaseMetadataKeysRouter, }); diff --git a/packages/validators/src/jobs/index.ts b/packages/validators/src/jobs/index.ts index 93623f3c4..d5a1de3d5 100644 --- a/packages/validators/src/jobs/index.ts +++ b/packages/validators/src/jobs/index.ts @@ -33,6 +33,7 @@ export const JobStatusReadable = { }; export const activeStatus = [JobStatus.InProgress, JobStatus.ActionRequired]; +export const activeStatusType = activeStatus.map((s) => s as JobStatusType); export const exitedStatus = [ JobStatus.Completed, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 70e34c1d7..ab97e4b4e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -158,7 +158,7 @@ importers: version: 9.14.0(jiti@2.3.3) postcss-load-config: specifier: ^6.0.1 - version: 6.0.1(jiti@2.3.3)(postcss@8.4.47)(tsx@4.19.2)(yaml@2.6.0) + version: 6.0.1(jiti@2.3.3)(postcss@8.4.49)(tsx@4.19.2)(yaml@2.6.0) prettier: specifier: 'catalog:' version: 3.4.2 @@ -636,6 +636,9 @@ importers: react-hook-form: specifier: ^7.51.4 version: 7.53.1(react@18.3.1) + react-intersection-observer: + specifier: ^9.14.0 + version: 9.14.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react-use: specifier: ^17.5.0 version: 17.5.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -1634,7 +1637,7 @@ importers: version: 1.13.4(eslint@9.14.0(jiti@2.3.3)) eslint-plugin-import: specifier: ^2.29.1 - version: 2.31.0(eslint@9.14.0(jiti@2.3.3)) + version: 2.31.0(@typescript-eslint/parser@8.13.0(eslint@9.14.0(jiti@2.3.3))(typescript@5.6.3))(eslint@9.14.0(jiti@2.3.3)) eslint-plugin-jsx-a11y: specifier: ^6.9.0 version: 6.10.2(eslint@9.14.0(jiti@2.3.3)) @@ -11279,6 +11282,15 @@ packages: peerDependencies: react: ^16.8.4 || ^17.0.0 || ^18.0.0 + react-intersection-observer@9.14.0: + resolution: {integrity: sha512-AYqlmDZn85VUmlODwYym9y5OlqY2cFyIu41dkN0GJWvhdbd19Mh16mz5IH6fO1gp5V4FfQOO4m0zGc04Tj13rQ==} + peerDependencies: + react: ^17.0.0 || ^18.0.0 || ^19.0.0 + react-dom: ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + react-dom: + optional: true + react-is@16.13.1: resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} @@ -21434,16 +21446,17 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-module-utils@2.12.0(eslint-import-resolver-node@0.3.9)(eslint@9.14.0(jiti@2.3.3)): + eslint-module-utils@2.12.0(@typescript-eslint/parser@8.13.0(eslint@9.14.0(jiti@2.3.3))(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint@9.14.0(jiti@2.3.3)): dependencies: debug: 3.2.7 optionalDependencies: + '@typescript-eslint/parser': 8.13.0(eslint@9.14.0(jiti@2.3.3))(typescript@5.6.3) eslint: 9.14.0(jiti@2.3.3) eslint-import-resolver-node: 0.3.9 transitivePeerDependencies: - supports-color - eslint-plugin-import@2.31.0(eslint@9.14.0(jiti@2.3.3)): + eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.13.0(eslint@9.14.0(jiti@2.3.3))(typescript@5.6.3))(eslint@9.14.0(jiti@2.3.3)): dependencies: '@rtsao/scc': 1.1.0 array-includes: 3.1.8 @@ -21454,7 +21467,7 @@ snapshots: doctrine: 2.1.0 eslint: 9.14.0(jiti@2.3.3) eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.12.0(eslint-import-resolver-node@0.3.9)(eslint@9.14.0(jiti@2.3.3)) + eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.13.0(eslint@9.14.0(jiti@2.3.3))(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint@9.14.0(jiti@2.3.3)) hasown: 2.0.2 is-core-module: 2.15.1 is-glob: 4.0.3 @@ -21465,6 +21478,8 @@ snapshots: semver: 6.3.1 string.prototype.trimend: 1.0.8 tsconfig-paths: 3.15.0 + optionalDependencies: + '@typescript-eslint/parser': 8.13.0(eslint@9.14.0(jiti@2.3.3))(typescript@5.6.3) transitivePeerDependencies: - eslint-import-resolver-typescript - eslint-import-resolver-webpack @@ -24976,15 +24991,6 @@ snapshots: postcss: 8.4.47 ts-node: 10.9.2(@types/node@22.8.1)(typescript@5.6.3) - postcss-load-config@6.0.1(jiti@2.3.3)(postcss@8.4.47)(tsx@4.19.2)(yaml@2.6.0): - dependencies: - lilconfig: 3.1.2 - optionalDependencies: - jiti: 2.3.3 - postcss: 8.4.47 - tsx: 4.19.2 - yaml: 2.6.0 - postcss-load-config@6.0.1(jiti@2.3.3)(postcss@8.4.49)(tsx@4.19.2)(yaml@2.6.0): dependencies: lilconfig: 3.1.2 @@ -25337,6 +25343,12 @@ snapshots: dependencies: react: 18.3.1 + react-intersection-observer@9.14.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + react: 18.3.1 + optionalDependencies: + react-dom: 18.3.1(react@18.3.1) + react-is@16.13.1: {} react-is@18.3.1: {} From 0e0d951898f7a7daf7e2a4257d19d05667d32481 Mon Sep 17 00:00:00 2001 From: Aditya Choudhari Date: Fri, 20 Dec 2024 20:39:54 -0500 Subject: [PATCH 2/2] cleanup --- packages/api/src/router/release.ts | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/packages/api/src/router/release.ts b/packages/api/src/router/release.ts index 0a81be3ba..b6e5514a0 100644 --- a/packages/api/src/router/release.ts +++ b/packages/api/src/router/release.ts @@ -336,21 +336,19 @@ export const releaseRouter = createTRPCRouter({ id: input.releaseId, }), }) - .query(async ({ input }) => { - const statuses = await db + .query(({ input: { releaseId, environmentId } }) => + db .selectDistinctOn([job.status]) .from(job) .innerJoin(releaseJobTrigger, eq(job.id, releaseJobTrigger.jobId)) .orderBy(job.status, desc(job.createdAt)) .where( and( - eq(releaseJobTrigger.releaseId, input.releaseId), - eq(releaseJobTrigger.environmentId, input.environmentId), + eq(releaseJobTrigger.releaseId, releaseId), + eq(releaseJobTrigger.environmentId, environmentId), ), - ); - - return statuses; - }), + ), + ), }), metadataKeys: releaseMetadataKeysRouter,