Skip to content

Commit 279aa99

Browse files
committed
finalize the realtime API
1 parent 91700d3 commit 279aa99

Some content is hidden

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

42 files changed

+1078
-621
lines changed

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

Lines changed: 44 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -62,8 +62,7 @@ type CommonRelatedRun = Prisma.Result<
6262
export class ApiRetrieveRunPresenter extends BasePresenter {
6363
public async call(
6464
friendlyId: string,
65-
env: AuthenticatedEnvironment,
66-
showSecretDetails: boolean
65+
env: AuthenticatedEnvironment
6766
): Promise<RetrieveRunResponse | undefined> {
6867
return this.traceWithEnv("call", env, async (span) => {
6968
const taskRun = await this._replica.taskRun.findFirst({
@@ -72,11 +71,7 @@ export class ApiRetrieveRunPresenter extends BasePresenter {
7271
runtimeEnvironmentId: env.id,
7372
},
7473
include: {
75-
attempts: {
76-
orderBy: {
77-
createdAt: "desc",
78-
},
79-
},
74+
attempts: true,
8075
lockedToVersion: true,
8176
schedule: true,
8277
tags: true,
@@ -111,50 +106,48 @@ export class ApiRetrieveRunPresenter extends BasePresenter {
111106
let $output: any;
112107
let $outputPresignedUrl: string | undefined;
113108

114-
if (showSecretDetails) {
115-
const payloadPacket = await conditionallyImportPacket({
116-
data: taskRun.payload,
117-
dataType: taskRun.payloadType,
118-
});
109+
const payloadPacket = await conditionallyImportPacket({
110+
data: taskRun.payload,
111+
dataType: taskRun.payloadType,
112+
});
119113

120-
if (
121-
payloadPacket.dataType === "application/store" &&
122-
typeof payloadPacket.data === "string"
123-
) {
124-
$payloadPresignedUrl = await generatePresignedUrl(
125-
env.project.externalRef,
126-
env.slug,
127-
payloadPacket.data,
128-
"GET"
129-
);
130-
} else {
131-
$payload = await parsePacket(payloadPacket);
132-
}
114+
if (
115+
payloadPacket.dataType === "application/store" &&
116+
typeof payloadPacket.data === "string"
117+
) {
118+
$payloadPresignedUrl = await generatePresignedUrl(
119+
env.project.externalRef,
120+
env.slug,
121+
payloadPacket.data,
122+
"GET"
123+
);
124+
} else {
125+
$payload = await parsePacket(payloadPacket);
126+
}
133127

134-
if (taskRun.status === "COMPLETED_SUCCESSFULLY") {
135-
const completedAttempt = taskRun.attempts.find(
136-
(a) => a.status === "COMPLETED" && typeof a.output !== null
137-
);
128+
if (taskRun.status === "COMPLETED_SUCCESSFULLY") {
129+
const completedAttempt = taskRun.attempts.find(
130+
(a) => a.status === "COMPLETED" && typeof a.output !== null
131+
);
138132

139-
if (completedAttempt && completedAttempt.output) {
140-
const outputPacket = await conditionallyImportPacket({
141-
data: completedAttempt.output,
142-
dataType: completedAttempt.outputType,
143-
});
133+
if (completedAttempt && completedAttempt.output) {
134+
const outputPacket = await conditionallyImportPacket({
135+
data: completedAttempt.output,
136+
dataType: completedAttempt.outputType,
137+
});
144138

145-
if (
146-
outputPacket.dataType === "application/store" &&
147-
typeof outputPacket.data === "string"
148-
) {
149-
$outputPresignedUrl = await generatePresignedUrl(
150-
env.project.externalRef,
151-
env.slug,
152-
outputPacket.data,
153-
"GET"
154-
);
155-
} else {
156-
$output = await parsePacket(outputPacket);
157-
}
139+
if (
140+
outputPacket.dataType === "application/store" &&
141+
typeof outputPacket.data === "string"
142+
) {
143+
$outputPresignedUrl = await generatePresignedUrl(
144+
env.project.externalRef,
145+
env.slug,
146+
outputPacket.data,
147+
"GET"
148+
);
149+
} else {
150+
$output = await parsePacket(outputPacket);
158151
}
159152
}
160153
}
@@ -165,6 +158,7 @@ export class ApiRetrieveRunPresenter extends BasePresenter {
165158
payloadPresignedUrl: $payloadPresignedUrl,
166159
output: $output,
167160
outputPresignedUrl: $outputPresignedUrl,
161+
error: ApiRetrieveRunPresenter.apiErrorFromError(taskRun.error),
168162
schedule: taskRun.schedule
169163
? {
170164
id: taskRun.schedule.friendlyId,
@@ -179,17 +173,9 @@ export class ApiRetrieveRunPresenter extends BasePresenter {
179173
},
180174
}
181175
: undefined,
182-
attempts: !showSecretDetails
183-
? []
184-
: taskRun.attempts.map((a) => ({
185-
id: a.friendlyId,
186-
status: ApiRetrieveRunPresenter.apiStatusFromAttemptStatus(a.status),
187-
createdAt: a.createdAt ?? undefined,
188-
updatedAt: a.updatedAt ?? undefined,
189-
startedAt: a.startedAt ?? undefined,
190-
completedAt: a.completedAt ?? undefined,
191-
error: ApiRetrieveRunPresenter.apiErrorFromError(a.error),
192-
})),
176+
// We're removing attempts from the API
177+
attemptCount: taskRun.attempts.length,
178+
attempts: [],
193179
relatedRuns: {
194180
root: taskRun.rootTaskRun
195181
? await createCommonRunStructure(taskRun.rootTaskRun)

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

Lines changed: 31 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ const CoercedDate = z.preprocess((arg) => {
2929
return arg;
3030
}, z.date().optional());
3131

32-
const SearchParamsSchema = z.object({
32+
export const ApiRunListSearchParams = z.object({
3333
"page[size]": z.coerce.number().int().positive().min(1).max(100).optional(),
3434
"page[after]": z.string().optional(),
3535
"page[before]": z.string().optional(),
@@ -121,58 +121,44 @@ const SearchParamsSchema = z.object({
121121
"filter[createdAt][period]": z.string().optional(),
122122
});
123123

124-
type SearchParamsSchema = z.infer<typeof SearchParamsSchema>;
124+
type ApiRunListSearchParams = z.infer<typeof ApiRunListSearchParams>;
125125

126126
export class ApiRunListPresenter extends BasePresenter {
127127
public async call(
128128
project: Project,
129-
searchParams: URLSearchParams,
129+
searchParams: ApiRunListSearchParams,
130130
environment?: RuntimeEnvironment
131131
): Promise<ListRunResponse> {
132132
return this.trace("call", async (span) => {
133-
const rawSearchParams = Object.fromEntries(searchParams.entries());
134-
const $searchParams = SearchParamsSchema.safeParse(rawSearchParams);
135-
136-
if (!$searchParams.success) {
137-
logger.error("Invalid search params", {
138-
searchParams: rawSearchParams,
139-
errors: $searchParams.error.errors,
140-
});
141-
142-
throw fromZodError($searchParams.error);
143-
}
144-
145-
logger.debug("Valid search params", { searchParams: $searchParams.data });
146-
147133
const options: RunListOptions = {
148134
projectId: project.id,
149135
};
150136

151137
// pagination
152-
if ($searchParams.data["page[size]"]) {
153-
options.pageSize = $searchParams.data["page[size]"];
138+
if (searchParams["page[size]"]) {
139+
options.pageSize = searchParams["page[size]"];
154140
}
155141

156-
if ($searchParams.data["page[after]"]) {
157-
options.cursor = $searchParams.data["page[after]"];
142+
if (searchParams["page[after]"]) {
143+
options.cursor = searchParams["page[after]"];
158144
options.direction = "forward";
159145
}
160146

161-
if ($searchParams.data["page[before]"]) {
162-
options.cursor = $searchParams.data["page[before]"];
147+
if (searchParams["page[before]"]) {
148+
options.cursor = searchParams["page[before]"];
163149
options.direction = "backward";
164150
}
165151

166152
// filters
167153
if (environment) {
168154
options.environments = [environment.id];
169155
} else {
170-
if ($searchParams.data["filter[env]"]) {
156+
if (searchParams["filter[env]"]) {
171157
const environments = await this._prisma.runtimeEnvironment.findMany({
172158
where: {
173159
projectId: project.id,
174160
slug: {
175-
in: $searchParams.data["filter[env]"],
161+
in: searchParams["filter[env]"],
176162
},
177163
},
178164
});
@@ -181,46 +167,46 @@ export class ApiRunListPresenter extends BasePresenter {
181167
}
182168
}
183169

184-
if ($searchParams.data["filter[status]"]) {
185-
options.statuses = $searchParams.data["filter[status]"].flatMap((status) =>
170+
if (searchParams["filter[status]"]) {
171+
options.statuses = searchParams["filter[status]"].flatMap((status) =>
186172
ApiRunListPresenter.apiStatusToRunStatuses(status)
187173
);
188174
}
189175

190-
if ($searchParams.data["filter[taskIdentifier]"]) {
191-
options.tasks = $searchParams.data["filter[taskIdentifier]"];
176+
if (searchParams["filter[taskIdentifier]"]) {
177+
options.tasks = searchParams["filter[taskIdentifier]"];
192178
}
193179

194-
if ($searchParams.data["filter[version]"]) {
195-
options.versions = $searchParams.data["filter[version]"];
180+
if (searchParams["filter[version]"]) {
181+
options.versions = searchParams["filter[version]"];
196182
}
197183

198-
if ($searchParams.data["filter[tag]"]) {
199-
options.tags = $searchParams.data["filter[tag]"];
184+
if (searchParams["filter[tag]"]) {
185+
options.tags = searchParams["filter[tag]"];
200186
}
201187

202-
if ($searchParams.data["filter[bulkAction]"]) {
203-
options.bulkId = $searchParams.data["filter[bulkAction]"];
188+
if (searchParams["filter[bulkAction]"]) {
189+
options.bulkId = searchParams["filter[bulkAction]"];
204190
}
205191

206-
if ($searchParams.data["filter[schedule]"]) {
207-
options.scheduleId = $searchParams.data["filter[schedule]"];
192+
if (searchParams["filter[schedule]"]) {
193+
options.scheduleId = searchParams["filter[schedule]"];
208194
}
209195

210-
if ($searchParams.data["filter[createdAt][from]"]) {
211-
options.from = $searchParams.data["filter[createdAt][from]"].getTime();
196+
if (searchParams["filter[createdAt][from]"]) {
197+
options.from = searchParams["filter[createdAt][from]"].getTime();
212198
}
213199

214-
if ($searchParams.data["filter[createdAt][to]"]) {
215-
options.to = $searchParams.data["filter[createdAt][to]"].getTime();
200+
if (searchParams["filter[createdAt][to]"]) {
201+
options.to = searchParams["filter[createdAt][to]"].getTime();
216202
}
217203

218-
if ($searchParams.data["filter[createdAt][period]"]) {
219-
options.period = $searchParams.data["filter[createdAt][period]"];
204+
if (searchParams["filter[createdAt][period]"]) {
205+
options.period = searchParams["filter[createdAt][period]"];
220206
}
221207

222-
if (typeof $searchParams.data["filter[isTest]"] === "boolean") {
223-
options.isTest = $searchParams.data["filter[isTest]"];
208+
if (typeof searchParams["filter[isTest]"] === "boolean") {
209+
options.isTest = searchParams["filter[isTest]"];
224210
}
225211

226212
const presenter = new RunListPresenter();
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import type { LoaderFunctionArgs } from "@remix-run/server-runtime";
2+
import { json } from "@remix-run/server-runtime";
3+
import { authenticateApiRequest } from "~/services/apiAuth.server";
4+
import { z } from "zod";
5+
import { generateJWT as internal_generateJWT } from "@trigger.dev/core/v3";
6+
7+
const RequestBodySchema = z.object({
8+
claims: z
9+
.object({
10+
scopes: z.array(z.string()).default([]),
11+
})
12+
.optional(),
13+
expirationTime: z.union([z.number(), z.string()]).optional(),
14+
});
15+
16+
export async function action({ request }: LoaderFunctionArgs) {
17+
// Next authenticate the request
18+
const authenticationResult = await authenticateApiRequest(request);
19+
20+
if (!authenticationResult) {
21+
return json({ error: "Invalid or Missing API key" }, { status: 401 });
22+
}
23+
24+
const parsedBody = RequestBodySchema.safeParse(await request.json());
25+
26+
if (!parsedBody.success) {
27+
return json(
28+
{ error: "Invalid request body", issues: parsedBody.error.issues },
29+
{ status: 400 }
30+
);
31+
}
32+
33+
const claims = {
34+
sub: authenticationResult.environment.id,
35+
pub: true,
36+
...parsedBody.data.claims,
37+
};
38+
39+
const jwt = await internal_generateJWT({
40+
secretKey: authenticationResult.apiKey,
41+
payload: claims,
42+
expirationTime: parsedBody.data.expirationTime ?? "1h",
43+
});
44+
45+
return json({ token: jwt });
46+
}

0 commit comments

Comments
 (0)