You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@airflow.apache.org by ds...@apache.org on 2025/07/28 19:14:59 UTC
(airflow) branch main updated: Combine latest run info and grid latest run (#53680)
This is an automated email from the ASF dual-hosted git repository.
dstandish 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 17523392858 Combine latest run info and grid latest run (#53680)
17523392858 is described below
commit 17523392858980f2478733cbc89bf8f4a07b19f3
Author: Daniel Standish <15...@users.noreply.github.com>
AuthorDate: Mon Jul 28 12:14:53 2025 -0700
Combine latest run info and grid latest run (#53680)
---
.../api_fastapi/core_api/datamodels/ui/common.py | 9 ---
.../api_fastapi/core_api/datamodels/ui/dag_runs.py | 3 +-
.../api_fastapi/core_api/openapi/_private_ui.yaml | 90 +++-------------------
.../airflow/api_fastapi/core_api/routes/ui/dags.py | 31 ++++----
.../airflow/api_fastapi/core_api/routes/ui/grid.py | 47 -----------
.../src/airflow/ui/openapi-gen/queries/common.ts | 6 --
.../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 | 38 ++-------
.../ui/openapi-gen/requests/services.gen.ts | 29 +------
.../airflow/ui/openapi-gen/requests/types.gen.ts | 46 +----------
airflow-core/src/airflow/ui/src/pages/Dag/Dag.tsx | 10 +--
.../ui/src/queries/useRefreshOnNewDagRuns.ts | 8 +-
.../api_fastapi/core_api/routes/ui/test_dags.py | 25 ++++++
.../api_fastapi/core_api/routes/ui/test_grid.py | 4 +-
17 files changed, 79 insertions(+), 327 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 6ff8b83825f..2dee03e6510 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
@@ -93,12 +93,3 @@ class BaseGraphResponse(BaseModel, Generic[E, N]):
edges: list[E]
nodes: list[N]
-
-
-class LatestRunResponse(BaseModel):
- """Base Node serializer for responses."""
-
- id: int
- dag_id: str
- run_id: str
- run_after: datetime
diff --git a/airflow-core/src/airflow/api_fastapi/core_api/datamodels/ui/dag_runs.py b/airflow-core/src/airflow/api_fastapi/core_api/datamodels/ui/dag_runs.py
index 78944e9e786..d99761f7457 100644
--- a/airflow-core/src/airflow/api_fastapi/core_api/datamodels/ui/dag_runs.py
+++ b/airflow-core/src/airflow/api_fastapi/core_api/datamodels/ui/dag_runs.py
@@ -25,8 +25,9 @@ from airflow.utils.state import DagRunState
class DAGRunLightResponse(BaseModel):
"""DAG Run serializer for responses."""
- run_id: str
+ id: int
dag_id: str
+ run_id: str
logical_date: datetime | None
run_after: datetime
start_date: datetime | 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 f8289bba786..742f4dafbc8 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
@@ -270,9 +270,9 @@ paths:
content:
application/json:
schema:
- type: array
- items:
- $ref: '#/components/schemas/DAGRunLightResponse'
+ anyOf:
+ - $ref: '#/components/schemas/DAGRunLightResponse'
+ - type: 'null'
title: Response Get Latest Run Info
'404':
content:
@@ -777,55 +777,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/HTTPValidationError'
- /ui/grid/latest_run/{dag_id}:
- get:
- tags:
- - Grid
- summary: Get Latest Run
- description: 'Get information about the latest dag run by run_after.
-
-
- This is used by the UI to figure out if it needs to rerun queries and resume
- auto refresh.'
- operationId: get_latest_run
- security:
- - OAuth2PasswordBearer: []
- - HTTPBearer: []
- parameters:
- - name: dag_id
- in: path
- required: true
- schema:
- type: string
- title: Dag Id
- responses:
- '200':
- description: Successful Response
- content:
- application/json:
- schema:
- anyOf:
- - $ref: '#/components/schemas/LatestRunResponse'
- - type: 'null'
- title: Response Get Latest Run
- '400':
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/HTTPExceptionResponse'
- description: Bad Request
- '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/calendar/{dag_id}:
get:
tags:
@@ -1191,12 +1142,15 @@ components:
the API server/Web UI can use this data to render connection form UI.'
DAGRunLightResponse:
properties:
- run_id:
- type: string
- title: Run Id
+ id:
+ type: integer
+ title: Id
dag_id:
type: string
title: Dag Id
+ run_id:
+ type: string
+ title: Run Id
logical_date:
anyOf:
- type: string
@@ -1223,8 +1177,9 @@ components:
$ref: '#/components/schemas/DagRunState'
type: object
required:
- - run_id
+ - id
- dag_id
+ - run_id
- logical_date
- run_after
- start_date
@@ -1877,29 +1832,6 @@ components:
- task_instance_states
title: HistoricalMetricDataResponse
description: Historical Metric Data serializer for responses.
- LatestRunResponse:
- properties:
- id:
- type: integer
- title: Id
- dag_id:
- type: string
- title: Dag Id
- run_id:
- type: string
- title: Run Id
- run_after:
- type: string
- format: date-time
- title: Run After
- type: object
- required:
- - id
- - dag_id
- - run_id
- - run_after
- title: LatestRunResponse
- description: Base Node serializer for responses.
LightGridTaskInstanceSummary:
properties:
task_id:
diff --git a/airflow-core/src/airflow/api_fastapi/core_api/routes/ui/dags.py b/airflow-core/src/airflow/api_fastapi/core_api/routes/ui/dags.py
index 1ca67321a64..e48241dda02 100644
--- a/airflow-core/src/airflow/api_fastapi/core_api/routes/ui/dags.py
+++ b/airflow-core/src/airflow/api_fastapi/core_api/routes/ui/dags.py
@@ -22,7 +22,6 @@ from typing import Annotated
from fastapi import Depends, status
from sqlalchemy import and_, func, select
-from sqlalchemy.orm import load_only
from airflow.api_fastapi.auth.managers.models.resource_details import DagAccessEntity
from airflow.api_fastapi.common.db.common import (
@@ -202,29 +201,25 @@ def get_dags(
responses=create_openapi_http_exception_doc([status.HTTP_404_NOT_FOUND]),
dependencies=[Depends(requires_access_dag(method="GET", access_entity=DagAccessEntity.RUN))],
)
-def get_latest_run_info(dag_id: str, session: SessionDep) -> list[DAGRunLightResponse]:
+def get_latest_run_info(dag_id: str, session: SessionDep) -> DAGRunLightResponse | None:
"""Get latest run."""
if dag_id == "~":
raise HTTPException(
status.HTTP_400_BAD_REQUEST,
"`~` was supplied as dag_id, but querying multiple dags is not supported.",
)
- query = (
- select(DagRun)
+ return session.execute(
+ select(
+ DagRun.id,
+ DagRun.dag_id,
+ DagRun.run_id,
+ DagRun.end_date,
+ DagRun.logical_date,
+ DagRun.run_after,
+ DagRun.start_date,
+ DagRun.state,
+ )
.where(DagRun.dag_id == dag_id)
.order_by(DagRun.run_after.desc())
.limit(1)
- .options(
- load_only(
- DagRun.dag_id,
- DagRun.run_id,
- DagRun.end_date,
- DagRun.logical_date,
- DagRun.run_after,
- DagRun.start_date,
- DagRun.state,
- )
- )
- )
- dag_runs = session.scalars(query)
- return dag_runs
+ ).one_or_none()
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 80e68bd890f..fd217f3fbdf 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
@@ -37,7 +37,6 @@ from airflow.api_fastapi.common.router import AirflowRouter
from airflow.api_fastapi.core_api.datamodels.ui.common import (
GridNodeResponse,
GridRunsResponse,
- LatestRunResponse,
)
from airflow.api_fastapi.core_api.datamodels.ui.grid import (
GridTISummaries,
@@ -348,49 +347,3 @@ def get_grid_ti_summaries(
"dag_id": dag_id,
"task_instances": list(get_node_sumaries()),
}
-
-
-@grid_router.get(
- "/latest_run/{dag_id}",
- responses=create_openapi_http_exception_doc(
- [
- status.HTTP_400_BAD_REQUEST,
- status.HTTP_404_NOT_FOUND,
- ]
- ),
- dependencies=[
- Depends(
- requires_access_dag(
- method="GET",
- access_entity=DagAccessEntity.TASK_INSTANCE,
- )
- ),
- Depends(
- requires_access_dag(
- method="GET",
- access_entity=DagAccessEntity.RUN,
- )
- ),
- ],
- response_model_exclude_none=True,
-)
-def get_latest_run(
- dag_id: str,
- session: SessionDep,
-) -> LatestRunResponse | None:
- """
- Get information about the latest dag run by run_after.
-
- This is used by the UI to figure out if it needs to rerun queries and resume auto refresh.
- """
- return session.execute(
- select(
- DagRun.id,
- DagRun.dag_id,
- DagRun.run_id,
- DagRun.run_after,
- )
- .where(DagRun.dag_id == dag_id)
- .order_by(DagRun.run_after.desc())
- .limit(1)
- ).one_or_none()
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 f33564ac279..e4e52a6c871 100644
--- a/airflow-core/src/airflow/ui/openapi-gen/queries/common.ts
+++ b/airflow-core/src/airflow/ui/openapi-gen/queries/common.ts
@@ -745,12 +745,6 @@ export const UseGridServiceGetGridTiSummariesKeyFn = ({ dagId, runId }: {
dagId: string;
runId: string;
}, queryKey?: Array<unknown>) => [useGridServiceGetGridTiSummariesKey, ...(queryKey ?? [{ dagId, runId }])];
-export type GridServiceGetLatestRunDefaultResponse = Awaited<ReturnType<typeof GridService.getLatestRun>>;
-export type GridServiceGetLatestRunQueryResult<TData = GridServiceGetLatestRunDefaultResponse, TError = unknown> = UseQueryResult<TData, TError>;
-export const useGridServiceGetLatestRunKey = "GridServiceGetLatestRun";
-export const UseGridServiceGetLatestRunKeyFn = ({ dagId }: {
- dagId: string;
-}, queryKey?: Array<unknown>) => [useGridServiceGetLatestRunKey, ...(queryKey ?? [{ dagId }])];
export type CalendarServiceGetCalendarDefaultResponse = Awaited<ReturnType<typeof CalendarService.getCalendar>>;
export type CalendarServiceGetCalendarQueryResult<TData = CalendarServiceGetCalendarDefaultResponse, TError = unknown> = UseQueryResult<TData, TError>;
export const useCalendarServiceGetCalendarKey = "CalendarServiceGetCalendar";
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 34ad54f9c65..e7138c41954 100644
--- a/airflow-core/src/airflow/ui/openapi-gen/queries/ensureQueryData.ts
+++ b/airflow-core/src/airflow/ui/openapi-gen/queries/ensureQueryData.ts
@@ -542,7 +542,7 @@ export const ensureUseDagServiceGetDagsUiData = (queryClient: QueryClient, { dag
* Get latest run.
* @param data The data for the request.
* @param data.dagId
-* @returns DAGRunLightResponse Successful Response
+* @returns unknown Successful Response
* @throws ApiError
*/
export const ensureUseDagServiceGetLatestRunInfoData = (queryClient: QueryClient, { dagId }: {
@@ -1414,19 +1414,6 @@ export const ensureUseGridServiceGetGridTiSummariesData = (queryClient: QueryCli
runId: string;
}) => queryClient.ensureQueryData({ queryKey: Common.UseGridServiceGetGridTiSummariesKeyFn({ dagId, runId }), queryFn: () => GridService.getGridTiSummaries({ dagId, runId }) });
/**
-* Get Latest Run
-* Get information about the latest dag run by run_after.
-*
-* This is used by the UI to figure out if it needs to rerun queries and resume auto refresh.
-* @param data The data for the request.
-* @param data.dagId
-* @returns unknown Successful Response
-* @throws ApiError
-*/
-export const ensureUseGridServiceGetLatestRunData = (queryClient: QueryClient, { dagId }: {
- dagId: string;
-}) => queryClient.ensureQueryData({ queryKey: Common.UseGridServiceGetLatestRunKeyFn({ dagId }), queryFn: () => GridService.getLatestRun({ dagId }) });
-/**
* Get Calendar
* Get calendar data for a DAG including historical and planned DAG runs.
* @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 8fa4da8b39f..a0c05257f65 100644
--- a/airflow-core/src/airflow/ui/openapi-gen/queries/prefetch.ts
+++ b/airflow-core/src/airflow/ui/openapi-gen/queries/prefetch.ts
@@ -542,7 +542,7 @@ export const prefetchUseDagServiceGetDagsUi = (queryClient: QueryClient, { dagDi
* Get latest run.
* @param data The data for the request.
* @param data.dagId
-* @returns DAGRunLightResponse Successful Response
+* @returns unknown Successful Response
* @throws ApiError
*/
export const prefetchUseDagServiceGetLatestRunInfo = (queryClient: QueryClient, { dagId }: {
@@ -1414,19 +1414,6 @@ export const prefetchUseGridServiceGetGridTiSummaries = (queryClient: QueryClien
runId: string;
}) => queryClient.prefetchQuery({ queryKey: Common.UseGridServiceGetGridTiSummariesKeyFn({ dagId, runId }), queryFn: () => GridService.getGridTiSummaries({ dagId, runId }) });
/**
-* Get Latest Run
-* Get information about the latest dag run by run_after.
-*
-* This is used by the UI to figure out if it needs to rerun queries and resume auto refresh.
-* @param data The data for the request.
-* @param data.dagId
-* @returns unknown Successful Response
-* @throws ApiError
-*/
-export const prefetchUseGridServiceGetLatestRun = (queryClient: QueryClient, { dagId }: {
- dagId: string;
-}) => queryClient.prefetchQuery({ queryKey: Common.UseGridServiceGetLatestRunKeyFn({ dagId }), queryFn: () => GridService.getLatestRun({ dagId }) });
-/**
* Get Calendar
* Get calendar data for a DAG including historical and planned DAG runs.
* @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 8e813d704b6..2fa2b17fefc 100644
--- a/airflow-core/src/airflow/ui/openapi-gen/queries/queries.ts
+++ b/airflow-core/src/airflow/ui/openapi-gen/queries/queries.ts
@@ -542,7 +542,7 @@ export const useDagServiceGetDagsUi = <TData = Common.DagServiceGetDagsUiDefault
* Get latest run.
* @param data The data for the request.
* @param data.dagId
-* @returns DAGRunLightResponse Successful Response
+* @returns unknown Successful Response
* @throws ApiError
*/
export const useDagServiceGetLatestRunInfo = <TData = Common.DagServiceGetLatestRunInfoDefaultResponse, TError = unknown, TQueryKey extends Array<unknown> = unknown[]>({ dagId }: {
@@ -1414,19 +1414,6 @@ export const useGridServiceGetGridTiSummaries = <TData = Common.GridServiceGetGr
runId: string;
}, queryKey?: TQueryKey, options?: Omit<UseQueryOptions<TData, TError>, "queryKey" | "queryFn">) => useQuery<TData, TError>({ queryKey: Common.UseGridServiceGetGridTiSummariesKeyFn({ dagId, runId }, queryKey), queryFn: () => GridService.getGridTiSummaries({ dagId, runId }) as TData, ...options });
/**
-* Get Latest Run
-* Get information about the latest dag run by run_after.
-*
-* This is used by the UI to figure out if it needs to rerun queries and resume auto refresh.
-* @param data The data for the request.
-* @param data.dagId
-* @returns unknown Successful Response
-* @throws ApiError
-*/
-export const useGridServiceGetLatestRun = <TData = Common.GridServiceGetLatestRunDefaultResponse, TError = unknown, TQueryKey extends Array<unknown> = unknown[]>({ dagId }: {
- dagId: string;
-}, queryKey?: TQueryKey, options?: Omit<UseQueryOptions<TData, TError>, "queryKey" | "queryFn">) => useQuery<TData, TError>({ queryKey: Common.UseGridServiceGetLatestRunKeyFn({ dagId }, queryKey), queryFn: () => GridService.getLatestRun({ dagId }) as TData, ...options });
-/**
* Get Calendar
* Get calendar data for a DAG including historical and planned DAG runs.
* @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 3ad13e7d9aa..3eb26bd5377 100644
--- a/airflow-core/src/airflow/ui/openapi-gen/queries/suspense.ts
+++ b/airflow-core/src/airflow/ui/openapi-gen/queries/suspense.ts
@@ -542,7 +542,7 @@ export const useDagServiceGetDagsUiSuspense = <TData = Common.DagServiceGetDagsU
* Get latest run.
* @param data The data for the request.
* @param data.dagId
-* @returns DAGRunLightResponse Successful Response
+* @returns unknown Successful Response
* @throws ApiError
*/
export const useDagServiceGetLatestRunInfoSuspense = <TData = Common.DagServiceGetLatestRunInfoDefaultResponse, TError = unknown, TQueryKey extends Array<unknown> = unknown[]>({ dagId }: {
@@ -1414,19 +1414,6 @@ export const useGridServiceGetGridTiSummariesSuspense = <TData = Common.GridServ
runId: string;
}, queryKey?: TQueryKey, options?: Omit<UseQueryOptions<TData, TError>, "queryKey" | "queryFn">) => useSuspenseQuery<TData, TError>({ queryKey: Common.UseGridServiceGetGridTiSummariesKeyFn({ dagId, runId }, queryKey), queryFn: () => GridService.getGridTiSummaries({ dagId, runId }) as TData, ...options });
/**
-* Get Latest Run
-* Get information about the latest dag run by run_after.
-*
-* This is used by the UI to figure out if it needs to rerun queries and resume auto refresh.
-* @param data The data for the request.
-* @param data.dagId
-* @returns unknown Successful Response
-* @throws ApiError
-*/
-export const useGridServiceGetLatestRunSuspense = <TData = Common.GridServiceGetLatestRunDefaultResponse, TError = unknown, TQueryKey extends Array<unknown> = unknown[]>({ dagId }: {
- dagId: string;
-}, queryKey?: TQueryKey, options?: Omit<UseQueryOptions<TData, TError>, "queryKey" | "queryFn">) => useSuspenseQuery<TData, TError>({ queryKey: Common.UseGridServiceGetLatestRunKeyFn({ dagId }, queryKey), queryFn: () => GridService.getLatestRun({ dagId }) as TData, ...options });
-/**
* Get Calendar
* Get calendar data for a DAG including historical and planned DAG runs.
* @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 58fff0b2020..d836de1bfce 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
@@ -6460,14 +6460,18 @@ the API server/Web UI can use this data to render connection form UI.`
export const $DAGRunLightResponse = {
properties: {
- run_id: {
- type: 'string',
- title: 'Run Id'
+ id: {
+ type: 'integer',
+ title: 'Id'
},
dag_id: {
type: 'string',
title: 'Dag Id'
},
+ run_id: {
+ type: 'string',
+ title: 'Run Id'
+ },
logical_date: {
anyOf: [
{
@@ -6514,7 +6518,7 @@ export const $DAGRunLightResponse = {
}
},
type: 'object',
- required: ['run_id', 'dag_id', 'logical_date', 'run_after', 'start_date', 'end_date', 'state'],
+ required: ['id', 'dag_id', 'run_id', 'logical_date', 'run_after', 'start_date', 'end_date', 'state'],
title: 'DAGRunLightResponse',
description: 'DAG Run serializer for responses.'
} as const;
@@ -7085,32 +7089,6 @@ export const $HistoricalMetricDataResponse = {
description: 'Historical Metric Data serializer for responses.'
} as const;
-export const $LatestRunResponse = {
- properties: {
- id: {
- type: 'integer',
- title: 'Id'
- },
- dag_id: {
- type: 'string',
- title: 'Dag Id'
- },
- run_id: {
- type: 'string',
- title: 'Run Id'
- },
- run_after: {
- type: 'string',
- format: 'date-time',
- title: 'Run After'
- }
- },
- type: 'object',
- required: ['id', 'dag_id', 'run_id', 'run_after'],
- title: 'LatestRunResponse',
- description: 'Base Node serializer for responses.'
-} as const;
-
export const $LightGridTaskInstanceSummary = {
properties: {
task_id: {
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 15b5ab5b58c..1251d2f83d0 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 {
/**
@@ -1686,7 +1686,7 @@ export class DagService {
* Get latest run.
* @param data The data for the request.
* @param data.dagId
- * @returns DAGRunLightResponse Successful Response
+ * @returns unknown Successful Response
* @throws ApiError
*/
public static getLatestRunInfo(data: GetLatestRunInfoData): CancelablePromise<GetLatestRunInfoResponse> {
@@ -3872,31 +3872,6 @@ export class GridService {
});
}
- /**
- * Get Latest Run
- * Get information about the latest dag run by run_after.
- *
- * This is used by the UI to figure out if it needs to rerun queries and resume auto refresh.
- * @param data The data for the request.
- * @param data.dagId
- * @returns unknown Successful Response
- * @throws ApiError
- */
- public static getLatestRun(data: GetLatestRunData): CancelablePromise<GetLatestRunResponse> {
- return __request(OpenAPI, {
- method: 'GET',
- url: '/ui/grid/latest_run/{dag_id}',
- path: {
- dag_id: data.dagId
- },
- errors: {
- 400: 'Bad Request',
- 404: 'Not Found',
- 422: 'Validation Error'
- }
- });
- }
-
}
export class CalendarService {
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 6d49ffe2d43..47d4b65cd89 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
@@ -1687,8 +1687,9 @@ export type ConnectionHookMetaData = {
* DAG Run serializer for responses.
*/
export type DAGRunLightResponse = {
- run_id: string;
+ id: number;
dag_id: string;
+ run_id: string;
logical_date: string | null;
run_after: string;
start_date: string | null;
@@ -1832,16 +1833,6 @@ export type HistoricalMetricDataResponse = {
task_instance_states: TaskInstanceStateCount;
};
-/**
- * Base Node serializer for responses.
- */
-export type LatestRunResponse = {
- id: number;
- dag_id: string;
- run_id: string;
- run_after: string;
-};
-
/**
* Task Instance Summary model for the Grid UI.
*/
@@ -2426,7 +2417,7 @@ export type GetLatestRunInfoData = {
dagId: string;
};
-export type GetLatestRunInfoResponse = Array<DAGRunLightResponse>;
+export type GetLatestRunInfoResponse = DAGRunLightResponse | null;
export type GetEventLogData = {
eventLogId: number;
@@ -3050,12 +3041,6 @@ export type GetGridTiSummariesData = {
export type GetGridTiSummariesResponse = GridTISummaries;
-export type GetLatestRunData = {
- dagId: string;
-};
-
-export type GetLatestRunResponse = LatestRunResponse | null;
-
export type GetCalendarData = {
dagId: string;
granularity?: 'hourly' | 'daily';
@@ -4585,7 +4570,7 @@ export type $OpenApiTs = {
/**
* Successful Response
*/
- 200: Array<DAGRunLightResponse>;
+ 200: DAGRunLightResponse | null;
/**
* Not Found
*/
@@ -6300,29 +6285,6 @@ export type $OpenApiTs = {
};
};
};
- '/ui/grid/latest_run/{dag_id}': {
- get: {
- req: GetLatestRunData;
- res: {
- /**
- * Successful Response
- */
- 200: LatestRunResponse | null;
- /**
- * Bad Request
- */
- 400: HTTPExceptionResponse;
- /**
- * Not Found
- */
- 404: HTTPExceptionResponse;
- /**
- * Validation Error
- */
- 422: HTTPValidationError;
- };
- };
- };
'/ui/calendar/{dag_id}': {
get: {
req: GetCalendarData;
diff --git a/airflow-core/src/airflow/ui/src/pages/Dag/Dag.tsx b/airflow-core/src/airflow/ui/src/pages/Dag/Dag.tsx
index 7f28dad5622..2857f4abd98 100644
--- a/airflow-core/src/airflow/ui/src/pages/Dag/Dag.tsx
+++ b/airflow-core/src/airflow/ui/src/pages/Dag/Dag.tsx
@@ -70,7 +70,7 @@ export const Dag = () => {
const displayTabs = tabs.filter((tab) => !(dag?.timetable_summary === null && tab.value === "backfills"));
const {
- data: latestRuns,
+ data: latestRun,
error: runsError,
isLoading: isLoadingRuns,
} = useDagServiceGetLatestRunInfo(
@@ -81,22 +81,22 @@ export const Dag = () => {
{
enabled: Boolean(dagId),
refetchInterval: (query) => {
- setHasPendingRuns(query.state.data?.some((run) => isStatePending(run.state)));
+ if (query.state.data && isStatePending(query.state.data.state)) {
+ setHasPendingRuns(true);
+ }
return hasPendingRuns ? refetchInterval : false;
},
},
);
- const [latestRun] = latestRuns ?? [];
-
return (
<ReactFlowProvider>
<DetailsLayout error={error ?? runsError} isLoading={isLoading || isLoadingRuns} tabs={displayTabs}>
<Header
dag={dag}
isRefreshing={Boolean(isStatePending(latestRun?.state) && Boolean(refetchInterval))}
- latestRunInfo={latestRun}
+ latestRunInfo={latestRun ?? undefined}
/>
</DetailsLayout>
</ReactFlowProvider>
diff --git a/airflow-core/src/airflow/ui/src/queries/useRefreshOnNewDagRuns.ts b/airflow-core/src/airflow/ui/src/queries/useRefreshOnNewDagRuns.ts
index d47f3a6e35b..5ceae8bbf69 100644
--- a/airflow-core/src/airflow/ui/src/queries/useRefreshOnNewDagRuns.ts
+++ b/airflow-core/src/airflow/ui/src/queries/useRefreshOnNewDagRuns.ts
@@ -25,9 +25,9 @@ import {
UseDagServiceGetDagDetailsKeyFn,
useDagServiceGetDagsUi,
UseTaskInstanceServiceGetTaskInstancesKeyFn,
- useGridServiceGetLatestRun,
UseGridServiceGetDagStructureKeyFn,
UseGridServiceGetGridRunsKeyFn,
+ useDagServiceGetLatestRunInfo,
} from "openapi/queries";
import { useConfig } from "./useConfig";
@@ -37,14 +37,12 @@ export const useRefreshOnNewDagRuns = (dagId: string, hasPendingRuns: boolean |
const previousDagRunIdRef = useRef<string>();
const autoRefreshInterval = useConfig("auto_refresh_interval") as number;
- const { data } = useGridServiceGetLatestRun({ dagId }, undefined, {
+ const { data: latestDagRun } = useDagServiceGetLatestRunInfo({ dagId }, undefined, {
enabled: Boolean(dagId) && !hasPendingRuns,
refetchInterval: Boolean(autoRefreshInterval) ? autoRefreshInterval * 1000 : 5000,
});
useEffect(() => {
- const latestDagRun = data;
-
const latestDagRunId = latestDagRun?.run_id;
if ((latestDagRunId ?? "") && previousDagRunIdRef.current !== latestDagRunId) {
@@ -64,5 +62,5 @@ export const useRefreshOnNewDagRuns = (dagId: string, hasPendingRuns: boolean |
void queryClient.invalidateQueries({ queryKey: key });
});
}
- }, [data, dagId, queryClient]);
+ }, [latestDagRun, dagId, queryClient]);
};
diff --git a/airflow-core/tests/unit/api_fastapi/core_api/routes/ui/test_dags.py b/airflow-core/tests/unit/api_fastapi/core_api/routes/ui/test_dags.py
index 860668c06b5..ce41173ee68 100644
--- a/airflow-core/tests/unit/api_fastapi/core_api/routes/ui/test_dags.py
+++ b/airflow-core/tests/unit/api_fastapi/core_api/routes/ui/test_dags.py
@@ -17,6 +17,7 @@
from __future__ import annotations
from datetime import datetime, timezone
+from unittest import mock
import pendulum
import pytest
@@ -114,3 +115,27 @@ class TestGetDagRuns(TestPublicDagEndpoint):
def test_should_response_403(self, unauthorized_test_client):
response = unauthorized_test_client.get("/dags", params={})
assert response.status_code == 403
+
+ @pytest.mark.usefixtures("configure_git_connection_for_dag_bundle")
+ def test_latest_run_should_return_200(self, test_client):
+ response = test_client.get(f"/dags/{DAG1_ID}/latest_run")
+ assert response.status_code == 200
+ body = response.json()
+ assert body == {
+ "id": mock.ANY,
+ "dag_id": "test_dag1",
+ "run_id": "run_id_5",
+ "logical_date": "2025-01-01T00:00:00Z",
+ "run_after": "2025-01-01T00:00:00Z",
+ "start_date": "2025-01-01T00:00:00Z",
+ "end_date": "2025-01-01T01:00:00Z",
+ "state": "failed",
+ }
+
+ def test_latest_run_should_response_401(self, unauthenticated_test_client):
+ response = unauthenticated_test_client.get(f"/dags/{DAG1_ID}/latest_run")
+ assert response.status_code == 401
+
+ def test_latest_run_should_response_403(self, unauthorized_test_client):
+ response = unauthorized_test_client.get(f"/dags/{DAG1_ID}/latest_run")
+ 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 96808bced38..d95d585e409 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
@@ -387,12 +387,12 @@ class TestGetGridDataEndpoint:
assert response.status_code == 200
assert response.json() == expected
- @pytest.mark.parametrize("endpoint", ["runs", "structure", "latest_run"])
+ @pytest.mark.parametrize("endpoint", ["runs", "structure"])
def test_should_response_401(self, unauthenticated_test_client, endpoint):
response = unauthenticated_test_client.get(f"/grid/{endpoint}/{DAG_ID_3}")
assert response.status_code == 401
- @pytest.mark.parametrize("endpoint", ["runs", "structure", "latest_run"])
+ @pytest.mark.parametrize("endpoint", ["runs", "structure"])
def test_should_response_403(self, unauthorized_test_client, endpoint):
response = unauthorized_test_client.get(f"/grid/{endpoint}/{DAG_ID_3}")
assert response.status_code == 403