Skip to content
Merged
Show file tree
Hide file tree
Changes from 11 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
5 changes: 4 additions & 1 deletion apps/webapp/app/components/runs/v3/RunIcon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
InformationCircleIcon,
RectangleStackIcon,
Squares2X2Icon,
TableCellsIcon,
TagIcon,
} from "@heroicons/react/20/solid";
import { AttemptIcon } from "~/assets/icons/AttemptIcon";
Expand Down Expand Up @@ -81,6 +82,8 @@ export function RunIcon({ name, className, spanName }: TaskIconProps) {
return <WaitpointTokenIcon className={cn(className, "text-sky-500")} />;
case "function":
return <FunctionIcon className={cn(className, "text-text-dimmed")} />;
case "query":
return <TableCellsIcon className={cn(className, "text-query")} />;
//log levels
case "debug":
case "log":
Expand Down Expand Up @@ -110,7 +113,7 @@ export function RunIcon({ name, className, spanName }: TaskIconProps) {
case "task-hook-catchError":
return <FunctionIcon className={cn(className, "text-error")} />;
case "streams":
return <StreamsIcon className={cn(className, "text-text-dimmed")} />;
return <StreamsIcon className={cn(className, "text-text-dimmed")} />;
}

return <InformationCircleIcon className={cn(className, "text-text-dimmed")} />;
Expand Down
24 changes: 4 additions & 20 deletions apps/webapp/app/components/runs/v3/TaskRunStatus.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
XCircleIcon,
} from "@heroicons/react/20/solid";
import type { TaskRunStatus } from "@trigger.dev/database";
import { runFriendlyStatus, type RunFriendlyStatus } from "@trigger.dev/core/v3";
import assertNever from "assert-never";
import { HourglassIcon } from "lucide-react";
import { TimedOutIcon } from "~/assets/icons/TimedOutIcon";
Expand Down Expand Up @@ -248,26 +249,9 @@ export function runStatusFromFriendlyTitle(friendly: RunFriendlyStatus): TaskRun
return result[0] as TaskRunStatus;
}

export const runFriendlyStatus = [
"Delayed",
"Queued",
"Pending version",
"Dequeued",
"Executing",
"Waiting",
"Reattempting",
"Paused",
"Canceled",
"Interrupted",
"Completed",
"Failed",
"System failure",
"Crashed",
"Expired",
"Timed out",
] as const;

export type RunFriendlyStatus = (typeof runFriendlyStatus)[number];
// runFriendlyStatus and RunFriendlyStatus are imported from @trigger.dev/core/v3
// and re-exported here for backward compatibility.
export { runFriendlyStatus, type RunFriendlyStatus } from "@trigger.dev/core/v3";

/**
* Check if a value is a valid TaskRunStatus
Expand Down
77 changes: 77 additions & 0 deletions apps/webapp/app/routes/api.v1.query.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import { json } from "@remix-run/server-runtime";
import { QueryError } from "@internal/clickhouse";
import { z } from "zod";
import { createActionApiRoute } from "~/services/routeBuilders/apiBuilder.server";
import { executeQuery, type QueryScope } from "~/services/queryService.server";
import { logger } from "~/services/logger.server";
import { rowsToCSV } from "~/utils/dataExport";

const BodySchema = z.object({
query: z.string(),
scope: z.enum(["organization", "project", "environment"]).default("environment"),
period: z.string().nullish(),
from: z.string().nullish(),
to: z.string().nullish(),
format: z.enum(["json", "csv"]).default("json"),
});

const { action, loader } = createActionApiRoute(
{
body: BodySchema,
corsStrategy: "all",
},
async ({ body, authentication }) => {
const { query, scope, period, from, to, format } = body;
const env = authentication.environment;

const queryResult = await executeQuery({
name: "api-query",
query,
scope: scope as QueryScope,
organizationId: env.organization.id,
projectId: env.project.id,
environmentId: env.id,
period,
from,
to,
history: {
source: "API",
},
});

if (!queryResult.success) {
const message =
queryResult.error instanceof QueryError
? queryResult.error.message
: "An unexpected error occurred while executing the query.";

logger.error("Query API error", {
error: queryResult.error,
query,
});

return json(
{ error: message },
{ status: queryResult.error instanceof QueryError ? 400 : 500 }
);
}

const { result, periodClipped, maxQueryPeriod } = queryResult;

if (format === "csv") {
const csv = rowsToCSV(result.rows, result.columns);

return json({
format: "csv",
results: csv,
});
}

return json({
format: "json",
results: result.rows,
});
}
);

export { action, loader };
35 changes: 35 additions & 0 deletions packages/core/src/v3/apiClient/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ import {
ListScheduleOptions,
QueueItem,
QueueTypeName,
QueryExecuteRequestBody,
QueryExecuteResponseBody,
QueryExecuteCSVResponseBody,
ReplayRunResponse,
RescheduleRunRequestBody,
ResetIdempotencyKeyResponse,
Expand Down Expand Up @@ -1406,6 +1409,38 @@ export class ApiClient {
);
}

async executeQuery(
query: string,
options?: {
scope?: "environment" | "project" | "organization";
period?: string;
from?: string;
to?: string;
format?: "json" | "csv";
},
requestOptions?: ZodFetchOptions
): Promise<QueryExecuteResponseBody> {
const body = {
query,
scope: options?.scope ?? "environment",
period: options?.period,
from: options?.from,
to: options?.to,
format: options?.format ?? "json",
};

return zodfetch(
QueryExecuteResponseBody,
`${this.baseUrl}/api/v1/query`,
{
method: "POST",
headers: this.#getHeaders(false),
body: JSON.stringify(body),
},
mergeRequestOptions(this.defaultRequestOptions, requestOptions)
);
}

#getHeaders(spanParentAsLink: boolean, additionalHeaders?: Record<string, string | undefined>) {
const headers: Record<string, string> = {
"Content-Type": "application/json",
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/v3/schemas/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,4 @@ export * from "./webhooks.js";
export * from "./checkpoints.js";
export * from "./warmStart.js";
export * from "./queues.js";
export * from "./query.js";
Loading
Loading