Skip to content

Commit 4832469

Browse files
committed
Associating runs with waitpoints
1 parent ca47383 commit 4832469

File tree

13 files changed

+154
-21
lines changed

13 files changed

+154
-21
lines changed

apps/webapp/app/components/runs/v3/WaitpointDetails.tsx

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,28 @@
11
import { DateTime, DateTimeAccurate } from "~/components/primitives/DateTime";
2+
import { Paragraph } from "~/components/primitives/Paragraph";
23
import * as Property from "~/components/primitives/PropertyTable";
34
import { type WaitpointDetail } from "~/presenters/v3/WaitpointPresenter.server";
45
import { ForceTimeout } from "~/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.waitpoints.$waitpointFriendlyId.complete/route";
56
import { PacketDisplay } from "./PacketDisplay";
67
import { WaitpointStatusCombo } from "./WaitpointStatus";
7-
import { Paragraph } from "~/components/primitives/Paragraph";
8+
import { v3WaitpointTokensPath } from "~/utils/pathBuilder";
9+
import { Link } from "@remix-run/react";
10+
import { TextLink } from "~/components/primitives/TextLink";
11+
import { useOrganization } from "~/hooks/useOrganizations";
12+
import { useProject } from "~/hooks/useProject";
13+
import { useEnvironment } from "~/hooks/useEnvironment";
14+
15+
export function WaitpointDetailTable({
16+
waitpoint,
17+
linkToList = false,
18+
}: {
19+
waitpoint: WaitpointDetail;
20+
linkToList?: boolean;
21+
}) {
22+
const organization = useOrganization();
23+
const project = useProject();
24+
const environment = useEnvironment();
825

9-
export function WaitpointDetailTable({ waitpoint }: { waitpoint: WaitpointDetail }) {
1026
const hasExpired =
1127
waitpoint.idempotencyKeyExpiresAt && waitpoint.idempotencyKeyExpiresAt < new Date();
1228

@@ -24,7 +40,19 @@ export function WaitpointDetailTable({ waitpoint }: { waitpoint: WaitpointDetail
2440
</Property.Item>
2541
<Property.Item>
2642
<Property.Label>ID</Property.Label>
27-
<Property.Value className="whitespace-pre-wrap">{waitpoint.friendlyId}</Property.Value>
43+
<Property.Value className="whitespace-pre-wrap">
44+
{linkToList ? (
45+
<TextLink
46+
to={v3WaitpointTokensPath(organization, project, environment, {
47+
id: waitpoint.friendlyId,
48+
})}
49+
>
50+
{waitpoint.friendlyId}
51+
</TextLink>
52+
) : (
53+
waitpoint.friendlyId
54+
)}
55+
</Property.Value>
2856
</Property.Item>
2957
<Property.Item>
3058
<Property.Label>Idempotency key</Property.Label>

apps/webapp/app/components/runs/v3/WaitpointTokenFilters.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ export const WaitpointSearchParamsSchema = z.object({
6565
cursor: z.string().optional(),
6666
direction: z.enum(["forward", "backward"]).optional(),
6767
});
68+
export type WaitpointSearchParams = z.infer<typeof WaitpointSearchParamsSchema>;
6869

6970
type WaitpointTokenFiltersProps = {
7071
hasFilters: boolean;
@@ -561,7 +562,7 @@ function AppliedWaitpointIdFilter() {
561562
trigger={
562563
<Ariakit.Select render={<div className="group cursor-pointer focus-custom" />}>
563564
<AppliedFilter
564-
label="Waitpoint ID"
565+
label="ID"
565566
value={id}
566567
onRemove={() => del(["id", "cursor", "direction"])}
567568
/>
@@ -629,7 +630,7 @@ function IdempotencyKeyDropdown({
629630
<div className="flex flex-col gap-1">
630631
<Label>Idempotency key</Label>
631632
<Input
632-
placeholder="run_"
633+
placeholder="waitpoint_"
633634
value={idempotencyKey ?? ""}
634635
onChange={(e) => setIdempotencyKey(e.target.value)}
635636
variant="small"

apps/webapp/app/presenters/v3/RunListPresenter.server.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ export type RunListOptions = {
2424
isTest?: boolean;
2525
rootOnly?: boolean;
2626
batchId?: string;
27-
runId?: string;
27+
runIds?: string[];
2828
//pagination
2929
direction?: Direction;
3030
cursor?: string;
@@ -52,7 +52,7 @@ export class RunListPresenter extends BasePresenter {
5252
isTest,
5353
rootOnly,
5454
batchId,
55-
runId,
55+
runIds,
5656
from,
5757
to,
5858
direction = "forward",
@@ -72,7 +72,7 @@ export class RunListPresenter extends BasePresenter {
7272
(scheduleId !== undefined && scheduleId !== "") ||
7373
(tags !== undefined && tags.length > 0) ||
7474
batchId !== undefined ||
75-
runId !== undefined ||
75+
(runIds !== undefined && runIds.length > 0) ||
7676
typeof isTest === "boolean" ||
7777
rootOnly === true;
7878

@@ -182,7 +182,7 @@ export class RunListPresenter extends BasePresenter {
182182
}
183183

184184
//show all runs if we are filtering by batchId or runId
185-
if (batchId || runId || scheduleId || tasks?.length) {
185+
if (batchId || runIds?.length || scheduleId || tasks?.length) {
186186
rootOnly = false;
187187
}
188188

@@ -261,7 +261,7 @@ WHERE
261261
: Prisma.empty
262262
}
263263
-- filters
264-
${runId ? Prisma.sql`AND tr."friendlyId" = ${runId}` : Prisma.empty}
264+
${runIds ? Prisma.sql`AND tr."friendlyId" IN (${Prisma.join(runIds)})` : Prisma.empty}
265265
${batchId ? Prisma.sql`AND tr."batchId" = ${batchId}` : Prisma.empty}
266266
${
267267
restrictToRunIds

apps/webapp/app/presenters/v3/WaitpointPresenter.server.ts

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,20 @@
11
import { isWaitpointOutputTimeout, prettyPrintPacket } from "@trigger.dev/core/v3";
22
import { logger } from "~/services/logger.server";
33
import { BasePresenter } from "./basePresenter.server";
4+
import { RunListItem, RunListPresenter } from "./RunListPresenter.server";
45

56
export type WaitpointDetail = NonNullable<Awaited<ReturnType<WaitpointPresenter["call"]>>>;
67

78
export class WaitpointPresenter extends BasePresenter {
8-
public async call({ friendlyId, environmentId }: { friendlyId: string; environmentId: string }) {
9+
public async call({
10+
friendlyId,
11+
environmentId,
12+
projectId,
13+
}: {
14+
friendlyId: string;
15+
environmentId: string;
16+
projectId: string;
17+
}) {
918
const waitpoint = await this._replica.waitpoint.findFirst({
1019
where: {
1120
friendlyId,
@@ -23,6 +32,12 @@ export class WaitpointPresenter extends BasePresenter {
2332
outputType: true,
2433
outputIsError: true,
2534
completedAfter: true,
35+
connectedRuns: {
36+
select: {
37+
friendlyId: true,
38+
},
39+
take: 5,
40+
},
2641
},
2742
});
2843

@@ -47,6 +62,20 @@ export class WaitpointPresenter extends BasePresenter {
4762
}
4863
}
4964

65+
const connectedRunIds = waitpoint.connectedRuns.map((run) => run.friendlyId);
66+
const connectedRuns: RunListItem[] = [];
67+
68+
if (connectedRunIds.length > 0) {
69+
const runPresenter = new RunListPresenter();
70+
const { runs } = await runPresenter.call({
71+
projectId: projectId,
72+
environments: [environmentId],
73+
runIds: connectedRunIds,
74+
pageSize: 5,
75+
});
76+
connectedRuns.push(...runs);
77+
}
78+
5079
return {
5180
friendlyId: waitpoint.friendlyId,
5281
type: waitpoint.type,
@@ -60,6 +89,7 @@ export class WaitpointPresenter extends BasePresenter {
6089
outputIsError: waitpoint.outputIsError,
6190
completedAfter: waitpoint.completedAfter,
6291
isTimeout,
92+
connectedRuns,
6393
};
6494
}
6595
}

apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs._index/route.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ export const loader = async ({ request, params }: LoaderFunctionArgs) => {
134134
from,
135135
to,
136136
batchId,
137-
runId,
137+
runIds: runId ? [runId] : undefined,
138138
scheduleId,
139139
rootOnly,
140140
direction: direction,

apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.waitpoints.tokens.$waitpointParam/route.tsx

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { typedjson, useTypedLoaderData } from "remix-typedjson";
44
import { z } from "zod";
55
import { ExitIcon } from "~/assets/icons/ExitIcon";
66
import { LinkButton } from "~/components/primitives/Buttons";
7-
import { Header2 } from "~/components/primitives/Headers";
7+
import { Header2, Header3 } from "~/components/primitives/Headers";
88
import { useEnvironment } from "~/hooks/useEnvironment";
99
import { useOrganization } from "~/hooks/useOrganizations";
1010
import { useProject } from "~/hooks/useProject";
@@ -16,6 +16,8 @@ import { cn } from "~/utils/cn";
1616
import { EnvironmentParamSchema, v3WaitpointTokensPath } from "~/utils/pathBuilder";
1717
import { CompleteWaitpointForm } from "../resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.waitpoints.$waitpointFriendlyId.complete/route";
1818
import { WaitpointDetailTable } from "~/components/runs/v3/WaitpointDetails";
19+
import { TaskRunsTable } from "~/components/runs/v3/TaskRunsTable";
20+
import { InfoIconTooltip } from "~/components/primitives/Tooltip";
1921

2022
const Params = EnvironmentParamSchema.extend({
2123
waitpointParam: z.string(),
@@ -46,6 +48,7 @@ export const loader = async ({ request, params }: LoaderFunctionArgs) => {
4648
const result = await presenter.call({
4749
friendlyId: waitpointParam,
4850
environmentId: environment.id,
51+
projectId: project.id,
4952
});
5053

5154
if (!result) {
@@ -94,8 +97,31 @@ export default function Page() {
9497
className="pl-1"
9598
/>
9699
</div>
97-
<div className="overflow-y-auto px-3 pt-3 scrollbar-thin scrollbar-track-transparent scrollbar-thumb-charcoal-600">
98-
<WaitpointDetailTable waitpoint={waitpoint} />
100+
<div className="overflow-y-auto pt-3 scrollbar-thin scrollbar-track-transparent scrollbar-thumb-charcoal-600">
101+
<div className="px-3">
102+
<WaitpointDetailTable waitpoint={waitpoint} />
103+
</div>
104+
<div className="flex flex-col gap-1 pt-6">
105+
<div className="mb-1 flex items-center gap-1 pl-3">
106+
<Header3>5 related runs</Header3>
107+
<InfoIconTooltip content="These runs have been blocked by this waitpoint." />
108+
</div>
109+
<TaskRunsTable
110+
total={waitpoint.connectedRuns.length}
111+
hasFilters={false}
112+
filters={{
113+
tasks: [],
114+
versions: [],
115+
statuses: [],
116+
environments: [],
117+
from: undefined,
118+
to: undefined,
119+
}}
120+
runs={waitpoint.connectedRuns}
121+
isLoading={false}
122+
variant="bright"
123+
/>
124+
</div>
99125
</div>
100126
{waitpoint.status === "PENDING" && (
101127
<div>

apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.waitpoints.tokens/route.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,7 @@ export default function Page() {
203203
"–"
204204
)}
205205
</TableCell>
206-
<TableCell to={path} actionClassName="py-1" className="pr-16">
206+
<TableCell to={path} actionClassName="py-1">
207207
<div className="flex gap-1">
208208
{token.tags.map((tag) => <RunTag key={tag} tag={tag} />) || "–"}
209209
</div>

apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.spans.$spanParam/route.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -950,7 +950,7 @@ function SpanEntity({ span }: { span: Span }) {
950950
<TextLink to={docsPath("wait")}>View docs</TextLink>.
951951
</Paragraph>
952952
</div>
953-
<WaitpointDetailTable waitpoint={span.entity.object} />
953+
<WaitpointDetailTable waitpoint={span.entity.object} linkToList />
954954
</div>
955955
{span.entity.object.status === "PENDING" && (
956956
<div>

apps/webapp/app/utils/pathBuilder.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@ import { type TaskRunListSearchFilters } from "~/components/runs/v3/RunFilters";
44
import type { Organization } from "~/models/organization.server";
55
import type { Project } from "~/models/project.server";
66
import { objectToSearchParams } from "./searchParams";
7+
import {
8+
WaitpointFilterStatus,
9+
WaitpointSearchParams,
10+
} from "~/components/runs/v3/WaitpointTokenFilters";
711

812
export type OrgForPath = Pick<Organization, "slug">;
913
export type ProjectForPath = Pick<Project, "slug">;
@@ -314,9 +318,12 @@ export function v3QueuesPath(
314318
export function v3WaitpointTokensPath(
315319
organization: OrgForPath,
316320
project: ProjectForPath,
317-
environment: EnvironmentForPath
321+
environment: EnvironmentForPath,
322+
filters?: WaitpointSearchParams
318323
) {
319-
return `${v3EnvironmentPath(organization, project, environment)}/waitpoints/tokens`;
324+
const searchParams = objectToSearchParams(filters);
325+
const query = searchParams ? `?${searchParams.toString()}` : "";
326+
return `${v3EnvironmentPath(organization, project, environment)}/waitpoints/tokens${query}`;
320327
}
321328

322329
export function v3WaitpointTokenPath(
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
-- CreateTable
2+
CREATE TABLE
3+
"_WaitpointRunConnections" ("A" TEXT NOT NULL, "B" TEXT NOT NULL);
4+
5+
-- CreateIndex
6+
CREATE UNIQUE INDEX "_WaitpointRunConnections_AB_unique" ON "_WaitpointRunConnections" ("A", "B");
7+
8+
-- CreateIndex
9+
CREATE INDEX "_WaitpointRunConnections_B_index" ON "_WaitpointRunConnections" ("B");
10+
11+
-- AddForeignKey
12+
ALTER TABLE "_WaitpointRunConnections" ADD CONSTRAINT "_WaitpointRunConnections_A_fkey" FOREIGN KEY ("A") REFERENCES "TaskRun" ("id") ON DELETE CASCADE ON UPDATE CASCADE;
13+
14+
-- AddForeignKey
15+
ALTER TABLE "_WaitpointRunConnections" ADD CONSTRAINT "_WaitpointRunConnections_B_fkey" FOREIGN KEY ("B") REFERENCES "Waitpoint" ("id") ON DELETE CASCADE ON UPDATE CASCADE;

0 commit comments

Comments
 (0)