Repository: googleapis/google-cloud-python

Issue:
BigQuery: Add support for use_avro_logical_types for extract jobs
use_avro_logical_types is supported for load jobs: https://github.com/googleapis/google-cloud-python/blob/89eaedb4a40bd1268df134cc40a4063d6cf8b823/bigquery/google/cloud/bigquery/job.py#L1318

We should add the same mapping for extract jobs. BigQuery already supports it in the API backend.


Discussion:

Resolving patch:
diff --git a/bigquery/google/cloud/bigquery/job.py b/bigquery/google/cloud/bigquery/job.py
--- a/bigquery/google/cloud/bigquery/job.py
+++ b/bigquery/google/cloud/bigquery/job.py
@@ -1317,7 +1317,7 @@ def time_partitioning(self, value):
     @property
     def use_avro_logical_types(self):
         """bool: For loads of Avro data, governs whether Avro logical types are
-        converted to their corresponding BigQuery types(e.g. TIMESTAMP) rather than
+        converted to their corresponding BigQuery types (e.g. TIMESTAMP) rather than
         raw types (e.g. INTEGER).
         """
         return self._get_sub_prop("useAvroLogicalTypes")
@@ -1910,6 +1910,18 @@ def print_header(self):
     def print_header(self, value):
         self._set_sub_prop("printHeader", value)
 
+    @property
+    def use_avro_logical_types(self):
+        """bool: For loads of Avro data, governs whether Avro logical types are
+        converted to their corresponding BigQuery types (e.g. TIMESTAMP) rather than
+        raw types (e.g. INTEGER).
+        """
+        return self._get_sub_prop("useAvroLogicalTypes")
+
+    @use_avro_logical_types.setter
+    def use_avro_logical_types(self, value):
+        self._set_sub_prop("useAvroLogicalTypes", bool(value))
+
 
 class ExtractJob(_AsyncJob):
     """Asynchronous job: extract data from a table into Cloud Storage.


Test patch:
diff --git a/bigquery/tests/unit/test_job.py b/bigquery/tests/unit/test_job.py
--- a/bigquery/tests/unit/test_job.py
+++ b/bigquery/tests/unit/test_job.py
@@ -2996,6 +2996,7 @@ def test_to_api_repr(self):
         config.field_delimiter = "ignored for avro"
         config.print_header = False
         config._properties["extract"]["someNewField"] = "some-value"
+        config.use_avro_logical_types = True
         resource = config.to_api_repr()
         self.assertEqual(
             resource,
@@ -3006,6 +3007,7 @@ def test_to_api_repr(self):
                     "fieldDelimiter": "ignored for avro",
                     "printHeader": False,
                     "someNewField": "some-value",
+                    "useAvroLogicalTypes": True,
                 }
             },
         )
@@ -3020,6 +3022,7 @@ def test_from_api_repr(self):
                     "fieldDelimiter": "\t",
                     "printHeader": True,
                     "someNewField": "some-value",
+                    "useAvroLogicalTypes": False,
                 }
             }
         )
@@ -3028,6 +3031,7 @@ def test_from_api_repr(self):
         self.assertEqual(config.field_delimiter, "\t")
         self.assertEqual(config.print_header, True)
         self.assertEqual(config._properties["extract"]["someNewField"], "some-value")
+        self.assertEqual(config.use_avro_logical_types, False)
 
 
 class TestExtractJob(unittest.TestCase, _Base):