chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,161 @@
|
||||
import type { ActionFunctionArgs } from "@remix-run/server-runtime";
|
||||
import { json } from "@remix-run/server-runtime";
|
||||
import type { BatchTriggerTaskV2RequestBody } from "@trigger.dev/core/v3";
|
||||
import { BatchTriggerTaskRequestBody } from "@trigger.dev/core/v3";
|
||||
import { z } from "zod";
|
||||
import { fromZodError } from "zod-validation-error";
|
||||
import { MAX_BATCH_TRIGGER_ITEMS } from "~/consts";
|
||||
import { clientSafeErrorMessage } from "~/utils/prismaErrors";
|
||||
import { env } from "~/env.server";
|
||||
import { authenticateApiRequest } from "~/services/apiAuth.server";
|
||||
import { logger } from "~/services/logger.server";
|
||||
import { BatchTriggerV3Service } from "~/v3/services/batchTriggerV3.server";
|
||||
import { HeadersSchema } from "./api.v1.tasks.$taskId.trigger";
|
||||
import { determineRealtimeStreamsVersion } from "~/services/realtime/v1StreamsGlobal.server";
|
||||
|
||||
const ParamsSchema = z.object({
|
||||
taskId: z.string(),
|
||||
});
|
||||
|
||||
export async function action({ request, params }: ActionFunctionArgs) {
|
||||
// Ensure this is a POST request
|
||||
if (request.method.toUpperCase() !== "POST") {
|
||||
return { status: 405, body: "Method Not Allowed" };
|
||||
}
|
||||
|
||||
// Next authenticate the request
|
||||
const authenticationResult = await authenticateApiRequest(request);
|
||||
|
||||
if (!authenticationResult) {
|
||||
return json({ error: "Invalid or Missing API key" }, { status: 401 });
|
||||
}
|
||||
|
||||
const rawHeaders = Object.fromEntries(request.headers);
|
||||
|
||||
const headers = HeadersSchema.safeParse(rawHeaders);
|
||||
|
||||
if (!headers.success) {
|
||||
return json({ error: "Invalid headers" }, { status: 400 });
|
||||
}
|
||||
|
||||
const {
|
||||
"idempotency-key": idempotencyKey,
|
||||
"trigger-version": triggerVersion,
|
||||
"x-trigger-span-parent-as-link": spanParentAsLink,
|
||||
"x-trigger-worker": isFromWorker,
|
||||
"x-trigger-realtime-streams-version": realtimeStreamsVersion,
|
||||
traceparent,
|
||||
tracestate,
|
||||
} = headers.data;
|
||||
|
||||
const { taskId } = ParamsSchema.parse(params);
|
||||
|
||||
const contentLength = request.headers.get("content-length");
|
||||
|
||||
if (!contentLength || parseInt(contentLength) > env.TASK_PAYLOAD_MAXIMUM_SIZE) {
|
||||
return json({ error: "Request body too large" }, { status: 413 });
|
||||
}
|
||||
|
||||
// Now parse the request body
|
||||
const anyBody = await request.json();
|
||||
|
||||
const body = BatchTriggerTaskRequestBody.safeParse(anyBody);
|
||||
|
||||
if (!body.success) {
|
||||
return json(
|
||||
{ error: fromZodError(body.error, { prefix: "Invalid batchTrigger call" }).toString() },
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
|
||||
logger.debug("Triggering batch", {
|
||||
taskId,
|
||||
idempotencyKey,
|
||||
triggerVersion,
|
||||
body: body.data,
|
||||
});
|
||||
|
||||
if (!body.data.items.length) {
|
||||
return json({ error: "No items to trigger" }, { status: 400 });
|
||||
}
|
||||
|
||||
// Check the there are fewer than 100 items
|
||||
if (body.data.items.length > MAX_BATCH_TRIGGER_ITEMS) {
|
||||
return json(
|
||||
{
|
||||
error: `Too many items. Maximum allowed batch size is ${MAX_BATCH_TRIGGER_ITEMS}.`,
|
||||
},
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
|
||||
const service = new BatchTriggerV3Service();
|
||||
|
||||
const traceContext =
|
||||
traceparent && isFromWorker // If the request is from a worker, we should pass the trace context
|
||||
? { traceparent, tracestate }
|
||||
: undefined;
|
||||
|
||||
const v3Body = convertV1BodyToV2Body(body.data, taskId);
|
||||
|
||||
try {
|
||||
const result = await service.call(authenticationResult.environment, v3Body, {
|
||||
idempotencyKey: idempotencyKey ?? undefined,
|
||||
triggerVersion: triggerVersion ?? undefined,
|
||||
traceContext,
|
||||
spanParentAsLink: spanParentAsLink === 1,
|
||||
realtimeStreamsVersion: determineRealtimeStreamsVersion(realtimeStreamsVersion ?? undefined),
|
||||
});
|
||||
|
||||
if (!result) {
|
||||
return json({ error: "Task not found" }, { status: 404 });
|
||||
}
|
||||
|
||||
return json(
|
||||
{
|
||||
batchId: result.id,
|
||||
runs: result.runs.map((run) => run.id),
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
"x-trigger-jwt-claims": JSON.stringify({
|
||||
sub: authenticationResult.environment.id,
|
||||
pub: true,
|
||||
}),
|
||||
},
|
||||
}
|
||||
);
|
||||
} catch (error) {
|
||||
if (error instanceof Error) {
|
||||
return json({ error: clientSafeErrorMessage(error) }, { status: 400 });
|
||||
}
|
||||
|
||||
return json({ error: "Something went wrong" }, { status: 500 });
|
||||
}
|
||||
}
|
||||
|
||||
// Strip from options:
|
||||
// - dependentBatch
|
||||
// - dependentAttempt
|
||||
// - parentBatch
|
||||
function convertV1BodyToV2Body(
|
||||
body: BatchTriggerTaskRequestBody,
|
||||
taskIdentifier: string
|
||||
): BatchTriggerTaskV2RequestBody {
|
||||
return {
|
||||
items: body.items.map((item) => ({
|
||||
task: taskIdentifier,
|
||||
payload: item.payload,
|
||||
context: item.context,
|
||||
options: item.options
|
||||
? {
|
||||
...item.options,
|
||||
dependentBatch: undefined,
|
||||
parentBatch: undefined,
|
||||
dependentAttempt: undefined,
|
||||
}
|
||||
: undefined,
|
||||
})),
|
||||
dependentAttempt: body.dependentAttempt,
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user