Skip to content

Commit c8a1f76

Browse files
committed
fix(webapp): preserve related run summaries for scoped reads
1 parent 608eb0f commit c8a1f76

3 files changed

Lines changed: 14 additions & 56 deletions

File tree

apps/webapp/app/presenters/v3/ApiRetrieveRunPresenter.server.ts

Lines changed: 12 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ import { parsePacketAsJson } from "@trigger.dev/core/v3/utils/ioSerialization";
1414
import { BatchId } from "@trigger.dev/core/v3/isomorphic";
1515
import { getUserProvidedIdempotencyKey } from "@trigger.dev/core/v3/serverOnly";
1616
import type { Prisma, TaskRunAttemptStatus, TaskRunStatus } from "@trigger.dev/database";
17-
import type { RbacAbility } from "@trigger.dev/rbac";
1817
import assertNever from "assert-never";
1918
import type { API_VERSIONS, RunStatusUnspecifiedApiVersion } from "~/api/versions";
2019
import { CURRENT_API_VERSION } from "~/api/versions";
@@ -84,24 +83,6 @@ type CommonRelatedRunWithVersion = CommonRelatedRun & {
8483
// ReturnType<typeof findRun>) so findRun can return a synthesised buffered
8584
// run without the type becoming self-referential. Exported so the
8685
// buffer-synthesis helper below can match this shape under unit test.
87-
function canReadRelatedRun(
88-
ability: RbacAbility | undefined,
89-
run: CommonRelatedRunWithVersion
90-
): boolean {
91-
if (!ability) return true;
92-
93-
const resources = [
94-
{ type: "runs", id: run.friendlyId },
95-
{ type: "tasks", id: run.taskIdentifier },
96-
...run.runTags.map((tag) => ({ type: "tags", id: tag })),
97-
];
98-
if (run.batch?.friendlyId) {
99-
resources.push({ type: "batch", id: run.batch.friendlyId });
100-
}
101-
102-
return ability.can("read", resources);
103-
}
104-
10586
export type FoundRun = CommonRelatedRunWithVersion & {
10687
traceId: string;
10788
payload: string;
@@ -231,7 +212,7 @@ export class ApiRetrieveRunPresenter {
231212
return synthesiseFoundRunFromBuffer(buffered);
232213
}
233214

234-
public async call(taskRun: FoundRun, env: AuthenticatedEnvironment, ability?: RbacAbility) {
215+
public async call(taskRun: FoundRun, env: AuthenticatedEnvironment) {
235216
return startSpanWithEnv(tracer, "ApiRetrieveRunPresenter.call", env, async () => {
236217
let $payload: any;
237218
let $payloadPresignedUrl: string | undefined;
@@ -308,19 +289,19 @@ export class ApiRetrieveRunPresenter {
308289
attemptCount:
309290
taskRun.engine === "V1" ? taskRun.attempts.length : (taskRun.attemptNumber ?? 0),
310291
attempts: [],
292+
// Related runs are an embedded projection of the authorized run, not independent reads.
293+
// Preserve the established response shape for run-scoped credentials.
311294
relatedRuns: {
312-
root:
313-
taskRun.rootTaskRun && canReadRelatedRun(ability, taskRun.rootTaskRun)
314-
? await createCommonRunStructure(taskRun.rootTaskRun, this.apiVersion)
315-
: undefined,
316-
parent:
317-
taskRun.parentTaskRun && canReadRelatedRun(ability, taskRun.parentTaskRun)
318-
? await createCommonRunStructure(taskRun.parentTaskRun, this.apiVersion)
319-
: undefined,
295+
root: taskRun.rootTaskRun
296+
? await createCommonRunStructure(taskRun.rootTaskRun, this.apiVersion)
297+
: undefined,
298+
parent: taskRun.parentTaskRun
299+
? await createCommonRunStructure(taskRun.parentTaskRun, this.apiVersion)
300+
: undefined,
320301
children: await Promise.all(
321-
taskRun.childRuns
322-
.filter((run) => canReadRelatedRun(ability, run))
323-
.map(async (run) => await createCommonRunStructure(run, this.apiVersion))
302+
taskRun.childRuns.map(
303+
async (run) => await createCommonRunStructure(run, this.apiVersion)
304+
)
324305
),
325306
},
326307
};

apps/webapp/app/routes/api.v3.runs.$runId.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,9 @@ export const loader = createLoaderApiRoute(
3131
},
3232
},
3333
},
34-
async ({ authentication, ability, resource, apiVersion }) => {
34+
async ({ authentication, resource, apiVersion }) => {
3535
const presenter = new ApiRetrieveRunPresenter(apiVersion);
36-
const result = await presenter.call(resource, authentication.environment, ability);
36+
const result = await presenter.call(resource, authentication.environment);
3737

3838
if (!result) {
3939
return json(

apps/webapp/test/apiRetrieveRunPresenter.readroute.test.ts

Lines changed: 0 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import { postgresTest, heteroPostgresTest } from "@internal/testcontainers";
22
import { PostgresRunStore } from "@internal/run-store";
33
import type { Prisma, PrismaClient } from "@trigger.dev/database";
44
import { generateRunOpsId } from "@trigger.dev/core/v3/isomorphic";
5-
import type { RbacAbility } from "@trigger.dev/rbac";
65
import { beforeEach, describe, expect, vi } from "vitest";
76

87
// `resolveSchedule` reads the module-level `prisma` (control-plane handle).
@@ -384,28 +383,6 @@ describe("ApiRetrieveRunPresenter.findRun store-routed read (single-DB invariant
384383
expect(out.relatedRuns.root?.id).toBe(tree.rootFriendlyId);
385384
expect(out.relatedRuns.children.map((c) => c.id)).toEqual([tree.childFriendlyId]);
386385
expect(out.attemptCount).toBe(found!.attemptNumber ?? 0);
387-
388-
const selectedTaskAbility: RbacAbility = {
389-
can: (action, resource) => {
390-
const resources = Array.isArray(resource) ? resource : [resource];
391-
return (
392-
action === "read" &&
393-
resources.some(
394-
(candidate) => candidate.type === "tasks" && candidate.id === found!.taskIdentifier
395-
)
396-
);
397-
},
398-
canSuper: () => false,
399-
};
400-
const scopedOut = await new ApiRetrieveRunPresenter(CURRENT_API_VERSION).call(
401-
found!,
402-
env,
403-
selectedTaskAbility
404-
);
405-
406-
expect(scopedOut.relatedRuns.parent?.id).toBe(tree.parentFriendlyId);
407-
expect(scopedOut.relatedRuns.root?.id).toBe(tree.rootFriendlyId);
408-
expect(scopedOut.relatedRuns.children).toEqual([]);
409386
}
410387
);
411388

0 commit comments

Comments
 (0)