Skip to content

Commit c71566b

Browse files
authored
Fix Task Instance “No Status” Filter (#51880)
* 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
1 parent 14c159b commit c71566b

File tree

2 files changed

+27
-3
lines changed

2 files changed

+27
-3
lines changed

airflow-core/src/airflow/api_fastapi/common/parameters.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@
3737
from fastapi import Depends, HTTPException, Query, status
3838
from pendulum.parsing.exceptions import ParserError
3939
from pydantic import AfterValidator, BaseModel, NonNegativeInt
40-
from sqlalchemy import Column, and_, case, or_
40+
from sqlalchemy import Column, and_, case, func, or_
4141
from sqlalchemy.inspection import inspect
4242

4343
from airflow.api_fastapi.core_api.base import OrmClause
@@ -493,9 +493,12 @@ def depends_datetime(
493493
lower_bound: datetime | None = Query(alias=f"{filter_name}_gte", default=None),
494494
upper_bound: datetime | None = Query(alias=f"{filter_name}_lte", default=None),
495495
) -> RangeFilter:
496+
attr = getattr(model, attribute_name or filter_name)
497+
if filter_name in ("start_date", "end_date"):
498+
attr = func.coalesce(attr, func.now())
496499
return RangeFilter(
497500
Range(lower_bound=lower_bound, upper_bound=upper_bound),
498-
getattr(model, attribute_name or filter_name),
501+
attr,
499502
)
500503

501504
return depends_datetime
@@ -610,7 +613,7 @@ def _transform_ti_states(states: list[str] | None) -> list[TaskInstanceState | N
610613
return None
611614

612615
try:
613-
return [None if s in ("none", None) else TaskInstanceState(s) for s in states]
616+
return [None if s in ("no_status", "none", None) else TaskInstanceState(s) for s in states]
614617
except ValueError:
615618
raise HTTPException(
616619
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,

airflow-core/tests/unit/api_fastapi/core_api/routes/public/test_task_instances.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -964,6 +964,19 @@ class TestGetTaskInstances(TestTaskInstanceEndpoint):
964964
3,
965965
id="test state filter",
966966
),
967+
pytest.param(
968+
[
969+
{"state": State.RUNNING},
970+
{"state": State.QUEUED},
971+
{"state": State.SUCCESS},
972+
{"state": State.NONE},
973+
],
974+
False,
975+
("/dags/example_python_operator/dagRuns/TEST_DAG_RUN_ID/taskInstances"),
976+
{"state": ["no_status"]},
977+
1,
978+
id="test no_status state filter",
979+
),
967980
pytest.param(
968981
[
969982
{"state": State.NONE},
@@ -977,6 +990,14 @@ class TestGetTaskInstances(TestTaskInstanceEndpoint):
977990
4,
978991
id="test null states with no filter",
979992
),
993+
pytest.param(
994+
[{"start_date": None, "end_date": None}],
995+
True,
996+
"/dags/example_python_operator/dagRuns/TEST_DAG_RUN_ID/taskInstances",
997+
{"start_date_gte": DEFAULT_DATETIME_STR_1},
998+
1,
999+
id="test start_date coalesce with null",
1000+
),
9801001
pytest.param(
9811002
[
9821003
{"pool": "test_pool_1"},

0 commit comments

Comments
 (0)