Merge remote-tracking branch 'origin/main' into bulk-cancel-replay
This commit is contained in:
@@ -4,9 +4,17 @@ import { SimpleTooltip } from "~/components/primitives/Tooltip";
|
||||
import { useCopy } from "~/hooks/useCopy";
|
||||
import { cn } from "~/utils/cn";
|
||||
|
||||
export function CopyableText({ value, className }: { value: string; className?: string }) {
|
||||
export function CopyableText({
|
||||
value,
|
||||
copyValue,
|
||||
className,
|
||||
}: {
|
||||
value: string;
|
||||
copyValue?: string;
|
||||
className?: string;
|
||||
}) {
|
||||
const [isHovered, setIsHovered] = useState(false);
|
||||
const { copy, copied } = useCopy(value);
|
||||
const { copy, copied } = useCopy(copyValue ?? value);
|
||||
|
||||
return (
|
||||
<span
|
||||
|
||||
@@ -52,6 +52,8 @@ import {
|
||||
TaskRunStatusCombo,
|
||||
} from "./TaskRunStatus";
|
||||
import { useEnvironment } from "~/hooks/useEnvironment";
|
||||
import { CopyableText } from "~/components/primitives/CopyableText";
|
||||
import { ClipboardField } from "~/components/primitives/ClipboardField";
|
||||
|
||||
type RunsTableProps = {
|
||||
total: number;
|
||||
@@ -134,7 +136,7 @@ export function TaskRunsTable({
|
||||
)}
|
||||
</TableHeaderCell>
|
||||
)}
|
||||
<TableHeaderCell alignment="right">Run #</TableHeaderCell>
|
||||
<TableHeaderCell>ID</TableHeaderCell>
|
||||
<TableHeaderCell>Task</TableHeaderCell>
|
||||
<TableHeaderCell>Version</TableHeaderCell>
|
||||
<TableHeaderCell
|
||||
@@ -306,8 +308,21 @@ export function TaskRunsTable({
|
||||
/>
|
||||
</TableCell>
|
||||
)}
|
||||
<TableCell to={path} alignment="right" isTabbableCell>
|
||||
{formatNumber(run.number)}
|
||||
<TableCell to={path} isTabbableCell>
|
||||
<SimpleTooltip
|
||||
content={run.friendlyId}
|
||||
button={
|
||||
<span className="flex h-6 items-center gap-1">
|
||||
<CopyableText
|
||||
value={run.friendlyId.slice(-8)}
|
||||
copyValue={run.friendlyId}
|
||||
className="font-mono"
|
||||
/>
|
||||
</span>
|
||||
}
|
||||
asChild
|
||||
disableHoverableContent
|
||||
/>
|
||||
</TableCell>
|
||||
<TableCell to={path}>
|
||||
<span className="flex items-center gap-x-1">
|
||||
|
||||
@@ -45,6 +45,7 @@ export class NextRunListPresenter {
|
||||
) {}
|
||||
|
||||
public async call(
|
||||
organizationId: string,
|
||||
environmentId: string,
|
||||
{
|
||||
userId,
|
||||
@@ -190,6 +191,7 @@ export class NextRunListPresenter {
|
||||
});
|
||||
|
||||
const { runs, pagination } = await runsRepository.listRuns({
|
||||
organizationId,
|
||||
environmentId,
|
||||
projectId,
|
||||
tasks,
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
import {
|
||||
PrismaClientOrTransaction,
|
||||
RuntimeEnvironmentType,
|
||||
type PrismaClientOrTransaction,
|
||||
type RuntimeEnvironmentType,
|
||||
type TaskTriggerSource,
|
||||
} from "@trigger.dev/database";
|
||||
import { $replica } from "~/db.server";
|
||||
import { clickhouseClient } from "~/services/clickhouseInstance.server";
|
||||
import {
|
||||
AverageDurations,
|
||||
type AverageDurations,
|
||||
ClickHouseEnvironmentMetricsRepository,
|
||||
CurrentRunningStats,
|
||||
DailyTaskActivity,
|
||||
EnvironmentMetricsRepository,
|
||||
type CurrentRunningStats,
|
||||
type DailyTaskActivity,
|
||||
type EnvironmentMetricsRepository,
|
||||
PostgrestEnvironmentMetricsRepository,
|
||||
} from "~/services/environmentMetricsRepository.server";
|
||||
import { singleton } from "~/utils/singleton";
|
||||
@@ -32,9 +32,13 @@ export class TaskListPresenter {
|
||||
) {}
|
||||
|
||||
public async call({
|
||||
organizationId,
|
||||
projectId,
|
||||
environmentId,
|
||||
environmentType,
|
||||
}: {
|
||||
organizationId: string;
|
||||
projectId: string;
|
||||
environmentId: string;
|
||||
environmentType: RuntimeEnvironmentType;
|
||||
}) {
|
||||
@@ -76,18 +80,24 @@ export class TaskListPresenter {
|
||||
// IMPORTANT: Don't await these, we want to return the promises
|
||||
// so we can defer the loading of the data
|
||||
const activity = this.environmentMetricsRepository.getDailyTaskActivity({
|
||||
organizationId,
|
||||
projectId,
|
||||
environmentId,
|
||||
days: 6, // This actually means 7 days, because we want to show the current day too
|
||||
tasks: slugs,
|
||||
});
|
||||
|
||||
const runningStats = this.environmentMetricsRepository.getCurrentRunningStats({
|
||||
organizationId,
|
||||
projectId,
|
||||
environmentId,
|
||||
days: 6,
|
||||
tasks: slugs,
|
||||
});
|
||||
|
||||
const durations = this.environmentMetricsRepository.getAverageDurations({
|
||||
organizationId,
|
||||
projectId,
|
||||
environmentId,
|
||||
days: 6,
|
||||
tasks: slugs,
|
||||
|
||||
+2
@@ -125,6 +125,8 @@ export const loader = async ({ request, params }: LoaderFunctionArgs) => {
|
||||
|
||||
try {
|
||||
const { tasks, activity, runningStats, durations } = await taskListPresenter.call({
|
||||
organizationId: project.organizationId,
|
||||
projectId: project.id,
|
||||
environmentId: environment.id,
|
||||
environmentType: environment.type,
|
||||
});
|
||||
|
||||
+1
-1
@@ -128,7 +128,7 @@ export const loader = async ({ request, params }: LoaderFunctionArgs) => {
|
||||
}
|
||||
|
||||
const presenter = new NextRunListPresenter($replica, clickhouseClient);
|
||||
const list = presenter.call(environment.id, {
|
||||
const list = presenter.call(project.organizationId, environment.id, {
|
||||
userId,
|
||||
projectId: project.id,
|
||||
tasks,
|
||||
|
||||
@@ -10,18 +10,24 @@ export type AverageDurations = Record<string, number>;
|
||||
|
||||
export interface EnvironmentMetricsRepository {
|
||||
getDailyTaskActivity(options: {
|
||||
organizationId: string;
|
||||
projectId: string;
|
||||
environmentId: string;
|
||||
days: number;
|
||||
tasks: string[];
|
||||
}): Promise<DailyTaskActivity>;
|
||||
|
||||
getCurrentRunningStats(options: {
|
||||
organizationId: string;
|
||||
projectId: string;
|
||||
environmentId: string;
|
||||
days: number;
|
||||
tasks: string[];
|
||||
}): Promise<CurrentRunningStats>;
|
||||
|
||||
getAverageDurations(options: {
|
||||
organizationId: string;
|
||||
projectId: string;
|
||||
environmentId: string;
|
||||
days: number;
|
||||
tasks: string[];
|
||||
@@ -177,10 +183,14 @@ export class ClickHouseEnvironmentMetricsRepository implements EnvironmentMetric
|
||||
constructor(private readonly options: ClickHouseEnvironmentMetricsRepositoryOptions) {}
|
||||
|
||||
public async getDailyTaskActivity({
|
||||
organizationId,
|
||||
projectId,
|
||||
environmentId,
|
||||
days,
|
||||
tasks,
|
||||
}: {
|
||||
organizationId: string;
|
||||
projectId: string;
|
||||
environmentId: string;
|
||||
days: number;
|
||||
tasks: string[];
|
||||
@@ -190,6 +200,8 @@ export class ClickHouseEnvironmentMetricsRepository implements EnvironmentMetric
|
||||
}
|
||||
|
||||
const [queryError, activity] = await this.options.clickhouse.taskRuns.getTaskActivity({
|
||||
organizationId,
|
||||
projectId,
|
||||
environmentId,
|
||||
days,
|
||||
});
|
||||
@@ -210,10 +222,14 @@ export class ClickHouseEnvironmentMetricsRepository implements EnvironmentMetric
|
||||
}
|
||||
|
||||
public async getCurrentRunningStats({
|
||||
organizationId,
|
||||
projectId,
|
||||
environmentId,
|
||||
days,
|
||||
tasks,
|
||||
}: {
|
||||
organizationId: string;
|
||||
projectId: string;
|
||||
environmentId: string;
|
||||
days: number;
|
||||
tasks: string[];
|
||||
@@ -223,6 +239,8 @@ export class ClickHouseEnvironmentMetricsRepository implements EnvironmentMetric
|
||||
}
|
||||
|
||||
const [queryError, stats] = await this.options.clickhouse.taskRuns.getCurrentRunningStats({
|
||||
organizationId,
|
||||
projectId,
|
||||
environmentId,
|
||||
days,
|
||||
});
|
||||
@@ -242,10 +260,14 @@ export class ClickHouseEnvironmentMetricsRepository implements EnvironmentMetric
|
||||
}
|
||||
|
||||
public async getAverageDurations({
|
||||
organizationId,
|
||||
projectId,
|
||||
environmentId,
|
||||
days,
|
||||
tasks,
|
||||
}: {
|
||||
organizationId: string;
|
||||
projectId: string;
|
||||
environmentId: string;
|
||||
days: number;
|
||||
tasks: string[];
|
||||
@@ -255,6 +277,8 @@ export class ClickHouseEnvironmentMetricsRepository implements EnvironmentMetric
|
||||
}
|
||||
|
||||
const [queryError, durations] = await this.options.clickhouse.taskRuns.getAverageDurations({
|
||||
organizationId,
|
||||
projectId,
|
||||
environmentId,
|
||||
days,
|
||||
});
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { ClickHouse } from "@internal/clickhouse";
|
||||
import { Tracer } from "@internal/tracing";
|
||||
import { Logger, LogLevel } from "@trigger.dev/core/logger";
|
||||
import { TaskRunStatus } from "@trigger.dev/database";
|
||||
import { PrismaClient } from "~/db.server";
|
||||
import { type ClickHouse } from "@internal/clickhouse";
|
||||
import { type Tracer } from "@internal/tracing";
|
||||
import { type Logger, type LogLevel } from "@trigger.dev/core/logger";
|
||||
import { type TaskRunStatus } from "@trigger.dev/database";
|
||||
import { type PrismaClient } from "~/db.server";
|
||||
|
||||
export type RunsRepositoryOptions = {
|
||||
clickhouse: ClickHouse;
|
||||
@@ -13,6 +13,7 @@ export type RunsRepositoryOptions = {
|
||||
};
|
||||
|
||||
export type ListRunsOptions = {
|
||||
organizationId: string;
|
||||
projectId: string;
|
||||
environmentId: string;
|
||||
//filters
|
||||
@@ -43,11 +44,14 @@ export class RunsRepository {
|
||||
async listRuns(options: ListRunsOptions) {
|
||||
const queryBuilder = this.options.clickhouse.taskRuns.queryBuilder();
|
||||
queryBuilder
|
||||
.where("environment_id = {environmentId: String}", {
|
||||
environmentId: options.environmentId,
|
||||
.where("organization_id = {organizationId: String}", {
|
||||
organizationId: options.organizationId,
|
||||
})
|
||||
.where("project_id = {projectId: String}", {
|
||||
projectId: options.projectId,
|
||||
})
|
||||
.where("environment_id = {environmentId: String}", {
|
||||
environmentId: options.environmentId,
|
||||
});
|
||||
|
||||
if (options.tasks && options.tasks.length > 0) {
|
||||
@@ -115,17 +119,17 @@ export class RunsRepository {
|
||||
if (options.page.direction === "forward") {
|
||||
queryBuilder
|
||||
.where("run_id < {runId: String}", { runId: options.page.cursor })
|
||||
.orderBy("run_id DESC")
|
||||
.orderBy("created_at DESC, run_id DESC")
|
||||
.limit(options.page.size + 1);
|
||||
} else {
|
||||
queryBuilder
|
||||
.where("run_id > {runId: String}", { runId: options.page.cursor })
|
||||
.orderBy("run_id DESC")
|
||||
.orderBy("created_at ASC, run_id ASC")
|
||||
.limit(options.page.size + 1);
|
||||
}
|
||||
} else {
|
||||
// Initial page - no cursor provided
|
||||
queryBuilder.orderBy("run_id DESC").limit(options.page.size + 1);
|
||||
queryBuilder.orderBy("created_at DESC, run_id DESC").limit(options.page.size + 1);
|
||||
}
|
||||
|
||||
const [queryError, result] = await queryBuilder.execute();
|
||||
@@ -143,38 +147,33 @@ export class RunsRepository {
|
||||
let previousCursor: string | null = null;
|
||||
|
||||
//get cursors for next and previous pages
|
||||
if (options.page.cursor) {
|
||||
switch (options.page.direction) {
|
||||
case "forward":
|
||||
previousCursor = runIds.at(0) ?? null;
|
||||
if (hasMore) {
|
||||
// The next cursor should be the last run ID from this page
|
||||
nextCursor = runIds[options.page.size - 1];
|
||||
}
|
||||
break;
|
||||
case "backward":
|
||||
// No need to reverse since we're using DESC ordering consistently
|
||||
if (hasMore) {
|
||||
previousCursor = runIds[options.page.size - 1];
|
||||
}
|
||||
nextCursor = runIds.at(0) ?? null;
|
||||
break;
|
||||
default:
|
||||
// This shouldn't happen if cursor is provided, but handle it
|
||||
if (hasMore) {
|
||||
nextCursor = runIds[options.page.size - 1];
|
||||
}
|
||||
break;
|
||||
const direction = options.page.direction ?? "forward";
|
||||
switch (direction) {
|
||||
case "forward": {
|
||||
previousCursor = options.page.cursor ? runIds.at(0) ?? null : null;
|
||||
if (hasMore) {
|
||||
// The next cursor should be the last run ID from this page
|
||||
nextCursor = runIds[options.page.size - 1];
|
||||
}
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
// Initial page - no cursor
|
||||
if (hasMore) {
|
||||
// The next cursor should be the last run ID from this page
|
||||
nextCursor = runIds[options.page.size - 1];
|
||||
case "backward": {
|
||||
const reversedRunIds = [...runIds].reverse();
|
||||
if (hasMore) {
|
||||
previousCursor = reversedRunIds.at(1) ?? null;
|
||||
nextCursor = reversedRunIds.at(options.page.size) ?? null;
|
||||
} else {
|
||||
nextCursor = reversedRunIds.at(options.page.size - 1) ?? null;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const runIdsToReturn = hasMore ? runIds.slice(0, -1) : runIds;
|
||||
const runIdsToReturn =
|
||||
options.page.direction === "backward" && hasMore
|
||||
? runIds.slice(1, options.page.size + 1)
|
||||
: runIds.slice(0, options.page.size);
|
||||
|
||||
const runs = await this.options.prisma.taskRun.findMany({
|
||||
where: {
|
||||
|
||||
@@ -72,6 +72,7 @@ describe("RunsRepository", () => {
|
||||
page: { size: 10 },
|
||||
projectId: project.id,
|
||||
environmentId: runtimeEnvironment.id,
|
||||
organizationId: organization.id,
|
||||
});
|
||||
|
||||
expect(runs).toHaveLength(1);
|
||||
@@ -180,6 +181,7 @@ describe("RunsRepository", () => {
|
||||
page: { size: 10 },
|
||||
projectId: project.id,
|
||||
environmentId: runtimeEnvironment.id,
|
||||
organizationId: organization.id,
|
||||
tasks: ["task-1", "task-2"],
|
||||
});
|
||||
|
||||
@@ -290,6 +292,7 @@ describe("RunsRepository", () => {
|
||||
page: { size: 10 },
|
||||
projectId: project.id,
|
||||
environmentId: runtimeEnvironment.id,
|
||||
organizationId: organization.id,
|
||||
versions: ["1.0.0", "3.0.0"],
|
||||
});
|
||||
|
||||
@@ -400,6 +403,7 @@ describe("RunsRepository", () => {
|
||||
page: { size: 10 },
|
||||
projectId: project.id,
|
||||
environmentId: runtimeEnvironment.id,
|
||||
organizationId: organization.id,
|
||||
statuses: ["PENDING", "COMPLETED_SUCCESSFULLY"],
|
||||
});
|
||||
|
||||
@@ -510,6 +514,7 @@ describe("RunsRepository", () => {
|
||||
page: { size: 10 },
|
||||
projectId: project.id,
|
||||
environmentId: runtimeEnvironment.id,
|
||||
organizationId: organization.id,
|
||||
tags: ["urgent"],
|
||||
});
|
||||
|
||||
@@ -619,6 +624,7 @@ describe("RunsRepository", () => {
|
||||
page: { size: 10 },
|
||||
projectId: project.id,
|
||||
environmentId: runtimeEnvironment.id,
|
||||
organizationId: organization.id,
|
||||
scheduleId: "schedule_1",
|
||||
});
|
||||
|
||||
@@ -712,6 +718,7 @@ describe("RunsRepository", () => {
|
||||
page: { size: 10 },
|
||||
projectId: project.id,
|
||||
environmentId: runtimeEnvironment.id,
|
||||
organizationId: organization.id,
|
||||
isTest: true,
|
||||
});
|
||||
|
||||
@@ -723,6 +730,7 @@ describe("RunsRepository", () => {
|
||||
page: { size: 10 },
|
||||
projectId: project.id,
|
||||
environmentId: runtimeEnvironment.id,
|
||||
organizationId: organization.id,
|
||||
isTest: false,
|
||||
});
|
||||
|
||||
@@ -816,6 +824,7 @@ describe("RunsRepository", () => {
|
||||
page: { size: 10 },
|
||||
projectId: project.id,
|
||||
environmentId: runtimeEnvironment.id,
|
||||
organizationId: organization.id,
|
||||
rootOnly: true,
|
||||
});
|
||||
|
||||
@@ -945,6 +954,7 @@ describe("RunsRepository", () => {
|
||||
page: { size: 10 },
|
||||
projectId: project.id,
|
||||
environmentId: runtimeEnvironment.id,
|
||||
organizationId: organization.id,
|
||||
batchId: batchRun1.id,
|
||||
});
|
||||
|
||||
@@ -1052,6 +1062,7 @@ describe("RunsRepository", () => {
|
||||
page: { size: 10 },
|
||||
projectId: project.id,
|
||||
environmentId: runtimeEnvironment.id,
|
||||
organizationId: organization.id,
|
||||
runFriendlyIds: ["run_abc", "run_xyz"],
|
||||
});
|
||||
|
||||
@@ -1159,6 +1170,7 @@ describe("RunsRepository", () => {
|
||||
page: { size: 10 },
|
||||
projectId: project.id,
|
||||
environmentId: runtimeEnvironment.id,
|
||||
organizationId: organization.id,
|
||||
runIds: [run1.id, run3.id],
|
||||
});
|
||||
|
||||
@@ -1273,6 +1285,7 @@ describe("RunsRepository", () => {
|
||||
page: { size: 10 },
|
||||
projectId: project.id,
|
||||
environmentId: runtimeEnvironment.id,
|
||||
organizationId: organization.id,
|
||||
from: yesterday.getTime(),
|
||||
to: now.getTime(),
|
||||
});
|
||||
@@ -1393,6 +1406,7 @@ describe("RunsRepository", () => {
|
||||
page: { size: 10 },
|
||||
projectId: project.id,
|
||||
environmentId: runtimeEnvironment.id,
|
||||
organizationId: organization.id,
|
||||
tasks: ["task-1"],
|
||||
versions: ["1.0.0"],
|
||||
statuses: ["COMPLETED_SUCCESSFULLY"],
|
||||
@@ -1476,6 +1490,7 @@ describe("RunsRepository", () => {
|
||||
page: { size: 2 },
|
||||
projectId: project.id,
|
||||
environmentId: runtimeEnvironment.id,
|
||||
organizationId: organization.id,
|
||||
});
|
||||
|
||||
expect(firstPage.runs).toHaveLength(2);
|
||||
@@ -1491,6 +1506,7 @@ describe("RunsRepository", () => {
|
||||
},
|
||||
projectId: project.id,
|
||||
environmentId: runtimeEnvironment.id,
|
||||
organizationId: organization.id,
|
||||
});
|
||||
|
||||
expect(secondPage.runs).toHaveLength(2);
|
||||
|
||||
@@ -59,6 +59,16 @@ Here are some common problems and their solutions:
|
||||
|
||||
There should be a link below the error message to the full build logs on your machine. Take a look at these to see what went wrong. Join [our Discord](https://trigger.dev/discord) and you share it privately with us if you can't figure out what's going wrong. Do NOT share these publicly as the verbose logs might reveal private information about your project.
|
||||
|
||||
### `Error: failed to solve: failed to resolve source metadata for docker.io/docker/dockerfile:1`
|
||||
|
||||
If you see this error after uninstalling Docker Desktop:
|
||||
|
||||
```
|
||||
Error: failed to solve: failed to resolve source metadata for docker.io/docker/dockerfile:1: error getting credentials - err: exec: "docker-credential-desktop": executable file not found in $PATH
|
||||
```
|
||||
|
||||
This happens because Docker Desktop left behind a config file that's still trying to use its credential store. To fix this, remove or update the `~/.docker/config.json` file. You don't need Docker Desktop installed to use Trigger.dev.
|
||||
|
||||
### `Deployment encountered an error`
|
||||
|
||||
Usually there will be some useful guidance below this message. If you can't figure out what's going wrong then join [our Discord](https://trigger.dev/discord) and create a Help forum post with a link to your deployment.
|
||||
|
||||
@@ -113,6 +113,8 @@ export const TaskActivityQueryResult = z.object({
|
||||
export type TaskActivityQueryResult = z.infer<typeof TaskActivityQueryResult>;
|
||||
|
||||
export const TaskActivityQueryParams = z.object({
|
||||
organizationId: z.string(),
|
||||
projectId: z.string(),
|
||||
environmentId: z.string(),
|
||||
days: z.number().int(),
|
||||
});
|
||||
@@ -128,7 +130,9 @@ export function getTaskActivityQueryBuilder(ch: ClickhouseReader, settings?: Cli
|
||||
count() as count
|
||||
FROM trigger_dev.task_runs_v2 FINAL
|
||||
WHERE
|
||||
environment_id = {environmentId: String}
|
||||
organization_id = {organizationId: String}
|
||||
AND project_id = {projectId: String}
|
||||
AND environment_id = {environmentId: String}
|
||||
AND created_at >= today() - {days: Int64}
|
||||
AND _is_deleted = 0
|
||||
GROUP BY
|
||||
@@ -155,6 +159,8 @@ export const CurrentRunningStatsQueryResult = z.object({
|
||||
export type CurrentRunningStatsQueryResult = z.infer<typeof CurrentRunningStatsQueryResult>;
|
||||
|
||||
export const CurrentRunningStatsQueryParams = z.object({
|
||||
organizationId: z.string(),
|
||||
projectId: z.string(),
|
||||
environmentId: z.string(),
|
||||
days: z.number().int(),
|
||||
});
|
||||
@@ -169,7 +175,9 @@ export function getCurrentRunningStats(ch: ClickhouseReader, settings?: ClickHou
|
||||
count() as count
|
||||
FROM trigger_dev.task_runs_v2 FINAL
|
||||
WHERE
|
||||
environment_id = {environmentId: String}
|
||||
organization_id = {organizationId: String}
|
||||
AND project_id = {projectId: String}
|
||||
AND environment_id = {environmentId: String}
|
||||
AND status IN ('PENDING', 'WAITING_FOR_DEPLOY', 'WAITING_TO_RESUME', 'QUEUED', 'EXECUTING')
|
||||
AND _is_deleted = 0
|
||||
AND created_at >= now() - INTERVAL {days: Int64} DAY
|
||||
@@ -193,6 +201,8 @@ export const AverageDurationsQueryResult = z.object({
|
||||
export type AverageDurationsQueryResult = z.infer<typeof AverageDurationsQueryResult>;
|
||||
|
||||
export const AverageDurationsQueryParams = z.object({
|
||||
organizationId: z.string(),
|
||||
projectId: z.string(),
|
||||
environmentId: z.string(),
|
||||
days: z.number().int(),
|
||||
});
|
||||
@@ -206,7 +216,9 @@ export function getAverageDurations(ch: ClickhouseReader, settings?: ClickHouseS
|
||||
avg(toUnixTimestamp(completed_at) - toUnixTimestamp(started_at)) as duration
|
||||
FROM trigger_dev.task_runs_v2 FINAL
|
||||
WHERE
|
||||
environment_id = {environmentId: String}
|
||||
organization_id = {organizationId: String}
|
||||
AND project_id = {projectId: String}
|
||||
AND environment_id = {environmentId: String}
|
||||
AND created_at >= today() - {days: Int64}
|
||||
AND status IN ('COMPLETED_SUCCESSFULLY', 'COMPLETED_WITH_ERRORS')
|
||||
AND started_at IS NOT NULL
|
||||
|
||||
Reference in New Issue
Block a user