Skip to content

Commit 969f09f

Browse files
committed
fix: make AccessEntry equality consistent for view entity type
1 parent 96b067d commit 969f09f

File tree

2 files changed

+78
-5
lines changed

2 files changed

+78
-5
lines changed

google/cloud/bigquery/dataset.py

Lines changed: 55 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -506,7 +506,20 @@ def entity_id(self) -> Optional[Union[Dict[str, Any], str]]:
506506
def __eq__(self, other):
507507
if not isinstance(other, AccessEntry):
508508
return NotImplemented
509-
return self._key() == other._key()
509+
510+
return (
511+
self.role == other.role
512+
and self.entity_type == other.entity_type
513+
and self._normalize_entity_id(self.entity_id) == self._normalize_entity_id(other.entity_id)
514+
and self.condition == other.condition
515+
)
516+
517+
@staticmethod
518+
def _normalize_entity_id(value):
519+
"""Ensure consistent equality for dicts like 'view'."""
520+
if isinstance(value, dict):
521+
return dict(sorted(value.items()))
522+
return value
510523

511524
def __ne__(self, other):
512525
return not self == other
@@ -542,8 +555,22 @@ def to_api_repr(self):
542555
Returns:
543556
Dict[str, object]: Access entry represented as an API resource
544557
"""
545-
resource = copy.deepcopy(self._properties)
558+
resource = {}
559+
560+
if self.role is not None:
561+
resource["role"] = self.role
562+
if self.entity_type is not None:
563+
resource[self.entity_type] = self.entity_id
564+
if self.condition is not None:
565+
resource["condition"] = self.condition.to_api_repr()
566+
567+
# Include any extra items preserved in _properties
568+
for k, v in self._properties.items():
569+
if k not in resource:
570+
resource[k] = v
571+
546572
return resource
573+
547574

548575
@classmethod
549576
def from_api_repr(cls, resource: dict) -> "AccessEntry":
@@ -557,10 +584,33 @@ def from_api_repr(cls, resource: dict) -> "AccessEntry":
557584
google.cloud.bigquery.dataset.AccessEntry:
558585
Access entry parsed from ``resource``.
559586
"""
587+
588+
role = resource.get("role")
589+
condition = None
590+
if "condition" in resource:
591+
from google.cloud.bigquery.dataset import Condition
592+
condition = Condition.from_api_repr(resource["condition"])
593+
594+
entity_type = None
595+
entity_id = None
596+
597+
for key, value in resource.items():
598+
if key in ("role", "condition"):
599+
continue
600+
entity_type = key
601+
entity_id = value
602+
break
603+
604+
entry = cls(
605+
role=role,
606+
entity_type=entity_type,
607+
entity_id=entity_id,
608+
condition=condition,
609+
)
560610

561-
access_entry = cls()
562-
access_entry._properties = resource.copy()
563-
return access_entry
611+
# Preserve additional _properties for roundtrip consistency:
612+
entry._properties = dict(resource)
613+
return entry
564614

565615

566616
class Dataset(object):

tests/unit/test_dataset.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1767,3 +1767,26 @@ def test__hash__with_minimal_inputs(self):
17671767
description=None,
17681768
)
17691769
assert hash(cond1) is not None
1770+
1771+
def test_access_entry_view_equality(self):
1772+
1773+
from google.cloud import bigquery
1774+
1775+
entry1 = bigquery.dataset.AccessEntry(
1776+
entity_type="view",
1777+
entity_id={
1778+
"projectId": "my_project",
1779+
"datasetId": "my_dataset",
1780+
"tableId": "my_table",
1781+
},
1782+
)
1783+
entry2 = bigquery.dataset.AccessEntry.from_api_repr({
1784+
"view": {
1785+
"projectId": "my_project",
1786+
"datasetId": "my_dataset",
1787+
"tableId": "my_table",
1788+
}
1789+
})
1790+
1791+
assert entry1 == entry2
1792+

0 commit comments

Comments
 (0)