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

pierrejeambrun 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 5b5f8f4650e AIP-84 De-nest Dag Tags endpoint (#44608)
5b5f8f4650e is described below

commit 5b5f8f4650e52a5a6c7e1ed6fb2cbbca1145d14e
Author: Pierre Jeambrun <[email protected]>
AuthorDate: Wed Dec 4 20:26:16 2024 +0800

    AIP-84 De-nest Dag Tags endpoint (#44608)
    
    * AIP-84 De-nest Tag Tags endpoint
    
    * Fix CI
---
 .../api_fastapi/core_api/datamodels/dag_tags.py    |  27 +++
 airflow/api_fastapi/core_api/datamodels/dags.py    |   7 -
 .../api_fastapi/core_api/openapi/v1-generated.yaml | 128 +++++------
 .../api_fastapi/core_api/routes/public/__init__.py |   2 +
 .../api_fastapi/core_api/routes/public/dag_tags.py |  71 ++++++
 airflow/api_fastapi/core_api/routes/public/dags.py |  38 +---
 airflow/ui/openapi-gen/queries/common.ts           |  50 ++---
 airflow/ui/openapi-gen/queries/prefetch.ts         |  70 +++---
 airflow/ui/openapi-gen/queries/queries.ts          |  88 ++++----
 airflow/ui/openapi-gen/queries/suspense.ts         |  88 ++++----
 airflow/ui/openapi-gen/requests/services.gen.ts    |  66 +++---
 airflow/ui/openapi-gen/requests/types.gen.ts       |  64 +++---
 .../core_api/routes/public/test_dag_tags.py        | 250 +++++++++++++++++++++
 .../core_api/routes/public/test_dags.py            | 114 ----------
 14 files changed, 629 insertions(+), 434 deletions(-)

diff --git a/airflow/api_fastapi/core_api/datamodels/dag_tags.py 
b/airflow/api_fastapi/core_api/datamodels/dag_tags.py
new file mode 100644
index 00000000000..9e67e1ce7b1
--- /dev/null
+++ b/airflow/api_fastapi/core_api/datamodels/dag_tags.py
@@ -0,0 +1,27 @@
+# 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
+
+from pydantic import BaseModel
+
+
+class DAGTagCollectionResponse(BaseModel):
+    """DAG Tags Collection serializer for responses."""
+
+    tags: list[str]
+    total_entries: int
diff --git a/airflow/api_fastapi/core_api/datamodels/dags.py 
b/airflow/api_fastapi/core_api/datamodels/dags.py
index f1dd7bd7980..eddf0e1be22 100644
--- a/airflow/api_fastapi/core_api/datamodels/dags.py
+++ b/airflow/api_fastapi/core_api/datamodels/dags.py
@@ -154,10 +154,3 @@ class DAGDetailsResponse(DAGResponse):
     def concurrency(self) -> int:
         """Return max_active_tasks as concurrency."""
         return self.max_active_tasks
-
-
-class DAGTagCollectionResponse(BaseModel):
-    """DAG Tags Collection serializer for responses."""
-
-    tags: list[str]
-    total_entries: int
diff --git a/airflow/api_fastapi/core_api/openapi/v1-generated.yaml 
b/airflow/api_fastapi/core_api/openapi/v1-generated.yaml
index 30a89a589f9..d6ea8dcec50 100644
--- a/airflow/api_fastapi/core_api/openapi/v1-generated.yaml
+++ b/airflow/api_fastapi/core_api/openapi/v1-generated.yaml
@@ -2636,70 +2636,6 @@ paths:
             application/json:
               schema:
                 $ref: '#/components/schemas/HTTPValidationError'
-  /public/dags/tags:
-    get:
-      tags:
-      - DAG
-      summary: Get Dag Tags
-      description: Get all DAG tags.
-      operationId: get_dag_tags
-      parameters:
-      - name: limit
-        in: query
-        required: false
-        schema:
-          type: integer
-          minimum: 0
-          default: 100
-          title: Limit
-      - name: offset
-        in: query
-        required: false
-        schema:
-          type: integer
-          minimum: 0
-          default: 0
-          title: Offset
-      - name: order_by
-        in: query
-        required: false
-        schema:
-          type: string
-          default: name
-          title: Order By
-      - name: tag_name_pattern
-        in: query
-        required: false
-        schema:
-          anyOf:
-          - type: string
-          - type: 'null'
-          title: Tag Name Pattern
-      responses:
-        '200':
-          description: Successful Response
-          content:
-            application/json:
-              schema:
-                $ref: '#/components/schemas/DAGTagCollectionResponse'
-        '401':
-          content:
-            application/json:
-              schema:
-                $ref: '#/components/schemas/HTTPExceptionResponse'
-          description: Unauthorized
-        '403':
-          content:
-            application/json:
-              schema:
-                $ref: '#/components/schemas/HTTPExceptionResponse'
-          description: Forbidden
-        '422':
-          description: Validation Error
-          content:
-            application/json:
-              schema:
-                $ref: '#/components/schemas/HTTPValidationError'
   /public/dags/{dag_id}:
     get:
       tags:
@@ -5702,6 +5638,70 @@ paths:
             application/json:
               schema:
                 $ref: '#/components/schemas/HTTPValidationError'
+  /public/dagTags:
+    get:
+      tags:
+      - DAG
+      summary: Get Dag Tags
+      description: Get all DAG tags.
+      operationId: get_dag_tags
+      parameters:
+      - name: limit
+        in: query
+        required: false
+        schema:
+          type: integer
+          minimum: 0
+          default: 100
+          title: Limit
+      - name: offset
+        in: query
+        required: false
+        schema:
+          type: integer
+          minimum: 0
+          default: 0
+          title: Offset
+      - name: order_by
+        in: query
+        required: false
+        schema:
+          type: string
+          default: name
+          title: Order By
+      - name: tag_name_pattern
+        in: query
+        required: false
+        schema:
+          anyOf:
+          - type: string
+          - type: 'null'
+          title: Tag Name Pattern
+      responses:
+        '200':
+          description: Successful Response
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/DAGTagCollectionResponse'
+        '401':
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/HTTPExceptionResponse'
+          description: Unauthorized
+        '403':
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/HTTPExceptionResponse'
+          description: Forbidden
+        '422':
+          description: Validation Error
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/HTTPValidationError'
   /public/monitor/health:
     get:
       tags:
diff --git a/airflow/api_fastapi/core_api/routes/public/__init__.py 
b/airflow/api_fastapi/core_api/routes/public/__init__.py
index fd9cfa1a2d3..2a7c81782a9 100644
--- a/airflow/api_fastapi/core_api/routes/public/__init__.py
+++ b/airflow/api_fastapi/core_api/routes/public/__init__.py
@@ -29,6 +29,7 @@ from airflow.api_fastapi.core_api.routes.public.dag_parsing 
import dag_parsing_r
 from airflow.api_fastapi.core_api.routes.public.dag_run import dag_run_router
 from airflow.api_fastapi.core_api.routes.public.dag_sources import 
dag_sources_router
 from airflow.api_fastapi.core_api.routes.public.dag_stats import 
dag_stats_router
+from airflow.api_fastapi.core_api.routes.public.dag_tags import dag_tags_router
 from airflow.api_fastapi.core_api.routes.public.dag_warning import 
dag_warning_router
 from airflow.api_fastapi.core_api.routes.public.dags import dags_router
 from airflow.api_fastapi.core_api.routes.public.event_logs import 
event_logs_router
@@ -75,6 +76,7 @@ authenticated_router.include_router(tasks_router)
 authenticated_router.include_router(variables_router)
 authenticated_router.include_router(task_instances_log_router)
 authenticated_router.include_router(dag_parsing_router)
+authenticated_router.include_router(dag_tags_router)
 
 
 # Include authenticated router in public router
diff --git a/airflow/api_fastapi/core_api/routes/public/dag_tags.py 
b/airflow/api_fastapi/core_api/routes/public/dag_tags.py
new file mode 100644
index 00000000000..73714177d0b
--- /dev/null
+++ b/airflow/api_fastapi/core_api/routes/public/dag_tags.py
@@ -0,0 +1,71 @@
+# 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
+
+from typing import Annotated
+
+from fastapi import Depends
+from sqlalchemy import select
+
+from airflow.api_fastapi.common.db.common import (
+    SessionDep,
+    paginated_select,
+)
+from airflow.api_fastapi.common.parameters import (
+    QueryDagTagPatternSearch,
+    QueryLimit,
+    QueryOffset,
+    SortParam,
+)
+from airflow.api_fastapi.common.router import AirflowRouter
+from airflow.api_fastapi.core_api.datamodels.dag_tags import 
DAGTagCollectionResponse
+from airflow.models.dag import DagTag
+
+dag_tags_router = AirflowRouter(tags=["DAG"], prefix="/dagTags")
+
+
+@dag_tags_router.get(
+    "",
+)
+def get_dag_tags(
+    limit: QueryLimit,
+    offset: QueryOffset,
+    order_by: Annotated[
+        SortParam,
+        Depends(
+            SortParam(
+                ["name"],
+                DagTag,
+            ).dynamic_depends()
+        ),
+    ],
+    tag_name_pattern: QueryDagTagPatternSearch,
+    session: SessionDep,
+) -> DAGTagCollectionResponse:
+    """Get all DAG tags."""
+    query = select(DagTag.name).group_by(DagTag.name)
+    dag_tags_select, total_entries = paginated_select(
+        statement=query,
+        filters=[tag_name_pattern],
+        order_by=order_by,
+        offset=offset,
+        limit=limit,
+        session=session,
+    )
+    dag_tags = session.execute(dag_tags_select).scalars().all()
+    return DAGTagCollectionResponse(tags=[x for x in dag_tags], 
total_entries=total_entries)
diff --git a/airflow/api_fastapi/core_api/routes/public/dags.py 
b/airflow/api_fastapi/core_api/routes/public/dags.py
index 9663c0f8662..395855a0a05 100644
--- a/airflow/api_fastapi/core_api/routes/public/dags.py
+++ b/airflow/api_fastapi/core_api/routes/public/dags.py
@@ -20,7 +20,7 @@ from __future__ import annotations
 from typing import Annotated
 
 from fastapi import Depends, HTTPException, Query, Request, Response, status
-from sqlalchemy import select, update
+from sqlalchemy import update
 
 from airflow.api.common import delete_dag as delete_dag_module
 from airflow.api_fastapi.common.db.common import (
@@ -32,7 +32,6 @@ from airflow.api_fastapi.common.parameters import (
     QueryDagDisplayNamePatternSearch,
     QueryDagIdPatternSearch,
     QueryDagIdPatternSearchWithNone,
-    QueryDagTagPatternSearch,
     QueryLastDagRunStateFilter,
     QueryLimit,
     QueryOffset,
@@ -48,11 +47,10 @@ from airflow.api_fastapi.core_api.datamodels.dags import (
     DAGDetailsResponse,
     DAGPatchBody,
     DAGResponse,
-    DAGTagCollectionResponse,
 )
 from airflow.api_fastapi.core_api.openapi.exceptions import 
create_openapi_http_exception_doc
 from airflow.exceptions import AirflowException, DagNotFound
-from airflow.models import DAG, DagModel, DagTag
+from airflow.models import DAG, DagModel
 from airflow.models.dagrun import DagRun
 
 dags_router = AirflowRouter(tags=["DAG"], prefix="/dags")
@@ -107,38 +105,6 @@ def get_dags(
     )
 
 
-@dags_router.get(
-    "/tags",
-)
-def get_dag_tags(
-    limit: QueryLimit,
-    offset: QueryOffset,
-    order_by: Annotated[
-        SortParam,
-        Depends(
-            SortParam(
-                ["name"],
-                DagTag,
-            ).dynamic_depends()
-        ),
-    ],
-    tag_name_pattern: QueryDagTagPatternSearch,
-    session: SessionDep,
-) -> DAGTagCollectionResponse:
-    """Get all DAG tags."""
-    query = select(DagTag.name).group_by(DagTag.name)
-    dag_tags_select, total_entries = paginated_select(
-        statement=query,
-        filters=[tag_name_pattern],
-        order_by=order_by,
-        offset=offset,
-        limit=limit,
-        session=session,
-    )
-    dag_tags = session.execute(dag_tags_select).scalars().all()
-    return DAGTagCollectionResponse(tags=[x for x in dag_tags], 
total_entries=total_entries)
-
-
 @dags_router.get(
     "/{dag_id}",
     responses=create_openapi_http_exception_doc(
diff --git a/airflow/ui/openapi-gen/queries/common.ts 
b/airflow/ui/openapi-gen/queries/common.ts
index 766c08ae909..c8bc4d06729 100644
--- a/airflow/ui/openapi-gen/queries/common.ts
+++ b/airflow/ui/openapi-gen/queries/common.ts
@@ -656,31 +656,6 @@ export const UseDagServiceGetDagsKeyFn = (
     },
   ]),
 ];
-export type DagServiceGetDagTagsDefaultResponse = Awaited<
-  ReturnType<typeof DagService.getDagTags>
->;
-export type DagServiceGetDagTagsQueryResult<
-  TData = DagServiceGetDagTagsDefaultResponse,
-  TError = unknown,
-> = UseQueryResult<TData, TError>;
-export const useDagServiceGetDagTagsKey = "DagServiceGetDagTags";
-export const UseDagServiceGetDagTagsKeyFn = (
-  {
-    limit,
-    offset,
-    orderBy,
-    tagNamePattern,
-  }: {
-    limit?: number;
-    offset?: number;
-    orderBy?: string;
-    tagNamePattern?: string;
-  } = {},
-  queryKey?: Array<unknown>,
-) => [
-  useDagServiceGetDagTagsKey,
-  ...(queryKey ?? [{ limit, offset, orderBy, tagNamePattern }]),
-];
 export type DagServiceGetDagDefaultResponse = Awaited<
   ReturnType<typeof DagService.getDag>
 >;
@@ -713,6 +688,31 @@ export const UseDagServiceGetDagDetailsKeyFn = (
   },
   queryKey?: Array<unknown>,
 ) => [useDagServiceGetDagDetailsKey, ...(queryKey ?? [{ dagId }])];
+export type DagServiceGetDagTagsDefaultResponse = Awaited<
+  ReturnType<typeof DagService.getDagTags>
+>;
+export type DagServiceGetDagTagsQueryResult<
+  TData = DagServiceGetDagTagsDefaultResponse,
+  TError = unknown,
+> = UseQueryResult<TData, TError>;
+export const useDagServiceGetDagTagsKey = "DagServiceGetDagTags";
+export const UseDagServiceGetDagTagsKeyFn = (
+  {
+    limit,
+    offset,
+    orderBy,
+    tagNamePattern,
+  }: {
+    limit?: number;
+    offset?: number;
+    orderBy?: string;
+    tagNamePattern?: string;
+  } = {},
+  queryKey?: Array<unknown>,
+) => [
+  useDagServiceGetDagTagsKey,
+  ...(queryKey ?? [{ limit, offset, orderBy, tagNamePattern }]),
+];
 export type EventLogServiceGetEventLogDefaultResponse = Awaited<
   ReturnType<typeof EventLogService.getEventLog>
 >;
diff --git a/airflow/ui/openapi-gen/queries/prefetch.ts 
b/airflow/ui/openapi-gen/queries/prefetch.ts
index aa5edc206be..40e7006e8f8 100644
--- a/airflow/ui/openapi-gen/queries/prefetch.ts
+++ b/airflow/ui/openapi-gen/queries/prefetch.ts
@@ -850,41 +850,6 @@ export const prefetchUseDagServiceGetDags = (
         tags,
       }),
   });
