import { type RuntimeEnvironmentType, WaitpointTokenStatus } from "@trigger.dev/core/v3"; import { type RunEngineVersion } from "@trigger.dev/database"; import { z } from "zod"; import { type PrismaClientOrTransaction } from "~/db.server"; import { CoercedDate } from "~/utils/zod"; import { ServiceValidationError } from "~/v3/services/baseService.server"; import { BasePresenter } from "./basePresenter.server"; import { type WaitpointListOptions, WaitpointListPresenter } from "./WaitpointListPresenter.server"; export const ApiWaitpointListSearchParams = z.object({ "page[size]": z.coerce.number().int().positive().min(1).max(100).optional(), "page[after]": z.string().optional(), "page[before]": z.string().optional(), "filter[status]": z .string() .optional() .transform((value, ctx) => { if (!value) { return undefined; } const statuses = value.split(","); const parsedStatuses = statuses.map((status) => WaitpointTokenStatus.safeParse(status)); if (parsedStatuses.some((result) => !result.success)) { const invalidStatuses: string[] = []; for (const [index, result] of parsedStatuses.entries()) { if (!result.success) { invalidStatuses.push(statuses[index]); } } ctx.addIssue({ code: z.ZodIssueCode.custom, message: `Invalid status values: ${invalidStatuses.join(", ")}`, }); return z.NEVER; } const $statuses = parsedStatuses .map((result) => (result.success ? result.data : undefined)) .filter(Boolean); return Array.from(new Set($statuses)); }), "filter[idempotencyKey]": z.string().optional(), "filter[tags]": z .string() .optional() .transform((value) => { if (!value) return undefined; return value.split(","); }), "filter[createdAt][period]": z.string().optional(), "filter[createdAt][from]": CoercedDate, "filter[createdAt][to]": CoercedDate, }); type ApiWaitpointListSearchParams = z.infer; export class ApiWaitpointListPresenter extends BasePresenter { constructor( prismaClient?: PrismaClientOrTransaction, replicaClient?: PrismaClientOrTransaction, private readonly readRoute?: { runOpsNew?: PrismaClientOrTransaction; runOpsLegacyReplica?: PrismaClientOrTransaction; splitEnabled?: boolean; } ) { super(prismaClient, replicaClient); } public async call( environment: { id: string; type: RuntimeEnvironmentType; project: { id: string; engine: RunEngineVersion; }; apiKey: string; }, searchParams: ApiWaitpointListSearchParams ) { return this.trace("call", async (span) => { const options: WaitpointListOptions = { environment, }; if (searchParams["page[size]"]) { options.pageSize = searchParams["page[size]"]; } if (searchParams["page[after]"]) { options.cursor = searchParams["page[after]"]; options.direction = "forward"; } if (searchParams["page[before]"]) { options.cursor = searchParams["page[before]"]; options.direction = "backward"; } if (searchParams["filter[status]"]) { options.statuses = searchParams["filter[status]"]; } if (searchParams["filter[idempotencyKey]"]) { options.idempotencyKey = searchParams["filter[idempotencyKey]"]; } if (searchParams["filter[tags]"]) { options.tags = searchParams["filter[tags]"]; } if (searchParams["filter[createdAt][period]"]) { options.period = searchParams["filter[createdAt][period]"]; } if (searchParams["filter[createdAt][from]"]) { options.from = searchParams["filter[createdAt][from]"].getTime(); } if (searchParams["filter[createdAt][to]"]) { options.to = searchParams["filter[createdAt][to]"].getTime(); } const presenter = new WaitpointListPresenter(undefined, undefined, this.readRoute); const result = await presenter.call(options); if (!result.success) { throw new ServiceValidationError(result.error); } return { data: result.tokens, pagination: result.pagination, }; }); } }