This is an automated email from the ASF dual-hosted git repository.

jasonliu 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 a1ba629c1b9 [v3-0-test] Don't create audit log for API calls with 
dry_run=True. (#55116) (#55188)
a1ba629c1b9 is described below

commit a1ba629c1b921a6d3bd9ed571fff2721fb8a9f22
Author: Karthikeyan Singaravelan <[email protected]>
AuthorDate: Wed Sep 3 09:29:38 2025 +0530

    [v3-0-test] Don't create audit log for API calls with dry_run=True. 
(#55116) (#55188)
    
    * Don't create audit log for API calls with dry_run=True.
    
    * Skip audit_log only for clear task instance and dag run dry runs.
    (cherry picked from commit bdc61474938554945892fd9ce1954a87aa3aad95)
---
 .../src/airflow/api_fastapi/logging/decorators.py  |  4 +++
 .../core_api/routes/public/test_dag_run.py         |  9 +++++-
 .../core_api/routes/public/test_task_instances.py  | 35 ++++++++++++++++++++--
 3 files changed, 45 insertions(+), 3 deletions(-)

diff --git a/airflow-core/src/airflow/api_fastapi/logging/decorators.py 
b/airflow-core/src/airflow/api_fastapi/logging/decorators.py
index ad65c8caac8..01e57ad6e5b 100644
--- a/airflow-core/src/airflow/api_fastapi/logging/decorators.py
+++ b/airflow-core/src/airflow/api_fastapi/logging/decorators.py
@@ -83,6 +83,7 @@ def action_logging(event: str | None = None):
     ):
         """Log user actions."""
         event_name = event or request.scope["endpoint"].__name__
+        skip_dry_run_events = {"clear_dag_run", "post_clear_task_instances"}
 
         if not user:
             user_name = "anonymous"
@@ -100,6 +101,9 @@ def action_logging(event: str | None = None):
             request_body = {}
             masked_body_json = {}
 
+        if event_name in skip_dry_run_events and request_body.get("dry_run", 
True):
+            return
+
         fields_skip_logging = {
             "csrf_token",
             "_csrf_token",
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 4ab197c2f59..dc9fc2a4596 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
@@ -27,7 +27,7 @@ from sqlalchemy import select
 
 from airflow.api_fastapi.core_api.datamodels.dag_versions import 
DagVersionResponse
 from airflow.listeners.listener import get_listener_manager
-from airflow.models import DagModel, DagRun
+from airflow.models import DagModel, DagRun, Log
 from airflow.models.asset import AssetEvent, AssetModel
 from airflow.providers.standard.operators.empty import EmptyOperator
 from airflow.sdk.definitions.asset import Asset
@@ -1208,6 +1208,13 @@ class TestClearDagRun:
         dag_run = session.scalar(select(DagRun).filter_by(dag_id=DAG1_ID, 
run_id=DAG1_RUN1_ID))
         assert dag_run.state == DAG1_RUN1_STATE
 
+        logs = (
+            session.query(Log)
+            .filter(Log.dag_id == DAG1_ID, Log.run_id == dag_run_id, Log.event 
== "clear_dag_run")
+            .count()
+        )
+        assert logs == 0
+
     def test_clear_dag_run_not_found(self, test_client):
         response = test_client.post(f"/dags/{DAG1_ID}/dagRuns/invalid/clear", 
json={"dry_run": False})
         assert response.status_code == 404
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 c0cdffd56b3..52f83780449 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
@@ -31,7 +31,7 @@ from airflow.dag_processing.bundles.manager import 
DagBundlesManager
 from airflow.jobs.job import Job
 from airflow.jobs.triggerer_job_runner import TriggererJobRunner
 from airflow.listeners.listener import get_listener_manager
-from airflow.models import DagRun, TaskInstance
+from airflow.models import DagRun, Log, TaskInstance
 from airflow.models.baseoperator import BaseOperator
 from airflow.models.dagbag import DagBag
 from airflow.models.renderedtifields import RenderedTaskInstanceFields as RTIF
@@ -2170,7 +2170,9 @@ class 
TestPostClearTaskInstances(TestTaskInstanceEndpoint):
         )
         assert response.status_code == 200
         assert response.json()["total_entries"] == expected_ti
-        check_last_log(session, dag_id=request_dag, 
event="post_clear_task_instances", logical_date=None)
+
+        if not payload.get("dry_run", True):
+            check_last_log(session, dag_id=request_dag, 
event="post_clear_task_instances", logical_date=None)
 
     @pytest.mark.parametrize("flag", ["include_future", "include_past"])
     def test_dag_run_with_future_or_past_flag_returns_400(self, test_client, 
session, flag):
@@ -2764,6 +2766,35 @@ class 
TestPostClearTaskInstances(TestTaskInstanceEndpoint):
         assert response.status_code == 404
         assert "DAG non-existent-dag not found" in response.text
 
+    @pytest.mark.parametrize(
+        "dry_run, audit_log_count",
+        [
+            (True, 0),
+            (False, 1),
+        ],
+    )
+    def test_dry_run_audit_log(self, test_client, session, dry_run, 
audit_log_count):
+        dag_id = "example_python_operator"
+        dag_run_id = "TEST_DAG_RUN_ID"
+        event = "post_clear_task_instances"
+
+        payload = {"dry_run": dry_run, "dag_run_id": dag_run_id}
+        self.create_task_instances(session, dag_id)
+
+        response = test_client.post(
+            f"/dags/{dag_id}/clearTaskInstances",
+            json=payload,
+        )
+
+        logs = (
+            session.query(Log)
+            .filter(Log.dag_id == dag_id, Log.run_id == dag_run_id, Log.event 
== event)
+            .count()
+        )
+
+        assert response.status_code == 200
+        assert logs == audit_log_count
+
 
 class TestGetTaskInstanceTries(TestTaskInstanceEndpoint):
     def test_should_respond_200(self, test_client, session):

Reply via email to