Skip to content

Commit 4b3b418

Browse files
authored
Set endpoint URLs to null, instead of deleting them (#878)
* Added Endpoint deletedAt column * Only show endpoints where they’re not deleted * Don’t delete Endpoints, set the deletedAt and change their slug name * Only perform indexing if the endpoint isn’t deleted * Have a nullable URL for endpoints * Deal with null URLs throughout the app * Re-running and retrying behaves properly when there’s no endpoint URL * Remove console.log * Better error message when doing a run
1 parent 2e354d3 commit 4b3b418

File tree

18 files changed

+103
-10
lines changed

18 files changed

+103
-10
lines changed

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/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/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/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],

apps/webapp/app/services/httpendpoint/HandleHttpEndpointService.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,14 @@ export class HandleHttpEndpointService {
9696
);
9797
}
9898

99+
if (!httpEndpointEnvironment.endpoint.url) {
100+
logger.debug("Endpoint has no url", {
101+
httpEndpointId: httpEndpoint.id,
102+
environmentId: environment.id,
103+
});
104+
return json({ error: true, message: "Endpoint has no url" }, { status: 404 });
105+
}
106+
99107
const immediateResponseFilter = RequestFilterSchema.nullable().safeParse(
100108
httpEndpointEnvironment.immediateResponseFilter
101109
);

apps/webapp/app/services/runs/createRun.server.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,11 @@ export class CreateRunService {
3737
},
3838
});
3939

40+
if (!endpoint.url) {
41+
logger.debug("Endpoint has no url", endpoint);
42+
return;
43+
}
44+
4045
const eventRecord = await this.#prismaClient.eventRecord.findUniqueOrThrow({
4146
where: {
4247
id: eventId,

apps/webapp/app/services/runs/deliverRunSubscription.server.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,10 @@ export class DeliverRunSubscriptionService {
9797
return true;
9898
}
9999

100+
if (subscription.run.endpoint.url === null) {
101+
return true;
102+
}
103+
100104
const client = new EndpointApi(
101105
subscription.run.environment.apiKey,
102106
subscription.run.endpoint.url

apps/webapp/app/services/runs/performRunExecutionV3.server.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,12 @@ export class PerformRunExecutionV3Service {
135135
return;
136136
}
137137

138+
if (!run.endpoint.url) {
139+
return await this.#failRunExecution(this.#prismaClient, run, {
140+
message: `Endpoint has no URL set`,
141+
});
142+
}
143+
138144
const client = new EndpointApi(run.environment.apiKey, run.endpoint.url);
139145
const event = eventRecordToApiJson(run.event);
140146

apps/webapp/app/services/sources/deliverHttpSourceRequest.server.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,10 @@ export class DeliverHttpSourceRequestService {
4343
return;
4444
}
4545

46+
if (!httpSourceRequest.endpoint.url) {
47+
return;
48+
}
49+
4650
const secretStore = getSecretStore(httpSourceRequest.source.secretReference.provider);
4751

4852
const secret = await secretStore.getSecret(

apps/webapp/app/services/sources/deliverWebhookRequest.server.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,10 @@ export class DeliverWebhookRequestService {
4949
return;
5050
}
5151

52+
if (!requestDelivery.endpoint.url) {
53+
return;
54+
}
55+
5256
const { secretReference } = requestDelivery.webhook.httpEndpoint;
5357

5458
const secretStore = getSecretStore(secretReference.provider);

apps/webapp/app/services/triggers/initializeTrigger.server.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,10 @@ export class InitializeTriggerService {
3535
},
3636
});
3737

38+
if (!endpoint.url) {
39+
throw new Error("This environment's endpoint doesn't have a URL set");
40+
}
41+
3842
const dynamicTrigger = await this.#prismaClient.dynamicTrigger.findUniqueOrThrow({
3943
where: {
4044
endpointId_slug_type: {
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
-- AlterTable
2+
ALTER TABLE "Endpoint" ADD COLUMN "deletedAt" TIMESTAMP(3);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
/*
2+
Warnings:
3+
4+
- You are about to drop the column `deletedAt` on the `Endpoint` table. All the data in the column will be lost.
5+
6+
*/
7+
-- AlterTable
8+
ALTER TABLE "Endpoint" DROP COLUMN "deletedAt",
9+
ALTER COLUMN "url" DROP NOT NULL;

packages/database/prisma/schema.prisma

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -413,9 +413,9 @@ model Project {
413413
}
414414

415415
model Endpoint {
416-
id String @id @default(cuid())
416+
id String @id @default(cuid())
417417
slug String
418-
url String
418+
url String?
419419
420420
environment RuntimeEnvironment @relation(fields: [environmentId], references: [id], onDelete: Cascade, onUpdate: Cascade)
421421
environmentId String

0 commit comments

Comments
 (0)