Skip to content

feat: Run metadata #1357

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 10 commits into from
Sep 26, 2024
Merged
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
6 changes: 6 additions & 0 deletions .changeset/tasty-rats-rhyme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@trigger.dev/sdk": patch
"@trigger.dev/core": patch
---

Add Run metadata to allow for storing up to 4KB of data on a run and update it during the run
4 changes: 2 additions & 2 deletions apps/webapp/app/components/code/JSONEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -112,9 +112,9 @@ export function JSONEditor(opts: JSONEditorProps) {
return (
<div
className={cn(
opts.className,
"grid",
showButtons ? "grid-rows-[2.5rem_1fr]" : "grid-rows-[1fr]"
showButtons ? "grid-rows-[2.5rem_1fr]" : "grid-rows-[1fr]",
opts.className
)}
>
{showButtons && (
Expand Down
1 change: 1 addition & 0 deletions apps/webapp/app/components/primitives/Tabs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ export function TabButton({
return (
<button
className={cn("group flex flex-col items-center pt-1", props.className)}
type="button"
ref={ref}
{...props}
>
Expand Down
1 change: 1 addition & 0 deletions apps/webapp/app/env.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,7 @@ const EnvironmentSchema = z.object({
MAXIMUM_TRACE_SUMMARY_VIEW_COUNT: z.coerce.number().int().default(25_000),
TASK_PAYLOAD_OFFLOAD_THRESHOLD: z.coerce.number().int().default(524_288), // 512KB
TASK_PAYLOAD_MAXIMUM_SIZE: z.coerce.number().int().default(3_145_728), // 3MB
TASK_RUN_METADATA_MAXIMUM_SIZE: z.coerce.number().int().default(4_096), // 4KB
});

export type Environment = z.infer<typeof EnvironmentSchema>;
Expand Down
24 changes: 17 additions & 7 deletions apps/webapp/app/presenters/v3/ApiRetrieveRunPresenter.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ const commonRunSelect = {
completedAt: true,
expiredAt: true,
delayUntil: true,
metadata: true,
metadataType: true,
ttl: true,
tags: true,
costInCents: true,
Expand Down Expand Up @@ -157,10 +159,8 @@ export class ApiRetrieveRunPresenter extends BasePresenter {
}
}

const apiStatus = ApiRetrieveRunPresenter.apiStatusFromRunStatus(taskRun.status);

return {
...createCommonRunStructure(taskRun),
...(await createCommonRunStructure(taskRun)),
payload: $payload,
payloadPresignedUrl: $payloadPresignedUrl,
output: $output,
Expand Down Expand Up @@ -191,11 +191,15 @@ export class ApiRetrieveRunPresenter extends BasePresenter {
error: ApiRetrieveRunPresenter.apiErrorFromError(a.error),
})),
relatedRuns: {
root: taskRun.rootTaskRun ? createCommonRunStructure(taskRun.rootTaskRun) : undefined,
root: taskRun.rootTaskRun
? await createCommonRunStructure(taskRun.rootTaskRun)
: undefined,
parent: taskRun.parentTaskRun
? createCommonRunStructure(taskRun.parentTaskRun)
? await createCommonRunStructure(taskRun.parentTaskRun)
: undefined,
children: taskRun.childRuns.map((r) => createCommonRunStructure(r)),
children: await Promise.all(
taskRun.childRuns.map(async (r) => await createCommonRunStructure(r))
),
},
};
});
Expand Down Expand Up @@ -329,7 +333,12 @@ export class ApiRetrieveRunPresenter extends BasePresenter {
}
}

function createCommonRunStructure(run: CommonRelatedRun) {
async function createCommonRunStructure(run: CommonRelatedRun) {
const metadata = await parsePacket({
data: run.metadata ?? undefined,
dataType: run.metadataType,
});

Comment on lines +336 to +341
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Add error handling for parsePacket in createCommonRunStructure

If parsePacket throws an error while parsing metadata, it could result in an unhandled exception, potentially affecting the API response. Consider adding error handling to gracefully manage any parsing errors and provide a default value if necessary.

Here’s a suggested modification:

async function createCommonRunStructure(run: CommonRelatedRun) {
+  let metadata;
+  try {
    metadata = await parsePacket({
      data: run.metadata ?? undefined,
      dataType: run.metadataType,
    });
+  } catch (error) {
+    // Optionally log the error
+    metadata = undefined; // or assign a default value
+  }

  return {
    // ... other properties
    metadata,
  };
}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
async function createCommonRunStructure(run: CommonRelatedRun) {
const metadata = await parsePacket({
data: run.metadata ?? undefined,
dataType: run.metadataType,
});
async function createCommonRunStructure(run: CommonRelatedRun) {
let metadata;
try {
metadata = await parsePacket({
data: run.metadata ?? undefined,
dataType: run.metadataType,
});
} catch (error) {
// Optionally log the error
metadata = undefined; // or assign a default value
}

return {
id: run.friendlyId,
taskIdentifier: run.taskIdentifier,
Expand All @@ -354,6 +363,7 @@ function createCommonRunStructure(run: CommonRelatedRun) {
...ApiRetrieveRunPresenter.apiBooleanHelpersFromTaskRunStatus(run.status),
triggerFunction: resolveTriggerFunction(run),
batchId: run.batch?.friendlyId,
metadata,
};
}

Expand Down
14 changes: 13 additions & 1 deletion apps/webapp/app/presenters/v3/SpanPresenter.server.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
import { MachinePresetName, prettyPrintPacket, TaskRunError } from "@trigger.dev/core/v3";
import {
MachinePresetName,
parsePacket,
prettyPrintPacket,
TaskRunError,
} from "@trigger.dev/core/v3";
import { RUNNING_STATUSES } from "~/components/runs/v3/TaskRunStatus";
import { eventRepository } from "~/v3/eventRepository.server";
import { machinePresetFromName } from "~/v3/machinePresets.server";
Expand Down Expand Up @@ -113,6 +118,8 @@ export class SpanPresenter extends BasePresenter {
},
payload: true,
payloadType: true,
metadata: true,
metadataType: true,
maxAttempts: true,
project: {
include: {
Expand Down Expand Up @@ -185,6 +192,10 @@ export class SpanPresenter extends BasePresenter {

const span = await eventRepository.getSpan(spanId, run.traceId);

const metadata = run.metadata
? await prettyPrintPacket(run.metadata, run.metadataType)
: undefined;

const context = {
task: {
id: run.taskIdentifier,
Expand Down Expand Up @@ -272,6 +283,7 @@ export class SpanPresenter extends BasePresenter {
error,
links: span?.links,
context: JSON.stringify(context, null, 2),
metadata,
};
}

Expand Down
16 changes: 9 additions & 7 deletions apps/webapp/app/presenters/v3/TestTaskPresenter.server.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,6 @@
import { ScheduledTaskPayload, parsePacket, prettyPrintPacket } from "@trigger.dev/core/v3";
import {
RuntimeEnvironmentType,
TaskRunAttemptStatus,
TaskRunStatus,
TaskTriggerSource,
} from "@trigger.dev/database";
import { sqlDatabaseSchema, PrismaClient, prisma } from "~/db.server";
import { RuntimeEnvironmentType, TaskRunStatus } from "@trigger.dev/database";
import { PrismaClient, prisma, sqlDatabaseSchema } from "~/db.server";
import { getTimezones } from "~/utils/timezones.server";
import { getUsername } from "~/utils/username";

Expand Down Expand Up @@ -51,6 +46,8 @@ type RawRun = {
payload: string;
payloadType: string;
runtimeEnvironmentId: string;
metadata?: string;
metadataType?: string;
};

export type StandardRun = Omit<RawRun, "number"> & {
Expand Down Expand Up @@ -131,6 +128,8 @@ export class TestTaskPresenter {
taskr.status,
taskr.payload,
taskr."payloadType",
taskr.metadata,
taskr."metadataType",
taskr."runtimeEnvironmentId"
FROM
taskruns AS taskr
Expand Down Expand Up @@ -166,6 +165,9 @@ export class TestTaskPresenter {
...r,
number,
payload: await prettyPrintPacket(r.payload, r.payloadType),
metadata: r.metadata
? await prettyPrintPacket(r.metadata, r.metadataType)
: undefined,
};
})
),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,11 @@ import {
ResizablePanelGroup,
} from "~/components/primitives/Resizable";
import { Select } from "~/components/primitives/Select";
import { TabButton, TabContainer } from "~/components/primitives/Tabs";
import { TextLink } from "~/components/primitives/TextLink";
import { TaskRunStatusCombo } from "~/components/runs/v3/TaskRunStatus";
import { TimezoneList } from "~/components/scheduled/timezones";
import { useSearchParams } from "~/hooks/useSearchParam";
import { redirectBackWithErrorMessage, redirectWithSuccessMessage } from "~/models/message.server";
import {
ScheduledRun,
Expand All @@ -39,6 +41,7 @@ import {
} from "~/presenters/v3/TestTaskPresenter.server";
import { logger } from "~/services/logger.server";
import { requireUserId } from "~/services/session.server";
import { cn } from "~/utils/cn";
import { docsPath, v3RunSpanPath, v3TaskParamsSchema } from "~/utils/pathBuilder";
import { TestTaskService } from "~/v3/services/testTask.server";
import { OutOfEntitlementError } from "~/v3/services/triggerTask.server";
Expand Down Expand Up @@ -129,27 +132,44 @@ export default function Page() {
const startingJson = "{\n\n}";

function StandardTaskForm({ task, runs }: { task: TestTask["task"]; runs: StandardRun[] }) {
const { value, replace } = useSearchParams();
const tab = value("tab");

//form submission
const submit = useSubmit();
const lastSubmission = useActionData();

//recent runs
const [selectedCodeSampleId, setSelectedCodeSampleId] = useState(runs.at(0)?.id);
const selectedCodeSample = runs.find((r) => r.id === selectedCodeSampleId)?.payload;
const selectedCodeSample = runs.find((r) => r.id === selectedCodeSampleId);
const selectedCodeSamplePayload = selectedCodeSample?.payload;
const selectedCodeSampleMetadata = selectedCodeSample?.metadata;

const [defaultJson, setDefaultJson] = useState<string>(selectedCodeSample ?? startingJson);
const setCode = useCallback((code: string) => {
setDefaultJson(code);
const [defaultPayloadJson, setDefaultPayloadJson] = useState<string>(
selectedCodeSamplePayload ?? startingJson
);
const setPayload = useCallback((code: string) => {
setDefaultPayloadJson(code);
}, []);

const currentJson = useRef<string>(defaultJson);
const currentPayloadJson = useRef<string>(defaultPayloadJson);

const [defaultMetadataJson, setDefaultMetadataJson] = useState<string>(
selectedCodeSampleMetadata ?? "{}"
);
const setMetadata = useCallback((code: string) => {
setDefaultMetadataJson(code);
}, []);

const currentMetadataJson = useRef<string>(defaultMetadataJson);

const submitForm = useCallback(
(e: React.FormEvent<HTMLFormElement>) => {
submit(
{
triggerSource: "STANDARD",
payload: currentJson.current,
payload: currentPayloadJson.current,
metadata: currentMetadataJson.current,
taskIdentifier: task.taskIdentifier,
environmentId: task.environment.id,
},
Expand All @@ -160,7 +180,7 @@ function StandardTaskForm({ task, runs }: { task: TestTask["task"]; runs: Standa
);
e.preventDefault();
},
[currentJson]
[currentPayloadJson, currentMetadataJson]
);

const [form, { environmentId, payload }] = useForm({
Expand All @@ -183,28 +203,73 @@ function StandardTaskForm({ task, runs }: { task: TestTask["task"]; runs: Standa
<ResizablePanelGroup orientation="horizontal">
<ResizablePanel id="test-task-main" min="100px" default="60%">
<div className="h-full bg-charcoal-900">
<JSONEditor
defaultValue={defaultJson}
readOnly={false}
basicSetup
onChange={(v) => {
currentJson.current = v;

//deselect the example if it's been edited
if (selectedCodeSampleId) {
if (v !== selectedCodeSample) {
setDefaultJson(v);
setSelectedCodeSampleId(undefined);
<TabContainer className="px-3 pt-2">
<TabButton
isActive={!tab || tab === "payload"}
layoutId="test-editor"
onClick={() => {
replace({ tab: "payload" });
}}
>
Payload
</TabButton>

<TabButton
isActive={tab === "metadata"}
layoutId="test-editor"
onClick={() => {
replace({ tab: "metadata" });
}}
>
Metadata
</TabButton>
</TabContainer>
<div>
<JSONEditor
defaultValue={defaultPayloadJson}
readOnly={false}
basicSetup
onChange={(v) => {
currentPayloadJson.current = v;

//deselect the example if it's been edited
if (selectedCodeSampleId) {
if (v !== selectedCodeSamplePayload) {
setDefaultPayloadJson(v);
setSelectedCodeSampleId(undefined);
}
}
}
}}
height="100%"
min-height="100%"
max-height="100%"
autoFocus
placeholder="Use your schema to enter valid JSON or add one of the recent payloads then click 'Run test'"
className="h-full"
/>
}}
height="100%"
min-height="100%"
max-height="100%"
autoFocus={!tab || tab === "payload"}
placeholder="{ }"
className={cn("h-full", tab === "metadata" && "hidden")}
/>
<JSONEditor
defaultValue={defaultMetadataJson}
readOnly={false}
basicSetup
onChange={(v) => {
currentMetadataJson.current = v;

//deselect the example if it's been edited
if (selectedCodeSampleId) {
if (v !== selectedCodeSampleMetadata) {
setDefaultMetadataJson(v);
setSelectedCodeSampleId(undefined);
}
}
}}
height="100%"
min-height="100%"
max-height="100%"
autoFocus={tab === "metadata"}
placeholder=""
className={cn("h-full", tab !== "metadata" && "hidden")}
/>
</div>
</div>
</ResizablePanel>
<ResizableHandle id="test-task-handle" />
Expand All @@ -213,9 +278,10 @@ function StandardTaskForm({ task, runs }: { task: TestTask["task"]; runs: Standa
runs={runs}
selectedId={selectedCodeSampleId}
onSelected={(id) => {
const payload = runs.find((r) => r.id === id)?.payload;
if (!payload) return;
setCode(payload);
const run = runs.find((r) => r.id === id);
if (!run) return;
setPayload(run.payload);
run.metadata && setMetadata(run.metadata);
setSelectedCodeSampleId(id);
}}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,13 @@ export default function Page() {
const navigation = useNavigation();

const location = useLocation();
const locationSearchParams = new URLSearchParams(location.search);
const navigationSearchParams = new URLSearchParams(navigation.location?.search);

const isLoadingTasks =
navigation.state === "loading" && navigation.location.pathname === location.pathname;
navigation.state === "loading" &&
navigation.location.pathname === location.pathname &&
navigationSearchParams.get("environment") !== locationSearchParams.get("environment");

return (
<PageContainer>
Expand Down
Loading