Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit eff365d

Browse filesBrowse files
authored
feat: support data_governance_type (#1708)
* feat: support data_governance_type * remove value validation, add sys test
1 parent 386fa86 commit eff365d
Copy full SHA for eff365d

File tree

Expand file treeCollapse file tree

3 files changed

+105
-2
lines changed
Open diff view settings
Filter options
Expand file treeCollapse file tree

3 files changed

+105
-2
lines changed
Open diff view settings
Collapse file

‎google/cloud/bigquery/routine/routine.py‎

Copy file name to clipboardExpand all lines: google/cloud/bigquery/routine/routine.py
+22-2Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ class Routine(object):
6868
"description": "description",
6969
"determinism_level": "determinismLevel",
7070
"remote_function_options": "remoteFunctionOptions",
71+
"data_governance_type": "dataGovernanceType",
7172
}
7273

7374
def __init__(self, routine_ref, **kwargs) -> None:
@@ -300,8 +301,8 @@ def determinism_level(self, value):
300301

301302
@property
302303
def remote_function_options(self):
303-
"""Optional[google.cloud.bigquery.routine.RemoteFunctionOptions]: Configures remote function
304-
options for a routine.
304+
"""Optional[google.cloud.bigquery.routine.RemoteFunctionOptions]:
305+
Configures remote function options for a routine.
305306
306307
Raises:
307308
ValueError:
@@ -329,6 +330,25 @@ def remote_function_options(self, value):
329330
self._PROPERTY_TO_API_FIELD["remote_function_options"]
330331
] = api_repr
331332

