This is an automated email from the ASF dual-hosted git repository. pierrejeambrun pushed a commit to branch v3-0-test in repository https://gitbox.apache.org/repos/asf/airflow.git
The following commit(s) were added to refs/heads/v3-0-test by this push: new c53b53a5f56 Move asset events to its own tab (#51625) (#51655) c53b53a5f56 is described below commit c53b53a5f56a62ef371c3293ece943fb0dbe6895 Author: Pierre Jeambrun <pierrejb...@gmail.com> AuthorDate: Thu Jun 12 15:35:35 2025 +0200 Move asset events to its own tab (#51625) (#51655) * Move asset events to its own tab * Address PR comments (cherry picked from commit e8505aaff3a081b87c57bb725598af1b2bb7c656) --- .../src/airflow/ui/src/pages/Run/AssetEvents.tsx | 45 +++++++++++++++++++ .../src/airflow/ui/src/pages/Run/Details.tsx | 11 +---- airflow-core/src/airflow/ui/src/pages/Run/Run.tsx | 5 ++- .../src/airflow/ui/src/pages/Task/Task.tsx | 2 +- .../ui/src/pages/TaskInstance/AssetEvents.tsx | 51 ++++++++++++++++++++++ .../airflow/ui/src/pages/TaskInstance/Details.tsx | 18 -------- .../ui/src/pages/TaskInstance/TaskInstance.tsx | 5 ++- airflow-core/src/airflow/ui/src/router.tsx | 4 ++ 8 files changed, 108 insertions(+), 33 deletions(-) diff --git a/airflow-core/src/airflow/ui/src/pages/Run/AssetEvents.tsx b/airflow-core/src/airflow/ui/src/pages/Run/AssetEvents.tsx new file mode 100644 index 00000000000..c7fd536b950 --- /dev/null +++ b/airflow-core/src/airflow/ui/src/pages/Run/AssetEvents.tsx @@ -0,0 +1,45 @@ +/*! + * 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 } from "react-router-dom"; + +import { useDagRunServiceGetDagRun, useDagRunServiceGetUpstreamAssetEvents } from "openapi/queries"; +import { AssetEvents as AssetEventsTable } from "src/components/Assets/AssetEvents"; +import { isStatePending, useAutoRefresh } from "src/utils"; + +export const AssetEvents = () => { + const { dagId = "", runId = "" } = useParams(); + + const refetchInterval = useAutoRefresh({ dagId }); + + const { data: dagRun } = useDagRunServiceGetDagRun( + { + dagId, + dagRunId: runId, + }, + undefined, + { refetchInterval: (query) => (isStatePending(query.state.data?.state) ? refetchInterval : false) }, + ); + + const { data, isLoading } = useDagRunServiceGetUpstreamAssetEvents({ dagId, dagRunId: runId }, undefined, { + enabled: dagRun?.run_type === "asset_triggered", + refetchInterval: () => (isStatePending(dagRun?.state) ? refetchInterval : false), + }); + + return <AssetEventsTable data={data} isLoading={isLoading} title="Source Asset Event" />; +}; diff --git a/airflow-core/src/airflow/ui/src/pages/Run/Details.tsx b/airflow-core/src/airflow/ui/src/pages/Run/Details.tsx index 43140625fbe..582f6182071 100644 --- a/airflow-core/src/airflow/ui/src/pages/Run/Details.tsx +++ b/airflow-core/src/airflow/ui/src/pages/Run/Details.tsx @@ -19,8 +19,7 @@ import { Box, Flex, HStack, StackSeparator, Table, Text, VStack } from "@chakra-ui/react"; import { useParams } from "react-router-dom"; -import { useDagRunServiceGetDagRun, useDagRunServiceGetUpstreamAssetEvents } from "openapi/queries"; -import { AssetEvents } from "src/components/Assets/AssetEvents"; +import { useDagRunServiceGetDagRun } from "openapi/queries"; import { DagVersionDetails } from "src/components/DagVersionDetails"; import RenderedJsonField from "src/components/RenderedJsonField"; import { RunTypeIcon } from "src/components/RunTypeIcon"; @@ -43,17 +42,9 @@ export const Details = () => { { refetchInterval: (query) => (isStatePending(query.state.data?.state) ? refetchInterval : false) }, ); - const { data, isLoading } = useDagRunServiceGetUpstreamAssetEvents({ dagId, dagRunId: runId }, undefined, { - enabled: dagRun?.run_type === "asset_triggered", - refetchInterval: () => (isStatePending(dagRun?.state) ? refetchInterval : false), - }); - // TODO : Render DagRun configuration object return ( <Box p={2}> - {data === undefined || dagRun?.run_type !== "asset_triggered" ? undefined : ( - <AssetEvents data={data} isLoading={isLoading} title="Source Asset Event" /> - )} {dagRun === undefined ? ( <div /> ) : ( diff --git a/airflow-core/src/airflow/ui/src/pages/Run/Run.tsx b/airflow-core/src/airflow/ui/src/pages/Run/Run.tsx index 04ffb61cd89..5b4dfffd3e5 100644 --- a/airflow-core/src/airflow/ui/src/pages/Run/Run.tsx +++ b/airflow-core/src/airflow/ui/src/pages/Run/Run.tsx @@ -17,7 +17,7 @@ * under the License. */ import { ReactFlowProvider } from "@xyflow/react"; -import { FiCode } from "react-icons/fi"; +import { FiCode, FiDatabase } from "react-icons/fi"; import { MdDetails, MdOutlineEventNote, MdOutlineTask } from "react-icons/md"; import { useParams } from "react-router-dom"; @@ -29,9 +29,10 @@ import { Header } from "./Header"; const tabs = [ { icon: <MdOutlineTask />, label: "Task Instances", value: "" }, - { icon: <MdOutlineEventNote />, label: "Events", value: "events" }, + { icon: <MdOutlineEventNote />, label: "Audit Logs", value: "events" }, { icon: <FiCode />, label: "Code", value: "code" }, { icon: <MdDetails />, label: "Details", value: "details" }, + { icon: <FiDatabase />, label: "Asset Events", value: "asset_events" }, ]; export const Run = () => { diff --git a/airflow-core/src/airflow/ui/src/pages/Task/Task.tsx b/airflow-core/src/airflow/ui/src/pages/Task/Task.tsx index 44da8f8368b..3c772cf32c1 100644 --- a/airflow-core/src/airflow/ui/src/pages/Task/Task.tsx +++ b/airflow-core/src/airflow/ui/src/pages/Task/Task.tsx @@ -29,7 +29,7 @@ import { Header } from "./Header"; const tabs = [ { icon: <LuChartColumn />, label: "Overview", value: "" }, { icon: <MdOutlineTask />, label: "Task Instances", value: "task_instances" }, - { icon: <MdOutlineEventNote />, label: "Events", value: "events" }, + { icon: <MdOutlineEventNote />, label: "Audit Logs", value: "events" }, ]; export const Task = () => { diff --git a/airflow-core/src/airflow/ui/src/pages/TaskInstance/AssetEvents.tsx b/airflow-core/src/airflow/ui/src/pages/TaskInstance/AssetEvents.tsx new file mode 100644 index 00000000000..d5917aa8aab --- /dev/null +++ b/airflow-core/src/airflow/ui/src/pages/TaskInstance/AssetEvents.tsx @@ -0,0 +1,51 @@ +/*! + * 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 } from "react-router-dom"; + +import { useAssetServiceGetAssetEvents, useTaskInstanceServiceGetMappedTaskInstance } from "openapi/queries"; +import { AssetEvents as AssetEventsTable } from "src/components/Assets/AssetEvents"; +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 refetchInterval = useAutoRefresh({ dagId }); + + const { data: assetEventsData, isLoading } = useAssetServiceGetAssetEvents( + { + sourceDagId: dagId, + sourceMapIndex: parseInt(mapIndex, 10), + sourceRunId: runId, + sourceTaskId: taskId, + }, + undefined, + { + refetchInterval: () => (isStatePending(taskInstance?.state) ? refetchInterval : false), + }, + ); + + return <AssetEventsTable data={assetEventsData} isLoading={isLoading} title="Created Asset Event" />; +}; 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 f02b27dbcfc..9fed186008f 100644 --- a/airflow-core/src/airflow/ui/src/pages/TaskInstance/Details.tsx +++ b/airflow-core/src/airflow/ui/src/pages/TaskInstance/Details.tsx @@ -20,11 +20,9 @@ import { Box, Flex, HStack, Table, Heading } from "@chakra-ui/react"; import { useParams, useSearchParams } from "react-router-dom"; import { - useAssetServiceGetAssetEvents, useTaskInstanceServiceGetMappedTaskInstance, useTaskInstanceServiceGetTaskInstanceTryDetails, } from "openapi/queries"; -import { AssetEvents } from "src/components/Assets/AssetEvents"; import { DagVersionDetails } from "src/components/DagVersionDetails"; import { StateBadge } from "src/components/StateBadge"; import { TaskTrySelect } from "src/components/TaskTrySelect"; @@ -76,24 +74,8 @@ export const Details = () => { }, ); - const { data: assetEventsData, isLoading: isLoadingAssetEvents } = useAssetServiceGetAssetEvents( - { - sourceDagId: dagId, - sourceMapIndex: parseInt(mapIndex, 10), - sourceRunId: runId, - sourceTaskId: taskId, - }, - undefined, - { - refetchInterval: () => (isStatePending(taskInstance?.state) ? refetchInterval : false), - }, - ); - return ( <Box p={2}> - {assetEventsData !== undefined && assetEventsData.asset_events.length > 0 ? ( - <AssetEvents data={assetEventsData} isLoading={isLoadingAssetEvents} title="Created Asset Event" /> - ) : undefined} {taskInstance === undefined || tryNumber === undefined || taskInstance.try_number <= 1 ? ( <div /> ) : ( 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 8e19e7dbec2..c11fce76741 100644 --- a/airflow-core/src/airflow/ui/src/pages/TaskInstance/TaskInstance.tsx +++ b/airflow-core/src/airflow/ui/src/pages/TaskInstance/TaskInstance.tsx @@ -17,7 +17,7 @@ * under the License. */ import { ReactFlowProvider } from "@xyflow/react"; -import { FiCode } from "react-icons/fi"; +import { FiCode, FiDatabase } from "react-icons/fi"; import { MdDetails, MdOutlineEventNote, MdOutlineTask, MdReorder, MdSyncAlt } from "react-icons/md"; import { PiBracketsCurlyBold } from "react-icons/pi"; import { useParams } from "react-router-dom"; @@ -36,9 +36,10 @@ const tabs = [ { icon: <MdReorder />, label: "Logs", value: "" }, { icon: <PiBracketsCurlyBold />, label: "Rendered Templates", value: "rendered_templates" }, { icon: <MdSyncAlt />, label: "XCom", value: "xcom" }, - { icon: <MdOutlineEventNote />, label: "Events", value: "events" }, + { icon: <MdOutlineEventNote />, label: "Audit Logs", value: "events" }, { icon: <FiCode />, label: "Code", value: "code" }, { icon: <MdDetails />, label: "Details", value: "details" }, + { icon: <FiDatabase />, label: "Asset Events", value: "asset_events" }, ]; export const TaskInstance = () => { diff --git a/airflow-core/src/airflow/ui/src/router.tsx b/airflow-core/src/airflow/ui/src/router.tsx index 77d55de10bf..30437d251bc 100644 --- a/airflow-core/src/airflow/ui/src/router.tsx +++ b/airflow-core/src/airflow/ui/src/router.tsx @@ -53,7 +53,9 @@ import { Variables } from "src/pages/Variables"; import { XCom } from "src/pages/XCom"; import { Configs } from "./pages/Configs"; +import { AssetEvents as DagRunAssetEvents } from "./pages/Run/AssetEvents"; import { Security } from "./pages/Security"; +import { AssetEvents as TaskInstanceAssetEvents } from "./pages/TaskInstance/AssetEvents"; import { client } from "./queryClient"; const taskInstanceRoutes = [ @@ -64,6 +66,7 @@ const taskInstanceRoutes = [ { element: <TaskInstanceDetails />, path: "details" }, { element: <RenderedTemplates />, path: "rendered_templates" }, { element: <TaskInstances />, path: "task_instances" }, + { element: <TaskInstanceAssetEvents />, path: "asset_events" }, ]; export const routerConfig = [ @@ -156,6 +159,7 @@ export const routerConfig = [ { element: <Events />, path: "events" }, { element: <Code />, path: "code" }, { element: <DagRunDetails />, path: "details" }, + { element: <DagRunAssetEvents />, path: "asset_events" }, ], element: <Run />, path: "dags/:dagId/runs/:runId",