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

shahar1 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 26d3bbb6cfc Derive DbtCloudGetJobRunArtifactOperator default file name 
after rendering (#70336)
26d3bbb6cfc is described below

commit 26d3bbb6cfc3a472e01f79e0c1da89bfe6dfe685
Author: Stefan Wang <[email protected]>
AuthorDate: Fri Jul 24 00:54:48 2026 -0700

    Derive DbtCloudGetJobRunArtifactOperator default file name after rendering 
(#70336)
    
    * Derive DbtCloudGetJobRunArtifactOperator default file name after rendering
    
    run_id, path and output_file_name are template fields, rendered after 
__init__
    runs. The constructor built the default output_file_name from self.run_id 
and
    self.path (the un-rendered Jinja expressions) and flattened slashes then. 
With a
    templated path, the '/'->'-' replacement ran on the expression, not the 
resolved
    path, so a rendered path like 'a/b/c.json' kept its slashes in the file 
name.
    Derive the default in execute(), after rendering.
    
    related: #70296
---
 .../airflow/providers/dbt/cloud/operators/dbt.py    |  6 +++++-
 .../tests/unit/dbt/cloud/operators/test_dbt.py      | 21 +++++++++++++++++++++
 .../ci/prek/validate_operators_init_exemptions.txt  |  1 -
 3 files changed, 26 insertions(+), 2 deletions(-)

diff --git 
a/providers/dbt/cloud/src/airflow/providers/dbt/cloud/operators/dbt.py 
b/providers/dbt/cloud/src/airflow/providers/dbt/cloud/operators/dbt.py
index 6547fadb0f9..b38d9a0f3cd 100644
--- a/providers/dbt/cloud/src/airflow/providers/dbt/cloud/operators/dbt.py
+++ b/providers/dbt/cloud/src/airflow/providers/dbt/cloud/operators/dbt.py
@@ -410,10 +410,14 @@ class DbtCloudGetJobRunArtifactOperator(BaseOperator):
         self.path = path
         self.account_id = account_id
         self.step = step
-        self.output_file_name = output_file_name or 
f"{self.run_id}_{self.path}".replace("/", "-")
+        self.output_file_name = output_file_name
         self.hook_params = hook_params or {}
 
     def execute(self, context: Context) -> str:
+        # run_id/path are template fields; derive the default name after 
rendering so it uses the
+        # resolved values, not the Jinja expressions.
+        if not self.output_file_name:
+            self.output_file_name = f"{self.run_id}_{self.path}".replace("/", 
"-")
         hook = DbtCloudHook(self.dbt_cloud_conn_id, **self.hook_params)
         response = hook.get_job_run_artifact(
             run_id=self.run_id, path=self.path, account_id=self.account_id, 
step=self.step
diff --git a/providers/dbt/cloud/tests/unit/dbt/cloud/operators/test_dbt.py 
b/providers/dbt/cloud/tests/unit/dbt/cloud/operators/test_dbt.py
index bb5988acdea..67d3fc33100 100644
--- a/providers/dbt/cloud/tests/unit/dbt/cloud/operators/test_dbt.py
+++ b/providers/dbt/cloud/tests/unit/dbt/cloud/operators/test_dbt.py
@@ -1058,6 +1058,27 @@ class TestDbtCloudGetJobRunArtifactOperator:
         assert os.path.exists(tmp_path / operator.output_file_name)
         assert return_value == operator.output_file_name
 
+    
@patch("airflow.providers.dbt.cloud.hooks.dbt.DbtCloudHook.get_job_run_artifact")
+    def test_default_output_file_name_uses_rendered_path(self, 
mock_get_artifact, tmp_path, monkeypatch):
+        # The default name flattens "/"->"-", so a templated path must resolve 
before that.
+        operator = DbtCloudGetJobRunArtifactOperator(
+            task_id=TASK_ID,
+            dbt_cloud_conn_id=ACCOUNT_ID_CONN,
+            run_id=RUN_ID,
+            path="{{ params.p }}",
+            params={"p": "path/to/my/manifest.json"},
+            dag=self.dag,
+        )
+        operator.render_template_fields({"params": {"p": 
"path/to/my/manifest.json"}})
+
+        mock_get_artifact.return_value.json.return_value = {"data": "file 
contents"}
+        with monkeypatch.context() as ctx:
+            ctx.chdir(tmp_path)
+            return_value = operator.execute(context={})
+
+        assert operator.output_file_name == 
f"{RUN_ID}_path-to-my-manifest.json"
+        assert return_value == operator.output_file_name
+
     
@patch("airflow.providers.dbt.cloud.hooks.dbt.DbtCloudHook.get_job_run_artifact")
     @pytest.mark.parametrize(
         ("conn_id", "account_id"),
diff --git a/scripts/ci/prek/validate_operators_init_exemptions.txt 
b/scripts/ci/prek/validate_operators_init_exemptions.txt
index ebf6526cc10..c2e43f97a97 100644
--- a/scripts/ci/prek/validate_operators_init_exemptions.txt
+++ b/scripts/ci/prek/validate_operators_init_exemptions.txt
@@ -34,7 +34,6 @@ 
providers/databricks/src/airflow/providers/databricks/operators/databricks_repos
 
providers/databricks/src/airflow/providers/databricks/operators/databricks_repos.py::DatabricksReposUpdateOperator
 
providers/databricks/src/airflow/providers/databricks/operators/databricks_sql.py::DatabricksCopyIntoOperator
 
providers/databricks/src/airflow/providers/databricks/sensors/databricks.py::DatabricksSQLStatementsSensor
-providers/dbt/cloud/src/airflow/providers/dbt/cloud/operators/dbt.py::DbtCloudGetJobRunArtifactOperator
 
providers/docker/src/airflow/providers/docker/operators/docker.py::DockerOperator
 
providers/google/src/airflow/providers/google/cloud/operators/bigquery.py::BigQueryInsertJobOperator
 
providers/google/src/airflow/providers/google/cloud/operators/cloud_batch.py::CloudBatchSubmitJobOperator

Reply via email to