This is an automated email from the ASF dual-hosted git repository.
pierrejeambrun pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/airflow.git
The following commit(s) were added to refs/heads/main by this push:
new fb320ab8a00 AIP-38 Default value for version selector (#47154)
fb320ab8a00 is described below
commit fb320ab8a009d1b89b96ac99d1628d9256f95deb
Author: Pierre Jeambrun <[email protected]>
AuthorDate: Mon Mar 3 15:10:24 2025 +0100
AIP-38 Default value for version selector (#47154)
* AIP-38 Default value for version selector
* Fix following code review
---
airflow/ui/src/components/DagVersionSelect.tsx | 26 ++++----
airflow/ui/src/hooks/useSelectedVersion.ts | 83 ++++++++++++++++++++++++
airflow/ui/src/layouts/Details/DetailsLayout.tsx | 2 +-
airflow/ui/src/layouts/Details/Graph/Graph.tsx | 11 ++--
airflow/ui/src/layouts/Details/PanelButtons.tsx | 5 +-
airflow/ui/src/pages/Dag/Code/Code.tsx | 14 ++--
6 files changed, 108 insertions(+), 33 deletions(-)
diff --git a/airflow/ui/src/components/DagVersionSelect.tsx
b/airflow/ui/src/components/DagVersionSelect.tsx
index 9b3e3ea9a5a..2dbb6089561 100644
--- a/airflow/ui/src/components/DagVersionSelect.tsx
+++ b/airflow/ui/src/components/DagVersionSelect.tsx
@@ -21,25 +21,23 @@ import { useQueryClient } from "@tanstack/react-query";
import type { OptionsOrGroups, GroupBase, SingleValue } from
"chakra-react-select";
import { AsyncSelect } from "chakra-react-select";
import { useCallback } from "react";
-import { useSearchParams } from "react-router-dom";
+import { useParams, useSearchParams } from "react-router-dom";
import { UseDagVersionServiceGetDagVersionsKeyFn } from "openapi/queries";
import { DagVersionService } from "openapi/requests/services.gen";
import type { DAGVersionCollectionResponse, DagVersionResponse } from
"openapi/requests/types.gen";
import { SearchParamsKeys } from "src/constants/searchParams";
+import useSelectedVersion from "src/hooks/useSelectedVersion";
import type { Option } from "src/utils/option";
-const DagVersionSelect = ({
- dagId,
- disabled = false,
-}: {
- readonly dagId: string | undefined;
- readonly disabled?: boolean;
-}) => {
+const DagVersionSelect = ({ disabled = false }: { readonly disabled?: boolean
}) => {
const queryClient = useQueryClient();
const [searchParams, setSearchParams] = useSearchParams();
- const selectedVersion = searchParams.get(SearchParamsKeys.VERSION_NUMBER);
+
+ const selectedVersion = useSelectedVersion();
+
+ const { dagId = "" } = useParams();
const loadVersions = (
_: string,
@@ -48,7 +46,7 @@ const DagVersionSelect = ({
queryClient.fetchQuery({
queryFn: () =>
DagVersionService.getDagVersions({
- dagId: dagId ?? "",
+ dagId,
}).then((data: DAGVersionCollectionResponse) => {
const options = data.dag_versions.map((version: DagVersionResponse)
=> {
const versionNumber = version.version_number.toString();
@@ -63,7 +61,7 @@ const DagVersionSelect = ({
return options;
}),
- queryKey: UseDagVersionServiceGetDagVersionsKeyFn({ dagId: dagId ?? ""
}),
+ queryKey: UseDagVersionServiceGetDagVersionsKeyFn({ dagId }),
staleTime: 0,
});
@@ -86,9 +84,9 @@ const DagVersionSelect = ({
loadOptions={loadVersions}
onChange={handleStateChange}
placeholder="Dag Version"
- // null is required
https://github.com/JedWatson/react-select/issues/3066
- // eslint-disable-next-line unicorn/no-null
- value={selectedVersion === null ? null : { label:
`v${selectedVersion}`, value: selectedVersion }}
+ value={
+ selectedVersion === undefined ? undefined : { label:
`v${selectedVersion}`, value: selectedVersion }
+ }
/>
</Field.Root>
);
diff --git a/airflow/ui/src/hooks/useSelectedVersion.ts
b/airflow/ui/src/hooks/useSelectedVersion.ts
new file mode 100644
index 00000000000..01258a238ba
--- /dev/null
+++ b/airflow/ui/src/hooks/useSelectedVersion.ts
@@ -0,0 +1,83 @@
+/*!
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import { useParams, useSearchParams } from "react-router-dom";
+
+import {
+ useDagRunServiceGetDagRun,
+ useDagServiceGetDagDetails,
+ useTaskInstanceServiceGetMappedTaskInstance,
+ useTaskServiceGetTask,
+} from "openapi/queries";
+import { SearchParamsKeys } from "src/constants/searchParams";
+
+const useSelectedVersion = (): string | undefined => {
+ const [searchParams] = useSearchParams();
+
+ const selectedVersionUrl = searchParams.get(SearchParamsKeys.VERSION_NUMBER);
+
+ const { dagId = "", mapIndex = "-1", runId = "", taskId = "" } = useParams();
+
+ const { data: dagData } = useDagServiceGetDagDetails(
+ {
+ dagId,
+ },
+ undefined,
+ { enabled: Boolean(dagId) },
+ );
+
+ const { data: runData } = useDagRunServiceGetDagRun(
+ {
+ dagId,
+ dagRunId: runId,
+ },
+ undefined,
+ { enabled: Boolean(dagId) && Boolean(runId) },
+ );
+
+ const { data: taskData } = useTaskServiceGetTask(
+ {
+ dagId,
+ taskId,
+ },
+ undefined,
+ { enabled: Boolean(dagId) && Boolean(runId) && Boolean(taskId) },
+ );
+
+ const { data: mappedTaskInstanceData } =
useTaskInstanceServiceGetMappedTaskInstance(
+ {
+ dagId,
+ dagRunId: runId,
+ mapIndex: parseInt(mapIndex, 10),
+ taskId,
+ },
+ undefined,
+ // Do not enable on a task instance summary. Mapped task but no mapIndex
defined.
+ { enabled: taskData !== undefined && !Boolean(mapIndex === "-1" &&
taskData.is_mapped) },
+ );
+
+ const selectedVersionNumber =
+ selectedVersionUrl ??
+ mappedTaskInstanceData?.dag_version?.version_number ??
+ runData?.dag_versions.at(-1)?.version_number ??
+ dagData?.latest_dag_version?.version_number;
+
+ return selectedVersionNumber?.toString();
+};
+
+export default useSelectedVersion;
diff --git a/airflow/ui/src/layouts/Details/DetailsLayout.tsx
b/airflow/ui/src/layouts/Details/DetailsLayout.tsx
index c93c8fe8b5f..cbe63d77d4a 100644
--- a/airflow/ui/src/layouts/Details/DetailsLayout.tsx
+++ b/airflow/ui/src/layouts/Details/DetailsLayout.tsx
@@ -71,7 +71,7 @@ export const DetailsLayout = ({ children, error, isLoading,
tabs }: Props) => {
<PanelGroup autoSaveId={dagId} direction="horizontal">
<Panel defaultSize={20} minSize={6}>
<Box height="100%" position="relative" pr={2}>
- <PanelButtons dagId={dagId} dagView={dagView}
setDagView={setDagView} />
+ <PanelButtons dagView={dagView} setDagView={setDagView} />
{dagView === "graph" ? <Graph /> : <Grid />}
</Box>
</Panel>
diff --git a/airflow/ui/src/layouts/Details/Graph/Graph.tsx
b/airflow/ui/src/layouts/Details/Graph/Graph.tsx
index 8b5b6c466e5..09c1e93e1de 100644
--- a/airflow/ui/src/layouts/Details/Graph/Graph.tsx
+++ b/airflow/ui/src/layouts/Details/Graph/Graph.tsx
@@ -19,12 +19,12 @@
import { useToken } from "@chakra-ui/react";
import { ReactFlow, Controls, Background, MiniMap, type Node as ReactFlowNode
} from "@xyflow/react";
import "@xyflow/react/dist/style.css";
-import { useParams, useSearchParams } from "react-router-dom";
+import { useParams } from "react-router-dom";
import { useGridServiceGridData, useStructureServiceStructureData } from
"openapi/queries";
-import { SearchParamsKeys } from "src/constants/searchParams";
import { useColorMode } from "src/context/colorMode";
import { useOpenGroups } from "src/context/openGroups";
+import useSelectedVersion from "src/hooks/useSelectedVersion";
import { isStatePending, useAutoRefresh } from "src/utils";
import Edge from "./Edge";
@@ -33,8 +33,6 @@ import { TaskNode } from "./TaskNode";
import type { CustomNodeProps } from "./reactflowUtils";
import { useGraphLayout } from "./useGraphLayout";
-const VERSION_NUMBER_PARAM = SearchParamsKeys.VERSION_NUMBER;
-
const nodeColor = (
{ data: { depth, height, isOpen, taskInstance, width }, type }:
ReactFlowNode<CustomNodeProps>,
evenColor?: string,
@@ -67,8 +65,7 @@ export const Graph = () => {
const { colorMode = "light" } = useColorMode();
const { dagId = "", runId, taskId } = useParams();
- const [searchParams] = useSearchParams();
- const selectedVersion = searchParams.get(VERSION_NUMBER_PARAM);
+ const selectedVersion = useSelectedVersion();
// corresponds to the "bg", "bg.emphasized", "border.inverted" semantic
tokens
const [oddLight, oddDark, evenLight, evenDark, selectedDarkColor,
selectedLightColor] = useToken("colors", [
@@ -86,7 +83,7 @@ export const Graph = () => {
const { data: graphData = { arrange: "LR", edges: [], nodes: [] } } =
useStructureServiceStructureData({
dagId,
- versionNumber: selectedVersion === null ? undefined :
parseInt(selectedVersion, 10),
+ versionNumber: selectedVersion === undefined ? undefined :
parseInt(selectedVersion, 10),
});
const { data } = useGraphLayout({
diff --git a/airflow/ui/src/layouts/Details/PanelButtons.tsx
b/airflow/ui/src/layouts/Details/PanelButtons.tsx
index 5898941ae12..30ad6b93c5a 100644
--- a/airflow/ui/src/layouts/Details/PanelButtons.tsx
+++ b/airflow/ui/src/layouts/Details/PanelButtons.tsx
@@ -23,12 +23,11 @@ import { MdOutlineAccountTree } from "react-icons/md";
import DagVersionSelect from "src/components/DagVersionSelect";
type Props = {
- readonly dagId: string;
readonly dagView: string;
readonly setDagView: (x: "graph" | "grid") => void;
} & StackProps;
-export const PanelButtons = ({ dagId, dagView, setDagView, ...rest }: Props)
=> (
+export const PanelButtons = ({ dagView, setDagView, ...rest }: Props) => (
<HStack justifyContent="space-between" position="absolute" top={0}
width="100%" zIndex={1} {...rest}>
<ButtonGroup attached size="sm" variant="outline">
<IconButton
@@ -51,7 +50,7 @@ export const PanelButtons = ({ dagId, dagView, setDagView,
...rest }: Props) =>
</IconButton>
</ButtonGroup>
<Box bg="bg" mr={2}>
- <DagVersionSelect dagId={dagId} disabled={dagView !== "graph"} />
+ <DagVersionSelect disabled={dagView !== "graph"} />
</Box>
</HStack>
);
diff --git a/airflow/ui/src/pages/Dag/Code/Code.tsx
b/airflow/ui/src/pages/Dag/Code/Code.tsx
index f32b31dbf47..1572403742d 100644
--- a/airflow/ui/src/pages/Dag/Code/Code.tsx
+++ b/airflow/ui/src/pages/Dag/Code/Code.tsx
@@ -18,7 +18,7 @@
*/
import { Box, Button, Heading, HStack } from "@chakra-ui/react";
import { useState } from "react";
-import { useParams, useSearchParams } from "react-router-dom";
+import { useParams } from "react-router-dom";
import { createElement, PrismLight as SyntaxHighlighter } from
"react-syntax-highlighter";
import python from "react-syntax-highlighter/dist/esm/languages/prism/python";
import { oneLight, oneDark } from
"react-syntax-highlighter/dist/esm/styles/prism";
@@ -28,18 +28,16 @@ import DagVersionSelect from
"src/components/DagVersionSelect";
import { ErrorAlert } from "src/components/ErrorAlert";
import Time from "src/components/Time";
import { ProgressBar } from "src/components/ui";
-import { SearchParamsKeys } from "src/constants/searchParams";
import { useColorMode } from "src/context/colorMode";
+import useSelectedVersion from "src/hooks/useSelectedVersion";
import { useConfig } from "src/queries/useConfig";
SyntaxHighlighter.registerLanguage("python", python);
-const VERSION_NUMBER_PARAM = SearchParamsKeys.VERSION_NUMBER;
-
export const Code = () => {
const { dagId } = useParams();
- const [searchParams] = useSearchParams();
- const selectedVersion = searchParams.get(VERSION_NUMBER_PARAM);
+
+ const selectedVersion = useSelectedVersion();
const {
data: dag,
@@ -55,7 +53,7 @@ export const Code = () => {
isLoading: isCodeLoading,
} = useDagSourceServiceGetDagSource({
dagId: dagId ?? "",
- versionNumber: selectedVersion === null ? undefined :
parseInt(selectedVersion, 10),
+ versionNumber: selectedVersion === undefined ? undefined :
parseInt(selectedVersion, 10),
});
const defaultWrap = Boolean(useConfig("default_wrap"));
@@ -81,7 +79,7 @@ export const Code = () => {
</Heading>
)}
<HStack>
- <DagVersionSelect dagId={dagId} />
+ <DagVersionSelect />
<Button aria-label={wrap ? "Unwrap" : "Wrap"} bg="bg.panel"
onClick={toggleWrap} variant="outline">
{wrap ? "Unwrap" : "Wrap"}
</Button>