From 7b4564709c242cb32a9f521f07fd216000eb5d3e Mon Sep 17 00:00:00 2001 From: Karthikeyan Singaravelan Date: Tue, 28 May 2024 21:39:58 +0530 Subject: [PATCH 1/4] Add task documentation to details tab in grid view. --- airflow/api_connexion/openapi/v1.yaml | 8 ++ airflow/api_connexion/schemas/task_schema.py | 1 + airflow/www/static/js/api/index.ts | 2 + airflow/www/static/js/api/useTaskDetail.tsx | 34 ++++++++ .../taskInstance/TaskDocumentation.tsx | 83 +++++++++++++++++++ .../js/dag/details/taskInstance/index.tsx | 2 + airflow/www/static/js/types/api-generated.ts | 6 ++ airflow/www/templates/airflow/dag.html | 4 +- 8 files changed, 138 insertions(+), 2 deletions(-) create mode 100644 airflow/www/static/js/api/useTaskDetail.tsx create mode 100644 airflow/www/static/js/dag/details/taskInstance/TaskDocumentation.tsx diff --git a/airflow/api_connexion/openapi/v1.yaml b/airflow/api_connexion/openapi/v1.yaml index 3efff1ebf5c71..63ac5768c33dc 100644 --- a/airflow/api_connexion/openapi/v1.yaml +++ b/airflow/api_connexion/openapi/v1.yaml @@ -4124,6 +4124,14 @@ components: readOnly: true items: type: string + doc_md: + type: string + readOnly: true + nullable: true + description: | + Task documentation. + + *New in version 2.10.0* TaskCollection: type: object diff --git a/airflow/api_connexion/schemas/task_schema.py b/airflow/api_connexion/schemas/task_schema.py index 8d8f1e33f2985..03bf4b59ef2e2 100644 --- a/airflow/api_connexion/schemas/task_schema.py +++ b/airflow/api_connexion/schemas/task_schema.py @@ -65,6 +65,7 @@ class TaskSchema(Schema): downstream_task_ids = fields.List(fields.String(), dump_only=True) params = fields.Method("_get_params", dump_only=True) is_mapped = fields.Method("_get_is_mapped", dump_only=True) + doc_md = fields.String(dump_only=True) @staticmethod def _get_class_reference(obj): diff --git a/airflow/www/static/js/api/index.ts b/airflow/www/static/js/api/index.ts index 96532c3903781..b48fdc2dbadbd 100644 --- a/airflow/www/static/js/api/index.ts +++ b/airflow/www/static/js/api/index.ts @@ -55,6 +55,7 @@ import useEventLogs from "./useEventLogs"; import useCalendarData from "./useCalendarData"; import useCreateDatasetEvent from "./useCreateDatasetEvent"; import useRenderedK8s from "./useRenderedK8s"; +import useTaskDetail from "./useTaskDetail"; axios.interceptors.request.use((config) => { config.paramsSerializer = { @@ -106,4 +107,5 @@ export { useCalendarData, useCreateDatasetEvent, useRenderedK8s, + useTaskDetail, }; diff --git a/airflow/www/static/js/api/useTaskDetail.tsx b/airflow/www/static/js/api/useTaskDetail.tsx new file mode 100644 index 0000000000000..ea9f184b04fb2 --- /dev/null +++ b/airflow/www/static/js/api/useTaskDetail.tsx @@ -0,0 +1,34 @@ +/*! + * 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 axios, { AxiosResponse } from "axios"; +import type { API } from "src/types"; +import { useQuery } from "react-query"; +import { getMetaValue } from "../utils"; + +const taskDetailURI = getMetaValue("task_detail_api"); + +export default function useTaskDetail({ taskId }: { taskId: string }) { + return useQuery(["taskDetails", taskId], async () => { + const url = taskDetailURI.replace("_TASK_ID_", taskId); + + const datum = await axios.get(url); + return datum; + }); +} diff --git a/airflow/www/static/js/dag/details/taskInstance/TaskDocumentation.tsx b/airflow/www/static/js/dag/details/taskInstance/TaskDocumentation.tsx new file mode 100644 index 0000000000000..f0078aec90b1a --- /dev/null +++ b/airflow/www/static/js/dag/details/taskInstance/TaskDocumentation.tsx @@ -0,0 +1,83 @@ +/*! + * 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 React from "react"; +import { + Accordion, + AccordionItem, + AccordionButton, + AccordionPanel, + AccordionIcon, + Alert, + AlertIcon, + Box, + Spinner, + Text, +} from "@chakra-ui/react"; + +import { useTaskDetail } from "src/api"; +import ReactMarkdown from "src/components/ReactMarkdown"; + +interface Props { + taskId: string; +} + +const TaskDocumentation = ({ taskId }: Props) => { + const { data, isLoading, error } = useTaskDetail({ + taskId, + }); + + if (isLoading) { + return ; + } + + if (error) { + return ( + + + An error occurred while fetching task documentation. + + ); + } + + if (data && data.docMd) { + return ( + + + + + + + Task Documentation: + + + + + + {data.docMd} + + + + + ); + } + return null; +}; + +export default TaskDocumentation; diff --git a/airflow/www/static/js/dag/details/taskInstance/index.tsx b/airflow/www/static/js/dag/details/taskInstance/index.tsx index 7dc3727ae4792..f33f3e23adbf0 100644 --- a/airflow/www/static/js/dag/details/taskInstance/index.tsx +++ b/airflow/www/static/js/dag/details/taskInstance/index.tsx @@ -31,6 +31,7 @@ import Details from "./Details"; import DatasetUpdateEvents from "./DatasetUpdateEvents"; import TriggererInfo from "./TriggererInfo"; import TaskFailedDependency from "./TaskFailedDependency"; +import TaskDocumentation from "./TaskDocumentation"; const dagId = getMetaValue("dag_id")!; @@ -90,6 +91,7 @@ const TaskInstance = ({ taskId, runId, mapIndex }: Props) => { operator={operator} /> )} + {!isGroupOrMappedTaskSummary && } {!isGroupOrMappedTaskSummary && ( - - + + From 45edb1f2e7548c638fe0307f350b975054b785e0 Mon Sep 17 00:00:00 2001 From: Karthikeyan Singaravelan Date: Tue, 28 May 2024 23:20:25 +0530 Subject: [PATCH 2/4] Fix tests. --- tests/api_connexion/endpoints/test_task_endpoint.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tests/api_connexion/endpoints/test_task_endpoint.py b/tests/api_connexion/endpoints/test_task_endpoint.py index 454b0db7525d1..64b7f4a8c5b7d 100644 --- a/tests/api_connexion/endpoints/test_task_endpoint.py +++ b/tests/api_connexion/endpoints/test_task_endpoint.py @@ -137,6 +137,7 @@ def test_should_respond_200(self): "wait_for_downstream": False, "weight_rule": "downstream", "is_mapped": False, + "doc_md": None, } response = self.client.get( f"/api/v1/dags/{self.dag_id}/tasks/{self.task_id}", environ_overrides={"REMOTE_USER": "test"} @@ -172,6 +173,7 @@ def test_mapped_task(self): "ui_fgcolor": "#000", "wait_for_downstream": False, "weight_rule": "downstream", + "doc_md": None, } response = self.client.get( f"/api/v1/dags/{self.mapped_dag_id}/tasks/{self.mapped_task_id}", @@ -225,6 +227,7 @@ def test_should_respond_200_serialized(self): "wait_for_downstream": False, "weight_rule": "downstream", "is_mapped": False, + "doc_md": None, } response = self.client.get( f"/api/v1/dags/{self.dag_id}/tasks/{self.task_id}", environ_overrides={"REMOTE_USER": "test"} @@ -301,6 +304,7 @@ def test_should_respond_200(self): "wait_for_downstream": False, "weight_rule": "downstream", "is_mapped": False, + "doc_md": None, }, { "class_ref": { @@ -332,6 +336,7 @@ def test_should_respond_200(self): "wait_for_downstream": False, "weight_rule": "downstream", "is_mapped": False, + "doc_md": None, }, ], "total_entries": 2, @@ -372,6 +377,7 @@ def test_get_tasks_mapped(self): "ui_fgcolor": "#000", "wait_for_downstream": False, "weight_rule": "downstream", + "doc_md": None, }, { "class_ref": { @@ -403,6 +409,7 @@ def test_get_tasks_mapped(self): "wait_for_downstream": False, "weight_rule": "downstream", "is_mapped": False, + "doc_md": None, }, ], "total_entries": 2, From 83425803b26cd9cf6a7781e3db5ec5b2bfe4c947 Mon Sep 17 00:00:00 2001 From: Karthikeyan Singaravelan Date: Tue, 28 May 2024 23:32:35 +0530 Subject: [PATCH 3/4] Fix tests. --- tests/api_connexion/schemas/test_task_schema.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/api_connexion/schemas/test_task_schema.py b/tests/api_connexion/schemas/test_task_schema.py index e7abd814b8605..5748529b864af 100644 --- a/tests/api_connexion/schemas/test_task_schema.py +++ b/tests/api_connexion/schemas/test_task_schema.py @@ -60,6 +60,7 @@ def test_serialize(self): "wait_for_downstream": False, "weight_rule": "downstream", "is_mapped": False, + "doc_md": None, } assert expected == result @@ -108,6 +109,7 @@ def test_serialize(self): "wait_for_downstream": False, "weight_rule": "downstream", "is_mapped": False, + "doc_md": None, } ], "total_entries": 1, From 7755ceb364e4c39ab6b7f10ac888fe171506b1c2 Mon Sep 17 00:00:00 2001 From: Karthikeyan Singaravelan Date: Wed, 29 May 2024 16:25:14 +0530 Subject: [PATCH 4/4] Fix PR comments. --- airflow/api_connexion/openapi/v1.yaml | 2 +- airflow/www/static/js/api/useTaskDetail.tsx | 3 +-- .../static/js/dag/details/taskInstance/TaskDocumentation.tsx | 2 +- airflow/www/static/js/types/api-generated.ts | 2 +- 4 files changed, 4 insertions(+), 5 deletions(-) diff --git a/airflow/api_connexion/openapi/v1.yaml b/airflow/api_connexion/openapi/v1.yaml index 63ac5768c33dc..c9b5a9d80852e 100644 --- a/airflow/api_connexion/openapi/v1.yaml +++ b/airflow/api_connexion/openapi/v1.yaml @@ -4129,7 +4129,7 @@ components: readOnly: true nullable: true description: | - Task documentation. + Task documentation in markdown. *New in version 2.10.0* diff --git a/airflow/www/static/js/api/useTaskDetail.tsx b/airflow/www/static/js/api/useTaskDetail.tsx index ea9f184b04fb2..d7e0ce81c1c43 100644 --- a/airflow/www/static/js/api/useTaskDetail.tsx +++ b/airflow/www/static/js/api/useTaskDetail.tsx @@ -28,7 +28,6 @@ export default function useTaskDetail({ taskId }: { taskId: string }) { return useQuery(["taskDetails", taskId], async () => { const url = taskDetailURI.replace("_TASK_ID_", taskId); - const datum = await axios.get(url); - return datum; + return axios.get(url); }); } diff --git a/airflow/www/static/js/dag/details/taskInstance/TaskDocumentation.tsx b/airflow/www/static/js/dag/details/taskInstance/TaskDocumentation.tsx index f0078aec90b1a..a4ac20b8d1490 100644 --- a/airflow/www/static/js/dag/details/taskInstance/TaskDocumentation.tsx +++ b/airflow/www/static/js/dag/details/taskInstance/TaskDocumentation.tsx @@ -64,7 +64,7 @@ const TaskDocumentation = ({ taskId }: Props) => { - Task Documentation: + Task Documentation diff --git a/airflow/www/static/js/types/api-generated.ts b/airflow/www/static/js/types/api-generated.ts index 6986020175894..a04c1cd0e07b4 100644 --- a/airflow/www/static/js/types/api-generated.ts +++ b/airflow/www/static/js/types/api-generated.ts @@ -1701,7 +1701,7 @@ export interface components { sub_dag?: components["schemas"]["DAG"]; downstream_task_ids?: string[]; /** - * @description Task documentation. + * @description Task documentation in markdown. * * *New in version 2.10.0* */