From 2a3ca45b41cdd7be055cc04e29800f782c6533f1 Mon Sep 17 00:00:00 2001 From: Ankit Chaurasia <8670962+sunank200@users.noreply.github.com> Date: Tue, 24 Jun 2025 17:33:50 +0545 Subject: [PATCH] =?UTF-8?q?[v3-0-test]=20Fix=20Task=20Instance=20=E2=80=9C?= =?UTF-8?q?No=20Status=E2=80=9D=20Filter=20(#51880)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Support no_status alias in TaskInstance state filter for REST API * Allow 'no_status' state filter and include no_status in valid state list; skip date filters when filtering for null state * Fix NULL-state filtering in get_mapped_task_instances by coalescing date fields * Refactor datetime_range_filter_factory: coalesce only start_date and end_date filters * Add a test (cherry picked from commit c71566b888b7aa48048649feccc4bed9bbf8b02a) Co-authored-by: Ankit Chaurasia <8670962+sunank200@users.noreply.github.com> --- .../airflow/api_fastapi/common/parameters.py | 9 +++++--- .../routes/public/test_task_instances.py | 21 +++++++++++++++++++ 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/airflow-core/src/airflow/api_fastapi/common/parameters.py b/airflow-core/src/airflow/api_fastapi/common/parameters.py index 141447488c26d..4219bb66284af 100644 --- a/airflow-core/src/airflow/api_fastapi/common/parameters.py +++ b/airflow-core/src/airflow/api_fastapi/common/parameters.py @@ -37,7 +37,7 @@ from fastapi import Depends, HTTPException, Query, status from pendulum.parsing.exceptions import ParserError from pydantic import AfterValidator, BaseModel, NonNegativeInt -from sqlalchemy import Column, and_, case, or_ +from sqlalchemy import Column, and_, case, func, or_ from sqlalchemy.inspection import inspect from airflow.api_fastapi.core_api.base import OrmClause @@ -484,9 +484,12 @@ def depends_datetime( lower_bound: datetime | None = Query(alias=f"{filter_name}_gte", default=None), upper_bound: datetime | None = Query(alias=f"{filter_name}_lte", default=None), ) -> RangeFilter: + attr = getattr(model, attribute_name or filter_name) + if filter_name in ("start_date", "end_date"): + attr = func.coalesce(attr, func.now()) return RangeFilter( Range(lower_bound=lower_bound, upper_bound=upper_bound), - getattr(model, attribute_name or filter_name), + attr, ) return depends_datetime @@ -601,7 +604,7 @@ def _transform_ti_states(states: list[str] | None) -> list[TaskInstanceState | N return None try: - return [None if s in ("none", None) else TaskInstanceState(s) for s in states] + return [None if s in ("no_status", "none", None) else TaskInstanceState(s) for s in states] except ValueError: raise HTTPException( status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, diff --git a/airflow-core/tests/unit/api_fastapi/core_api/routes/public/test_task_instances.py b/airflow-core/tests/unit/api_fastapi/core_api/routes/public/test_task_instances.py index 49769007fa89d..c0cdffd56b3e9 100644 --- a/airflow-core/tests/unit/api_fastapi/core_api/routes/public/test_task_instances.py +++ b/airflow-core/tests/unit/api_fastapi/core_api/routes/public/test_task_instances.py @@ -956,6 +956,19 @@ class TestGetTaskInstances(TestTaskInstanceEndpoint): 3, id="test state filter", ), + pytest.param( + [ + {"state": State.RUNNING}, + {"state": State.QUEUED}, + {"state": State.SUCCESS}, + {"state": State.NONE}, + ], + False, + ("/dags/example_python_operator/dagRuns/TEST_DAG_RUN_ID/taskInstances"), + {"state": ["no_status"]}, + 1, + id="test no_status state filter", + ), pytest.param( [ {"state": State.NONE}, @@ -969,6 +982,14 @@ class TestGetTaskInstances(TestTaskInstanceEndpoint): 4, id="test null states with no filter", ), + pytest.param( + [{"start_date": None, "end_date": None}], + True, + "/dags/example_python_operator/dagRuns/TEST_DAG_RUN_ID/taskInstances", + {"start_date_gte": DEFAULT_DATETIME_STR_1}, + 1, + id="test start_date coalesce with null", + ), pytest.param( [ {"pool": "test_pool_1"},