This is an automated email from the ASF dual-hosted git repository. rahulvats pushed a commit to branch execution-api-consolidate-2026-04-06 in repository https://gitbox.apache.org/repos/asf/airflow.git
commit 29f7228809e75a787e1b10797c837cde3c369aaf Author: vatsrahul1001 <[email protected]> AuthorDate: Mon Mar 30 18:58:16 2026 +0530 Consolidate unreleased Execution API versions under 2026-04-06 --- .../api_fastapi/execution_api/versions/__init__.py | 19 ++-- .../execution_api/versions/v2025_11_07.py | 54 ----------- .../execution_api/versions/v2025_12_08.py | 41 -------- .../versions/{v2026_03_31.py => v2026_04_06.py} | 105 ++++++++++++++++----- .../execution_api/versions/v2026_04_13.py | 28 ------ .../execution_api/versions/head/test_dag_runs.py | 2 +- .../execution_api/versions/v2026_03_31/__init__.py | 16 ---- .../{v2025_11_07 => v2026_04_06}/__init__.py | 0 .../{v2025_11_07 => v2026_04_06}/test_dag_runs.py | 3 +- .../{v2026_03_31 => v2026_04_06}/test_dags.py | 3 +- .../test_task_instances.py | 4 +- .../src/airflow/sdk/api/datamodels/_generated.py | 2 +- 12 files changed, 100 insertions(+), 177 deletions(-) diff --git a/airflow-core/src/airflow/api_fastapi/execution_api/versions/__init__.py b/airflow-core/src/airflow/api_fastapi/execution_api/versions/__init__.py index 2cbe2e3007b..646831501a7 100644 --- a/airflow-core/src/airflow/api_fastapi/execution_api/versions/__init__.py +++ b/airflow-core/src/airflow/api_fastapi/execution_api/versions/__init__.py @@ -28,31 +28,30 @@ from airflow.api_fastapi.execution_api.versions.v2025_08_10 import ( from airflow.api_fastapi.execution_api.versions.v2025_09_23 import AddDagVersionIdField from airflow.api_fastapi.execution_api.versions.v2025_10_27 import MakeDagRunConfNullable from airflow.api_fastapi.execution_api.versions.v2025_11_05 import AddTriggeringUserNameField -from airflow.api_fastapi.execution_api.versions.v2025_11_07 import AddPartitionKeyField -from airflow.api_fastapi.execution_api.versions.v2025_12_08 import ( +from airflow.api_fastapi.execution_api.versions.v2026_04_06 import ( + AddDagEndpoint, AddDagRunDetailEndpoint, - MovePreviousRunEndpoint, -) -from airflow.api_fastapi.execution_api.versions.v2026_03_31 import ( AddNoteField, + AddPartitionKeyField, MakeDagRunStartDateNullable, ModifyDeferredTaskKwargsToJsonValue, + MovePreviousRunEndpoint, RemoveUpstreamMapIndexesField, ) -from airflow.api_fastapi.execution_api.versions.v2026_04_13 import AddDagEndpoint bundle = VersionBundle( HeadVersion(), - Version("2026-04-13", AddDagEndpoint), Version( - "2026-03-31", + "2026-04-06", + AddPartitionKeyField, + MovePreviousRunEndpoint, + AddDagRunDetailEndpoint, MakeDagRunStartDateNullable, ModifyDeferredTaskKwargsToJsonValue, RemoveUpstreamMapIndexesField, AddNoteField, + AddDagEndpoint, ), - Version("2025-12-08", MovePreviousRunEndpoint, AddDagRunDetailEndpoint), - Version("2025-11-07", AddPartitionKeyField), Version("2025-11-05", AddTriggeringUserNameField), Version("2025-10-27", MakeDagRunConfNullable), Version("2025-09-23", AddDagVersionIdField), diff --git a/airflow-core/src/airflow/api_fastapi/execution_api/versions/v2025_11_07.py b/airflow-core/src/airflow/api_fastapi/execution_api/versions/v2025_11_07.py deleted file mode 100644 index 117ba492455..00000000000 --- a/airflow-core/src/airflow/api_fastapi/execution_api/versions/v2025_11_07.py +++ /dev/null @@ -1,54 +0,0 @@ -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. - -from __future__ import annotations - -from cadwyn import ResponseInfo, VersionChange, convert_response_to_previous_version_for, schema - -from airflow.api_fastapi.execution_api.datamodels.asset_event import ( - AssetEventResponse, - AssetEventsResponse, - DagRunAssetReference, -) -from airflow.api_fastapi.execution_api.datamodels.dagrun import TriggerDAGRunPayload -from airflow.api_fastapi.execution_api.datamodels.taskinstance import DagRun, TIRunContext - - -class AddPartitionKeyField(VersionChange): - """Add the `partition_key` field to DagRun model.""" - - description = __doc__ - - instructions_to_migrate_to_previous_version = ( - schema(DagRun).field("partition_key").didnt_exist, - schema(AssetEventResponse).field("partition_key").didnt_exist, - schema(TriggerDAGRunPayload).field("partition_key").didnt_exist, - schema(DagRunAssetReference).field("partition_key").didnt_exist, - ) - - @convert_response_to_previous_version_for(TIRunContext) # type: ignore[arg-type] - def remove_partition_key_from_dag_run(response: ResponseInfo) -> None: # type: ignore[misc] - """Remove the `partition_key` field from the dag_run object when converting to the previous version.""" - if "dag_run" in response.body and isinstance(response.body["dag_run"], dict): - response.body["dag_run"].pop("partition_key", None) - - @convert_response_to_previous_version_for(AssetEventsResponse) # type: ignore[arg-type] - def remove_partition_key_from_asset_events(response: ResponseInfo) -> None: # type: ignore[misc] - """Remove the `partition_key` field from the dag_run object when converting to the previous version.""" - events = response.body["asset_events"] - for elem in events: - elem.pop("partition_key", None) diff --git a/airflow-core/src/airflow/api_fastapi/execution_api/versions/v2025_12_08.py b/airflow-core/src/airflow/api_fastapi/execution_api/versions/v2025_12_08.py deleted file mode 100644 index 3394036bf15..00000000000 --- a/airflow-core/src/airflow/api_fastapi/execution_api/versions/v2025_12_08.py +++ /dev/null @@ -1,41 +0,0 @@ -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. - -from __future__ import annotations - -from cadwyn import VersionChange, endpoint - - -class MovePreviousRunEndpoint(VersionChange): - """Add new previous-run endpoint and migrate old endpoint.""" - - description = __doc__ - - instructions_to_migrate_to_previous_version = ( - endpoint("/dag-runs/previous", ["GET"]).didnt_exist, - endpoint("/dag-runs/{dag_id}/previous", ["GET"]).existed, - ) - - -class AddDagRunDetailEndpoint(VersionChange): - """Add dag run detail endpoint.""" - - description = __doc__ - - instructions_to_migrate_to_previous_version = ( - endpoint("/dag-runs/{dag_id}/{run_id}", ["GET"]).didnt_exist, - ) diff --git a/airflow-core/src/airflow/api_fastapi/execution_api/versions/v2026_03_31.py b/airflow-core/src/airflow/api_fastapi/execution_api/versions/v2026_04_06.py similarity index 62% rename from airflow-core/src/airflow/api_fastapi/execution_api/versions/v2026_03_31.py rename to airflow-core/src/airflow/api_fastapi/execution_api/versions/v2026_04_06.py index 2d14493e81f..85ec1f2a608 100644 --- a/airflow-core/src/airflow/api_fastapi/execution_api/versions/v2026_03_31.py +++ b/airflow-core/src/airflow/api_fastapi/execution_api/versions/v2026_04_06.py @@ -19,9 +19,15 @@ from __future__ import annotations from typing import Any -from cadwyn import ResponseInfo, VersionChange, convert_response_to_previous_version_for, schema +from cadwyn import ResponseInfo, VersionChange, convert_response_to_previous_version_for, endpoint, schema from airflow.api_fastapi.common.types import UtcDateTime +from airflow.api_fastapi.execution_api.datamodels.asset_event import ( + AssetEventResponse, + AssetEventsResponse, + DagRunAssetReference, +) +from airflow.api_fastapi.execution_api.datamodels.dagrun import TriggerDAGRunPayload from airflow.api_fastapi.execution_api.datamodels.taskinstance import ( DagRun, TIDeferredStatePayload, @@ -29,6 +35,79 @@ from airflow.api_fastapi.execution_api.datamodels.taskinstance import ( ) +class AddPartitionKeyField(VersionChange): + """Add the `partition_key` field to DagRun model.""" + + description = __doc__ + + instructions_to_migrate_to_previous_version = ( + schema(DagRun).field("partition_key").didnt_exist, + schema(AssetEventResponse).field("partition_key").didnt_exist, + schema(TriggerDAGRunPayload).field("partition_key").didnt_exist, + schema(DagRunAssetReference).field("partition_key").didnt_exist, + ) + + @convert_response_to_previous_version_for(TIRunContext) # type: ignore[arg-type] + def remove_partition_key_from_dag_run(response: ResponseInfo) -> None: # type: ignore[misc] + """Remove the `partition_key` field from the dag_run object when converting to the previous version.""" + if "dag_run" in response.body and isinstance(response.body["dag_run"], dict): + response.body["dag_run"].pop("partition_key", None) + + @convert_response_to_previous_version_for(AssetEventsResponse) # type: ignore[arg-type] + def remove_partition_key_from_asset_events(response: ResponseInfo) -> None: # type: ignore[misc] + """Remove the `partition_key` field from the dag_run object when converting to the previous version.""" + events = response.body["asset_events"] + for elem in events: + elem.pop("partition_key", None) + + +class MovePreviousRunEndpoint(VersionChange): + """Add new previous-run endpoint and migrate old endpoint.""" + + description = __doc__ + + instructions_to_migrate_to_previous_version = ( + endpoint("/dag-runs/previous", ["GET"]).didnt_exist, + endpoint("/dag-runs/{dag_id}/previous", ["GET"]).existed, + ) + + +class AddDagRunDetailEndpoint(VersionChange): + """Add dag run detail endpoint.""" + + description = __doc__ + + instructions_to_migrate_to_previous_version = ( + endpoint("/dag-runs/{dag_id}/{run_id}", ["GET"]).didnt_exist, + ) + + +class MakeDagRunStartDateNullable(VersionChange): + """Make DagRun.start_date field nullable for runs that haven't started yet.""" + + description = __doc__ + + instructions_to_migrate_to_previous_version = (schema(DagRun).field("start_date").had(type=UtcDateTime),) + + @convert_response_to_previous_version_for(TIRunContext) # type: ignore[arg-type] + def ensure_start_date_in_ti_run_context(response: ResponseInfo) -> None: # type: ignore[misc] + """ + Ensure start_date is never None in DagRun for previous API versions. + + Older Task SDK clients expect start_date to be non-nullable. When the + DagRun hasn't started yet (e.g. queued), fall back to run_after. + """ + dag_run = response.body.get("dag_run") + if isinstance(dag_run, dict) and dag_run.get("start_date") is None: + dag_run["start_date"] = dag_run.get("run_after") + + @convert_response_to_previous_version_for(DagRun) # type: ignore[arg-type] + def ensure_start_date_in_dag_run(response: ResponseInfo) -> None: # type: ignore[misc] + """Ensure start_date is never None in direct DagRun responses for previous API versions.""" + if response.body.get("start_date") is None: + response.body["start_date"] = response.body.get("run_after") + + class ModifyDeferredTaskKwargsToJsonValue(VersionChange): """Change the types of `trigger_kwargs` and `next_kwargs` in TIDeferredStatePayload to JsonValue.""" @@ -71,27 +150,9 @@ class AddNoteField(VersionChange): response.body["dag_run"].pop("note", None) -class MakeDagRunStartDateNullable(VersionChange): - """Make DagRun.start_date field nullable for runs that haven't started yet.""" +class AddDagEndpoint(VersionChange): + """Add the `/dags/{dag_id}` endpoint.""" description = __doc__ - instructions_to_migrate_to_previous_version = (schema(DagRun).field("start_date").had(type=UtcDateTime),) - - @convert_response_to_previous_version_for(TIRunContext) # type: ignore[arg-type] - def ensure_start_date_in_ti_run_context(response: ResponseInfo) -> None: # type: ignore[misc] - """ - Ensure start_date is never None in DagRun for previous API versions. - - Older Task SDK clients expect start_date to be non-nullable. When the - DagRun hasn't started yet (e.g. queued), fall back to run_after. - """ - dag_run = response.body.get("dag_run") - if isinstance(dag_run, dict) and dag_run.get("start_date") is None: - dag_run["start_date"] = dag_run.get("run_after") - - @convert_response_to_previous_version_for(DagRun) # type: ignore[arg-type] - def ensure_start_date_in_dag_run(response: ResponseInfo) -> None: # type: ignore[misc] - """Ensure start_date is never None in direct DagRun responses for previous API versions.""" - if response.body.get("start_date") is None: - response.body["start_date"] = response.body.get("run_after") + instructions_to_migrate_to_previous_version = (endpoint("/dags/{dag_id}", ["GET"]).didnt_exist,) diff --git a/airflow-core/src/airflow/api_fastapi/execution_api/versions/v2026_04_13.py b/airflow-core/src/airflow/api_fastapi/execution_api/versions/v2026_04_13.py deleted file mode 100644 index 95da513d7bc..00000000000 --- a/airflow-core/src/airflow/api_fastapi/execution_api/versions/v2026_04_13.py +++ /dev/null @@ -1,28 +0,0 @@ -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. - -from __future__ import annotations - -from cadwyn import VersionChange, endpoint - - -class AddDagEndpoint(VersionChange): - """Add the `/dags/{dag_id}` endpoint.""" - - description = __doc__ - - instructions_to_migrate_to_previous_version = (endpoint("/dags/{dag_id}", ["GET"]).didnt_exist,) diff --git a/airflow-core/tests/unit/api_fastapi/execution_api/versions/head/test_dag_runs.py b/airflow-core/tests/unit/api_fastapi/execution_api/versions/head/test_dag_runs.py index 2aa3be51220..a2910313951 100644 --- a/airflow-core/tests/unit/api_fastapi/execution_api/versions/head/test_dag_runs.py +++ b/airflow-core/tests/unit/api_fastapi/execution_api/versions/head/test_dag_runs.py @@ -264,7 +264,7 @@ class TestDagRunDetail: def test_get_state(self, client, session, dag_maker): dag_id = "test_dag_id" # Named deliberately to check if this routes correctly. - # See v2025_11_07.test_dag_runs::test_get_previous_dag_run_redirect + # See v2026_04_06.test_dag_runs::test_get_previous_dag_run_redirect run_id = "previous" with dag_maker(dag_id=dag_id, schedule=None, session=session, serialized=True): diff --git a/airflow-core/tests/unit/api_fastapi/execution_api/versions/v2026_03_31/__init__.py b/airflow-core/tests/unit/api_fastapi/execution_api/versions/v2026_03_31/__init__.py deleted file mode 100644 index 13a83393a91..00000000000 --- a/airflow-core/tests/unit/api_fastapi/execution_api/versions/v2026_03_31/__init__.py +++ /dev/null @@ -1,16 +0,0 @@ -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. diff --git a/airflow-core/tests/unit/api_fastapi/execution_api/versions/v2025_11_07/__init__.py b/airflow-core/tests/unit/api_fastapi/execution_api/versions/v2026_04_06/__init__.py similarity index 100% rename from airflow-core/tests/unit/api_fastapi/execution_api/versions/v2025_11_07/__init__.py rename to airflow-core/tests/unit/api_fastapi/execution_api/versions/v2026_04_06/__init__.py diff --git a/airflow-core/tests/unit/api_fastapi/execution_api/versions/v2025_11_07/test_dag_runs.py b/airflow-core/tests/unit/api_fastapi/execution_api/versions/v2026_04_06/test_dag_runs.py similarity index 93% rename from airflow-core/tests/unit/api_fastapi/execution_api/versions/v2025_11_07/test_dag_runs.py rename to airflow-core/tests/unit/api_fastapi/execution_api/versions/v2026_04_06/test_dag_runs.py index c2821dce55b..a3c7ff77d14 100644 --- a/airflow-core/tests/unit/api_fastapi/execution_api/versions/v2025_11_07/test_dag_runs.py +++ b/airflow-core/tests/unit/api_fastapi/execution_api/versions/v2026_04_06/test_dag_runs.py @@ -27,7 +27,8 @@ pytestmark = pytest.mark.db_test @pytest.fixture def ver_client(client): - client.headers["Airflow-API-Version"] = "2025-11-07" + """Last released execution API (before 2026-04-06); uses legacy previous-run path.""" + client.headers["Airflow-API-Version"] = "2025-11-05" return client diff --git a/airflow-core/tests/unit/api_fastapi/execution_api/versions/v2026_03_31/test_dags.py b/airflow-core/tests/unit/api_fastapi/execution_api/versions/v2026_04_06/test_dags.py similarity index 89% rename from airflow-core/tests/unit/api_fastapi/execution_api/versions/v2026_03_31/test_dags.py rename to airflow-core/tests/unit/api_fastapi/execution_api/versions/v2026_04_06/test_dags.py index 72693443944..5fe7fb26fda 100644 --- a/airflow-core/tests/unit/api_fastapi/execution_api/versions/v2026_03_31/test_dags.py +++ b/airflow-core/tests/unit/api_fastapi/execution_api/versions/v2026_04_06/test_dags.py @@ -24,7 +24,8 @@ pytestmark = pytest.mark.db_test @pytest.fixture def old_ver_client(client): - client.headers["Airflow-API-Version"] = "2026-03-31" + """Last released execution API before `/dags/{dag_id}` was added.""" + client.headers["Airflow-API-Version"] = "2025-11-05" return client diff --git a/airflow-core/tests/unit/api_fastapi/execution_api/versions/v2026_03_31/test_task_instances.py b/airflow-core/tests/unit/api_fastapi/execution_api/versions/v2026_04_06/test_task_instances.py similarity index 96% rename from airflow-core/tests/unit/api_fastapi/execution_api/versions/v2026_03_31/test_task_instances.py rename to airflow-core/tests/unit/api_fastapi/execution_api/versions/v2026_04_06/test_task_instances.py index 7fb44ce7ebe..8117ac6b69c 100644 --- a/airflow-core/tests/unit/api_fastapi/execution_api/versions/v2026_03_31/test_task_instances.py +++ b/airflow-core/tests/unit/api_fastapi/execution_api/versions/v2026_04_06/test_task_instances.py @@ -40,8 +40,8 @@ RUN_PATCH_BODY = { @pytest.fixture def old_ver_client(client): - """Client configured to use API version before start_date nullable change.""" - client.headers["Airflow-API-Version"] = "2025-12-08" + """Last released execution API before nullable DagRun.start_date (2026-04-06 bundle).""" + client.headers["Airflow-API-Version"] = "2025-11-05" return client diff --git a/task-sdk/src/airflow/sdk/api/datamodels/_generated.py b/task-sdk/src/airflow/sdk/api/datamodels/_generated.py index b6c08e9d76c..e08f00562d3 100644 --- a/task-sdk/src/airflow/sdk/api/datamodels/_generated.py +++ b/task-sdk/src/airflow/sdk/api/datamodels/_generated.py @@ -27,7 +27,7 @@ from uuid import UUID from pydantic import AwareDatetime, BaseModel, ConfigDict, Field, JsonValue, RootModel -API_VERSION: Final[str] = "2026-04-13" +API_VERSION: Final[str] = "2026-04-06" class AssetAliasReferenceAssetEventDagRun(BaseModel):
