SameerMesiah97 commented on code in PR #71675:
URL: https://github.com/apache/airflow/pull/71675#discussion_r3792288071


##########
providers/google/tests/unit/google/cloud/transfers/test_gcs_to_bigquery.py:
##########
@@ -2124,6 +2131,110 @@ def test_schema_fields_is_templated(self):
 
         assert operator.schema_fields == SCHEMA_FIELDS
 
+    @pytest.mark.parametrize(
+        ("operator_kwargs", "expects_warning"),
+        [
+            pytest.param({"ignore_unknown_values": True}, True, 
id="autodetect_defaults_to_true"),
+            pytest.param({"ignore_unknown_values": True, "autodetect": True}, 
True, id="autodetect_true"),
+            pytest.param({"ignore_unknown_values": True, "autodetect": None}, 
False, id="autodetect_none"),
+            pytest.param(
+                {"ignore_unknown_values": True, "schema_fields": 
SCHEMA_FIELDS},
+                False,
+                id="schema_fields_supplied",
+            ),
+            pytest.param({"ignore_unknown_values": False}, False, 
id="ignore_unknown_values_off"),
+            pytest.param(
+                {"ignore_unknown_values": True, "extra_config": {"autodetect": 
None}},
+                False,
+                id="autodetect_cleared_by_extra_config",
+            ),
+            pytest.param(
+                {"ignore_unknown_values": True, "extra_config": {"schema": 
{"fields": SCHEMA_FIELDS}}},
+                False,
+                id="schema_supplied_by_extra_config",
+            ),
+            pytest.param(
+                {"ignore_unknown_values": False, "extra_config": 
{"ignoreUnknownValues": True}},
+                True,
+                id="ignore_unknown_values_set_by_extra_config",
+            ),
+        ],
+    )

Review Comment:
   Since autodetect is explicitly a three-state parameter, could we also cover 
`autodetect=False `here?



##########
providers/google/src/airflow/providers/google/cloud/transfers/gcs_to_bigquery.py:
##########
@@ -162,12 +164,21 @@ class GCSToBigQueryOperator(BaseOperator):
         by one or more columns. BigQuery supports clustering for both 
partitioned and
         non-partitioned tables. The order of columns given determines the sort 
order.
         Not applicable for external tables.
-    :param autodetect: [Optional] Indicates if we should automatically infer 
the
-        options and schema for CSV and JSON sources. (Default: ``True``).
-        Parameter must be set to True if 'schema_fields' and 'schema_object' 
are undefined.
-        It is suggested to set to True if table are create outside of Airflow.
-        If autodetect is None and no schema is provided (neither via 
schema_fields
-        nor a schema_object), assume the table already exists.
+    :param autodetect: [Optional] Indicates whether the options and schema 
should be inferred
+        from the source data for CSV and JSON sources. (Default: ``True``). 
This parameter is
+        three-state:
+
+        * ``True`` - infer the schema from the source files. The inferred 
schema is sent to
+          BigQuery as the load job's schema, so ``ignore_unknown_values`` has 
no effect: no field
+          in the source data can be unknown to a schema derived from that same 
data. If the
+          destination table already exists and the inferred schema differs 
from it, the load
+          fails with a schema mismatch unless ``schema_update_options`` 
permits the change.
+        * ``False`` - do not infer the schema. One of ``schema_fields`` or 
``schema_object``
+          must be provided, otherwise an exception is raised.
+        * ``None`` - neither infer a schema nor supply one, so the destination 
table's own
+          schema is used. The table must already exist. This is the setting to 
use when you want
+          ``ignore_unknown_values`` to drop fields that are present in the 
source data but absent
+          from the destination table.

Review Comment:
   This is too long. There is no need to go deep into the interaction between 
this and the other parameters here. I would suggest the below instead:
   
   `:param autodetect: [Optional] Whether to infer the schema from the source 
data for CSV and JSON sources. If ``True``, the schema is inferred from the 
source data. If ``False``, either ``schema_fields`` or ``schema_object`` must 
be provided. If ``None``, no schema is supplied and the existing destination 
table's schema is used. (Default: ``True``).`



##########
providers/google/src/airflow/providers/google/cloud/transfers/gcs_to_bigquery.py:
##########
@@ -638,6 +649,23 @@ def _create_external_table(self):
         self.log.info("External table created successfully: %s", 
self.destination_project_dataset_table)
         return table_obj_api_repr
 
