Skip to content

Commit 182adc5

Browse files
authored
Merge branch 'main' into refactor/email-whitelisting
2 parents 1202779 + a9ff324 commit 182adc5

File tree

85 files changed

+491
-142
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

85 files changed

+491
-142
lines changed

.changeset/orange-eggs-fold.md

Lines changed: 0 additions & 6 deletions
This file was deleted.

apps/webapp/app/components/run/RunOverview.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,7 @@ export function RunOverview({ run, trigger, showRerun, paths, currentUser }: Run
117117
{showRerun && run.isFinished && (
118118
<RerunPopover
119119
runId={run.id}
120+
runPath={paths.run}
120121
runsPath={paths.runsPath}
121122
environmentType={run.environment.type}
122123
status={run.basicStatus}
@@ -317,18 +318,20 @@ function BlankTasks({ status }: { status: RunBasicStatus }) {
317318

318319
function RerunPopover({
319320
runId,
321+
runPath,
320322
runsPath,
321323
environmentType,
322324
status,
323325
}: {
324326
runId: string;
327+
runPath: string;
325328
runsPath: string;
326329
environmentType: RuntimeEnvironmentType;
327330
status: RunBasicStatus;
328331
}) {
329332
const lastSubmission = useActionData();
330333

331-
const [form, { successRedirect }] = useForm({
334+
const [form, { successRedirect, failureRedirect }] = useForm({
332335
id: "rerun",
333336
// TODO: type this
334337
lastSubmission: lastSubmission as any,
@@ -347,6 +350,7 @@ function RerunPopover({
347350
<PopoverContent className="flex min-w-[20rem] max-w-[20rem] flex-col gap-2 p-0" align="end">
348351
<Form method="post" action={`/resources/runs/${runId}/rerun`} {...form.props}>
349352
<input {...conform.input(successRedirect, { type: "hidden" })} defaultValue={runsPath} />
353+
<input {...conform.input(failureRedirect, { type: "hidden" })} defaultValue={runPath} />
350354
{environmentType === "PRODUCTION" && (
351355
<div className="px-4 pt-4">
352356
<Callout variant="warning">

apps/webapp/app/components/runs/RunsTable.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,11 +77,11 @@ export function RunsTable({
7777
<TableBody>
7878
{total === 0 && !hasFilters ? (
7979
<TableBlankRow colSpan={showJob ? 10 : 9}>
80-
<NoRuns title="No runs found" />
80+
{!isLoading && <NoRuns title="No runs found" />}
8181
</TableBlankRow>
8282
) : runs.length === 0 ? (
8383
<TableBlankRow colSpan={showJob ? 10 : 9}>
84-
<NoRuns title="No runs match your filters" />
84+
{!isLoading && <NoRuns title="No runs match your filters" />}
8585
</TableBlankRow>
8686
) : (
8787
runs.map((run) => {

apps/webapp/app/presenters/EnvironmentsPresenter.server.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ export type ClientEndpoint =
3838
state: "configured";
3939
id: string;
4040
slug: string;
41-
url: string;
41+
url: string | null;
4242
indexWebhookPath: string;
4343
latestIndex?: {
4444
status: EndpointIndexStatus;
@@ -102,6 +102,11 @@ export class EnvironmentsPresenter {
102102
},
103103
},
104104
},
105+
where: {
106+
url: {
107+
not: null,
108+
},
109+
},
105110
},
106111
},
107112
where: {

apps/webapp/app/presenters/JobListPresenter.server.ts

Lines changed: 35 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { Project } from "~/models/project.server";
99
import { User } from "~/models/user.server";
1010
import { z } from "zod";
1111
import { projectPath } from "~/utils/pathBuilder";
12+
import { JobRunStatus } from "@trigger.dev/database";
1213

1314
export type ProjectJob = Awaited<ReturnType<JobListPresenter["call"]>>[0];
1415

@@ -43,14 +44,6 @@ export class JobListPresenter {
4344
id: true,
4445
slug: true,
4546
title: true,
46-
runs: {
47-
select: {
48-
createdAt: true,
49-
status: true,
50-
},
51-
take: 1,
52-
orderBy: [{ createdAt: "desc" }],
53-
},
5447
integrations: {
5548
select: {
5649
key: true,
@@ -105,6 +98,37 @@ export class JobListPresenter {
10598
orderBy: [{ title: "asc" }],
10699
});
107100

101+
let latestRuns = [] as {
102+
createdAt: Date;
103+
status: JobRunStatus;
104+
jobId: string;
105+
rn: BigInt;
106+
}[];
107+
108+
if (jobs.length > 0) {
109+
latestRuns = await this.#prismaClient.$queryRaw<
110+
{
111+
createdAt: Date;
112+
status: JobRunStatus;
113+
jobId: string;
114+
rn: BigInt;
115+
}[]
116+
>`
117+
SELECT * FROM (
118+
SELECT
119+
"id",
120+
"createdAt",
121+
"status",
122+
"jobId",
123+
ROW_NUMBER() OVER(PARTITION BY "jobId" ORDER BY "createdAt" DESC) as rn
124+
FROM
125+
"public"."JobRun"
126+
WHERE
127+
"jobId" IN (${Prisma.join(jobs.map((j) => j.id))})
128+
) t
129+
WHERE rn = 1;`;
130+
}
131+
108132
return jobs
109133
.flatMap((job) => {
110134
const version = job.versions.at(0);
@@ -132,6 +156,8 @@ export class JobListPresenter {
132156
properties = [...properties, ...versionProperties];
133157
}
134158

159+
const latestRun = latestRuns.find((r) => r.jobId === job.id);
160+
135161
return [
136162
{
137163
id: job.id,
@@ -155,7 +181,7 @@ export class JobListPresenter {
155181
(i) => i.setupStatus === "MISSING_FIELDS"
156182
),
157183
environment: version.environment,
158-
lastRun: job.runs.at(0),
184+
lastRun: latestRun,
159185
properties,
160186
projectSlug: job.project.slug,
161187
},

apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.environments/ConfigureEndpointSheet.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ export function ConfigureEndpointSheet({ slug, endpoint, onClose }: ConfigureEnd
111111
<Input
112112
className="rounded-r-none"
113113
{...conform.input(url, { type: "url" })}
114-
defaultValue={"url" in endpoint ? endpoint.url : ""}
114+
defaultValue={"url" in endpoint ? endpoint.url ?? "" : ""}
115115
placeholder="URL for your Trigger API route"
116116
/>
117117
<Button

apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.jobs.$jobParam._index/ListPagination.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ import { cn } from "~/utils/cn";
55

66
type List = {
77
pagination: {
8-
next: string | undefined;
9-
previous: string | undefined;
8+
next?: string | undefined;
9+
previous?: string | undefined;
1010
};
1111
};
1212

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

Lines changed: 49 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import { useLocation, useNavigate, useNavigation } from "@remix-run/react";
2-
import { LoaderFunctionArgs } from "@remix-run/server-runtime";
1+
import { Await, useLoaderData, useLocation, useNavigate, useNavigation } from "@remix-run/react";
2+
import { LoaderFunctionArgs, defer } from "@remix-run/server-runtime";
33
import { typedjson, useTypedLoaderData } from "remix-typedjson";
44
import { PageBody, PageContainer } from "~/components/layout/AppLayout";
55
import { LinkButton } from "~/components/primitives/Buttons";
@@ -20,6 +20,8 @@ import { ProjectParamSchema, docsPath, projectPath } from "~/utils/pathBuilder";
2020
import { ListPagination } from "../_app.orgs.$organizationSlug.projects.$projectParam.jobs.$jobParam._index/ListPagination";
2121
import { RunListSearchSchema } from "~/components/runs/RunStatuses";
2222
import { RunsFilters } from "~/components/runs/RunFilters";
23+
import { Suspense } from "react";
24+
import { Spinner } from "~/components/primitives/Spinner";
2325

2426
export const loader = async ({ request, params }: LoaderFunctionArgs) => {
2527
const userId = await requireUserId(request);
@@ -31,7 +33,7 @@ export const loader = async ({ request, params }: LoaderFunctionArgs) => {
3133

3234
const presenter = new RunListPresenter();
3335

34-
const list = await presenter.call({
36+
const list = presenter.call({
3537
userId,
3638
filterEnvironment: searchParams.environment,
3739
filterStatus: searchParams.status,
@@ -44,13 +46,13 @@ export const loader = async ({ request, params }: LoaderFunctionArgs) => {
4446
to: searchParams.to,
4547
});
4648

47-
return typedjson({
49+
return defer({
4850
list,
4951
});
5052
};
5153

5254
export default function Page() {
53-
const { list } = useTypedLoaderData<typeof loader>();
55+
const { list } = useLoaderData<typeof loader>();
5456
const navigation = useNavigation();
5557
const isLoading = navigation.state !== "idle";
5658
const organization = useOrganization();
@@ -79,18 +81,49 @@ export default function Page() {
7981
<div className="h-full overflow-y-auto p-4 scrollbar-thin scrollbar-track-transparent scrollbar-thumb-slate-700">
8082
<div className="mb-2 flex items-center justify-between gap-x-2">
8183
<RunsFilters />
82-
<ListPagination list={list} />
84+
<Suspense fallback={<></>}>
85+
<Await resolve={list}>{(data) => <ListPagination list={data} />}</Await>
86+
</Suspense>
8387
</div>
84-
<RunsTable
85-
total={list.runs.length}
86-
hasFilters={false}
87-
showJob={true}
88-
runs={list.runs}
89-
isLoading={isLoading}
90-
runsParentPath={projectPath(organization, project)}
91-
currentUser={user}
92-
/>
93-
<ListPagination list={list} className="mt-2 justify-end" />
88+
<Suspense
89+
fallback={
90+
<RunsTable
91+
total={0}
92+
hasFilters={false}
93+
showJob={true}
94+
runs={[]}
95+
isLoading={true}
96+
runsParentPath={projectPath(organization, project)}
97+
currentUser={user}
98+
/>
99+
}
100+
>
101+
<Await resolve={list}>
102+
{(data) => {
103+
const runs = data.runs.map((run) => ({
104+
...run,
105+
startedAt: run.startedAt ? new Date(run.startedAt) : null,
106+
completedAt: run.completedAt ? new Date(run.completedAt) : null,
107+
createdAt: new Date(run.createdAt),
108+
}));
109+
110+
return (
111+
<>
112+
<RunsTable
113+
total={data.runs.length}
114+
hasFilters={false}
115+
showJob={true}
116+
runs={runs}
117+
isLoading={isLoading}
118+
runsParentPath={projectPath(organization, project)}
119+
currentUser={user}
120+
/>
121+
<ListPagination list={data} className="mt-2 justify-end" />
122+
</>
123+
);
124+
}}
125+
</Await>
126+
</Suspense>
94127
</div>
95128
</PageBody>
96129
</PageContainer>

apps/webapp/app/routes/resources.runs.$runId.rerun.ts

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,18 @@
11
import { parse } from "@conform-to/zod";
22
import { ActionFunction, json } from "@remix-run/node";
33
import { z } from "zod";
4-
import { redirectBackWithErrorMessage, redirectWithSuccessMessage } from "~/models/message.server";
4+
import {
5+
redirectBackWithErrorMessage,
6+
redirectWithErrorMessage,
7+
redirectWithSuccessMessage,
8+
} from "~/models/message.server";
59
import { ContinueRunService } from "~/services/runs/continueRun.server";
610
import { ReRunService } from "~/services/runs/reRun.server";
11+
import { rootPath, runPath } from "~/utils/pathBuilder";
712

813
export const schema = z.object({
914
successRedirect: z.string(),
15+
failureRedirect: z.string(),
1016
});
1117

1218
const ParamSchema = z.object({
@@ -20,7 +26,11 @@ export const action: ActionFunction = async ({ request, params }) => {
2026
const submission = parse(formData, { schema });
2127

2228
if (!submission.value) {
23-
return json(submission);
29+
return redirectWithErrorMessage(
30+
rootPath(),
31+
request,
32+
submission.error ? JSON.stringify(submission.error) : "Invalid form"
33+
);
2434
}
2535

2636
try {
@@ -29,7 +39,11 @@ export const action: ActionFunction = async ({ request, params }) => {
2939
const run = await rerunService.call({ runId });
3040

3141
if (!run) {
32-
return redirectBackWithErrorMessage(request, "Unable to retry run");
42+
return redirectWithErrorMessage(
43+
submission.value.failureRedirect,
44+
request,
45+
"Unable to retry run"
46+
);
3347
}
3448

3549
return redirectWithSuccessMessage(
@@ -48,6 +62,10 @@ export const action: ActionFunction = async ({ request, params }) => {
4862
);
4963
}
5064
} catch (error: any) {
51-
return json({ errors: { body: error.message } }, { status: 400 });
65+
return redirectWithErrorMessage(
66+
submission.value.failureRedirect,
67+
request,
68+
error instanceof Error ? error.message : JSON.stringify(error)
69+
);
5270
}
5371
};

apps/webapp/app/services/endpoints/deleteEndpointService.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,10 @@ export class DeleteEndpointIndexService {
99
}
1010

1111
public async call(id: string, userId: string): Promise<void> {
12-
await this.#prismaClient.endpoint.delete({
12+
await this.#prismaClient.endpoint.update({
13+
data: {
14+
url: null,
15+
},
1316
where: {
1417
id,
1518
organization: {

apps/webapp/app/services/endpoints/performEndpointIndexService.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,13 @@ export class PerformEndpointIndexService {
5454

5555
logger.debug("Performing endpoint index", endpointIndex);
5656

57+
if (!endpointIndex.endpoint.url) {
58+
logger.debug("Endpoint URL is not set", endpointIndex);
59+
return updateEndpointIndexWithError(this.#prismaClient, id, {
60+
message: "Endpoint URL is not set",
61+
});
62+
}
63+
5764
// Make a request to the endpoint to fetch a list of jobs
5865
const client = new EndpointApi(
5966
endpointIndex.endpoint.environment.apiKey,

apps/webapp/app/services/endpoints/probeEndpoint.server.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,13 @@ export class ProbeEndpointService {
2929
id,
3030
});
3131

32+
if (!endpoint.url) {
33+
logger.debug(`Endpoint has no url`, {
34+
id,
35+
});
36+
return;
37+
}
38+
3239
const client = new EndpointApi(endpoint.environment.apiKey, endpoint.url);
3340

3441
const { response, durationInMs } = await client.probe(MAX_RUN_CHUNK_EXECUTION_LIMIT);

apps/webapp/app/services/endpoints/recurringEndpointIndex.server.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@ export class RecurringEndpointIndexService {
1616

1717
const endpoints = await this.#prismaClient.endpoint.findMany({
1818
where: {
19+
url: {
20+
not: null,
21+
},
1922
environment: {
2023
type: {
2124
in: [RuntimeEnvironmentType.PRODUCTION, RuntimeEnvironmentType.STAGING],

0 commit comments

Comments
 (0)