diff --git a/airflow/www/static/js/api/useClearTaskDryRun.ts b/airflow/www/static/js/api/useClearTaskDryRun.ts index cea46723f49cc..7f068a001f65b 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(clearUrl, params.toString(), { - headers: { - "Content-Type": "application/x-www-form-urlencoded", - }, - }); + return axios.post( + 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 8e872ed8eafec..31bc278644ad9 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(confirmUrl, { params }); + return axios.get(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 ef89681150035..57128bacd13aa 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[]; +}) => ; + const ActionModal = ({ isOpen, onClose, @@ -87,11 +109,7 @@ const ActionModal = ({ - {(affectedTasks || []).map((ti) => ( - - {ti} - - ))} + diff --git a/airflow/www/static/js/types/index.ts b/airflow/www/static/js/types/index.ts index 94e0e60526e18..8f4eb7d450db1 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; + 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 cabc8e9e1a35b..a0e9ce33d2708 100644 --- a/airflow/www/views.py +++ b/airflow/www/views.py @@ -1050,7 +1050,6 @@ def task_stats(self, session: Session = NEW_SESSION): ) 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 @@ def _clear_dag_tis( 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 @@ def confirm(self): ) 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 @@ def action_mulduplicate(self, connections, session: Session = NEW_SESSION): "warning", ) else: - dup_conn = Connection( new_conn_id, selected_conn.conn_type, @@ -5683,7 +5691,6 @@ def list(self): ) def _calculate_graph(self): - nodes_dict: dict[str, Any] = {} edge_tuples: set[dict[str, str]] = set()