This is an automated email from the ASF dual-hosted git repository.
pierrejeambrun pushed a commit to branch v3-0-test
in repository https://gitbox.apache.org/repos/asf/airflow.git
The following commit(s) were added to refs/heads/v3-0-test by this push:
new ebbe3b79e22 [v3-0-test] Fix Task Instance “No Status” Filter (#51880)
(#52154)
ebbe3b79e22 is described below
commit ebbe3b79e227d60885bba7adc87652ebcc7850b7
Author: github-actions[bot]
<41898282+github-actions[bot]@users.noreply.github.com>
AuthorDate: Tue Jun 24 15:23:09 2025 +0200
[v3-0-test] Fix Task Instance “No Status” Filter (#51880) (#52154)
* 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 <[email protected]>
---
.../src/airflow/api_fastapi/common/parameters.py | 9 ++++++---
.../core_api/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 141447488c2..4219bb66284 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 typing import (
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 datetime_range_filter_factory(
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 49769007fa8..c0cdffd56b3 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"},