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 4ed8bdd9ac6 Update UI list deadlines endpoint (#62374)
4ed8bdd9ac6 is described below

commit 4ed8bdd9ac6fdd4ce95521c5ea243b504326e760
Author: Pierre Jeambrun <[email protected]>
AuthorDate: Tue Feb 24 11:05:42 2026 +0100

    Update UI list deadlines endpoint (#62374)
    
    * Adjustments to ui list deadlines endpoint
    
    * Update tests
---
 .../api_fastapi/core_api/datamodels/ui/deadline.py | 18 ++++-
 .../api_fastapi/core_api/openapi/_private_ui.yaml  | 61 ++++++++++++++---
 .../api_fastapi/core_api/routes/ui/deadlines.py    | 74 +++++++++++++--------
 .../src/airflow/ui/openapi-gen/queries/common.ts   |  9 ++-
 .../ui/openapi-gen/queries/ensureQueryData.ts      | 16 +++--
 .../src/airflow/ui/openapi-gen/queries/prefetch.ts | 16 +++--
 .../src/airflow/ui/openapi-gen/queries/queries.ts  | 16 +++--
 .../src/airflow/ui/openapi-gen/queries/suspense.ts | 16 +++--
 .../airflow/ui/openapi-gen/requests/schemas.gen.ts | 22 ++++++-
 .../ui/openapi-gen/requests/services.gen.ts        | 16 +++--
 .../airflow/ui/openapi-gen/requests/types.gen.ts   | 24 +++++--
 .../core_api/routes/ui/test_deadlines.py           | 76 ++++++++++++++--------
 12 files changed, 264 insertions(+), 100 deletions(-)

diff --git 
a/airflow-core/src/airflow/api_fastapi/core_api/datamodels/ui/deadline.py 
b/airflow-core/src/airflow/api_fastapi/core_api/datamodels/ui/deadline.py
index 61391885bc4..625d5ae7585 100644
--- a/airflow-core/src/airflow/api_fastapi/core_api/datamodels/ui/deadline.py
+++ b/airflow-core/src/airflow/api_fastapi/core_api/datamodels/ui/deadline.py
@@ -17,18 +17,30 @@
 
 from __future__ import annotations
 
+from collections.abc import Iterable
 from datetime import datetime
 from uuid import UUID
 
+from pydantic import AliasPath, Field
+
 from airflow.api_fastapi.core_api.base import BaseModel
 
 
 class DeadlineResponse(BaseModel):
-    """Deadline data for the DAG run deadlines tab."""
+    """Deadline serializer for responses."""
 
     id: UUID
     deadline_time: datetime
     missed: bool
     created_at: datetime
-    alert_name: str | None = None
-    alert_description: str | None = None
+    alert_name: str | None = 
Field(validation_alias=AliasPath("deadline_alert", "name"), default=None)
+    alert_description: str | None = Field(
+        validation_alias=AliasPath("deadline_alert", "description"), 
default=None
+    )
+
+
+class DealineCollectionResponse(BaseModel):
+    """Deadline Collection serializer for responses."""
+
+    deadlines: Iterable[DeadlineResponse]
+    total_entries: int
diff --git 
a/airflow-core/src/airflow/api_fastapi/core_api/openapi/_private_ui.yaml 
b/airflow-core/src/airflow/api_fastapi/core_api/openapi/_private_ui.yaml
index fd4e56aef64..e6698ad40d1 100644
--- a/airflow-core/src/airflow/api_fastapi/core_api/openapi/_private_ui.yaml
+++ b/airflow-core/src/airflow/api_fastapi/core_api/openapi/_private_ui.yaml
@@ -554,7 +554,7 @@ paths:
       security:
       - OAuth2PasswordBearer: []
       - HTTPBearer: []
-  /ui/deadlines/{dag_id}/{run_id}:
+  /ui/dags/{dag_id}/dagRuns/{dag_run_id}/deadlines:
     get:
       tags:
       - Deadlines
@@ -571,22 +571,51 @@ paths:
         schema:
           type: string
           title: Dag Id
-      - name: run_id
+      - name: dag_run_id
         in: path
         required: true
         schema:
           type: string
-          title: Run Id
+          title: Dag Run Id
+      - name: limit
+        in: query
+        required: false
+        schema:
+          type: integer
+          minimum: 0
+          default: 50
+          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: array
+          items:
+            type: string
+          description: 'Attributes to order by, multi criteria sort is 
supported.
+            Prefix with `-` for descending order. Supported attributes: `id, 
deadline_time,
+            created_at, alert_name`'
+          default:
+          - deadline_time
+          title: Order By
+        description: 'Attributes to order by, multi criteria sort is 
supported. Prefix
+          with `-` for descending order. Supported attributes: `id, 
deadline_time,
+          created_at, alert_name`'
       responses:
         '200':
           description: Successful Response
           content:
             application/json:
               schema:
-                type: array
-                items:
-                  $ref: '#/components/schemas/DeadlineResponse'
-                title: Response Get Dag Run Deadlines
+                $ref: '#/components/schemas/DealineCollectionResponse'
         '404':
           content:
             application/json:
@@ -2081,7 +2110,23 @@ components:
       - missed
       - created_at
       title: DeadlineResponse
-      description: Deadline data for the DAG run deadlines tab.
+      description: Deadline serializer for responses.
+    DealineCollectionResponse:
+      properties:
+        deadlines:
+          items:
+            $ref: '#/components/schemas/DeadlineResponse'
+          type: array
+          title: Deadlines
+        total_entries:
+          type: integer
+          title: Total Entries
+      type: object
+      required:
+      - deadlines
+      - total_entries
+      title: DealineCollectionResponse
+      description: Deadline Collection serializer for responses.
     EdgeResponse:
       properties:
         source_id:
diff --git 
a/airflow-core/src/airflow/api_fastapi/core_api/routes/ui/deadlines.py 
b/airflow-core/src/airflow/api_fastapi/core_api/routes/ui/deadlines.py
index 03aeed13e4f..ffe6ae5ddd2 100644
--- a/airflow-core/src/airflow/api_fastapi/core_api/routes/ui/deadlines.py
+++ b/airflow-core/src/airflow/api_fastapi/core_api/routes/ui/deadlines.py
@@ -17,24 +17,28 @@
 
 from __future__ import annotations
 
+from typing import Annotated
+
 from fastapi import Depends, HTTPException, status
 from sqlalchemy import select
 from sqlalchemy.orm import joinedload
 
 from airflow.api_fastapi.auth.managers.models.resource_details import 
DagAccessEntity
-from airflow.api_fastapi.common.db.common import SessionDep
+from airflow.api_fastapi.common.db.common import SessionDep, paginated_select
+from airflow.api_fastapi.common.parameters import QueryLimit, QueryOffset, 
SortParam
 from airflow.api_fastapi.common.router import AirflowRouter
-from airflow.api_fastapi.core_api.datamodels.ui.deadline import 
DeadlineResponse
+from airflow.api_fastapi.core_api.datamodels.ui.deadline import 
DealineCollectionResponse
 from airflow.api_fastapi.core_api.openapi.exceptions import 
create_openapi_http_exception_doc
 from airflow.api_fastapi.core_api.security import requires_access_dag
 from airflow.models.dagrun import DagRun
 from airflow.models.deadline import Deadline
+from airflow.models.deadline_alert import DeadlineAlert
 
-deadlines_router = AirflowRouter(prefix="/deadlines", tags=["Deadlines"])
+deadlines_router = 
AirflowRouter(prefix="/dags/{dag_id}/dagRuns/{dag_run_id}/deadlines", 
tags=["Deadlines"])
 
 
 @deadlines_router.get(
-    "/{dag_id}/{run_id}",
+    "",
     responses=create_openapi_http_exception_doc(
         [
             status.HTTP_404_NOT_FOUND,
@@ -51,36 +55,50 @@ deadlines_router = AirflowRouter(prefix="/deadlines", 
tags=["Deadlines"])
 )
 def get_dag_run_deadlines(
     dag_id: str,
-    run_id: str,
+    dag_run_id: str,
     session: SessionDep,
-) -> list[DeadlineResponse]:
+    limit: QueryLimit,
+    offset: QueryOffset,
+    order_by: Annotated[
+        SortParam,
+        Depends(
+            SortParam(
+                ["id", "deadline_time", "created_at"],
+                Deadline,
+                to_replace={
+                    "alert_name": DeadlineAlert.name,
+                },
+            ).dynamic_depends(default="deadline_time")
+        ),
+    ],
+) -> DealineCollectionResponse:
     """Get all deadlines for a specific DAG run."""
-    dag_run = session.scalar(select(DagRun).where(DagRun.dag_id == dag_id, 
DagRun.run_id == run_id))
+    dag_run = session.scalar(select(DagRun).where(DagRun.dag_id == dag_id, 
DagRun.run_id == dag_run_id))
+
     if not dag_run:
         raise HTTPException(
             status.HTTP_404_NOT_FOUND,
-            f"No DAG run found for dag_id={dag_id} run_id={run_id}",
+            f"No DAG run found for dag_id={dag_id} dag_run_id={dag_run_id}",
         )
 
-    deadlines = (
-        session.scalars(
-            select(Deadline)
-            .where(Deadline.dagrun_id == dag_run.id)
-            .options(joinedload(Deadline.deadline_alert))
-            .order_by(Deadline.deadline_time.asc())
-        )
-        .unique()
-        .all()
+    query = (
+        select(Deadline)
+        .join(Deadline.dagrun)
+        .outerjoin(Deadline.deadline_alert)
+        .where(Deadline.dagrun_id == dag_run.id)
+        .where(DagRun.dag_id == dag_id)
+        .options(joinedload(Deadline.deadline_alert))
     )
 
-    return [
-        DeadlineResponse(
-            id=d.id,
-            deadline_time=d.deadline_time,
-            missed=d.missed,
-            created_at=d.created_at,
-            alert_name=d.deadline_alert.name if d.deadline_alert else None,
-            alert_description=d.deadline_alert.description if d.deadline_alert 
else None,
-        )
-        for d in deadlines
-    ]
+    deadlines_select, total_entries = paginated_select(
+        statement=query,
+        filters=None,
+        order_by=order_by,
+        offset=offset,
+        limit=limit,
+        session=session,
+    )
+
+    deadlines = session.scalars(deadlines_select)
+
+    return DealineCollectionResponse(deadlines=deadlines, 
total_entries=total_entries)
diff --git a/airflow-core/src/airflow/ui/openapi-gen/queries/common.ts 
b/airflow-core/src/airflow/ui/openapi-gen/queries/common.ts
index 705a35f4cca..4f32c3773cf 100644
--- a/airflow-core/src/airflow/ui/openapi-gen/queries/common.ts
+++ b/airflow-core/src/airflow/ui/openapi-gen/queries/common.ts
@@ -824,10 +824,13 @@ export const UseDashboardServiceDagStatsKeyFn = 
(queryKey?: Array<unknown>) => [
 export type DeadlinesServiceGetDagRunDeadlinesDefaultResponse = 
Awaited<ReturnType<typeof DeadlinesService.getDagRunDeadlines>>;
 export type DeadlinesServiceGetDagRunDeadlinesQueryResult<TData = 
DeadlinesServiceGetDagRunDeadlinesDefaultResponse, TError = unknown> = 
UseQueryResult<TData, TError>;
 export const useDeadlinesServiceGetDagRunDeadlinesKey = 
"DeadlinesServiceGetDagRunDeadlines";
-export const UseDeadlinesServiceGetDagRunDeadlinesKeyFn = ({ dagId, runId }: {
+export const UseDeadlinesServiceGetDagRunDeadlinesKeyFn = ({ dagId, dagRunId, 
limit, offset, orderBy }: {
   dagId: string;
-  runId: string;
-}, queryKey?: Array<unknown>) => [useDeadlinesServiceGetDagRunDeadlinesKey, 
...(queryKey ?? [{ dagId, runId }])];
+  dagRunId: string;
+  limit?: number;
+  offset?: number;
+  orderBy?: string[];
+}, queryKey?: Array<unknown>) => [useDeadlinesServiceGetDagRunDeadlinesKey, 
...(queryKey ?? [{ dagId, dagRunId, limit, offset, orderBy }])];
 export type StructureServiceStructureDataDefaultResponse = 
Awaited<ReturnType<typeof StructureService.structureData>>;
 export type StructureServiceStructureDataQueryResult<TData = 
StructureServiceStructureDataDefaultResponse, TError = unknown> = 
UseQueryResult<TData, TError>;
 export const useStructureServiceStructureDataKey = 
"StructureServiceStructureData";
diff --git a/airflow-core/src/airflow/ui/openapi-gen/queries/ensureQueryData.ts 
b/airflow-core/src/airflow/ui/openapi-gen/queries/ensureQueryData.ts
index a16531feb37..13bd08260c7 100644
--- a/airflow-core/src/airflow/ui/openapi-gen/queries/ensureQueryData.ts
+++ b/airflow-core/src/airflow/ui/openapi-gen/queries/ensureQueryData.ts
@@ -1562,14 +1562,20 @@ export const ensureUseDashboardServiceDagStatsData = 
(queryClient: QueryClient)
 * Get all deadlines for a specific DAG run.
 * @param data The data for the request.
 * @param data.dagId
-* @param data.runId
-* @returns DeadlineResponse Successful Response
+* @param data.dagRunId
+* @param data.limit
+* @param data.offset
+* @param data.orderBy Attributes to order by, multi criteria sort is 
supported. Prefix with `-` for descending order. Supported attributes: `id, 
deadline_time, created_at, alert_name`
+* @returns DealineCollectionResponse Successful Response
 * @throws ApiError
 */
-export const ensureUseDeadlinesServiceGetDagRunDeadlinesData = (queryClient: 
QueryClient, { dagId, runId }: {
+export const ensureUseDeadlinesServiceGetDagRunDeadlinesData = (queryClient: 
QueryClient, { dagId, dagRunId, limit, offset, orderBy }: {
   dagId: string;
-  runId: string;
-}) => queryClient.ensureQueryData({ queryKey: 
Common.UseDeadlinesServiceGetDagRunDeadlinesKeyFn({ dagId, runId }), queryFn: 
() => DeadlinesService.getDagRunDeadlines({ dagId, runId }) });
+  dagRunId: string;
+  limit?: number;
+  offset?: number;
+  orderBy?: string[];
+}) => queryClient.ensureQueryData({ queryKey: 
Common.UseDeadlinesServiceGetDagRunDeadlinesKeyFn({ dagId, dagRunId, limit, 
offset, orderBy }), queryFn: () => DeadlinesService.getDagRunDeadlines({ dagId, 
dagRunId, limit, offset, orderBy }) });
 /**
 * Structure Data
 * Get Structure Data.
diff --git a/airflow-core/src/airflow/ui/openapi-gen/queries/prefetch.ts 
b/airflow-core/src/airflow/ui/openapi-gen/queries/prefetch.ts
index a4b5afb54df..5320a7f8d59 100644
--- a/airflow-core/src/airflow/ui/openapi-gen/queries/prefetch.ts
+++ b/airflow-core/src/airflow/ui/openapi-gen/queries/prefetch.ts
@@ -1562,14 +1562,20 @@ export const prefetchUseDashboardServiceDagStats = 
(queryClient: QueryClient) =>
 * Get all deadlines for a specific DAG run.
 * @param data The data for the request.
 * @param data.dagId
-* @param data.runId
-* @returns DeadlineResponse Successful Response
+* @param data.dagRunId
+* @param data.limit
+* @param data.offset
+* @param data.orderBy Attributes to order by, multi criteria sort is 
supported. Prefix with `-` for descending order. Supported attributes: `id, 
deadline_time, created_at, alert_name`
+* @returns DealineCollectionResponse Successful Response
 * @throws ApiError
 */
-export const prefetchUseDeadlinesServiceGetDagRunDeadlines = (queryClient: 
QueryClient, { dagId, runId }: {
+export const prefetchUseDeadlinesServiceGetDagRunDeadlines = (queryClient: 
QueryClient, { dagId, dagRunId, limit, offset, orderBy }: {
   dagId: string;
-  runId: string;
-}) => queryClient.prefetchQuery({ queryKey: 
Common.UseDeadlinesServiceGetDagRunDeadlinesKeyFn({ dagId, runId }), queryFn: 
() => DeadlinesService.getDagRunDeadlines({ dagId, runId }) });
+  dagRunId: string;
+  limit?: number;
+  offset?: number;
+  orderBy?: string[];
+}) => queryClient.prefetchQuery({ queryKey: 
Common.UseDeadlinesServiceGetDagRunDeadlinesKeyFn({ dagId, dagRunId, limit, 
offset, orderBy }), queryFn: () => DeadlinesService.getDagRunDeadlines({ dagId, 
dagRunId, limit, offset, orderBy }) });
 /**
 * Structure Data
 * Get Structure Data.
diff --git a/airflow-core/src/airflow/ui/openapi-gen/queries/queries.ts 
b/airflow-core/src/airflow/ui/openapi-gen/queries/queries.ts
index 7bafcd6b706..62c9073d5d4 100644
--- a/airflow-core/src/airflow/ui/openapi-gen/queries/queries.ts
+++ b/airflow-core/src/airflow/ui/openapi-gen/queries/queries.ts
@@ -1562,14 +1562,20 @@ export const useDashboardServiceDagStats = <TData = 
Common.DashboardServiceDagSt
 * Get all deadlines for a specific DAG run.
 * @param data The data for the request.
 * @param data.dagId
-* @param data.runId
-* @returns DeadlineResponse Successful Response
+* @param data.dagRunId
+* @param data.limit
+* @param data.offset
+* @param data.orderBy Attributes to order by, multi criteria sort is 
supported. Prefix with `-` for descending order. Supported attributes: `id, 
deadline_time, created_at, alert_name`
+* @returns DealineCollectionResponse Successful Response
 * @throws ApiError
 */
-export const useDeadlinesServiceGetDagRunDeadlines = <TData = 
Common.DeadlinesServiceGetDagRunDeadlinesDefaultResponse, TError = unknown, 
TQueryKey extends Array<unknown> = unknown[]>({ dagId, runId }: {
+export const useDeadlinesServiceGetDagRunDeadlines = <TData = 
Common.DeadlinesServiceGetDagRunDeadlinesDefaultResponse, TError = unknown, 
TQueryKey extends Array<unknown> = unknown[]>({ dagId, dagRunId, limit, offset, 
orderBy }: {
   dagId: string;
-  runId: string;
-}, queryKey?: TQueryKey, options?: Omit<UseQueryOptions<TData, TError>, 
"queryKey" | "queryFn">) => useQuery<TData, TError>({ queryKey: 
Common.UseDeadlinesServiceGetDagRunDeadlinesKeyFn({ dagId, runId }, queryKey), 
queryFn: () => DeadlinesService.getDagRunDeadlines({ dagId, runId }) as TData, 
...options });
+  dagRunId: string;
+  limit?: number;
+  offset?: number;
+  orderBy?: string[];
+}, queryKey?: TQueryKey, options?: Omit<UseQueryOptions<TData, TError>, 
"queryKey" | "queryFn">) => useQuery<TData, TError>({ queryKey: 
Common.UseDeadlinesServiceGetDagRunDeadlinesKeyFn({ dagId, dagRunId, limit, 
offset, orderBy }, queryKey), queryFn: () => 
DeadlinesService.getDagRunDeadlines({ dagId, dagRunId, limit, offset, orderBy 
}) as TData, ...options });
 /**
 * Structure Data
 * Get Structure Data.
diff --git a/airflow-core/src/airflow/ui/openapi-gen/queries/suspense.ts 
b/airflow-core/src/airflow/ui/openapi-gen/queries/suspense.ts
index 867f08383cf..d8058605e46 100644
--- a/airflow-core/src/airflow/ui/openapi-gen/queries/suspense.ts
+++ b/airflow-core/src/airflow/ui/openapi-gen/queries/suspense.ts
@@ -1562,14 +1562,20 @@ export const useDashboardServiceDagStatsSuspense = 
<TData = Common.DashboardServ
 * Get all deadlines for a specific DAG run.
 * @param data The data for the request.
 * @param data.dagId
-* @param data.runId
-* @returns DeadlineResponse Successful Response
+* @param data.dagRunId
+* @param data.limit
+* @param data.offset
+* @param data.orderBy Attributes to order by, multi criteria sort is 
supported. Prefix with `-` for descending order. Supported attributes: `id, 
deadline_time, created_at, alert_name`
+* @returns DealineCollectionResponse Successful Response
 * @throws ApiError
 */
-export const useDeadlinesServiceGetDagRunDeadlinesSuspense = <TData = 
Common.DeadlinesServiceGetDagRunDeadlinesDefaultResponse, TError = unknown, 
TQueryKey extends Array<unknown> = unknown[]>({ dagId, runId }: {
+export const useDeadlinesServiceGetDagRunDeadlinesSuspense = <TData = 
Common.DeadlinesServiceGetDagRunDeadlinesDefaultResponse, TError = unknown, 
TQueryKey extends Array<unknown> = unknown[]>({ dagId, dagRunId, limit, offset, 
orderBy }: {
   dagId: string;
-  runId: string;
-}, queryKey?: TQueryKey, options?: Omit<UseQueryOptions<TData, TError>, 
"queryKey" | "queryFn">) => useSuspenseQuery<TData, TError>({ queryKey: 
Common.UseDeadlinesServiceGetDagRunDeadlinesKeyFn({ dagId, runId }, queryKey), 
queryFn: () => DeadlinesService.getDagRunDeadlines({ dagId, runId }) as TData, 
...options });
+  dagRunId: string;
+  limit?: number;
+  offset?: number;
+  orderBy?: string[];
+}, queryKey?: TQueryKey, options?: Omit<UseQueryOptions<TData, TError>, 
"queryKey" | "queryFn">) => useSuspenseQuery<TData, TError>({ queryKey: 
Common.UseDeadlinesServiceGetDagRunDeadlinesKeyFn({ dagId, dagRunId, limit, 
offset, orderBy }, queryKey), queryFn: () => 
DeadlinesService.getDagRunDeadlines({ dagId, dagRunId, limit, offset, orderBy 
}) as TData, ...options });
 /**
 * Structure Data
 * Get Structure Data.
diff --git a/airflow-core/src/airflow/ui/openapi-gen/requests/schemas.gen.ts 
b/airflow-core/src/airflow/ui/openapi-gen/requests/schemas.gen.ts
index 5a784efaed8..2e1a39ad880 100644
--- a/airflow-core/src/airflow/ui/openapi-gen/requests/schemas.gen.ts
+++ b/airflow-core/src/airflow/ui/openapi-gen/requests/schemas.gen.ts
@@ -7897,7 +7897,27 @@ export const $DeadlineResponse = {
     type: 'object',
     required: ['id', 'deadline_time', 'missed', 'created_at'],
     title: 'DeadlineResponse',
-    description: 'Deadline data for the DAG run deadlines tab.'
+    description: 'Deadline serializer for responses.'
+} as const;
+
+export const $DealineCollectionResponse = {
+    properties: {
+        deadlines: {
+            items: {
+                '$ref': '#/components/schemas/DeadlineResponse'
+            },
+            type: 'array',
+            title: 'Deadlines'
+        },
+        total_entries: {
+            type: 'integer',
+            title: 'Total Entries'
+        }
+    },
+    type: 'object',
+    required: ['deadlines', 'total_entries'],
+    title: 'DealineCollectionResponse',
+    description: 'Deadline Collection serializer for responses.'
 } as const;
 
 export const $EdgeResponse = {
diff --git a/airflow-core/src/airflow/ui/openapi-gen/requests/services.gen.ts 
b/airflow-core/src/airflow/ui/openapi-gen/requests/services.gen.ts
index 99a9f0d0e23..929afa7e5e6 100644
--- a/airflow-core/src/airflow/ui/openapi-gen/requests/services.gen.ts
+++ b/airflow-core/src/airflow/ui/openapi-gen/requests/services.gen.ts
@@ -3972,17 +3972,25 @@ export class DeadlinesService {
      * Get all deadlines for a specific DAG run.
      * @param data The data for the request.
      * @param data.dagId
-     * @param data.runId
-     * @returns DeadlineResponse Successful Response
+     * @param data.dagRunId
+     * @param data.limit
+     * @param data.offset
+     * @param data.orderBy Attributes to order by, multi criteria sort is 
supported. Prefix with `-` for descending order. Supported attributes: `id, 
deadline_time, created_at, alert_name`
+     * @returns DealineCollectionResponse Successful Response
      * @throws ApiError
      */
     public static getDagRunDeadlines(data: GetDagRunDeadlinesData): 
CancelablePromise<GetDagRunDeadlinesResponse> {
         return __request(OpenAPI, {
             method: 'GET',
-            url: '/ui/deadlines/{dag_id}/{run_id}',
+            url: '/ui/dags/{dag_id}/dagRuns/{dag_run_id}/deadlines',
             path: {
                 dag_id: data.dagId,
-                run_id: data.runId
+                dag_run_id: data.dagRunId
+            },
+            query: {
+                limit: data.limit,
+                offset: data.offset,
+                order_by: data.orderBy
             },
             errors: {
                 404: 'Not Found',
diff --git a/airflow-core/src/airflow/ui/openapi-gen/requests/types.gen.ts 
b/airflow-core/src/airflow/ui/openapi-gen/requests/types.gen.ts
index 9c68149385e..a0e7c2e8d77 100644
--- a/airflow-core/src/airflow/ui/openapi-gen/requests/types.gen.ts
+++ b/airflow-core/src/airflow/ui/openapi-gen/requests/types.gen.ts
@@ -1928,7 +1928,7 @@ export type DashboardDagStatsResponse = {
 };
 
 /**
- * Deadline data for the DAG run deadlines tab.
+ * Deadline serializer for responses.
  */
 export type DeadlineResponse = {
     id: string;
@@ -1939,6 +1939,14 @@ export type DeadlineResponse = {
     alert_description?: string | null;
 };
 
+/**
+ * Deadline Collection serializer for responses.
+ */
+export type DealineCollectionResponse = {
+    deadlines: Array<DeadlineResponse>;
+    total_entries: number;
+};
+
 /**
  * Edge serializer for responses.
  */
@@ -3549,10 +3557,16 @@ export type DagStatsResponse2 = 
DashboardDagStatsResponse;
 
 export type GetDagRunDeadlinesData = {
     dagId: string;
-    runId: string;
+    dagRunId: string;
+    limit?: number;
+    offset?: number;
+    /**
+     * Attributes to order by, multi criteria sort is supported. Prefix with 
`-` for descending order. Supported attributes: `id, deadline_time, created_at, 
alert_name`
+     */
+    orderBy?: Array<(string)>;
 };
 
-export type GetDagRunDeadlinesResponse = Array<DeadlineResponse>;
+export type GetDagRunDeadlinesResponse = DealineCollectionResponse;
 
 export type StructureDataData = {
     dagId: string;
@@ -6798,14 +6812,14 @@ export type $OpenApiTs = {
             };
         };
     };
-    '/ui/deadlines/{dag_id}/{run_id}': {
+    '/ui/dags/{dag_id}/dagRuns/{dag_run_id}/deadlines': {
         get: {
             req: GetDagRunDeadlinesData;
             res: {
                 /**
                  * Successful Response
                  */
-                200: Array<DeadlineResponse>;
+                200: DealineCollectionResponse;
                 /**
                  * Not Found
                  */
diff --git 
a/airflow-core/tests/unit/api_fastapi/core_api/routes/ui/test_deadlines.py 
b/airflow-core/tests/unit/api_fastapi/core_api/routes/ui/test_deadlines.py
index 4d3184a6094..4dec594eb64 100644
--- a/airflow-core/tests/unit/api_fastapi/core_api/routes/ui/test_deadlines.py
+++ b/airflow-core/tests/unit/api_fastapi/core_api/routes/ui/test_deadlines.py
@@ -30,6 +30,7 @@ from airflow.sdk.definitions.deadline import DeadlineReference
 from airflow.utils.state import DagRunState
 from airflow.utils.types import DagRunTriggeredByType, DagRunType
 
+from tests_common.test_utils.asserts import assert_queries_count
 from tests_common.test_utils.db import (
     clear_db_dags,
     clear_db_deadline,
@@ -205,63 +206,82 @@ def setup(dag_maker, session):
 
 
 class TestGetDagRunDeadlines:
-    """Tests for GET /deadlines/{dag_id}/{run_id}."""
+    """Tests for GET /dags/{dag_id}/dagRuns/{dag_run_id}/deadlines."""
 
     # ------------------------------------------------------------------
     # 200 – happy paths
     # ------------------------------------------------------------------
 
     def test_no_deadlines_returns_empty_list(self, test_client):
-        response = test_client.get(f"/deadlines/{DAG_ID}/{RUN_EMPTY}")
+        response = 
test_client.get(f"/dags/{DAG_ID}/dagRuns/{RUN_EMPTY}/deadlines")
         assert response.status_code == 200
-        assert response.json() == []
+        assert response.json() == {"deadlines": [], "total_entries": 0}
 
     def test_single_deadline_without_alert(self, test_client):
-        response = test_client.get(f"/deadlines/{DAG_ID}/{RUN_SINGLE}")
+        response = 
test_client.get(f"/dags/{DAG_ID}/dagRuns/{RUN_SINGLE}/deadlines")
         assert response.status_code == 200
         data = response.json()
-        assert len(data) == 1
-        assert data[0]["deadline_time"] == "2025-01-01T12:00:00Z"
-        assert data[0]["missed"] is False
-        assert data[0]["alert_name"] is None
-        assert data[0]["alert_description"] is None
-        assert "id" in data[0]
-        assert "created_at" in data[0]
+        assert data["total_entries"] == 1
+        deadline1 = data["deadlines"][0]
+        assert deadline1["deadline_time"] == "2025-01-01T12:00:00Z"
+        assert deadline1["missed"] is False
+        assert deadline1["alert_name"] is None
+        assert deadline1["alert_description"] is None
+        assert "id" in deadline1
+        assert "created_at" in deadline1
 
     def test_missed_deadline_is_reflected(self, test_client):
-        response = test_client.get(f"/deadlines/{DAG_ID}/{RUN_MISSED}")
+        response = 
test_client.get(f"/dags/{DAG_ID}/dagRuns/{RUN_MISSED}/deadlines")
         assert response.status_code == 200
         data = response.json()
-        assert len(data) == 1
-        assert data[0]["missed"] is True
+        assert data["total_entries"] == 1
+        assert data["deadlines"][0]["missed"] is True
 
     def test_deadline_with_alert_name_and_description(self, test_client):
-        response = test_client.get(f"/deadlines/{DAG_ID}/{RUN_ALERT}")
+        with assert_queries_count(4):
+            response = 
test_client.get(f"/dags/{DAG_ID}/dagRuns/{RUN_ALERT}/deadlines")
         assert response.status_code == 200
         data = response.json()
-        assert len(data) == 1
-        assert data[0]["alert_name"] == ALERT_NAME
-        assert data[0]["alert_description"] == ALERT_DESCRIPTION
+        assert data["total_entries"] == 1
+        assert data["deadlines"][0]["alert_name"] == ALERT_NAME
+        assert data["deadlines"][0]["alert_description"] == ALERT_DESCRIPTION
 
     def test_deadlines_ordered_by_deadline_time_ascending(self, test_client):
-        response = test_client.get(f"/deadlines/{DAG_ID}/{RUN_MULTI}")
+        with assert_queries_count(4):
+            response = 
test_client.get(f"/dags/{DAG_ID}/dagRuns/{RUN_MULTI}/deadlines")
         assert response.status_code == 200
         data = response.json()
-        assert len(data) == 3
-        returned_times = [d["deadline_time"] for d in data]
+        assert data["total_entries"] == 3
+        returned_times = [d["deadline_time"] for d in data["deadlines"]]
         assert returned_times == sorted(returned_times)
 
+    @pytest.mark.parametrize(
+        "order_by",
+        ["deadline_time", "id", "created_at", "alert_name"],
+        ids=["deadline_time", "id", "created_at", "alert_name"],
+    )
+    def test_should_response_200_order_by(self, test_client, order_by):
+        url = f"/dags/{DAG_ID}/dagRuns/{RUN_MULTI}/deadlines"
+        with assert_queries_count(8):
+            response_asc = test_client.get(url, params={"order_by": order_by})
+            response_desc = test_client.get(url, params={"order_by": 
f"-{order_by}"})
+        assert response_asc.status_code == 200
+        assert response_desc.status_code == 200
+        ids_asc = [d["id"] for d in response_asc.json()["deadlines"]]
+        ids_desc = [d["id"] for d in response_desc.json()["deadlines"]]
+        assert ids_desc == list(reversed(ids_asc))
+
     def test_only_returns_deadlines_for_requested_run(self, test_client):
         """Deadlines belonging to a different run must not appear in the 
response."""
         # RUN_EMPTY has no deadlines; RUN_OTHER has one — querying RUN_EMPTY 
must return [].
-        response = test_client.get(f"/deadlines/{DAG_ID}/{RUN_EMPTY}")
+        response = 
test_client.get(f"/dags/{DAG_ID}/dagRuns/{RUN_EMPTY}/deadlines")
         assert response.status_code == 200
-        assert response.json() == []
+        assert response.json() == {"deadlines": [], "total_entries": 0}
 
         # And querying RUN_OTHER returns only its own deadline.
-        response = test_client.get(f"/deadlines/{DAG_ID}/{RUN_OTHER}")
+        response = 
test_client.get(f"/dags/{DAG_ID}/dagRuns/{RUN_OTHER}/deadlines")
         assert response.status_code == 200
-        assert len(response.json()) == 1
+        assert response.json()["total_entries"] == 1
 
     # ------------------------------------------------------------------
     # 404
@@ -276,7 +296,7 @@ class TestGetDagRunDeadlines:
         ],
     )
     def test_should_response_404(self, test_client, dag_id, run_id):
-        response = test_client.get(f"/deadlines/{dag_id}/{run_id}")
+        response = 
test_client.get(f"/dags/{dag_id}/dagRuns/{run_id}/deadlines")
         assert response.status_code == 404
 
     # ------------------------------------------------------------------
@@ -284,9 +304,9 @@ class TestGetDagRunDeadlines:
     # ------------------------------------------------------------------
 
     def test_should_response_401(self, unauthenticated_test_client):
-        response = 
unauthenticated_test_client.get(f"/deadlines/{DAG_ID}/{RUN_EMPTY}")
+        response = 
unauthenticated_test_client.get(f"/dags/{DAG_ID}/dagRuns/{RUN_EMPTY}/deadlines")
         assert response.status_code == 401
 
     def test_should_response_403(self, unauthorized_test_client):
-        response = 
unauthorized_test_client.get(f"/deadlines/{DAG_ID}/{RUN_EMPTY}")
+        response = 
unauthorized_test_client.get(f"/dags/{DAG_ID}/dagRuns/{RUN_EMPTY}/deadlines")
         assert response.status_code == 403

Reply via email to