Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .server-changes/fix-scheduled-task-test-page-empty-payload.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
area: webapp
type: fix
---

Prevent scheduled task test page crashes when prior runs have empty payloads

11 changes: 2 additions & 9 deletions apps/webapp/app/presenters/v3/TestTaskPresenter.server.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { ClickHouse } from "@internal/clickhouse";
import { ScheduledTaskPayload, parsePacket, prettyPrintPacket } from "@trigger.dev/core/v3";
import { parsePacket, prettyPrintPacket } from "@trigger.dev/core/v3";
import {
type RuntimeEnvironmentType,
type TaskRunStatus,
Expand All @@ -13,6 +13,7 @@ import { RunsRepository } from "~/services/runsRepository/runsRepository.server"
import { getTimezones } from "~/utils/timezones.server";
import { findCurrentWorkerDeployment } from "~/v3/models/workerDeployment.server";
import { queueTypeFromType } from "./QueueRetrievePresenter.server";
import { getScheduleTaskRunPayload } from "./getScheduleTaskRunPayload.server";

export type RunTemplate = TaskRunTemplate & {
scheduledTaskPayload?: ScheduledRun["payload"];
Expand Down Expand Up @@ -380,11 +381,3 @@ export class TestTaskPresenter {
}
}

async function getScheduleTaskRunPayload(payload: string, payloadType: string) {
const packet = await parsePacket({ data: payload, dataType: payloadType });
if (!packet.timezone) {
packet.timezone = "UTC";
}
const parsed = ScheduledTaskPayload.safeParse(packet);
return parsed;
}
22 changes: 22 additions & 0 deletions apps/webapp/app/presenters/v3/getScheduleTaskRunPayload.server.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { ScheduledTaskPayload, parsePacket } from "@trigger.dev/core/v3";

export async function getScheduleTaskRunPayload(payload: string, payloadType: string) {
let packet: unknown;

try {
packet = await parsePacket({ data: payload, dataType: payloadType });
} catch {
packet = undefined;
}

if (packet && typeof packet === "object" && !Array.isArray(packet)) {
const maybeTimezone = (packet as { timezone?: unknown }).timezone;

if (typeof maybeTimezone !== "string" || maybeTimezone.length === 0) {
(packet as { timezone: string }).timezone = "UTC";
}
}

return ScheduledTaskPayload.safeParse(packet);
}

44 changes: 44 additions & 0 deletions apps/webapp/test/scheduledTaskTestPageEmptyPayload.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { describe, it, expect } from "vitest";
import superjson from "superjson";
import { getScheduleTaskRunPayload } from "../app/presenters/v3/getScheduleTaskRunPayload.server";

describe("getScheduleTaskRunPayload", () => {
it("should return failure when payload is empty", async () => {
const result = await getScheduleTaskRunPayload("", "application/json");

expect(result.success).toBe(false);
if (!result.success) {
expect(result.error).toBeDefined();
}
});

it("should parse a valid scheduled payload", async () => {
const now = new Date();
const result = await getScheduleTaskRunPayload(
superjson.stringify({
scheduleId: "sch_123",
type: "DECLARATIVE",
timestamp: now,
timezone: "UTC",
upcoming: [now],
}),
"application/super+json"
);

expect(result.success).toBe(true);
if (result.success) {
expect(result.data.scheduleId).toBe("sch_123");
expect(result.data.type).toBe("DECLARATIVE");
expect(result.data.timezone).toBe("UTC");
expect(result.data.upcoming.length).toBe(1);
expect(result.data.timestamp).toBeInstanceOf(Date);
}
});

it("should return failure for invalid JSON", async () => {
const result = await getScheduleTaskRunPayload("{invalid", "application/json");

expect(result.success).toBe(false);
});
});