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

bbovenzi pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/airflow.git


The following commit(s) were added to refs/heads/main by this push:
     new d36cf052d6 Use Task Tries rest API and not viewspy endpoint (#41196)
d36cf052d6 is described below

commit d36cf052d6eb3ce6c73fb2a5dbfba3d99d45cd1a
Author: Brent Bovenzi <[email protected]>
AuthorDate: Thu Aug 1 15:08:55 2024 -0400

    Use Task Tries rest API and not viewspy endpoint (#41196)
---
 airflow/www/static/js/api/useTIHistory.ts          | 46 +++++++++++-----------
 airflow/www/static/js/dag/details/gantt/Row.tsx    |  8 ++--
 .../static/js/dag/details/taskInstance/Details.tsx |  8 ++--
 .../dag/details/taskInstance/Logs/index.test.tsx   | 38 ++++++++++--------
 .../js/dag/details/taskInstance/TrySelector.tsx    |  8 ++--
 airflow/www/templates/airflow/dag.html             |  2 +-
 airflow/www/views.py                               | 35 ----------------
 tests/www/views/test_views_ti_history.py           | 44 ---------------------
 8 files changed, 60 insertions(+), 129 deletions(-)

diff --git a/airflow/www/static/js/api/useTIHistory.ts 
b/airflow/www/static/js/api/useTIHistory.ts
index 60e29e351e..0293a39b93 100644
--- a/airflow/www/static/js/api/useTIHistory.ts
+++ b/airflow/www/static/js/api/useTIHistory.ts
@@ -17,48 +17,46 @@
  * under the License.
  */
 
-import axios, { AxiosResponse } from "axios";
-import { useQuery } from "react-query";
+import axios from "axios";
+import { useQuery, UseQueryOptions } from "react-query";
 import { useAutoRefresh } from "src/context/autorefresh";
-import type { TaskInstance } from "src/types/api-generated";
+import type {
+  GetTaskInstanceTriesVariables,
+  TaskInstanceCollection,
+} from "src/types/api-generated";
 
 import { getMetaValue } from "src/utils";
 
-interface Props {
-  dagId: string;
-  runId: string;
-  taskId: string;
+interface Props extends GetTaskInstanceTriesVariables {
   mapIndex?: number;
-  enabled?: boolean;
+  options?: UseQueryOptions<TaskInstanceCollection>;
 }
 
 export default function useTIHistory({
   dagId,
-  runId,
+  dagRunId,
   taskId,
-  mapIndex = -1,
-  enabled,
+  mapIndex,
+  options,
 }: Props) {
   const { isRefreshOn } = useAutoRefresh();
-  return useQuery(
-    ["tiHistory", dagId, runId, taskId, mapIndex],
+  return useQuery<TaskInstanceCollection>(
+    ["tiHistory", dagId, dagRunId, taskId, mapIndex],
     () => {
-      const tiHistoryUrl = getMetaValue("ti_history_url");
+      const tiHistoryUrl = getMetaValue("task_tries_api")
+        .replace("_DAG_ID_", dagId)
+        .replace("_DAG_RUN_ID_", dagRunId)
+        .replace("_TASK_ID_", taskId);
 
-      const params = {
-        dag_id: dagId,
-        run_id: runId,
-        task_id: taskId,
-        map_index: mapIndex,
-      };
+      if (mapIndex && mapIndex > -1) {
+        tiHistoryUrl.replace("/tries", `/${mapIndex}/tries`);
+      }
 
-      return axios.get<AxiosResponse, Partial<TaskInstance>[]>(tiHistoryUrl, {
-        params,
-      });
+      return axios.get(tiHistoryUrl);
     },
     {
-      enabled,
       refetchInterval: isRefreshOn && (autoRefreshInterval || 1) * 1000,
+      ...options,
     }
   );
 }
diff --git a/airflow/www/static/js/dag/details/gantt/Row.tsx 
b/airflow/www/static/js/dag/details/gantt/Row.tsx
index dfb529c381..c93d1d7fde 100644
--- a/airflow/www/static/js/dag/details/gantt/Row.tsx
+++ b/airflow/www/static/js/dag/details/gantt/Row.tsx
@@ -52,8 +52,10 @@ const Row = ({
   const { data: tiHistory } = useTIHistory({
     dagId,
     taskId: task.id || "",
-    runId: runId || "",
-    enabled: !!(instance?.tryNumber && instance?.tryNumber > 1) && !!task.id, 
// Only try to look up task tries if try number > 1
+    dagRunId: runId || "",
+    options: {
+      enabled: !!(instance?.tryNumber && instance?.tryNumber > 1) && 
!!task.id, // Only try to look up task tries if try number > 1
+    },
   });
 
   const isSelected = taskId === instance?.taskId;
@@ -83,7 +85,7 @@ const Row = ({
             ganttEndDate={ganttEndDate}
           />
         )}
-        {(tiHistory || []).map((ti) => (
+        {(tiHistory?.taskInstances || []).map((ti) => (
           <InstanceBar
             key={`${taskId}-${ti.tryNumber}`}
             instance={ti}
diff --git a/airflow/www/static/js/dag/details/taskInstance/Details.tsx 
b/airflow/www/static/js/dag/details/taskInstance/Details.tsx
index dfd2a921d3..d5bb4210d2 100644
--- a/airflow/www/static/js/dag/details/taskInstance/Details.tsx
+++ b/airflow/www/static/js/dag/details/taskInstance/Details.tsx
@@ -55,9 +55,11 @@ const Details = ({ gridInstance, taskInstance, group }: 
Props) => {
   const { data: tiHistory } = useTIHistory({
     dagId,
     taskId: taskId || "",
-    runId: runId || "",
+    dagRunId: runId || "",
     mapIndex: taskInstance?.mapIndex || -1,
-    enabled: !!(finalTryNumber && finalTryNumber > 1) && !!taskId, // Only try 
to look up task tries if try number > 1
+    options: {
+      enabled: !!(finalTryNumber && finalTryNumber > 1) && !!taskId, // Only 
try to look up task tries if try number > 1
+    },
   });
 
   const [selectedTryNumber, setSelectedTryNumber] = useState(
@@ -69,7 +71,7 @@ const Details = ({ gridInstance, taskInstance, group }: 
Props) => {
     if (finalTryNumber) setSelectedTryNumber(finalTryNumber);
   }, [finalTryNumber]);
 
-  const tryInstance = tiHistory?.find(
+  const tryInstance = tiHistory?.taskInstances?.find(
     (ti) => ti.tryNumber === selectedTryNumber
   );
 
diff --git a/airflow/www/static/js/dag/details/taskInstance/Logs/index.test.tsx 
b/airflow/www/static/js/dag/details/taskInstance/Logs/index.test.tsx
index e9c29ed4a8..74db45a7d2 100644
--- a/airflow/www/static/js/dag/details/taskInstance/Logs/index.test.tsx
+++ b/airflow/www/static/js/dag/details/taskInstance/Logs/index.test.tsx
@@ -27,7 +27,10 @@ import * as utils from "src/utils";
 import * as useTaskLogModule from "src/api/useTaskLog";
 import * as useTIHistory from "src/api/useTIHistory";
 import * as useTaskInstance from "src/api/useTaskInstance";
-import type { TaskInstance } from "src/types/api-generated";
+import type {
+  TaskInstance,
+  TaskInstanceCollection,
+} from "src/types/api-generated";
 
 import Logs from "./index";
 
@@ -64,22 +67,25 @@ describe("Test Logs Component.", () => {
   } as UseQueryResult<TaskInstance, unknown>;
 
   const tiHistoryValue = {
-    data: [
-      {
-        tryNumber: 1,
-        startDate: "2024-06-17T01:47:51.724946+00:00",
-        endDate: "2024-06-17T01:50:51.724946+00:00",
-        state: "failed",
-      },
-      {
-        tryNumber: 2,
-        startDate: "2024-06-18T01:47:51.724946+00:00",
-        endDate: "2024-06-18T01:50:51.724946+00:00",
-        state: "failed",
-      },
-    ],
+    data: {
+      taskInstances: [
+        {
+          tryNumber: 1,
+          startDate: "2024-06-17T01:47:51.724946+00:00",
+          endDate: "2024-06-17T01:50:51.724946+00:00",
+          state: "failed",
+        },
+        {
+          tryNumber: 2,
+          startDate: "2024-06-18T01:47:51.724946+00:00",
+          endDate: "2024-06-18T01:50:51.724946+00:00",
+          state: "failed",
+        },
+      ],
+      totalEntries: 2,
+    },
     isSuccess: true,
-  } as UseQueryResult<Partial<TaskInstance>[], unknown>;
+  } as UseQueryResult<TaskInstanceCollection>;
 
   beforeEach(() => {
     useTaskLogMock = jest
diff --git a/airflow/www/static/js/dag/details/taskInstance/TrySelector.tsx 
b/airflow/www/static/js/dag/details/taskInstance/TrySelector.tsx
index 4377773f6b..5fc96803c8 100644
--- a/airflow/www/static/js/dag/details/taskInstance/TrySelector.tsx
+++ b/airflow/www/static/js/dag/details/taskInstance/TrySelector.tsx
@@ -49,9 +49,11 @@ const TrySelector = ({
   const { data: tiHistory } = useTIHistory({
     dagId: dagId || "",
     taskId: taskId || "",
-    runId: dagRunId || "",
+    dagRunId: dagRunId || "",
     mapIndex,
-    enabled: !!(finalTryNumber && finalTryNumber > 1) && !!taskId, // Only try 
to look up task tries if try number > 1
+    options: {
+      enabled: !!(finalTryNumber && finalTryNumber > 1) && !!taskId, // Only 
try to look up task tries if try number > 1
+    },
   });
 
   if (!finalTryNumber || finalTryNumber <= 1) return null;
@@ -59,7 +61,7 @@ const TrySelector = ({
   const logAttemptDropdownLimit = 10;
   const showDropdown = finalTryNumber > logAttemptDropdownLimit;
 
-  const tries = (tiHistory || []).filter(
+  const tries = (tiHistory?.taskInstances || []).filter(
     (t) => t?.startDate !== taskInstance?.startDate
   );
   tries?.push(taskInstance);
diff --git a/airflow/www/templates/airflow/dag.html 
b/airflow/www/templates/airflow/dag.html
index f4174e55f0..d3a7995440 100644
--- a/airflow/www/templates/airflow/dag.html
+++ b/airflow/www/templates/airflow/dag.html
@@ -60,7 +60,7 @@
   <meta name="calendar_data_url" content="{{ url_for('Airflow.calendar_data') 
}}">
   <meta name="next_run_datasets_url" content="{{ 
url_for('Airflow.next_run_datasets', dag_id=dag.dag_id) }}">
   <meta name="grid_url" content="{{ url_for('Airflow.grid', dag_id=dag.dag_id) 
}}">
-  <meta name="ti_history_url" content="{{ url_for('Airflow.ti_history') }}">
+  <meta name="task_tries_api" content="{{ 
url_for('/api/v1.airflow_api_connexion_endpoints_task_instance_endpoint_get_task_instance_tries',
 dag_id='_DAG_ID_', dag_run_id='_DAG_RUN_ID_', task_id='_TASK_ID_') }}">
   <meta name="datasets_url" content="{{ url_for('Airflow.datasets') }}">
   <meta name="grid_url_no_root" content="{{ url_for('Airflow.grid', 
dag_id=dag.dag_id, num_runs=num_runs_arg, base_date=base_date_arg) }}">
   <meta name="graph_url" content="{{ url_for('Airflow.graph', 
dag_id=dag.dag_id, root=root) }}">
diff --git a/airflow/www/views.py b/airflow/www/views.py
index f98702ff6e..cb19dbc494 100644
--- a/airflow/www/views.py
+++ b/airflow/www/views.py
@@ -110,7 +110,6 @@ from airflow.models.dataset import 
DagScheduleDatasetReference, DatasetDagRunQue
 from airflow.models.errors import ParseImportError
 from airflow.models.serialized_dag import SerializedDagModel
 from airflow.models.taskinstance import TaskInstance, TaskInstanceNote
-from airflow.models.taskinstancehistory import TaskInstanceHistory as TIHistory
 from airflow.plugins_manager import PLUGINS_ATTRIBUTES_TO_DUMP
 from airflow.providers_manager import ProvidersManager
 from airflow.security import permissions
@@ -3636,40 +3635,6 @@ class Airflow(AirflowBaseView):
                 {"Content-Type": "application/json; charset=utf-8"},
             )
 
-    @expose("/object/task_instance_history")
-    @provide_session
-    @auth.has_access_dag("GET", DagAccessEntity.TASK_INSTANCE)
-    def ti_history(self, session: Session = NEW_SESSION):
-        dag_id = request.args.get("dag_id")
-        task_id = request.args.get("task_id")
-        run_id = request.args.get("run_id")
-        map_index = request.args.get("map_index", -1, type=int)
-
-        ti_history = (
-            session.query(TIHistory)
-            .filter(
-                TIHistory.dag_id == dag_id,
-                TIHistory.task_id == task_id,
-                TIHistory.run_id == run_id,
-                TIHistory.map_index == map_index,
-            )
-            .order_by(TIHistory.try_number.asc())
-            .all()
-        )
-
-        attrs = TaskInstance.__table__.columns.keys()
-
-        data = [{attr: getattr(ti, attr) for attr in attrs} for ti in 
ti_history]
-
-        for entity in data:
-            entity["dag_run_id"] = entity.pop("run_id")
-            entity["queued_when"] = entity.pop("queued_dttm")
-
-        return (
-            htmlsafe_json_dumps(data, separators=(",", ":"), 
cls=utils_json.WebEncoder),
-            {"Content-Type": "application/json; charset=utf-8"},
-        )
-
     @expose("/robots.txt")
     @action_logging
     def robots(self):
diff --git a/tests/www/views/test_views_ti_history.py 
b/tests/www/views/test_views_ti_history.py
deleted file mode 100644
index cc7a933e94..0000000000
--- a/tests/www/views/test_views_ti_history.py
+++ /dev/null
@@ -1,44 +0,0 @@
-# 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.
-from __future__ import annotations
-
-import pytest
-
-from airflow.models.taskinstancehistory import TaskInstanceHistory
-from tests.test_utils.db import clear_db_runs
-
-pytestmark = pytest.mark.db_test
-
-
-class TestTIHistoryEndpoint:
-    @pytest.fixture(autouse=True)
-    def cleanup(self):
-        clear_db_runs()
-        yield
-        clear_db_runs()
-
-
-class TestGetTIHistory(TestTIHistoryEndpoint):
-    def test_should_respond_200(self, admin_client, create_task_instance, 
session):
-        ti = create_task_instance(dag_id="test_dag", task_id="test_task", 
run_id="test_run_id")
-        TaskInstanceHistory.record_ti(ti)
-        assert session.query(TaskInstanceHistory).count() == 1
-        response = admin_client.get(
-            
"/object/task_instance_history?dag_id=test_dag&task_id=test_task&run_id=test_run_id"
-        )
-        assert response.status_code == 200
-        assert len(response.json) == 1

Reply via email to