-/**
- * Get Dag Tags
- * Get all DAG tags.
- * @param data The data for the request.
- * @param data.limit
- * @param data.offset
- * @param data.orderBy
- * @param data.tagNamePattern
- * @returns DAGTagCollectionResponse Successful Response
- * @throws ApiError
- */
-export const prefetchUseDagServiceGetDagTags = (
-  queryClient: QueryClient,
-  {
-    limit,
-    offset,
-    orderBy,
-    tagNamePattern,
-  }: {
-    limit?: number;
-    offset?: number;
-    orderBy?: string;
-    tagNamePattern?: string;
-  } = {},
-) =>
-  queryClient.prefetchQuery({
-    queryKey: Common.UseDagServiceGetDagTagsKeyFn({
-      limit,
-      offset,
-      orderBy,
-      tagNamePattern,
-    }),
-    queryFn: () =>
-      DagService.getDagTags({ limit, offset, orderBy, tagNamePattern }),
-  });
 /**
  * Get Dag
  * Get basic information about a DAG.
@@ -925,6 +890,41 @@ export const prefetchUseDagServiceGetDagDetails = (
     queryKey: Common.UseDagServiceGetDagDetailsKeyFn({ dagId }),
     queryFn: () => DagService.getDagDetails({ dagId }),
   });
+/**
+ * Get Dag Tags
+ * Get all DAG tags.
+ * @param data The data for the request.
+ * @param data.limit
+ * @param data.offset
+ * @param data.orderBy
+ * @param data.tagNamePattern
+ * @returns DAGTagCollectionResponse Successful Response
+ * @throws ApiError
+ */
+export const prefetchUseDagServiceGetDagTags = (
+  queryClient: QueryClient,
+  {
+    limit,
+    offset,
+    orderBy,
+    tagNamePattern,
+  }: {
+    limit?: number;
+    offset?: number;
+    orderBy?: string;
+    tagNamePattern?: string;
+  } = {},
+) =>
+  queryClient.prefetchQuery({
+    queryKey: Common.UseDagServiceGetDagTagsKeyFn({
+      limit,
+      offset,
+      orderBy,
+      tagNamePattern,
+    }),
+    queryFn: () =>
+      DagService.getDagTags({ limit, offset, orderBy, tagNamePattern }),
+  });
 /**
  * Get Event Log
  * @param data The data for the request.
diff --git a/airflow/ui/openapi-gen/queries/queries.ts 
b/airflow/ui/openapi-gen/queries/queries.ts
index efaaf0002d8..624b9c38be8 100644
--- a/airflow/ui/openapi-gen/queries/queries.ts
+++ b/airflow/ui/openapi-gen/queries/queries.ts
@@ -1046,50 +1046,6 @@ export const useDagServiceGetDags = <
       }) as TData,
     ...options,
   });
-/**
- * Get Dag Tags
- * Get all DAG tags.
- * @param data The data for the request.
- * @param data.limit
- * @param data.offset
- * @param data.orderBy
- * @param data.tagNamePattern
- * @returns DAGTagCollectionResponse Successful Response
- * @throws ApiError
- */
-export const useDagServiceGetDagTags = <
-  TData = Common.DagServiceGetDagTagsDefaultResponse,
-  TError = unknown,
-  TQueryKey extends Array<unknown> = unknown[],
->(
-  {
-    limit,
-    offset,
-    orderBy,
-    tagNamePattern,
-  }: {
-    limit?: number;
-    offset?: number;
-    orderBy?: string;
-    tagNamePattern?: string;
-  } = {},
-  queryKey?: TQueryKey,
-  options?: Omit<UseQueryOptions<TData, TError>, "queryKey" | "queryFn">,
-) =>
-  useQuery<TData, TError>({
-    queryKey: Common.UseDagServiceGetDagTagsKeyFn(
-      { limit, offset, orderBy, tagNamePattern },
-      queryKey,
-    ),
-    queryFn: () =>
-      DagService.getDagTags({
-        limit,
-        offset,
-        orderBy,
-        tagNamePattern,
-      }) as TData,
-    ...options,
-  });
 /**
  * Get Dag
  * Get basic information about a DAG.
@@ -1142,6 +1098,50 @@ export const useDagServiceGetDagDetails = <
     queryFn: () => DagService.getDagDetails({ dagId }) as TData,
     ...options,
   });
+/**
+ * Get Dag Tags
+ * Get all DAG tags.
+ * @param data The data for the request.
+ * @param data.limit
+ * @param data.offset
+ * @param data.orderBy
+ * @param data.tagNamePattern
+ * @returns DAGTagCollectionResponse Successful Response
+ * @throws ApiError
+ */
+export const useDagServiceGetDagTags = <
+  TData = Common.DagServiceGetDagTagsDefaultResponse,
+  TError = unknown,
+  TQueryKey extends Array<unknown> = unknown[],
+>(
+  {
+    limit,
+    offset,
+    orderBy,
+    tagNamePattern,
+  }: {
+    limit?: number;
+    offset?: number;
+    orderBy?: string;
+    tagNamePattern?: string;
+  } = {},
+  queryKey?: TQueryKey,
+  options?: Omit<UseQueryOptions<TData, TError>, "queryKey" | "queryFn">,
+) =>
+  useQuery<TData, TError>({
+    queryKey: Common.UseDagServiceGetDagTagsKeyFn(
+      { limit, offset, orderBy, tagNamePattern },
+      queryKey,
+    ),
+    queryFn: () =>
+      DagService.getDagTags({
+        limit,
+        offset,
+        orderBy,
+        tagNamePattern,
+      }) as TData,
+    ...options,
+  });
 /**
  * Get Event Log
  * @param data The data for the request.
diff --git a/airflow/ui/openapi-gen/queries/suspense.ts 
b/airflow/ui/openapi-gen/queries/suspense.ts
index e1349743f2b..250b2038a8c 100644
--- a/airflow/ui/openapi-gen/queries/suspense.ts
+++ b/airflow/ui/openapi-gen/queries/suspense.ts
@@ -1021,50 +1021,6 @@ export const useDagServiceGetDagsSuspense = <
       }) as TData,
     ...options,
   });
-/**
- * Get Dag Tags
- * Get all DAG tags.
- * @param data The data for the request.
- * @param data.limit
- * @param data.offset
- * @param data.orderBy
- * @param data.tagNamePattern
- * @returns DAGTagCollectionResponse Successful Response
- * @throws ApiError
- */
-export const useDagServiceGetDagTagsSuspense = <
-  TData = Common.DagServiceGetDagTagsDefaultResponse,
-  TError = unknown,
-  TQueryKey extends Array<unknown> = unknown[],
->(
-  {
-    limit,
-    offset,
-    orderBy,
-    tagNamePattern,
-  }: {
-    limit?: number;
-    offset?: number;
-    orderBy?: string;
-    tagNamePattern?: string;
-  } = {},
-  queryKey?: TQueryKey,
-  options?: Omit<UseQueryOptions<TData, TError>, "queryKey" | "queryFn">,
-) =>
-  useSuspenseQuery<TData, TError>({
-    queryKey: Common.UseDagServiceGetDagTagsKeyFn(
-      { limit, offset, orderBy, tagNamePattern },
-      queryKey,
-    ),
-    queryFn: () =>
-      DagService.getDagTags({
-        limit,
-        offset,
-        orderBy,
-        tagNamePattern,
-      }) as TData,
-    ...options,
-  });
 /**
  * Get Dag
  * Get basic information about a DAG.
@@ -1117,6 +1073,50 @@ export const useDagServiceGetDagDetailsSuspense = <
     queryFn: () => DagService.getDagDetails({ dagId }) as TData,
     ...options,
   });
+/**
+ * Get Dag Tags
+ * Get all DAG tags.
+ * @param data The data for the request.
+ * @param data.limit
+ * @param data.offset
+ * @param data.orderBy
+ * @param data.tagNamePattern
+ * @returns DAGTagCollectionResponse Successful Response
+ * @throws ApiError
+ */
+export const useDagServiceGetDagTagsSuspense = <
+  TData = Common.DagServiceGetDagTagsDefaultResponse,
+  TError = unknown,
+  TQueryKey extends Array<unknown> = unknown[],
+>(
+  {
+    limit,
+    offset,
+    orderBy,
+    tagNamePattern,
+  }: {
+    limit?: number;
+    offset?: number;
+    orderBy?: string;
+    tagNamePattern?: string;
+  } = {},
+  queryKey?: TQueryKey,
+  options?: Omit<UseQueryOptions<TData, TError>, "queryKey" | "queryFn">,
+) =>
+  useSuspenseQuery<TData, TError>({
+    queryKey: Common.UseDagServiceGetDagTagsKeyFn(
+      { limit, offset, orderBy, tagNamePattern },
+      queryKey,
+    ),
+    queryFn: () =>
+      DagService.getDagTags({
+        limit,
+        offset,
+        orderBy,
+        tagNamePattern,
+      }) as TData,
+    ...options,
+  });
 /**
  * Get Event Log
  * @param data The data for the request.
diff --git a/airflow/ui/openapi-gen/requests/services.gen.ts 
b/airflow/ui/openapi-gen/requests/services.gen.ts
index b4952122c0e..4bec27ae465 100644
--- a/airflow/ui/openapi-gen/requests/services.gen.ts
+++ b/airflow/ui/openapi-gen/requests/services.gen.ts
@@ -88,8 +88,6 @@ import type {
   GetDagsResponse,
   PatchDagsData,
   PatchDagsResponse,
-  GetDagTagsData,
-  GetDagTagsResponse,
   GetDagData,
   GetDagResponse,
   PatchDagData,
@@ -98,6 +96,8 @@ import type {
   DeleteDagResponse,
   GetDagDetailsData,
   GetDagDetailsResponse,
+  GetDagTagsData,
+  GetDagTagsResponse,
   GetEventLogData,
   GetEventLogResponse,
   GetEventLogsData,
@@ -1511,37 +1511,6 @@ export class DagService {
     });
   }
 
-  /**
-   * Get Dag Tags
-   * Get all DAG tags.
-   * @param data The data for the request.
-   * @param data.limit
-   * @param data.offset
-   * @param data.orderBy
-   * @param data.tagNamePattern
-   * @returns DAGTagCollectionResponse Successful Response
-   * @throws ApiError
-   */
-  public static getDagTags(
-    data: GetDagTagsData = {},
-  ): CancelablePromise<GetDagTagsResponse> {
-    return __request(OpenAPI, {
-      method: "GET",
-      url: "/public/dags/tags",
-      query: {
-        limit: data.limit,
-        offset: data.offset,
-        order_by: data.orderBy,
-        tag_name_pattern: data.tagNamePattern,
-      },
-      errors: {
-        401: "Unauthorized",
-        403: "Forbidden",
-        422: "Validation Error",
-      },
-    });
-  }
-
   /**
    * Get Dag
    * Get basic information about a DAG.
@@ -1654,6 +1623,37 @@ export class DagService {
       },
     });
   }
+
+  /**
+   * Get Dag Tags
+   * Get all DAG tags.
+   * @param data The data for the request.
+   * @param data.limit
+   * @param data.offset
+   * @param data.orderBy
+   * @param data.tagNamePattern
+   * @returns DAGTagCollectionResponse Successful Response
+   * @throws ApiError
+   */
+  public static getDagTags(
+    data: GetDagTagsData = {},
+  ): CancelablePromise<GetDagTagsResponse> {
+    return __request(OpenAPI, {
+      method: "GET",
+      url: "/public/dagTags",
+      query: {
+        limit: data.limit,
+        offset: data.offset,
+        order_by: data.orderBy,
+        tag_name_pattern: data.tagNamePattern,
+      },
+      errors: {
+        401: "Unauthorized",
+        403: "Forbidden",
+        422: "Validation Error",
+      },
+    });
+  }
 }
 
 export class EventLogService {
diff --git a/airflow/ui/openapi-gen/requests/types.gen.ts 
b/airflow/ui/openapi-gen/requests/types.gen.ts
index 9fc97c3adeb..e5aed24a98c 100644
--- a/airflow/ui/openapi-gen/requests/types.gen.ts
+++ b/airflow/ui/openapi-gen/requests/types.gen.ts
@@ -1636,15 +1636,6 @@ export type PatchDagsData = {
 
 export type PatchDagsResponse = DAGCollectionResponse;
 
-export type GetDagTagsData = {
-  limit?: number;
-  offset?: number;
-  orderBy?: string;
-  tagNamePattern?: string | null;
-};
-
-export type GetDagTagsResponse = DAGTagCollectionResponse;
-
 export type GetDagData = {
   dagId: string;
 };
@@ -1671,6 +1662,15 @@ export type GetDagDetailsData = {
 
 export type GetDagDetailsResponse = DAGDetailsResponse;
 
+export type GetDagTagsData = {
+  limit?: number;
+  offset?: number;
+  orderBy?: string;
+  tagNamePattern?: string | null;
+};
+
+export type GetDagTagsResponse = DAGTagCollectionResponse;
+
 export type GetEventLogData = {
   eventLogId: number;
 };
@@ -3188,29 +3188,6 @@ export type $OpenApiTs = {
       };
     };
   };
-  "/public/dags/tags": {
-    get: {
-      req: GetDagTagsData;
-      res: {
-        /**
-         * Successful Response
-         */
-        200: DAGTagCollectionResponse;
-        /**
-         * Unauthorized
-         */
-        401: HTTPExceptionResponse;
-        /**
-         * Forbidden
-         */
-        403: HTTPExceptionResponse;
-        /**
-         * Validation Error
-         */
-        422: HTTPValidationError;
-      };
-    };
-  };
   "/public/dags/{dag_id}": {
     get: {
       req: GetDagData;
@@ -3331,6 +3308,29 @@ export type $OpenApiTs = {
       };
     };
   };
+  "/public/dagTags": {
+    get: {
+      req: GetDagTagsData;
+      res: {
+        /**
+         * Successful Response
+         */
+        200: DAGTagCollectionResponse;
+        /**
+         * Unauthorized
+         */
+        401: HTTPExceptionResponse;
+        /**
+         * Forbidden
+         */
+        403: HTTPExceptionResponse;
+        /**
+         * Validation Error
+         */
+        422: HTTPValidationError;
+      };
+    };
+  };
   "/public/eventLogs/{event_log_id}": {
     get: {
       req: GetEventLogData;
diff --git a/tests/api_fastapi/core_api/routes/public/test_dag_tags.py 
b/tests/api_fastapi/core_api/routes/public/test_dag_tags.py
new file mode 100644
index 00000000000..784bb480c43
--- /dev/null
+++ b/tests/api_fastapi/core_api/routes/public/test_dag_tags.py
@@ -0,0 +1,250 @@
+# 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
+
+from datetime import datetime, timezone
+
+import pendulum
+import pytest
+
+from airflow.models.dag import DagModel, DagTag
+from airflow.models.dagrun import DagRun
+from airflow.operators.empty import EmptyOperator
+from airflow.utils.session import provide_session
+from airflow.utils.state import DagRunState
+from airflow.utils.types import DagRunTriggeredByType, DagRunType
+
+from tests_common.test_utils.db import clear_db_dags, clear_db_runs, 
clear_db_serialized_dags
+
+pytestmark = pytest.mark.db_test
+
+DAG1_ID = "test_dag1"
+DAG1_DISPLAY_NAME = "display1"
+DAG2_ID = "test_dag2"
+DAG2_START_DATE = datetime(2021, 6, 15, tzinfo=timezone.utc)
+DAG3_ID = "test_dag3"
+DAG4_ID = "test_dag4"
+DAG4_DISPLAY_NAME = "display4"
+DAG5_ID = "test_dag5"
+DAG5_DISPLAY_NAME = "display5"
+TASK_ID = "op1"
+UTC_JSON_REPR = "UTC" if pendulum.__version__.startswith("3") else 
"Timezone('UTC')"
+API_PREFIX = "/public/dags"
+
+
+class TestDagEndpoint:
+    """Common class for /public/dags related unit tests."""
+
+    @staticmethod
+    def _clear_db():
+        clear_db_runs()
+        clear_db_dags()
+        clear_db_serialized_dags()
+
+    def _create_deactivated_paused_dag(self, session=None):
+        dag_model = DagModel(
+            dag_id=DAG3_ID,
+            fileloc="/tmp/dag_del_1.py",
+            timetable_summary="2 2 * * *",
+            is_active=False,
+            is_paused=True,
+            owners="test_owner,another_test_owner",
+            next_dagrun=datetime(2021, 1, 1, 12, 0, 0, tzinfo=timezone.utc),
+        )
+
+        dagrun_failed = DagRun(
+            dag_id=DAG3_ID,
+            run_id="run1",
+            logical_date=datetime(2018, 1, 1, 12, 0, 0, tzinfo=timezone.utc),
+            start_date=datetime(2018, 1, 1, 12, 0, 0, tzinfo=timezone.utc),
+            run_type=DagRunType.SCHEDULED,
+            state=DagRunState.FAILED,
+            triggered_by=DagRunTriggeredByType.TEST,
+        )
+
+        dagrun_success = DagRun(
+            dag_id=DAG3_ID,
+            run_id="run2",
+            logical_date=datetime(2019, 1, 1, 12, 0, 0, tzinfo=timezone.utc),
+            start_date=datetime(2019, 1, 1, 12, 0, 0, tzinfo=timezone.utc),
+            run_type=DagRunType.MANUAL,
+            state=DagRunState.SUCCESS,
+            triggered_by=DagRunTriggeredByType.TEST,
+        )
+
+        session.add(dag_model)
+        session.add(dagrun_failed)
+        session.add(dagrun_success)
+
+    def _create_dag_tags(self, session=None):
+        session.add(DagTag(dag_id=DAG1_ID, name="tag_2"))
+        session.add(DagTag(dag_id=DAG2_ID, name="tag_1"))
+        session.add(DagTag(dag_id=DAG3_ID, name="tag_1"))
+
+    @pytest.fixture(autouse=True)
+    @provide_session
+    def setup(self, dag_maker, session=None) -> None:
+        self._clear_db()
+
+        with dag_maker(
+            DAG1_ID,
+            dag_display_name=DAG1_DISPLAY_NAME,
+            schedule=None,
+            start_date=datetime(2018, 6, 15, 0, 0, tzinfo=timezone.utc),
+            doc_md="details",
+            params={"foo": 1},
+            tags=["example"],
+        ):
+            EmptyOperator(task_id=TASK_ID)
+
+        dag_maker.create_dagrun(state=DagRunState.FAILED)
+
+        with dag_maker(
+            DAG2_ID,
+            schedule=None,
+            start_date=DAG2_START_DATE,
+            doc_md="details",
+            params={"foo": 1},
+            max_active_tasks=16,
+            max_active_runs=16,
+        ):
+            EmptyOperator(task_id=TASK_ID)
+
+        self._create_deactivated_paused_dag(session)
+        self._create_dag_tags(session)
+
+        dag_maker.dagbag.sync_to_db()
+        dag_maker.dag_model.has_task_concurrency_limits = True
+        session.merge(dag_maker.dag_model)
+        session.commit()
+
+    def teardown_method(self) -> None:
+        self._clear_db()
+
+
+class TestDagTags(TestDagEndpoint):
+    """Unit tests for Get DAG Tags."""
+
+    @pytest.mark.parametrize(
+        "query_params, expected_status_code, expected_dag_tags, 
expected_total_entries",
+        [
+            # test with offset, limit, and without any tag_name_pattern
+            (
+                {},
+                200,
+                [
+                    "example",
+                    "tag_1",
+                    "tag_2",
+                ],
+                3,
+            ),
+            (
+                {"offset": 1},
+                200,
+                [
+                    "tag_1",
+                    "tag_2",
+                ],
+                3,
+            ),
+            (
+                {"limit": 2},
+                200,
+                [
+                    "example",
+                    "tag_1",
+                ],
+                3,
+            ),
+            (
+                {"offset": 1, "limit": 2},
+                200,
+                [
+                    "tag_1",
+                    "tag_2",
+                ],
+                3,
+            ),
+            # test with tag_name_pattern
+            (
+                {"tag_name_pattern": "invalid"},
+                200,
+                [],
+                0,
+            ),
+            (
+                {"tag_name_pattern": "1"},
+                200,
+                ["tag_1"],
+                1,
+            ),
+            (
+                {"tag_name_pattern": "tag%"},
+                200,
+                ["tag_1", "tag_2"],
+                2,
+            ),
+            # test order_by
+            (
+                {"order_by": "-name"},
+                200,
+                ["tag_2", "tag_1", "example"],
+                3,
+            ),
+            # test all query params
+            (
+                {"tag_name_pattern": "t%", "order_by": "-name", "offset": 1, 
"limit": 1},
+                200,
+                ["tag_1"],
+                2,
+            ),
+            (
+                {"tag_name_pattern": "~", "offset": 1, "limit": 2},
+                200,
+                ["tag_1", "tag_2"],
+                3,
+            ),
+            # test invalid query params
+            (
+                {"order_by": "dag_id"},
+                400,
+                None,
+                None,
+            ),
+            (
+                {"order_by": "-dag_id"},
+                400,
+                None,
+                None,
+            ),
+        ],
+    )
+    def test_get_dag_tags(
+        self, test_client, query_params, expected_status_code, 
expected_dag_tags, expected_total_entries
+    ):
+        response = test_client.get("/public/dagTags", params=query_params)
+        assert response.status_code == expected_status_code
+        if expected_status_code != 200:
+            return
+
+        res_json = response.json()
+        expected = {
+            "tags": expected_dag_tags,
+            "total_entries": expected_total_entries,
+        }
+        assert res_json == expected
diff --git a/tests/api_fastapi/core_api/routes/public/test_dags.py 
b/tests/api_fastapi/core_api/routes/public/test_dags.py
index daa680b42d1..05a49c44dfe 100644
--- a/tests/api_fastapi/core_api/routes/public/test_dags.py
+++ b/tests/api_fastapi/core_api/routes/public/test_dags.py
@@ -387,120 +387,6 @@ class TestGetDag(TestDagEndpoint):
         assert res_json == expected
 
 
-class TestGetDagTags(TestDagEndpoint):
-    """Unit tests for Get DAG Tags."""
-
-    @pytest.mark.parametrize(
-        "query_params, expected_status_code, expected_dag_tags, 
expected_total_entries",
-        [
-            # test with offset, limit, and without any tag_name_pattern
-            (
-                {},
-                200,
-                [
-                    "example",
-                    "tag_1",
-                    "tag_2",
-                ],
-                3,
-            ),
-            (
-                {"offset": 1},
-                200,
-                [
-                    "tag_1",
-                    "tag_2",
-                ],
-                3,
-            ),
-            (
-                {"limit": 2},
-                200,
-                [
-                    "example",
-                    "tag_1",
-                ],
-                3,
-            ),
-            (
-                {"offset": 1, "limit": 2},
-                200,
-                [
-                    "tag_1",
-                    "tag_2",
-                ],
-                3,
-            ),
-            # test with tag_name_pattern
-            (
-                {"tag_name_pattern": "invalid"},
-                200,
-                [],
-                0,
-            ),
-            (
-                {"tag_name_pattern": "1"},
-                200,
-                ["tag_1"],
-                1,
-            ),
-            (
-                {"tag_name_pattern": "tag%"},
-                200,
-                ["tag_1", "tag_2"],
-                2,
-            ),
-            # test order_by
-            (
-                {"order_by": "-name"},
-                200,
-                ["tag_2", "tag_1", "example"],
-                3,
-            ),
-            # test all query params
-            (
-                {"tag_name_pattern": "t%", "order_by": "-name", "offset": 1, 
"limit": 1},
-                200,
-                ["tag_1"],
-                2,
-            ),
-            (
-                {"tag_name_pattern": "~", "offset": 1, "limit": 2},
-                200,
-                ["tag_1", "tag_2"],
-                3,
-            ),
-            # test invalid query params
-            (
-                {"order_by": "dag_id"},
-                400,
-                None,
-                None,
-            ),
-            (
-                {"order_by": "-dag_id"},
-                400,
-                None,
-                None,
-            ),
-        ],
-    )
-    def test_get_dag_tags(
-        self, test_client, query_params, expected_status_code, 
expected_dag_tags, expected_total_entries
-    ):
-        response = test_client.get("/public/dags/tags", params=query_params)
-        assert response.status_code == expected_status_code
-        if expected_status_code != 200:
-            return
-
-        res_json = response.json()
-        expected = {
-            "tags": expected_dag_tags,
-            "total_entries": expected_total_entries,
-        }
-        assert res_json == expected
-
-
 class TestDeleteDAG(TestDagEndpoint):
     """Unit tests for Delete DAG."""
 


Reply via email to