From acd5612d2fc469633936dbc463ce4d70951e7fdd Mon Sep 17 00:00:00 2001 From: Steffany Brown <30247553+steffnay@users.noreply.github.com> Date: Fri, 8 Apr 2022 12:40:34 -0700 Subject: [PATCH] feat: refactor AccessEntry to use _properties pattern (#1125) * add view, dataset, routine properties * add properties and unit tests * lint * add properties and tests * remove unused imports * update test * add tests * add tests * add tests * add more tests * update return type * Update google/cloud/bigquery/dataset.py Co-authored-by: Tim Swast * Update google/cloud/bigquery/dataset.py Co-authored-by: Tim Swast * Update google/cloud/bigquery/dataset.py Co-authored-by: Tim Swast * refactor to use get() and remove self._entity_id * delete unnecessary file * Update google/cloud/bigquery/dataset.py Co-authored-by: Tim Swast * Update google/cloud/bigquery/dataset.py Co-authored-by: Tim Swast * Update google/cloud/bigquery/dataset.py Co-authored-by: Tim Swast * add types, remove unnecessary checks * fix types * types * add type casting * refactor AccessEntry repr * update to return DatasetReference * update to use RoutineRef and TableRef * add table test * update to use api_repr * lint * Update google/cloud/bigquery/dataset.py Co-authored-by: Tim Swast * Update google/cloud/bigquery/dataset.py Co-authored-by: Tim Swast * Update google/cloud/bigquery/dataset.py Co-authored-by: Tim Swast * update tests * remove repeat type import Co-authored-by: Tim Swast --- google/cloud/bigquery/dataset.py | 461 +++++++++++++++++++----------- tests/unit/test_create_dataset.py | 5 +- tests/unit/test_dataset.py | 356 ++++++++++++++++++++--- 3 files changed, 612 insertions(+), 210 deletions(-) diff --git a/google/cloud/bigquery/dataset.py b/google/cloud/bigquery/dataset.py index 0fafd5783..c30204067 100644 --- a/google/cloud/bigquery/dataset.py +++ b/google/cloud/bigquery/dataset.py @@ -17,16 +17,19 @@ from __future__ import absolute_import import copy -from typing import Dict, Any + +import typing import google.cloud._helpers # type: ignore from google.cloud.bigquery import _helpers from google.cloud.bigquery.model import ModelReference -from google.cloud.bigquery.routine import RoutineReference -from google.cloud.bigquery.table import TableReference +from google.cloud.bigquery.routine import Routine, RoutineReference +from google.cloud.bigquery.table import Table, TableReference from google.cloud.bigquery.encryption_configuration import EncryptionConfiguration +from typing import Optional, List, Dict, Any, Union + def _get_table_reference(self, table_id: str) -> TableReference: """Constructs a TableReference. @@ -75,173 +78,6 @@ def _get_routine_reference(self, routine_id): ) -class AccessEntry(object): - """Represents grant of an access role to an entity. - - An entry must have exactly one of the allowed - :class:`google.cloud.bigquery.enums.EntityTypes`. If anything but ``view``, ``routine``, - or ``dataset`` are set, a ``role`` is also required. ``role`` is omitted for ``view``, - ``routine``, ``dataset``, because they are always read-only. - - See https://cloud.google.com/bigquery/docs/reference/rest/v2/datasets. - - Args: - role (str): - Role granted to the entity. The following string values are - supported: `'READER'`, `'WRITER'`, `'OWNER'`. It may also be - :data:`None` if the ``entity_type`` is ``view``, ``routine``, or ``dataset``. - - entity_type (str): - Type of entity being granted the role. See - :class:`google.cloud.bigquery.enums.EntityTypes` for supported types. - - entity_id (Union[str, Dict[str, str]]): - If the ``entity_type`` is not 'view', 'routine', or 'dataset', the - ``entity_id`` is the ``str`` ID of the entity being granted the role. If - the ``entity_type`` is 'view' or 'routine', the ``entity_id`` is a ``dict`` - representing the view or routine from a different dataset to grant access - to in the following format for views:: - - { - 'projectId': string, - 'datasetId': string, - 'tableId': string - } - - For routines:: - - { - 'projectId': string, - 'datasetId': string, - 'routineId': string - } - - If the ``entity_type`` is 'dataset', the ``entity_id`` is a ``dict`` that includes - a 'dataset' field with a ``dict`` representing the dataset and a 'target_types' - field with a ``str`` value of the dataset's resource type:: - - { - 'dataset': { - 'projectId': string, - 'datasetId': string, - }, - 'target_types: 'VIEWS' - } - - Raises: - ValueError: - If a ``view``, ``routine``, or ``dataset`` has ``role`` set, or a non ``view``, - non ``routine``, and non ``dataset`` **does not** have a ``role`` set. - - Examples: - >>> entry = AccessEntry('OWNER', 'userByEmail', 'user@example.com') - - >>> view = { - ... 'projectId': 'my-project', - ... 'datasetId': 'my_dataset', - ... 'tableId': 'my_table' - ... } - >>> entry = AccessEntry(None, 'view', view) - """ - - def __init__(self, role=None, entity_type=None, entity_id=None) -> None: - self._properties: Dict[str, Any] = {} - if entity_type in ("view", "routine", "dataset"): - if role is not None: - raise ValueError( - "Role must be None for a %r. Received " - "role: %r" % (entity_type, role) - ) - else: - if role is None: - raise ValueError( - "Role must be set for entity " "type %r" % (entity_type,) - ) - self._role = role - self._entity_type = entity_type - self._entity_id = entity_id - - @property - def role(self): - """str: The role of the entry.""" - return self._role - - @property - def entity_type(self): - """str: The entity_type of the entry.""" - return self._entity_type - - @property - def entity_id(self): - """str: The entity_id of the entry.""" - return self._entity_id - - def __eq__(self, other): - if not isinstance(other, AccessEntry): - return NotImplemented - return self._key() == other._key() - - def __ne__(self, other): - return not self == other - - def __repr__(self): - return "" % ( - self._role, - self._entity_type, - self._entity_id, - ) - - def _key(self): - """A tuple key that uniquely describes this field. - Used to compute this instance's hashcode and evaluate equality. - Returns: - Tuple: The contents of this :class:`~google.cloud.bigquery.dataset.AccessEntry`. - """ - return (self._role, self._entity_type, self._entity_id) - - def __hash__(self): - return hash(self._key()) - - def to_api_repr(self): - """Construct the API resource representation of this access entry - - Returns: - Dict[str, object]: Access entry represented as an API resource - """ - resource = copy.deepcopy(self._properties) - resource[self._entity_type] = self._entity_id - if self._role is not None: - resource["role"] = self._role - return resource - - @classmethod - def from_api_repr(cls, resource: dict) -> "AccessEntry": - """Factory: construct an access entry given its API representation - - Args: - resource (Dict[str, object]): - Access entry resource representation returned from the API - - Returns: - google.cloud.bigquery.dataset.AccessEntry: - Access entry parsed from ``resource``. - - Raises: - ValueError: - If the resource has more keys than ``role`` and one additional - key. - """ - entry = resource.copy() - role = entry.pop("role", None) - entity_type, entity_id = entry.popitem() - if len(entry) != 0: - raise ValueError("Entry has unexpected keys remaining.", entry) - - config = cls(role, entity_type, entity_id) - config._properties = copy.deepcopy(resource) - return config - - class DatasetReference(object): """DatasetReferences are pointers to datasets. @@ -383,6 +219,291 @@ def __repr__(self): return "DatasetReference{}".format(self._key()) +class AccessEntry(object): + """Represents grant of an access role to an entity. + + An entry must have exactly one of the allowed + :class:`google.cloud.bigquery.enums.EntityTypes`. If anything but ``view``, ``routine``, + or ``dataset`` are set, a ``role`` is also required. ``role`` is omitted for ``view``, + ``routine``, ``dataset``, because they are always read-only. + + See https://cloud.google.com/bigquery/docs/reference/rest/v2/datasets. + + Args: + role: + Role granted to the entity. The following string values are + supported: `'READER'`, `'WRITER'`, `'OWNER'`. It may also be + :data:`None` if the ``entity_type`` is ``view``, ``routine``, or ``dataset``. + + entity_type: + Type of entity being granted the role. See + :class:`google.cloud.bigquery.enums.EntityTypes` for supported types. + + entity_id: + If the ``entity_type`` is not 'view', 'routine', or 'dataset', the + ``entity_id`` is the ``str`` ID of the entity being granted the role. If + the ``entity_type`` is 'view' or 'routine', the ``entity_id`` is a ``dict`` + representing the view or routine from a different dataset to grant access + to in the following format for views:: + + { + 'projectId': string, + 'datasetId': string, + 'tableId': string + } + + For routines:: + + { + 'projectId': string, + 'datasetId': string, + 'routineId': string + } + + If the ``entity_type`` is 'dataset', the ``entity_id`` is a ``dict`` that includes + a 'dataset' field with a ``dict`` representing the dataset and a 'target_types' + field with a ``str`` value of the dataset's resource type:: + + { + 'dataset': { + 'projectId': string, + 'datasetId': string, + }, + 'target_types: 'VIEWS' + } + + Raises: + ValueError: + If a ``view``, ``routine``, or ``dataset`` has ``role`` set, or a non ``view``, + non ``routine``, and non ``dataset`` **does not** have a ``role`` set. + + Examples: + >>> entry = AccessEntry('OWNER', 'userByEmail', 'user@example.com') + + >>> view = { + ... 'projectId': 'my-project', + ... 'datasetId': 'my_dataset', + ... 'tableId': 'my_table' + ... } + >>> entry = AccessEntry(None, 'view', view) + """ + + def __init__( + self, + role: Optional[str] = None, + entity_type: Optional[str] = None, + entity_id: Optional[Union[Dict[str, Any], str]] = None, + ): + self._properties = {} + if entity_type is not None: + self._properties[entity_type] = entity_id + self._properties["role"] = role + self._entity_type = entity_type + + @property + def role(self) -> Optional[str]: + """The role of the entry.""" + return typing.cast(Optional[str], self._properties.get("role")) + + @role.setter + def role(self, value): + self._properties["role"] = value + + @property + def dataset(self) -> Optional[DatasetReference]: + """API resource representation of a dataset reference.""" + value = _helpers._get_sub_prop(self._properties, ["dataset", "dataset"]) + return DatasetReference.from_api_repr(value) if value else None + + @dataset.setter + def dataset(self, value): + if self.role is not None: + raise ValueError( + "Role must be None for a dataset. Current " "role: %r" % (self.role) + ) + + if isinstance(value, str): + value = DatasetReference.from_string(value).to_api_repr() + + if isinstance(value, (Dataset, DatasetListItem)): + value = value.reference.to_api_repr() + + _helpers._set_sub_prop(self._properties, ["dataset", "dataset"], value) + _helpers._set_sub_prop( + self._properties, + ["dataset", "targetTypes"], + self._properties.get("targetTypes"), + ) + + @property + def dataset_target_types(self) -> Optional[List[str]]: + """Which resources that the dataset in this entry applies to.""" + return typing.cast( + Optional[List[str]], + _helpers._get_sub_prop(self._properties, ["dataset", "targetTypes"]), + ) + + @dataset_target_types.setter + def dataset_target_types(self, value): + self._properties.setdefault("dataset", {}) + _helpers._set_sub_prop(self._properties, ["dataset", "targetTypes"], value) + + @property + def routine(self) -> Optional[RoutineReference]: + """API resource representation of a routine reference.""" + value = typing.cast(Optional[Dict], self._properties.get("routine")) + return RoutineReference.from_api_repr(value) if value else None + + @routine.setter + def routine(self, value): + if self.role is not None: + raise ValueError( + "Role must be None for a routine. Current " "role: %r" % (self.role) + ) + + if isinstance(value, str): + value = RoutineReference.from_string(value).to_api_repr() + + if isinstance(value, RoutineReference): + value = value.to_api_repr() + + if isinstance(value, Routine): + value = value.reference.to_api_repr() + + self._properties["routine"] = value + + @property + def view(self) -> Optional[TableReference]: + """API resource representation of a view reference.""" + value = typing.cast(Optional[Dict], self._properties.get("view")) + return TableReference.from_api_repr(value) if value else None + + @view.setter + def view(self, value): + if self.role is not None: + raise ValueError( + "Role must be None for a view. Current " "role: %r" % (self.role) + ) + + if isinstance(value, str): + value = TableReference.from_string(value).to_api_repr() + + if isinstance(value, TableReference): + value = value.to_api_repr() + + if isinstance(value, Table): + value = value.reference.to_api_repr() + + self._properties["view"] = value + + @property + def group_by_email(self) -> Optional[str]: + """An email address of a Google Group to grant access to.""" + return typing.cast(Optional[str], self._properties.get("groupByEmail")) + + @group_by_email.setter + def group_by_email(self, value): + self._properties["groupByEmail"] = value + + @property + def user_by_email(self) -> Optional[str]: + """An email address of a user to grant access to.""" + return typing.cast(Optional[str], self._properties.get("userByEmail")) + + @user_by_email.setter + def user_by_email(self, value): + self._properties["userByEmail"] = value + + @property + def domain(self) -> Optional[str]: + """A domain to grant access to.""" + return typing.cast(Optional[str], self._properties.get("domain")) + + @domain.setter + def domain(self, value): + self._properties["domain"] = value + + @property + def special_group(self) -> Optional[str]: + """A special group to grant access to.""" + return typing.cast(Optional[str], self._properties.get("specialGroup")) + + @special_group.setter + def special_group(self, value): + self._properties["specialGroup"] = value + + @property + def entity_type(self) -> Optional[str]: + """The entity_type of the entry.""" + return self._entity_type + + @property + def entity_id(self) -> Optional[Union[Dict[str, Any], str]]: + """The entity_id of the entry.""" + return self._properties.get(self._entity_type) if self._entity_type else None + + def __eq__(self, other): + if not isinstance(other, AccessEntry): + return NotImplemented + return self._key() == other._key() + + def __ne__(self, other): + return not self == other + + def __repr__(self): + + return f"" + + def _key(self): + """A tuple key that uniquely describes this field. + Used to compute this instance's hashcode and evaluate equality. + Returns: + Tuple: The contents of this :class:`~google.cloud.bigquery.dataset.AccessEntry`. + """ + properties = self._properties.copy() + prop_tup = tuple(sorted(properties.items())) + return (self.role, self._entity_type, self.entity_id, prop_tup) + + def __hash__(self): + return hash(self._key()) + + def to_api_repr(self): + """Construct the API resource representation of this access entry + + Returns: + Dict[str, object]: Access entry represented as an API resource + """ + resource = copy.deepcopy(self._properties) + return resource + + @classmethod + def from_api_repr(cls, resource: dict) -> "AccessEntry": + """Factory: construct an access entry given its API representation + + Args: + resource (Dict[str, object]): + Access entry resource representation returned from the API + + Returns: + google.cloud.bigquery.dataset.AccessEntry: + Access entry parsed from ``resource``. + + Raises: + ValueError: + If the resource has more keys than ``role`` and one additional + key. + """ + entry = resource.copy() + role = entry.pop("role", None) + entity_type, entity_id = entry.popitem() + if len(entry) != 0: + raise ValueError("Entry has unexpected keys remaining.", entry) + + config = cls(role, entity_type, entity_id) + config._properties = copy.deepcopy(resource) + return config + + class Dataset(object): """Datasets are containers for tables. diff --git a/tests/unit/test_create_dataset.py b/tests/unit/test_create_dataset.py index 67b21225d..81af52261 100644 --- a/tests/unit/test_create_dataset.py +++ b/tests/unit/test_create_dataset.py @@ -109,7 +109,10 @@ def test_create_dataset_w_attrs(client, PROJECT, DS_ID): "friendlyName": FRIENDLY_NAME, "location": LOCATION, "defaultTableExpirationMs": "3600", - "access": [{"role": "OWNER", "userByEmail": USER_EMAIL}, {"view": VIEW}], + "access": [ + {"role": "OWNER", "userByEmail": USER_EMAIL}, + {"view": VIEW, "role": None}, + ], "labels": LABELS, }, timeout=DEFAULT_TIMEOUT, diff --git a/tests/unit/test_dataset.py b/tests/unit/test_dataset.py index c554782bf..856674daf 100644 --- a/tests/unit/test_dataset.py +++ b/tests/unit/test_dataset.py @@ -15,14 +15,20 @@ import unittest import mock +from google.cloud.bigquery.routine.routine import Routine, RoutineReference import pytest +from google.cloud.bigquery.dataset import ( + AccessEntry, + Dataset, + DatasetReference, + Table, + TableReference, +) class TestAccessEntry(unittest.TestCase): @staticmethod def _get_target_class(): - from google.cloud.bigquery.dataset import AccessEntry - return AccessEntry def _make_one(self, *args, **kw): @@ -34,16 +40,6 @@ def test_ctor_defaults(self): self.assertEqual(entry.entity_type, "userByEmail") self.assertEqual(entry.entity_id, "phred@example.com") - def test_ctor_bad_entity_type(self): - with self.assertRaises(ValueError): - self._make_one(None, "unknown", None) - - def test_ctor_view_with_role(self): - role = "READER" - entity_type = "view" - with self.assertRaises(ValueError): - self._make_one(role, entity_type, None) - def test_ctor_view_success(self): role = None entity_type = "view" @@ -53,12 +49,6 @@ def test_ctor_view_success(self): self.assertEqual(entry.entity_type, entity_type) self.assertEqual(entry.entity_id, entity_id) - def test_ctor_routine_with_role(self): - role = "READER" - entity_type = "routine" - with self.assertRaises(ValueError): - self._make_one(role, entity_type, None) - def test_ctor_routine_success(self): role = None entity_type = "routine" @@ -68,12 +58,6 @@ def test_ctor_routine_success(self): self.assertEqual(entry.entity_type, entity_type) self.assertEqual(entry.entity_id, entity_id) - def test_ctor_nonview_without_role(self): - role = None - entity_type = "userByEmail" - with self.assertRaises(ValueError): - self._make_one(role, entity_type, None) - def test___eq___role_mismatch(self): entry = self._make_one("OWNER", "userByEmail", "phred@example.com") other = self._make_one("WRITER", "userByEmail", "phred@example.com") @@ -127,7 +111,7 @@ def test_to_api_repr_view(self): } entry = self._make_one(None, "view", view) resource = entry.to_api_repr() - exp_resource = {"view": view} + exp_resource = {"view": view, "role": None} self.assertEqual(resource, exp_resource) def test_to_api_repr_routine(self): @@ -136,9 +120,10 @@ def test_to_api_repr_routine(self): "datasetId": "my_dataset", "routineId": "my_routine", } + entry = self._make_one(None, "routine", routine) resource = entry.to_api_repr() - exp_resource = {"routine": routine} + exp_resource = {"routine": routine, "role": None} self.assertEqual(resource, exp_resource) def test_to_api_repr_dataset(self): @@ -148,21 +133,9 @@ def test_to_api_repr_dataset(self): } entry = self._make_one(None, "dataset", dataset) resource = entry.to_api_repr() - exp_resource = {"dataset": dataset} + exp_resource = {"dataset": dataset, "role": None} self.assertEqual(resource, exp_resource) - def test_to_api_w_incorrect_role(self): - dataset = { - "dataset": { - "projectId": "my-project", - "datasetId": "my_dataset", - "tableId": "my_table", - }, - "target_type": "VIEW", - } - with self.assertRaises(ValueError): - self._make_one("READER", "dataset", dataset) - def test_from_api_repr(self): resource = {"role": "OWNER", "userByEmail": "salmon@example.com"} entry = self._get_target_class().from_api_repr(resource) @@ -198,6 +171,311 @@ def test_from_api_repr_entries_w_extra_keys(self): with self.assertRaises(ValueError): self._get_target_class().from_api_repr(resource) + def test_view_getter_setter(self): + view = { + "projectId": "my_project", + "datasetId": "my_dataset", + "tableId": "my_table", + } + view_ref = TableReference.from_api_repr(view) + entry = self._make_one(None) + entry.view = view + resource = entry.to_api_repr() + exp_resource = {"view": view, "role": None} + self.assertEqual(entry.view, view_ref) + self.assertEqual(resource, exp_resource) + + def test_view_getter_setter_none(self): + entry = self._make_one(None) + self.assertEqual(entry.view, None) + + def test_view_getter_setter_string(self): + project = "my_project" + dataset = "my_dataset" + table = "my_table" + view = { + "projectId": project, + "datasetId": dataset, + "tableId": table, + } + entry = self._make_one(None) + entry.view = f"{project}.{dataset}.{table}" + resource = entry.to_api_repr() + exp_resource = {"view": view, "role": None} + self.assertEqual(resource, exp_resource) + + def test_view_getter_setter_table(self): + project = "my_project" + dataset = "my_dataset" + table = "my_table" + view = { + "projectId": project, + "datasetId": dataset, + "tableId": table, + } + view_ref = Table.from_string(f"{project}.{dataset}.{table}") + entry = self._make_one(None) + entry.view = view_ref + resource = entry.to_api_repr() + exp_resource = {"view": view, "role": None} + self.assertEqual(resource, exp_resource) + + def test_view_getter_setter_table_ref(self): + project = "my_project" + dataset = "my_dataset" + table = "my_table" + view = { + "projectId": project, + "datasetId": dataset, + "tableId": table, + } + view_ref = TableReference.from_string(f"{project}.{dataset}.{table}") + entry = self._make_one(None) + entry.view = view_ref + resource = entry.to_api_repr() + exp_resource = {"view": view, "role": None} + self.assertEqual(resource, exp_resource) + + def test_view_getter_setter_incorrect_role(self): + view = { + "projectId": "my_project", + "datasetId": "my_dataset", + "tableId": "my_table", + } + view_ref = TableReference.from_api_repr(view) + entry = self._make_one("READER") + with self.assertRaises(ValueError): + entry.view = view_ref + + def test_dataset_getter_setter(self): + dataset = {"projectId": "my-project", "datasetId": "my_dataset"} + entry = self._make_one(None) + entry.dataset = dataset + resource = entry.to_api_repr() + exp_resource = { + "dataset": {"dataset": dataset, "targetTypes": None}, + "role": None, + } + dataset_ref = DatasetReference.from_api_repr(dataset) + prop = entry.dataset + self.assertEqual(resource, exp_resource) + self.assertEqual(prop, dataset_ref) + + def test_dataset_getter_setter_none(self): + entry = self._make_one(None) + self.assertEqual(entry.dataset, None) + + def test_dataset_getter_setter_string(self): + project = "my-project" + dataset_id = "my_dataset" + dataset = { + "projectId": project, + "datasetId": dataset_id, + } + entry = self._make_one(None) + string_ref = f"{project}.{dataset_id}" + entry.dataset = string_ref + resource = entry.to_api_repr() + exp_resource = { + "dataset": {"dataset": dataset, "targetTypes": None}, + "role": None, + } + self.assertEqual(resource, exp_resource) + + def test_dataset_getter_setter_dataset_ref(self): + project = "my-project" + dataset_id = "my_dataset" + dataset_ref = DatasetReference(project, dataset_id) + entry = self._make_one(None) + entry.dataset = dataset_ref + resource = entry.to_api_repr() + exp_resource = { + "dataset": {"dataset": dataset_ref, "targetTypes": None}, + "role": None, + } + self.assertEqual(resource, exp_resource) + + def test_dataset_getter_setter_dataset(self): + project = "my-project" + dataset_id = "my_dataset" + dataset_repr = { + "projectId": project, + "datasetId": dataset_id, + } + dataset = Dataset(f"{project}.{dataset_id}") + entry = self._make_one(None) + entry.dataset = dataset + resource = entry.to_api_repr() + exp_resource = { + "role": None, + "dataset": {"dataset": dataset_repr, "targetTypes": None}, + } + self.assertEqual(resource, exp_resource) + + def test_dataset_getter_setter_incorrect_role(self): + dataset = {"dataset": {"projectId": "my-project", "datasetId": "my_dataset"}} + entry = self._make_one("READER") + with self.assertRaises(ValueError): + entry.dataset = dataset + + def test_routine_getter_setter(self): + routine = { + "projectId": "my-project", + "datasetId": "my_dataset", + "routineId": "my_routine", + } + entry = self._make_one(None) + entry.routine = routine + resource = entry.to_api_repr() + exp_resource = {"routine": routine, "role": None} + self.assertEqual(resource, exp_resource) + + def test_routine_getter_setter_none(self): + entry = self._make_one(None) + self.assertEqual(entry.routine, None) + + def test_routine_getter_setter_string(self): + project = "my-project" + dataset_id = "my_dataset" + routine_id = "my_routine" + routine = { + "projectId": project, + "datasetId": dataset_id, + "routineId": routine_id, + } + entry = self._make_one(None) + entry.routine = f"{project}.{dataset_id}.{routine_id}" + resource = entry.to_api_repr() + exp_resource = { + "routine": routine, + "role": None, + } + self.assertEqual(resource, exp_resource) + + def test_routine_getter_setter_routine_ref(self): + routine = { + "projectId": "my-project", + "datasetId": "my_dataset", + "routineId": "my_routine", + } + entry = self._make_one(None) + entry.routine = RoutineReference.from_api_repr(routine) + resource = entry.to_api_repr() + exp_resource = { + "routine": routine, + "role": None, + } + self.assertEqual(resource, exp_resource) + + def test_routine_getter_setter_routine(self): + routine = { + "projectId": "my-project", + "datasetId": "my_dataset", + "routineId": "my_routine", + } + routine_ref = RoutineReference.from_api_repr(routine) + entry = self._make_one(None) + entry.routine = Routine(routine_ref) + resource = entry.to_api_repr() + exp_resource = { + "routine": routine, + "role": None, + } + self.assertEqual(entry.routine, routine_ref) + self.assertEqual(resource, exp_resource) + + def test_routine_getter_setter_incorrect_role(self): + routine = { + "projectId": "my-project", + "datasetId": "my_dataset", + "routineId": "my_routine", + } + entry = self._make_one("READER") + with self.assertRaises(ValueError): + entry.routine = routine + + def test_group_by_email_getter_setter(self): + email = "cloud-developer-relations@google.com" + entry = self._make_one(None) + entry.group_by_email = email + resource = entry.to_api_repr() + exp_resource = {"groupByEmail": email, "role": None} + self.assertEqual(entry.group_by_email, email) + self.assertEqual(resource, exp_resource) + + def test_group_by_email_getter_setter_none(self): + entry = self._make_one(None) + self.assertEqual(entry.group_by_email, None) + + def test_user_by_email_getter_setter(self): + email = "cloud-developer-relations@google.com" + entry = self._make_one(None) + entry.user_by_email = email + resource = entry.to_api_repr() + exp_resource = {"userByEmail": email, "role": None} + self.assertEqual(entry.user_by_email, email) + self.assertEqual(resource, exp_resource) + + def test_user_by_email_getter_setter_none(self): + entry = self._make_one(None) + self.assertEqual(entry.user_by_email, None) + + def test_domain_setter(self): + domain = "my_domain" + entry = self._make_one(None) + entry.domain = domain + resource = entry.to_api_repr() + exp_resource = {"domain": domain, "role": None} + self.assertEqual(entry.domain, domain) + self.assertEqual(resource, exp_resource) + + def test_domain_getter_setter_none(self): + entry = self._make_one(None) + self.assertEqual(entry.domain, None) + + def test_special_group_getter_setter(self): + special_group = "my_special_group" + entry = self._make_one(None) + entry.special_group = special_group + resource = entry.to_api_repr() + exp_resource = {"specialGroup": special_group, "role": None} + self.assertEqual(entry.special_group, special_group) + self.assertEqual(resource, exp_resource) + + def test_special_group_getter_setter_none(self): + entry = self._make_one(None) + self.assertEqual(entry.special_group, None) + + def test_role_getter_setter(self): + role = "READER" + entry = self._make_one(None) + entry.role = role + resource = entry.to_api_repr() + exp_resource = {"role": role} + self.assertEqual(resource, exp_resource) + + def test_role_getter_setter_none(self): + entry = self._make_one(None) + self.assertEqual(entry.role, None) + + def test_dataset_target_types_getter_setter(self): + target_types = ["VIEWS"] + entry = self._make_one(None) + entry.dataset_target_types = target_types + self.assertEqual(entry.dataset_target_types, target_types) + + def test_dataset_target_types_getter_setter_none(self): + entry = self._make_one(None) + self.assertEqual(entry.dataset_target_types, None) + + def test_dataset_target_types_getter_setter_w_dataset(self): + dataset = {"projectId": "my-project", "datasetId": "my_dataset"} + target_types = ["VIEWS"] + entry = self._make_one(None) + entry.dataset = dataset + entry.dataset_target_types = target_types + self.assertEqual(entry.dataset_target_types, target_types) + class TestDatasetReference(unittest.TestCase): @staticmethod