+    def _warn_if_ignore_unknown_values_is_ineffective(self) -> None:
+        # Checked against the assembled load config rather than the operator 
attributes, because
+        # src_fmt_configs and extra_config can still override any of these 
three keys.
+        load_config = self.configuration["load"]
+        if (
+            load_config.get("autodetect")
+            and load_config.get("ignoreUnknownValues")
+            and "schema" not in load_config
+        ):
+            self.log.warning(
+                "`ignore_unknown_values` is set but will have no effect. With 
`autodetect` enabled "
+                "and no schema supplied, the load job's schema is inferred 
from the source data "
+                "itself, so no source field can be unknown to it. To drop 
source fields that are "
+                "absent from an existing destination table, set 
`autodetect=None` so that the "
+                "destination table's own schema is used instead."
+            )

Review Comment:
   1) This helper is not needed as it is only called once. I would inline it. 
   2) The warning is too verbose. I would suggest the below:
   
   ```
   self.log.warning(
       "`ignore_unknown_values` has no effect when `autodetect=True` and no 
schema is provided. "
       "Set `autodetect=None` to use the existing destination table's schema 
instead."
   )
   ```



##########
providers/google/tests/unit/google/cloud/transfers/test_gcs_to_bigquery.py:
##########
@@ -2124,6 +2131,110 @@ def test_schema_fields_is_templated(self):
 
         assert operator.schema_fields == SCHEMA_FIELDS
 
+    @pytest.mark.parametrize(
+        ("operator_kwargs", "expects_warning"),
+        [
+            pytest.param({"ignore_unknown_values": True}, True, 
id="autodetect_defaults_to_true"),
+            pytest.param({"ignore_unknown_values": True, "autodetect": True}, 
True, id="autodetect_true"),
+            pytest.param({"ignore_unknown_values": True, "autodetect": None}, 
False, id="autodetect_none"),
+            pytest.param(
+                {"ignore_unknown_values": True, "schema_fields": 
SCHEMA_FIELDS},
+                False,
+                id="schema_fields_supplied",
+            ),
+            pytest.param({"ignore_unknown_values": False}, False, 
id="ignore_unknown_values_off"),
+            pytest.param(
+                {"ignore_unknown_values": True, "extra_config": {"autodetect": 
None}},
+                False,
+                id="autodetect_cleared_by_extra_config",
+            ),
+            pytest.param(
+                {"ignore_unknown_values": True, "extra_config": {"schema": 
{"fields": SCHEMA_FIELDS}}},
+                False,
+                id="schema_supplied_by_extra_config",
+            ),
+            pytest.param(
+                {"ignore_unknown_values": False, "extra_config": 
{"ignoreUnknownValues": True}},
+                True,
+                id="ignore_unknown_values_set_by_extra_config",
+            ),
+        ],
+    )
+    @mock.patch(GCS_TO_BQ_PATH.format("BigQueryHook"))
+    def test_ignore_unknown_values_no_op_warning(self, bq_hook, 
operator_kwargs, expects_warning):
+        bq_hook.return_value.insert_job.side_effect = [
+            MagicMock(job_id=REAL_JOB_ID, error_result=False),
+            REAL_JOB_ID,
+        ]
+        bq_hook.return_value.generate_job_id.return_value = REAL_JOB_ID
+        bq_hook.return_value.split_tablename.return_value = (PROJECT_ID, 
DATASET, TABLE)
+
+        operator = GCSToBigQueryOperator(
+            task_id=TASK_ID,
+            bucket=TEST_BUCKET,
+            source_objects=TEST_SOURCE_OBJECTS,
+            destination_project_dataset_table=TEST_EXPLICIT_DEST,
+            write_disposition=WRITE_DISPOSITION,
+            project_id=JOB_PROJECT_ID,
+            **operator_kwargs,
+        )
+
+        with mock.patch.object(operator.log, "warning") as mock_warning:
+            operator.execute(context=MagicMock())
+
+        assert 
mock_warning.call_args_list.count(call(IGNORE_UNKNOWN_VALUES_WARNING)) == (
+            1 if expects_warning else 0
+        )

Review Comment:
   Do these parametrized cases need to assert the complete warning text? The 
behaviour under test seems to be whether the warning is emitted. Using 
`assert_called_once() / assert_not_called()` would make these cases less 
coupled to the wording, with the message itself asserted in a single test if we 
want to protect it.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to