This is an automated email from the ASF dual-hosted git repository.

ephraimanierobi pushed a commit to branch v2-6-test
in repository https://gitbox.apache.org/repos/asf/airflow.git

commit 4ac3be81d2f636afd7a5a5f4196f8d32555da413
Author: Pierre Jeambrun <[email protected]>
AuthorDate: Fri Apr 14 21:27:40 2023 +0200

    Present affected task instances as table (#30633)
    
    (cherry picked from commit 90ee7639478bc8534164c4bbb05a1ccfb04272dd)
---
 airflow/www/static/js/api/useClearTaskDryRun.ts    | 15 ++++++----
 airflow/www/static/js/api/useMarkTaskDryRun.ts     |  6 ++--
 .../taskInstance/taskActions/ActionModal.tsx       | 32 +++++++++++++++++-----
 airflow/www/static/js/types/index.ts               | 17 +++++++-----
 airflow/www/views.py                               | 19 +++++++++----
 5 files changed, 62 insertions(+), 27 deletions(-)

diff --git a/airflow/www/static/js/api/useClearTaskDryRun.ts 
b/airflow/www/static/js/api/useClearTaskDryRun.ts
index cea46723f4..7f068a001f 100644
--- a/airflow/www/static/js/api/useClearTaskDryRun.ts
+++ b/airflow/www/static/js/api/useClearTaskDryRun.ts
@@ -19,6 +19,7 @@
 
 import axios, { AxiosResponse } from "axios";
 import { useQuery } from "react-query";
+import type { MinimalTaskInstance } from "src/types";
 import URLSearchParamsWrapper from "src/utils/URLSearchParamWrapper";
 import { getMetaValue } from "../utils";
 
@@ -91,11 +92,15 @@ const useClearTaskDryRun = ({
         params.append("map_index", mi.toString());
       });
 
-      return axios.post<AxiosResponse, string[]>(clearUrl, params.toString(), {
-        headers: {
-          "Content-Type": "application/x-www-form-urlencoded",
-        },
-      });
+      return axios.post<AxiosResponse, MinimalTaskInstance[]>(
+        clearUrl,
+        params.toString(),
+        {
+          headers: {
+            "Content-Type": "application/x-www-form-urlencoded",
+          },
+        }
+      );
     }
   );
 
diff --git a/airflow/www/static/js/api/useMarkTaskDryRun.ts 
b/airflow/www/static/js/api/useMarkTaskDryRun.ts
index 8e872ed8ea..31bc278644 100644
--- a/airflow/www/static/js/api/useMarkTaskDryRun.ts
+++ b/airflow/www/static/js/api/useMarkTaskDryRun.ts
@@ -19,7 +19,7 @@
 
 import axios, { AxiosResponse } from "axios";
 import { useQuery } from "react-query";
-import type { TaskState } from "src/types";
+import type { TaskState, MinimalTaskInstance } from "src/types";
 import URLSearchParamsWrapper from "src/utils/URLSearchParamWrapper";
 import { getMetaValue } from "../utils";
 
@@ -74,7 +74,9 @@ const useMarkTaskDryRun = ({
       mapIndexes.forEach((mi: number) => {
         params.append("map_index", mi.toString());
       });
-      return axios.get<AxiosResponse, string[]>(confirmUrl, { params });
+      return axios.get<AxiosResponse, MinimalTaskInstance[]>(confirmUrl, {
+        params,
+      });
     }
   );
 
diff --git 
a/airflow/www/static/js/dag/details/taskInstance/taskActions/ActionModal.tsx 
b/airflow/www/static/js/dag/details/taskInstance/taskActions/ActionModal.tsx
index ef89681150..57128bacd1 100644
--- a/airflow/www/static/js/dag/details/taskInstance/taskActions/ActionModal.tsx
+++ b/airflow/www/static/js/dag/details/taskInstance/taskActions/ActionModal.tsx
@@ -35,18 +35,40 @@ import {
   AccordionPanel,
   AccordionItem,
   AccordionIcon,
-  Code,
 } from "@chakra-ui/react";
 
 import { useContainerRef } from "src/context/containerRef";
+import { Table } from "src/components/Table";
+import type { MinimalTaskInstance } from "src/types";
 
 interface Props extends ModalProps {
-  affectedTasks?: string[];
+  affectedTasks?: MinimalTaskInstance[];
   header: ReactNode | string;
   subheader?: ReactNode | string;
   submitButton: ReactNode;
 }
 
