Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
2 changes: 1 addition & 1 deletion src/components/Home/RunSection/RunSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ export const RunSection = ({ onEmptyList }: { onEmptyList?: () => void }) => {

const { data, isLoading, isFetching, error, isFetched } =
useQuery<ListPipelineJobsResponse>({
queryKey: ["runs", backendUrl, pageToken, search.filter],
queryKey: ["runs", pageToken, search.filter],
refetchOnWindowFocus: false,
enabled: configured && available,
queryFn: async () => {
Expand Down
56 changes: 53 additions & 3 deletions src/components/PipelineRun/RunDetails.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { ContextPanelProvider } from "@/providers/ContextPanelProvider";
import { ExecutionDataProvider } from "@/providers/ExecutionDataProvider";
import * as executionService from "@/services/executionService";
import type { ComponentSpec } from "@/utils/componentSpec";
import { BACKEND_QUERY_KEY } from "@/utils/constants";

import { RunDetails } from "./RunDetails";

Expand Down Expand Up @@ -67,6 +68,8 @@ describe("<RunDetails/>", () => {
},
});

const MOCK_BACKEND_URL = "http://localhost:8000";

const mockExecutionDetails: GetExecutionInfoResponse = {
id: "test-execution-id",
pipeline_run_id: "123",
Expand Down Expand Up @@ -138,7 +141,10 @@ describe("<RunDetails/>", () => {
error: null,
});

queryClient.setQueryData(["pipeline-run-metadata", "123"], mockPipelineRun);
queryClient.setQueryData(
[BACKEND_QUERY_KEY, "pipeline-run-metadata", "123"],
mockPipelineRun,
);

vi.mocked(executionService.countTaskStatuses).mockReturnValue({
total: 2,
Expand All @@ -160,7 +166,7 @@ describe("<RunDetails/>", () => {
configured: true,
available: true,
ready: true,
backendUrl: "http://localhost:8000",
backendUrl: MOCK_BACKEND_URL,
isConfiguredFromEnv: false,
isConfiguredFromRelativePath: false,
setEnvConfig: vi.fn(),
Expand Down Expand Up @@ -193,6 +199,50 @@ describe("<RunDetails/>", () => {
});
};

describe("Backend Configuration", () => {
test("should render run details when backend is configured", async () => {
// The default mock has configured: true and backendUrl: MOCK_BACKEND_URL
// act
renderWithProviders(<RunDetails />);

// assert
await waitFor(() => {
expect(screen.getByText("Test Pipeline")).toBeInTheDocument();
expect(screen.getByText("Run Id:")).toBeInTheDocument();
expect(screen.getByText("123")).toBeInTheDocument();
});
});

test("should render run details when backendUrl is empty string", async () => {
// arrange - simulate custom backend toggle disabled (empty backendUrl)
vi.mocked(useBackend).mockReturnValue({
configured: true,
available: true,
ready: true,
backendUrl: "",
isConfiguredFromEnv: false,
isConfiguredFromRelativePath: true,
setEnvConfig: vi.fn(),
setRelativePathConfig: vi.fn(),
setBackendUrl: vi.fn(),
ping: vi.fn(),
});

// Query key no longer includes backendUrl - cache is shared regardless of URL
// and invalidated when backend URL changes

// act
renderWithProviders(<RunDetails />);

// assert
await waitFor(() => {
expect(screen.getByText("Test Pipeline")).toBeInTheDocument();
expect(screen.getByText("Run Id:")).toBeInTheDocument();
expect(screen.getByText("123")).toBeInTheDocument();
});
});
});

describe("Inspect Pipeline Button", () => {
test("should render inspect button when pipeline exists", async () => {
// arrange
Expand Down Expand Up @@ -282,7 +332,7 @@ describe("<RunDetails/>", () => {
};

queryClient.setQueryData(
["pipeline-run-metadata", "123"],
[BACKEND_QUERY_KEY, "pipeline-run-metadata", "123"],
pipelineRunWithDifferentCreator,
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { useBackend } from "@/providers/BackendProvider";
import { getExecutionArtifacts } from "@/services/executionService";
import { getBackendStatusString } from "@/utils/backend";
import type { TaskSpec } from "@/utils/componentSpec";
import { BACKEND_QUERY_KEY } from "@/utils/constants";

import IOExtras from "./IOExtras";
import IOInputs from "./IOInputs";
Expand All @@ -28,9 +29,9 @@ const IOSection = ({ taskSpec, executionId, readOnly }: IOSectionProps) => {
isFetching,
error,
} = useQuery({
queryKey: ["artifacts", executionId],
queryKey: [BACKEND_QUERY_KEY, "artifacts", executionId],
queryFn: () => getExecutionArtifacts(String(executionId), backendUrl),
enabled: !!executionId,
enabled: !!executionId && configured,
});

if (!configured) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { Spinner } from "@/components/ui/spinner";
import { useBackend } from "@/providers/BackendProvider";
import type { RunStatus } from "@/types/pipelineRun";
import { getBackendStatusString } from "@/utils/backend";
import { BACKEND_QUERY_KEY } from "@/utils/constants";

const LogDisplay = ({
logs,
Expand Down Expand Up @@ -114,10 +115,10 @@ const Logs = ({
log_text?: string;
system_error_exception_full?: string;
}>();
const { data, isLoading, error, refetch } = useQuery({
queryKey: ["logs", executionId],
const { data, isLoading, error } = useQuery({
queryKey: [BACKEND_QUERY_KEY, "logs", executionId],
queryFn: () => getLogs(String(executionId), backendUrl),
enabled: isLogging,
enabled: isLogging && configured,
refetchInterval: 5000,
refetchIntervalInBackground: false,
});
Expand All @@ -142,10 +143,6 @@ const Logs = ({
}
}, [data, error]);

useEffect(() => {
refetch();
}, [backendUrl, refetch]);

if (!configured) {
return (
<InfoBox title="Backend not configured" variant="warning">
Expand Down
17 changes: 9 additions & 8 deletions src/hooks/usePipelineRunData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,12 @@ import {
getRunStatus,
isStatusComplete,
} from "@/services/executionService";
import { BACKEND_QUERY_KEY } from "@/utils/constants";

const useRootExecutionId = (id: string) => {
const { backendUrl } = useBackend();
const { backendUrl, configured } = useBackend();
const { data: rootExecutionId } = useQuery({
queryKey: ["pipeline-run-execution-id", id],
queryKey: [BACKEND_QUERY_KEY, "pipeline-run-execution-id", id],
queryFn: async () => {
const rootExecutionId = await fetchPipelineRun(id, backendUrl)
.then((res) => res.root_execution_id)
Expand All @@ -27,7 +28,7 @@ const useRootExecutionId = (id: string) => {
// assuming id is root_execution_id
return id;
},
enabled: !!id && id.length > 0,
enabled: !!id && id.length > 0 && configured,
staleTime: Infinity,
});

Expand All @@ -36,13 +37,13 @@ const useRootExecutionId = (id: string) => {

/* Accepts root_execution_id or run_id and returns execution details and state */
export const usePipelineRunData = (id: string) => {
const { backendUrl } = useBackend();
const { backendUrl, configured } = useBackend();

const rootExecutionId = useRootExecutionId(id);

const { data: executionDetails } = useQuery({
enabled: !!rootExecutionId,
queryKey: ["execution-details", rootExecutionId],
enabled: !!rootExecutionId && configured,
queryKey: [BACKEND_QUERY_KEY, "execution-details", rootExecutionId],
queryFn: async () => {
if (!rootExecutionId) {
throw new Error("No root execution id found");
Expand All @@ -58,8 +59,8 @@ export const usePipelineRunData = (id: string) => {
error,
isLoading,
} = useQuery({
enabled: !!rootExecutionId && !!executionDetails,
queryKey: ["pipeline-run", rootExecutionId],
enabled: !!rootExecutionId && !!executionDetails && configured,
queryKey: [BACKEND_QUERY_KEY, "pipeline-run", rootExecutionId],
queryFn: async () => {
if (!rootExecutionId) {
throw new Error("No root execution id found");
Expand Down
18 changes: 12 additions & 6 deletions src/hooks/useSubgraphBreadcrumbs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { useMemo } from "react";
import { useBackend } from "@/providers/BackendProvider";
import { fetchExecutionDetails } from "@/services/executionService";
import { isGraphImplementationOutput } from "@/utils/componentSpec";
import { ONE_MINUTE_IN_MS } from "@/utils/constants";
import { BACKEND_QUERY_KEY, ONE_MINUTE_IN_MS } from "@/utils/constants";

export interface BreadcrumbSegment {
taskId: string;
Expand All @@ -27,11 +27,16 @@ export const useSubgraphBreadcrumbs = (
rootExecutionId: string | undefined,
subgraphExecutionId: string | undefined,
): SubgraphBreadcrumbsResult => {
const { backendUrl } = useBackend();
const { backendUrl, configured } = useBackend();
const queryClient = useQueryClient();

const { data, isLoading, error } = useQuery({
queryKey: ["subgraph-breadcrumbs", rootExecutionId, subgraphExecutionId],
queryKey: [
BACKEND_QUERY_KEY,
"subgraph-breadcrumbs",
rootExecutionId,
subgraphExecutionId,
],
queryFn: async () => {
if (!rootExecutionId || !subgraphExecutionId) {
return { segments: [] };
Expand All @@ -44,7 +49,7 @@ export const useSubgraphBreadcrumbs = (
const segmentsInReverseOrder: BreadcrumbSegment[] = [];
let currentExecutionId = subgraphExecutionId;
let currentDetails = await queryClient.ensureQueryData({
queryKey: ["execution-details", currentExecutionId],
queryKey: [BACKEND_QUERY_KEY, "execution-details", currentExecutionId],
queryFn: () => fetchExecutionDetails(currentExecutionId, backendUrl),
staleTime: ONE_MINUTE_IN_MS,
});
Expand All @@ -57,7 +62,7 @@ export const useSubgraphBreadcrumbs = (
}

const parentDetails = await queryClient.ensureQueryData({
queryKey: ["execution-details", parentExecutionId],
queryKey: [BACKEND_QUERY_KEY, "execution-details", parentExecutionId],
queryFn: () => fetchExecutionDetails(parentExecutionId, backendUrl),
staleTime: ONE_MINUTE_IN_MS,
});
Expand Down Expand Up @@ -95,7 +100,8 @@ export const useSubgraphBreadcrumbs = (
enabled:
!!rootExecutionId &&
!!subgraphExecutionId &&
rootExecutionId !== subgraphExecutionId,
rootExecutionId !== subgraphExecutionId &&
configured,
staleTime: ONE_MINUTE_IN_MS,
retry: 1,
});
Expand Down
19 changes: 18 additions & 1 deletion src/providers/BackendProvider.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
import { useQueryClient } from "@tanstack/react-query";
import {
type ReactNode,
useCallback,
useEffect,
useMemo,
useRef,
useState,
} from "react";

import useToastNotification from "@/hooks/useToastNotification";
import { API_URL } from "@/utils/constants";
import { API_URL, BACKEND_QUERY_KEY } from "@/utils/constants";
import {
getUseEnv,
getUserBackendUrl,
Expand Down Expand Up @@ -45,6 +47,7 @@ const BackendContext =

export const BackendProvider = ({ children }: { children: ReactNode }) => {
const notify = useToastNotification();
const queryClient = useQueryClient();

const backendUrlFromEnv = API_URL;

Expand All @@ -55,6 +58,9 @@ export const BackendProvider = ({ children }: { children: ReactNode }) => {
const [settingsLoaded, setSettingsLoaded] = useState(false);
const [ready, setReady] = useState(false);

// Track the previous backend URL to detect changes
const previousBackendUrlRef = useRef<string | null>(null);

let backendUrl = "";
if (useEnv && backendUrlFromEnv) {
backendUrl = backendUrlFromEnv;
Expand Down Expand Up @@ -133,6 +139,17 @@ export const BackendProvider = ({ children }: { children: ReactNode }) => {
}
}, [backendUrl, settingsLoaded]);

// Invalidate only backend-dependent queries when the backend URL changes
useEffect(() => {
if (
previousBackendUrlRef.current !== null &&
previousBackendUrlRef.current !== backendUrl
) {
queryClient.invalidateQueries({ queryKey: [BACKEND_QUERY_KEY] });
}
previousBackendUrlRef.current = backendUrl;
}, [backendUrl, queryClient]);

useEffect(() => {
const getSettings = async () => {
const url = await getUserBackendUrl();
Expand Down
13 changes: 8 additions & 5 deletions src/services/executionService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import type {
import { useBackend } from "@/providers/BackendProvider";
import type { RunStatus, TaskStatusCounts } from "@/types/pipelineRun";
import {
BACKEND_QUERY_KEY,
DEFAULT_RATE_LIMIT_RPS,
TWENTY_FOUR_HOURS_IN_MS,
} from "@/utils/constants";
Expand Down Expand Up @@ -45,12 +46,12 @@ export const fetchPipelineRun = async (
};

export const useFetchPipelineRunMetadata = (runId: string | undefined) => {
const { backendUrl } = useBackend();
const { backendUrl, configured } = useBackend();

return useQuery<PipelineRunResponse>({
queryKey: ["pipeline-run-metadata", runId],
queryKey: [BACKEND_QUERY_KEY, "pipeline-run-metadata", runId],
queryFn: () => fetchPipelineRun(runId!, backendUrl),
enabled: !!runId,
enabled: !!runId && configured,
refetchOnWindowFocus: false,
staleTime: TWENTY_FOUR_HOURS_IN_MS,
});
Expand All @@ -68,10 +69,12 @@ export const useFetchContainerExecutionState = (
executionId: string | undefined,
backendUrl: string,
) => {
const { configured } = useBackend();

return useQuery<GetContainerExecutionStateResponse>({
queryKey: ["container-execution-state", executionId],
queryKey: [BACKEND_QUERY_KEY, "container-execution-state", executionId],
queryFn: () => fetchContainerExecutionState(executionId!, backendUrl),
enabled: !!executionId,
enabled: !!executionId && configured,
refetchOnWindowFocus: false,
});
};
Expand Down
4 changes: 4 additions & 0 deletions src/utils/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,3 +80,7 @@ export const ISO8601_DURATION_ZERO_DAYS = "P0D";
export const DEFAULT_RATE_LIMIT_RPS = 10; // requests per second

export const MINUTES = 60 * 1000;

// Query key prefix for backend-dependent queries
// All queries with this prefix are invalidated when the backend URL changes
export const BACKEND_QUERY_KEY = "backend";
Loading