feat: allow cancelling job runs for an event-id

This commit is contained in:
hmacr
2023-10-15 19:06:03 +05:30
committed by Eric Allam
parent 9900f19b70
commit 6e1b8a11d4
9 changed files with 203 additions and 0 deletions
+6
View File
@@ -0,0 +1,6 @@
---
"@trigger.dev/sdk": patch
"@trigger.dev/core": patch
---
implement functionality to cancel job runs triggered by a given eventId.
@@ -0,0 +1,49 @@
import type { ActionArgs } from "@remix-run/server-runtime";
import { json } from "@remix-run/server-runtime";
import { z } from "zod";
import { authenticateApiRequest } from "~/services/apiAuth.server";
import { CancelEventService } from "~/services/events/cancelEvent.server";
import { logger } from "~/services/logger.server";
import { CancelRunsForEventService } from "~/services/events/cancelRunsForEvent.server";
const ParamsSchema = z.object({
eventId: z.string(),
});
export async function action({ request, params }: ActionArgs) {
// 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 authenticatedEnv = authenticationResult.environment;
const parsed = ParamsSchema.safeParse(params);
if (!parsed.success) {
return json({ error: "Invalid or Missing eventId" }, { status: 400 });
}
const { eventId } = parsed.data;
const service = new CancelRunsForEventService();
try {
const res = await service.call(authenticatedEnv, eventId);
if (!res) {
return json({ error: "Event not found" }, { status: 404 });
}
return json(res);
} catch (err) {
logger.error("CancelRunsForEventService.call() error", { error: err });
return json({ error: "Internal Server Error" }, { status: 500 });
}
}
@@ -0,0 +1,70 @@
import { $transaction, PrismaClient, prisma } from "~/db.server";
import { AuthenticatedEnvironment } from "../apiAuth.server";
import { JobRunStatus } from "@trigger.dev/database";
import { CancelRunService } from "../runs/cancelRun.server";
import { logger } from "../logger.server";
import { CancelRunsForEvent } from "@trigger.dev/core/schemas/events";
const CANCELLABLE_JOB_RUN_STATUS: JobRunStatus[] = [
JobRunStatus.PENDING,
JobRunStatus.QUEUED,
JobRunStatus.WAITING_ON_CONNECTIONS,
JobRunStatus.PREPROCESSING,
JobRunStatus.STARTED,
];
export class CancelRunsForEventService {
#prismaClient: PrismaClient;
constructor(prismaClient: PrismaClient = prisma) {
this.#prismaClient = prismaClient;
}
public async call(environment: AuthenticatedEnvironment, eventId: string) {
return await $transaction<CancelRunsForEvent | undefined>(this.#prismaClient, async (tx) => {
const event = await tx.eventRecord.findUnique({
where: {
eventId_environmentId: {
eventId: eventId,
environmentId: environment.id,
},
},
});
if (!event) {
return;
}
const jobRuns = await tx.jobRun.findMany({
where: {
eventId: event.id,
status: {
in: CANCELLABLE_JOB_RUN_STATUS,
},
},
select: {
id: true,
},
});
const cancelRunService = new CancelRunService(this.#prismaClient);
const cancelledRunIds: string[] = [];
const failedToCancelRunIds: string[] = [];
for (const jobRun of jobRuns) {
try {
await cancelRunService.call({ runId: jobRun.id });
cancelledRunIds.push(jobRun.id);
} catch (err) {
logger.debug(`failed to cancel job run with id ${jobRun.id} for event id ${eventId}`);
failedToCancelRunIds.push(jobRun.id);
}
}
return {
cancelled_run_ids: cancelledRunIds,
failed_to_cancel_run_ids: failedToCancelRunIds,
};
});
}
}
+1
View File
@@ -312,6 +312,7 @@
"sdk/triggerclient/instancemethods/sendevent", "sdk/triggerclient/instancemethods/sendevent",
"sdk/triggerclient/instancemethods/getevent", "sdk/triggerclient/instancemethods/getevent",
"sdk/triggerclient/instancemethods/cancel-event", "sdk/triggerclient/instancemethods/cancel-event",
"sdk/triggerclient/instancemethods/cancel-runs-for-event",
"sdk/triggerclient/instancemethods/getruns", "sdk/triggerclient/instancemethods/getruns",
"sdk/triggerclient/instancemethods/getrun", "sdk/triggerclient/instancemethods/getrun",
"sdk/triggerclient/instancemethods/define-job", "sdk/triggerclient/instancemethods/define-job",
@@ -0,0 +1,41 @@
---
title: "TriggerClient: cancelRunsForEvent() Instance Method"
sidebarTitle: "cancelRunsForEvent()"
description: "The `cancelRunsForEvent()` instance method will cancel all the job runs (yet to be executed) that are triggered by a given eventId."
---
## Parameters
<ResponseField name="eventId" type="string" required>
The event ID to cancel the job runs for. This is returned when calling either
[client.sendEvent()](/sdk/triggerclient/instancemethods/sendevent) or
[io.sendEvent()](/sdk/io/sendevent).
</ResponseField>
## Returns
<ResponseField type="object">
<Expandable title="properties" defaultOpen>
<ResponseField name="cancelled_run_ids" type="array" required>
List of Job Run IDs that are cancelled.
</ResponseField>
<ResponseField name="failed_to_cancel_run_ids" type="array" required>
List of Job Run IDs that have been failed to be cancelled.
</ResponseField>
</Expandable>
</ResponseField>
<RequestExample>
```ts Cancelling Runs for an Event
const event = client.sendEvent({
name: "test.job",
});
const res = client.cancelRunsForEvent(event.id);
console.log(res.cancelled_run_ids);
console.log(res.failed_to_cancel_run_ids);
```
</RequestExample>
+4
View File
@@ -46,6 +46,10 @@ The `getEvent()` method gets the event details for a given eventId.
The `cancelEvent()` method cancels an event that is scheduled to be delivered in the future. The `cancelEvent()` method cancels an event that is scheduled to be delivered in the future.
#### [cancelRunsForEvent()](/sdk/triggerclient/instancemethods/cancel-runs-for-event)
The `cancelRunsForEvent()` method cancels the job runs (yet to be executed) that are triggered by a given eventId.
#### [getRuns()](/sdk/triggerclient/instancemethods/getruns) #### [getRuns()](/sdk/triggerclient/instancemethods/getruns)
The `getRuns()` method gets runs for a Job. The `getRuns()` method gets runs for a Job.
+7
View File
@@ -26,3 +26,10 @@ export const GetEventSchema = z.object({
}); });
export type GetEvent = z.infer<typeof GetEventSchema>; export type GetEvent = z.infer<typeof GetEventSchema>;
export const CancelRunsForEventSchema = z.object({
cancelled_run_ids: z.array(z.string()),
failed_to_cancel_run_ids: z.array(z.string()),
});
export type CancelRunsForEvent = z.infer<typeof CancelRunsForEventSchema>;
+21
View File
@@ -1,6 +1,7 @@
import { import {
ApiEventLog, ApiEventLog,
ApiEventLogSchema, ApiEventLogSchema,
CancelRunsForEventSchema,
CompleteTaskBodyInput, CompleteTaskBodyInput,
ConnectionAuthSchema, ConnectionAuthSchema,
FailTaskBodyInput, FailTaskBodyInput,
@@ -215,6 +216,26 @@ export class ApiClient {
}); });
} }
async cancelRunsForEvent(eventId: string) {
const apiKey = await this.#apiKey();
this.#logger.debug("Cancelling runs for event", {
eventId,
});
return await zodfetch(
CancelRunsForEventSchema,
`${this.#apiUrl}/api/v1/events/${eventId}/cancel-runs`,
{
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${apiKey}`,
},
}
);
}
async updateStatus(runId: string, id: string, status: StatusUpdate) { async updateStatus(runId: string, id: string, status: StatusUpdate) {
const apiKey = await this.#apiKey(); const apiKey = await this.#apiKey();
@@ -644,6 +644,10 @@ export class TriggerClient {
return this.#client.cancelEvent(eventId); return this.#client.cancelEvent(eventId);
} }
async cancelRunsForEvent(eventId: string) {
return this.#client.cancelRunsForEvent(eventId);
}
async updateStatus(runId: string, id: string, status: StatusUpdate) { async updateStatus(runId: string, id: string, status: StatusUpdate) {
return this.#client.updateStatus(runId, id, status); return this.#client.updateStatus(runId, id, status);
} }