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

ferruzzi 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 3130fff7ba1 Add DAG run missed-deadline metadata to Grid API (#62189)
3130fff7ba1 is described below

commit 3130fff7ba1386a49d9d8bdeeef205e3e58e6a86
Author: Richard Wu <[email protected]>
AuthorDate: Fri Feb 20 16:32:49 2026 -0700

    Add DAG run missed-deadline metadata to Grid API (#62189)
    
    * feat: add deadlines API endpoint and has_missed_deadline field for DAG 
runs
    
    - Added DeadlinesService and new /deadlines route for fetching DAG run 
deadlines
    - Added deadline datamodels (DeadlineResponse, DeadlineCollectionResponse)
    - Added has_missed_deadline field to GridDAGRunwithTIDetails
    - Registered deadlines router in UI routes
    - Updated OpenAPI spec and regenerated openapi-gen client files
---
 .../api_fastapi/core_api/datamodels/ui/common.py   |   1 +
 .../api_fastapi/core_api/datamodels/ui/deadline.py |  34 +++
 .../api_fastapi/core_api/openapi/_private_ui.yaml  |  84 ++++++
 .../api_fastapi/core_api/routes/ui/__init__.py     |   2 +
 .../api_fastapi/core_api/routes/ui/deadlines.py    |  86 ++++++
 .../airflow/api_fastapi/core_api/routes/ui/grid.py |  10 +-
 .../src/airflow/ui/openapi-gen/queries/common.ts   |   9 +-
 .../ui/openapi-gen/queries/ensureQueryData.ts      |  15 +-
 .../src/airflow/ui/openapi-gen/queries/prefetch.ts |  15 +-
 .../src/airflow/ui/openapi-gen/queries/queries.ts  |  15 +-
 .../src/airflow/ui/openapi-gen/queries/suspense.ts |  15 +-
 .../airflow/ui/openapi-gen/requests/schemas.gen.ts |  56 +++-
 .../ui/openapi-gen/requests/services.gen.ts        |  29 +-
 .../airflow/ui/openapi-gen/requests/types.gen.ts   |  39 +++
 .../core_api/routes/ui/test_deadlines.py           | 292 +++++++++++++++++++++
 .../api_fastapi/core_api/routes/ui/test_grid.py    |   4 +
 16 files changed, 698 insertions(+), 8 deletions(-)

diff --git 
a/airflow-core/src/airflow/api_fastapi/core_api/datamodels/ui/common.py 
b/airflow-core/src/airflow/api_fastapi/core_api/datamodels/ui/common.py
index a18042b4960..2c5832f3246 100644
--- a/airflow-core/src/airflow/api_fastapi/core_api/datamodels/ui/common.py
+++ b/airflow-core/src/airflow/api_fastapi/core_api/datamodels/ui/common.py
@@ -79,6 +79,7 @@ class GridRunsResponse(BaseModel):
     run_after: datetime
     state: DagRunState | None
     run_type: DagRunType
+    has_missed_deadline: bool
 
     @computed_field
     def duration(self) -> float:
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
new file mode 100644
index 00000000000..61391885bc4
--- /dev/null
+++ b/airflow-core/src/airflow/api_fastapi/core_api/datamodels/ui/deadline.py
@@ -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.
+
+from __future__ import annotations
+
+from datetime import datetime
+from uuid import UUID
+
+from airflow.api_fastapi.core_api.base import BaseModel
+
+
+class DeadlineResponse(BaseModel):
+    """Deadline data for the DAG run deadlines tab."""
+
+    id: UUID
+    deadline_time: datetime
+    missed: bool
+    created_at: datetime
+    alert_name: str | None = None
+    alert_description: str | None = None
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 d047a7ce3be..c3f307afbe7 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
@@ -478,6 +478,51 @@ paths:
       security:
       - OAuth2PasswordBearer: []
       - HTTPBearer: []
+  /ui/deadlines/{dag_id}/{run_id}:
+    get:
+      tags:
+      - Deadlines
+      summary: Get Dag Run Deadlines
+      description: Get all deadlines for a specific DAG run.
+      operationId: get_dag_run_deadlines
+      security:
+      - OAuth2PasswordBearer: []
+      - HTTPBearer: []
+      parameters:
+      - name: dag_id
+        in: path
+        required: true
+        schema:
+          type: string
+          title: Dag Id
+      - name: run_id
+        in: path
+        required: true
+        schema:
+          type: string
+          title: Run Id
+      responses:
+        '200':
+          description: Successful Response
+          content:
+            application/json:
+              schema:
+                type: array
+                items:
+                  $ref: '#/components/schemas/DeadlineResponse'
+                title: Response Get Dag Run Deadlines
+        '404':
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/HTTPExceptionResponse'
+          description: Not Found
+        '422':
+          description: Validation Error
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/HTTPValidationError'
   /ui/structure/structure_data:
     get:
       tags:
@@ -1925,6 +1970,41 @@ components:
       - queued_dag_count
       title: DashboardDagStatsResponse
       description: Dashboard DAG Stats serializer for responses.
+    DeadlineResponse:
+      properties:
+        id:
+          type: string
+          format: uuid
+          title: Id
+        deadline_time:
+          type: string
+          format: date-time
+          title: Deadline Time
+        missed:
+          type: boolean
+          title: Missed
+        created_at:
+          type: string
+          format: date-time
+          title: Created At
+        alert_name:
+          anyOf:
+          - type: string
+          - type: 'null'
+          title: Alert Name
+        alert_description:
+          anyOf:
+          - type: string
+          - type: 'null'
+          title: Alert Description
+      type: object
+      required:
+      - id
+      - deadline_time
+      - missed
+      - created_at
+      title: DeadlineResponse
+      description: Deadline data for the DAG run deadlines tab.
     EdgeResponse:
       properties:
         source_id:
@@ -2103,6 +2183,9 @@ components:
           - type: 'null'
         run_type:
           $ref: '#/components/schemas/DagRunType'
+        has_missed_deadline:
+          type: boolean
+          title: Has Missed Deadline
         duration:
           type: number
           title: Duration
@@ -2117,6 +2200,7 @@ components:
       - run_after
       - state
       - run_type
+      - has_missed_deadline
       - duration
       title: GridRunsResponse
       description: Base Node serializer for responses.
diff --git 
a/airflow-core/src/airflow/api_fastapi/core_api/routes/ui/__init__.py 
b/airflow-core/src/airflow/api_fastapi/core_api/routes/ui/__init__.py
index 3a3cadc46f9..ce8744785cd 100644
--- a/airflow-core/src/airflow/api_fastapi/core_api/routes/ui/__init__.py
+++ b/airflow-core/src/airflow/api_fastapi/core_api/routes/ui/__init__.py
@@ -25,6 +25,7 @@ from airflow.api_fastapi.core_api.routes.ui.config import 
config_router
 from airflow.api_fastapi.core_api.routes.ui.connections import 
connections_router
 from airflow.api_fastapi.core_api.routes.ui.dags import dags_router
 from airflow.api_fastapi.core_api.routes.ui.dashboard import dashboard_router
+from airflow.api_fastapi.core_api.routes.ui.deadlines import deadlines_router
 from airflow.api_fastapi.core_api.routes.ui.dependencies import 
dependencies_router
 from airflow.api_fastapi.core_api.routes.ui.gantt import gantt_router
 from airflow.api_fastapi.core_api.routes.ui.grid import grid_router
@@ -40,6 +41,7 @@ ui_router.include_router(connections_router)
 ui_router.include_router(dags_router)
 ui_router.include_router(dependencies_router)
 ui_router.include_router(dashboard_router)
+ui_router.include_router(deadlines_router)
 ui_router.include_router(structure_router)
 ui_router.include_router(backfills_router)
 ui_router.include_router(grid_router)
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
new file mode 100644
index 00000000000..03aeed13e4f
--- /dev/null
+++ b/airflow-core/src/airflow/api_fastapi/core_api/routes/ui/deadlines.py
@@ -0,0 +1,86 @@
+# 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 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.router import AirflowRouter
+from airflow.api_fastapi.core_api.datamodels.ui.deadline import 
DeadlineResponse
+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
+
+deadlines_router = AirflowRouter(prefix="/deadlines", tags=["Deadlines"])
+
+
+@deadlines_router.get(
+    "/{dag_id}/{run_id}",
+    responses=create_openapi_http_exception_doc(
+        [
+            status.HTTP_404_NOT_FOUND,
+        ]
+    ),
+    dependencies=[
+        Depends(
+            requires_access_dag(
+                method="GET",
+                access_entity=DagAccessEntity.RUN,
+            )
+        ),
+    ],
+)
+def get_dag_run_deadlines(
+    dag_id: str,
+    run_id: str,
+    session: SessionDep,
+) -> list[DeadlineResponse]:
+    """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))
+    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}",
+        )
+
+    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()
+    )
+
+    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
+    ]
diff --git a/airflow-core/src/airflow/api_fastapi/core_api/routes/ui/grid.py 
b/airflow-core/src/airflow/api_fastapi/core_api/routes/ui/grid.py
index 9c7436ba462..7762656415e 100644
--- a/airflow-core/src/airflow/api_fastapi/core_api/routes/ui/grid.py
+++ b/airflow-core/src/airflow/api_fastapi/core_api/routes/ui/grid.py
@@ -22,7 +22,7 @@ from typing import TYPE_CHECKING, Annotated, Any
 
 import structlog
 from fastapi import Depends, HTTPException, status
-from sqlalchemy import select
+from sqlalchemy import exists, select
 from sqlalchemy.orm import joinedload
 
 from airflow.api_fastapi.auth.managers.models.resource_details import 
DagAccessEntity
@@ -60,6 +60,7 @@ from airflow.api_fastapi.core_api.services.ui.task_group 
import (
 )
 from airflow.models.dag_version import DagVersion
 from airflow.models.dagrun import DagRun
+from airflow.models.deadline import Deadline
 from airflow.models.serialized_dag import SerializedDagModel
 from airflow.models.taskinstance import TaskInstance
 
@@ -275,6 +276,12 @@ def get_grid_runs(
 ) -> list[GridRunsResponse]:
     """Get info about a run for the grid."""
     # Retrieve, sort the previous DAG Runs
+    has_missed_deadline = (
+        exists()
+        .where(Deadline.dagrun_id == DagRun.id, Deadline.missed.is_(True))
+        .correlate(DagRun)
+        .label("has_missed_deadline")
+    )
     base_query = select(
         DagRun.dag_id,
         DagRun.run_id,
@@ -284,6 +291,7 @@ def get_grid_runs(
         DagRun.run_after,
         DagRun.state,
         DagRun.run_type,
+        has_missed_deadline,
     ).where(DagRun.dag_id == dag_id)
 
     # This comparison is to fall back to DAG timetable when no order_by is 
provided
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 823c49cdb1a..f660990ec61 100644
--- a/airflow-core/src/airflow/ui/openapi-gen/queries/common.ts
+++ b/airflow-core/src/airflow/ui/openapi-gen/queries/common.ts
@@ -1,7 +1,7 @@
 // generated with @7nohe/[email protected] 
 
 import { UseQueryResult } from "@tanstack/react-query";
-import { AssetService, AuthLinksService, BackfillService, CalendarService, 
ConfigService, ConnectionService, DagParsingService, DagRunService, DagService, 
DagSourceService, DagStatsService, DagVersionService, DagWarningService, 
DashboardService, DependenciesService, EventLogService, ExperimentalService, 
ExtraLinksService, GanttService, GridService, ImportErrorService, JobService, 
LoginService, MonitorService, PluginService, PoolService, ProviderService, 
StructureService, TaskInstanceServ [...]
+import { AssetService, AuthLinksService, BackfillService, CalendarService, 
ConfigService, ConnectionService, DagParsingService, DagRunService, DagService, 
DagSourceService, DagStatsService, DagVersionService, DagWarningService, 
DashboardService, DeadlinesService, DependenciesService, EventLogService, 
ExperimentalService, ExtraLinksService, GanttService, GridService, 
ImportErrorService, JobService, LoginService, MonitorService, PluginService, 
PoolService, ProviderService, StructureService [...]
 import { DagRunState, DagWarningType } from "../requests/types.gen";
 export type AssetServiceGetAssetsDefaultResponse = Awaited<ReturnType<typeof 
AssetService.getAssets>>;
 export type AssetServiceGetAssetsQueryResult<TData = 
AssetServiceGetAssetsDefaultResponse, TError = unknown> = UseQueryResult<TData, 
TError>;
@@ -807,6 +807,13 @@ export type DashboardServiceDagStatsDefaultResponse = 
Awaited<ReturnType<typeof
 export type DashboardServiceDagStatsQueryResult<TData = 
DashboardServiceDagStatsDefaultResponse, TError = unknown> = 
UseQueryResult<TData, TError>;
 export const useDashboardServiceDagStatsKey = "DashboardServiceDagStats";
 export const UseDashboardServiceDagStatsKeyFn = (queryKey?: Array<unknown>) => 
[useDashboardServiceDagStatsKey, ...(queryKey ?? [])];
+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 }: {
+  dagId: string;
+  runId: string;
+}, queryKey?: Array<unknown>) => [useDeadlinesServiceGetDagRunDeadlinesKey, 
...(queryKey ?? [{ dagId, runId }])];
 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 459b6fb4d6e..20d67fcd1aa 100644
--- a/airflow-core/src/airflow/ui/openapi-gen/queries/ensureQueryData.ts
+++ b/airflow-core/src/airflow/ui/openapi-gen/queries/ensureQueryData.ts
@@ -1,7 +1,7 @@
 // generated with @7nohe/[email protected] 
 
 import { type QueryClient } from "@tanstack/react-query";
-import { AssetService, AuthLinksService, BackfillService, CalendarService, 
ConfigService, ConnectionService, DagRunService, DagService, DagSourceService, 
DagStatsService, DagVersionService, DagWarningService, DashboardService, 
DependenciesService, EventLogService, ExperimentalService, ExtraLinksService, 
GanttService, GridService, ImportErrorService, JobService, LoginService, 
MonitorService, PluginService, PoolService, ProviderService, StructureService, 
TaskInstanceService, TaskService, T [...]
+import { AssetService, AuthLinksService, BackfillService, CalendarService, 
ConfigService, ConnectionService, DagRunService, DagService, DagSourceService, 
DagStatsService, DagVersionService, DagWarningService, DashboardService, 
DeadlinesService, DependenciesService, EventLogService, ExperimentalService, 
ExtraLinksService, GanttService, GridService, ImportErrorService, JobService, 
LoginService, MonitorService, PluginService, PoolService, ProviderService, 
StructureService, TaskInstanceServi [...]
 import { DagRunState, DagWarningType } from "../requests/types.gen";
 import * as Common from "./common";
 /**
@@ -1532,6 +1532,19 @@ export const 
ensureUseDashboardServiceHistoricalMetricsData = (queryClient: Quer
 */
 export const ensureUseDashboardServiceDagStatsData = (queryClient: 
QueryClient) => queryClient.ensureQueryData({ queryKey: 
Common.UseDashboardServiceDagStatsKeyFn(), queryFn: () => 
DashboardService.dagStats() });
 /**
+* Get Dag Run Deadlines
+* 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
+* @throws ApiError
+*/
+export const ensureUseDeadlinesServiceGetDagRunDeadlinesData = (queryClient: 
QueryClient, { dagId, runId }: {
+  dagId: string;
+  runId: string;
+}) => queryClient.ensureQueryData({ queryKey: 
Common.UseDeadlinesServiceGetDagRunDeadlinesKeyFn({ dagId, runId }), queryFn: 
() => DeadlinesService.getDagRunDeadlines({ dagId, runId }) });
+/**
 * Structure Data
 * Get Structure Data.
 * @param data The data for the request.
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 72772c79656..bed863f93cd 100644
--- a/airflow-core/src/airflow/ui/openapi-gen/queries/prefetch.ts
+++ b/airflow-core/src/airflow/ui/openapi-gen/queries/prefetch.ts
@@ -1,7 +1,7 @@
 // generated with @7nohe/[email protected] 
 
 import { type QueryClient } from "@tanstack/react-query";
-import { AssetService, AuthLinksService, BackfillService, CalendarService, 
ConfigService, ConnectionService, DagRunService, DagService, DagSourceService, 
DagStatsService, DagVersionService, DagWarningService, DashboardService, 
DependenciesService, EventLogService, ExperimentalService, ExtraLinksService, 
GanttService, GridService, ImportErrorService, JobService, LoginService, 
MonitorService, PluginService, PoolService, ProviderService, StructureService, 
TaskInstanceService, TaskService, T [...]
+import { AssetService, AuthLinksService, BackfillService, CalendarService, 
ConfigService, ConnectionService, DagRunService, DagService, DagSourceService, 
DagStatsService, DagVersionService, DagWarningService, DashboardService, 
DeadlinesService, DependenciesService, EventLogService, ExperimentalService, 
ExtraLinksService, GanttService, GridService, ImportErrorService, JobService, 
LoginService, MonitorService, PluginService, PoolService, ProviderService, 
StructureService, TaskInstanceServi [...]
 import { DagRunState, DagWarningType } from "../requests/types.gen";
 import * as Common from "./common";
 /**
@@ -1532,6 +1532,19 @@ export const 
prefetchUseDashboardServiceHistoricalMetrics = (queryClient: QueryC
 */
 export const prefetchUseDashboardServiceDagStats = (queryClient: QueryClient) 
=> queryClient.prefetchQuery({ queryKey: 
Common.UseDashboardServiceDagStatsKeyFn(), queryFn: () => 
DashboardService.dagStats() });
 /**
+* Get Dag Run Deadlines
+* 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
+* @throws ApiError
+*/
+export const prefetchUseDeadlinesServiceGetDagRunDeadlines = (queryClient: 
QueryClient, { dagId, runId }: {
+  dagId: string;
+  runId: string;
+}) => queryClient.prefetchQuery({ queryKey: 
Common.UseDeadlinesServiceGetDagRunDeadlinesKeyFn({ dagId, runId }), queryFn: 
() => DeadlinesService.getDagRunDeadlines({ dagId, runId }) });
+/**
 * Structure Data
 * Get Structure Data.
 * @param data The data for the request.
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 717486d0ece..434f0af6dcd 100644
--- a/airflow-core/src/airflow/ui/openapi-gen/queries/queries.ts
+++ b/airflow-core/src/airflow/ui/openapi-gen/queries/queries.ts
@@ -1,7 +1,7 @@
 // generated with @7nohe/[email protected] 
 
 import { UseMutationOptions, UseQueryOptions, useMutation, useQuery } from 
"@tanstack/react-query";
-import { AssetService, AuthLinksService, BackfillService, CalendarService, 
ConfigService, ConnectionService, DagParsingService, DagRunService, DagService, 
DagSourceService, DagStatsService, DagVersionService, DagWarningService, 
DashboardService, DependenciesService, EventLogService, ExperimentalService, 
ExtraLinksService, GanttService, GridService, ImportErrorService, JobService, 
LoginService, MonitorService, PluginService, PoolService, ProviderService, 
StructureService, TaskInstanceServ [...]
+import { AssetService, AuthLinksService, BackfillService, CalendarService, 
ConfigService, ConnectionService, DagParsingService, DagRunService, DagService, 
DagSourceService, DagStatsService, DagVersionService, DagWarningService, 
DashboardService, DeadlinesService, DependenciesService, EventLogService, 
ExperimentalService, ExtraLinksService, GanttService, GridService, 
ImportErrorService, JobService, LoginService, MonitorService, PluginService, 
PoolService, ProviderService, StructureService [...]
 import { BackfillPostBody, BulkBody_BulkTaskInstanceBody_, 
BulkBody_ConnectionBody_, BulkBody_PoolBody_, BulkBody_VariableBody_, 
ClearTaskInstancesBody, ConnectionBody, CreateAssetEventsBody, DAGPatchBody, 
DAGRunClearBody, DAGRunPatchBody, DAGRunsBatchBody, DagRunState, 
DagWarningType, PatchTaskInstanceBody, PoolBody, PoolPatchBody, 
TaskInstancesBatchBody, TriggerDAGRunPostBody, UpdateHITLDetailPayload, 
VariableBody, XComCreateBody, XComUpdateBody } from "../requests/types.gen";
 import * as Common from "./common";
 /**
@@ -1532,6 +1532,19 @@ export const useDashboardServiceHistoricalMetrics = 
<TData = Common.DashboardSer
 */
 export const useDashboardServiceDagStats = <TData = 
Common.DashboardServiceDagStatsDefaultResponse, TError = unknown, TQueryKey 
extends Array<unknown> = unknown[]>(queryKey?: TQueryKey, options?: 
Omit<UseQueryOptions<TData, TError>, "queryKey" | "queryFn">) => 
useQuery<TData, TError>({ queryKey: 
Common.UseDashboardServiceDagStatsKeyFn(queryKey), queryFn: () => 
DashboardService.dagStats() as TData, ...options });
 /**
+* Get Dag Run Deadlines
+* 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
+* @throws ApiError
+*/
+export const useDeadlinesServiceGetDagRunDeadlines = <TData = 
Common.DeadlinesServiceGetDagRunDeadlinesDefaultResponse, TError = unknown, 
TQueryKey extends Array<unknown> = unknown[]>({ dagId, runId }: {
+  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 });
+/**
 * Structure Data
 * Get Structure Data.
 * @param data The data for the request.
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 57aa208df95..05a2d3941ab 100644
--- a/airflow-core/src/airflow/ui/openapi-gen/queries/suspense.ts
+++ b/airflow-core/src/airflow/ui/openapi-gen/queries/suspense.ts
@@ -1,7 +1,7 @@
 // generated with @7nohe/[email protected] 
 
 import { UseQueryOptions, useSuspenseQuery } from "@tanstack/react-query";
-import { AssetService, AuthLinksService, BackfillService, CalendarService, 
ConfigService, ConnectionService, DagRunService, DagService, DagSourceService, 
DagStatsService, DagVersionService, DagWarningService, DashboardService, 
DependenciesService, EventLogService, ExperimentalService, ExtraLinksService, 
GanttService, GridService, ImportErrorService, JobService, LoginService, 
MonitorService, PluginService, PoolService, ProviderService, StructureService, 
TaskInstanceService, TaskService, T [...]
+import { AssetService, AuthLinksService, BackfillService, CalendarService, 
ConfigService, ConnectionService, DagRunService, DagService, DagSourceService, 
DagStatsService, DagVersionService, DagWarningService, DashboardService, 
DeadlinesService, DependenciesService, EventLogService, ExperimentalService, 
ExtraLinksService, GanttService, GridService, ImportErrorService, JobService, 
LoginService, MonitorService, PluginService, PoolService, ProviderService, 
StructureService, TaskInstanceServi [...]
 import { DagRunState, DagWarningType } from "../requests/types.gen";
 import * as Common from "./common";
 /**
@@ -1532,6 +1532,19 @@ export const 
useDashboardServiceHistoricalMetricsSuspense = <TData = Common.Dash
 */
 export const useDashboardServiceDagStatsSuspense = <TData = 
Common.DashboardServiceDagStatsDefaultResponse, TError = unknown, TQueryKey 
extends Array<unknown> = unknown[]>(queryKey?: TQueryKey, options?: 
Omit<UseQueryOptions<TData, TError>, "queryKey" | "queryFn">) => 
useSuspenseQuery<TData, TError>({ queryKey: 
Common.UseDashboardServiceDagStatsKeyFn(queryKey), queryFn: () => 
DashboardService.dagStats() as TData, ...options });
 /**
+* Get Dag Run Deadlines
+* 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
+* @throws ApiError
+*/
+export const useDeadlinesServiceGetDagRunDeadlinesSuspense = <TData = 
Common.DeadlinesServiceGetDagRunDeadlinesDefaultResponse, TError = unknown, 
TQueryKey extends Array<unknown> = unknown[]>({ dagId, runId }: {
+  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 });
+/**
 * Structure Data
 * Get Structure Data.
 * @param data The data for the request.
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 8e6a9ff3e68..b9e305f07c9 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
@@ -7820,6 +7820,56 @@ export const $DashboardDagStatsResponse = {
     description: 'Dashboard DAG Stats serializer for responses.'
 } as const;
 
+export const $DeadlineResponse = {
+    properties: {
+        id: {
+            type: 'string',
+            format: 'uuid',
+            title: 'Id'
+        },
+        deadline_time: {
+            type: 'string',
+            format: 'date-time',
+            title: 'Deadline Time'
+        },
+        missed: {
+            type: 'boolean',
+            title: 'Missed'
+        },
+        created_at: {
+            type: 'string',
+            format: 'date-time',
+            title: 'Created At'
+        },
+        alert_name: {
+            anyOf: [
+                {
+                    type: 'string'
+                },
+                {
+                    type: 'null'
+                }
+            ],
+            title: 'Alert Name'
+        },
+        alert_description: {
+            anyOf: [
+                {
+                    type: 'string'
+                },
+                {
+                    type: 'null'
+                }
+            ],
+            title: 'Alert Description'
+        }
+    },
+    type: 'object',
+    required: ['id', 'deadline_time', 'missed', 'created_at'],
+    title: 'DeadlineResponse',
+    description: 'Deadline data for the DAG run deadlines tab.'
+} as const;
+
 export const $EdgeResponse = {
     properties: {
         source_id: {
@@ -8093,6 +8143,10 @@ export const $GridRunsResponse = {
         run_type: {
             '$ref': '#/components/schemas/DagRunType'
         },
+        has_missed_deadline: {
+            type: 'boolean',
+            title: 'Has Missed Deadline'
+        },
         duration: {
             type: 'number',
             title: 'Duration',
@@ -8100,7 +8154,7 @@ export const $GridRunsResponse = {
         }
     },
     type: 'object',
-    required: ['dag_id', 'run_id', 'queued_at', 'start_date', 'end_date', 
'run_after', 'state', 'run_type', 'duration'],
+    required: ['dag_id', 'run_id', 'queued_at', 'start_date', 'end_date', 
'run_after', 'state', 'run_type', 'has_missed_deadline', 'duration'],
     title: 'GridRunsResponse',
     description: 'Base Node serializer for responses.'
 } as const;
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 ccc3d86fc0d..63107cb97b6 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
@@ -3,7 +3,7 @@
 import type { CancelablePromise } from './core/CancelablePromise';
 import { OpenAPI } from './core/OpenAPI';
 import { request as __request } from './core/request';
-import type { GetAssetsData, GetAssetsResponse, GetAssetAliasesData, 
GetAssetAliasesResponse, GetAssetAliasData, GetAssetAliasResponse, 
GetAssetEventsData, GetAssetEventsResponse, CreateAssetEventData, 
CreateAssetEventResponse, MaterializeAssetData, MaterializeAssetResponse, 
GetAssetQueuedEventsData, GetAssetQueuedEventsResponse, 
DeleteAssetQueuedEventsData, DeleteAssetQueuedEventsResponse, GetAssetData, 
GetAssetResponse, GetDagAssetQueuedEventsData, GetDagAssetQueuedEventsResponse, 
Dele [...]
+import type { GetAssetsData, GetAssetsResponse, GetAssetAliasesData, 
GetAssetAliasesResponse, GetAssetAliasData, GetAssetAliasResponse, 
GetAssetEventsData, GetAssetEventsResponse, CreateAssetEventData, 
CreateAssetEventResponse, MaterializeAssetData, MaterializeAssetResponse, 
GetAssetQueuedEventsData, GetAssetQueuedEventsResponse, 
DeleteAssetQueuedEventsData, DeleteAssetQueuedEventsResponse, GetAssetData, 
GetAssetResponse, GetDagAssetQueuedEventsData, GetDagAssetQueuedEventsResponse, 
Dele [...]
 
 export class AssetService {
     /**
@@ -3917,6 +3917,33 @@ export class DashboardService {
     
 }
 
+export class DeadlinesService {
+    /**
+     * Get Dag Run Deadlines
+     * 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
+     * @throws ApiError
+     */
+    public static getDagRunDeadlines(data: GetDagRunDeadlinesData): 
CancelablePromise<GetDagRunDeadlinesResponse> {
+        return __request(OpenAPI, {
+            method: 'GET',
+            url: '/ui/deadlines/{dag_id}/{run_id}',
+            path: {
+                dag_id: data.dagId,
+                run_id: data.runId
+            },
+            errors: {
+                404: 'Not Found',
+                422: 'Validation Error'
+            }
+        });
+    }
+    
+}
+
 export class StructureService {
     /**
      * Structure Data
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 ac57f493ff6..aa596d50253 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
@@ -1925,6 +1925,18 @@ export type DashboardDagStatsResponse = {
     queued_dag_count: number;
 };
 
+/**
+ * Deadline data for the DAG run deadlines tab.
+ */
+export type DeadlineResponse = {
+    id: string;
+    deadline_time: string;
+    missed: boolean;
+    created_at: string;
+    alert_name?: string | null;
+    alert_description?: string | null;
+};
+
 /**
  * Edge serializer for responses.
  */
@@ -1987,6 +1999,7 @@ export type GridRunsResponse = {
     run_after: string;
     state: DagRunState | null;
     run_type: DagRunType;
+    has_missed_deadline: boolean;
     readonly duration: number;
 };
 
@@ -3463,6 +3476,13 @@ export type HistoricalMetricsResponse = 
HistoricalMetricDataResponse;
 
 export type DagStatsResponse2 = DashboardDagStatsResponse;
 
+export type GetDagRunDeadlinesData = {
+    dagId: string;
+    runId: string;
+};
+
+export type GetDagRunDeadlinesResponse = Array<DeadlineResponse>;
+
 export type StructureDataData = {
     dagId: string;
     depth?: number | null;
@@ -6677,6 +6697,25 @@ export type $OpenApiTs = {
             };
         };
     };
+    '/ui/deadlines/{dag_id}/{run_id}': {
+        get: {
+            req: GetDagRunDeadlinesData;
+            res: {
+                /**
+                 * Successful Response
+                 */
+                200: Array<DeadlineResponse>;
+                /**
+                 * Not Found
+                 */
+                404: HTTPExceptionResponse;
+                /**
+                 * Validation Error
+                 */
+                422: HTTPValidationError;
+            };
+        };
+    };
     '/ui/structure/structure_data': {
         get: {
             req: StructureDataData;
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
new file mode 100644
index 00000000000..4d3184a6094
--- /dev/null
+++ b/airflow-core/tests/unit/api_fastapi/core_api/routes/ui/test_deadlines.py
@@ -0,0 +1,292 @@
+# 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 sqlalchemy import select
+
+from airflow._shared.timezones import timezone
+from airflow.models.deadline import Deadline
+from airflow.models.deadline_alert import DeadlineAlert
+from airflow.models.serialized_dag import SerializedDagModel
+from airflow.providers.standard.operators.empty import EmptyOperator
+from airflow.sdk.definitions.callback import AsyncCallback
+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.db import (
+    clear_db_dags,
+    clear_db_deadline,
+    clear_db_deadline_alert,
+    clear_db_runs,
+    clear_db_serialized_dags,
+)
+
+pytestmark = pytest.mark.db_test
+
+DAG_ID = "test_deadlines_dag"
+
+# Each run represents a different deadline scenario tested below.
+RUN_EMPTY = "run_empty"  # no deadlines
+RUN_SINGLE = "run_single"  # 1 deadline, not missed, no alert
+RUN_MISSED = "run_missed"  # 1 deadline, missed=True
+RUN_ALERT = "run_alert"  # 1 deadline linked to a DeadlineAlert
+RUN_MULTI = "run_multi"  # 3 deadlines added out-of-order (ordering test)
+RUN_OTHER = "run_other"  # has 1 deadline; used to verify per-run isolation
+
+ALERT_NAME = "SLA Breach Alert"
+ALERT_DESCRIPTION = "Fires when SLA is breached"
+
+_CALLBACK_PATH = 
"tests.unit.api_fastapi.core_api.routes.ui.test_deadlines._noop_callback"
+
+
+async def _noop_callback(**kwargs):
+    """No-op callback used to satisfy Deadline creation in tests."""
+
+
+def _cb() -> AsyncCallback:
+    return AsyncCallback(_CALLBACK_PATH)
+
+
[email protected](autouse=True)
+def setup(dag_maker, session):
+    clear_db_deadline()
+    clear_db_deadline_alert()
+    clear_db_runs()
+    clear_db_dags()
+    clear_db_serialized_dags()
+
+    with dag_maker(DAG_ID, serialized=True, session=session):
+        EmptyOperator(task_id="task")
+
+    # ---- create runs -------------------------------------------------------
+    dag_maker.create_dagrun(
+        run_id=RUN_EMPTY,
+        state=DagRunState.SUCCESS,
+        run_type=DagRunType.SCHEDULED,
+        logical_date=timezone.datetime(2024, 11, 1),
+        triggered_by=DagRunTriggeredByType.TEST,
+    )
+
+    run_single = dag_maker.create_dagrun(
+        run_id=RUN_SINGLE,
+        state=DagRunState.SUCCESS,
+        run_type=DagRunType.SCHEDULED,
+        logical_date=timezone.datetime(2024, 11, 2),
+        triggered_by=DagRunTriggeredByType.TEST,
+    )
+
+    run_missed = dag_maker.create_dagrun(
+        run_id=RUN_MISSED,
+        state=DagRunState.SUCCESS,
+        run_type=DagRunType.SCHEDULED,
+        logical_date=timezone.datetime(2024, 11, 3),
+        triggered_by=DagRunTriggeredByType.TEST,
+    )
+
+    run_alert = dag_maker.create_dagrun(
+        run_id=RUN_ALERT,
+        state=DagRunState.SUCCESS,
+        run_type=DagRunType.SCHEDULED,
+        logical_date=timezone.datetime(2024, 11, 4),
+        triggered_by=DagRunTriggeredByType.TEST,
+    )
+
+    run_multi = dag_maker.create_dagrun(
+        run_id=RUN_MULTI,
+        state=DagRunState.SUCCESS,
+        run_type=DagRunType.SCHEDULED,
+        logical_date=timezone.datetime(2024, 11, 5),
+        triggered_by=DagRunTriggeredByType.TEST,
+    )
+
+    run_other = dag_maker.create_dagrun(
+        run_id=RUN_OTHER,
+        state=DagRunState.SUCCESS,
+        run_type=DagRunType.SCHEDULED,
+        logical_date=timezone.datetime(2024, 11, 6),
+        triggered_by=DagRunTriggeredByType.TEST,
+    )
+
+    # ---- deadlines ---------------------------------------------------------
+
+    # run_empty: intentionally no deadlines
+
+    # run_single: one active, non-missed deadline with no alert
+    session.add(
+        Deadline(
+            deadline_time=timezone.datetime(2025, 1, 1, 12, 0, 0),
+            callback=_cb(),
+            dagrun_id=run_single.id,
+            deadline_alert_id=None,
+        )
+    )
+
+    # run_missed: one missed deadline
+    missed_dl = Deadline(
+        deadline_time=timezone.datetime(2024, 12, 1),
+        callback=_cb(),
+        dagrun_id=run_missed.id,
+        deadline_alert_id=None,
+    )
+    missed_dl.missed = True
+    session.add(missed_dl)
+
+    # run_alert: one deadline linked to a DeadlineAlert
+    serialized_dag = 
session.scalar(select(SerializedDagModel).where(SerializedDagModel.dag_id == 
DAG_ID))
+    alert = DeadlineAlert(
+        serialized_dag_id=serialized_dag.id,
+        name=ALERT_NAME,
+        description=ALERT_DESCRIPTION,
+        reference=DeadlineReference.DAGRUN_QUEUED_AT.serialize_reference(),
+        interval=3600.0,
+        callback_def={"path": _CALLBACK_PATH},
+    )
+    session.add(alert)
+    session.flush()
+    session.add(
+        Deadline(
+            deadline_time=timezone.datetime(2025, 1, 1, 12, 0, 0),
+            callback=_cb(),
+            dagrun_id=run_alert.id,
+            deadline_alert_id=alert.id,
+        )
+    )
+
+    # run_multi: three deadlines intentionally added in non-chronological order
+    for dl_time in [
+        timezone.datetime(2025, 3, 1),
+        timezone.datetime(2025, 1, 1),
+        timezone.datetime(2025, 2, 1),
+    ]:
+        session.add(
+            Deadline(
+                deadline_time=dl_time,
+                callback=_cb(),
+                dagrun_id=run_multi.id,
+                deadline_alert_id=None,
+            )
+        )
+
+    # run_other: one deadline (for isolation verification)
+    session.add(
+        Deadline(
+            deadline_time=timezone.datetime(2025, 6, 1),
+            callback=_cb(),
+            dagrun_id=run_other.id,
+            deadline_alert_id=None,
+        )
+    )
+
+    dag_maker.sync_dagbag_to_db()
+    session.commit()
+    yield
+    clear_db_deadline()
+    clear_db_deadline_alert()
+    clear_db_runs()
+    clear_db_dags()
+    clear_db_serialized_dags()
+
+
+class TestGetDagRunDeadlines:
+    """Tests for GET /deadlines/{dag_id}/{run_id}."""
+
+    # ------------------------------------------------------------------
+    # 200 – happy paths
+    # ------------------------------------------------------------------
+
+    def test_no_deadlines_returns_empty_list(self, test_client):
+        response = test_client.get(f"/deadlines/{DAG_ID}/{RUN_EMPTY}")
+        assert response.status_code == 200
+        assert response.json() == []
+
+    def test_single_deadline_without_alert(self, test_client):
+        response = test_client.get(f"/deadlines/{DAG_ID}/{RUN_SINGLE}")
+        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]
+
+    def test_missed_deadline_is_reflected(self, test_client):
+        response = test_client.get(f"/deadlines/{DAG_ID}/{RUN_MISSED}")
+        assert response.status_code == 200
+        data = response.json()
+        assert len(data) == 1
+        assert data[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}")
+        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
+
+    def test_deadlines_ordered_by_deadline_time_ascending(self, test_client):
+        response = test_client.get(f"/deadlines/{DAG_ID}/{RUN_MULTI}")
+        assert response.status_code == 200
+        data = response.json()
+        assert len(data) == 3
+        returned_times = [d["deadline_time"] for d in data]
+        assert returned_times == sorted(returned_times)
+
+    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}")
+        assert response.status_code == 200
+        assert response.json() == []
+
+        # And querying RUN_OTHER returns only its own deadline.
+        response = test_client.get(f"/deadlines/{DAG_ID}/{RUN_OTHER}")
+        assert response.status_code == 200
+        assert len(response.json()) == 1
+
+    # ------------------------------------------------------------------
+    # 404
+    # ------------------------------------------------------------------
+
+    @pytest.mark.parametrize(
+        ("dag_id", "run_id"),
+        [
+            pytest.param("nonexistent_dag", RUN_EMPTY, id="wrong_dag_id"),
+            pytest.param(DAG_ID, "nonexistent_run", id="wrong_run_id"),
+            pytest.param("nonexistent_dag", "nonexistent_run", 
id="both_wrong"),
+        ],
+    )
+    def test_should_response_404(self, test_client, dag_id, run_id):
+        response = test_client.get(f"/deadlines/{dag_id}/{run_id}")
+        assert response.status_code == 404
+
+    # ------------------------------------------------------------------
+    # 401 / 403
+    # ------------------------------------------------------------------
+
+    def test_should_response_401(self, unauthenticated_test_client):
+        response = 
unauthenticated_test_client.get(f"/deadlines/{DAG_ID}/{RUN_EMPTY}")
+        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}")
+        assert response.status_code == 403
diff --git 
a/airflow-core/tests/unit/api_fastapi/core_api/routes/ui/test_grid.py 
b/airflow-core/tests/unit/api_fastapi/core_api/routes/ui/test_grid.py
index fd67e7e3267..ed4d6fb2e0e 100644
--- a/airflow-core/tests/unit/api_fastapi/core_api/routes/ui/test_grid.py
+++ b/airflow-core/tests/unit/api_fastapi/core_api/routes/ui/test_grid.py
@@ -63,6 +63,7 @@ GRID_RUN_1 = {
     "dag_id": "test_dag",
     "duration": 283996800.0,
     "end_date": "2024-12-31T00:00:00Z",
+    "has_missed_deadline": False,
     "run_after": "2024-11-30T00:00:00Z",
     "run_id": "run_1",
     "run_type": "scheduled",
@@ -74,6 +75,7 @@ GRID_RUN_2 = {
     "dag_id": "test_dag",
     "duration": 283996800.0,
     "end_date": "2024-12-31T00:00:00Z",
+    "has_missed_deadline": False,
     "run_after": "2024-11-30T00:00:00Z",
     "run_id": "run_2",
     "run_type": "manual",
@@ -611,6 +613,7 @@ class TestGetGridDataEndpoint:
                 "dag_id": "test_dag",
                 "duration": 283996800.0,
                 "end_date": "2024-12-31T00:00:00Z",
+                "has_missed_deadline": False,
                 "run_after": "2024-11-30T00:00:00Z",
                 "run_id": "run_1",
                 "run_type": "scheduled",
@@ -621,6 +624,7 @@ class TestGetGridDataEndpoint:
                 "dag_id": "test_dag",
                 "duration": 283996800.0,
                 "end_date": "2024-12-31T00:00:00Z",
+                "has_missed_deadline": False,
                 "run_after": "2024-11-30T00:00:00Z",
                 "run_id": "run_2",
                 "run_type": "manual",


Reply via email to