fix(webapp): project scoping for runs (#2553)
* fix: project scoping for runs
* Apply some 🐰 suggestions
This commit is contained in:
committed by
James Ritchie
parent
543d9cd887
commit
9ef745d049
@@ -28,7 +28,6 @@ export class RunPresenter {
|
||||
public async call({
|
||||
userId,
|
||||
projectSlug,
|
||||
organizationSlug,
|
||||
environmentSlug,
|
||||
runFriendlyId,
|
||||
showDeletedLogs,
|
||||
@@ -36,7 +35,6 @@ export class RunPresenter {
|
||||
}: {
|
||||
userId: string;
|
||||
projectSlug: string;
|
||||
organizationSlug: string;
|
||||
environmentSlug: string;
|
||||
runFriendlyId: string;
|
||||
showDeletedLogs: boolean;
|
||||
@@ -93,6 +91,13 @@ export class RunPresenter {
|
||||
friendlyId: runFriendlyId,
|
||||
project: {
|
||||
slug: projectSlug,
|
||||
organization: {
|
||||
members: {
|
||||
some: {
|
||||
userId,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
@@ -28,10 +28,12 @@ type GetSpanResult = NonNullable<Awaited<ReturnType<(typeof eventRepository)["ge
|
||||
|
||||
export class SpanPresenter extends BasePresenter {
|
||||
public async call({
|
||||
userId,
|
||||
projectSlug,
|
||||
spanId,
|
||||
runFriendlyId,
|
||||
}: {
|
||||
userId: string;
|
||||
projectSlug: string;
|
||||
spanId: string;
|
||||
runFriendlyId: string;
|
||||
@@ -39,6 +41,13 @@ export class SpanPresenter extends BasePresenter {
|
||||
const project = await this._replica.project.findFirst({
|
||||
where: {
|
||||
slug: projectSlug,
|
||||
organization: {
|
||||
members: {
|
||||
some: {
|
||||
userId,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
@@ -57,6 +66,7 @@ export class SpanPresenter extends BasePresenter {
|
||||
},
|
||||
where: {
|
||||
friendlyId: runFriendlyId,
|
||||
projectId: project.id,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -64,13 +74,11 @@ export class SpanPresenter extends BasePresenter {
|
||||
return;
|
||||
}
|
||||
|
||||
const { traceId } = parentRun;
|
||||
|
||||
const eventStore = getTaskEventStoreTableForRun(parentRun);
|
||||
|
||||
const run = await this.getRun({
|
||||
eventStore,
|
||||
traceId,
|
||||
environmentId: parentRun.runtimeEnvironmentId,
|
||||
spanId,
|
||||
createdAt: parentRun.createdAt,
|
||||
completedAt: parentRun.completedAt,
|
||||
@@ -82,10 +90,8 @@ export class SpanPresenter extends BasePresenter {
|
||||
};
|
||||
}
|
||||
|
||||
//get the run
|
||||
const span = await this.#getSpan({
|
||||
eventStore,
|
||||
traceId,
|
||||
spanId,
|
||||
environmentId: parentRun.runtimeEnvironmentId,
|
||||
projectId: parentRun.projectId,
|
||||
@@ -105,24 +111,24 @@ export class SpanPresenter extends BasePresenter {
|
||||
|
||||
async getRun({
|
||||
eventStore,
|
||||
traceId,
|
||||
environmentId,
|
||||
spanId,
|
||||
createdAt,
|
||||
completedAt,
|
||||
}: {
|
||||
eventStore: TaskEventStoreTable;
|
||||
traceId: string;
|
||||
environmentId: string;
|
||||
spanId: string;
|
||||
createdAt: Date;
|
||||
completedAt: Date | null;
|
||||
}) {
|
||||
const span = await eventRepository.getSpan(
|
||||
eventStore,
|
||||
const span = await eventRepository.getSpan({
|
||||
storeTable: eventStore,
|
||||
spanId,
|
||||
traceId,
|
||||
createdAt,
|
||||
completedAt ?? undefined
|
||||
);
|
||||
environmentId,
|
||||
startCreatedAt: createdAt,
|
||||
endCreatedAt: completedAt ?? undefined,
|
||||
});
|
||||
|
||||
if (!span) {
|
||||
return;
|
||||
@@ -412,14 +418,12 @@ export class SpanPresenter extends BasePresenter {
|
||||
|
||||
async #getSpan({
|
||||
eventStore,
|
||||
traceId,
|
||||
spanId,
|
||||
environmentId,
|
||||
projectId,
|
||||
createdAt,
|
||||
completedAt,
|
||||
}: {
|
||||
traceId: string;
|
||||
spanId: string;
|
||||
environmentId: string;
|
||||
projectId: string;
|
||||
@@ -427,14 +431,15 @@ export class SpanPresenter extends BasePresenter {
|
||||
createdAt: Date;
|
||||
completedAt: Date | null;
|
||||
}) {
|
||||
const span = await eventRepository.getSpan(
|
||||
eventStore,
|
||||
const span = await eventRepository.getSpan({
|
||||
storeTable: eventStore,
|
||||
spanId,
|
||||
traceId,
|
||||
createdAt,
|
||||
completedAt ?? undefined,
|
||||
{ includeDebugLogs: true }
|
||||
);
|
||||
environmentId,
|
||||
startCreatedAt: createdAt,
|
||||
endCreatedAt: completedAt ?? undefined,
|
||||
options: { includeDebugLogs: true },
|
||||
});
|
||||
|
||||
if (!span) {
|
||||
return;
|
||||
}
|
||||
|
||||
-1
@@ -142,7 +142,6 @@ export const loader = async ({ request, params }: LoaderFunctionArgs) => {
|
||||
const [error, result] = await tryCatch(
|
||||
presenter.call({
|
||||
userId,
|
||||
organizationSlug,
|
||||
showDeletedLogs: !!impersonationId,
|
||||
projectSlug: projectParam,
|
||||
runFriendlyId: runParam,
|
||||
|
||||
+10
-2
@@ -3,14 +3,14 @@ import { type LoaderFunctionArgs } from "@remix-run/server-runtime";
|
||||
import { z } from "zod";
|
||||
import { prisma } from "~/db.server";
|
||||
import { requireUserId } from "~/services/session.server";
|
||||
import { ProjectParamSchema, v3DeploymentPath, v3RunPath } from "~/utils/pathBuilder";
|
||||
import { ProjectParamSchema, v3RunPath } from "~/utils/pathBuilder";
|
||||
|
||||
const ParamSchema = ProjectParamSchema.extend({
|
||||
runParam: z.string(),
|
||||
});
|
||||
|
||||
export const loader = async ({ request, params }: LoaderFunctionArgs) => {
|
||||
await requireUserId(request);
|
||||
const userId = await requireUserId(request);
|
||||
const { organizationSlug, projectParam, runParam } = ParamSchema.parse(params);
|
||||
|
||||
const run = await prisma.taskRun.findFirst({
|
||||
@@ -18,6 +18,14 @@ export const loader = async ({ request, params }: LoaderFunctionArgs) => {
|
||||
friendlyId: runParam,
|
||||
project: {
|
||||
slug: projectParam,
|
||||
organization: {
|
||||
slug: organizationSlug,
|
||||
members: {
|
||||
some: {
|
||||
userId,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
select: {
|
||||
|
||||
+3
@@ -78,8 +78,10 @@ import {
|
||||
} from "~/utils/pathBuilder";
|
||||
import { createTimelineSpanEventsFromSpanEvents } from "~/utils/timelineSpanEvents";
|
||||
import { CompleteWaitpointForm } from "../resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.waitpoints.$waitpointFriendlyId.complete/route";
|
||||
import { requireUserId } from "~/services/session.server";
|
||||
|
||||
export const loader = async ({ request, params }: LoaderFunctionArgs) => {
|
||||
const userId = await requireUserId(request);
|
||||
const { projectParam, organizationSlug, envParam, runParam, spanParam } =
|
||||
v3SpanParamsSchema.parse(params);
|
||||
|
||||
@@ -90,6 +92,7 @@ export const loader = async ({ request, params }: LoaderFunctionArgs) => {
|
||||
projectSlug: projectParam,
|
||||
spanId: spanParam,
|
||||
runFriendlyId: runParam,
|
||||
userId,
|
||||
});
|
||||
|
||||
return typedjson(result);
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import { parse } from "@conform-to/zod";
|
||||
import { ActionFunction, json } from "@remix-run/node";
|
||||
import { type ActionFunction, json } from "@remix-run/node";
|
||||
import { z } from "zod";
|
||||
import { prisma } from "~/db.server";
|
||||
import { redirectWithErrorMessage, redirectWithSuccessMessage } from "~/models/message.server";
|
||||
import { logger } from "~/services/logger.server";
|
||||
import { requireUserId } from "~/services/session.server";
|
||||
import { CancelTaskRunService } from "~/v3/services/cancelTaskRun.server";
|
||||
|
||||
export const cancelSchema = z.object({
|
||||
@@ -15,6 +16,7 @@ const ParamSchema = z.object({
|
||||
});
|
||||
|
||||
export const action: ActionFunction = async ({ request, params }) => {
|
||||
const userId = await requireUserId(request);
|
||||
const { runParam } = ParamSchema.parse(params);
|
||||
|
||||
const formData = await request.formData();
|
||||
@@ -25,9 +27,18 @@ export const action: ActionFunction = async ({ request, params }) => {
|
||||
}
|
||||
|
||||
try {
|
||||
const taskRun = await prisma.taskRun.findUnique({
|
||||
const taskRun = await prisma.taskRun.findFirst({
|
||||
where: {
|
||||
friendlyId: runParam,
|
||||
project: {
|
||||
organization: {
|
||||
members: {
|
||||
some: {
|
||||
userId,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { LoaderFunctionArgs } from "@remix-run/node";
|
||||
import { type LoaderFunctionArgs } from "@remix-run/node";
|
||||
import { typedjson } from "remix-typedjson";
|
||||
import { z } from "zod";
|
||||
import { $replica } from "~/db.server";
|
||||
|
||||
@@ -970,22 +970,30 @@ export class EventRepository {
|
||||
|
||||
// A Span can be cancelled if it is partial and has a parent that is cancelled
|
||||
// And a span's duration, if it is partial and has a cancelled parent, is the time between the start of the span and the time of the cancellation event of the parent
|
||||
public async getSpan(
|
||||
storeTable: TaskEventStoreTable,
|
||||
spanId: string,
|
||||
traceId: string,
|
||||
startCreatedAt: Date,
|
||||
endCreatedAt?: Date,
|
||||
options?: { includeDebugLogs?: boolean }
|
||||
) {
|
||||
return await startActiveSpan("getSpan", async (s) => {
|
||||
const spanEvent = await this.#getSpanEvent(
|
||||
public async getSpan({
|
||||
storeTable,
|
||||
spanId,
|
||||
environmentId,
|
||||
startCreatedAt,
|
||||
endCreatedAt,
|
||||
options,
|
||||
}: {
|
||||
storeTable: TaskEventStoreTable;
|
||||
spanId: string;
|
||||
environmentId: string;
|
||||
startCreatedAt: Date;
|
||||
endCreatedAt?: Date;
|
||||
options?: { includeDebugLogs?: boolean };
|
||||
}) {
|
||||
return await startActiveSpan("getSpan", async () => {
|
||||
const spanEvent = await this.#getSpanEvent({
|
||||
storeTable,
|
||||
spanId,
|
||||
environmentId,
|
||||
startCreatedAt,
|
||||
endCreatedAt,
|
||||
options
|
||||
);
|
||||
options,
|
||||
});
|
||||
|
||||
if (!spanEvent) {
|
||||
return;
|
||||
@@ -996,6 +1004,7 @@ export class EventRepository {
|
||||
const span = await this.#createSpanFromEvent(
|
||||
storeTable,
|
||||
preparedEvent,
|
||||
environmentId,
|
||||
startCreatedAt,
|
||||
endCreatedAt
|
||||
);
|
||||
@@ -1081,6 +1090,7 @@ export class EventRepository {
|
||||
async #createSpanFromEvent(
|
||||
storeTable: TaskEventStoreTable,
|
||||
event: PreparedEvent,
|
||||
environmentId: string,
|
||||
startCreatedAt: Date,
|
||||
endCreatedAt?: Date
|
||||
) {
|
||||
@@ -1091,6 +1101,7 @@ export class EventRepository {
|
||||
await this.#walkSpanAncestors(
|
||||
storeTable,
|
||||
event,
|
||||
environmentId,
|
||||
startCreatedAt,
|
||||
endCreatedAt,
|
||||
(ancestorEvent, level) => {
|
||||
@@ -1185,6 +1196,7 @@ export class EventRepository {
|
||||
async #walkSpanAncestors(
|
||||
storeTable: TaskEventStoreTable,
|
||||
event: PreparedEvent,
|
||||
environmentId: string,
|
||||
startCreatedAt: Date,
|
||||
endCreatedAt: Date | undefined,
|
||||
callback: (event: PreparedEvent, level: number) => { stop: boolean }
|
||||
@@ -1195,12 +1207,13 @@ export class EventRepository {
|
||||
}
|
||||
|
||||
await startActiveSpan("walkSpanAncestors", async (s) => {
|
||||
let parentEvent = await this.#getSpanEvent(
|
||||
let parentEvent = await this.#getSpanEvent({
|
||||
storeTable,
|
||||
parentId,
|
||||
spanId: parentId,
|
||||
environmentId,
|
||||
startCreatedAt,
|
||||
endCreatedAt
|
||||
);
|
||||
endCreatedAt,
|
||||
});
|
||||
let level = 1;
|
||||
|
||||
while (parentEvent) {
|
||||
@@ -1216,29 +1229,38 @@ export class EventRepository {
|
||||
return;
|
||||
}
|
||||
|
||||
parentEvent = await this.#getSpanEvent(
|
||||
parentEvent = await this.#getSpanEvent({
|
||||
storeTable,
|
||||
preparedParentEvent.parentId,
|
||||
spanId: preparedParentEvent.parentId,
|
||||
environmentId,
|
||||
startCreatedAt,
|
||||
endCreatedAt
|
||||
);
|
||||
endCreatedAt,
|
||||
});
|
||||
|
||||
level++;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async #getSpanEvent(
|
||||
storeTable: TaskEventStoreTable,
|
||||
spanId: string,
|
||||
startCreatedAt: Date,
|
||||
endCreatedAt?: Date,
|
||||
options?: { includeDebugLogs?: boolean }
|
||||
) {
|
||||
async #getSpanEvent({
|
||||
storeTable,
|
||||
spanId,
|
||||
environmentId,
|
||||
startCreatedAt,
|
||||
endCreatedAt,
|
||||
options,
|
||||
}: {
|
||||
storeTable: TaskEventStoreTable;
|
||||
spanId: string;
|
||||
environmentId: string;
|
||||
startCreatedAt: Date;
|
||||
endCreatedAt?: Date;
|
||||
options?: { includeDebugLogs?: boolean };
|
||||
}) {
|
||||
return await startActiveSpan("getSpanEvent", async (s) => {
|
||||
const events = await this.taskEventStore.findMany(
|
||||
storeTable,
|
||||
{ spanId },
|
||||
{ spanId, environmentId },
|
||||
startCreatedAt,
|
||||
endCreatedAt,
|
||||
undefined,
|
||||
|
||||
Reference in New Issue
Block a user