This is an automated email from the ASF dual-hosted git repository. kaxilnaik pushed a commit to branch v3-1-test in repository https://gitbox.apache.org/repos/asf/airflow.git
commit a524ff8eeec8c0bca40a7d78964448d180ff1365 Author: Guan Ming(Wesley) Chiu <[email protected]> AuthorDate: Thu Sep 18 06:30:19 2025 +0800 Fix mapIndex type validation error (#55794) (cherry picked from commit 7f9a96aa54327729c0bc737ac20cf43cad624a84) --- .../src/airflow/ui/src/hooks/useSelectedVersion.ts | 6 ++++-- .../airflow/ui/src/layouts/Details/DagBreadcrumb.tsx | 5 +++-- .../ui/src/pages/TaskInstance/AssetEvents.tsx | 20 ++++++++++++++------ .../airflow/ui/src/pages/TaskInstance/Details.tsx | 19 +++++++++++++------ .../airflow/ui/src/pages/TaskInstance/Logs/Logs.tsx | 19 +++++++++++++------ .../ui/src/pages/TaskInstance/TaskInstance.tsx | 13 ++++++++++--- 6 files changed, 57 insertions(+), 25 deletions(-) diff --git a/airflow-core/src/airflow/ui/src/hooks/useSelectedVersion.ts b/airflow-core/src/airflow/ui/src/hooks/useSelectedVersion.ts index fb33fca1719..a5c33c981bc 100644 --- a/airflow-core/src/airflow/ui/src/hooks/useSelectedVersion.ts +++ b/airflow-core/src/airflow/ui/src/hooks/useSelectedVersion.ts @@ -65,18 +65,20 @@ const useSelectedVersion = (): number | undefined => { const taskNode = structureData?.nodes.find((node) => node.id === taskId); const isMapped = taskNode?.is_mapped; + const parsedMapIndex = parseInt(mapIndex, 10); const { data: mappedTaskInstanceData } = useTaskInstanceServiceGetMappedTaskInstance( { dagId, dagRunId: runId, - mapIndex: parseInt(mapIndex, 10), + mapIndex: parsedMapIndex, taskId, }, undefined, // Do not enable on a task instance summary. (mapped task but no mapIndex defined) { - enabled: taskNode !== undefined && !Boolean(mapIndex === "-1" && isMapped), + enabled: + taskNode !== undefined && !Boolean(parsedMapIndex === -1 && isMapped) && !isNaN(parsedMapIndex), }, ); diff --git a/airflow-core/src/airflow/ui/src/layouts/Details/DagBreadcrumb.tsx b/airflow-core/src/airflow/ui/src/layouts/Details/DagBreadcrumb.tsx index b9d144944b6..c93475ecbfd 100644 --- a/airflow-core/src/airflow/ui/src/layouts/Details/DagBreadcrumb.tsx +++ b/airflow-core/src/airflow/ui/src/layouts/Details/DagBreadcrumb.tsx @@ -36,6 +36,7 @@ export const DagBreadcrumb = () => { const { t: translate } = useTranslation(); const { dagId = "", groupId, mapIndex = "-1", runId, taskId } = useParams(); const refetchInterval = useAutoRefresh({ dagId }); + const parsedMapIndex = parseInt(mapIndex, 10); const { data: dag } = useDagServiceGetDagDetails({ dagId, @@ -56,9 +57,9 @@ export const DagBreadcrumb = () => { const { data: task } = useTaskServiceGetTask({ dagId, taskId }, undefined, { enabled: Boolean(taskId) }); const { data: mappedTaskInstance } = useTaskInstanceServiceGetMappedTaskInstance( - { dagId, dagRunId: runId ?? "", mapIndex: parseInt(mapIndex, 10), taskId: taskId ?? "" }, + { dagId, dagRunId: runId ?? "", mapIndex: parsedMapIndex, taskId: taskId ?? "" }, undefined, - { enabled: Boolean(runId) && Boolean(taskId) && mapIndex !== "-1" }, + { enabled: Boolean(runId) && Boolean(taskId) && mapIndex !== "-1" && !isNaN(parsedMapIndex) }, ); const links: Array<{ label: ReactNode | string; labelExtra?: ReactNode; title?: string; value?: string }> = diff --git a/airflow-core/src/airflow/ui/src/pages/TaskInstance/AssetEvents.tsx b/airflow-core/src/airflow/ui/src/pages/TaskInstance/AssetEvents.tsx index 56081790d32..7e2fd720863 100644 --- a/airflow-core/src/airflow/ui/src/pages/TaskInstance/AssetEvents.tsx +++ b/airflow-core/src/airflow/ui/src/pages/TaskInstance/AssetEvents.tsx @@ -25,12 +25,20 @@ import { isStatePending, useAutoRefresh } from "src/utils"; export const AssetEvents = () => { const { dagId = "", mapIndex = "-1", runId = "", taskId = "" } = useParams(); - const { data: taskInstance } = useTaskInstanceServiceGetMappedTaskInstance({ - dagId, - dagRunId: runId, - mapIndex: parseInt(mapIndex, 10), - taskId, - }); + const parsedMapIndex = parseInt(mapIndex, 10); + + const { data: taskInstance } = useTaskInstanceServiceGetMappedTaskInstance( + { + dagId, + dagRunId: runId, + mapIndex: parsedMapIndex, + taskId, + }, + undefined, + { + enabled: !isNaN(parsedMapIndex), + }, + ); const refetchInterval = useAutoRefresh({ dagId }); diff --git a/airflow-core/src/airflow/ui/src/pages/TaskInstance/Details.tsx b/airflow-core/src/airflow/ui/src/pages/TaskInstance/Details.tsx index 15b35e4b5b4..400846d1e2a 100644 --- a/airflow-core/src/airflow/ui/src/pages/TaskInstance/Details.tsx +++ b/airflow-core/src/airflow/ui/src/pages/TaskInstance/Details.tsx @@ -42,13 +42,20 @@ export const Details = () => { const [searchParams, setSearchParams] = useSearchParams(); const tryNumberParam = searchParams.get(SearchParamsKeys.TRY_NUMBER); + const parsedMapIndex = parseInt(mapIndex, 10); - const { data: taskInstance } = useTaskInstanceServiceGetMappedTaskInstance({ - dagId, - dagRunId: runId, - mapIndex: parseInt(mapIndex, 10), - taskId, - }); + const { data: taskInstance } = useTaskInstanceServiceGetMappedTaskInstance( + { + dagId, + dagRunId: runId, + mapIndex: parsedMapIndex, + taskId, + }, + undefined, + { + enabled: !isNaN(parsedMapIndex), + }, + ); const onSelectTryNumber = (newTryNumber: number) => { if (newTryNumber === taskInstance?.try_number) { diff --git a/airflow-core/src/airflow/ui/src/pages/TaskInstance/Logs/Logs.tsx b/airflow-core/src/airflow/ui/src/pages/TaskInstance/Logs/Logs.tsx index c03ec5c49cb..86678a308c6 100644 --- a/airflow-core/src/airflow/ui/src/pages/TaskInstance/Logs/Logs.tsx +++ b/airflow-core/src/airflow/ui/src/pages/TaskInstance/Logs/Logs.tsx @@ -41,17 +41,24 @@ export const Logs = () => { const tryNumberParam = searchParams.get(SearchParamsKeys.TRY_NUMBER); const logLevelFilters = searchParams.getAll(SearchParamsKeys.LOG_LEVEL); const sourceFilters = searchParams.getAll(SearchParamsKeys.SOURCE); + const parsedMapIndex = parseInt(mapIndex, 10); const { data: taskInstance, error, isLoading, - } = useTaskInstanceServiceGetMappedTaskInstance({ - dagId, - dagRunId: runId, - mapIndex: parseInt(mapIndex, 10), - taskId, - }); + } = useTaskInstanceServiceGetMappedTaskInstance( + { + dagId, + dagRunId: runId, + mapIndex: parsedMapIndex, + taskId, + }, + undefined, + { + enabled: !isNaN(parsedMapIndex), + }, + ); const onSelectTryNumber = (newTryNumber: number) => { if (newTryNumber === taskInstance?.try_number) { diff --git a/airflow-core/src/airflow/ui/src/pages/TaskInstance/TaskInstance.tsx b/airflow-core/src/airflow/ui/src/pages/TaskInstance/TaskInstance.tsx index 0eb684f0d89..11f67d3df18 100644 --- a/airflow-core/src/airflow/ui/src/pages/TaskInstance/TaskInstance.tsx +++ b/airflow-core/src/airflow/ui/src/pages/TaskInstance/TaskInstance.tsx @@ -16,6 +16,7 @@ * specific language governing permissions and limitations * under the License. */ +import { Heading } from "@chakra-ui/react"; import { ReactFlowProvider } from "@xyflow/react"; import { useMemo } from "react"; import { useTranslation } from "react-i18next"; @@ -34,7 +35,7 @@ import { isStatePending, useAutoRefresh } from "src/utils"; import { Header } from "./Header"; export const TaskInstance = () => { - const { t: translate } = useTranslation(["dag", "hitl"]); + const { t: translate } = useTranslation(["dag", "common", "hitl"]); const { dagId = "", mapIndex = "-1", runId = "", taskId = "" } = useParams(); // Get external views with task_instance destination const externalTabs = usePluginTabs("task_instance"); @@ -56,6 +57,7 @@ export const TaskInstance = () => { ]; const refetchInterval = useAutoRefresh({ dagId }); + const parsedMapIndex = parseInt(mapIndex, 10); const { data: taskInstance, @@ -65,11 +67,12 @@ export const TaskInstance = () => { { dagId, dagRunId: runId, - mapIndex: parseInt(mapIndex, 10), + mapIndex: parsedMapIndex, taskId, }, undefined, { + enabled: !isNaN(parsedMapIndex), refetchInterval: (query) => (isStatePending(query.state.data?.state) ? refetchInterval : false), }, ); @@ -108,7 +111,11 @@ export const TaskInstance = () => { return ( <ReactFlowProvider> <DetailsLayout error={error} isLoading={isLoading} tabs={displayTabs}> - {taskInstance === undefined ? undefined : ( + {taskInstance === undefined ? ( + <Heading p={2} size="lg"> + {translate("common:noItemsFound", { modelName: translate("common:taskInstance_one") })} + </Heading> + ) : ( <Header isRefreshing={Boolean(isStatePending(taskInstance.state) && Boolean(refetchInterval))} taskInstance={taskInstance}
