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 25716a9b729 [v3-0-test] Refresh Dag details page on new run (#51173) 
(#51204)
25716a9b729 is described below

commit 25716a9b729ed742a91ffe2875dccd6389605c32
Author: github-actions[bot] 
<41898282+github-actions[bot]@users.noreply.github.com>
AuthorDate: Mon Jun 2 15:01:12 2025 +0200

    [v3-0-test] Refresh Dag details page on new run (#51173) (#51204)
    
    * refresh dag page on new run
    
    * comments
    (cherry picked from commit 05a7fc2007c369c82b0702ab3e399dbbb6f14f24)
    
    Co-authored-by: Shubham Raj 
<[email protected]>
---
 airflow-core/src/airflow/ui/src/pages/Dag/Dag.tsx  | 21 +++++--
 .../ui/src/queries/useRefreshOnNewDagRuns.ts       | 66 ++++++++++++++++++++++
 2 files changed, 81 insertions(+), 6 deletions(-)

diff --git a/airflow-core/src/airflow/ui/src/pages/Dag/Dag.tsx 
b/airflow-core/src/airflow/ui/src/pages/Dag/Dag.tsx
index 22462c6022c..9d8e3ed403b 100644
--- a/airflow-core/src/airflow/ui/src/pages/Dag/Dag.tsx
+++ b/airflow-core/src/airflow/ui/src/pages/Dag/Dag.tsx
@@ -17,6 +17,7 @@
  * under the License.
  */
 import { ReactFlowProvider } from "@xyflow/react";
+import { useState } from "react";
 import { FiBarChart, FiCode } from "react-icons/fi";
 import { LuChartColumn } from "react-icons/lu";
 import { MdDetails, MdOutlineEventNote } from "react-icons/md";
@@ -27,6 +28,7 @@ import { useDagServiceGetDagDetails, 
useDagsServiceRecentDagRuns } from "openapi
 import type { DAGWithLatestDagRunsResponse } from "openapi/requests/types.gen";
 import { TaskIcon } from "src/assets/TaskIcon";
 import { DetailsLayout } from "src/layouts/Details/DetailsLayout";
+import { useRefreshOnNewDagRuns } from "src/queries/useRefreshOnNewDagRuns";
 import { isStatePending, useAutoRefresh } from "src/utils";
 
 import { Header } from "./Header";
@@ -53,6 +55,7 @@ export const Dag = () => {
   });
 
   const refetchInterval = useAutoRefresh({ dagId });
+  const [hasPendingRuns, setHasPendingRuns] = useState<boolean | 
undefined>(false);
 
   // TODO: replace with with a list dag runs by dag id request
   const {
@@ -61,14 +64,20 @@ export const Dag = () => {
     isLoading: isLoadingRuns,
   } = useDagsServiceRecentDagRuns({ dagIds: [dagId], dagRunsLimit: 1 }, 
undefined, {
     enabled: Boolean(dagId),
-    refetchInterval: (query) =>
-      query.state.data?.dags
-        .find((recentDag) => recentDag.dag_id === dagId)
-        ?.latest_dag_runs.some((run) => isStatePending(run.state))
-        ? refetchInterval
-        : false,
+    refetchInterval: (query) => {
+      setHasPendingRuns(
+        query.state.data?.dags
+          .find((recentDag) => recentDag.dag_id === dagId)
+          ?.latest_dag_runs.some((run) => isStatePending(run.state)),
+      );
+
+      return hasPendingRuns ? refetchInterval : false;
+    },
   });
 
+  // Ensures continuous refresh to detect new runs when there's no pending 
state and new runs are initiated from other page
+  useRefreshOnNewDagRuns(dagId, hasPendingRuns);
+
   let dagWithRuns = runsData?.dags.find((recentDag) => recentDag.dag_id === 
dagId);
 
   if (dagWithRuns === undefined && dag !== undefined) {
diff --git a/airflow-core/src/airflow/ui/src/queries/useRefreshOnNewDagRuns.ts 
b/airflow-core/src/airflow/ui/src/queries/useRefreshOnNewDagRuns.ts
new file mode 100644
index 00000000000..4c792993da5
--- /dev/null
+++ b/airflow-core/src/airflow/ui/src/queries/useRefreshOnNewDagRuns.ts
@@ -0,0 +1,66 @@
+/*!
+ * 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 { useQueryClient } from "@tanstack/react-query";
+import { useEffect, useRef } from "react";
+
+import {
+  useDagRunServiceGetDagRuns,
+  useDagServiceGetDagDetailsKey,
+  UseDagRunServiceGetDagRunsKeyFn,
+  UseDagServiceGetDagDetailsKeyFn,
+  useDagsServiceRecentDagRunsKey,
+  UseGridServiceGridDataKeyFn,
+  UseTaskInstanceServiceGetTaskInstancesKeyFn,
+} from "openapi/queries";
+
+import { useConfig } from "./useConfig";
+
+export const useRefreshOnNewDagRuns = (dagId: string, hasPendingRuns: boolean 
| undefined) => {
+  const queryClient = useQueryClient();
+  const previousDagRunIdRef = useRef<string>();
+  const autoRefreshInterval = useConfig("auto_refresh_interval") as number;
+
+  const { data } = useDagRunServiceGetDagRuns({ dagId, limit: 1, orderBy: 
"-run_after" }, undefined, {
+    enabled: Boolean(dagId) && !hasPendingRuns,
+    refetchInterval: Boolean(autoRefreshInterval) ? autoRefreshInterval * 1000 
: 5000,
+  });
+
+  useEffect(() => {
+    const latestDagRun = data?.dag_runs[0];
+
+    const latestDagRunId = latestDagRun?.dag_run_id;
+
+    if ((latestDagRunId ?? "") && previousDagRunIdRef.current !== 
latestDagRunId) {
+      previousDagRunIdRef.current = latestDagRunId;
+
+      const queryKeys = [
+        [useDagsServiceRecentDagRunsKey],
+        [useDagServiceGetDagDetailsKey],
+        UseDagServiceGetDagDetailsKeyFn({ dagId }, [{ dagId }]),
+        UseDagRunServiceGetDagRunsKeyFn({ dagId }, [{ dagId }]),
+        UseTaskInstanceServiceGetTaskInstancesKeyFn({ dagId, dagRunId: "~" }, 
[{ dagId, dagRunId: "~" }]),
+        UseGridServiceGridDataKeyFn({ dagId }, [{ dagId }]),
+      ];
+
+      queryKeys.forEach((key) => {
+        void queryClient.invalidateQueries({ queryKey: key });
+      });
+    }
+  }, [data, dagId, queryClient]);
+};

Reply via email to