This is an automated email from the ASF dual-hosted git repository.
kaxil 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 d0450666eb3 Remove a Dag Run or Task Instance note when its content is
cleared (#69033)
d0450666eb3 is described below
commit d0450666eb377f8f1cfad0d7addfbffa61768da8
Author: Pierre Jeambrun <[email protected]>
AuthorDate: Sat Jun 27 02:21:08 2026 +0200
Remove a Dag Run or Task Instance note when its content is cleared (#69033)
There was no way to remove a note from the UI: emptying the text persisted
an empty note and the Dag Run or Task Instance stayed flagged as having one.
An empty note now unsets it, while a null note keeps leaving the existing
note untouched so the bulk and clear flows are unaffected.
---
.../api_fastapi/core_api/services/public/dag_run.py | 6 ++++--
.../core_api/services/public/task_instances.py | 4 +++-
.../api_fastapi/core_api/routes/public/test_dag_run.py | 16 ++++++++++++++--
.../core_api/routes/public/test_task_instances.py | 14 ++++++++++++++
4 files changed, 35 insertions(+), 5 deletions(-)
diff --git
a/airflow-core/src/airflow/api_fastapi/core_api/services/public/dag_run.py
b/airflow-core/src/airflow/api_fastapi/core_api/services/public/dag_run.py
index 6d43d18cd34..186cd3671c6 100644
--- a/airflow-core/src/airflow/api_fastapi/core_api/services/public/dag_run.py
+++ b/airflow-core/src/airflow/api_fastapi/core_api/services/public/dag_run.py
@@ -212,8 +212,10 @@ def patch_dag_run_state(
def patch_dag_run_note(*, dag_run: DagRun, note: str | None, user: BaseUser)
-> None:
- """Set or update a Dag Run's note."""
- if dag_run.dag_run_note is None:
+ """Set, update, or clear a Dag Run's note. An empty note removes it so the
run is left without a note."""
+ if note == "":
+ dag_run.dag_run_note = None
+ elif dag_run.dag_run_note is None:
dag_run.note = (note, user.get_id())
else:
dag_run.dag_run_note.content = note
diff --git
a/airflow-core/src/airflow/api_fastapi/core_api/services/public/task_instances.py
b/airflow-core/src/airflow/api_fastapi/core_api/services/public/task_instances.py
index a5874035e27..24ae587680a 100644
---
a/airflow-core/src/airflow/api_fastapi/core_api/services/public/task_instances.py
+++
b/airflow-core/src/airflow/api_fastapi/core_api/services/public/task_instances.py
@@ -315,7 +315,9 @@ def _patch_task_instance_note(
) -> None:
for ti in tis:
if update_mask or task_instance_body.note is not None:
- if ti.task_instance_note is None:
+ if task_instance_body.note == "":
+ ti.task_instance_note = None
+ elif ti.task_instance_note is None:
ti.note = (task_instance_body.note, user.get_id())
else:
ti.task_instance_note.content = task_instance_body.note
diff --git
a/airflow-core/tests/unit/api_fastapi/core_api/routes/public/test_dag_run.py
b/airflow-core/tests/unit/api_fastapi/core_api/routes/public/test_dag_run.py
index 39a391f80f1..fe7c2739de2 100644
--- a/airflow-core/tests/unit/api_fastapi/core_api/routes/public/test_dag_run.py
+++ b/airflow-core/tests/unit/api_fastapi/core_api/routes/public/test_dag_run.py
@@ -1440,6 +1440,13 @@ class TestPatchDagRun:
{"state": DagRunState.SUCCESS, "note": "updated note"},
{"user_id": "test", "content": "updated note"},
),
+ (
+ DAG1_ID,
+ DAG1_RUN1_ID,
+ {"note": ""},
+ {"state": DagRunState.SUCCESS, "note": None},
+ None,
+ ),
(
DAG1_ID,
DAG1_RUN2_ID,
@@ -1758,11 +1765,16 @@ class TestClearDagRun:
("body", "expected_note"),
[
({"dry_run": False, "note": "cleared by test"}, "cleared by test"),
- ({"dry_run": False, "note": ""}, ""),
+ ({"dry_run": False, "note": ""}, None),
({"dry_run": False, "note": None}, "test_note"),
({"dry_run": False}, "test_note"),
],
- ids=["set-new-note", "set-empty-note",
"explicit-null-leaves-existing", "omit-leaves-existing"],
+ ids=[
+ "set-new-note",
+ "empty-note-removes-existing",
+ "explicit-null-leaves-existing",
+ "omit-leaves-existing",
+ ],
)
@pytest.mark.usefixtures("configure_git_connection_for_dag_bundle")
def test_clear_dag_run_applies_note(self, test_client, session, body,
expected_note):
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 40337bb9be7..11f69edb6f6 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
@@ -5128,6 +5128,20 @@ class TestPatchTaskInstance(TestTaskInstanceEndpoint):
session, response_data["task_instances"][0]["id"], {"content":
new_note_value, "user_id": "test"}
)
+ def test_set_empty_note_removes_existing_note(self, test_client, session):
+ self.create_task_instances(session)
+ url =
"/dags/example_python_operator/dagRuns/TEST_DAG_RUN_ID/taskInstances/print_the_context"
+
+ set_response = test_client.patch(url, json={"note": "a note to
remove"})
+ assert set_response.status_code == 200, set_response.text
+ ti_id = set_response.json()["task_instances"][0]["id"]
+ _check_task_instance_note(session, ti_id, {"content": "a note to
remove", "user_id": "test"})
+
+ clear_response = test_client.patch(url, json={"note": ""})
+ assert clear_response.status_code == 200, clear_response.text
+ assert clear_response.json()["task_instances"][0]["note"] is None
+ _check_task_instance_note(session, ti_id, None)
+
def test_set_note_should_respond_200_mapped_task_with_rtif(self,
test_client, session):
"""Verify we don't duplicate rows through join to RTIF"""
tis = self.create_task_instances(session)