Skip to content

Commit cbed66f

Browse files
chore: add universe compatibility with older versions of api-core (#2372)
* fix: add compatibility with older versions of api-core * check universe is not supported for api-core <= 2.18.0 * use None as universe domain for mocked values * check api-core compatibility if http is passed in * run tests only if universe helpers do not exist * check if the universe attribute exists * 🦉 Updates from OwlBot post-processor See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md --------- Co-authored-by: Owl Bot <gcf-owl-bot[bot]@users.noreply.github.com>
1 parent 727c073 commit cbed66f

File tree

2 files changed

+103
-3
lines changed

2 files changed

+103
-3
lines changed

googleapiclient/discovery.py

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,12 +124,21 @@
124124
GOOGLE_API_USE_CLIENT_CERTIFICATE = "GOOGLE_API_USE_CLIENT_CERTIFICATE"
125125
GOOGLE_API_USE_MTLS_ENDPOINT = "GOOGLE_API_USE_MTLS_ENDPOINT"
126126
GOOGLE_CLOUD_UNIVERSE_DOMAIN = "GOOGLE_CLOUD_UNIVERSE_DOMAIN"
127-
127+
DEFAULT_UNIVERSE = "googleapis.com"
128128
# Parameters accepted by the stack, but not visible via discovery.
129129
# TODO(dhermes): Remove 'userip' in 'v2'.
130130
STACK_QUERY_PARAMETERS = frozenset(["trace", "pp", "userip", "strict"])
131131
STACK_QUERY_PARAMETER_DEFAULT_VALUE = {"type": "string", "location": "query"}
132132

133+
134+
class APICoreVersionError(ValueError):
135+
def __init__(self):
136+
message = (
137+
"google-api-core >= 2.18.0 is required to use the universe domain feature."
138+
)
139+
super().__init__(message)
140+
141+
133142
# Library-specific reserved words beyond Python keywords.
134143
RESERVED_WORDS = frozenset(["body"])
135144

@@ -444,6 +453,13 @@ def _retrieve_discovery_doc(
444453
return content
445454

446455

456+
def _check_api_core_compatible_with_credentials_universe(credentials):
457+
if not HAS_UNIVERSE:
458+
credentials_universe = getattr(credentials, "universe_domain", None)
459+
if credentials_universe and credentials_universe != DEFAULT_UNIVERSE:
460+
raise APICoreVersionError
461+
462+
447463
@positional(1)
448464
def build_from_document(
449465
service,
@@ -559,6 +575,10 @@ def build_from_document(
559575
client_options.universe_domain, universe_domain_env
560576
)
561577
base = base.replace(universe.DEFAULT_UNIVERSE, universe_domain)
578+
else:
579+
client_universe = getattr(client_options, "universe_domain", None)
580+
if client_universe:
581+
raise APICoreVersionError
562582

563583
audience_for_self_signed_jwt = base
564584
if client_options.api_endpoint:
@@ -598,6 +618,9 @@ def build_from_document(
598618
quota_project_id=client_options.quota_project_id,
599619
)
600620

621+
# Check google-api-core >= 2.18.0 if credentials' universe != "googleapis.com".
622+
_check_api_core_compatible_with_credentials_universe(credentials)
623+
601624
# The credentials need to be scoped.
602625
# If the user provided scopes via client_options don't override them
603626
if not client_options.scopes:
@@ -687,6 +710,10 @@ def build_from_document(
687710
f"mTLS is not supported in any universe other than {universe.DEFAULT_UNIVERSE}."
688711
)
689712
base = mtls_endpoint
713+
else:
714+
# Check google-api-core >= 2.18.0 if credentials' universe != "googleapis.com".
715+
http_credentials = getattr(http, "credentials", None)
716+
_check_api_core_compatible_with_credentials_universe(http_credentials)
690717

691718
if model is None:
692719
features = service.get("features", [])

tests/test_discovery.py

Lines changed: 75 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@
7070
STACK_QUERY_PARAMETERS,
7171
V1_DISCOVERY_URI,
7272
V2_DISCOVERY_URI,
73+
APICoreVersionError,
7374
ResourceMethodParameters,
7475
_fix_up_media_path_base_url,
7576
_fix_up_media_upload,
@@ -107,6 +108,11 @@
107108
DATA_DIR = os.path.join(os.path.dirname(__file__), "data")
108109

109110

111+
def _reset_universe_domain(credentials, universe_domain=None):
112+
if hasattr(credentials, "universe_domain"):
113+
credentials.universe_domain = universe_domain
114+
115+
110116
def assertUrisEqual(testcase, expected, actual):
111117
"""Test that URIs are the same, up to reordering of query parameters."""
112118
expected = urllib.parse.urlparse(expected)
@@ -541,6 +547,7 @@ def test_credentials_and_credentials_file_mutually_exclusive(self):
541547

542548
class DiscoveryFromDocument(unittest.TestCase):
543549
MOCK_CREDENTIALS = mock.Mock(spec=google.auth.credentials.Credentials)
550+
_reset_universe_domain(MOCK_CREDENTIALS)
544551

545552
def test_can_build_from_local_document(self):
546553
discovery = read_datafile("plus.json")
@@ -693,6 +700,7 @@ def test_scopes_from_client_options(self):
693700
discovery = read_datafile("plus.json")
694701

695702
with mock.patch("googleapiclient._auth.default_credentials") as default:
703+
_reset_universe_domain(default.return_value)
696704
plus = build_from_document(
697705
discovery,
698706
client_options={"scopes": ["1", "2"]},
@@ -704,6 +712,7 @@ def test_quota_project_from_client_options(self):
704712
discovery = read_datafile("plus.json")
705713

706714
with mock.patch("googleapiclient._auth.default_credentials") as default:
715+
_reset_universe_domain(default.return_value)
707716
plus = build_from_document(
708717
discovery,
709718
client_options=google.api_core.client_options.ClientOptions(
@@ -717,6 +726,7 @@ def test_credentials_file_from_client_options(self):
717726
discovery = read_datafile("plus.json")
718727

719728
with mock.patch("googleapiclient._auth.credentials_from_file") as default:
729+
_reset_universe_domain(default.return_value)
720730
plus = build_from_document(
721731
discovery,
722732
client_options=google.api_core.client_options.ClientOptions(
@@ -772,6 +782,7 @@ def test_self_signed_jwt_disabled(self):
772782

773783
class DiscoveryFromDocumentMutualTLS(unittest.TestCase):
774784
MOCK_CREDENTIALS = mock.Mock(spec=google.auth.credentials.Credentials)
785+
_reset_universe_domain(MOCK_CREDENTIALS)
775786
ADC_CERT_PATH = "adc_cert_path"
776787
ADC_KEY_PATH = "adc_key_path"
777788
ADC_PASSPHRASE = "adc_passphrase"
@@ -1528,6 +1539,7 @@ def test_plus_resources(self):
15281539
@unittest.skipIf(not HAS_OAUTH2CLIENT, "oauth2client unavailable.")
15291540
def test_oauth2client_credentials(self):
15301541
credentials = mock.Mock(spec=GoogleCredentials)
1542+
_reset_universe_domain(credentials)
15311543
credentials.create_scoped_required.return_value = False
15321544

15331545
discovery = read_datafile("plus.json")
@@ -1536,6 +1548,7 @@ def test_oauth2client_credentials(self):
15361548

15371549
def test_google_auth_credentials(self):
15381550
credentials = mock.Mock(spec=google.auth.credentials.Credentials)
1551+
_reset_universe_domain(credentials)
15391552
discovery = read_datafile("plus.json")
15401553
service = build_from_document(discovery, credentials=credentials)
15411554

@@ -2340,9 +2353,9 @@ def test_get_media(self):
23402353
self.assertEqual(b"standing in for media", response)
23412354

23422355

2343-
if HAS_UNIVERSE:
2356+
class Universe(unittest.TestCase):
2357+
if HAS_UNIVERSE:
23442358

2345-
class Universe(unittest.TestCase):
23462359
def test_validate_credentials_with_no_client_options(self):
23472360
http = build_http()
23482361
discovery = read_datafile("zoo.json")
@@ -2665,6 +2678,66 @@ def test_universe_env_var_configured_with_client_options_universe(self):
26652678

26662679
assert tasks._universe_domain == fake_universe
26672680

2681+
else:
2682+
if hasattr(google.api_core.client_options.ClientOptions, "universe_domain"):
2683+
2684+
def test_client_options_universe_with_older_version_of_api_core(self):
2685+
fake_universe = "foo.com"
2686+
credentials = mock.Mock(spec=google.auth.credentials.Credentials)
2687+
credentials.universe_domain = fake_universe
2688+
discovery = read_datafile("tasks.json")
2689+
with self.assertRaises(APICoreVersionError):
2690+
tasks = build_from_document(
2691+
discovery,
2692+
credentials=credentials,
2693+
client_options=google.api_core.client_options.ClientOptions(
2694+
universe_domain=fake_universe
2695+
),
2696+
)
2697+
2698+
def test_credentials_universe_with_older_version_of_api_core(self):
2699+
fake_universe = "foo.com"
2700+
credentials = mock.Mock(spec=google.auth.credentials.Credentials)
2701+
credentials.universe_domain = fake_universe
2702+
discovery = read_datafile("tasks.json")
2703+
with self.assertRaises(APICoreVersionError):
2704+
tasks = build_from_document(
2705+
discovery,
2706+
credentials=credentials,
2707+
)
2708+
2709+
def test_credentials_default_universe_with_older_version_of_api_core(self):
2710+
credentials = mock.Mock(spec=google.auth.credentials.Credentials)
2711+
credentials.universe_domain = "googleapis.com"
2712+
discovery = read_datafile("tasks.json")
2713+
tasks = build_from_document(
2714+
discovery,
2715+
credentials=credentials,
2716+
)
2717+
2718+
def test_http_credentials_universe_with_older_version_of_api_core(self):
2719+
fake_universe = "foo.com"
2720+
http = google_auth_httplib2.AuthorizedHttp(
2721+
credentials=mock.Mock(universe_domain=fake_universe), http=build_http()
2722+
)
2723+
discovery = read_datafile("tasks.json")
2724+
with self.assertRaises(APICoreVersionError):
2725+
tasks = build_from_document(
2726+
discovery,
2727+
http=http,
2728+
)
2729+
2730+
def test_http_credentials_default_universe_with_older_version_of_api_core(self):
2731+
http = google_auth_httplib2.AuthorizedHttp(
2732+
credentials=mock.Mock(universe_domain="googleapis.com"),
2733+
http=build_http(),
2734+
)
2735+
discovery = read_datafile("tasks.json")
2736+
tasks = build_from_document(
2737+
discovery,
2738+
http=http,
2739+
)
2740+
26682741

26692742
if __name__ == "__main__":
26702743
unittest.main()

0 commit comments

Comments
 (0)