This is an automated email from the ASF dual-hosted git repository.
jscheffl 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 7a0c18613b2 feat(ui): add DAG run state filter to grid view options
(#55898)
7a0c18613b2 is described below
commit 7a0c18613b24fa03b852552d154bd4ed1a965ad6
Author: Dheeraj Turaga <[email protected]>
AuthorDate: Sat Sep 20 05:16:05 2025 -0500
feat(ui): add DAG run state filter to grid view options (#55898)
* feat(ui): add DAG run state filter to grid view options
Add filtering by DAG run state (queued, running, success, failed) to the
grid view options panel, following the same pattern as the
existing DagRuns.tsx implementation.
* Add testsgit add .
* Gantt chart filtering issue by applying the same pattern as PR #55845
The issue was that the Gantt chart wasn't using the same filters as the
Grid view. When you set a DAG run state filter in the Options
menu, it would filter the Grid view but not the Gantt chart. Now both
views will be synchronized with the same filtering criteria.
---
.../api_fastapi/core_api/datamodels/ui/common.py | 4 +-
.../api_fastapi/core_api/openapi/_private_ui.yaml | 18 +++++-
.../airflow/api_fastapi/core_api/routes/ui/grid.py | 7 ++-
.../src/airflow/ui/openapi-gen/queries/common.ts | 10 ++--
.../ui/openapi-gen/queries/ensureQueryData.ts | 12 ++--
.../src/airflow/ui/openapi-gen/queries/prefetch.ts | 12 ++--
.../src/airflow/ui/openapi-gen/queries/queries.ts | 12 ++--
.../src/airflow/ui/openapi-gen/queries/suspense.ts | 12 ++--
.../airflow/ui/openapi-gen/requests/schemas.gen.ts | 2 +-
.../ui/openapi-gen/requests/services.gen.ts | 4 ++
.../airflow/ui/openapi-gen/requests/types.gen.ts | 4 +-
.../ui/src/layouts/Details/DetailsLayout.tsx | 16 +++++-
.../airflow/ui/src/layouts/Details/Gantt/Gantt.tsx | 15 ++++-
.../airflow/ui/src/layouts/Details/Grid/Grid.tsx | 15 +++--
.../ui/src/layouts/Details/PanelButtons.tsx | 64 +++++++++++++++++++++-
.../src/airflow/ui/src/queries/useGridRuns.ts | 5 +-
.../src/airflow/ui/src/queries/useGridStructure.ts | 5 +-
.../api_fastapi/core_api/routes/ui/test_grid.py | 16 ++++++
18 files changed, 193 insertions(+), 40 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 2dee03e6510..27d21f0508d 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
@@ -24,7 +24,7 @@ from pydantic import computed_field
from airflow._shared.timezones import timezone
from airflow.api_fastapi.core_api.base import BaseModel
-from airflow.utils.state import TaskInstanceState
+from airflow.utils.state import DagRunState
from airflow.utils.types import DagRunType
@@ -77,7 +77,7 @@ class GridRunsResponse(BaseModel):
start_date: datetime | None
end_date: datetime | None
run_after: datetime
- state: TaskInstanceState | None
+ state: DagRunState | None
run_type: DagRunType
@computed_field
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 ec079113922..7e1b2193a9e 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
@@ -672,6 +672,14 @@ paths:
items:
type: string
title: Run Type
+ - name: state
+ in: query
+ required: false
+ schema:
+ type: array
+ items:
+ type: string
+ title: State
- name: triggering_user
in: query
required: false
@@ -799,6 +807,14 @@ paths:
items:
type: string
title: Run Type
+ - name: state
+ in: query
+ required: false
+ schema:
+ type: array
+ items:
+ type: string
+ title: State
- name: triggering_user
in: query
required: false
@@ -1920,7 +1936,7 @@ components:
title: Run After
state:
anyOf:
- - $ref: '#/components/schemas/TaskInstanceState'
+ - $ref: '#/components/schemas/DagRunState'
- type: 'null'
run_type:
$ref: '#/components/schemas/DagRunType'
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 fea5abc52e0..7c969b7c6d3 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
@@ -29,6 +29,7 @@ from
airflow.api_fastapi.auth.managers.models.resource_details import DagAccessE
from airflow.api_fastapi.common.db.common import SessionDep, paginated_select
from airflow.api_fastapi.common.parameters import (
QueryDagRunRunTypesFilter,
+ QueryDagRunStateFilter,
QueryDagRunTriggeringUserSearch,
QueryLimit,
QueryOffset,
@@ -129,6 +130,7 @@ def get_dag_structure(
],
run_after: Annotated[RangeFilter,
Depends(datetime_range_filter_factory("run_after", DagRun))],
run_type: QueryDagRunRunTypesFilter,
+ state: QueryDagRunStateFilter,
triggering_user: QueryDagRunTriggeringUserSearch,
) -> list[GridNodeResponse]:
"""Return dag structure for grid view."""
@@ -148,7 +150,7 @@ def get_dag_structure(
statement=base_query,
order_by=order_by,
offset=offset,
- filters=[run_after, run_type, triggering_user],
+ filters=[run_after, run_type, state, triggering_user],
limit=limit,
)
run_ids = list(session.scalars(dag_runs_select_filter))
@@ -227,6 +229,7 @@ def get_grid_runs(
],
run_after: Annotated[RangeFilter,
Depends(datetime_range_filter_factory("run_after", DagRun))],
run_type: QueryDagRunRunTypesFilter,
+ state: QueryDagRunStateFilter,
triggering_user: QueryDagRunTriggeringUserSearch,
) -> list[GridRunsResponse]:
"""Get info about a run for the grid."""
@@ -255,7 +258,7 @@ def get_grid_runs(
statement=base_query,
order_by=order_by,
offset=offset,
- filters=[run_after, run_type, triggering_user],
+ filters=[run_after, run_type, state, triggering_user],
limit=limit,
)
return session.execute(dag_runs_select_filter)
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 bfb860cfc3d..64d1234171f 100644
--- a/airflow-core/src/airflow/ui/openapi-gen/queries/common.ts
+++ b/airflow-core/src/airflow/ui/openapi-gen/queries/common.ts
@@ -805,7 +805,7 @@ export const UseStructureServiceStructureDataKeyFn = ({
dagId, externalDependenc
export type GridServiceGetDagStructureDefaultResponse =
Awaited<ReturnType<typeof GridService.getDagStructure>>;
export type GridServiceGetDagStructureQueryResult<TData =
GridServiceGetDagStructureDefaultResponse, TError = unknown> =
UseQueryResult<TData, TError>;
export const useGridServiceGetDagStructureKey = "GridServiceGetDagStructure";
-export const UseGridServiceGetDagStructureKeyFn = ({ dagId, limit, offset,
orderBy, runAfterGt, runAfterGte, runAfterLt, runAfterLte, runType,
triggeringUser }: {
+export const UseGridServiceGetDagStructureKeyFn = ({ dagId, limit, offset,
orderBy, runAfterGt, runAfterGte, runAfterLt, runAfterLte, runType, state,
triggeringUser }: {
dagId: string;
limit?: number;
offset?: number;
@@ -815,12 +815,13 @@ export const UseGridServiceGetDagStructureKeyFn = ({
dagId, limit, offset, order
runAfterLt?: string;
runAfterLte?: string;
runType?: string[];
+ state?: string[];
triggeringUser?: string;
-}, queryKey?: Array<unknown>) => [useGridServiceGetDagStructureKey,
...(queryKey ?? [{ dagId, limit, offset, orderBy, runAfterGt, runAfterGte,
runAfterLt, runAfterLte, runType, triggeringUser }])];
+}, queryKey?: Array<unknown>) => [useGridServiceGetDagStructureKey,
...(queryKey ?? [{ dagId, limit, offset, orderBy, runAfterGt, runAfterGte,
runAfterLt, runAfterLte, runType, state, triggeringUser }])];
export type GridServiceGetGridRunsDefaultResponse = Awaited<ReturnType<typeof
GridService.getGridRuns>>;
export type GridServiceGetGridRunsQueryResult<TData =
GridServiceGetGridRunsDefaultResponse, TError = unknown> =
UseQueryResult<TData, TError>;
export const useGridServiceGetGridRunsKey = "GridServiceGetGridRuns";
-export const UseGridServiceGetGridRunsKeyFn = ({ dagId, limit, offset,
orderBy, runAfterGt, runAfterGte, runAfterLt, runAfterLte, runType,
triggeringUser }: {
+export const UseGridServiceGetGridRunsKeyFn = ({ dagId, limit, offset,
orderBy, runAfterGt, runAfterGte, runAfterLt, runAfterLte, runType, state,
triggeringUser }: {
dagId: string;
limit?: number;
offset?: number;
@@ -830,8 +831,9 @@ export const UseGridServiceGetGridRunsKeyFn = ({ dagId,
limit, offset, orderBy,
runAfterLt?: string;
runAfterLte?: string;
runType?: string[];
+ state?: string[];
triggeringUser?: string;
-}, queryKey?: Array<unknown>) => [useGridServiceGetGridRunsKey, ...(queryKey
?? [{ dagId, limit, offset, orderBy, runAfterGt, runAfterGte, runAfterLt,
runAfterLte, runType, triggeringUser }])];
+}, queryKey?: Array<unknown>) => [useGridServiceGetGridRunsKey, ...(queryKey
?? [{ dagId, limit, offset, orderBy, runAfterGt, runAfterGte, runAfterLt,
runAfterLte, runType, state, triggeringUser }])];
export type GridServiceGetGridTiSummariesDefaultResponse =
Awaited<ReturnType<typeof GridService.getGridTiSummaries>>;
export type GridServiceGetGridTiSummariesQueryResult<TData =
GridServiceGetGridTiSummariesDefaultResponse, TError = unknown> =
UseQueryResult<TData, TError>;
export const useGridServiceGetGridTiSummariesKey =
"GridServiceGetGridTiSummaries";
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 54c822bdc23..f6f354d707c 100644
--- a/airflow-core/src/airflow/ui/openapi-gen/queries/ensureQueryData.ts
+++ b/airflow-core/src/airflow/ui/openapi-gen/queries/ensureQueryData.ts
@@ -1533,11 +1533,12 @@ export const ensureUseStructureServiceStructureDataData
= (queryClient: QueryCli
* @param data.runAfterLte
* @param data.runAfterLt
* @param data.runType
+* @param data.state
* @param data.triggeringUser SQL LIKE expression — use `%` / `_` wildcards
(e.g. `%customer_%`). Regular expressions are **not** supported.
* @returns GridNodeResponse Successful Response
* @throws ApiError
*/
-export const ensureUseGridServiceGetDagStructureData = (queryClient:
QueryClient, { dagId, limit, offset, orderBy, runAfterGt, runAfterGte,
runAfterLt, runAfterLte, runType, triggeringUser }: {
+export const ensureUseGridServiceGetDagStructureData = (queryClient:
QueryClient, { dagId, limit, offset, orderBy, runAfterGt, runAfterGte,
runAfterLt, runAfterLte, runType, state, triggeringUser }: {
dagId: string;
limit?: number;
offset?: number;
@@ -1547,8 +1548,9 @@ export const ensureUseGridServiceGetDagStructureData =
(queryClient: QueryClient
runAfterLt?: string;
runAfterLte?: string;
runType?: string[];
+ state?: string[];
triggeringUser?: string;
-}) => queryClient.ensureQueryData({ queryKey:
Common.UseGridServiceGetDagStructureKeyFn({ dagId, limit, offset, orderBy,
runAfterGt, runAfterGte, runAfterLt, runAfterLte, runType, triggeringUser }),
queryFn: () => GridService.getDagStructure({ dagId, limit, offset, orderBy,
runAfterGt, runAfterGte, runAfterLt, runAfterLte, runType, triggeringUser }) });
+}) => queryClient.ensureQueryData({ queryKey:
Common.UseGridServiceGetDagStructureKeyFn({ dagId, limit, offset, orderBy,
runAfterGt, runAfterGte, runAfterLt, runAfterLte, runType, state,
triggeringUser }), queryFn: () => GridService.getDagStructure({ dagId, limit,
offset, orderBy, runAfterGt, runAfterGte, runAfterLt, runAfterLte, runType,
state, triggeringUser }) });
/**
* Get Grid Runs
* Get info about a run for the grid.
@@ -1562,11 +1564,12 @@ export const ensureUseGridServiceGetDagStructureData =
(queryClient: QueryClient
* @param data.runAfterLte
* @param data.runAfterLt
* @param data.runType
+* @param data.state
* @param data.triggeringUser SQL LIKE expression — use `%` / `_` wildcards
(e.g. `%customer_%`). Regular expressions are **not** supported.
* @returns GridRunsResponse Successful Response
* @throws ApiError
*/
-export const ensureUseGridServiceGetGridRunsData = (queryClient: QueryClient,
{ dagId, limit, offset, orderBy, runAfterGt, runAfterGte, runAfterLt,
runAfterLte, runType, triggeringUser }: {
+export const ensureUseGridServiceGetGridRunsData = (queryClient: QueryClient,
{ dagId, limit, offset, orderBy, runAfterGt, runAfterGte, runAfterLt,
runAfterLte, runType, state, triggeringUser }: {
dagId: string;
limit?: number;
offset?: number;
@@ -1576,8 +1579,9 @@ export const ensureUseGridServiceGetGridRunsData =
(queryClient: QueryClient, {
runAfterLt?: string;
runAfterLte?: string;
runType?: string[];
+ state?: string[];
triggeringUser?: string;
-}) => queryClient.ensureQueryData({ queryKey:
Common.UseGridServiceGetGridRunsKeyFn({ dagId, limit, offset, orderBy,
runAfterGt, runAfterGte, runAfterLt, runAfterLte, runType, triggeringUser }),
queryFn: () => GridService.getGridRuns({ dagId, limit, offset, orderBy,
runAfterGt, runAfterGte, runAfterLt, runAfterLte, runType, triggeringUser }) });
+}) => queryClient.ensureQueryData({ queryKey:
Common.UseGridServiceGetGridRunsKeyFn({ dagId, limit, offset, orderBy,
runAfterGt, runAfterGte, runAfterLt, runAfterLte, runType, state,
triggeringUser }), queryFn: () => GridService.getGridRuns({ dagId, limit,
offset, orderBy, runAfterGt, runAfterGte, runAfterLt, runAfterLte, runType,
state, triggeringUser }) });
/**
* Get Grid Ti Summaries
* Get states for TIs / "groups" of TIs.
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 b267c936712..35acd1db35c 100644
--- a/airflow-core/src/airflow/ui/openapi-gen/queries/prefetch.ts
+++ b/airflow-core/src/airflow/ui/openapi-gen/queries/prefetch.ts
@@ -1533,11 +1533,12 @@ export const prefetchUseStructureServiceStructureData =
(queryClient: QueryClien
* @param data.runAfterLte
* @param data.runAfterLt
* @param data.runType
+* @param data.state
* @param data.triggeringUser SQL LIKE expression — use `%` / `_` wildcards
(e.g. `%customer_%`). Regular expressions are **not** supported.
* @returns GridNodeResponse Successful Response
* @throws ApiError
*/
-export const prefetchUseGridServiceGetDagStructure = (queryClient:
QueryClient, { dagId, limit, offset, orderBy, runAfterGt, runAfterGte,
runAfterLt, runAfterLte, runType, triggeringUser }: {
+export const prefetchUseGridServiceGetDagStructure = (queryClient:
QueryClient, { dagId, limit, offset, orderBy, runAfterGt, runAfterGte,
runAfterLt, runAfterLte, runType, state, triggeringUser }: {
dagId: string;
limit?: number;
offset?: number;
@@ -1547,8 +1548,9 @@ export const prefetchUseGridServiceGetDagStructure =
(queryClient: QueryClient,
runAfterLt?: string;
runAfterLte?: string;
runType?: string[];
+ state?: string[];
triggeringUser?: string;
-}) => queryClient.prefetchQuery({ queryKey:
Common.UseGridServiceGetDagStructureKeyFn({ dagId, limit, offset, orderBy,
runAfterGt, runAfterGte, runAfterLt, runAfterLte, runType, triggeringUser }),
queryFn: () => GridService.getDagStructure({ dagId, limit, offset, orderBy,
runAfterGt, runAfterGte, runAfterLt, runAfterLte, runType, triggeringUser }) });
+}) => queryClient.prefetchQuery({ queryKey:
Common.UseGridServiceGetDagStructureKeyFn({ dagId, limit, offset, orderBy,
runAfterGt, runAfterGte, runAfterLt, runAfterLte, runType, state,
triggeringUser }), queryFn: () => GridService.getDagStructure({ dagId, limit,
offset, orderBy, runAfterGt, runAfterGte, runAfterLt, runAfterLte, runType,
state, triggeringUser }) });
/**
* Get Grid Runs
* Get info about a run for the grid.
@@ -1562,11 +1564,12 @@ export const prefetchUseGridServiceGetDagStructure =
(queryClient: QueryClient,
* @param data.runAfterLte
* @param data.runAfterLt
* @param data.runType
+* @param data.state
* @param data.triggeringUser SQL LIKE expression — use `%` / `_` wildcards
(e.g. `%customer_%`). Regular expressions are **not** supported.
* @returns GridRunsResponse Successful Response
* @throws ApiError
*/
-export const prefetchUseGridServiceGetGridRuns = (queryClient: QueryClient, {
dagId, limit, offset, orderBy, runAfterGt, runAfterGte, runAfterLt,
runAfterLte, runType, triggeringUser }: {
+export const prefetchUseGridServiceGetGridRuns = (queryClient: QueryClient, {
dagId, limit, offset, orderBy, runAfterGt, runAfterGte, runAfterLt,
runAfterLte, runType, state, triggeringUser }: {
dagId: string;
limit?: number;
offset?: number;
@@ -1576,8 +1579,9 @@ export const prefetchUseGridServiceGetGridRuns =
(queryClient: QueryClient, { da
runAfterLt?: string;
runAfterLte?: string;
runType?: string[];
+ state?: string[];
triggeringUser?: string;
-}) => queryClient.prefetchQuery({ queryKey:
Common.UseGridServiceGetGridRunsKeyFn({ dagId, limit, offset, orderBy,
runAfterGt, runAfterGte, runAfterLt, runAfterLte, runType, triggeringUser }),
queryFn: () => GridService.getGridRuns({ dagId, limit, offset, orderBy,
runAfterGt, runAfterGte, runAfterLt, runAfterLte, runType, triggeringUser }) });
+}) => queryClient.prefetchQuery({ queryKey:
Common.UseGridServiceGetGridRunsKeyFn({ dagId, limit, offset, orderBy,
runAfterGt, runAfterGte, runAfterLt, runAfterLte, runType, state,
triggeringUser }), queryFn: () => GridService.getGridRuns({ dagId, limit,
offset, orderBy, runAfterGt, runAfterGte, runAfterLt, runAfterLte, runType,
state, triggeringUser }) });
/**
* Get Grid Ti Summaries
* Get states for TIs / "groups" of TIs.
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 62e4fc99d36..d0c15fc387e 100644
--- a/airflow-core/src/airflow/ui/openapi-gen/queries/queries.ts
+++ b/airflow-core/src/airflow/ui/openapi-gen/queries/queries.ts
@@ -1533,11 +1533,12 @@ export const useStructureServiceStructureData = <TData
= Common.StructureService
* @param data.runAfterLte
* @param data.runAfterLt
* @param data.runType
+* @param data.state
* @param data.triggeringUser SQL LIKE expression — use `%` / `_` wildcards
(e.g. `%customer_%`). Regular expressions are **not** supported.
* @returns GridNodeResponse Successful Response
* @throws ApiError
*/
-export const useGridServiceGetDagStructure = <TData =
Common.GridServiceGetDagStructureDefaultResponse, TError = unknown, TQueryKey
extends Array<unknown> = unknown[]>({ dagId, limit, offset, orderBy,
runAfterGt, runAfterGte, runAfterLt, runAfterLte, runType, triggeringUser }: {
+export const useGridServiceGetDagStructure = <TData =
Common.GridServiceGetDagStructureDefaultResponse, TError = unknown, TQueryKey
extends Array<unknown> = unknown[]>({ dagId, limit, offset, orderBy,
runAfterGt, runAfterGte, runAfterLt, runAfterLte, runType, state,
triggeringUser }: {
dagId: string;
limit?: number;
offset?: number;
@@ -1547,8 +1548,9 @@ export const useGridServiceGetDagStructure = <TData =
Common.GridServiceGetDagSt
runAfterLt?: string;
runAfterLte?: string;
runType?: string[];
+ state?: string[];
triggeringUser?: string;
-}, queryKey?: TQueryKey, options?: Omit<UseQueryOptions<TData, TError>,
"queryKey" | "queryFn">) => useQuery<TData, TError>({ queryKey:
Common.UseGridServiceGetDagStructureKeyFn({ dagId, limit, offset, orderBy,
runAfterGt, runAfterGte, runAfterLt, runAfterLte, runType, triggeringUser },
queryKey), queryFn: () => GridService.getDagStructure({ dagId, limit, offset,
orderBy, runAfterGt, runAfterGte, runAfterLt, runAfterLte, runType,
triggeringUser }) as TData, ...options });
+}, queryKey?: TQueryKey, options?: Omit<UseQueryOptions<TData, TError>,
"queryKey" | "queryFn">) => useQuery<TData, TError>({ queryKey:
Common.UseGridServiceGetDagStructureKeyFn({ dagId, limit, offset, orderBy,
runAfterGt, runAfterGte, runAfterLt, runAfterLte, runType, state,
triggeringUser }, queryKey), queryFn: () => GridService.getDagStructure({
dagId, limit, offset, orderBy, runAfterGt, runAfterGte, runAfterLt,
runAfterLte, runType, state, triggeringUser }) as TData, ...options });
/**
* Get Grid Runs
* Get info about a run for the grid.
@@ -1562,11 +1564,12 @@ export const useGridServiceGetDagStructure = <TData =
Common.GridServiceGetDagSt
* @param data.runAfterLte
* @param data.runAfterLt
* @param data.runType
+* @param data.state
* @param data.triggeringUser SQL LIKE expression — use `%` / `_` wildcards
(e.g. `%customer_%`). Regular expressions are **not** supported.
* @returns GridRunsResponse Successful Response
* @throws ApiError
*/
-export const useGridServiceGetGridRuns = <TData =
Common.GridServiceGetGridRunsDefaultResponse, TError = unknown, TQueryKey
extends Array<unknown> = unknown[]>({ dagId, limit, offset, orderBy,
runAfterGt, runAfterGte, runAfterLt, runAfterLte, runType, triggeringUser }: {
+export const useGridServiceGetGridRuns = <TData =
Common.GridServiceGetGridRunsDefaultResponse, TError = unknown, TQueryKey
extends Array<unknown> = unknown[]>({ dagId, limit, offset, orderBy,
runAfterGt, runAfterGte, runAfterLt, runAfterLte, runType, state,
triggeringUser }: {
dagId: string;
limit?: number;
offset?: number;
@@ -1576,8 +1579,9 @@ export const useGridServiceGetGridRuns = <TData =
Common.GridServiceGetGridRunsD
runAfterLt?: string;
runAfterLte?: string;
runType?: string[];
+ state?: string[];
triggeringUser?: string;
-}, queryKey?: TQueryKey, options?: Omit<UseQueryOptions<TData, TError>,
"queryKey" | "queryFn">) => useQuery<TData, TError>({ queryKey:
Common.UseGridServiceGetGridRunsKeyFn({ dagId, limit, offset, orderBy,
runAfterGt, runAfterGte, runAfterLt, runAfterLte, runType, triggeringUser },
queryKey), queryFn: () => GridService.getGridRuns({ dagId, limit, offset,
orderBy, runAfterGt, runAfterGte, runAfterLt, runAfterLte, runType,
triggeringUser }) as TData, ...options });
+}, queryKey?: TQueryKey, options?: Omit<UseQueryOptions<TData, TError>,
"queryKey" | "queryFn">) => useQuery<TData, TError>({ queryKey:
Common.UseGridServiceGetGridRunsKeyFn({ dagId, limit, offset, orderBy,
runAfterGt, runAfterGte, runAfterLt, runAfterLte, runType, state,
triggeringUser }, queryKey), queryFn: () => GridService.getGridRuns({ dagId,
limit, offset, orderBy, runAfterGt, runAfterGte, runAfterLt, runAfterLte,
runType, state, triggeringUser }) as TData, ...options });
/**
* Get Grid Ti Summaries
* Get states for TIs / "groups" of TIs.
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 5fafc55f54a..4967a3cb498 100644
--- a/airflow-core/src/airflow/ui/openapi-gen/queries/suspense.ts
+++ b/airflow-core/src/airflow/ui/openapi-gen/queries/suspense.ts
@@ -1533,11 +1533,12 @@ export const useStructureServiceStructureDataSuspense =
<TData = Common.Structur
* @param data.runAfterLte
* @param data.runAfterLt
* @param data.runType
+* @param data.state
* @param data.triggeringUser SQL LIKE expression — use `%` / `_` wildcards
(e.g. `%customer_%`). Regular expressions are **not** supported.
* @returns GridNodeResponse Successful Response
* @throws ApiError
*/
-export const useGridServiceGetDagStructureSuspense = <TData =
Common.GridServiceGetDagStructureDefaultResponse, TError = unknown, TQueryKey
extends Array<unknown> = unknown[]>({ dagId, limit, offset, orderBy,
runAfterGt, runAfterGte, runAfterLt, runAfterLte, runType, triggeringUser }: {
+export const useGridServiceGetDagStructureSuspense = <TData =
Common.GridServiceGetDagStructureDefaultResponse, TError = unknown, TQueryKey
extends Array<unknown> = unknown[]>({ dagId, limit, offset, orderBy,
runAfterGt, runAfterGte, runAfterLt, runAfterLte, runType, state,
triggeringUser }: {
dagId: string;
limit?: number;
offset?: number;
@@ -1547,8 +1548,9 @@ export const useGridServiceGetDagStructureSuspense =
<TData = Common.GridService
runAfterLt?: string;
runAfterLte?: string;
runType?: string[];
+ state?: string[];
triggeringUser?: string;
-}, queryKey?: TQueryKey, options?: Omit<UseQueryOptions<TData, TError>,
"queryKey" | "queryFn">) => useSuspenseQuery<TData, TError>({ queryKey:
Common.UseGridServiceGetDagStructureKeyFn({ dagId, limit, offset, orderBy,
runAfterGt, runAfterGte, runAfterLt, runAfterLte, runType, triggeringUser },
queryKey), queryFn: () => GridService.getDagStructure({ dagId, limit, offset,
orderBy, runAfterGt, runAfterGte, runAfterLt, runAfterLte, runType,
triggeringUser }) as TData, ...options });
+}, queryKey?: TQueryKey, options?: Omit<UseQueryOptions<TData, TError>,
"queryKey" | "queryFn">) => useSuspenseQuery<TData, TError>({ queryKey:
Common.UseGridServiceGetDagStructureKeyFn({ dagId, limit, offset, orderBy,
runAfterGt, runAfterGte, runAfterLt, runAfterLte, runType, state,
triggeringUser }, queryKey), queryFn: () => GridService.getDagStructure({
dagId, limit, offset, orderBy, runAfterGt, runAfterGte, runAfterLt,
runAfterLte, runType, state, triggeringUser }) as TData, ...options });
/**
* Get Grid Runs
* Get info about a run for the grid.
@@ -1562,11 +1564,12 @@ export const useGridServiceGetDagStructureSuspense =
<TData = Common.GridService
* @param data.runAfterLte
* @param data.runAfterLt
* @param data.runType
+* @param data.state
* @param data.triggeringUser SQL LIKE expression — use `%` / `_` wildcards
(e.g. `%customer_%`). Regular expressions are **not** supported.
* @returns GridRunsResponse Successful Response
* @throws ApiError
*/
-export const useGridServiceGetGridRunsSuspense = <TData =
Common.GridServiceGetGridRunsDefaultResponse, TError = unknown, TQueryKey
extends Array<unknown> = unknown[]>({ dagId, limit, offset, orderBy,
runAfterGt, runAfterGte, runAfterLt, runAfterLte, runType, triggeringUser }: {
+export const useGridServiceGetGridRunsSuspense = <TData =
Common.GridServiceGetGridRunsDefaultResponse, TError = unknown, TQueryKey
extends Array<unknown> = unknown[]>({ dagId, limit, offset, orderBy,
runAfterGt, runAfterGte, runAfterLt, runAfterLte, runType, state,
triggeringUser }: {
dagId: string;
limit?: number;
offset?: number;
@@ -1576,8 +1579,9 @@ export const useGridServiceGetGridRunsSuspense = <TData =
Common.GridServiceGetG
runAfterLt?: string;
runAfterLte?: string;
runType?: string[];
+ state?: string[];
triggeringUser?: string;
-}, queryKey?: TQueryKey, options?: Omit<UseQueryOptions<TData, TError>,
"queryKey" | "queryFn">) => useSuspenseQuery<TData, TError>({ queryKey:
Common.UseGridServiceGetGridRunsKeyFn({ dagId, limit, offset, orderBy,
runAfterGt, runAfterGte, runAfterLt, runAfterLte, runType, triggeringUser },
queryKey), queryFn: () => GridService.getGridRuns({ dagId, limit, offset,
orderBy, runAfterGt, runAfterGte, runAfterLt, runAfterLte, runType,
triggeringUser }) as TData, ...options });
+}, queryKey?: TQueryKey, options?: Omit<UseQueryOptions<TData, TError>,
"queryKey" | "queryFn">) => useSuspenseQuery<TData, TError>({ queryKey:
Common.UseGridServiceGetGridRunsKeyFn({ dagId, limit, offset, orderBy,
runAfterGt, runAfterGte, runAfterLt, runAfterLte, runType, state,
triggeringUser }, queryKey), queryFn: () => GridService.getGridRuns({ dagId,
limit, offset, orderBy, runAfterGt, runAfterGte, runAfterLt, runAfterLte,
runType, state, triggeringUser }) as TData, ...options });
/**
* Get Grid Ti Summaries
* Get states for TIs / "groups" of TIs.
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 d90c6ae850f..36273f8818a 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
@@ -7440,7 +7440,7 @@ export const $GridRunsResponse = {
state: {
anyOf: [
{
- '$ref': '#/components/schemas/TaskInstanceState'
+ '$ref': '#/components/schemas/DagRunState'
},
{
type: 'null'
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 292be91e27b..ca8435d0be9 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
@@ -3906,6 +3906,7 @@ export class GridService {
* @param data.runAfterLte
* @param data.runAfterLt
* @param data.runType
+ * @param data.state
* @param data.triggeringUser SQL LIKE expression — use `%` / `_`
wildcards (e.g. `%customer_%`). Regular expressions are **not** supported.
* @returns GridNodeResponse Successful Response
* @throws ApiError
@@ -3926,6 +3927,7 @@ export class GridService {
run_after_lte: data.runAfterLte,
run_after_lt: data.runAfterLt,
run_type: data.runType,
+ state: data.state,
triggering_user: data.triggeringUser
},
errors: {
@@ -3949,6 +3951,7 @@ export class GridService {
* @param data.runAfterLte
* @param data.runAfterLt
* @param data.runType
+ * @param data.state
* @param data.triggeringUser SQL LIKE expression — use `%` / `_`
wildcards (e.g. `%customer_%`). Regular expressions are **not** supported.
* @returns GridRunsResponse Successful Response
* @throws ApiError
@@ -3969,6 +3972,7 @@ export class GridService {
run_after_lte: data.runAfterLte,
run_after_lt: data.runAfterLt,
run_type: data.runType,
+ state: data.state,
triggering_user: data.triggeringUser
},
errors: {
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 de3372607a5..e08a66b5da2 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
@@ -1866,7 +1866,7 @@ export type GridRunsResponse = {
start_date: string | null;
end_date: string | null;
run_after: string;
- state: TaskInstanceState | null;
+ state: DagRunState | null;
run_type: DagRunType;
readonly duration: number;
};
@@ -3218,6 +3218,7 @@ export type GetDagStructureData = {
runAfterLt?: string | null;
runAfterLte?: string | null;
runType?: Array<(string)>;
+ state?: Array<(string)>;
/**
* SQL LIKE expression — use `%` / `_` wildcards (e.g. `%customer_%`).
Regular expressions are **not** supported.
*/
@@ -3236,6 +3237,7 @@ export type GetGridRunsData = {
runAfterLt?: string | null;
runAfterLte?: string | null;
runType?: Array<(string)>;
+ state?: Array<(string)>;
/**
* SQL LIKE expression — use `%` / `_` wildcards (e.g. `%customer_%`).
Regular expressions are **not** supported.
*/
diff --git a/airflow-core/src/airflow/ui/src/layouts/Details/DetailsLayout.tsx
b/airflow-core/src/airflow/ui/src/layouts/Details/DetailsLayout.tsx
index f7d4662ca05..18c93692bd9 100644
--- a/airflow-core/src/airflow/ui/src/layouts/Details/DetailsLayout.tsx
+++ b/airflow-core/src/airflow/ui/src/layouts/Details/DetailsLayout.tsx
@@ -28,7 +28,7 @@ import { Outlet, useParams } from "react-router-dom";
import { useLocalStorage } from "usehooks-ts";
import { useDagServiceGetDag, useDagWarningServiceListDagWarnings } from
"openapi/queries";
-import type { DagRunType } from "openapi/requests/types.gen";
+import type { DagRunState, DagRunType } from "openapi/requests/types.gen";
import BackfillBanner from "src/components/Banner/BackfillBanner";
import { SearchDagsButton } from "src/components/SearchDags";
import TriggerDAGButton from "src/components/TriggerDag/TriggerDAGButton";
@@ -69,6 +69,10 @@ export const DetailsLayout = ({ children, error, isLoading,
tabs }: Props) => {
`triggering_user_filter-${dagId}`,
undefined,
);
+ const [dagRunStateFilter, setDagRunStateFilter] =
useLocalStorage<DagRunState | undefined>(
+ `dag_run_state_filter-${dagId}`,
+ undefined,
+ );
const [showGantt, setShowGantt] =
useLocalStorage<boolean>(`show_gantt-${dagId}`, true);
const { fitView, getZoom } = useReactFlow();
@@ -131,10 +135,12 @@ export const DetailsLayout = ({ children, error,
isLoading, tabs }: Props) => {
>
<Box height="100%" marginInlineEnd={2} overflowY="auto"
position="relative">
<PanelButtons
+ dagRunStateFilter={dagRunStateFilter}
dagView={dagView}
limit={limit}
panelGroupRef={panelGroupRef}
runTypeFilter={runTypeFilter}
+ setDagRunStateFilter={setDagRunStateFilter}
setDagView={setDagView}
setLimit={setLimit}
setRunTypeFilter={setRunTypeFilter}
@@ -148,13 +154,19 @@ export const DetailsLayout = ({ children, error,
isLoading, tabs }: Props) => {
) : (
<HStack gap={0}>
<Grid
+ dagRunState={dagRunStateFilter}
limit={limit}
runType={runTypeFilter}
showGantt={Boolean(runId) && showGantt}
triggeringUser={triggeringUserFilter}
/>
{showGantt ? (
- <Gantt limit={limit} runType={runTypeFilter}
triggeringUser={triggeringUserFilter} />
+ <Gantt
+ dagRunState={dagRunStateFilter}
+ limit={limit}
+ runType={runTypeFilter}
+ triggeringUser={triggeringUserFilter}
+ />
) : undefined}
</HStack>
)}
diff --git a/airflow-core/src/airflow/ui/src/layouts/Details/Gantt/Gantt.tsx
b/airflow-core/src/airflow/ui/src/layouts/Details/Gantt/Gantt.tsx
index 57121c1921b..fa5b4ccc6f8 100644
--- a/airflow-core/src/airflow/ui/src/layouts/Details/Gantt/Gantt.tsx
+++ b/airflow-core/src/airflow/ui/src/layouts/Details/Gantt/Gantt.tsx
@@ -1,3 +1,5 @@
+/* eslint-disable max-lines */
+
/*!
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
@@ -40,7 +42,7 @@ import { useTranslation } from "react-i18next";
import { useParams, useNavigate, useLocation } from "react-router-dom";
import { useTaskInstanceServiceGetTaskInstances } from "openapi/queries";
-import type { DagRunType } from "openapi/requests/types.gen";
+import type { DagRunState, DagRunType } from "openapi/requests/types.gen";
import { useColorMode } from "src/context/colorMode";
import { useHover } from "src/context/hover";
import { useOpenGroups } from "src/context/openGroups";
@@ -70,6 +72,7 @@ ChartJS.register(
);
type Props = {
+ readonly dagRunState?: DagRunState | undefined;
readonly limit: number;
readonly runType?: DagRunType | undefined;
readonly triggeringUser?: string | undefined;
@@ -79,7 +82,7 @@ const CHART_PADDING = 36;
const CHART_ROW_HEIGHT = 20;
const MIN_BAR_WIDTH = 10;
-export const Gantt = ({ limit, runType, triggeringUser }: Props) => {
+export const Gantt = ({ dagRunState, limit, runType, triggeringUser }: Props)
=> {
const { dagId = "", groupId: selectedGroupId, runId = "", taskId:
selectedTaskId } = useParams();
const { openGroupIds } = useOpenGroups();
const deferredOpenGroupIds = useDeferredValue(openGroupIds);
@@ -102,8 +105,14 @@ export const Gantt = ({ limit, runType, triggeringUser }:
Props) => {
const selectedItemColor = colorMode === "light" ? lightSelectedColor :
darkSelectedColor;
const hoveredItemColor = colorMode === "light" ? lightHoverColor :
darkHoverColor;
- const { data: gridRuns, isLoading: runsLoading } = useGridRuns({ limit,
runType, triggeringUser });
+ const { data: gridRuns, isLoading: runsLoading } = useGridRuns({
+ dagRunState,
+ limit,
+ runType,
+ triggeringUser,
+ });
const { data: dagStructure, isLoading: structureLoading } =
useGridStructure({
+ dagRunState,
limit,
runType,
triggeringUser,
diff --git a/airflow-core/src/airflow/ui/src/layouts/Details/Grid/Grid.tsx
b/airflow-core/src/airflow/ui/src/layouts/Details/Grid/Grid.tsx
index 8c2537aa29f..b0325c89867 100644
--- a/airflow-core/src/airflow/ui/src/layouts/Details/Grid/Grid.tsx
+++ b/airflow-core/src/airflow/ui/src/layouts/Details/Grid/Grid.tsx
@@ -24,7 +24,7 @@ import { useTranslation } from "react-i18next";
import { FiChevronsRight } from "react-icons/fi";
import { Link, useParams } from "react-router-dom";
-import type { DagRunType, GridRunsResponse } from "openapi/requests";
+import type { DagRunState, DagRunType, GridRunsResponse } from
"openapi/requests";
import { useOpenGroups } from "src/context/openGroups";
import { useNavigation } from "src/hooks/navigation";
import { useGridRuns } from "src/queries/useGridRuns.ts";
@@ -40,13 +40,14 @@ import { flattenNodes } from "./utils";
dayjs.extend(dayjsDuration);
type Props = {
+ readonly dagRunState?: DagRunState | undefined;
readonly limit: number;
readonly runType?: DagRunType | undefined;
readonly showGantt?: boolean;
readonly triggeringUser?: string | undefined;
};
-export const Grid = ({ limit, runType, showGantt, triggeringUser }: Props) => {
+export const Grid = ({ dagRunState, limit, runType, showGantt, triggeringUser
}: Props) => {
const { t: translate } = useTranslation("dag");
const gridRef = useRef<HTMLDivElement>(null);
@@ -55,7 +56,7 @@ export const Grid = ({ limit, runType, showGantt,
triggeringUser }: Props) => {
const { openGroupIds, toggleGroupId } = useOpenGroups();
const { dagId = "", runId = "" } = useParams();
- const { data: gridRuns, isLoading } = useGridRuns({ limit, runType,
triggeringUser });
+ const { data: gridRuns, isLoading } = useGridRuns({ dagRunState, limit,
runType, triggeringUser });
const selectedRun = gridRuns?.find((run) => run.run_id === runId);
// Check if the selected dag run is inside of the grid response, if not,
we'll update the grid filters
@@ -80,7 +81,13 @@ export const Grid = ({ limit, runType, showGantt,
triggeringUser }: Props) => {
}
}, [gridRuns, setHasActiveRun]);
- const { data: dagStructure } = useGridStructure({ hasActiveRun, limit,
runType, triggeringUser });
+ const { data: dagStructure } = useGridStructure({
+ dagRunState,
+ hasActiveRun,
+ limit,
+ runType,
+ triggeringUser,
+ });
// calculate dag run bar heights relative to max
const max = Math.max.apply(
diff --git a/airflow-core/src/airflow/ui/src/layouts/Details/PanelButtons.tsx
b/airflow-core/src/airflow/ui/src/layouts/Details/PanelButtons.tsx
index 8bf65852c31..65f0eb25441 100644
--- a/airflow-core/src/airflow/ui/src/layouts/Details/PanelButtons.tsx
+++ b/airflow-core/src/airflow/ui/src/layouts/Details/PanelButtons.tsx
@@ -41,24 +41,27 @@ import { MdOutlineAccountTree } from "react-icons/md";
import { useParams } from "react-router-dom";
import { useLocalStorage } from "usehooks-ts";
-import type { DagRunType } from "openapi/requests/types.gen";
+import type { DagRunState, DagRunType } from "openapi/requests/types.gen";
import { DagVersionSelect } from "src/components/DagVersionSelect";
import { directionOptions, type Direction } from
"src/components/Graph/useGraphLayout";
import { RunTypeIcon } from "src/components/RunTypeIcon";
import { SearchBar } from "src/components/SearchBar";
+import { StateBadge } from "src/components/StateBadge";
import { Button, Tooltip } from "src/components/ui";
import { Checkbox } from "src/components/ui/Checkbox";
-import { dagRunTypeOptions } from "src/constants/stateOptions";
+import { dagRunTypeOptions, dagRunStateOptions } from
"src/constants/stateOptions";
import { useContainerWidth } from "src/utils/useContainerWidth";
import { DagRunSelect } from "./DagRunSelect";
import { ToggleGroups } from "./ToggleGroups";
type Props = {
+ readonly dagRunStateFilter: DagRunState | undefined;
readonly dagView: string;
readonly limit: number;
readonly panelGroupRef: React.RefObject<{ setLayout?: (layout:
Array<number>) => void } & HTMLDivElement>;
readonly runTypeFilter: DagRunType | undefined;
+ readonly setDagRunStateFilter:
React.Dispatch<React.SetStateAction<DagRunState | undefined>>;
readonly setDagView: (x: "graph" | "grid") => void;
readonly setLimit: React.Dispatch<React.SetStateAction<number>>;
readonly setRunTypeFilter: React.Dispatch<React.SetStateAction<DagRunType |
undefined>>;
@@ -102,10 +105,12 @@ const deps = ["all", "immediate", "tasks"];
type Dependency = (typeof deps)[number];
export const PanelButtons = ({
+ dagRunStateFilter,
dagView,
limit,
panelGroupRef,
runTypeFilter,
+ setDagRunStateFilter,
setDagView,
setLimit,
setRunTypeFilter,
@@ -170,6 +175,16 @@ export const PanelButtons = ({
}
};
+ const handleDagRunStateChange = (event: SelectValueChangeDetails<string>) =>
{
+ const [val] = event.value;
+
+ if (val === undefined || val === "all") {
+ setDagRunStateFilter(undefined);
+ } else {
+ setDagRunStateFilter(val as DagRunState);
+ }
+ };
+
const handleTriggeringUserChange = (value: string) => {
const trimmedValue = value.trim();
@@ -394,6 +409,51 @@ export const PanelButtons = ({
</Select.Content>
</Select.Positioner>
</Select.Root>
+ <Select.Root
+ // @ts-expect-error The expected option type is
incorrect
+ collection={dagRunStateOptions}
+ data-testid="dag-run-state-filter"
+ onValueChange={handleDagRunStateChange}
+ size="sm"
+ value={[dagRunStateFilter ?? "all"]}
+ >
+
<Select.Label>{translate("common:state")}</Select.Label>
+ <Select.Control>
+ <Select.Trigger>
+ <Select.ValueText>
+ {dagRunStateFilter ? (
+ <StateBadge state={dagRunStateFilter}>
+ {translate(
+ dagRunStateOptions.items.find(
+ (item) => item.value ===
dagRunStateFilter,
+ )?.label ?? "",
+ )}
+ </StateBadge>
+ ) : (
+ translate("dags:filters.allStates")
+ )}
+ </Select.ValueText>
+ </Select.Trigger>
+ <Select.IndicatorGroup>
+ <Select.Indicator />
+ </Select.IndicatorGroup>
+ </Select.Control>
+ <Select.Positioner>
+ <Select.Content>
+ {dagRunStateOptions.items.map((option) => (
+ <Select.Item item={option} key={option.value}>
+ {option.value === "all" ? (
+ translate(option.label)
+ ) : (
+ <StateBadge state={option.value as
DagRunState}>
+ {translate(option.label)}
+ </StateBadge>
+ )}
+ </Select.Item>
+ ))}
+ </Select.Content>
+ </Select.Positioner>
+ </Select.Root>
<VStack alignItems="flex-start">
<Text fontSize="xs" mb={1}>
{translate("common:dagRun.triggeringUser")}
diff --git a/airflow-core/src/airflow/ui/src/queries/useGridRuns.ts
b/airflow-core/src/airflow/ui/src/queries/useGridRuns.ts
index af077b7b1af..f5772aedcd4 100644
--- a/airflow-core/src/airflow/ui/src/queries/useGridRuns.ts
+++ b/airflow-core/src/airflow/ui/src/queries/useGridRuns.ts
@@ -19,14 +19,16 @@
import { useParams } from "react-router-dom";
import { useGridServiceGetGridRuns } from "openapi/queries";
-import type { DagRunType } from "openapi/requests/types.gen";
+import type { DagRunState, DagRunType } from "openapi/requests/types.gen";
import { isStatePending, useAutoRefresh } from "src/utils";
export const useGridRuns = ({
+ dagRunState,
limit,
runType,
triggeringUser,
}: {
+ dagRunState?: DagRunState | undefined;
limit: number;
runType?: DagRunType | undefined;
triggeringUser?: string | undefined;
@@ -41,6 +43,7 @@ export const useGridRuns = ({
limit,
orderBy: ["-run_after"],
runType: runType ? [runType] : undefined,
+ state: dagRunState ? [dagRunState] : undefined,
triggeringUser: triggeringUser ?? undefined,
},
undefined,
diff --git a/airflow-core/src/airflow/ui/src/queries/useGridStructure.ts
b/airflow-core/src/airflow/ui/src/queries/useGridStructure.ts
index f312b028e67..c7a2580c2f7 100644
--- a/airflow-core/src/airflow/ui/src/queries/useGridStructure.ts
+++ b/airflow-core/src/airflow/ui/src/queries/useGridStructure.ts
@@ -19,15 +19,17 @@
import { useParams } from "react-router-dom";
import { useGridServiceGetDagStructure } from "openapi/queries";
-import type { DagRunType } from "openapi/requests/types.gen";
+import type { DagRunState, DagRunType } from "openapi/requests/types.gen";
import { useAutoRefresh } from "src/utils";
export const useGridStructure = ({
+ dagRunState,
hasActiveRun = undefined,
limit,
runType,
triggeringUser,
}: {
+ dagRunState?: DagRunState | undefined;
hasActiveRun?: boolean;
limit?: number;
runType?: DagRunType | undefined;
@@ -43,6 +45,7 @@ export const useGridStructure = ({
limit,
orderBy: ["-run_after"],
runType: runType ? [runType] : undefined,
+ state: dagRunState ? [dagRunState] : undefined,
triggeringUser: triggeringUser ?? undefined,
},
undefined,
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 32f8f2683f0..b90c1a9f9a7 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
@@ -531,6 +531,22 @@ class TestGetGridDataEndpoint:
assert response.status_code == 200
assert response.json() == [GRID_RUN_2]
+ @pytest.mark.parametrize(
+ "endpoint,state,expected",
+ [
+ ("runs", "success", [GRID_RUN_1]),
+ ("runs", "failed", [GRID_RUN_2]),
+ ("runs", "running", []),
+ ("structure", "success", GRID_NODES),
+ ("structure", "failed", GRID_NODES),
+ ],
+ )
+ def test_filter_by_state(self, session, test_client, endpoint, state,
expected):
+ session.commit()
+ response = test_client.get(f"/grid/{endpoint}/{DAG_ID}?state={state}")
+ assert response.status_code == 200
+ assert response.json() == expected
+
def test_grid_ti_summaries_group(self, session, test_client):
run_id = "run_4-1"
session.commit()