+const columns = [
+  {
+    Header: "Task name",
+    accessor: "taskId",
+  },
+  {
+    Header: "Map Index",
+    accessor: "mapIndex",
+  },
+  {
+    Header: "Run Id",
+    accessor: "runId",
+  },
+];
+
+const AffectedTasksTable = ({
+  affectedTasks,
+}: {
+  affectedTasks: MinimalTaskInstance[];
+}) => <Table data={affectedTasks} columns={columns} />;
+
 const ActionModal = ({
   isOpen,
   onClose,
@@ -87,11 +109,7 @@ const ActionModal = ({
                 </AccordionButton>
                 <AccordionPanel>
                   <Box maxHeight="400px" overflowY="auto">
-                    {(affectedTasks || []).map((ti) => (
-                      <Code width="100%" key={ti} fontSize="lg">
-                        {ti}
-                      </Code>
-                    ))}
+                    <AffectedTasksTable affectedTasks={affectedTasks} />
                   </Box>
                 </AccordionPanel>
               </AccordionItem>
diff --git a/airflow/www/static/js/types/index.ts 
b/airflow/www/static/js/types/index.ts
index 94e0e60526..8f4eb7d450 100644
--- a/airflow/www/static/js/types/index.ts
+++ b/airflow/www/static/js/types/index.ts
@@ -130,16 +130,19 @@ interface DatasetListItem extends API.Dataset {
   totalUpdates: number;
 }
 
+type MinimalTaskInstance = Pick<TaskInstance, "taskId" | "mapIndex" | "runId">;
+
 export type {
+  API,
+  MinimalTaskInstance,
   Dag,
   DagRun,
-  RunState,
-  TaskState,
-  TaskInstance,
-  Task,
-  DepNode,
+  DatasetListItem,
   DepEdge,
-  API,
+  DepNode,
   RunOrdering,
-  DatasetListItem,
+  RunState,
+  Task,
+  TaskInstance,
+  TaskState,
 };
diff --git a/airflow/www/views.py b/airflow/www/views.py
index 62bde217ee..c6d1b612b3 100644
--- a/airflow/www/views.py
+++ b/airflow/www/views.py
@@ -1050,7 +1050,6 @@ class Airflow(AirflowBaseView):
         )
 
         if conf.getboolean("webserver", 
"SHOW_RECENT_STATS_FOR_COMPLETED_RUNS", fallback=True):
-
             last_dag_run = (
                 session.query(DagRun.dag_id, 
sqla.func.max(DagRun.execution_date).label("execution_date"))
                 .join(DagModel, DagModel.dag_id == DagRun.dag_id)
@@ -2122,7 +2121,12 @@ class Airflow(AirflowBaseView):
         if not details:
             return redirect_or_json(origin, "No task instances to clear", 
status="error", status_code=404)
         elif request.headers.get("Accept") == "application/json":
-            return htmlsafe_json_dumps(details, separators=(",", ":"))
+            if confirmed:
+                return htmlsafe_json_dumps(details, separators=(",", ":"))
+            return htmlsafe_json_dumps(
+                [{"task_id": ti.task_id, "map_index": ti.map_index, "run_id": 
ti.run_id} for ti in tis],
+                separators=(",", ":"),
+            )
         return self.render_template(
             "airflow/confirm.html",
             endpoint=None,
@@ -2528,8 +2532,13 @@ class Airflow(AirflowBaseView):
         )
 
         if request.headers.get("Accept") == "application/json":
-            details = [str(t) for t in to_be_altered]
-            return htmlsafe_json_dumps(details, separators=(",", ":"))
+            return htmlsafe_json_dumps(
+                [
+                    {"task_id": ti.task_id, "map_index": ti.map_index, 
"run_id": ti.run_id}
+                    for ti in to_be_altered
+                ],
+                separators=(",", ":"),
+            )
 
         details = "\n".join(str(t) for t in to_be_altered)
 
@@ -4475,7 +4484,6 @@ class ConnectionModelView(AirflowModelView):
                     "warning",
                 )
             else:
-
                 dup_conn = Connection(
                     new_conn_id,
                     selected_conn.conn_type,
@@ -5689,7 +5697,6 @@ class DagDependenciesView(AirflowBaseView):
         )
 
     def _calculate_graph(self):
-
         nodes_dict: dict[str, Any] = {}
         edge_tuples: set[dict[str, str]] = set()
 

Reply via email to