Skip to content

Commit 59b7126

Browse files
authored
Load Pipeline Run by run_id or root_execution_id (#1051)
## Description <!-- Please provide a brief description of the changes made in this pull request. Include any relevant context or reasoning for the changes. --> Supersedes #859, #1045 and remedies the issues that caused the rollback in #1052 Allows users to navigate to a pipeline run using the `run_id` instead of the `root_execution_id` if they choose to. This logic is handled through a new hook that will attempt to fetch data from an input `id` first assuming it is a `root_execution_id` and, if not, a `run_id`. ## Related Issue and Pull requests <!-- Link to any related issues using the format #<issue-number> --> Partially addresses #128 ## Type of Change <!-- Please delete options that are not relevant --> - [x] Bug fix - [x] Improvement ## Checklist <!-- Please ensure the following are completed before submitting the PR --> - [ ] I have tested this does not break current pipelines / runs functionality - [ ] I have tested the changes on staging ## Screenshots (if applicable) <!-- Include any screenshots that might help explain the changes or provide visual context --> ## Test Instructions <!-- Detail steps and prerequisites for testing the changes in this PR --> Verify you can navigate to and from pipeline runs via the run id, and also still use the clone & inspect & rerun buttons as expected. ## Additional Comments <!-- Add any additional context or information that reviewers might need to know regarding this PR -->
1 parent 82a3310 commit 59b7126

File tree

4 files changed

+104
-32
lines changed

4 files changed

+104
-32
lines changed

src/hooks/usePipelineRunData.ts

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
import { useQuery } from "@tanstack/react-query";
2+
3+
import { useBackend } from "@/providers/BackendProvider";
4+
import {
5+
fetchExecutionDetails,
6+
fetchExecutionState,
7+
fetchPipelineRun,
8+
} from "@/services/executionService";
9+
10+
async function fetchPipelineExecutionInfo(
11+
rootExecutionId: string,
12+
backendUrl: string,
13+
) {
14+
const data = await Promise.all([
15+
fetchExecutionDetails(rootExecutionId, backendUrl),
16+
fetchExecutionState(rootExecutionId, backendUrl),
17+
]).catch((_) => undefined);
18+
19+
if (data) {
20+
return {
21+
rootExecutionId,
22+
details: data[0],
23+
state: data[1],
24+
};
25+
}
26+
27+
return undefined;
28+
}
29+
30+
/* Accepts root_execution_id or run_id and returns execution details and state */
31+
export const usePipelineRunData = (id: string) => {
32+
const { backendUrl } = useBackend();
33+
34+
const {
35+
data: executionData,
36+
error,
37+
isLoading,
38+
} = useQuery({
39+
queryKey: ["pipeline-run", id],
40+
queryFn: async () => {
41+
// id is root_execution_id
42+
const executionDetails = await fetchPipelineExecutionInfo(
43+
id,
44+
backendUrl,
45+
).catch((_) => undefined);
46+
47+
if (executionDetails?.rootExecutionId) {
48+
return executionDetails;
49+
}
50+
51+
// id is run_id
52+
const executionDetailsDerivedFromRun = await fetchPipelineRun(
53+
id,
54+
backendUrl,
55+
)
56+
.then((res) =>
57+
fetchPipelineExecutionInfo(res.root_execution_id, backendUrl),
58+
)
59+
.catch((_) => undefined);
60+
61+
if (executionDetailsDerivedFromRun?.rootExecutionId) {
62+
return executionDetailsDerivedFromRun;
63+
}
64+
65+
throw new Error("No pipeline run or execution details found");
66+
},
67+
});
68+
69+
return {
70+
executionData,
71+
rootExecutionId: executionData?.rootExecutionId ?? "",
72+
isLoading,
73+
error,
74+
};
75+
};

src/routes/PipelineRun/PipelineRun.test.tsx

Lines changed: 11 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import type {
88
GetGraphExecutionStateResponse,
99
} from "@/api/types.gen";
1010
import { ComponentSpecProvider } from "@/providers/ComponentSpecProvider";
11-
import * as executionService from "@/services/executionService";
1211

1312
import PipelineRun from "./PipelineRun";
1413

@@ -50,15 +49,6 @@ vi.mock("@/providers/BackendProvider", () => ({
5049
}));
5150

5251
vi.mock("@/services/executionService", () => ({
53-
useFetchExecutionInfo: vi.fn(),
54-
useFetchPipelineRun: () => ({
55-
data: null,
56-
isLoading: false,
57-
error: null,
58-
isFetching: false,
59-
refetch: () => {},
60-
enabled: false,
61-
}),
6252
countTaskStatuses: vi.fn(),
6353
getRunStatus: vi.fn(),
6454
STATUS: {
@@ -71,6 +61,11 @@ vi.mock("@/services/executionService", () => ({
7161
},
7262
}));
7363

64+
const mockUsePipelineRunData = vi.fn();
65+
vi.mock("@/hooks/usePipelineRunData", () => ({
66+
usePipelineRunData: () => mockUsePipelineRunData(),
67+
}));
68+
7469
const mockUseComponentSpec = vi.fn();
7570
vi.mock("@/providers/ComponentSpecProvider", async (importOriginal) => {
7671
const actual = (await importOriginal()) as object;
@@ -172,15 +167,14 @@ describe("<PipelineRun/>", () => {
172167
beforeEach(() => {
173168
vi.clearAllMocks();
174169

175-
vi.mocked(executionService.useFetchExecutionInfo).mockReturnValue({
176-
data: {
170+
mockUsePipelineRunData.mockReturnValue({
171+
executionData: {
177172
details: mockExecutionDetails,
178173
state: mockExecutionState,
179174
},
175+
rootExecutionId: "test-execution-id",
180176
isLoading: false,
181177
error: null,
182-
isFetching: false,
183-
refetch: () => {},
184178
});
185179
});
186180

@@ -249,15 +243,14 @@ describe("<PipelineRun/>", () => {
249243
child_task_execution_ids: {},
250244
};
251245

252-
vi.mocked(executionService.useFetchExecutionInfo).mockReturnValue({
253-
data: {
246+
mockUsePipelineRunData.mockReturnValue({
247+
executionData: {
254248
details: mockExecutionDetailsWithoutIds,
255249
state: mockExecutionState,
256250
},
251+
rootExecutionId: "test-execution-id",
257252
isLoading: false,
258253
error: null,
259-
isFetching: false,
260-
refetch: () => {},
261254
});
262255

263256
// act

src/routes/PipelineRun/PipelineRun.tsx

Lines changed: 6 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -11,33 +11,29 @@ import { InfoBox } from "@/components/shared/InfoBox";
1111
import { Spinner } from "@/components/ui/spinner";
1212
import { faviconManager } from "@/favicon";
1313
import { useDocumentTitle } from "@/hooks/useDocumentTitle";
14+
import { usePipelineRunData } from "@/hooks/usePipelineRunData";
1415
import { useBackend } from "@/providers/BackendProvider";
1516
import { useComponentSpec } from "@/providers/ComponentSpecProvider";
1617
import { type RunDetailParams, runDetailRoute } from "@/routes/router";
1718
import {
1819
countTaskStatuses,
1920
getRunStatus,
2021
STATUS,
21-
useFetchExecutionInfo,
2222
} from "@/services/executionService";
2323
import { getBackendStatusString } from "@/utils/backend";
2424
import type { ComponentSpec } from "@/utils/componentSpec";
2525

2626
const PipelineRun = () => {
2727
const { setComponentSpec, clearComponentSpec, componentSpec } =
2828
useComponentSpec();
29-
const { backendUrl, configured, available, ready } = useBackend();
30-
const { id: rootExecutionId } = runDetailRoute.useParams() as RunDetailParams;
29+
const { configured, available, ready } = useBackend();
30+
const { id } = runDetailRoute.useParams() as RunDetailParams;
3131

32-
const { data, isLoading, error, refetch } = useFetchExecutionInfo(
33-
rootExecutionId,
34-
backendUrl,
35-
false,
36-
);
32+
const { executionData, rootExecutionId, isLoading, error } =
33+
usePipelineRunData(id);
3734

38-
const { details, state } = data;
35+
const { details, state } = executionData || {};
3936

40-
// Update favicon based on pipeline status
4137
useEffect(() => {
4238
if (!details || !state) {
4339
faviconManager.reset();
@@ -73,10 +69,6 @@ const PipelineRun = () => {
7369
`Oasis - ${componentSpec?.name || ""} - ${params.id}`,
7470
});
7571

76-
useEffect(() => {
77-
refetch();
78-
}, [backendUrl, refetch]);
79-
8072
if (isLoading || !ready) {
8173
return (
8274
<div className="flex items-center justify-center h-full w-full gap-2">

src/services/executionService.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import type {
66
GetContainerExecutionStateResponse,
77
GetExecutionInfoResponse,
88
GetGraphExecutionStateResponse,
9+
PipelineRunResponse,
910
} from "@/api/types.gen";
1011
import type { TaskStatusCounts } from "@/types/pipelineRun";
1112
import { fetchWithErrorHandling } from "@/utils/fetchWithErrorHandling";
@@ -26,6 +27,17 @@ export const fetchExecutionDetails = async (
2627
return fetchWithErrorHandling(url);
2728
};
2829

30+
export const fetchPipelineRun = async (
31+
runId: string,
32+
backendUrl: string,
33+
): Promise<PipelineRunResponse> => {
34+
const response = await fetch(`${backendUrl}/api/pipeline_runs/${runId}`);
35+
if (!response.ok) {
36+
throw new Error(`Failed to fetch pipeline run: ${response.statusText}`);
37+
}
38+
return response.json();
39+
};
40+
2941
const fetchContainerExecutionState = async (
3042
executionId: string,
3143
backendUrl: string,

0 commit comments

Comments
 (0)