Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Adds ForeignTypeInfo class and tests #2110

Merged
merged 7 commits into from
Jan 15, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 62 additions & 2 deletions google/cloud/bigquery/schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -560,6 +560,63 @@ def to_api_repr(self) -> dict:
return answer


class ForeignTypeInfo:
"""Metadata about the foreign data type definition such as the system in which the
type is defined.

Args:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: needs a blank line to separate the sections

Suggested change
Args:
Args:

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Resolved.

type_system (str): Required. Specifies the system which defines the
foreign data type.

TypeSystem enum currently includes:
* "TYPE_SYSTEM_UNSPECIFIED"
* "HIVE"
"""

def __init__(self, type_system: Optional[str] = None):
self._properties: Dict[str, Any] = {}
self.type_system = type_system

@property
def type_system(self) -> Optional[str]:
"""Required. Specifies the system which defines the foreign data
type."""

return self._properties.get("typeSystem")

@type_system.setter
def type_system(self, value: Optional[str]):
value = _helpers._isinstance_or_raise(value, str, none_allowed=True)
self._properties["typeSystem"] = value

def to_api_repr(self) -> dict:
"""Build an API representation of this object.

Returns:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Resolved.

Dict[str, Any]:
A dictionary in the format used by the BigQuery API.
"""

return self._properties

@classmethod
def from_api_repr(cls, api_repr: Dict[str, Any]) -> "ForeignTypeInfo":
"""Factory: constructs an instance of the class (cls)
given its API representation.

Args:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Resolved.

api_repr (Dict[str, Any]):
API representation of the object to be instantiated.

Returns:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Resolved.

An instance of the class initialized with data from 'api_repr'.
"""

config = cls()
config._properties = api_repr
return config


class SerDeInfo:
"""Serializer and deserializer information.

Expand Down Expand Up @@ -625,6 +682,7 @@ def parameters(self, value: Optional[dict[str, str]] = None):

def to_api_repr(self) -> dict:
"""Build an API representation of this object.

Returns:
Dict[str, Any]:
A dictionary in the format used by the BigQuery API.
Expand All @@ -635,11 +693,13 @@ def to_api_repr(self) -> dict:
def from_api_repr(cls, api_repr: dict) -> SerDeInfo:
"""Factory: constructs an instance of the class (cls)
given its API representation.

Args:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Args:
Args:

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Resolved.

resource (Dict[str, Any]):
api_repr (Dict[str, Any]):
API representation of the object to be instantiated.

Returns:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Returns:
Returns:

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Resolved.

An instance of the class initialized with data from 'resource'.
An instance of the class initialized with data from 'api_repr'.
"""
config = cls("PLACEHOLDER")
config._properties = api_repr
Expand Down
71 changes: 69 additions & 2 deletions tests/unit/test_schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -1128,6 +1128,73 @@ def test_to_api_repr_parameterized(field, api):
assert SchemaField(**field).to_api_repr() == api


class TestForeignTypeInfo:
"""Tests for ForeignTypeInfo objects."""

@staticmethod
def _get_target_class():
from google.cloud.bigquery.schema import ForeignTypeInfo

return ForeignTypeInfo

def _make_one(self, *args, **kw):
return self._get_target_class()(*args, **kw)

@pytest.mark.parametrize(
"type_system,expected",
[
(None, None),
("TYPE_SYSTEM_UNSPECIFIED", "TYPE_SYSTEM_UNSPECIFIED"),
("HIVE", "HIVE"),
],
)
def test_ctor_valid_input(self, type_system, expected):
result = self._make_one(type_system=type_system)

assert result.type_system == expected

def test_ctor_invalid_input(self):
with pytest.raises(TypeError) as e:
self._make_one(type_system=123)

# Looking for the first word from the string "Pass <variable> as..."
assert "Pass " in str(e.value)

@pytest.mark.parametrize(
"type_system,expected",
[
("TYPE_SYSTEM_UNSPECIFIED", {"typeSystem": "TYPE_SYSTEM_UNSPECIFIED"}),
("HIVE", {"typeSystem": "HIVE"}),
(None, {"typeSystem": None}),
],
)
def test_to_api_repr(self, type_system, expected):
result = self._make_one(type_system=type_system)

assert result.to_api_repr() == expected

def test_from_api_repr(self):
"""GIVEN an api representation of a ForeignTypeInfo object (i.e. api_repr)
WHEN converted into a ForeignTypeInfo object using from_api_repr()
THEN it will have the same representation in dict format as a ForeignTypeInfo
object made directly (via _make_one()) and represented in dict format.
"""
api_repr = {
"typeSystem": "TYPE_SYSTEM_UNSPECIFIED",
}

expected = self._make_one(
type_system="TYPE_SYSTEM_UNSPECIFIED",
)

klass = self._get_target_class()
result = klass.from_api_repr(api_repr)

# We convert both to dict format because these classes do not have a
# __eq__() method to facilitate direct equality comparisons.
assert result.to_api_repr() == expected.to_api_repr()


class TestSerDeInfo:
"""Tests for the SerDeInfo class."""

Expand Down Expand Up @@ -1190,9 +1257,9 @@ def test_to_api_repr(self):
assert serde_info.to_api_repr() == expected_repr

def test_from_api_repr(self):
"""GIVEN an api representation of a SerDeInfo object (i.e. resource)
"""GIVEN an api representation of a SerDeInfo object (i.e. api_repr)
WHEN converted into a SerDeInfo object using from_api_repr()
THEN it will have the representation in dict format as a SerDeInfo
THEN it will have the same representation in dict format as a SerDeInfo
object made directly (via _make_one()) and represented in dict format.
"""
api_repr = {
Expand Down
Loading