333+
@property
334+
def data_governance_type(self):
335+
"""Optional[str]: If set to ``DATA_MASKING``, the function is validated
336+
and made available as a masking function.
337+
338+
Raises:
339+
ValueError:
340+
If the value is not :data:`string` or :data:`None`.
341+
"""
342+
return self._properties.get(self._PROPERTY_TO_API_FIELD["data_governance_type"])
343+
344+
@data_governance_type.setter
345+
def data_governance_type(self, value):
346+
if value is not None and not isinstance(value, str):
347+
raise ValueError(
348+
"invalid data_governance_type, must be a string or `None`."
349+
)
350+
self._properties[self._PROPERTY_TO_API_FIELD["data_governance_type"]] = value
351+
332352
@classmethod
333353
def from_api_repr(cls, resource: dict) -> "Routine":
334354
"""Factory: construct a routine given its API representation.
Collapse file

‎tests/system/test_client.py‎

Copy file name to clipboardExpand all lines: tests/system/test_client.py
+36Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
# limitations under the License.
1414

1515
import base64
16+
import copy
1617
import csv
1718
import datetime
1819
import decimal
@@ -2236,6 +2237,41 @@ def test_create_tvf_routine(self):
22362237
]
22372238
assert result_rows == expected
22382239

2240+
def test_create_routine_w_data_governance(self):
2241+
routine_name = "routine_with_data_governance"
2242+
dataset = self.temp_dataset(_make_dataset_id("create_routine"))
2243+
2244+
routine = bigquery.Routine(
2245+
dataset.routine(routine_name),
2246+
type_="SCALAR_FUNCTION",
2247+
language="SQL",
2248+
body="x",
2249+
arguments=[
2250+
bigquery.RoutineArgument(
2251+
name="x",
2252+
data_type=bigquery.StandardSqlDataType(
2253+
type_kind=bigquery.StandardSqlTypeNames.INT64
2254+
),
2255+
)
2256+
],
2257+
data_governance_type="DATA_MASKING",
2258+
return_type=bigquery.StandardSqlDataType(
2259+
type_kind=bigquery.StandardSqlTypeNames.INT64
2260+
),
2261+
)
2262+
routine_original = copy.deepcopy(routine)
2263+
2264+
client = Config.CLIENT
2265+
routine_new = client.create_routine(routine)
2266+
2267+
assert routine_new.reference == routine_original.reference
2268+
assert routine_new.type_ == routine_original.type_
2269+
assert routine_new.language == routine_original.language
2270+
assert routine_new.body == routine_original.body
2271+
assert routine_new.arguments == routine_original.arguments
2272+
assert routine_new.return_type == routine_original.return_type
2273+
assert routine_new.data_governance_type == routine_original.data_governance_type
2274+
22392275
def test_create_table_rows_fetch_nested_schema(self):
22402276
table_name = "test_table"
22412277
dataset = self.temp_dataset(_make_dataset_id("create_table_nested_schema"))
Collapse file

‎tests/unit/routine/test_routine.py‎

Copy file name to clipboardExpand all lines: tests/unit/routine/test_routine.py
+47Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,7 @@ def test_from_api_repr(target_class):
154154
"foo": "bar",
155155
},
156156
},
157+
"dataGovernanceType": "DATA_MASKING",
157158
}
158159
actual_routine = target_class.from_api_repr(resource)
159160

@@ -192,6 +193,7 @@ def test_from_api_repr(target_class):
192193
assert actual_routine.remote_function_options.connection == "connection_string"
193194
assert actual_routine.remote_function_options.max_batching_rows == 50
194195
assert actual_routine.remote_function_options.user_defined_context == {"foo": "bar"}
196+
assert actual_routine.data_governance_type == "DATA_MASKING"
195197

196198

197199
def test_from_api_repr_tvf_function(target_class):
@@ -294,6 +296,7 @@ def test_from_api_repr_w_minimal_resource(target_class):
294296
assert actual_routine.description is None
295297
assert actual_routine.determinism_level is None
296298
assert actual_routine.remote_function_options is None
299+
assert actual_routine.data_governance_type is None
297300

298301

299302
def test_from_api_repr_w_unknown_fields(target_class):
@@ -428,6 +431,20 @@ def test_from_api_repr_w_unknown_fields(target_class):
428431
"determinismLevel": bigquery.DeterminismLevel.DETERMINISM_LEVEL_UNSPECIFIED
429432
},
430433
),
434+
(
435+
{
436+
"arguments": [{"name": "x", "dataType": {"typeKind": "INT64"}}],
437+
"definitionBody": "x * 3",
438+
"language": "SQL",
439+
"returnType": {"typeKind": "INT64"},
440+
"routineType": "SCALAR_FUNCTION",
441+
"description": "A routine description.",
442+
"determinismLevel": bigquery.DeterminismLevel.DETERMINISM_LEVEL_UNSPECIFIED,
443+
"dataGovernanceType": "DATA_MASKING",
444+
},
445+
["data_governance_type"],
446+
{"dataGovernanceType": "DATA_MASKING"},
447+
),
431448
(
432449
{},
433450
[
@@ -554,6 +571,36 @@ def test_set_remote_function_options_w_none(object_under_test):
554571
assert object_under_test._properties["remoteFunctionOptions"] is None
555572

556573

574+
def test_set_data_governance_type_w_none(object_under_test):
575+
object_under_test.data_governance_type = None
576+
assert object_under_test.data_governance_type is None
577+
assert object_under_test._properties["dataGovernanceType"] is None
578+
579+
580+
def test_set_data_governance_type_valid(object_under_test):
581+
object_under_test.data_governance_type = "DATA_MASKING"
582+
assert object_under_test.data_governance_type == "DATA_MASKING"
583+
assert object_under_test._properties["dataGovernanceType"] == "DATA_MASKING"
584+
585+
586+
def test_set_data_governance_type_wrong_type(object_under_test):
587+
with pytest.raises(ValueError) as exp:
588+
object_under_test.data_governance_type = 1
589+
assert "invalid data_governance_type" in str(exp)
590+
assert object_under_test.data_governance_type is None
591+
assert object_under_test._properties.get("dataGovernanceType") is None
592+
593+
594+
def test_set_data_governance_type_wrong_str(object_under_test):
595+
"""Client does not verify the content of data_governance_type string to be
596+
compatible with future upgrades. If the value is not supported, BigQuery
597+
itself will report an error.
598+
"""
599+
object_under_test.data_governance_type = "RANDOM_STRING"
600+
assert object_under_test.data_governance_type == "RANDOM_STRING"
601+
assert object_under_test._properties["dataGovernanceType"] == "RANDOM_STRING"
602+
603+
557604
def test_repr(target_class):
558605
model = target_class("my-proj.my_dset.my_routine")
559606
actual_routine = repr(model)

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.