import { json } from "@remix-run/server-runtime"; import { z } from "zod"; import { getTaskEventStoreTableForRun } from "~/v3/taskEventStore.server"; import { anyResource, createLoaderApiRoute } from "~/services/routeBuilders/apiBuilder.server"; import { ApiRetrieveRunPresenter } from "~/presenters/v3/ApiRetrieveRunPresenter.server"; import { getEventRepositoryForStore } from "~/v3/eventRepository/index.server"; const ParamsSchema = z.object({ runId: z.string(), // This is the run friendly ID }); // TODO: paginate the results export const loader = createLoaderApiRoute( { params: ParamsSchema, allowJWT: true, corsStrategy: "all", findResource: (params, auth) => { return ApiRetrieveRunPresenter.findRun(params.runId, auth.environment); }, shouldRetryNotFound: true, authorization: { action: "read", resource: (run) => { const resources = [ { type: "runs", id: run.friendlyId }, { type: "tasks", id: run.taskIdentifier }, ...run.runTags.map((tag) => ({ type: "tags", id: tag })), ]; if (run.batch?.friendlyId) { resources.push({ type: "batch", id: run.batch.friendlyId }); } return anyResource(resources); }, }, }, async ({ resource: run, authentication }) => { // Short-circuit for mollifier-buffered runs. The drainer hasn't // materialised execution events yet (the gate intercepts before // any trace event is written), so a ClickHouse round-trip is // guaranteed to come back empty. `findRun` now sets `isBuffered` // explicitly on its return value — gate on that rather than // probing surrogate fields like `traceId === ""`. if (run.isBuffered) { return json({ events: [] }, { status: 200 }); } const eventRepository = await getEventRepositoryForStore( run.taskEventStore, authentication.environment.organization.id ); const runEvents = await eventRepository.getRunEvents( getTaskEventStoreTableForRun(run), authentication.environment.id, run.traceId, run.friendlyId, run.createdAt, run.completedAt ?? undefined ); // TODO: return only relevant fields, avoid returning the whole events return json( { events: runEvents.map((event) => { return JSON.parse( JSON.stringify(event, (_, value) => // needed as JSON.stringify doesn't know how to handle BigInt values by default typeof value === "bigint" ? value.toString() : value ) ); }), }, { status: 200 } ); } );