From 8808918355959b3dafb8b7dad365dc1288d34f9f Mon Sep 17 00:00:00 2001 From: Heba Alazzeh Date: Thu, 16 Jul 2026 14:54:01 +0000 Subject: [PATCH 01/12] feat(api-core): move universe and endpoint routing logic to gapic_v1 public helpers Introduces routing module containing get_api_endpoint, get_default_mtls_endpoint, and get_universe_domain helpers. Exposes the module as public in gapic_v1. --- .../google/api_core/gapic_v1/__init__.py | 4 +- .../google/api_core/gapic_v1/routing.py | 101 ++++++++++ packages/google-api-core/noxfile.py | 2 +- packages/google-api-core/tests/conftest.py | 31 ++++ .../tests/unit/gapic/test_routing.py | 175 ++++++++++++++++++ .../google-api-core/tests/unit/test_bidi.py | 5 +- 6 files changed, 313 insertions(+), 5 deletions(-) create mode 100644 packages/google-api-core/google/api_core/gapic_v1/routing.py create mode 100644 packages/google-api-core/tests/conftest.py create mode 100644 packages/google-api-core/tests/unit/gapic/test_routing.py diff --git a/packages/google-api-core/google/api_core/gapic_v1/__init__.py b/packages/google-api-core/google/api_core/gapic_v1/__init__.py index 48a27ec21d24..dd17bfd55975 100644 --- a/packages/google-api-core/google/api_core/gapic_v1/__init__.py +++ b/packages/google-api-core/google/api_core/gapic_v1/__init__.py @@ -25,9 +25,10 @@ # Older Python versions safely ignore this variable. __lazy_modules__: Set[str] = { "google.api_core.gapic_v1.client_info", + "google.api_core.gapic_v1.routing", "google.api_core.gapic_v1.routing_header", } -__all__ = ["client_info", "routing_header"] +__all__ = ["client_info", "routing", "routing_header"] if _has_grpc: __lazy_modules__.update( @@ -41,6 +42,7 @@ from google.api_core.gapic_v1 import ( # noqa: E402 client_info, + routing, routing_header, ) diff --git a/packages/google-api-core/google/api_core/gapic_v1/routing.py b/packages/google-api-core/google/api_core/gapic_v1/routing.py new file mode 100644 index 000000000000..f2e98acc8a1c --- /dev/null +++ b/packages/google-api-core/google/api_core/gapic_v1/routing.py @@ -0,0 +1,101 @@ +# -*- coding: utf-8 -*- +# Copyright 2026 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +"""Helpers for routing and endpoint resolution.""" + +import re +from typing import Any, Optional + +from google.auth.exceptions import MutualTLSChannelError # type: ignore + +_MTLS_ENDPOINT_RE = re.compile( + r"(?P[^.]+)(?P\.mtls)?(?P\.sandbox)?" + r"(?P\.googleapis\.com)?" +) + + +def get_default_mtls_endpoint(api_endpoint: Optional[str]) -> Optional[str]: + """Converts api endpoint to mTLS endpoint. + + Convert "*.sandbox.googleapis.com" and "*.googleapis.com" to + "*.mtls.sandbox.googleapis.com" and "*.mtls.googleapis.com" respectively. + Args: + api_endpoint (Optional[str]): the api endpoint to convert. + Returns: + Optional[str]: converted mTLS api endpoint. + """ + if not api_endpoint: + return api_endpoint + + m = _MTLS_ENDPOINT_RE.match(api_endpoint) + if m is None: + # Could not parse api_endpoint; return as-is. + return api_endpoint + + name, mtls_group, sandbox, googledomain = m.groups() + if mtls_group or not googledomain: + return api_endpoint + + if sandbox: + return api_endpoint.replace( + "sandbox.googleapis.com", "mtls.sandbox.googleapis.com" + ) + + return api_endpoint.replace(".googleapis.com", ".mtls.googleapis.com") + + +def get_api_endpoint( + api_override: Optional[str], + client_cert_source: Optional[Any], + universe_domain: str, + use_mtls_endpoint: str, + default_universe: str, + default_mtls_endpoint: Optional[str], + default_endpoint_template: Optional[str], +) -> Optional[str]: + """Return the API endpoint used by the client.""" + if api_override is not None: + return api_override + elif use_mtls_endpoint == "always" or ( + use_mtls_endpoint == "auto" and client_cert_source + ): + if universe_domain.lower() != default_universe.lower(): + raise MutualTLSChannelError( + f"mTLS is not supported in any universe other than {default_universe}." + ) + return default_mtls_endpoint + else: + return ( + default_endpoint_template.format(UNIVERSE_DOMAIN=universe_domain) + if default_endpoint_template + else None + ) + + +def get_universe_domain( + client_universe_domain: Optional[str], + universe_domain_env: Optional[str], + default_universe: str, +) -> str: + """Return the universe domain used by the client.""" + universe_domain = default_universe + if client_universe_domain is not None: + universe_domain = client_universe_domain.strip() + elif universe_domain_env is not None: + universe_domain = universe_domain_env.strip() + if not universe_domain: + raise ValueError("Universe Domain cannot be an empty string.") + return universe_domain diff --git a/packages/google-api-core/noxfile.py b/packages/google-api-core/noxfile.py index 0bad668a80dd..3cbebbaa84d2 100644 --- a/packages/google-api-core/noxfile.py +++ b/packages/google-api-core/noxfile.py @@ -350,7 +350,7 @@ def prerelease_deps(session): @nox.session(python=DEFAULT_PYTHON_VERSION) def core_deps_from_source(session): """Run the test suite installing dependencies from source.""" - default(session, prerelease=True) + default(session, install_deps_from_source=True) @nox.session(python=DEFAULT_PYTHON_VERSION) diff --git a/packages/google-api-core/tests/conftest.py b/packages/google-api-core/tests/conftest.py new file mode 100644 index 000000000000..1872207be107 --- /dev/null +++ b/packages/google-api-core/tests/conftest.py @@ -0,0 +1,31 @@ +# -*- coding: utf-8 -*- +# Copyright 2026 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import os +from unittest import mock +import pytest + + +@pytest.fixture(scope="session", autouse=True) +def mock_mtls_env(): + """Autouse session-scoped fixture to isolate unit tests from workstation mTLS environments.""" + with mock.patch.dict( + os.environ, + { + "GOOGLE_API_USE_CLIENT_CERTIFICATE": "false", + "CLOUDSDK_CONTEXT_AWARE_USE_CLIENT_CERTIFICATE": "false", + }, + ): + yield diff --git a/packages/google-api-core/tests/unit/gapic/test_routing.py b/packages/google-api-core/tests/unit/gapic/test_routing.py new file mode 100644 index 000000000000..1200f2589133 --- /dev/null +++ b/packages/google-api-core/tests/unit/gapic/test_routing.py @@ -0,0 +1,175 @@ +# -*- coding: utf-8 -*- +# Copyright 2026 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from unittest import mock + +import pytest + +from google.auth.exceptions import MutualTLSChannelError + +from google.api_core.gapic_v1.routing import ( + get_api_endpoint, + get_default_mtls_endpoint, + get_universe_domain, +) + + +def test_get_default_mtls_endpoint(): + # Test valid API endpoints + assert get_default_mtls_endpoint("foo.googleapis.com") == "foo.mtls.googleapis.com" + assert ( + get_default_mtls_endpoint("foo.sandbox.googleapis.com") + == "foo.mtls.sandbox.googleapis.com" + ) + + # Test endpoints that shouldn't be converted + assert ( + get_default_mtls_endpoint("foo.mtls.googleapis.com") + == "foo.mtls.googleapis.com" + ) + assert get_default_mtls_endpoint("foo.com") == "foo.com" + + # Test empty/None endpoints + assert get_default_mtls_endpoint("") == "" + assert get_default_mtls_endpoint(None) is None + + +def test_get_api_endpoint_override(): + # If api_override is provided, it should be returned + # regardless of other args + endpoint = get_api_endpoint( + api_override="custom.endpoint.com", + client_cert_source=None, + universe_domain="googleapis.com", + use_mtls_endpoint="auto", + default_universe="googleapis.com", + default_mtls_endpoint="foo.mtls.googleapis.com", + default_endpoint_template="foo.{UNIVERSE_DOMAIN}", + ) + assert endpoint == "custom.endpoint.com" + + +def test_get_api_endpoint_mtls_always(): + # use_mtls_endpoint == "always" should use the default mtls endpoint + endpoint = get_api_endpoint( + api_override=None, + client_cert_source=None, + universe_domain="googleapis.com", + use_mtls_endpoint="always", + default_universe="googleapis.com", + default_mtls_endpoint="foo.mtls.googleapis.com", + default_endpoint_template="foo.{UNIVERSE_DOMAIN}", + ) + assert endpoint == "foo.mtls.googleapis.com" + + +def test_get_api_endpoint_mtls_auto_with_cert(): + # "auto" with client_cert_source should use mtls + endpoint = get_api_endpoint( + api_override=None, + client_cert_source=mock.Mock(), + universe_domain="googleapis.com", + use_mtls_endpoint="auto", + default_universe="googleapis.com", + default_mtls_endpoint="foo.mtls.googleapis.com", + default_endpoint_template="foo.{UNIVERSE_DOMAIN}", + ) + assert endpoint == "foo.mtls.googleapis.com" + + +def test_get_api_endpoint_mtls_auto_no_cert(): + # "auto" without client_cert_source should use the default template + endpoint = get_api_endpoint( + api_override=None, + client_cert_source=None, + universe_domain="googleapis.com", + use_mtls_endpoint="auto", + default_universe="googleapis.com", + default_mtls_endpoint="foo.mtls.googleapis.com", + default_endpoint_template="foo.{UNIVERSE_DOMAIN}", + ) + assert endpoint == "foo.googleapis.com" + + +def test_get_api_endpoint_mtls_universe_mismatch(): + # mTLS is only supported in the default universe + with pytest.raises(MutualTLSChannelError, match="mTLS is not supported"): + get_api_endpoint( + api_override=None, + client_cert_source=mock.Mock(), + universe_domain="custom-universe.com", + use_mtls_endpoint="auto", + default_universe="googleapis.com", + default_mtls_endpoint="foo.mtls.googleapis.com", + default_endpoint_template="foo.{UNIVERSE_DOMAIN}", + ) + + +def test_get_api_endpoint_mtls_case_insensitive(): + # mTLS universe check should be case insensitive + endpoint = get_api_endpoint( + api_override=None, + client_cert_source=mock.Mock(), + universe_domain="GOOGLEAPIS.COM", + use_mtls_endpoint="auto", + default_universe="googleapis.com", + default_mtls_endpoint="foo.mtls.googleapis.com", + default_endpoint_template="foo.{UNIVERSE_DOMAIN}", + ) + assert endpoint == "foo.mtls.googleapis.com" + + +def test_get_universe_domain(): + # client_universe_domain takes precedence + assert ( + get_universe_domain("client.com", "env.com", "default.com") # noqa: E501 + == "client.com" + ) + + # env takes precedence over default + assert ( + get_universe_domain(None, "env.com", "default.com") == "env.com" # noqa: E501 + ) + + # fallback to default + assert get_universe_domain(None, None, "default.com") == "default.com" # noqa: E501 + + +def test_get_universe_domain_strip(): + # check that whitespace is stripped + assert ( + get_universe_domain(" client.com ", "env.com", "default.com") == "client.com" + ) + assert get_universe_domain(None, " env.com ", "default.com") == "env.com" + + +def test_get_universe_domain_empty(): + with pytest.raises(ValueError, match="cannot be an empty string"): + get_universe_domain("", None, "default.com") + with pytest.raises(ValueError, match="cannot be an empty string"): + get_universe_domain(" ", None, "default.com") + + +def test_get_api_endpoint_none_template(): + endpoint = get_api_endpoint( + api_override=None, + client_cert_source=None, + universe_domain="googleapis.com", + use_mtls_endpoint="never", + default_universe="googleapis.com", + default_mtls_endpoint=None, + default_endpoint_template=None, + ) + assert endpoint is None diff --git a/packages/google-api-core/tests/unit/test_bidi.py b/packages/google-api-core/tests/unit/test_bidi.py index 4a8eb74fac94..0f4810cb0a12 100644 --- a/packages/google-api-core/tests/unit/test_bidi.py +++ b/packages/google-api-core/tests/unit/test_bidi.py @@ -31,8 +31,7 @@ except ImportError: # pragma: NO COVER pytest.skip("No GRPC", allow_module_level=True) -from google.api_core import bidi -from google.api_core import exceptions +from google.api_core import bidi, exceptions class Test_RequestQueueGenerator(object): @@ -195,7 +194,7 @@ def test_delays_entry_attempts_above_threshold(self): # (NOTE: not using assert all(...), b/c the coverage check would complain) for i, entry in enumerate(entries): if i != 3: - assert entry["reported_wait"] == 0.0 + assert entry["reported_wait"] < 0.01 # The delayed entry is expected to have been delayed for a significant # chunk of the full second, and the actual and reported delay times From 3c47f56582f7e3c8780ee5fad9d8ac06d1626d57 Mon Sep 17 00:00:00 2001 From: Heba Alazzeh Date: Thu, 16 Jul 2026 19:20:20 +0000 Subject: [PATCH 02/12] refactor(api-core): revert unrelated changes in noxfile and test_bidi --- packages/google-api-core/noxfile.py | 2 +- packages/google-api-core/tests/unit/test_bidi.py | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/google-api-core/noxfile.py b/packages/google-api-core/noxfile.py index 3cbebbaa84d2..0bad668a80dd 100644 --- a/packages/google-api-core/noxfile.py +++ b/packages/google-api-core/noxfile.py @@ -350,7 +350,7 @@ def prerelease_deps(session): @nox.session(python=DEFAULT_PYTHON_VERSION) def core_deps_from_source(session): """Run the test suite installing dependencies from source.""" - default(session, install_deps_from_source=True) + default(session, prerelease=True) @nox.session(python=DEFAULT_PYTHON_VERSION) diff --git a/packages/google-api-core/tests/unit/test_bidi.py b/packages/google-api-core/tests/unit/test_bidi.py index 0f4810cb0a12..4a8eb74fac94 100644 --- a/packages/google-api-core/tests/unit/test_bidi.py +++ b/packages/google-api-core/tests/unit/test_bidi.py @@ -31,7 +31,8 @@ except ImportError: # pragma: NO COVER pytest.skip("No GRPC", allow_module_level=True) -from google.api_core import bidi, exceptions +from google.api_core import bidi +from google.api_core import exceptions class Test_RequestQueueGenerator(object): @@ -194,7 +195,7 @@ def test_delays_entry_attempts_above_threshold(self): # (NOTE: not using assert all(...), b/c the coverage check would complain) for i, entry in enumerate(entries): if i != 3: - assert entry["reported_wait"] < 0.01 + assert entry["reported_wait"] == 0.0 # The delayed entry is expected to have been delayed for a significant # chunk of the full second, and the actual and reported delay times From f953c809e54d78c7ae01671d80f4d742353bc2e6 Mon Sep 17 00:00:00 2001 From: Heba Alazzeh Date: Thu, 16 Jul 2026 19:28:08 +0000 Subject: [PATCH 03/12] refactor(api-core): update routing tests to match generator's test template precisely --- .../tests/unit/gapic/test_routing.py | 232 ++++++++++-------- 1 file changed, 123 insertions(+), 109 deletions(-) diff --git a/packages/google-api-core/tests/unit/gapic/test_routing.py b/packages/google-api-core/tests/unit/gapic/test_routing.py index 1200f2589133..76fb5e2d6c8d 100644 --- a/packages/google-api-core/tests/unit/gapic/test_routing.py +++ b/packages/google-api-core/tests/unit/gapic/test_routing.py @@ -26,6 +26,12 @@ ) +class MockClient: + _DEFAULT_UNIVERSE = "googleapis.com" + DEFAULT_MTLS_ENDPOINT = "foo.mtls.googleapis.com" + _DEFAULT_ENDPOINT_TEMPLATE = "foo.{UNIVERSE_DOMAIN}" + + def test_get_default_mtls_endpoint(): # Test valid API endpoints assert get_default_mtls_endpoint("foo.googleapis.com") == "foo.mtls.googleapis.com" @@ -46,130 +52,138 @@ def test_get_default_mtls_endpoint(): assert get_default_mtls_endpoint(None) is None -def test_get_api_endpoint_override(): - # If api_override is provided, it should be returned - # regardless of other args - endpoint = get_api_endpoint( - api_override="custom.endpoint.com", - client_cert_source=None, - universe_domain="googleapis.com", - use_mtls_endpoint="auto", - default_universe="googleapis.com", - default_mtls_endpoint="foo.mtls.googleapis.com", - default_endpoint_template="foo.{UNIVERSE_DOMAIN}", +def test__get_api_endpoint(): + api_override = "foo.com" + mock_client_cert_source = mock.Mock() + default_universe = MockClient._DEFAULT_UNIVERSE + default_endpoint = MockClient._DEFAULT_ENDPOINT_TEMPLATE.format( + UNIVERSE_DOMAIN=default_universe ) - assert endpoint == "custom.endpoint.com" - - -def test_get_api_endpoint_mtls_always(): - # use_mtls_endpoint == "always" should use the default mtls endpoint - endpoint = get_api_endpoint( - api_override=None, - client_cert_source=None, - universe_domain="googleapis.com", - use_mtls_endpoint="always", - default_universe="googleapis.com", - default_mtls_endpoint="foo.mtls.googleapis.com", - default_endpoint_template="foo.{UNIVERSE_DOMAIN}", + mock_universe = "bar.com" + mock_endpoint = MockClient._DEFAULT_ENDPOINT_TEMPLATE.format( + UNIVERSE_DOMAIN=mock_universe ) - assert endpoint == "foo.mtls.googleapis.com" - - -def test_get_api_endpoint_mtls_auto_with_cert(): - # "auto" with client_cert_source should use mtls - endpoint = get_api_endpoint( - api_override=None, - client_cert_source=mock.Mock(), - universe_domain="googleapis.com", - use_mtls_endpoint="auto", - default_universe="googleapis.com", - default_mtls_endpoint="foo.mtls.googleapis.com", - default_endpoint_template="foo.{UNIVERSE_DOMAIN}", + + assert ( + get_api_endpoint( + api_override, + mock_client_cert_source, + default_universe, + "always", + MockClient._DEFAULT_UNIVERSE, + MockClient.DEFAULT_MTLS_ENDPOINT, + MockClient._DEFAULT_ENDPOINT_TEMPLATE, + ) + == api_override ) - assert endpoint == "foo.mtls.googleapis.com" - - -def test_get_api_endpoint_mtls_auto_no_cert(): - # "auto" without client_cert_source should use the default template - endpoint = get_api_endpoint( - api_override=None, - client_cert_source=None, - universe_domain="googleapis.com", - use_mtls_endpoint="auto", - default_universe="googleapis.com", - default_mtls_endpoint="foo.mtls.googleapis.com", - default_endpoint_template="foo.{UNIVERSE_DOMAIN}", + assert ( + get_api_endpoint( + None, + mock_client_cert_source, + default_universe, + "auto", + MockClient._DEFAULT_UNIVERSE, + MockClient.DEFAULT_MTLS_ENDPOINT, + MockClient._DEFAULT_ENDPOINT_TEMPLATE, + ) + == MockClient.DEFAULT_MTLS_ENDPOINT ) - assert endpoint == "foo.googleapis.com" - - -def test_get_api_endpoint_mtls_universe_mismatch(): - # mTLS is only supported in the default universe - with pytest.raises(MutualTLSChannelError, match="mTLS is not supported"): + assert ( get_api_endpoint( - api_override=None, - client_cert_source=mock.Mock(), - universe_domain="custom-universe.com", - use_mtls_endpoint="auto", - default_universe="googleapis.com", - default_mtls_endpoint="foo.mtls.googleapis.com", - default_endpoint_template="foo.{UNIVERSE_DOMAIN}", + None, + None, + default_universe, + "auto", + MockClient._DEFAULT_UNIVERSE, + MockClient.DEFAULT_MTLS_ENDPOINT, + MockClient._DEFAULT_ENDPOINT_TEMPLATE, ) - - -def test_get_api_endpoint_mtls_case_insensitive(): - # mTLS universe check should be case insensitive - endpoint = get_api_endpoint( - api_override=None, - client_cert_source=mock.Mock(), - universe_domain="GOOGLEAPIS.COM", - use_mtls_endpoint="auto", - default_universe="googleapis.com", - default_mtls_endpoint="foo.mtls.googleapis.com", - default_endpoint_template="foo.{UNIVERSE_DOMAIN}", + == default_endpoint ) - assert endpoint == "foo.mtls.googleapis.com" - - -def test_get_universe_domain(): - # client_universe_domain takes precedence assert ( - get_universe_domain("client.com", "env.com", "default.com") # noqa: E501 - == "client.com" + get_api_endpoint( + None, + None, + default_universe, + "always", + MockClient._DEFAULT_UNIVERSE, + MockClient.DEFAULT_MTLS_ENDPOINT, + MockClient._DEFAULT_ENDPOINT_TEMPLATE, + ) + == MockClient.DEFAULT_MTLS_ENDPOINT ) - - # env takes precedence over default assert ( - get_universe_domain(None, "env.com", "default.com") == "env.com" # noqa: E501 + get_api_endpoint( + None, + mock_client_cert_source, + default_universe, + "always", + MockClient._DEFAULT_UNIVERSE, + MockClient.DEFAULT_MTLS_ENDPOINT, + MockClient._DEFAULT_ENDPOINT_TEMPLATE, + ) + == MockClient.DEFAULT_MTLS_ENDPOINT + ) + assert ( + get_api_endpoint( + None, + None, + mock_universe, + "never", + MockClient._DEFAULT_UNIVERSE, + MockClient.DEFAULT_MTLS_ENDPOINT, + MockClient._DEFAULT_ENDPOINT_TEMPLATE, + ) + == mock_endpoint ) - - # fallback to default - assert get_universe_domain(None, None, "default.com") == "default.com" # noqa: E501 - - -def test_get_universe_domain_strip(): - # check that whitespace is stripped assert ( - get_universe_domain(" client.com ", "env.com", "default.com") == "client.com" + get_api_endpoint( + None, + None, + default_universe, + "never", + MockClient._DEFAULT_UNIVERSE, + MockClient.DEFAULT_MTLS_ENDPOINT, + MockClient._DEFAULT_ENDPOINT_TEMPLATE, + ) + == default_endpoint ) - assert get_universe_domain(None, " env.com ", "default.com") == "env.com" + with pytest.raises(MutualTLSChannelError) as excinfo: + get_api_endpoint( + None, + mock_client_cert_source, + mock_universe, + "auto", + MockClient._DEFAULT_UNIVERSE, + MockClient.DEFAULT_MTLS_ENDPOINT, + MockClient._DEFAULT_ENDPOINT_TEMPLATE, + ) + assert ( + str(excinfo.value) + == "mTLS is not supported in any universe other than googleapis.com." + ) -def test_get_universe_domain_empty(): - with pytest.raises(ValueError, match="cannot be an empty string"): - get_universe_domain("", None, "default.com") - with pytest.raises(ValueError, match="cannot be an empty string"): - get_universe_domain(" ", None, "default.com") +def test__get_universe_domain(): + client_universe_domain = "foo.com" + universe_domain_env = "bar.com" -def test_get_api_endpoint_none_template(): - endpoint = get_api_endpoint( - api_override=None, - client_cert_source=None, - universe_domain="googleapis.com", - use_mtls_endpoint="never", - default_universe="googleapis.com", - default_mtls_endpoint=None, - default_endpoint_template=None, + assert ( + get_universe_domain( + client_universe_domain, universe_domain_env, MockClient._DEFAULT_UNIVERSE + ) + == client_universe_domain ) - assert endpoint is None + assert ( + get_universe_domain(None, universe_domain_env, MockClient._DEFAULT_UNIVERSE) + == universe_domain_env + ) + assert ( + get_universe_domain(None, None, MockClient._DEFAULT_UNIVERSE) + == MockClient._DEFAULT_UNIVERSE + ) + + with pytest.raises(ValueError) as excinfo: + get_universe_domain("", None, MockClient._DEFAULT_UNIVERSE) + assert str(excinfo.value) == "Universe Domain cannot be an empty string." From 0fe63b534dd5633be4623382824b3890a36356be Mon Sep 17 00:00:00 2001 From: Heba Alazzeh Date: Thu, 16 Jul 2026 19:38:58 +0000 Subject: [PATCH 04/12] fix(api-core): update get_default_mtls_endpoint to use robust string slicing --- .../google/api_core/gapic_v1/routing.py | 28 ++++++------------- 1 file changed, 8 insertions(+), 20 deletions(-) diff --git a/packages/google-api-core/google/api_core/gapic_v1/routing.py b/packages/google-api-core/google/api_core/gapic_v1/routing.py index f2e98acc8a1c..be094a7d455e 100644 --- a/packages/google-api-core/google/api_core/gapic_v1/routing.py +++ b/packages/google-api-core/google/api_core/gapic_v1/routing.py @@ -16,16 +16,10 @@ """Helpers for routing and endpoint resolution.""" -import re from typing import Any, Optional from google.auth.exceptions import MutualTLSChannelError # type: ignore -_MTLS_ENDPOINT_RE = re.compile( - r"(?P[^.]+)(?P\.mtls)?(?P\.sandbox)?" - r"(?P\.googleapis\.com)?" -) - def get_default_mtls_endpoint(api_endpoint: Optional[str]) -> Optional[str]: """Converts api endpoint to mTLS endpoint. @@ -37,24 +31,18 @@ def get_default_mtls_endpoint(api_endpoint: Optional[str]) -> Optional[str]: Returns: Optional[str]: converted mTLS api endpoint. """ - if not api_endpoint: + if not api_endpoint or ".mtls." in api_endpoint: return api_endpoint - m = _MTLS_ENDPOINT_RE.match(api_endpoint) - if m is None: - # Could not parse api_endpoint; return as-is. - return api_endpoint + if api_endpoint.endswith(".sandbox.googleapis.com"): + # len(".sandbox.googleapis.com") == 23 + return api_endpoint[:-23] + ".mtls.sandbox.googleapis.com" - name, mtls_group, sandbox, googledomain = m.groups() - if mtls_group or not googledomain: - return api_endpoint - - if sandbox: - return api_endpoint.replace( - "sandbox.googleapis.com", "mtls.sandbox.googleapis.com" - ) + if api_endpoint.endswith(".googleapis.com"): + # len(".googleapis.com") == 15 + return api_endpoint[:-15] + ".mtls.googleapis.com" - return api_endpoint.replace(".googleapis.com", ".mtls.googleapis.com") + return api_endpoint def get_api_endpoint( From 05fb32ed5443fcafd86351196daba25ea8097dab Mon Sep 17 00:00:00 2001 From: Heba Alazzeh Date: Thu, 16 Jul 2026 19:10:56 +0000 Subject: [PATCH 05/12] refactor(generator): use routing helper from api_core in templates and goldens --- .../%sub/services/%service/client.py.j2 | 102 +++++------------- .../%name_%version/%sub/test_%service.py.j2 | 32 ------ .../asset_v1/services/asset_service/client.py | 102 +++++------------- .../unit/gapic/asset_v1/test_asset_service.py | 35 ------ .../services/iam_credentials/client.py | 102 +++++------------- .../credentials_v1/test_iam_credentials.py | 35 ------ .../eventarc_v1/services/eventarc/client.py | 102 +++++------------- .../unit/gapic/eventarc_v1/test_eventarc.py | 35 ------ .../services/config_service_v2/client.py | 102 +++++------------- .../services/logging_service_v2/client.py | 102 +++++------------- .../services/metrics_service_v2/client.py | 102 +++++------------- .../logging_v2/test_config_service_v2.py | 35 ------ .../logging_v2/test_logging_service_v2.py | 35 ------ .../logging_v2/test_metrics_service_v2.py | 35 ------ .../services/config_service_v2/client.py | 102 +++++------------- .../services/logging_service_v2/client.py | 102 +++++------------- .../services/metrics_service_v2/client.py | 102 +++++------------- .../logging_v2/test_config_service_v2.py | 35 ------ .../logging_v2/test_logging_service_v2.py | 35 ------ .../logging_v2/test_metrics_service_v2.py | 35 ------ .../redis_v1/services/cloud_redis/client.py | 102 +++++------------- .../unit/gapic/redis_v1/test_cloud_redis.py | 35 ------ .../redis_v1/services/cloud_redis/client.py | 102 +++++------------- .../unit/gapic/redis_v1/test_cloud_redis.py | 35 ------ .../storage_batch_operations/client.py | 102 +++++------------- .../test_storage_batch_operations.py | 35 ------ 26 files changed, 338 insertions(+), 1440 deletions(-) diff --git a/packages/gapic-generator/gapic/templates/%namespace/%name_%version/%sub/services/%service/client.py.j2 b/packages/gapic-generator/gapic/templates/%namespace/%name_%version/%sub/services/%service/client.py.j2 index e2e3edb24967..6d1ddd993591 100644 --- a/packages/gapic-generator/gapic/templates/%namespace/%name_%version/%sub/services/%service/client.py.j2 +++ b/packages/gapic-generator/gapic/templates/%namespace/%name_%version/%sub/services/%service/client.py.j2 @@ -30,6 +30,7 @@ from google.api_core import exceptions as core_exceptions from google.api_core import extended_operation {% endif %} from google.api_core import gapic_v1 +from google.api_core.gapic_v1 import routing from google.api_core import retry as retries from google.auth import credentials as ga_credentials # type: ignore from google.auth.transport import mtls # type: ignore @@ -144,38 +145,9 @@ class {{ service.client_name }}(metaclass={{ service.client_name }}Meta): This class implements API version {{ service.version }}.{% endif %}""" @staticmethod - def _get_default_mtls_endpoint(api_endpoint) -> Optional[str]: - """Converts api endpoint to mTLS endpoint. - - Convert "*.sandbox.googleapis.com" and "*.googleapis.com" to - "*.mtls.sandbox.googleapis.com" and "*.mtls.googleapis.com" respectively. - Args: - api_endpoint (Optional[str]): the api endpoint to convert. - Returns: - Optional[str]: converted mTLS api endpoint. - """ - if not api_endpoint: - return api_endpoint - - mtls_endpoint_re = re.compile( - r"(?P[^.]+)(?P\.mtls)?(?P\.sandbox)?(?P\.googleapis\.com)?" - ) - - m = mtls_endpoint_re.match(api_endpoint) - if m is None: - # Could not parse api_endpoint; return as-is. - return api_endpoint - - name, mtls, sandbox, googledomain = m.groups() - if mtls or not googledomain: - return api_endpoint - - if sandbox: - return api_endpoint.replace( - "sandbox.googleapis.com", "mtls.sandbox.googleapis.com" - ) - - return api_endpoint.replace(".googleapis.com", ".mtls.googleapis.com") + def _get_default_mtls_endpoint(api_endpoint: Optional[str]) -> Optional[str]: + """Converts api endpoint to mTLS endpoint.""" + return routing.get_default_mtls_endpoint(api_endpoint) # Note: DEFAULT_ENDPOINT is deprecated. Use _DEFAULT_ENDPOINT_TEMPLATE instead. DEFAULT_ENDPOINT = {% if service.host %}"{{ service.host }}"{% else %}None{% endif %} @@ -392,53 +364,31 @@ class {{ service.client_name }}(metaclass={{ service.client_name }}Meta): return client_cert_source @staticmethod - def _get_api_endpoint(api_override, client_cert_source, universe_domain, use_mtls_endpoint) -> str: - """Return the API endpoint used by the client. - - Args: - api_override (str): The API endpoint override. If specified, this is always - the return value of this function and the other arguments are not used. - client_cert_source (bytes): The client certificate source used by the client. - universe_domain (str): The universe domain used by the client. - use_mtls_endpoint (str): How to use the mTLS endpoint, which depends also on the other parameters. - Possible values are "always", "auto", or "never". - - Returns: - str: The API endpoint to be used by the client. - """ - if api_override is not None: - api_endpoint = api_override - elif use_mtls_endpoint == "always" or (use_mtls_endpoint == "auto" and client_cert_source): - _default_universe = {{ service.client_name }}._DEFAULT_UNIVERSE - if universe_domain != _default_universe: - raise MutualTLSChannelError(f"mTLS is not supported in any universe other than {_default_universe}.") - api_endpoint = {{ service.client_name }}.DEFAULT_MTLS_ENDPOINT - else: - api_endpoint = {{ service.client_name }}._DEFAULT_ENDPOINT_TEMPLATE.format(UNIVERSE_DOMAIN=universe_domain) - return api_endpoint + def _get_api_endpoint( + api_override: Optional[str], + client_cert_source: Optional[Callable[[], Tuple[bytes, bytes]]], + universe_domain: str, + use_mtls_endpoint: str, + ) -> str: + """Return the API endpoint used by the client.""" + return routing.get_api_endpoint( + api_override, + client_cert_source, + universe_domain, + use_mtls_endpoint, + {{ service.client_name }}._DEFAULT_UNIVERSE, + {{ service.client_name }}.DEFAULT_MTLS_ENDPOINT, + {{ service.client_name }}._DEFAULT_ENDPOINT_TEMPLATE, + ) @staticmethod def _get_universe_domain(client_universe_domain: Optional[str], universe_domain_env: Optional[str]) -> str: - """Return the universe domain used by the client. - - Args: - client_universe_domain (Optional[str]): The universe domain configured via the client options. - universe_domain_env (Optional[str]): The universe domain configured via the "GOOGLE_CLOUD_UNIVERSE_DOMAIN" environment variable. - - Returns: - str: The universe domain to be used by the client. - - Raises: - ValueError: If the universe domain is an empty string. - """ - universe_domain = {{ service.client_name }}._DEFAULT_UNIVERSE - if client_universe_domain is not None: - universe_domain = client_universe_domain - elif universe_domain_env is not None: - universe_domain = universe_domain_env - if len(universe_domain.strip()) == 0: - raise ValueError("Universe Domain cannot be an empty string.") - return universe_domain + """Return the universe domain used by the client.""" + return routing.get_universe_domain( + client_universe_domain, + universe_domain_env, + {{ service.client_name }}._DEFAULT_UNIVERSE, + ) def _validate_universe_domain(self): """Validates client's and credentials' universe domains are consistent. diff --git a/packages/gapic-generator/gapic/templates/tests/unit/gapic/%name_%version/%sub/test_%service.py.j2 b/packages/gapic-generator/gapic/templates/tests/unit/gapic/%name_%version/%sub/test_%service.py.j2 index c68b390b7c84..83c540b7c1ec 100644 --- a/packages/gapic-generator/gapic/templates/tests/unit/gapic/%name_%version/%sub/test_%service.py.j2 +++ b/packages/gapic-generator/gapic/templates/tests/unit/gapic/%name_%version/%sub/test_%service.py.j2 @@ -313,29 +313,7 @@ def test__get_client_cert_source(): assert {{ service.client_name }}._get_client_cert_source(None, True) is mock_default_cert_source assert {{ service.client_name }}._get_client_cert_source(mock_provided_cert_source, "true") is mock_provided_cert_source -@mock.patch.object({{ service.client_name }}, "_DEFAULT_ENDPOINT_TEMPLATE", modify_default_endpoint_template({{ service.client_name }})) -{% if 'grpc' in opts.transport %} -@mock.patch.object({{ service.async_client_name }}, "_DEFAULT_ENDPOINT_TEMPLATE", modify_default_endpoint_template({{ service.async_client_name }})) -{% endif %} -def test__get_api_endpoint(): - api_override = "foo.com" - mock_client_cert_source = mock.Mock() - default_universe = {{ service.client_name }}._DEFAULT_UNIVERSE - default_endpoint = {{ service.client_name }}._DEFAULT_ENDPOINT_TEMPLATE.format(UNIVERSE_DOMAIN=default_universe) - mock_universe = "bar.com" - mock_endpoint = {{ service.client_name }}._DEFAULT_ENDPOINT_TEMPLATE.format(UNIVERSE_DOMAIN=mock_universe) - - assert {{ service.client_name }}._get_api_endpoint(api_override, mock_client_cert_source, default_universe, "always") == api_override - assert {{ service.client_name }}._get_api_endpoint(None, mock_client_cert_source, default_universe, "auto") == {{ service.client_name }}.DEFAULT_MTLS_ENDPOINT - assert {{ service.client_name }}._get_api_endpoint(None, None, default_universe, "auto") == default_endpoint - assert {{ service.client_name }}._get_api_endpoint(None, None, default_universe, "always") == {{ service.client_name }}.DEFAULT_MTLS_ENDPOINT - assert {{ service.client_name }}._get_api_endpoint(None, mock_client_cert_source, default_universe, "always") == {{ service.client_name }}.DEFAULT_MTLS_ENDPOINT - assert {{ service.client_name }}._get_api_endpoint(None, None, mock_universe, "never") == mock_endpoint - assert {{ service.client_name }}._get_api_endpoint(None, None, default_universe, "never") == default_endpoint - with pytest.raises(MutualTLSChannelError) as excinfo: - {{ service.client_name }}._get_api_endpoint(None, mock_client_cert_source, mock_universe, "auto") - assert str(excinfo.value) == "mTLS is not supported in any universe other than googleapis.com." {% if service.version %} {% for method in service.methods.values() %}{% with method_name = method.name|snake_case %} @@ -384,17 +362,7 @@ def test_{{ method_name }}_api_version_header(transport_name): {% endfor %} {% endif %}{# service.version #} -def test__get_universe_domain(): - client_universe_domain = "foo.com" - universe_domain_env = "bar.com" - - assert {{ service.client_name }}._get_universe_domain(client_universe_domain, universe_domain_env) == client_universe_domain - assert {{ service.client_name }}._get_universe_domain(None, universe_domain_env) == universe_domain_env - assert {{ service.client_name }}._get_universe_domain(None, None) == {{ service.client_name }}._DEFAULT_UNIVERSE - with pytest.raises(ValueError) as excinfo: - {{ service.client_name }}._get_universe_domain("", None) - assert str(excinfo.value) == "Universe Domain cannot be an empty string." @pytest.mark.parametrize("error_code,cred_info_json,show_cred_info", [ (401, CRED_INFO_JSON, True), diff --git a/packages/gapic-generator/tests/integration/goldens/asset/google/cloud/asset_v1/services/asset_service/client.py b/packages/gapic-generator/tests/integration/goldens/asset/google/cloud/asset_v1/services/asset_service/client.py index 590b6fa1c615..a7c41468e352 100755 --- a/packages/gapic-generator/tests/integration/goldens/asset/google/cloud/asset_v1/services/asset_service/client.py +++ b/packages/gapic-generator/tests/integration/goldens/asset/google/cloud/asset_v1/services/asset_service/client.py @@ -27,6 +27,7 @@ from google.api_core import client_options as client_options_lib from google.api_core import exceptions as core_exceptions from google.api_core import gapic_v1 +from google.api_core.gapic_v1 import routing from google.api_core import retry as retries from google.auth import credentials as ga_credentials # type: ignore from google.auth.transport import mtls # type: ignore @@ -101,38 +102,9 @@ class AssetServiceClient(metaclass=AssetServiceClientMeta): """Asset service definition.""" @staticmethod - def _get_default_mtls_endpoint(api_endpoint) -> Optional[str]: - """Converts api endpoint to mTLS endpoint. - - Convert "*.sandbox.googleapis.com" and "*.googleapis.com" to - "*.mtls.sandbox.googleapis.com" and "*.mtls.googleapis.com" respectively. - Args: - api_endpoint (Optional[str]): the api endpoint to convert. - Returns: - Optional[str]: converted mTLS api endpoint. - """ - if not api_endpoint: - return api_endpoint - - mtls_endpoint_re = re.compile( - r"(?P[^.]+)(?P\.mtls)?(?P\.sandbox)?(?P\.googleapis\.com)?" - ) - - m = mtls_endpoint_re.match(api_endpoint) - if m is None: - # Could not parse api_endpoint; return as-is. - return api_endpoint - - name, mtls, sandbox, googledomain = m.groups() - if mtls or not googledomain: - return api_endpoint - - if sandbox: - return api_endpoint.replace( - "sandbox.googleapis.com", "mtls.sandbox.googleapis.com" - ) - - return api_endpoint.replace(".googleapis.com", ".mtls.googleapis.com") + def _get_default_mtls_endpoint(api_endpoint: Optional[str]) -> Optional[str]: + """Converts api endpoint to mTLS endpoint.""" + return routing.get_default_mtls_endpoint(api_endpoint) # Note: DEFAULT_ENDPOINT is deprecated. Use _DEFAULT_ENDPOINT_TEMPLATE instead. DEFAULT_ENDPOINT = "cloudasset.googleapis.com" @@ -450,53 +422,31 @@ def _get_client_cert_source(provided_cert_source, use_cert_flag): return client_cert_source @staticmethod - def _get_api_endpoint(api_override, client_cert_source, universe_domain, use_mtls_endpoint) -> str: - """Return the API endpoint used by the client. - - Args: - api_override (str): The API endpoint override. If specified, this is always - the return value of this function and the other arguments are not used. - client_cert_source (bytes): The client certificate source used by the client. - universe_domain (str): The universe domain used by the client. - use_mtls_endpoint (str): How to use the mTLS endpoint, which depends also on the other parameters. - Possible values are "always", "auto", or "never". - - Returns: - str: The API endpoint to be used by the client. - """ - if api_override is not None: - api_endpoint = api_override - elif use_mtls_endpoint == "always" or (use_mtls_endpoint == "auto" and client_cert_source): - _default_universe = AssetServiceClient._DEFAULT_UNIVERSE - if universe_domain != _default_universe: - raise MutualTLSChannelError(f"mTLS is not supported in any universe other than {_default_universe}.") - api_endpoint = AssetServiceClient.DEFAULT_MTLS_ENDPOINT - else: - api_endpoint = AssetServiceClient._DEFAULT_ENDPOINT_TEMPLATE.format(UNIVERSE_DOMAIN=universe_domain) - return api_endpoint + def _get_api_endpoint( + api_override: Optional[str], + client_cert_source: Optional[Callable[[], Tuple[bytes, bytes]]], + universe_domain: str, + use_mtls_endpoint: str, + ) -> str: + """Return the API endpoint used by the client.""" + return routing.get_api_endpoint( + api_override, + client_cert_source, + universe_domain, + use_mtls_endpoint, + AssetServiceClient._DEFAULT_UNIVERSE, + AssetServiceClient.DEFAULT_MTLS_ENDPOINT, + AssetServiceClient._DEFAULT_ENDPOINT_TEMPLATE, + ) @staticmethod def _get_universe_domain(client_universe_domain: Optional[str], universe_domain_env: Optional[str]) -> str: - """Return the universe domain used by the client. - - Args: - client_universe_domain (Optional[str]): The universe domain configured via the client options. - universe_domain_env (Optional[str]): The universe domain configured via the "GOOGLE_CLOUD_UNIVERSE_DOMAIN" environment variable. - - Returns: - str: The universe domain to be used by the client. - - Raises: - ValueError: If the universe domain is an empty string. - """ - universe_domain = AssetServiceClient._DEFAULT_UNIVERSE - if client_universe_domain is not None: - universe_domain = client_universe_domain - elif universe_domain_env is not None: - universe_domain = universe_domain_env - if len(universe_domain.strip()) == 0: - raise ValueError("Universe Domain cannot be an empty string.") - return universe_domain + """Return the universe domain used by the client.""" + return routing.get_universe_domain( + client_universe_domain, + universe_domain_env, + AssetServiceClient._DEFAULT_UNIVERSE, + ) def _validate_universe_domain(self): """Validates client's and credentials' universe domains are consistent. diff --git a/packages/gapic-generator/tests/integration/goldens/asset/tests/unit/gapic/asset_v1/test_asset_service.py b/packages/gapic-generator/tests/integration/goldens/asset/tests/unit/gapic/asset_v1/test_asset_service.py index ea110a38acc3..2273922d7367 100755 --- a/packages/gapic-generator/tests/integration/goldens/asset/tests/unit/gapic/asset_v1/test_asset_service.py +++ b/packages/gapic-generator/tests/integration/goldens/asset/tests/unit/gapic/asset_v1/test_asset_service.py @@ -278,41 +278,6 @@ def test__get_client_cert_source(): assert AssetServiceClient._get_client_cert_source(None, True) is mock_default_cert_source assert AssetServiceClient._get_client_cert_source(mock_provided_cert_source, "true") is mock_provided_cert_source -@mock.patch.object(AssetServiceClient, "_DEFAULT_ENDPOINT_TEMPLATE", modify_default_endpoint_template(AssetServiceClient)) -@mock.patch.object(AssetServiceAsyncClient, "_DEFAULT_ENDPOINT_TEMPLATE", modify_default_endpoint_template(AssetServiceAsyncClient)) -def test__get_api_endpoint(): - api_override = "foo.com" - mock_client_cert_source = mock.Mock() - default_universe = AssetServiceClient._DEFAULT_UNIVERSE - default_endpoint = AssetServiceClient._DEFAULT_ENDPOINT_TEMPLATE.format(UNIVERSE_DOMAIN=default_universe) - mock_universe = "bar.com" - mock_endpoint = AssetServiceClient._DEFAULT_ENDPOINT_TEMPLATE.format(UNIVERSE_DOMAIN=mock_universe) - - assert AssetServiceClient._get_api_endpoint(api_override, mock_client_cert_source, default_universe, "always") == api_override - assert AssetServiceClient._get_api_endpoint(None, mock_client_cert_source, default_universe, "auto") == AssetServiceClient.DEFAULT_MTLS_ENDPOINT - assert AssetServiceClient._get_api_endpoint(None, None, default_universe, "auto") == default_endpoint - assert AssetServiceClient._get_api_endpoint(None, None, default_universe, "always") == AssetServiceClient.DEFAULT_MTLS_ENDPOINT - assert AssetServiceClient._get_api_endpoint(None, mock_client_cert_source, default_universe, "always") == AssetServiceClient.DEFAULT_MTLS_ENDPOINT - assert AssetServiceClient._get_api_endpoint(None, None, mock_universe, "never") == mock_endpoint - assert AssetServiceClient._get_api_endpoint(None, None, default_universe, "never") == default_endpoint - - with pytest.raises(MutualTLSChannelError) as excinfo: - AssetServiceClient._get_api_endpoint(None, mock_client_cert_source, mock_universe, "auto") - assert str(excinfo.value) == "mTLS is not supported in any universe other than googleapis.com." - - -def test__get_universe_domain(): - client_universe_domain = "foo.com" - universe_domain_env = "bar.com" - - assert AssetServiceClient._get_universe_domain(client_universe_domain, universe_domain_env) == client_universe_domain - assert AssetServiceClient._get_universe_domain(None, universe_domain_env) == universe_domain_env - assert AssetServiceClient._get_universe_domain(None, None) == AssetServiceClient._DEFAULT_UNIVERSE - - with pytest.raises(ValueError) as excinfo: - AssetServiceClient._get_universe_domain("", None) - assert str(excinfo.value) == "Universe Domain cannot be an empty string." - @pytest.mark.parametrize("error_code,cred_info_json,show_cred_info", [ (401, CRED_INFO_JSON, True), (403, CRED_INFO_JSON, True), diff --git a/packages/gapic-generator/tests/integration/goldens/credentials/google/iam/credentials_v1/services/iam_credentials/client.py b/packages/gapic-generator/tests/integration/goldens/credentials/google/iam/credentials_v1/services/iam_credentials/client.py index 4ad970da32e9..caacd71e79b2 100755 --- a/packages/gapic-generator/tests/integration/goldens/credentials/google/iam/credentials_v1/services/iam_credentials/client.py +++ b/packages/gapic-generator/tests/integration/goldens/credentials/google/iam/credentials_v1/services/iam_credentials/client.py @@ -27,6 +27,7 @@ from google.api_core import client_options as client_options_lib from google.api_core import exceptions as core_exceptions from google.api_core import gapic_v1 +from google.api_core.gapic_v1 import routing from google.api_core import retry as retries from google.auth import credentials as ga_credentials # type: ignore from google.auth.transport import mtls # type: ignore @@ -104,38 +105,9 @@ class IAMCredentialsClient(metaclass=IAMCredentialsClientMeta): """ @staticmethod - def _get_default_mtls_endpoint(api_endpoint) -> Optional[str]: - """Converts api endpoint to mTLS endpoint. - - Convert "*.sandbox.googleapis.com" and "*.googleapis.com" to - "*.mtls.sandbox.googleapis.com" and "*.mtls.googleapis.com" respectively. - Args: - api_endpoint (Optional[str]): the api endpoint to convert. - Returns: - Optional[str]: converted mTLS api endpoint. - """ - if not api_endpoint: - return api_endpoint - - mtls_endpoint_re = re.compile( - r"(?P[^.]+)(?P\.mtls)?(?P\.sandbox)?(?P\.googleapis\.com)?" - ) - - m = mtls_endpoint_re.match(api_endpoint) - if m is None: - # Could not parse api_endpoint; return as-is. - return api_endpoint - - name, mtls, sandbox, googledomain = m.groups() - if mtls or not googledomain: - return api_endpoint - - if sandbox: - return api_endpoint.replace( - "sandbox.googleapis.com", "mtls.sandbox.googleapis.com" - ) - - return api_endpoint.replace(".googleapis.com", ".mtls.googleapis.com") + def _get_default_mtls_endpoint(api_endpoint: Optional[str]) -> Optional[str]: + """Converts api endpoint to mTLS endpoint.""" + return routing.get_default_mtls_endpoint(api_endpoint) # Note: DEFAULT_ENDPOINT is deprecated. Use _DEFAULT_ENDPOINT_TEMPLATE instead. DEFAULT_ENDPOINT = "iamcredentials.googleapis.com" @@ -387,53 +359,31 @@ def _get_client_cert_source(provided_cert_source, use_cert_flag): return client_cert_source @staticmethod - def _get_api_endpoint(api_override, client_cert_source, universe_domain, use_mtls_endpoint) -> str: - """Return the API endpoint used by the client. - - Args: - api_override (str): The API endpoint override. If specified, this is always - the return value of this function and the other arguments are not used. - client_cert_source (bytes): The client certificate source used by the client. - universe_domain (str): The universe domain used by the client. - use_mtls_endpoint (str): How to use the mTLS endpoint, which depends also on the other parameters. - Possible values are "always", "auto", or "never". - - Returns: - str: The API endpoint to be used by the client. - """ - if api_override is not None: - api_endpoint = api_override - elif use_mtls_endpoint == "always" or (use_mtls_endpoint == "auto" and client_cert_source): - _default_universe = IAMCredentialsClient._DEFAULT_UNIVERSE - if universe_domain != _default_universe: - raise MutualTLSChannelError(f"mTLS is not supported in any universe other than {_default_universe}.") - api_endpoint = IAMCredentialsClient.DEFAULT_MTLS_ENDPOINT - else: - api_endpoint = IAMCredentialsClient._DEFAULT_ENDPOINT_TEMPLATE.format(UNIVERSE_DOMAIN=universe_domain) - return api_endpoint + def _get_api_endpoint( + api_override: Optional[str], + client_cert_source: Optional[Callable[[], Tuple[bytes, bytes]]], + universe_domain: str, + use_mtls_endpoint: str, + ) -> str: + """Return the API endpoint used by the client.""" + return routing.get_api_endpoint( + api_override, + client_cert_source, + universe_domain, + use_mtls_endpoint, + IAMCredentialsClient._DEFAULT_UNIVERSE, + IAMCredentialsClient.DEFAULT_MTLS_ENDPOINT, + IAMCredentialsClient._DEFAULT_ENDPOINT_TEMPLATE, + ) @staticmethod def _get_universe_domain(client_universe_domain: Optional[str], universe_domain_env: Optional[str]) -> str: - """Return the universe domain used by the client. - - Args: - client_universe_domain (Optional[str]): The universe domain configured via the client options. - universe_domain_env (Optional[str]): The universe domain configured via the "GOOGLE_CLOUD_UNIVERSE_DOMAIN" environment variable. - - Returns: - str: The universe domain to be used by the client. - - Raises: - ValueError: If the universe domain is an empty string. - """ - universe_domain = IAMCredentialsClient._DEFAULT_UNIVERSE - if client_universe_domain is not None: - universe_domain = client_universe_domain - elif universe_domain_env is not None: - universe_domain = universe_domain_env - if len(universe_domain.strip()) == 0: - raise ValueError("Universe Domain cannot be an empty string.") - return universe_domain + """Return the universe domain used by the client.""" + return routing.get_universe_domain( + client_universe_domain, + universe_domain_env, + IAMCredentialsClient._DEFAULT_UNIVERSE, + ) def _validate_universe_domain(self): """Validates client's and credentials' universe domains are consistent. diff --git a/packages/gapic-generator/tests/integration/goldens/credentials/tests/unit/gapic/credentials_v1/test_iam_credentials.py b/packages/gapic-generator/tests/integration/goldens/credentials/tests/unit/gapic/credentials_v1/test_iam_credentials.py index 14a6074a40f9..062512a8b572 100755 --- a/packages/gapic-generator/tests/integration/goldens/credentials/tests/unit/gapic/credentials_v1/test_iam_credentials.py +++ b/packages/gapic-generator/tests/integration/goldens/credentials/tests/unit/gapic/credentials_v1/test_iam_credentials.py @@ -268,41 +268,6 @@ def test__get_client_cert_source(): assert IAMCredentialsClient._get_client_cert_source(None, True) is mock_default_cert_source assert IAMCredentialsClient._get_client_cert_source(mock_provided_cert_source, "true") is mock_provided_cert_source -@mock.patch.object(IAMCredentialsClient, "_DEFAULT_ENDPOINT_TEMPLATE", modify_default_endpoint_template(IAMCredentialsClient)) -@mock.patch.object(IAMCredentialsAsyncClient, "_DEFAULT_ENDPOINT_TEMPLATE", modify_default_endpoint_template(IAMCredentialsAsyncClient)) -def test__get_api_endpoint(): - api_override = "foo.com" - mock_client_cert_source = mock.Mock() - default_universe = IAMCredentialsClient._DEFAULT_UNIVERSE - default_endpoint = IAMCredentialsClient._DEFAULT_ENDPOINT_TEMPLATE.format(UNIVERSE_DOMAIN=default_universe) - mock_universe = "bar.com" - mock_endpoint = IAMCredentialsClient._DEFAULT_ENDPOINT_TEMPLATE.format(UNIVERSE_DOMAIN=mock_universe) - - assert IAMCredentialsClient._get_api_endpoint(api_override, mock_client_cert_source, default_universe, "always") == api_override - assert IAMCredentialsClient._get_api_endpoint(None, mock_client_cert_source, default_universe, "auto") == IAMCredentialsClient.DEFAULT_MTLS_ENDPOINT - assert IAMCredentialsClient._get_api_endpoint(None, None, default_universe, "auto") == default_endpoint - assert IAMCredentialsClient._get_api_endpoint(None, None, default_universe, "always") == IAMCredentialsClient.DEFAULT_MTLS_ENDPOINT - assert IAMCredentialsClient._get_api_endpoint(None, mock_client_cert_source, default_universe, "always") == IAMCredentialsClient.DEFAULT_MTLS_ENDPOINT - assert IAMCredentialsClient._get_api_endpoint(None, None, mock_universe, "never") == mock_endpoint - assert IAMCredentialsClient._get_api_endpoint(None, None, default_universe, "never") == default_endpoint - - with pytest.raises(MutualTLSChannelError) as excinfo: - IAMCredentialsClient._get_api_endpoint(None, mock_client_cert_source, mock_universe, "auto") - assert str(excinfo.value) == "mTLS is not supported in any universe other than googleapis.com." - - -def test__get_universe_domain(): - client_universe_domain = "foo.com" - universe_domain_env = "bar.com" - - assert IAMCredentialsClient._get_universe_domain(client_universe_domain, universe_domain_env) == client_universe_domain - assert IAMCredentialsClient._get_universe_domain(None, universe_domain_env) == universe_domain_env - assert IAMCredentialsClient._get_universe_domain(None, None) == IAMCredentialsClient._DEFAULT_UNIVERSE - - with pytest.raises(ValueError) as excinfo: - IAMCredentialsClient._get_universe_domain("", None) - assert str(excinfo.value) == "Universe Domain cannot be an empty string." - @pytest.mark.parametrize("error_code,cred_info_json,show_cred_info", [ (401, CRED_INFO_JSON, True), (403, CRED_INFO_JSON, True), diff --git a/packages/gapic-generator/tests/integration/goldens/eventarc/google/cloud/eventarc_v1/services/eventarc/client.py b/packages/gapic-generator/tests/integration/goldens/eventarc/google/cloud/eventarc_v1/services/eventarc/client.py index 7255d3c91709..a55a86f853e5 100755 --- a/packages/gapic-generator/tests/integration/goldens/eventarc/google/cloud/eventarc_v1/services/eventarc/client.py +++ b/packages/gapic-generator/tests/integration/goldens/eventarc/google/cloud/eventarc_v1/services/eventarc/client.py @@ -27,6 +27,7 @@ from google.api_core import client_options as client_options_lib from google.api_core import exceptions as core_exceptions from google.api_core import gapic_v1 +from google.api_core.gapic_v1 import routing from google.api_core import retry as retries from google.auth import credentials as ga_credentials # type: ignore from google.auth.transport import mtls # type: ignore @@ -122,38 +123,9 @@ class EventarcClient(metaclass=EventarcClientMeta): """ @staticmethod - def _get_default_mtls_endpoint(api_endpoint) -> Optional[str]: - """Converts api endpoint to mTLS endpoint. - - Convert "*.sandbox.googleapis.com" and "*.googleapis.com" to - "*.mtls.sandbox.googleapis.com" and "*.mtls.googleapis.com" respectively. - Args: - api_endpoint (Optional[str]): the api endpoint to convert. - Returns: - Optional[str]: converted mTLS api endpoint. - """ - if not api_endpoint: - return api_endpoint - - mtls_endpoint_re = re.compile( - r"(?P[^.]+)(?P\.mtls)?(?P\.sandbox)?(?P\.googleapis\.com)?" - ) - - m = mtls_endpoint_re.match(api_endpoint) - if m is None: - # Could not parse api_endpoint; return as-is. - return api_endpoint - - name, mtls, sandbox, googledomain = m.groups() - if mtls or not googledomain: - return api_endpoint - - if sandbox: - return api_endpoint.replace( - "sandbox.googleapis.com", "mtls.sandbox.googleapis.com" - ) - - return api_endpoint.replace(".googleapis.com", ".mtls.googleapis.com") + def _get_default_mtls_endpoint(api_endpoint: Optional[str]) -> Optional[str]: + """Converts api endpoint to mTLS endpoint.""" + return routing.get_default_mtls_endpoint(api_endpoint) # Note: DEFAULT_ENDPOINT is deprecated. Use _DEFAULT_ENDPOINT_TEMPLATE instead. DEFAULT_ENDPOINT = "eventarc.googleapis.com" @@ -570,53 +542,31 @@ def _get_client_cert_source(provided_cert_source, use_cert_flag): return client_cert_source @staticmethod - def _get_api_endpoint(api_override, client_cert_source, universe_domain, use_mtls_endpoint) -> str: - """Return the API endpoint used by the client. - - Args: - api_override (str): The API endpoint override. If specified, this is always - the return value of this function and the other arguments are not used. - client_cert_source (bytes): The client certificate source used by the client. - universe_domain (str): The universe domain used by the client. - use_mtls_endpoint (str): How to use the mTLS endpoint, which depends also on the other parameters. - Possible values are "always", "auto", or "never". - - Returns: - str: The API endpoint to be used by the client. - """ - if api_override is not None: - api_endpoint = api_override - elif use_mtls_endpoint == "always" or (use_mtls_endpoint == "auto" and client_cert_source): - _default_universe = EventarcClient._DEFAULT_UNIVERSE - if universe_domain != _default_universe: - raise MutualTLSChannelError(f"mTLS is not supported in any universe other than {_default_universe}.") - api_endpoint = EventarcClient.DEFAULT_MTLS_ENDPOINT - else: - api_endpoint = EventarcClient._DEFAULT_ENDPOINT_TEMPLATE.format(UNIVERSE_DOMAIN=universe_domain) - return api_endpoint + def _get_api_endpoint( + api_override: Optional[str], + client_cert_source: Optional[Callable[[], Tuple[bytes, bytes]]], + universe_domain: str, + use_mtls_endpoint: str, + ) -> str: + """Return the API endpoint used by the client.""" + return routing.get_api_endpoint( + api_override, + client_cert_source, + universe_domain, + use_mtls_endpoint, + EventarcClient._DEFAULT_UNIVERSE, + EventarcClient.DEFAULT_MTLS_ENDPOINT, + EventarcClient._DEFAULT_ENDPOINT_TEMPLATE, + ) @staticmethod def _get_universe_domain(client_universe_domain: Optional[str], universe_domain_env: Optional[str]) -> str: - """Return the universe domain used by the client. - - Args: - client_universe_domain (Optional[str]): The universe domain configured via the client options. - universe_domain_env (Optional[str]): The universe domain configured via the "GOOGLE_CLOUD_UNIVERSE_DOMAIN" environment variable. - - Returns: - str: The universe domain to be used by the client. - - Raises: - ValueError: If the universe domain is an empty string. - """ - universe_domain = EventarcClient._DEFAULT_UNIVERSE - if client_universe_domain is not None: - universe_domain = client_universe_domain - elif universe_domain_env is not None: - universe_domain = universe_domain_env - if len(universe_domain.strip()) == 0: - raise ValueError("Universe Domain cannot be an empty string.") - return universe_domain + """Return the universe domain used by the client.""" + return routing.get_universe_domain( + client_universe_domain, + universe_domain_env, + EventarcClient._DEFAULT_UNIVERSE, + ) def _validate_universe_domain(self): """Validates client's and credentials' universe domains are consistent. diff --git a/packages/gapic-generator/tests/integration/goldens/eventarc/tests/unit/gapic/eventarc_v1/test_eventarc.py b/packages/gapic-generator/tests/integration/goldens/eventarc/tests/unit/gapic/eventarc_v1/test_eventarc.py index 533e401eb1e7..b7348f7aded3 100755 --- a/packages/gapic-generator/tests/integration/goldens/eventarc/tests/unit/gapic/eventarc_v1/test_eventarc.py +++ b/packages/gapic-generator/tests/integration/goldens/eventarc/tests/unit/gapic/eventarc_v1/test_eventarc.py @@ -299,41 +299,6 @@ def test__get_client_cert_source(): assert EventarcClient._get_client_cert_source(None, True) is mock_default_cert_source assert EventarcClient._get_client_cert_source(mock_provided_cert_source, "true") is mock_provided_cert_source -@mock.patch.object(EventarcClient, "_DEFAULT_ENDPOINT_TEMPLATE", modify_default_endpoint_template(EventarcClient)) -@mock.patch.object(EventarcAsyncClient, "_DEFAULT_ENDPOINT_TEMPLATE", modify_default_endpoint_template(EventarcAsyncClient)) -def test__get_api_endpoint(): - api_override = "foo.com" - mock_client_cert_source = mock.Mock() - default_universe = EventarcClient._DEFAULT_UNIVERSE - default_endpoint = EventarcClient._DEFAULT_ENDPOINT_TEMPLATE.format(UNIVERSE_DOMAIN=default_universe) - mock_universe = "bar.com" - mock_endpoint = EventarcClient._DEFAULT_ENDPOINT_TEMPLATE.format(UNIVERSE_DOMAIN=mock_universe) - - assert EventarcClient._get_api_endpoint(api_override, mock_client_cert_source, default_universe, "always") == api_override - assert EventarcClient._get_api_endpoint(None, mock_client_cert_source, default_universe, "auto") == EventarcClient.DEFAULT_MTLS_ENDPOINT - assert EventarcClient._get_api_endpoint(None, None, default_universe, "auto") == default_endpoint - assert EventarcClient._get_api_endpoint(None, None, default_universe, "always") == EventarcClient.DEFAULT_MTLS_ENDPOINT - assert EventarcClient._get_api_endpoint(None, mock_client_cert_source, default_universe, "always") == EventarcClient.DEFAULT_MTLS_ENDPOINT - assert EventarcClient._get_api_endpoint(None, None, mock_universe, "never") == mock_endpoint - assert EventarcClient._get_api_endpoint(None, None, default_universe, "never") == default_endpoint - - with pytest.raises(MutualTLSChannelError) as excinfo: - EventarcClient._get_api_endpoint(None, mock_client_cert_source, mock_universe, "auto") - assert str(excinfo.value) == "mTLS is not supported in any universe other than googleapis.com." - - -def test__get_universe_domain(): - client_universe_domain = "foo.com" - universe_domain_env = "bar.com" - - assert EventarcClient._get_universe_domain(client_universe_domain, universe_domain_env) == client_universe_domain - assert EventarcClient._get_universe_domain(None, universe_domain_env) == universe_domain_env - assert EventarcClient._get_universe_domain(None, None) == EventarcClient._DEFAULT_UNIVERSE - - with pytest.raises(ValueError) as excinfo: - EventarcClient._get_universe_domain("", None) - assert str(excinfo.value) == "Universe Domain cannot be an empty string." - @pytest.mark.parametrize("error_code,cred_info_json,show_cred_info", [ (401, CRED_INFO_JSON, True), (403, CRED_INFO_JSON, True), diff --git a/packages/gapic-generator/tests/integration/goldens/logging/google/cloud/logging_v2/services/config_service_v2/client.py b/packages/gapic-generator/tests/integration/goldens/logging/google/cloud/logging_v2/services/config_service_v2/client.py index 15922e6d865a..ef738f779665 100755 --- a/packages/gapic-generator/tests/integration/goldens/logging/google/cloud/logging_v2/services/config_service_v2/client.py +++ b/packages/gapic-generator/tests/integration/goldens/logging/google/cloud/logging_v2/services/config_service_v2/client.py @@ -27,6 +27,7 @@ from google.api_core import client_options as client_options_lib from google.api_core import exceptions as core_exceptions from google.api_core import gapic_v1 +from google.api_core.gapic_v1 import routing from google.api_core import retry as retries from google.auth import credentials as ga_credentials # type: ignore from google.auth.transport import mtls # type: ignore @@ -97,38 +98,9 @@ class ConfigServiceV2Client(metaclass=ConfigServiceV2ClientMeta): """Service for configuring sinks used to route log entries.""" @staticmethod - def _get_default_mtls_endpoint(api_endpoint) -> Optional[str]: - """Converts api endpoint to mTLS endpoint. - - Convert "*.sandbox.googleapis.com" and "*.googleapis.com" to - "*.mtls.sandbox.googleapis.com" and "*.mtls.googleapis.com" respectively. - Args: - api_endpoint (Optional[str]): the api endpoint to convert. - Returns: - Optional[str]: converted mTLS api endpoint. - """ - if not api_endpoint: - return api_endpoint - - mtls_endpoint_re = re.compile( - r"(?P[^.]+)(?P\.mtls)?(?P\.sandbox)?(?P\.googleapis\.com)?" - ) - - m = mtls_endpoint_re.match(api_endpoint) - if m is None: - # Could not parse api_endpoint; return as-is. - return api_endpoint - - name, mtls, sandbox, googledomain = m.groups() - if mtls or not googledomain: - return api_endpoint - - if sandbox: - return api_endpoint.replace( - "sandbox.googleapis.com", "mtls.sandbox.googleapis.com" - ) - - return api_endpoint.replace(".googleapis.com", ".mtls.googleapis.com") + def _get_default_mtls_endpoint(api_endpoint: Optional[str]) -> Optional[str]: + """Converts api endpoint to mTLS endpoint.""" + return routing.get_default_mtls_endpoint(api_endpoint) # Note: DEFAULT_ENDPOINT is deprecated. Use _DEFAULT_ENDPOINT_TEMPLATE instead. DEFAULT_ENDPOINT = "logging.googleapis.com" @@ -446,53 +418,31 @@ def _get_client_cert_source(provided_cert_source, use_cert_flag): return client_cert_source @staticmethod - def _get_api_endpoint(api_override, client_cert_source, universe_domain, use_mtls_endpoint) -> str: - """Return the API endpoint used by the client. - - Args: - api_override (str): The API endpoint override. If specified, this is always - the return value of this function and the other arguments are not used. - client_cert_source (bytes): The client certificate source used by the client. - universe_domain (str): The universe domain used by the client. - use_mtls_endpoint (str): How to use the mTLS endpoint, which depends also on the other parameters. - Possible values are "always", "auto", or "never". - - Returns: - str: The API endpoint to be used by the client. - """ - if api_override is not None: - api_endpoint = api_override - elif use_mtls_endpoint == "always" or (use_mtls_endpoint == "auto" and client_cert_source): - _default_universe = ConfigServiceV2Client._DEFAULT_UNIVERSE - if universe_domain != _default_universe: - raise MutualTLSChannelError(f"mTLS is not supported in any universe other than {_default_universe}.") - api_endpoint = ConfigServiceV2Client.DEFAULT_MTLS_ENDPOINT - else: - api_endpoint = ConfigServiceV2Client._DEFAULT_ENDPOINT_TEMPLATE.format(UNIVERSE_DOMAIN=universe_domain) - return api_endpoint + def _get_api_endpoint( + api_override: Optional[str], + client_cert_source: Optional[Callable[[], Tuple[bytes, bytes]]], + universe_domain: str, + use_mtls_endpoint: str, + ) -> str: + """Return the API endpoint used by the client.""" + return routing.get_api_endpoint( + api_override, + client_cert_source, + universe_domain, + use_mtls_endpoint, + ConfigServiceV2Client._DEFAULT_UNIVERSE, + ConfigServiceV2Client.DEFAULT_MTLS_ENDPOINT, + ConfigServiceV2Client._DEFAULT_ENDPOINT_TEMPLATE, + ) @staticmethod def _get_universe_domain(client_universe_domain: Optional[str], universe_domain_env: Optional[str]) -> str: - """Return the universe domain used by the client. - - Args: - client_universe_domain (Optional[str]): The universe domain configured via the client options. - universe_domain_env (Optional[str]): The universe domain configured via the "GOOGLE_CLOUD_UNIVERSE_DOMAIN" environment variable. - - Returns: - str: The universe domain to be used by the client. - - Raises: - ValueError: If the universe domain is an empty string. - """ - universe_domain = ConfigServiceV2Client._DEFAULT_UNIVERSE - if client_universe_domain is not None: - universe_domain = client_universe_domain - elif universe_domain_env is not None: - universe_domain = universe_domain_env - if len(universe_domain.strip()) == 0: - raise ValueError("Universe Domain cannot be an empty string.") - return universe_domain + """Return the universe domain used by the client.""" + return routing.get_universe_domain( + client_universe_domain, + universe_domain_env, + ConfigServiceV2Client._DEFAULT_UNIVERSE, + ) def _validate_universe_domain(self): """Validates client's and credentials' universe domains are consistent. diff --git a/packages/gapic-generator/tests/integration/goldens/logging/google/cloud/logging_v2/services/logging_service_v2/client.py b/packages/gapic-generator/tests/integration/goldens/logging/google/cloud/logging_v2/services/logging_service_v2/client.py index e89762755eda..effd54581fac 100755 --- a/packages/gapic-generator/tests/integration/goldens/logging/google/cloud/logging_v2/services/logging_service_v2/client.py +++ b/packages/gapic-generator/tests/integration/goldens/logging/google/cloud/logging_v2/services/logging_service_v2/client.py @@ -27,6 +27,7 @@ from google.api_core import client_options as client_options_lib from google.api_core import exceptions as core_exceptions from google.api_core import gapic_v1 +from google.api_core.gapic_v1 import routing from google.api_core import retry as retries from google.auth import credentials as ga_credentials # type: ignore from google.auth.transport import mtls # type: ignore @@ -94,38 +95,9 @@ class LoggingServiceV2Client(metaclass=LoggingServiceV2ClientMeta): """Service for ingesting and querying logs.""" @staticmethod - def _get_default_mtls_endpoint(api_endpoint) -> Optional[str]: - """Converts api endpoint to mTLS endpoint. - - Convert "*.sandbox.googleapis.com" and "*.googleapis.com" to - "*.mtls.sandbox.googleapis.com" and "*.mtls.googleapis.com" respectively. - Args: - api_endpoint (Optional[str]): the api endpoint to convert. - Returns: - Optional[str]: converted mTLS api endpoint. - """ - if not api_endpoint: - return api_endpoint - - mtls_endpoint_re = re.compile( - r"(?P[^.]+)(?P\.mtls)?(?P\.sandbox)?(?P\.googleapis\.com)?" - ) - - m = mtls_endpoint_re.match(api_endpoint) - if m is None: - # Could not parse api_endpoint; return as-is. - return api_endpoint - - name, mtls, sandbox, googledomain = m.groups() - if mtls or not googledomain: - return api_endpoint - - if sandbox: - return api_endpoint.replace( - "sandbox.googleapis.com", "mtls.sandbox.googleapis.com" - ) - - return api_endpoint.replace(".googleapis.com", ".mtls.googleapis.com") + def _get_default_mtls_endpoint(api_endpoint: Optional[str]) -> Optional[str]: + """Converts api endpoint to mTLS endpoint.""" + return routing.get_default_mtls_endpoint(api_endpoint) # Note: DEFAULT_ENDPOINT is deprecated. Use _DEFAULT_ENDPOINT_TEMPLATE instead. DEFAULT_ENDPOINT = "logging.googleapis.com" @@ -377,53 +349,31 @@ def _get_client_cert_source(provided_cert_source, use_cert_flag): return client_cert_source @staticmethod - def _get_api_endpoint(api_override, client_cert_source, universe_domain, use_mtls_endpoint) -> str: - """Return the API endpoint used by the client. - - Args: - api_override (str): The API endpoint override. If specified, this is always - the return value of this function and the other arguments are not used. - client_cert_source (bytes): The client certificate source used by the client. - universe_domain (str): The universe domain used by the client. - use_mtls_endpoint (str): How to use the mTLS endpoint, which depends also on the other parameters. - Possible values are "always", "auto", or "never". - - Returns: - str: The API endpoint to be used by the client. - """ - if api_override is not None: - api_endpoint = api_override - elif use_mtls_endpoint == "always" or (use_mtls_endpoint == "auto" and client_cert_source): - _default_universe = LoggingServiceV2Client._DEFAULT_UNIVERSE - if universe_domain != _default_universe: - raise MutualTLSChannelError(f"mTLS is not supported in any universe other than {_default_universe}.") - api_endpoint = LoggingServiceV2Client.DEFAULT_MTLS_ENDPOINT - else: - api_endpoint = LoggingServiceV2Client._DEFAULT_ENDPOINT_TEMPLATE.format(UNIVERSE_DOMAIN=universe_domain) - return api_endpoint + def _get_api_endpoint( + api_override: Optional[str], + client_cert_source: Optional[Callable[[], Tuple[bytes, bytes]]], + universe_domain: str, + use_mtls_endpoint: str, + ) -> str: + """Return the API endpoint used by the client.""" + return routing.get_api_endpoint( + api_override, + client_cert_source, + universe_domain, + use_mtls_endpoint, + LoggingServiceV2Client._DEFAULT_UNIVERSE, + LoggingServiceV2Client.DEFAULT_MTLS_ENDPOINT, + LoggingServiceV2Client._DEFAULT_ENDPOINT_TEMPLATE, + ) @staticmethod def _get_universe_domain(client_universe_domain: Optional[str], universe_domain_env: Optional[str]) -> str: - """Return the universe domain used by the client. - - Args: - client_universe_domain (Optional[str]): The universe domain configured via the client options. - universe_domain_env (Optional[str]): The universe domain configured via the "GOOGLE_CLOUD_UNIVERSE_DOMAIN" environment variable. - - Returns: - str: The universe domain to be used by the client. - - Raises: - ValueError: If the universe domain is an empty string. - """ - universe_domain = LoggingServiceV2Client._DEFAULT_UNIVERSE - if client_universe_domain is not None: - universe_domain = client_universe_domain - elif universe_domain_env is not None: - universe_domain = universe_domain_env - if len(universe_domain.strip()) == 0: - raise ValueError("Universe Domain cannot be an empty string.") - return universe_domain + """Return the universe domain used by the client.""" + return routing.get_universe_domain( + client_universe_domain, + universe_domain_env, + LoggingServiceV2Client._DEFAULT_UNIVERSE, + ) def _validate_universe_domain(self): """Validates client's and credentials' universe domains are consistent. diff --git a/packages/gapic-generator/tests/integration/goldens/logging/google/cloud/logging_v2/services/metrics_service_v2/client.py b/packages/gapic-generator/tests/integration/goldens/logging/google/cloud/logging_v2/services/metrics_service_v2/client.py index 90e9355f8c26..75ac58cd141c 100755 --- a/packages/gapic-generator/tests/integration/goldens/logging/google/cloud/logging_v2/services/metrics_service_v2/client.py +++ b/packages/gapic-generator/tests/integration/goldens/logging/google/cloud/logging_v2/services/metrics_service_v2/client.py @@ -27,6 +27,7 @@ from google.api_core import client_options as client_options_lib from google.api_core import exceptions as core_exceptions from google.api_core import gapic_v1 +from google.api_core.gapic_v1 import routing from google.api_core import retry as retries from google.auth import credentials as ga_credentials # type: ignore from google.auth.transport import mtls # type: ignore @@ -95,38 +96,9 @@ class MetricsServiceV2Client(metaclass=MetricsServiceV2ClientMeta): """Service for configuring logs-based metrics.""" @staticmethod - def _get_default_mtls_endpoint(api_endpoint) -> Optional[str]: - """Converts api endpoint to mTLS endpoint. - - Convert "*.sandbox.googleapis.com" and "*.googleapis.com" to - "*.mtls.sandbox.googleapis.com" and "*.mtls.googleapis.com" respectively. - Args: - api_endpoint (Optional[str]): the api endpoint to convert. - Returns: - Optional[str]: converted mTLS api endpoint. - """ - if not api_endpoint: - return api_endpoint - - mtls_endpoint_re = re.compile( - r"(?P[^.]+)(?P\.mtls)?(?P\.sandbox)?(?P\.googleapis\.com)?" - ) - - m = mtls_endpoint_re.match(api_endpoint) - if m is None: - # Could not parse api_endpoint; return as-is. - return api_endpoint - - name, mtls, sandbox, googledomain = m.groups() - if mtls or not googledomain: - return api_endpoint - - if sandbox: - return api_endpoint.replace( - "sandbox.googleapis.com", "mtls.sandbox.googleapis.com" - ) - - return api_endpoint.replace(".googleapis.com", ".mtls.googleapis.com") + def _get_default_mtls_endpoint(api_endpoint: Optional[str]) -> Optional[str]: + """Converts api endpoint to mTLS endpoint.""" + return routing.get_default_mtls_endpoint(api_endpoint) # Note: DEFAULT_ENDPOINT is deprecated. Use _DEFAULT_ENDPOINT_TEMPLATE instead. DEFAULT_ENDPOINT = "logging.googleapis.com" @@ -378,53 +350,31 @@ def _get_client_cert_source(provided_cert_source, use_cert_flag): return client_cert_source @staticmethod - def _get_api_endpoint(api_override, client_cert_source, universe_domain, use_mtls_endpoint) -> str: - """Return the API endpoint used by the client. - - Args: - api_override (str): The API endpoint override. If specified, this is always - the return value of this function and the other arguments are not used. - client_cert_source (bytes): The client certificate source used by the client. - universe_domain (str): The universe domain used by the client. - use_mtls_endpoint (str): How to use the mTLS endpoint, which depends also on the other parameters. - Possible values are "always", "auto", or "never". - - Returns: - str: The API endpoint to be used by the client. - """ - if api_override is not None: - api_endpoint = api_override - elif use_mtls_endpoint == "always" or (use_mtls_endpoint == "auto" and client_cert_source): - _default_universe = MetricsServiceV2Client._DEFAULT_UNIVERSE - if universe_domain != _default_universe: - raise MutualTLSChannelError(f"mTLS is not supported in any universe other than {_default_universe}.") - api_endpoint = MetricsServiceV2Client.DEFAULT_MTLS_ENDPOINT - else: - api_endpoint = MetricsServiceV2Client._DEFAULT_ENDPOINT_TEMPLATE.format(UNIVERSE_DOMAIN=universe_domain) - return api_endpoint + def _get_api_endpoint( + api_override: Optional[str], + client_cert_source: Optional[Callable[[], Tuple[bytes, bytes]]], + universe_domain: str, + use_mtls_endpoint: str, + ) -> str: + """Return the API endpoint used by the client.""" + return routing.get_api_endpoint( + api_override, + client_cert_source, + universe_domain, + use_mtls_endpoint, + MetricsServiceV2Client._DEFAULT_UNIVERSE, + MetricsServiceV2Client.DEFAULT_MTLS_ENDPOINT, + MetricsServiceV2Client._DEFAULT_ENDPOINT_TEMPLATE, + ) @staticmethod def _get_universe_domain(client_universe_domain: Optional[str], universe_domain_env: Optional[str]) -> str: - """Return the universe domain used by the client. - - Args: - client_universe_domain (Optional[str]): The universe domain configured via the client options. - universe_domain_env (Optional[str]): The universe domain configured via the "GOOGLE_CLOUD_UNIVERSE_DOMAIN" environment variable. - - Returns: - str: The universe domain to be used by the client. - - Raises: - ValueError: If the universe domain is an empty string. - """ - universe_domain = MetricsServiceV2Client._DEFAULT_UNIVERSE - if client_universe_domain is not None: - universe_domain = client_universe_domain - elif universe_domain_env is not None: - universe_domain = universe_domain_env - if len(universe_domain.strip()) == 0: - raise ValueError("Universe Domain cannot be an empty string.") - return universe_domain + """Return the universe domain used by the client.""" + return routing.get_universe_domain( + client_universe_domain, + universe_domain_env, + MetricsServiceV2Client._DEFAULT_UNIVERSE, + ) def _validate_universe_domain(self): """Validates client's and credentials' universe domains are consistent. diff --git a/packages/gapic-generator/tests/integration/goldens/logging/tests/unit/gapic/logging_v2/test_config_service_v2.py b/packages/gapic-generator/tests/integration/goldens/logging/tests/unit/gapic/logging_v2/test_config_service_v2.py index eada5b433c55..732ea0f50b31 100755 --- a/packages/gapic-generator/tests/integration/goldens/logging/tests/unit/gapic/logging_v2/test_config_service_v2.py +++ b/packages/gapic-generator/tests/integration/goldens/logging/tests/unit/gapic/logging_v2/test_config_service_v2.py @@ -269,41 +269,6 @@ def test__get_client_cert_source(): assert ConfigServiceV2Client._get_client_cert_source(None, True) is mock_default_cert_source assert ConfigServiceV2Client._get_client_cert_source(mock_provided_cert_source, "true") is mock_provided_cert_source -@mock.patch.object(ConfigServiceV2Client, "_DEFAULT_ENDPOINT_TEMPLATE", modify_default_endpoint_template(ConfigServiceV2Client)) -@mock.patch.object(ConfigServiceV2AsyncClient, "_DEFAULT_ENDPOINT_TEMPLATE", modify_default_endpoint_template(ConfigServiceV2AsyncClient)) -def test__get_api_endpoint(): - api_override = "foo.com" - mock_client_cert_source = mock.Mock() - default_universe = ConfigServiceV2Client._DEFAULT_UNIVERSE - default_endpoint = ConfigServiceV2Client._DEFAULT_ENDPOINT_TEMPLATE.format(UNIVERSE_DOMAIN=default_universe) - mock_universe = "bar.com" - mock_endpoint = ConfigServiceV2Client._DEFAULT_ENDPOINT_TEMPLATE.format(UNIVERSE_DOMAIN=mock_universe) - - assert ConfigServiceV2Client._get_api_endpoint(api_override, mock_client_cert_source, default_universe, "always") == api_override - assert ConfigServiceV2Client._get_api_endpoint(None, mock_client_cert_source, default_universe, "auto") == ConfigServiceV2Client.DEFAULT_MTLS_ENDPOINT - assert ConfigServiceV2Client._get_api_endpoint(None, None, default_universe, "auto") == default_endpoint - assert ConfigServiceV2Client._get_api_endpoint(None, None, default_universe, "always") == ConfigServiceV2Client.DEFAULT_MTLS_ENDPOINT - assert ConfigServiceV2Client._get_api_endpoint(None, mock_client_cert_source, default_universe, "always") == ConfigServiceV2Client.DEFAULT_MTLS_ENDPOINT - assert ConfigServiceV2Client._get_api_endpoint(None, None, mock_universe, "never") == mock_endpoint - assert ConfigServiceV2Client._get_api_endpoint(None, None, default_universe, "never") == default_endpoint - - with pytest.raises(MutualTLSChannelError) as excinfo: - ConfigServiceV2Client._get_api_endpoint(None, mock_client_cert_source, mock_universe, "auto") - assert str(excinfo.value) == "mTLS is not supported in any universe other than googleapis.com." - - -def test__get_universe_domain(): - client_universe_domain = "foo.com" - universe_domain_env = "bar.com" - - assert ConfigServiceV2Client._get_universe_domain(client_universe_domain, universe_domain_env) == client_universe_domain - assert ConfigServiceV2Client._get_universe_domain(None, universe_domain_env) == universe_domain_env - assert ConfigServiceV2Client._get_universe_domain(None, None) == ConfigServiceV2Client._DEFAULT_UNIVERSE - - with pytest.raises(ValueError) as excinfo: - ConfigServiceV2Client._get_universe_domain("", None) - assert str(excinfo.value) == "Universe Domain cannot be an empty string." - @pytest.mark.parametrize("error_code,cred_info_json,show_cred_info", [ (401, CRED_INFO_JSON, True), (403, CRED_INFO_JSON, True), diff --git a/packages/gapic-generator/tests/integration/goldens/logging/tests/unit/gapic/logging_v2/test_logging_service_v2.py b/packages/gapic-generator/tests/integration/goldens/logging/tests/unit/gapic/logging_v2/test_logging_service_v2.py index 65559a5d1073..fb366d46e4ba 100755 --- a/packages/gapic-generator/tests/integration/goldens/logging/tests/unit/gapic/logging_v2/test_logging_service_v2.py +++ b/packages/gapic-generator/tests/integration/goldens/logging/tests/unit/gapic/logging_v2/test_logging_service_v2.py @@ -270,41 +270,6 @@ def test__get_client_cert_source(): assert LoggingServiceV2Client._get_client_cert_source(None, True) is mock_default_cert_source assert LoggingServiceV2Client._get_client_cert_source(mock_provided_cert_source, "true") is mock_provided_cert_source -@mock.patch.object(LoggingServiceV2Client, "_DEFAULT_ENDPOINT_TEMPLATE", modify_default_endpoint_template(LoggingServiceV2Client)) -@mock.patch.object(LoggingServiceV2AsyncClient, "_DEFAULT_ENDPOINT_TEMPLATE", modify_default_endpoint_template(LoggingServiceV2AsyncClient)) -def test__get_api_endpoint(): - api_override = "foo.com" - mock_client_cert_source = mock.Mock() - default_universe = LoggingServiceV2Client._DEFAULT_UNIVERSE - default_endpoint = LoggingServiceV2Client._DEFAULT_ENDPOINT_TEMPLATE.format(UNIVERSE_DOMAIN=default_universe) - mock_universe = "bar.com" - mock_endpoint = LoggingServiceV2Client._DEFAULT_ENDPOINT_TEMPLATE.format(UNIVERSE_DOMAIN=mock_universe) - - assert LoggingServiceV2Client._get_api_endpoint(api_override, mock_client_cert_source, default_universe, "always") == api_override - assert LoggingServiceV2Client._get_api_endpoint(None, mock_client_cert_source, default_universe, "auto") == LoggingServiceV2Client.DEFAULT_MTLS_ENDPOINT - assert LoggingServiceV2Client._get_api_endpoint(None, None, default_universe, "auto") == default_endpoint - assert LoggingServiceV2Client._get_api_endpoint(None, None, default_universe, "always") == LoggingServiceV2Client.DEFAULT_MTLS_ENDPOINT - assert LoggingServiceV2Client._get_api_endpoint(None, mock_client_cert_source, default_universe, "always") == LoggingServiceV2Client.DEFAULT_MTLS_ENDPOINT - assert LoggingServiceV2Client._get_api_endpoint(None, None, mock_universe, "never") == mock_endpoint - assert LoggingServiceV2Client._get_api_endpoint(None, None, default_universe, "never") == default_endpoint - - with pytest.raises(MutualTLSChannelError) as excinfo: - LoggingServiceV2Client._get_api_endpoint(None, mock_client_cert_source, mock_universe, "auto") - assert str(excinfo.value) == "mTLS is not supported in any universe other than googleapis.com." - - -def test__get_universe_domain(): - client_universe_domain = "foo.com" - universe_domain_env = "bar.com" - - assert LoggingServiceV2Client._get_universe_domain(client_universe_domain, universe_domain_env) == client_universe_domain - assert LoggingServiceV2Client._get_universe_domain(None, universe_domain_env) == universe_domain_env - assert LoggingServiceV2Client._get_universe_domain(None, None) == LoggingServiceV2Client._DEFAULT_UNIVERSE - - with pytest.raises(ValueError) as excinfo: - LoggingServiceV2Client._get_universe_domain("", None) - assert str(excinfo.value) == "Universe Domain cannot be an empty string." - @pytest.mark.parametrize("error_code,cred_info_json,show_cred_info", [ (401, CRED_INFO_JSON, True), (403, CRED_INFO_JSON, True), diff --git a/packages/gapic-generator/tests/integration/goldens/logging/tests/unit/gapic/logging_v2/test_metrics_service_v2.py b/packages/gapic-generator/tests/integration/goldens/logging/tests/unit/gapic/logging_v2/test_metrics_service_v2.py index 90cdab2be2b2..0ea7c5f6ac0f 100755 --- a/packages/gapic-generator/tests/integration/goldens/logging/tests/unit/gapic/logging_v2/test_metrics_service_v2.py +++ b/packages/gapic-generator/tests/integration/goldens/logging/tests/unit/gapic/logging_v2/test_metrics_service_v2.py @@ -268,41 +268,6 @@ def test__get_client_cert_source(): assert MetricsServiceV2Client._get_client_cert_source(None, True) is mock_default_cert_source assert MetricsServiceV2Client._get_client_cert_source(mock_provided_cert_source, "true") is mock_provided_cert_source -@mock.patch.object(MetricsServiceV2Client, "_DEFAULT_ENDPOINT_TEMPLATE", modify_default_endpoint_template(MetricsServiceV2Client)) -@mock.patch.object(MetricsServiceV2AsyncClient, "_DEFAULT_ENDPOINT_TEMPLATE", modify_default_endpoint_template(MetricsServiceV2AsyncClient)) -def test__get_api_endpoint(): - api_override = "foo.com" - mock_client_cert_source = mock.Mock() - default_universe = MetricsServiceV2Client._DEFAULT_UNIVERSE - default_endpoint = MetricsServiceV2Client._DEFAULT_ENDPOINT_TEMPLATE.format(UNIVERSE_DOMAIN=default_universe) - mock_universe = "bar.com" - mock_endpoint = MetricsServiceV2Client._DEFAULT_ENDPOINT_TEMPLATE.format(UNIVERSE_DOMAIN=mock_universe) - - assert MetricsServiceV2Client._get_api_endpoint(api_override, mock_client_cert_source, default_universe, "always") == api_override - assert MetricsServiceV2Client._get_api_endpoint(None, mock_client_cert_source, default_universe, "auto") == MetricsServiceV2Client.DEFAULT_MTLS_ENDPOINT - assert MetricsServiceV2Client._get_api_endpoint(None, None, default_universe, "auto") == default_endpoint - assert MetricsServiceV2Client._get_api_endpoint(None, None, default_universe, "always") == MetricsServiceV2Client.DEFAULT_MTLS_ENDPOINT - assert MetricsServiceV2Client._get_api_endpoint(None, mock_client_cert_source, default_universe, "always") == MetricsServiceV2Client.DEFAULT_MTLS_ENDPOINT - assert MetricsServiceV2Client._get_api_endpoint(None, None, mock_universe, "never") == mock_endpoint - assert MetricsServiceV2Client._get_api_endpoint(None, None, default_universe, "never") == default_endpoint - - with pytest.raises(MutualTLSChannelError) as excinfo: - MetricsServiceV2Client._get_api_endpoint(None, mock_client_cert_source, mock_universe, "auto") - assert str(excinfo.value) == "mTLS is not supported in any universe other than googleapis.com." - - -def test__get_universe_domain(): - client_universe_domain = "foo.com" - universe_domain_env = "bar.com" - - assert MetricsServiceV2Client._get_universe_domain(client_universe_domain, universe_domain_env) == client_universe_domain - assert MetricsServiceV2Client._get_universe_domain(None, universe_domain_env) == universe_domain_env - assert MetricsServiceV2Client._get_universe_domain(None, None) == MetricsServiceV2Client._DEFAULT_UNIVERSE - - with pytest.raises(ValueError) as excinfo: - MetricsServiceV2Client._get_universe_domain("", None) - assert str(excinfo.value) == "Universe Domain cannot be an empty string." - @pytest.mark.parametrize("error_code,cred_info_json,show_cred_info", [ (401, CRED_INFO_JSON, True), (403, CRED_INFO_JSON, True), diff --git a/packages/gapic-generator/tests/integration/goldens/logging_internal/google/cloud/logging_v2/services/config_service_v2/client.py b/packages/gapic-generator/tests/integration/goldens/logging_internal/google/cloud/logging_v2/services/config_service_v2/client.py index 61204cb87a52..2d7bf1f9147f 100755 --- a/packages/gapic-generator/tests/integration/goldens/logging_internal/google/cloud/logging_v2/services/config_service_v2/client.py +++ b/packages/gapic-generator/tests/integration/goldens/logging_internal/google/cloud/logging_v2/services/config_service_v2/client.py @@ -27,6 +27,7 @@ from google.api_core import client_options as client_options_lib from google.api_core import exceptions as core_exceptions from google.api_core import gapic_v1 +from google.api_core.gapic_v1 import routing from google.api_core import retry as retries from google.auth import credentials as ga_credentials # type: ignore from google.auth.transport import mtls # type: ignore @@ -97,38 +98,9 @@ class BaseConfigServiceV2Client(metaclass=BaseConfigServiceV2ClientMeta): """Service for configuring sinks used to route log entries.""" @staticmethod - def _get_default_mtls_endpoint(api_endpoint) -> Optional[str]: - """Converts api endpoint to mTLS endpoint. - - Convert "*.sandbox.googleapis.com" and "*.googleapis.com" to - "*.mtls.sandbox.googleapis.com" and "*.mtls.googleapis.com" respectively. - Args: - api_endpoint (Optional[str]): the api endpoint to convert. - Returns: - Optional[str]: converted mTLS api endpoint. - """ - if not api_endpoint: - return api_endpoint - - mtls_endpoint_re = re.compile( - r"(?P[^.]+)(?P\.mtls)?(?P\.sandbox)?(?P\.googleapis\.com)?" - ) - - m = mtls_endpoint_re.match(api_endpoint) - if m is None: - # Could not parse api_endpoint; return as-is. - return api_endpoint - - name, mtls, sandbox, googledomain = m.groups() - if mtls or not googledomain: - return api_endpoint - - if sandbox: - return api_endpoint.replace( - "sandbox.googleapis.com", "mtls.sandbox.googleapis.com" - ) - - return api_endpoint.replace(".googleapis.com", ".mtls.googleapis.com") + def _get_default_mtls_endpoint(api_endpoint: Optional[str]) -> Optional[str]: + """Converts api endpoint to mTLS endpoint.""" + return routing.get_default_mtls_endpoint(api_endpoint) # Note: DEFAULT_ENDPOINT is deprecated. Use _DEFAULT_ENDPOINT_TEMPLATE instead. DEFAULT_ENDPOINT = "logging.googleapis.com" @@ -446,53 +418,31 @@ def _get_client_cert_source(provided_cert_source, use_cert_flag): return client_cert_source @staticmethod - def _get_api_endpoint(api_override, client_cert_source, universe_domain, use_mtls_endpoint) -> str: - """Return the API endpoint used by the client. - - Args: - api_override (str): The API endpoint override. If specified, this is always - the return value of this function and the other arguments are not used. - client_cert_source (bytes): The client certificate source used by the client. - universe_domain (str): The universe domain used by the client. - use_mtls_endpoint (str): How to use the mTLS endpoint, which depends also on the other parameters. - Possible values are "always", "auto", or "never". - - Returns: - str: The API endpoint to be used by the client. - """ - if api_override is not None: - api_endpoint = api_override - elif use_mtls_endpoint == "always" or (use_mtls_endpoint == "auto" and client_cert_source): - _default_universe = BaseConfigServiceV2Client._DEFAULT_UNIVERSE - if universe_domain != _default_universe: - raise MutualTLSChannelError(f"mTLS is not supported in any universe other than {_default_universe}.") - api_endpoint = BaseConfigServiceV2Client.DEFAULT_MTLS_ENDPOINT - else: - api_endpoint = BaseConfigServiceV2Client._DEFAULT_ENDPOINT_TEMPLATE.format(UNIVERSE_DOMAIN=universe_domain) - return api_endpoint + def _get_api_endpoint( + api_override: Optional[str], + client_cert_source: Optional[Callable[[], Tuple[bytes, bytes]]], + universe_domain: str, + use_mtls_endpoint: str, + ) -> str: + """Return the API endpoint used by the client.""" + return routing.get_api_endpoint( + api_override, + client_cert_source, + universe_domain, + use_mtls_endpoint, + BaseConfigServiceV2Client._DEFAULT_UNIVERSE, + BaseConfigServiceV2Client.DEFAULT_MTLS_ENDPOINT, + BaseConfigServiceV2Client._DEFAULT_ENDPOINT_TEMPLATE, + ) @staticmethod def _get_universe_domain(client_universe_domain: Optional[str], universe_domain_env: Optional[str]) -> str: - """Return the universe domain used by the client. - - Args: - client_universe_domain (Optional[str]): The universe domain configured via the client options. - universe_domain_env (Optional[str]): The universe domain configured via the "GOOGLE_CLOUD_UNIVERSE_DOMAIN" environment variable. - - Returns: - str: The universe domain to be used by the client. - - Raises: - ValueError: If the universe domain is an empty string. - """ - universe_domain = BaseConfigServiceV2Client._DEFAULT_UNIVERSE - if client_universe_domain is not None: - universe_domain = client_universe_domain - elif universe_domain_env is not None: - universe_domain = universe_domain_env - if len(universe_domain.strip()) == 0: - raise ValueError("Universe Domain cannot be an empty string.") - return universe_domain + """Return the universe domain used by the client.""" + return routing.get_universe_domain( + client_universe_domain, + universe_domain_env, + BaseConfigServiceV2Client._DEFAULT_UNIVERSE, + ) def _validate_universe_domain(self): """Validates client's and credentials' universe domains are consistent. diff --git a/packages/gapic-generator/tests/integration/goldens/logging_internal/google/cloud/logging_v2/services/logging_service_v2/client.py b/packages/gapic-generator/tests/integration/goldens/logging_internal/google/cloud/logging_v2/services/logging_service_v2/client.py index e89762755eda..effd54581fac 100755 --- a/packages/gapic-generator/tests/integration/goldens/logging_internal/google/cloud/logging_v2/services/logging_service_v2/client.py +++ b/packages/gapic-generator/tests/integration/goldens/logging_internal/google/cloud/logging_v2/services/logging_service_v2/client.py @@ -27,6 +27,7 @@ from google.api_core import client_options as client_options_lib from google.api_core import exceptions as core_exceptions from google.api_core import gapic_v1 +from google.api_core.gapic_v1 import routing from google.api_core import retry as retries from google.auth import credentials as ga_credentials # type: ignore from google.auth.transport import mtls # type: ignore @@ -94,38 +95,9 @@ class LoggingServiceV2Client(metaclass=LoggingServiceV2ClientMeta): """Service for ingesting and querying logs.""" @staticmethod - def _get_default_mtls_endpoint(api_endpoint) -> Optional[str]: - """Converts api endpoint to mTLS endpoint. - - Convert "*.sandbox.googleapis.com" and "*.googleapis.com" to - "*.mtls.sandbox.googleapis.com" and "*.mtls.googleapis.com" respectively. - Args: - api_endpoint (Optional[str]): the api endpoint to convert. - Returns: - Optional[str]: converted mTLS api endpoint. - """ - if not api_endpoint: - return api_endpoint - - mtls_endpoint_re = re.compile( - r"(?P[^.]+)(?P\.mtls)?(?P\.sandbox)?(?P\.googleapis\.com)?" - ) - - m = mtls_endpoint_re.match(api_endpoint) - if m is None: - # Could not parse api_endpoint; return as-is. - return api_endpoint - - name, mtls, sandbox, googledomain = m.groups() - if mtls or not googledomain: - return api_endpoint - - if sandbox: - return api_endpoint.replace( - "sandbox.googleapis.com", "mtls.sandbox.googleapis.com" - ) - - return api_endpoint.replace(".googleapis.com", ".mtls.googleapis.com") + def _get_default_mtls_endpoint(api_endpoint: Optional[str]) -> Optional[str]: + """Converts api endpoint to mTLS endpoint.""" + return routing.get_default_mtls_endpoint(api_endpoint) # Note: DEFAULT_ENDPOINT is deprecated. Use _DEFAULT_ENDPOINT_TEMPLATE instead. DEFAULT_ENDPOINT = "logging.googleapis.com" @@ -377,53 +349,31 @@ def _get_client_cert_source(provided_cert_source, use_cert_flag): return client_cert_source @staticmethod - def _get_api_endpoint(api_override, client_cert_source, universe_domain, use_mtls_endpoint) -> str: - """Return the API endpoint used by the client. - - Args: - api_override (str): The API endpoint override. If specified, this is always - the return value of this function and the other arguments are not used. - client_cert_source (bytes): The client certificate source used by the client. - universe_domain (str): The universe domain used by the client. - use_mtls_endpoint (str): How to use the mTLS endpoint, which depends also on the other parameters. - Possible values are "always", "auto", or "never". - - Returns: - str: The API endpoint to be used by the client. - """ - if api_override is not None: - api_endpoint = api_override - elif use_mtls_endpoint == "always" or (use_mtls_endpoint == "auto" and client_cert_source): - _default_universe = LoggingServiceV2Client._DEFAULT_UNIVERSE - if universe_domain != _default_universe: - raise MutualTLSChannelError(f"mTLS is not supported in any universe other than {_default_universe}.") - api_endpoint = LoggingServiceV2Client.DEFAULT_MTLS_ENDPOINT - else: - api_endpoint = LoggingServiceV2Client._DEFAULT_ENDPOINT_TEMPLATE.format(UNIVERSE_DOMAIN=universe_domain) - return api_endpoint + def _get_api_endpoint( + api_override: Optional[str], + client_cert_source: Optional[Callable[[], Tuple[bytes, bytes]]], + universe_domain: str, + use_mtls_endpoint: str, + ) -> str: + """Return the API endpoint used by the client.""" + return routing.get_api_endpoint( + api_override, + client_cert_source, + universe_domain, + use_mtls_endpoint, + LoggingServiceV2Client._DEFAULT_UNIVERSE, + LoggingServiceV2Client.DEFAULT_MTLS_ENDPOINT, + LoggingServiceV2Client._DEFAULT_ENDPOINT_TEMPLATE, + ) @staticmethod def _get_universe_domain(client_universe_domain: Optional[str], universe_domain_env: Optional[str]) -> str: - """Return the universe domain used by the client. - - Args: - client_universe_domain (Optional[str]): The universe domain configured via the client options. - universe_domain_env (Optional[str]): The universe domain configured via the "GOOGLE_CLOUD_UNIVERSE_DOMAIN" environment variable. - - Returns: - str: The universe domain to be used by the client. - - Raises: - ValueError: If the universe domain is an empty string. - """ - universe_domain = LoggingServiceV2Client._DEFAULT_UNIVERSE - if client_universe_domain is not None: - universe_domain = client_universe_domain - elif universe_domain_env is not None: - universe_domain = universe_domain_env - if len(universe_domain.strip()) == 0: - raise ValueError("Universe Domain cannot be an empty string.") - return universe_domain + """Return the universe domain used by the client.""" + return routing.get_universe_domain( + client_universe_domain, + universe_domain_env, + LoggingServiceV2Client._DEFAULT_UNIVERSE, + ) def _validate_universe_domain(self): """Validates client's and credentials' universe domains are consistent. diff --git a/packages/gapic-generator/tests/integration/goldens/logging_internal/google/cloud/logging_v2/services/metrics_service_v2/client.py b/packages/gapic-generator/tests/integration/goldens/logging_internal/google/cloud/logging_v2/services/metrics_service_v2/client.py index fa55137223d1..7105f0d4b61e 100755 --- a/packages/gapic-generator/tests/integration/goldens/logging_internal/google/cloud/logging_v2/services/metrics_service_v2/client.py +++ b/packages/gapic-generator/tests/integration/goldens/logging_internal/google/cloud/logging_v2/services/metrics_service_v2/client.py @@ -27,6 +27,7 @@ from google.api_core import client_options as client_options_lib from google.api_core import exceptions as core_exceptions from google.api_core import gapic_v1 +from google.api_core.gapic_v1 import routing from google.api_core import retry as retries from google.auth import credentials as ga_credentials # type: ignore from google.auth.transport import mtls # type: ignore @@ -95,38 +96,9 @@ class BaseMetricsServiceV2Client(metaclass=BaseMetricsServiceV2ClientMeta): """Service for configuring logs-based metrics.""" @staticmethod - def _get_default_mtls_endpoint(api_endpoint) -> Optional[str]: - """Converts api endpoint to mTLS endpoint. - - Convert "*.sandbox.googleapis.com" and "*.googleapis.com" to - "*.mtls.sandbox.googleapis.com" and "*.mtls.googleapis.com" respectively. - Args: - api_endpoint (Optional[str]): the api endpoint to convert. - Returns: - Optional[str]: converted mTLS api endpoint. - """ - if not api_endpoint: - return api_endpoint - - mtls_endpoint_re = re.compile( - r"(?P[^.]+)(?P\.mtls)?(?P\.sandbox)?(?P\.googleapis\.com)?" - ) - - m = mtls_endpoint_re.match(api_endpoint) - if m is None: - # Could not parse api_endpoint; return as-is. - return api_endpoint - - name, mtls, sandbox, googledomain = m.groups() - if mtls or not googledomain: - return api_endpoint - - if sandbox: - return api_endpoint.replace( - "sandbox.googleapis.com", "mtls.sandbox.googleapis.com" - ) - - return api_endpoint.replace(".googleapis.com", ".mtls.googleapis.com") + def _get_default_mtls_endpoint(api_endpoint: Optional[str]) -> Optional[str]: + """Converts api endpoint to mTLS endpoint.""" + return routing.get_default_mtls_endpoint(api_endpoint) # Note: DEFAULT_ENDPOINT is deprecated. Use _DEFAULT_ENDPOINT_TEMPLATE instead. DEFAULT_ENDPOINT = "logging.googleapis.com" @@ -378,53 +350,31 @@ def _get_client_cert_source(provided_cert_source, use_cert_flag): return client_cert_source @staticmethod - def _get_api_endpoint(api_override, client_cert_source, universe_domain, use_mtls_endpoint) -> str: - """Return the API endpoint used by the client. - - Args: - api_override (str): The API endpoint override. If specified, this is always - the return value of this function and the other arguments are not used. - client_cert_source (bytes): The client certificate source used by the client. - universe_domain (str): The universe domain used by the client. - use_mtls_endpoint (str): How to use the mTLS endpoint, which depends also on the other parameters. - Possible values are "always", "auto", or "never". - - Returns: - str: The API endpoint to be used by the client. - """ - if api_override is not None: - api_endpoint = api_override - elif use_mtls_endpoint == "always" or (use_mtls_endpoint == "auto" and client_cert_source): - _default_universe = BaseMetricsServiceV2Client._DEFAULT_UNIVERSE - if universe_domain != _default_universe: - raise MutualTLSChannelError(f"mTLS is not supported in any universe other than {_default_universe}.") - api_endpoint = BaseMetricsServiceV2Client.DEFAULT_MTLS_ENDPOINT - else: - api_endpoint = BaseMetricsServiceV2Client._DEFAULT_ENDPOINT_TEMPLATE.format(UNIVERSE_DOMAIN=universe_domain) - return api_endpoint + def _get_api_endpoint( + api_override: Optional[str], + client_cert_source: Optional[Callable[[], Tuple[bytes, bytes]]], + universe_domain: str, + use_mtls_endpoint: str, + ) -> str: + """Return the API endpoint used by the client.""" + return routing.get_api_endpoint( + api_override, + client_cert_source, + universe_domain, + use_mtls_endpoint, + BaseMetricsServiceV2Client._DEFAULT_UNIVERSE, + BaseMetricsServiceV2Client.DEFAULT_MTLS_ENDPOINT, + BaseMetricsServiceV2Client._DEFAULT_ENDPOINT_TEMPLATE, + ) @staticmethod def _get_universe_domain(client_universe_domain: Optional[str], universe_domain_env: Optional[str]) -> str: - """Return the universe domain used by the client. - - Args: - client_universe_domain (Optional[str]): The universe domain configured via the client options. - universe_domain_env (Optional[str]): The universe domain configured via the "GOOGLE_CLOUD_UNIVERSE_DOMAIN" environment variable. - - Returns: - str: The universe domain to be used by the client. - - Raises: - ValueError: If the universe domain is an empty string. - """ - universe_domain = BaseMetricsServiceV2Client._DEFAULT_UNIVERSE - if client_universe_domain is not None: - universe_domain = client_universe_domain - elif universe_domain_env is not None: - universe_domain = universe_domain_env - if len(universe_domain.strip()) == 0: - raise ValueError("Universe Domain cannot be an empty string.") - return universe_domain + """Return the universe domain used by the client.""" + return routing.get_universe_domain( + client_universe_domain, + universe_domain_env, + BaseMetricsServiceV2Client._DEFAULT_UNIVERSE, + ) def _validate_universe_domain(self): """Validates client's and credentials' universe domains are consistent. diff --git a/packages/gapic-generator/tests/integration/goldens/logging_internal/tests/unit/gapic/logging_v2/test_config_service_v2.py b/packages/gapic-generator/tests/integration/goldens/logging_internal/tests/unit/gapic/logging_v2/test_config_service_v2.py index 9eec837e6f58..721065281a44 100755 --- a/packages/gapic-generator/tests/integration/goldens/logging_internal/tests/unit/gapic/logging_v2/test_config_service_v2.py +++ b/packages/gapic-generator/tests/integration/goldens/logging_internal/tests/unit/gapic/logging_v2/test_config_service_v2.py @@ -269,41 +269,6 @@ def test__get_client_cert_source(): assert BaseConfigServiceV2Client._get_client_cert_source(None, True) is mock_default_cert_source assert BaseConfigServiceV2Client._get_client_cert_source(mock_provided_cert_source, "true") is mock_provided_cert_source -@mock.patch.object(BaseConfigServiceV2Client, "_DEFAULT_ENDPOINT_TEMPLATE", modify_default_endpoint_template(BaseConfigServiceV2Client)) -@mock.patch.object(BaseConfigServiceV2AsyncClient, "_DEFAULT_ENDPOINT_TEMPLATE", modify_default_endpoint_template(BaseConfigServiceV2AsyncClient)) -def test__get_api_endpoint(): - api_override = "foo.com" - mock_client_cert_source = mock.Mock() - default_universe = BaseConfigServiceV2Client._DEFAULT_UNIVERSE - default_endpoint = BaseConfigServiceV2Client._DEFAULT_ENDPOINT_TEMPLATE.format(UNIVERSE_DOMAIN=default_universe) - mock_universe = "bar.com" - mock_endpoint = BaseConfigServiceV2Client._DEFAULT_ENDPOINT_TEMPLATE.format(UNIVERSE_DOMAIN=mock_universe) - - assert BaseConfigServiceV2Client._get_api_endpoint(api_override, mock_client_cert_source, default_universe, "always") == api_override - assert BaseConfigServiceV2Client._get_api_endpoint(None, mock_client_cert_source, default_universe, "auto") == BaseConfigServiceV2Client.DEFAULT_MTLS_ENDPOINT - assert BaseConfigServiceV2Client._get_api_endpoint(None, None, default_universe, "auto") == default_endpoint - assert BaseConfigServiceV2Client._get_api_endpoint(None, None, default_universe, "always") == BaseConfigServiceV2Client.DEFAULT_MTLS_ENDPOINT - assert BaseConfigServiceV2Client._get_api_endpoint(None, mock_client_cert_source, default_universe, "always") == BaseConfigServiceV2Client.DEFAULT_MTLS_ENDPOINT - assert BaseConfigServiceV2Client._get_api_endpoint(None, None, mock_universe, "never") == mock_endpoint - assert BaseConfigServiceV2Client._get_api_endpoint(None, None, default_universe, "never") == default_endpoint - - with pytest.raises(MutualTLSChannelError) as excinfo: - BaseConfigServiceV2Client._get_api_endpoint(None, mock_client_cert_source, mock_universe, "auto") - assert str(excinfo.value) == "mTLS is not supported in any universe other than googleapis.com." - - -def test__get_universe_domain(): - client_universe_domain = "foo.com" - universe_domain_env = "bar.com" - - assert BaseConfigServiceV2Client._get_universe_domain(client_universe_domain, universe_domain_env) == client_universe_domain - assert BaseConfigServiceV2Client._get_universe_domain(None, universe_domain_env) == universe_domain_env - assert BaseConfigServiceV2Client._get_universe_domain(None, None) == BaseConfigServiceV2Client._DEFAULT_UNIVERSE - - with pytest.raises(ValueError) as excinfo: - BaseConfigServiceV2Client._get_universe_domain("", None) - assert str(excinfo.value) == "Universe Domain cannot be an empty string." - @pytest.mark.parametrize("error_code,cred_info_json,show_cred_info", [ (401, CRED_INFO_JSON, True), (403, CRED_INFO_JSON, True), diff --git a/packages/gapic-generator/tests/integration/goldens/logging_internal/tests/unit/gapic/logging_v2/test_logging_service_v2.py b/packages/gapic-generator/tests/integration/goldens/logging_internal/tests/unit/gapic/logging_v2/test_logging_service_v2.py index 65559a5d1073..fb366d46e4ba 100755 --- a/packages/gapic-generator/tests/integration/goldens/logging_internal/tests/unit/gapic/logging_v2/test_logging_service_v2.py +++ b/packages/gapic-generator/tests/integration/goldens/logging_internal/tests/unit/gapic/logging_v2/test_logging_service_v2.py @@ -270,41 +270,6 @@ def test__get_client_cert_source(): assert LoggingServiceV2Client._get_client_cert_source(None, True) is mock_default_cert_source assert LoggingServiceV2Client._get_client_cert_source(mock_provided_cert_source, "true") is mock_provided_cert_source -@mock.patch.object(LoggingServiceV2Client, "_DEFAULT_ENDPOINT_TEMPLATE", modify_default_endpoint_template(LoggingServiceV2Client)) -@mock.patch.object(LoggingServiceV2AsyncClient, "_DEFAULT_ENDPOINT_TEMPLATE", modify_default_endpoint_template(LoggingServiceV2AsyncClient)) -def test__get_api_endpoint(): - api_override = "foo.com" - mock_client_cert_source = mock.Mock() - default_universe = LoggingServiceV2Client._DEFAULT_UNIVERSE - default_endpoint = LoggingServiceV2Client._DEFAULT_ENDPOINT_TEMPLATE.format(UNIVERSE_DOMAIN=default_universe) - mock_universe = "bar.com" - mock_endpoint = LoggingServiceV2Client._DEFAULT_ENDPOINT_TEMPLATE.format(UNIVERSE_DOMAIN=mock_universe) - - assert LoggingServiceV2Client._get_api_endpoint(api_override, mock_client_cert_source, default_universe, "always") == api_override - assert LoggingServiceV2Client._get_api_endpoint(None, mock_client_cert_source, default_universe, "auto") == LoggingServiceV2Client.DEFAULT_MTLS_ENDPOINT - assert LoggingServiceV2Client._get_api_endpoint(None, None, default_universe, "auto") == default_endpoint - assert LoggingServiceV2Client._get_api_endpoint(None, None, default_universe, "always") == LoggingServiceV2Client.DEFAULT_MTLS_ENDPOINT - assert LoggingServiceV2Client._get_api_endpoint(None, mock_client_cert_source, default_universe, "always") == LoggingServiceV2Client.DEFAULT_MTLS_ENDPOINT - assert LoggingServiceV2Client._get_api_endpoint(None, None, mock_universe, "never") == mock_endpoint - assert LoggingServiceV2Client._get_api_endpoint(None, None, default_universe, "never") == default_endpoint - - with pytest.raises(MutualTLSChannelError) as excinfo: - LoggingServiceV2Client._get_api_endpoint(None, mock_client_cert_source, mock_universe, "auto") - assert str(excinfo.value) == "mTLS is not supported in any universe other than googleapis.com." - - -def test__get_universe_domain(): - client_universe_domain = "foo.com" - universe_domain_env = "bar.com" - - assert LoggingServiceV2Client._get_universe_domain(client_universe_domain, universe_domain_env) == client_universe_domain - assert LoggingServiceV2Client._get_universe_domain(None, universe_domain_env) == universe_domain_env - assert LoggingServiceV2Client._get_universe_domain(None, None) == LoggingServiceV2Client._DEFAULT_UNIVERSE - - with pytest.raises(ValueError) as excinfo: - LoggingServiceV2Client._get_universe_domain("", None) - assert str(excinfo.value) == "Universe Domain cannot be an empty string." - @pytest.mark.parametrize("error_code,cred_info_json,show_cred_info", [ (401, CRED_INFO_JSON, True), (403, CRED_INFO_JSON, True), diff --git a/packages/gapic-generator/tests/integration/goldens/logging_internal/tests/unit/gapic/logging_v2/test_metrics_service_v2.py b/packages/gapic-generator/tests/integration/goldens/logging_internal/tests/unit/gapic/logging_v2/test_metrics_service_v2.py index 310677b64bc6..8720f8d417ff 100755 --- a/packages/gapic-generator/tests/integration/goldens/logging_internal/tests/unit/gapic/logging_v2/test_metrics_service_v2.py +++ b/packages/gapic-generator/tests/integration/goldens/logging_internal/tests/unit/gapic/logging_v2/test_metrics_service_v2.py @@ -268,41 +268,6 @@ def test__get_client_cert_source(): assert BaseMetricsServiceV2Client._get_client_cert_source(None, True) is mock_default_cert_source assert BaseMetricsServiceV2Client._get_client_cert_source(mock_provided_cert_source, "true") is mock_provided_cert_source -@mock.patch.object(BaseMetricsServiceV2Client, "_DEFAULT_ENDPOINT_TEMPLATE", modify_default_endpoint_template(BaseMetricsServiceV2Client)) -@mock.patch.object(BaseMetricsServiceV2AsyncClient, "_DEFAULT_ENDPOINT_TEMPLATE", modify_default_endpoint_template(BaseMetricsServiceV2AsyncClient)) -def test__get_api_endpoint(): - api_override = "foo.com" - mock_client_cert_source = mock.Mock() - default_universe = BaseMetricsServiceV2Client._DEFAULT_UNIVERSE - default_endpoint = BaseMetricsServiceV2Client._DEFAULT_ENDPOINT_TEMPLATE.format(UNIVERSE_DOMAIN=default_universe) - mock_universe = "bar.com" - mock_endpoint = BaseMetricsServiceV2Client._DEFAULT_ENDPOINT_TEMPLATE.format(UNIVERSE_DOMAIN=mock_universe) - - assert BaseMetricsServiceV2Client._get_api_endpoint(api_override, mock_client_cert_source, default_universe, "always") == api_override - assert BaseMetricsServiceV2Client._get_api_endpoint(None, mock_client_cert_source, default_universe, "auto") == BaseMetricsServiceV2Client.DEFAULT_MTLS_ENDPOINT - assert BaseMetricsServiceV2Client._get_api_endpoint(None, None, default_universe, "auto") == default_endpoint - assert BaseMetricsServiceV2Client._get_api_endpoint(None, None, default_universe, "always") == BaseMetricsServiceV2Client.DEFAULT_MTLS_ENDPOINT - assert BaseMetricsServiceV2Client._get_api_endpoint(None, mock_client_cert_source, default_universe, "always") == BaseMetricsServiceV2Client.DEFAULT_MTLS_ENDPOINT - assert BaseMetricsServiceV2Client._get_api_endpoint(None, None, mock_universe, "never") == mock_endpoint - assert BaseMetricsServiceV2Client._get_api_endpoint(None, None, default_universe, "never") == default_endpoint - - with pytest.raises(MutualTLSChannelError) as excinfo: - BaseMetricsServiceV2Client._get_api_endpoint(None, mock_client_cert_source, mock_universe, "auto") - assert str(excinfo.value) == "mTLS is not supported in any universe other than googleapis.com." - - -def test__get_universe_domain(): - client_universe_domain = "foo.com" - universe_domain_env = "bar.com" - - assert BaseMetricsServiceV2Client._get_universe_domain(client_universe_domain, universe_domain_env) == client_universe_domain - assert BaseMetricsServiceV2Client._get_universe_domain(None, universe_domain_env) == universe_domain_env - assert BaseMetricsServiceV2Client._get_universe_domain(None, None) == BaseMetricsServiceV2Client._DEFAULT_UNIVERSE - - with pytest.raises(ValueError) as excinfo: - BaseMetricsServiceV2Client._get_universe_domain("", None) - assert str(excinfo.value) == "Universe Domain cannot be an empty string." - @pytest.mark.parametrize("error_code,cred_info_json,show_cred_info", [ (401, CRED_INFO_JSON, True), (403, CRED_INFO_JSON, True), diff --git a/packages/gapic-generator/tests/integration/goldens/redis/google/cloud/redis_v1/services/cloud_redis/client.py b/packages/gapic-generator/tests/integration/goldens/redis/google/cloud/redis_v1/services/cloud_redis/client.py index 33ccce478c8e..5a302701e64e 100755 --- a/packages/gapic-generator/tests/integration/goldens/redis/google/cloud/redis_v1/services/cloud_redis/client.py +++ b/packages/gapic-generator/tests/integration/goldens/redis/google/cloud/redis_v1/services/cloud_redis/client.py @@ -27,6 +27,7 @@ from google.api_core import client_options as client_options_lib from google.api_core import exceptions as core_exceptions from google.api_core import gapic_v1 +from google.api_core.gapic_v1 import routing from google.api_core import retry as retries from google.auth import credentials as ga_credentials # type: ignore from google.auth.transport import mtls # type: ignore @@ -132,38 +133,9 @@ class CloudRedisClient(metaclass=CloudRedisClientMeta): """ @staticmethod - def _get_default_mtls_endpoint(api_endpoint) -> Optional[str]: - """Converts api endpoint to mTLS endpoint. - - Convert "*.sandbox.googleapis.com" and "*.googleapis.com" to - "*.mtls.sandbox.googleapis.com" and "*.mtls.googleapis.com" respectively. - Args: - api_endpoint (Optional[str]): the api endpoint to convert. - Returns: - Optional[str]: converted mTLS api endpoint. - """ - if not api_endpoint: - return api_endpoint - - mtls_endpoint_re = re.compile( - r"(?P[^.]+)(?P\.mtls)?(?P\.sandbox)?(?P\.googleapis\.com)?" - ) - - m = mtls_endpoint_re.match(api_endpoint) - if m is None: - # Could not parse api_endpoint; return as-is. - return api_endpoint - - name, mtls, sandbox, googledomain = m.groups() - if mtls or not googledomain: - return api_endpoint - - if sandbox: - return api_endpoint.replace( - "sandbox.googleapis.com", "mtls.sandbox.googleapis.com" - ) - - return api_endpoint.replace(".googleapis.com", ".mtls.googleapis.com") + def _get_default_mtls_endpoint(api_endpoint: Optional[str]) -> Optional[str]: + """Converts api endpoint to mTLS endpoint.""" + return routing.get_default_mtls_endpoint(api_endpoint) # Note: DEFAULT_ENDPOINT is deprecated. Use _DEFAULT_ENDPOINT_TEMPLATE instead. DEFAULT_ENDPOINT = "redis.googleapis.com" @@ -415,53 +387,31 @@ def _get_client_cert_source(provided_cert_source, use_cert_flag): return client_cert_source @staticmethod - def _get_api_endpoint(api_override, client_cert_source, universe_domain, use_mtls_endpoint) -> str: - """Return the API endpoint used by the client. - - Args: - api_override (str): The API endpoint override. If specified, this is always - the return value of this function and the other arguments are not used. - client_cert_source (bytes): The client certificate source used by the client. - universe_domain (str): The universe domain used by the client. - use_mtls_endpoint (str): How to use the mTLS endpoint, which depends also on the other parameters. - Possible values are "always", "auto", or "never". - - Returns: - str: The API endpoint to be used by the client. - """ - if api_override is not None: - api_endpoint = api_override - elif use_mtls_endpoint == "always" or (use_mtls_endpoint == "auto" and client_cert_source): - _default_universe = CloudRedisClient._DEFAULT_UNIVERSE - if universe_domain != _default_universe: - raise MutualTLSChannelError(f"mTLS is not supported in any universe other than {_default_universe}.") - api_endpoint = CloudRedisClient.DEFAULT_MTLS_ENDPOINT - else: - api_endpoint = CloudRedisClient._DEFAULT_ENDPOINT_TEMPLATE.format(UNIVERSE_DOMAIN=universe_domain) - return api_endpoint + def _get_api_endpoint( + api_override: Optional[str], + client_cert_source: Optional[Callable[[], Tuple[bytes, bytes]]], + universe_domain: str, + use_mtls_endpoint: str, + ) -> str: + """Return the API endpoint used by the client.""" + return routing.get_api_endpoint( + api_override, + client_cert_source, + universe_domain, + use_mtls_endpoint, + CloudRedisClient._DEFAULT_UNIVERSE, + CloudRedisClient.DEFAULT_MTLS_ENDPOINT, + CloudRedisClient._DEFAULT_ENDPOINT_TEMPLATE, + ) @staticmethod def _get_universe_domain(client_universe_domain: Optional[str], universe_domain_env: Optional[str]) -> str: - """Return the universe domain used by the client. - - Args: - client_universe_domain (Optional[str]): The universe domain configured via the client options. - universe_domain_env (Optional[str]): The universe domain configured via the "GOOGLE_CLOUD_UNIVERSE_DOMAIN" environment variable. - - Returns: - str: The universe domain to be used by the client. - - Raises: - ValueError: If the universe domain is an empty string. - """ - universe_domain = CloudRedisClient._DEFAULT_UNIVERSE - if client_universe_domain is not None: - universe_domain = client_universe_domain - elif universe_domain_env is not None: - universe_domain = universe_domain_env - if len(universe_domain.strip()) == 0: - raise ValueError("Universe Domain cannot be an empty string.") - return universe_domain + """Return the universe domain used by the client.""" + return routing.get_universe_domain( + client_universe_domain, + universe_domain_env, + CloudRedisClient._DEFAULT_UNIVERSE, + ) def _validate_universe_domain(self): """Validates client's and credentials' universe domains are consistent. diff --git a/packages/gapic-generator/tests/integration/goldens/redis/tests/unit/gapic/redis_v1/test_cloud_redis.py b/packages/gapic-generator/tests/integration/goldens/redis/tests/unit/gapic/redis_v1/test_cloud_redis.py index 8ca1fb5194a6..c01842d5353e 100755 --- a/packages/gapic-generator/tests/integration/goldens/redis/tests/unit/gapic/redis_v1/test_cloud_redis.py +++ b/packages/gapic-generator/tests/integration/goldens/redis/tests/unit/gapic/redis_v1/test_cloud_redis.py @@ -286,41 +286,6 @@ def test__get_client_cert_source(): assert CloudRedisClient._get_client_cert_source(None, True) is mock_default_cert_source assert CloudRedisClient._get_client_cert_source(mock_provided_cert_source, "true") is mock_provided_cert_source -@mock.patch.object(CloudRedisClient, "_DEFAULT_ENDPOINT_TEMPLATE", modify_default_endpoint_template(CloudRedisClient)) -@mock.patch.object(CloudRedisAsyncClient, "_DEFAULT_ENDPOINT_TEMPLATE", modify_default_endpoint_template(CloudRedisAsyncClient)) -def test__get_api_endpoint(): - api_override = "foo.com" - mock_client_cert_source = mock.Mock() - default_universe = CloudRedisClient._DEFAULT_UNIVERSE - default_endpoint = CloudRedisClient._DEFAULT_ENDPOINT_TEMPLATE.format(UNIVERSE_DOMAIN=default_universe) - mock_universe = "bar.com" - mock_endpoint = CloudRedisClient._DEFAULT_ENDPOINT_TEMPLATE.format(UNIVERSE_DOMAIN=mock_universe) - - assert CloudRedisClient._get_api_endpoint(api_override, mock_client_cert_source, default_universe, "always") == api_override - assert CloudRedisClient._get_api_endpoint(None, mock_client_cert_source, default_universe, "auto") == CloudRedisClient.DEFAULT_MTLS_ENDPOINT - assert CloudRedisClient._get_api_endpoint(None, None, default_universe, "auto") == default_endpoint - assert CloudRedisClient._get_api_endpoint(None, None, default_universe, "always") == CloudRedisClient.DEFAULT_MTLS_ENDPOINT - assert CloudRedisClient._get_api_endpoint(None, mock_client_cert_source, default_universe, "always") == CloudRedisClient.DEFAULT_MTLS_ENDPOINT - assert CloudRedisClient._get_api_endpoint(None, None, mock_universe, "never") == mock_endpoint - assert CloudRedisClient._get_api_endpoint(None, None, default_universe, "never") == default_endpoint - - with pytest.raises(MutualTLSChannelError) as excinfo: - CloudRedisClient._get_api_endpoint(None, mock_client_cert_source, mock_universe, "auto") - assert str(excinfo.value) == "mTLS is not supported in any universe other than googleapis.com." - - -def test__get_universe_domain(): - client_universe_domain = "foo.com" - universe_domain_env = "bar.com" - - assert CloudRedisClient._get_universe_domain(client_universe_domain, universe_domain_env) == client_universe_domain - assert CloudRedisClient._get_universe_domain(None, universe_domain_env) == universe_domain_env - assert CloudRedisClient._get_universe_domain(None, None) == CloudRedisClient._DEFAULT_UNIVERSE - - with pytest.raises(ValueError) as excinfo: - CloudRedisClient._get_universe_domain("", None) - assert str(excinfo.value) == "Universe Domain cannot be an empty string." - @pytest.mark.parametrize("error_code,cred_info_json,show_cred_info", [ (401, CRED_INFO_JSON, True), (403, CRED_INFO_JSON, True), diff --git a/packages/gapic-generator/tests/integration/goldens/redis_selective/google/cloud/redis_v1/services/cloud_redis/client.py b/packages/gapic-generator/tests/integration/goldens/redis_selective/google/cloud/redis_v1/services/cloud_redis/client.py index 031573ef83d7..fadfa3b9a781 100755 --- a/packages/gapic-generator/tests/integration/goldens/redis_selective/google/cloud/redis_v1/services/cloud_redis/client.py +++ b/packages/gapic-generator/tests/integration/goldens/redis_selective/google/cloud/redis_v1/services/cloud_redis/client.py @@ -27,6 +27,7 @@ from google.api_core import client_options as client_options_lib from google.api_core import exceptions as core_exceptions from google.api_core import gapic_v1 +from google.api_core.gapic_v1 import routing from google.api_core import retry as retries from google.auth import credentials as ga_credentials # type: ignore from google.auth.transport import mtls # type: ignore @@ -132,38 +133,9 @@ class CloudRedisClient(metaclass=CloudRedisClientMeta): """ @staticmethod - def _get_default_mtls_endpoint(api_endpoint) -> Optional[str]: - """Converts api endpoint to mTLS endpoint. - - Convert "*.sandbox.googleapis.com" and "*.googleapis.com" to - "*.mtls.sandbox.googleapis.com" and "*.mtls.googleapis.com" respectively. - Args: - api_endpoint (Optional[str]): the api endpoint to convert. - Returns: - Optional[str]: converted mTLS api endpoint. - """ - if not api_endpoint: - return api_endpoint - - mtls_endpoint_re = re.compile( - r"(?P[^.]+)(?P\.mtls)?(?P\.sandbox)?(?P\.googleapis\.com)?" - ) - - m = mtls_endpoint_re.match(api_endpoint) - if m is None: - # Could not parse api_endpoint; return as-is. - return api_endpoint - - name, mtls, sandbox, googledomain = m.groups() - if mtls or not googledomain: - return api_endpoint - - if sandbox: - return api_endpoint.replace( - "sandbox.googleapis.com", "mtls.sandbox.googleapis.com" - ) - - return api_endpoint.replace(".googleapis.com", ".mtls.googleapis.com") + def _get_default_mtls_endpoint(api_endpoint: Optional[str]) -> Optional[str]: + """Converts api endpoint to mTLS endpoint.""" + return routing.get_default_mtls_endpoint(api_endpoint) # Note: DEFAULT_ENDPOINT is deprecated. Use _DEFAULT_ENDPOINT_TEMPLATE instead. DEFAULT_ENDPOINT = "redis.googleapis.com" @@ -415,53 +387,31 @@ def _get_client_cert_source(provided_cert_source, use_cert_flag): return client_cert_source @staticmethod - def _get_api_endpoint(api_override, client_cert_source, universe_domain, use_mtls_endpoint) -> str: - """Return the API endpoint used by the client. - - Args: - api_override (str): The API endpoint override. If specified, this is always - the return value of this function and the other arguments are not used. - client_cert_source (bytes): The client certificate source used by the client. - universe_domain (str): The universe domain used by the client. - use_mtls_endpoint (str): How to use the mTLS endpoint, which depends also on the other parameters. - Possible values are "always", "auto", or "never". - - Returns: - str: The API endpoint to be used by the client. - """ - if api_override is not None: - api_endpoint = api_override - elif use_mtls_endpoint == "always" or (use_mtls_endpoint == "auto" and client_cert_source): - _default_universe = CloudRedisClient._DEFAULT_UNIVERSE - if universe_domain != _default_universe: - raise MutualTLSChannelError(f"mTLS is not supported in any universe other than {_default_universe}.") - api_endpoint = CloudRedisClient.DEFAULT_MTLS_ENDPOINT - else: - api_endpoint = CloudRedisClient._DEFAULT_ENDPOINT_TEMPLATE.format(UNIVERSE_DOMAIN=universe_domain) - return api_endpoint + def _get_api_endpoint( + api_override: Optional[str], + client_cert_source: Optional[Callable[[], Tuple[bytes, bytes]]], + universe_domain: str, + use_mtls_endpoint: str, + ) -> str: + """Return the API endpoint used by the client.""" + return routing.get_api_endpoint( + api_override, + client_cert_source, + universe_domain, + use_mtls_endpoint, + CloudRedisClient._DEFAULT_UNIVERSE, + CloudRedisClient.DEFAULT_MTLS_ENDPOINT, + CloudRedisClient._DEFAULT_ENDPOINT_TEMPLATE, + ) @staticmethod def _get_universe_domain(client_universe_domain: Optional[str], universe_domain_env: Optional[str]) -> str: - """Return the universe domain used by the client. - - Args: - client_universe_domain (Optional[str]): The universe domain configured via the client options. - universe_domain_env (Optional[str]): The universe domain configured via the "GOOGLE_CLOUD_UNIVERSE_DOMAIN" environment variable. - - Returns: - str: The universe domain to be used by the client. - - Raises: - ValueError: If the universe domain is an empty string. - """ - universe_domain = CloudRedisClient._DEFAULT_UNIVERSE - if client_universe_domain is not None: - universe_domain = client_universe_domain - elif universe_domain_env is not None: - universe_domain = universe_domain_env - if len(universe_domain.strip()) == 0: - raise ValueError("Universe Domain cannot be an empty string.") - return universe_domain + """Return the universe domain used by the client.""" + return routing.get_universe_domain( + client_universe_domain, + universe_domain_env, + CloudRedisClient._DEFAULT_UNIVERSE, + ) def _validate_universe_domain(self): """Validates client's and credentials' universe domains are consistent. diff --git a/packages/gapic-generator/tests/integration/goldens/redis_selective/tests/unit/gapic/redis_v1/test_cloud_redis.py b/packages/gapic-generator/tests/integration/goldens/redis_selective/tests/unit/gapic/redis_v1/test_cloud_redis.py index 3f6b7aa521f3..1bd0d55af86a 100755 --- a/packages/gapic-generator/tests/integration/goldens/redis_selective/tests/unit/gapic/redis_v1/test_cloud_redis.py +++ b/packages/gapic-generator/tests/integration/goldens/redis_selective/tests/unit/gapic/redis_v1/test_cloud_redis.py @@ -286,41 +286,6 @@ def test__get_client_cert_source(): assert CloudRedisClient._get_client_cert_source(None, True) is mock_default_cert_source assert CloudRedisClient._get_client_cert_source(mock_provided_cert_source, "true") is mock_provided_cert_source -@mock.patch.object(CloudRedisClient, "_DEFAULT_ENDPOINT_TEMPLATE", modify_default_endpoint_template(CloudRedisClient)) -@mock.patch.object(CloudRedisAsyncClient, "_DEFAULT_ENDPOINT_TEMPLATE", modify_default_endpoint_template(CloudRedisAsyncClient)) -def test__get_api_endpoint(): - api_override = "foo.com" - mock_client_cert_source = mock.Mock() - default_universe = CloudRedisClient._DEFAULT_UNIVERSE - default_endpoint = CloudRedisClient._DEFAULT_ENDPOINT_TEMPLATE.format(UNIVERSE_DOMAIN=default_universe) - mock_universe = "bar.com" - mock_endpoint = CloudRedisClient._DEFAULT_ENDPOINT_TEMPLATE.format(UNIVERSE_DOMAIN=mock_universe) - - assert CloudRedisClient._get_api_endpoint(api_override, mock_client_cert_source, default_universe, "always") == api_override - assert CloudRedisClient._get_api_endpoint(None, mock_client_cert_source, default_universe, "auto") == CloudRedisClient.DEFAULT_MTLS_ENDPOINT - assert CloudRedisClient._get_api_endpoint(None, None, default_universe, "auto") == default_endpoint - assert CloudRedisClient._get_api_endpoint(None, None, default_universe, "always") == CloudRedisClient.DEFAULT_MTLS_ENDPOINT - assert CloudRedisClient._get_api_endpoint(None, mock_client_cert_source, default_universe, "always") == CloudRedisClient.DEFAULT_MTLS_ENDPOINT - assert CloudRedisClient._get_api_endpoint(None, None, mock_universe, "never") == mock_endpoint - assert CloudRedisClient._get_api_endpoint(None, None, default_universe, "never") == default_endpoint - - with pytest.raises(MutualTLSChannelError) as excinfo: - CloudRedisClient._get_api_endpoint(None, mock_client_cert_source, mock_universe, "auto") - assert str(excinfo.value) == "mTLS is not supported in any universe other than googleapis.com." - - -def test__get_universe_domain(): - client_universe_domain = "foo.com" - universe_domain_env = "bar.com" - - assert CloudRedisClient._get_universe_domain(client_universe_domain, universe_domain_env) == client_universe_domain - assert CloudRedisClient._get_universe_domain(None, universe_domain_env) == universe_domain_env - assert CloudRedisClient._get_universe_domain(None, None) == CloudRedisClient._DEFAULT_UNIVERSE - - with pytest.raises(ValueError) as excinfo: - CloudRedisClient._get_universe_domain("", None) - assert str(excinfo.value) == "Universe Domain cannot be an empty string." - @pytest.mark.parametrize("error_code,cred_info_json,show_cred_info", [ (401, CRED_INFO_JSON, True), (403, CRED_INFO_JSON, True), diff --git a/packages/gapic-generator/tests/integration/goldens/storagebatchoperations/google/cloud/storagebatchoperations_v1/services/storage_batch_operations/client.py b/packages/gapic-generator/tests/integration/goldens/storagebatchoperations/google/cloud/storagebatchoperations_v1/services/storage_batch_operations/client.py index 5f79cf8e016a..8f82bcc8fda6 100755 --- a/packages/gapic-generator/tests/integration/goldens/storagebatchoperations/google/cloud/storagebatchoperations_v1/services/storage_batch_operations/client.py +++ b/packages/gapic-generator/tests/integration/goldens/storagebatchoperations/google/cloud/storagebatchoperations_v1/services/storage_batch_operations/client.py @@ -28,6 +28,7 @@ from google.api_core import client_options as client_options_lib from google.api_core import exceptions as core_exceptions from google.api_core import gapic_v1 +from google.api_core.gapic_v1 import routing from google.api_core import retry as retries from google.auth import credentials as ga_credentials # type: ignore from google.auth.transport import mtls # type: ignore @@ -105,38 +106,9 @@ class StorageBatchOperationsClient(metaclass=StorageBatchOperationsClientMeta): """ @staticmethod - def _get_default_mtls_endpoint(api_endpoint) -> Optional[str]: - """Converts api endpoint to mTLS endpoint. - - Convert "*.sandbox.googleapis.com" and "*.googleapis.com" to - "*.mtls.sandbox.googleapis.com" and "*.mtls.googleapis.com" respectively. - Args: - api_endpoint (Optional[str]): the api endpoint to convert. - Returns: - Optional[str]: converted mTLS api endpoint. - """ - if not api_endpoint: - return api_endpoint - - mtls_endpoint_re = re.compile( - r"(?P[^.]+)(?P\.mtls)?(?P\.sandbox)?(?P\.googleapis\.com)?" - ) - - m = mtls_endpoint_re.match(api_endpoint) - if m is None: - # Could not parse api_endpoint; return as-is. - return api_endpoint - - name, mtls, sandbox, googledomain = m.groups() - if mtls or not googledomain: - return api_endpoint - - if sandbox: - return api_endpoint.replace( - "sandbox.googleapis.com", "mtls.sandbox.googleapis.com" - ) - - return api_endpoint.replace(".googleapis.com", ".mtls.googleapis.com") + def _get_default_mtls_endpoint(api_endpoint: Optional[str]) -> Optional[str]: + """Converts api endpoint to mTLS endpoint.""" + return routing.get_default_mtls_endpoint(api_endpoint) # Note: DEFAULT_ENDPOINT is deprecated. Use _DEFAULT_ENDPOINT_TEMPLATE instead. DEFAULT_ENDPOINT = "storagebatchoperations.googleapis.com" @@ -410,53 +382,31 @@ def _get_client_cert_source(provided_cert_source, use_cert_flag): return client_cert_source @staticmethod - def _get_api_endpoint(api_override, client_cert_source, universe_domain, use_mtls_endpoint) -> str: - """Return the API endpoint used by the client. - - Args: - api_override (str): The API endpoint override. If specified, this is always - the return value of this function and the other arguments are not used. - client_cert_source (bytes): The client certificate source used by the client. - universe_domain (str): The universe domain used by the client. - use_mtls_endpoint (str): How to use the mTLS endpoint, which depends also on the other parameters. - Possible values are "always", "auto", or "never". - - Returns: - str: The API endpoint to be used by the client. - """ - if api_override is not None: - api_endpoint = api_override - elif use_mtls_endpoint == "always" or (use_mtls_endpoint == "auto" and client_cert_source): - _default_universe = StorageBatchOperationsClient._DEFAULT_UNIVERSE - if universe_domain != _default_universe: - raise MutualTLSChannelError(f"mTLS is not supported in any universe other than {_default_universe}.") - api_endpoint = StorageBatchOperationsClient.DEFAULT_MTLS_ENDPOINT - else: - api_endpoint = StorageBatchOperationsClient._DEFAULT_ENDPOINT_TEMPLATE.format(UNIVERSE_DOMAIN=universe_domain) - return api_endpoint + def _get_api_endpoint( + api_override: Optional[str], + client_cert_source: Optional[Callable[[], Tuple[bytes, bytes]]], + universe_domain: str, + use_mtls_endpoint: str, + ) -> str: + """Return the API endpoint used by the client.""" + return routing.get_api_endpoint( + api_override, + client_cert_source, + universe_domain, + use_mtls_endpoint, + StorageBatchOperationsClient._DEFAULT_UNIVERSE, + StorageBatchOperationsClient.DEFAULT_MTLS_ENDPOINT, + StorageBatchOperationsClient._DEFAULT_ENDPOINT_TEMPLATE, + ) @staticmethod def _get_universe_domain(client_universe_domain: Optional[str], universe_domain_env: Optional[str]) -> str: - """Return the universe domain used by the client. - - Args: - client_universe_domain (Optional[str]): The universe domain configured via the client options. - universe_domain_env (Optional[str]): The universe domain configured via the "GOOGLE_CLOUD_UNIVERSE_DOMAIN" environment variable. - - Returns: - str: The universe domain to be used by the client. - - Raises: - ValueError: If the universe domain is an empty string. - """ - universe_domain = StorageBatchOperationsClient._DEFAULT_UNIVERSE - if client_universe_domain is not None: - universe_domain = client_universe_domain - elif universe_domain_env is not None: - universe_domain = universe_domain_env - if len(universe_domain.strip()) == 0: - raise ValueError("Universe Domain cannot be an empty string.") - return universe_domain + """Return the universe domain used by the client.""" + return routing.get_universe_domain( + client_universe_domain, + universe_domain_env, + StorageBatchOperationsClient._DEFAULT_UNIVERSE, + ) def _validate_universe_domain(self): """Validates client's and credentials' universe domains are consistent. diff --git a/packages/gapic-generator/tests/integration/goldens/storagebatchoperations/tests/unit/gapic/storagebatchoperations_v1/test_storage_batch_operations.py b/packages/gapic-generator/tests/integration/goldens/storagebatchoperations/tests/unit/gapic/storagebatchoperations_v1/test_storage_batch_operations.py index 5c53e97f8d12..c25b91400269 100755 --- a/packages/gapic-generator/tests/integration/goldens/storagebatchoperations/tests/unit/gapic/storagebatchoperations_v1/test_storage_batch_operations.py +++ b/packages/gapic-generator/tests/integration/goldens/storagebatchoperations/tests/unit/gapic/storagebatchoperations_v1/test_storage_batch_operations.py @@ -278,41 +278,6 @@ def test__get_client_cert_source(): assert StorageBatchOperationsClient._get_client_cert_source(None, True) is mock_default_cert_source assert StorageBatchOperationsClient._get_client_cert_source(mock_provided_cert_source, "true") is mock_provided_cert_source -@mock.patch.object(StorageBatchOperationsClient, "_DEFAULT_ENDPOINT_TEMPLATE", modify_default_endpoint_template(StorageBatchOperationsClient)) -@mock.patch.object(StorageBatchOperationsAsyncClient, "_DEFAULT_ENDPOINT_TEMPLATE", modify_default_endpoint_template(StorageBatchOperationsAsyncClient)) -def test__get_api_endpoint(): - api_override = "foo.com" - mock_client_cert_source = mock.Mock() - default_universe = StorageBatchOperationsClient._DEFAULT_UNIVERSE - default_endpoint = StorageBatchOperationsClient._DEFAULT_ENDPOINT_TEMPLATE.format(UNIVERSE_DOMAIN=default_universe) - mock_universe = "bar.com" - mock_endpoint = StorageBatchOperationsClient._DEFAULT_ENDPOINT_TEMPLATE.format(UNIVERSE_DOMAIN=mock_universe) - - assert StorageBatchOperationsClient._get_api_endpoint(api_override, mock_client_cert_source, default_universe, "always") == api_override - assert StorageBatchOperationsClient._get_api_endpoint(None, mock_client_cert_source, default_universe, "auto") == StorageBatchOperationsClient.DEFAULT_MTLS_ENDPOINT - assert StorageBatchOperationsClient._get_api_endpoint(None, None, default_universe, "auto") == default_endpoint - assert StorageBatchOperationsClient._get_api_endpoint(None, None, default_universe, "always") == StorageBatchOperationsClient.DEFAULT_MTLS_ENDPOINT - assert StorageBatchOperationsClient._get_api_endpoint(None, mock_client_cert_source, default_universe, "always") == StorageBatchOperationsClient.DEFAULT_MTLS_ENDPOINT - assert StorageBatchOperationsClient._get_api_endpoint(None, None, mock_universe, "never") == mock_endpoint - assert StorageBatchOperationsClient._get_api_endpoint(None, None, default_universe, "never") == default_endpoint - - with pytest.raises(MutualTLSChannelError) as excinfo: - StorageBatchOperationsClient._get_api_endpoint(None, mock_client_cert_source, mock_universe, "auto") - assert str(excinfo.value) == "mTLS is not supported in any universe other than googleapis.com." - - -def test__get_universe_domain(): - client_universe_domain = "foo.com" - universe_domain_env = "bar.com" - - assert StorageBatchOperationsClient._get_universe_domain(client_universe_domain, universe_domain_env) == client_universe_domain - assert StorageBatchOperationsClient._get_universe_domain(None, universe_domain_env) == universe_domain_env - assert StorageBatchOperationsClient._get_universe_domain(None, None) == StorageBatchOperationsClient._DEFAULT_UNIVERSE - - with pytest.raises(ValueError) as excinfo: - StorageBatchOperationsClient._get_universe_domain("", None) - assert str(excinfo.value) == "Universe Domain cannot be an empty string." - @pytest.mark.parametrize("error_code,cred_info_json,show_cred_info", [ (401, CRED_INFO_JSON, True), (403, CRED_INFO_JSON, True), From 2d0808b205a9b3f4bc97c6ae94364df29ef9eb34 Mon Sep 17 00:00:00 2001 From: Heba Alazzeh Date: Thu, 16 Jul 2026 23:11:31 +0000 Subject: [PATCH 06/12] docs(api-core): address routing review feedback on docstrings, types, and variables --- .../google/api_core/gapic_v1/routing.py | 57 +++++++++++++++---- .../tests/unit/gapic/test_routing.py | 12 ++++ 2 files changed, 59 insertions(+), 10 deletions(-) diff --git a/packages/google-api-core/google/api_core/gapic_v1/routing.py b/packages/google-api-core/google/api_core/gapic_v1/routing.py index be094a7d455e..2ba3af412cd6 100644 --- a/packages/google-api-core/google/api_core/gapic_v1/routing.py +++ b/packages/google-api-core/google/api_core/gapic_v1/routing.py @@ -26,8 +26,10 @@ def get_default_mtls_endpoint(api_endpoint: Optional[str]) -> Optional[str]: Convert "*.sandbox.googleapis.com" and "*.googleapis.com" to "*.mtls.sandbox.googleapis.com" and "*.mtls.googleapis.com" respectively. + Args: api_endpoint (Optional[str]): the api endpoint to convert. + Returns: Optional[str]: converted mTLS api endpoint. """ @@ -52,9 +54,30 @@ def get_api_endpoint( use_mtls_endpoint: str, default_universe: str, default_mtls_endpoint: Optional[str], - default_endpoint_template: Optional[str], -) -> Optional[str]: - """Return the API endpoint used by the client.""" + default_endpoint_template: str, +) -> str: + """Return the API endpoint used by the client. + + Args: + api_override (Optional[str]): The API endpoint override. If specified, + this is always returned. + client_cert_source (Optional[Any]): The client certificate source used by the client. + universe_domain (str): The universe domain used by the client. + use_mtls_endpoint (str): How to use the mTLS endpoint. Possible values + are "always", "auto", or "never". + default_universe (str): The default universe domain. + default_mtls_endpoint (Optional[str]): The default mTLS endpoint. + default_endpoint_template (str): The default endpoint template containing + a placeholder `{UNIVERSE_DOMAIN}`. + + Returns: + str: The API endpoint to be used by the client. + + Raises: + google.auth.exceptions.MutualTLSChannelError: If mTLS is requested but + not supported in the configured universe domain. + ValueError: If mTLS is requested but no mTLS endpoint is available. + """ if api_override is not None: return api_override elif use_mtls_endpoint == "always" or ( @@ -64,13 +87,11 @@ def get_api_endpoint( raise MutualTLSChannelError( f"mTLS is not supported in any universe other than {default_universe}." ) + if not default_mtls_endpoint: + raise ValueError("mTLS endpoint is not available.") return default_mtls_endpoint else: - return ( - default_endpoint_template.format(UNIVERSE_DOMAIN=universe_domain) - if default_endpoint_template - else None - ) + return default_endpoint_template.format(UNIVERSE_DOMAIN=universe_domain) def get_universe_domain( @@ -78,12 +99,28 @@ def get_universe_domain( universe_domain_env: Optional[str], default_universe: str, ) -> str: - """Return the universe domain used by the client.""" - universe_domain = default_universe + """Return the universe domain used by the client. + + Args: + client_universe_domain (Optional[str]): The universe domain configured + via client options. + universe_domain_env (Optional[str]): The universe domain configured + via environment variable. + default_universe (str): The default universe domain. + + Returns: + str: The universe domain to be used by the client. + + Raises: + ValueError: If the resolved universe domain is an empty string. + """ if client_universe_domain is not None: universe_domain = client_universe_domain.strip() elif universe_domain_env is not None: universe_domain = universe_domain_env.strip() + else: + universe_domain = default_universe + if not universe_domain: raise ValueError("Universe Domain cannot be an empty string.") return universe_domain diff --git a/packages/google-api-core/tests/unit/gapic/test_routing.py b/packages/google-api-core/tests/unit/gapic/test_routing.py index 76fb5e2d6c8d..0fcf3131fa69 100644 --- a/packages/google-api-core/tests/unit/gapic/test_routing.py +++ b/packages/google-api-core/tests/unit/gapic/test_routing.py @@ -164,6 +164,18 @@ def test__get_api_endpoint(): == "mTLS is not supported in any universe other than googleapis.com." ) + with pytest.raises(ValueError) as excinfo: + get_api_endpoint( + None, + mock_client_cert_source, + default_universe, + "always", + MockClient._DEFAULT_UNIVERSE, + None, + MockClient._DEFAULT_ENDPOINT_TEMPLATE, + ) + assert str(excinfo.value) == "mTLS endpoint is not available." + def test__get_universe_domain(): client_universe_domain = "foo.com" From 331a643a8cd1a57c5cbab30acfa19259b1703a4e Mon Sep 17 00:00:00 2001 From: Heba Alazzeh Date: Thu, 16 Jul 2026 23:55:15 +0000 Subject: [PATCH 07/12] refactor: address review comments on centralized routing helpers --- .../%sub/services/%service/client.py.j2 | 34 ++++++++++++++----- .../%name_%version/%sub/test_%service.py.j2 | 16 --------- 2 files changed, 26 insertions(+), 24 deletions(-) diff --git a/packages/gapic-generator/gapic/templates/%namespace/%name_%version/%sub/services/%service/client.py.j2 b/packages/gapic-generator/gapic/templates/%namespace/%name_%version/%sub/services/%service/client.py.j2 index 6d1ddd993591..20a77b03c7c1 100644 --- a/packages/gapic-generator/gapic/templates/%namespace/%name_%version/%sub/services/%service/client.py.j2 +++ b/packages/gapic-generator/gapic/templates/%namespace/%name_%version/%sub/services/%service/client.py.j2 @@ -144,15 +144,10 @@ class {{ service.client_name }}(metaclass={{ service.client_name }}Meta): """{{ service.meta.doc|rst(width=72, indent=4) }}{% if service.version|length %} This class implements API version {{ service.version }}.{% endif %}""" - @staticmethod - def _get_default_mtls_endpoint(api_endpoint: Optional[str]) -> Optional[str]: - """Converts api endpoint to mTLS endpoint.""" - return routing.get_default_mtls_endpoint(api_endpoint) - # Note: DEFAULT_ENDPOINT is deprecated. Use _DEFAULT_ENDPOINT_TEMPLATE instead. DEFAULT_ENDPOINT = {% if service.host %}"{{ service.host }}"{% else %}None{% endif %} - DEFAULT_MTLS_ENDPOINT = _get_default_mtls_endpoint.__func__( # type: ignore + DEFAULT_MTLS_ENDPOINT = routing.get_default_mtls_endpoint( DEFAULT_ENDPOINT ) @@ -370,7 +365,19 @@ class {{ service.client_name }}(metaclass={{ service.client_name }}Meta): universe_domain: str, use_mtls_endpoint: str, ) -> str: - """Return the API endpoint used by the client.""" + """Return the API endpoint used by the client. + + Args: + api_override (Optional[str]): The API endpoint override. If specified, this is always + the return value of this function and the other arguments are not used. + client_cert_source (Union[Callable[[], Tuple[bytes, bytes]], None]): The client certificate source used by the client. + universe_domain (str): The universe domain used by the client. + use_mtls_endpoint (str): How to use the mTLS endpoint, which depends also on the other parameters. + Possible values are "always", "auto", or "never". + + Returns: + str: The API endpoint to be used by the client. + """ return routing.get_api_endpoint( api_override, client_cert_source, @@ -383,7 +390,18 @@ class {{ service.client_name }}(metaclass={{ service.client_name }}Meta): @staticmethod def _get_universe_domain(client_universe_domain: Optional[str], universe_domain_env: Optional[str]) -> str: - """Return the universe domain used by the client.""" + """Return the universe domain used by the client. + + Args: + client_universe_domain (Optional[str]): The universe domain configured via the client options. + universe_domain_env (Optional[str]): The universe domain configured via the "GOOGLE_CLOUD_UNIVERSE_DOMAIN" environment variable. + + Returns: + str: The universe domain to be used by the client. + + Raises: + ValueError: If the universe domain is an empty string. + """ return routing.get_universe_domain( client_universe_domain, universe_domain_env, diff --git a/packages/gapic-generator/gapic/templates/tests/unit/gapic/%name_%version/%sub/test_%service.py.j2 b/packages/gapic-generator/gapic/templates/tests/unit/gapic/%name_%version/%sub/test_%service.py.j2 index 83c540b7c1ec..7843ceb31bff 100644 --- a/packages/gapic-generator/gapic/templates/tests/unit/gapic/%name_%version/%sub/test_%service.py.j2 +++ b/packages/gapic-generator/gapic/templates/tests/unit/gapic/%name_%version/%sub/test_%service.py.j2 @@ -156,22 +156,6 @@ def set_event_loop(): asyncio.set_event_loop(None) -def test__get_default_mtls_endpoint(): - api_endpoint = "example.googleapis.com" - api_mtls_endpoint = "example.mtls.googleapis.com" - sandbox_endpoint = "example.sandbox.googleapis.com" - sandbox_mtls_endpoint = "example.mtls.sandbox.googleapis.com" - non_googleapi = "api.example.com" - custom_endpoint = ".custom" - - assert {{ service.client_name }}._get_default_mtls_endpoint(None) is None - assert {{ service.client_name }}._get_default_mtls_endpoint(api_endpoint) == api_mtls_endpoint - assert {{ service.client_name }}._get_default_mtls_endpoint(api_mtls_endpoint) == api_mtls_endpoint - assert {{ service.client_name }}._get_default_mtls_endpoint(sandbox_endpoint) == sandbox_mtls_endpoint - assert {{ service.client_name }}._get_default_mtls_endpoint(sandbox_mtls_endpoint) == sandbox_mtls_endpoint - assert {{ service.client_name }}._get_default_mtls_endpoint(non_googleapi) == non_googleapi - assert {{ service.client_name }}._get_default_mtls_endpoint(custom_endpoint) == custom_endpoint - def test__read_environment_variables(): assert {{ service.client_name }}._read_environment_variables() == (False, "auto", None) From 6596ac571354ecec8e632ef7100c4e4f8d59c558 Mon Sep 17 00:00:00 2001 From: Heba Alazzeh Date: Fri, 17 Jul 2026 13:57:47 +0000 Subject: [PATCH 08/12] refactor(api-core): rename routing.py to client_utils.py as consolidated client helper --- .../google-api-core/google/api_core/gapic_v1/__init__.py | 6 +++--- .../api_core/gapic_v1/{routing.py => client_utils.py} | 2 +- .../unit/gapic/{test_routing.py => test_client_utils.py} | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) rename packages/google-api-core/google/api_core/gapic_v1/{routing.py => client_utils.py} (98%) rename packages/google-api-core/tests/unit/gapic/{test_routing.py => test_client_utils.py} (99%) diff --git a/packages/google-api-core/google/api_core/gapic_v1/__init__.py b/packages/google-api-core/google/api_core/gapic_v1/__init__.py index dd17bfd55975..1f6d80d916a6 100644 --- a/packages/google-api-core/google/api_core/gapic_v1/__init__.py +++ b/packages/google-api-core/google/api_core/gapic_v1/__init__.py @@ -25,10 +25,10 @@ # Older Python versions safely ignore this variable. __lazy_modules__: Set[str] = { "google.api_core.gapic_v1.client_info", - "google.api_core.gapic_v1.routing", + "google.api_core.gapic_v1.client_utils", "google.api_core.gapic_v1.routing_header", } -__all__ = ["client_info", "routing", "routing_header"] +__all__ = ["client_info", "client_utils", "routing_header"] if _has_grpc: __lazy_modules__.update( @@ -42,7 +42,7 @@ from google.api_core.gapic_v1 import ( # noqa: E402 client_info, - routing, + client_utils, routing_header, ) diff --git a/packages/google-api-core/google/api_core/gapic_v1/routing.py b/packages/google-api-core/google/api_core/gapic_v1/client_utils.py similarity index 98% rename from packages/google-api-core/google/api_core/gapic_v1/routing.py rename to packages/google-api-core/google/api_core/gapic_v1/client_utils.py index 2ba3af412cd6..083b4eb29499 100644 --- a/packages/google-api-core/google/api_core/gapic_v1/routing.py +++ b/packages/google-api-core/google/api_core/gapic_v1/client_utils.py @@ -14,7 +14,7 @@ # limitations under the License. # -"""Helpers for routing and endpoint resolution.""" +"""Helpers for client setup and configuration.""" from typing import Any, Optional diff --git a/packages/google-api-core/tests/unit/gapic/test_routing.py b/packages/google-api-core/tests/unit/gapic/test_client_utils.py similarity index 99% rename from packages/google-api-core/tests/unit/gapic/test_routing.py rename to packages/google-api-core/tests/unit/gapic/test_client_utils.py index 0fcf3131fa69..6c6d6f945c5c 100644 --- a/packages/google-api-core/tests/unit/gapic/test_routing.py +++ b/packages/google-api-core/tests/unit/gapic/test_client_utils.py @@ -19,7 +19,7 @@ from google.auth.exceptions import MutualTLSChannelError -from google.api_core.gapic_v1.routing import ( +from google.api_core.gapic_v1.client_utils import ( get_api_endpoint, get_default_mtls_endpoint, get_universe_domain, From 5aabd90ac31c657f1ef7c4365e3e9f3b0bf259e6 Mon Sep 17 00:00:00 2001 From: Heba Alazzeh Date: Fri, 17 Jul 2026 13:59:27 +0000 Subject: [PATCH 09/12] refactor(generator): update templates and goldens to use client_utils instead of routing --- .../%name_%version/%sub/services/%service/client.py.j2 | 8 ++++---- .../cloud/asset_v1/services/asset_service/client.py | 8 ++++---- .../iam/credentials_v1/services/iam_credentials/client.py | 8 ++++---- .../google/cloud/eventarc_v1/services/eventarc/client.py | 8 ++++---- .../cloud/logging_v2/services/config_service_v2/client.py | 8 ++++---- .../logging_v2/services/logging_service_v2/client.py | 8 ++++---- .../logging_v2/services/metrics_service_v2/client.py | 8 ++++---- .../cloud/logging_v2/services/config_service_v2/client.py | 8 ++++---- .../logging_v2/services/logging_service_v2/client.py | 8 ++++---- .../logging_v2/services/metrics_service_v2/client.py | 8 ++++---- .../google/cloud/redis_v1/services/cloud_redis/client.py | 8 ++++---- .../google/cloud/redis_v1/services/cloud_redis/client.py | 8 ++++---- .../services/storage_batch_operations/client.py | 8 ++++---- 13 files changed, 52 insertions(+), 52 deletions(-) diff --git a/packages/gapic-generator/gapic/templates/%namespace/%name_%version/%sub/services/%service/client.py.j2 b/packages/gapic-generator/gapic/templates/%namespace/%name_%version/%sub/services/%service/client.py.j2 index 20a77b03c7c1..3b3b9474effd 100644 --- a/packages/gapic-generator/gapic/templates/%namespace/%name_%version/%sub/services/%service/client.py.j2 +++ b/packages/gapic-generator/gapic/templates/%namespace/%name_%version/%sub/services/%service/client.py.j2 @@ -30,7 +30,7 @@ from google.api_core import exceptions as core_exceptions from google.api_core import extended_operation {% endif %} from google.api_core import gapic_v1 -from google.api_core.gapic_v1 import routing +from google.api_core.gapic_v1 import client_utils from google.api_core import retry as retries from google.auth import credentials as ga_credentials # type: ignore from google.auth.transport import mtls # type: ignore @@ -147,7 +147,7 @@ class {{ service.client_name }}(metaclass={{ service.client_name }}Meta): # Note: DEFAULT_ENDPOINT is deprecated. Use _DEFAULT_ENDPOINT_TEMPLATE instead. DEFAULT_ENDPOINT = {% if service.host %}"{{ service.host }}"{% else %}None{% endif %} - DEFAULT_MTLS_ENDPOINT = routing.get_default_mtls_endpoint( + DEFAULT_MTLS_ENDPOINT = client_utils.get_default_mtls_endpoint( DEFAULT_ENDPOINT ) @@ -378,7 +378,7 @@ class {{ service.client_name }}(metaclass={{ service.client_name }}Meta): Returns: str: The API endpoint to be used by the client. """ - return routing.get_api_endpoint( + return client_utils.get_api_endpoint( api_override, client_cert_source, universe_domain, @@ -402,7 +402,7 @@ class {{ service.client_name }}(metaclass={{ service.client_name }}Meta): Raises: ValueError: If the universe domain is an empty string. """ - return routing.get_universe_domain( + return client_utils.get_universe_domain( client_universe_domain, universe_domain_env, {{ service.client_name }}._DEFAULT_UNIVERSE, diff --git a/packages/gapic-generator/tests/integration/goldens/asset/google/cloud/asset_v1/services/asset_service/client.py b/packages/gapic-generator/tests/integration/goldens/asset/google/cloud/asset_v1/services/asset_service/client.py index a7c41468e352..8da37edbad1c 100755 --- a/packages/gapic-generator/tests/integration/goldens/asset/google/cloud/asset_v1/services/asset_service/client.py +++ b/packages/gapic-generator/tests/integration/goldens/asset/google/cloud/asset_v1/services/asset_service/client.py @@ -27,7 +27,7 @@ from google.api_core import client_options as client_options_lib from google.api_core import exceptions as core_exceptions from google.api_core import gapic_v1 -from google.api_core.gapic_v1 import routing +from google.api_core.gapic_v1 import client_utils from google.api_core import retry as retries from google.auth import credentials as ga_credentials # type: ignore from google.auth.transport import mtls # type: ignore @@ -104,7 +104,7 @@ class AssetServiceClient(metaclass=AssetServiceClientMeta): @staticmethod def _get_default_mtls_endpoint(api_endpoint: Optional[str]) -> Optional[str]: """Converts api endpoint to mTLS endpoint.""" - return routing.get_default_mtls_endpoint(api_endpoint) + return client_utils.get_default_mtls_endpoint(api_endpoint) # Note: DEFAULT_ENDPOINT is deprecated. Use _DEFAULT_ENDPOINT_TEMPLATE instead. DEFAULT_ENDPOINT = "cloudasset.googleapis.com" @@ -429,7 +429,7 @@ def _get_api_endpoint( use_mtls_endpoint: str, ) -> str: """Return the API endpoint used by the client.""" - return routing.get_api_endpoint( + return client_utils.get_api_endpoint( api_override, client_cert_source, universe_domain, @@ -442,7 +442,7 @@ def _get_api_endpoint( @staticmethod def _get_universe_domain(client_universe_domain: Optional[str], universe_domain_env: Optional[str]) -> str: """Return the universe domain used by the client.""" - return routing.get_universe_domain( + return client_utils.get_universe_domain( client_universe_domain, universe_domain_env, AssetServiceClient._DEFAULT_UNIVERSE, diff --git a/packages/gapic-generator/tests/integration/goldens/credentials/google/iam/credentials_v1/services/iam_credentials/client.py b/packages/gapic-generator/tests/integration/goldens/credentials/google/iam/credentials_v1/services/iam_credentials/client.py index caacd71e79b2..cecdaaf047a9 100755 --- a/packages/gapic-generator/tests/integration/goldens/credentials/google/iam/credentials_v1/services/iam_credentials/client.py +++ b/packages/gapic-generator/tests/integration/goldens/credentials/google/iam/credentials_v1/services/iam_credentials/client.py @@ -27,7 +27,7 @@ from google.api_core import client_options as client_options_lib from google.api_core import exceptions as core_exceptions from google.api_core import gapic_v1 -from google.api_core.gapic_v1 import routing +from google.api_core.gapic_v1 import client_utils from google.api_core import retry as retries from google.auth import credentials as ga_credentials # type: ignore from google.auth.transport import mtls # type: ignore @@ -107,7 +107,7 @@ class IAMCredentialsClient(metaclass=IAMCredentialsClientMeta): @staticmethod def _get_default_mtls_endpoint(api_endpoint: Optional[str]) -> Optional[str]: """Converts api endpoint to mTLS endpoint.""" - return routing.get_default_mtls_endpoint(api_endpoint) + return client_utils.get_default_mtls_endpoint(api_endpoint) # Note: DEFAULT_ENDPOINT is deprecated. Use _DEFAULT_ENDPOINT_TEMPLATE instead. DEFAULT_ENDPOINT = "iamcredentials.googleapis.com" @@ -366,7 +366,7 @@ def _get_api_endpoint( use_mtls_endpoint: str, ) -> str: """Return the API endpoint used by the client.""" - return routing.get_api_endpoint( + return client_utils.get_api_endpoint( api_override, client_cert_source, universe_domain, @@ -379,7 +379,7 @@ def _get_api_endpoint( @staticmethod def _get_universe_domain(client_universe_domain: Optional[str], universe_domain_env: Optional[str]) -> str: """Return the universe domain used by the client.""" - return routing.get_universe_domain( + return client_utils.get_universe_domain( client_universe_domain, universe_domain_env, IAMCredentialsClient._DEFAULT_UNIVERSE, diff --git a/packages/gapic-generator/tests/integration/goldens/eventarc/google/cloud/eventarc_v1/services/eventarc/client.py b/packages/gapic-generator/tests/integration/goldens/eventarc/google/cloud/eventarc_v1/services/eventarc/client.py index a55a86f853e5..f9bbb928521f 100755 --- a/packages/gapic-generator/tests/integration/goldens/eventarc/google/cloud/eventarc_v1/services/eventarc/client.py +++ b/packages/gapic-generator/tests/integration/goldens/eventarc/google/cloud/eventarc_v1/services/eventarc/client.py @@ -27,7 +27,7 @@ from google.api_core import client_options as client_options_lib from google.api_core import exceptions as core_exceptions from google.api_core import gapic_v1 -from google.api_core.gapic_v1 import routing +from google.api_core.gapic_v1 import client_utils from google.api_core import retry as retries from google.auth import credentials as ga_credentials # type: ignore from google.auth.transport import mtls # type: ignore @@ -125,7 +125,7 @@ class EventarcClient(metaclass=EventarcClientMeta): @staticmethod def _get_default_mtls_endpoint(api_endpoint: Optional[str]) -> Optional[str]: """Converts api endpoint to mTLS endpoint.""" - return routing.get_default_mtls_endpoint(api_endpoint) + return client_utils.get_default_mtls_endpoint(api_endpoint) # Note: DEFAULT_ENDPOINT is deprecated. Use _DEFAULT_ENDPOINT_TEMPLATE instead. DEFAULT_ENDPOINT = "eventarc.googleapis.com" @@ -549,7 +549,7 @@ def _get_api_endpoint( use_mtls_endpoint: str, ) -> str: """Return the API endpoint used by the client.""" - return routing.get_api_endpoint( + return client_utils.get_api_endpoint( api_override, client_cert_source, universe_domain, @@ -562,7 +562,7 @@ def _get_api_endpoint( @staticmethod def _get_universe_domain(client_universe_domain: Optional[str], universe_domain_env: Optional[str]) -> str: """Return the universe domain used by the client.""" - return routing.get_universe_domain( + return client_utils.get_universe_domain( client_universe_domain, universe_domain_env, EventarcClient._DEFAULT_UNIVERSE, diff --git a/packages/gapic-generator/tests/integration/goldens/logging/google/cloud/logging_v2/services/config_service_v2/client.py b/packages/gapic-generator/tests/integration/goldens/logging/google/cloud/logging_v2/services/config_service_v2/client.py index ef738f779665..29406311d09c 100755 --- a/packages/gapic-generator/tests/integration/goldens/logging/google/cloud/logging_v2/services/config_service_v2/client.py +++ b/packages/gapic-generator/tests/integration/goldens/logging/google/cloud/logging_v2/services/config_service_v2/client.py @@ -27,7 +27,7 @@ from google.api_core import client_options as client_options_lib from google.api_core import exceptions as core_exceptions from google.api_core import gapic_v1 -from google.api_core.gapic_v1 import routing +from google.api_core.gapic_v1 import client_utils from google.api_core import retry as retries from google.auth import credentials as ga_credentials # type: ignore from google.auth.transport import mtls # type: ignore @@ -100,7 +100,7 @@ class ConfigServiceV2Client(metaclass=ConfigServiceV2ClientMeta): @staticmethod def _get_default_mtls_endpoint(api_endpoint: Optional[str]) -> Optional[str]: """Converts api endpoint to mTLS endpoint.""" - return routing.get_default_mtls_endpoint(api_endpoint) + return client_utils.get_default_mtls_endpoint(api_endpoint) # Note: DEFAULT_ENDPOINT is deprecated. Use _DEFAULT_ENDPOINT_TEMPLATE instead. DEFAULT_ENDPOINT = "logging.googleapis.com" @@ -425,7 +425,7 @@ def _get_api_endpoint( use_mtls_endpoint: str, ) -> str: """Return the API endpoint used by the client.""" - return routing.get_api_endpoint( + return client_utils.get_api_endpoint( api_override, client_cert_source, universe_domain, @@ -438,7 +438,7 @@ def _get_api_endpoint( @staticmethod def _get_universe_domain(client_universe_domain: Optional[str], universe_domain_env: Optional[str]) -> str: """Return the universe domain used by the client.""" - return routing.get_universe_domain( + return client_utils.get_universe_domain( client_universe_domain, universe_domain_env, ConfigServiceV2Client._DEFAULT_UNIVERSE, diff --git a/packages/gapic-generator/tests/integration/goldens/logging/google/cloud/logging_v2/services/logging_service_v2/client.py b/packages/gapic-generator/tests/integration/goldens/logging/google/cloud/logging_v2/services/logging_service_v2/client.py index effd54581fac..d2865e288932 100755 --- a/packages/gapic-generator/tests/integration/goldens/logging/google/cloud/logging_v2/services/logging_service_v2/client.py +++ b/packages/gapic-generator/tests/integration/goldens/logging/google/cloud/logging_v2/services/logging_service_v2/client.py @@ -27,7 +27,7 @@ from google.api_core import client_options as client_options_lib from google.api_core import exceptions as core_exceptions from google.api_core import gapic_v1 -from google.api_core.gapic_v1 import routing +from google.api_core.gapic_v1 import client_utils from google.api_core import retry as retries from google.auth import credentials as ga_credentials # type: ignore from google.auth.transport import mtls # type: ignore @@ -97,7 +97,7 @@ class LoggingServiceV2Client(metaclass=LoggingServiceV2ClientMeta): @staticmethod def _get_default_mtls_endpoint(api_endpoint: Optional[str]) -> Optional[str]: """Converts api endpoint to mTLS endpoint.""" - return routing.get_default_mtls_endpoint(api_endpoint) + return client_utils.get_default_mtls_endpoint(api_endpoint) # Note: DEFAULT_ENDPOINT is deprecated. Use _DEFAULT_ENDPOINT_TEMPLATE instead. DEFAULT_ENDPOINT = "logging.googleapis.com" @@ -356,7 +356,7 @@ def _get_api_endpoint( use_mtls_endpoint: str, ) -> str: """Return the API endpoint used by the client.""" - return routing.get_api_endpoint( + return client_utils.get_api_endpoint( api_override, client_cert_source, universe_domain, @@ -369,7 +369,7 @@ def _get_api_endpoint( @staticmethod def _get_universe_domain(client_universe_domain: Optional[str], universe_domain_env: Optional[str]) -> str: """Return the universe domain used by the client.""" - return routing.get_universe_domain( + return client_utils.get_universe_domain( client_universe_domain, universe_domain_env, LoggingServiceV2Client._DEFAULT_UNIVERSE, diff --git a/packages/gapic-generator/tests/integration/goldens/logging/google/cloud/logging_v2/services/metrics_service_v2/client.py b/packages/gapic-generator/tests/integration/goldens/logging/google/cloud/logging_v2/services/metrics_service_v2/client.py index 75ac58cd141c..82e9fa651892 100755 --- a/packages/gapic-generator/tests/integration/goldens/logging/google/cloud/logging_v2/services/metrics_service_v2/client.py +++ b/packages/gapic-generator/tests/integration/goldens/logging/google/cloud/logging_v2/services/metrics_service_v2/client.py @@ -27,7 +27,7 @@ from google.api_core import client_options as client_options_lib from google.api_core import exceptions as core_exceptions from google.api_core import gapic_v1 -from google.api_core.gapic_v1 import routing +from google.api_core.gapic_v1 import client_utils from google.api_core import retry as retries from google.auth import credentials as ga_credentials # type: ignore from google.auth.transport import mtls # type: ignore @@ -98,7 +98,7 @@ class MetricsServiceV2Client(metaclass=MetricsServiceV2ClientMeta): @staticmethod def _get_default_mtls_endpoint(api_endpoint: Optional[str]) -> Optional[str]: """Converts api endpoint to mTLS endpoint.""" - return routing.get_default_mtls_endpoint(api_endpoint) + return client_utils.get_default_mtls_endpoint(api_endpoint) # Note: DEFAULT_ENDPOINT is deprecated. Use _DEFAULT_ENDPOINT_TEMPLATE instead. DEFAULT_ENDPOINT = "logging.googleapis.com" @@ -357,7 +357,7 @@ def _get_api_endpoint( use_mtls_endpoint: str, ) -> str: """Return the API endpoint used by the client.""" - return routing.get_api_endpoint( + return client_utils.get_api_endpoint( api_override, client_cert_source, universe_domain, @@ -370,7 +370,7 @@ def _get_api_endpoint( @staticmethod def _get_universe_domain(client_universe_domain: Optional[str], universe_domain_env: Optional[str]) -> str: """Return the universe domain used by the client.""" - return routing.get_universe_domain( + return client_utils.get_universe_domain( client_universe_domain, universe_domain_env, MetricsServiceV2Client._DEFAULT_UNIVERSE, diff --git a/packages/gapic-generator/tests/integration/goldens/logging_internal/google/cloud/logging_v2/services/config_service_v2/client.py b/packages/gapic-generator/tests/integration/goldens/logging_internal/google/cloud/logging_v2/services/config_service_v2/client.py index 2d7bf1f9147f..9cae27c01971 100755 --- a/packages/gapic-generator/tests/integration/goldens/logging_internal/google/cloud/logging_v2/services/config_service_v2/client.py +++ b/packages/gapic-generator/tests/integration/goldens/logging_internal/google/cloud/logging_v2/services/config_service_v2/client.py @@ -27,7 +27,7 @@ from google.api_core import client_options as client_options_lib from google.api_core import exceptions as core_exceptions from google.api_core import gapic_v1 -from google.api_core.gapic_v1 import routing +from google.api_core.gapic_v1 import client_utils from google.api_core import retry as retries from google.auth import credentials as ga_credentials # type: ignore from google.auth.transport import mtls # type: ignore @@ -100,7 +100,7 @@ class BaseConfigServiceV2Client(metaclass=BaseConfigServiceV2ClientMeta): @staticmethod def _get_default_mtls_endpoint(api_endpoint: Optional[str]) -> Optional[str]: """Converts api endpoint to mTLS endpoint.""" - return routing.get_default_mtls_endpoint(api_endpoint) + return client_utils.get_default_mtls_endpoint(api_endpoint) # Note: DEFAULT_ENDPOINT is deprecated. Use _DEFAULT_ENDPOINT_TEMPLATE instead. DEFAULT_ENDPOINT = "logging.googleapis.com" @@ -425,7 +425,7 @@ def _get_api_endpoint( use_mtls_endpoint: str, ) -> str: """Return the API endpoint used by the client.""" - return routing.get_api_endpoint( + return client_utils.get_api_endpoint( api_override, client_cert_source, universe_domain, @@ -438,7 +438,7 @@ def _get_api_endpoint( @staticmethod def _get_universe_domain(client_universe_domain: Optional[str], universe_domain_env: Optional[str]) -> str: """Return the universe domain used by the client.""" - return routing.get_universe_domain( + return client_utils.get_universe_domain( client_universe_domain, universe_domain_env, BaseConfigServiceV2Client._DEFAULT_UNIVERSE, diff --git a/packages/gapic-generator/tests/integration/goldens/logging_internal/google/cloud/logging_v2/services/logging_service_v2/client.py b/packages/gapic-generator/tests/integration/goldens/logging_internal/google/cloud/logging_v2/services/logging_service_v2/client.py index effd54581fac..d2865e288932 100755 --- a/packages/gapic-generator/tests/integration/goldens/logging_internal/google/cloud/logging_v2/services/logging_service_v2/client.py +++ b/packages/gapic-generator/tests/integration/goldens/logging_internal/google/cloud/logging_v2/services/logging_service_v2/client.py @@ -27,7 +27,7 @@ from google.api_core import client_options as client_options_lib from google.api_core import exceptions as core_exceptions from google.api_core import gapic_v1 -from google.api_core.gapic_v1 import routing +from google.api_core.gapic_v1 import client_utils from google.api_core import retry as retries from google.auth import credentials as ga_credentials # type: ignore from google.auth.transport import mtls # type: ignore @@ -97,7 +97,7 @@ class LoggingServiceV2Client(metaclass=LoggingServiceV2ClientMeta): @staticmethod def _get_default_mtls_endpoint(api_endpoint: Optional[str]) -> Optional[str]: """Converts api endpoint to mTLS endpoint.""" - return routing.get_default_mtls_endpoint(api_endpoint) + return client_utils.get_default_mtls_endpoint(api_endpoint) # Note: DEFAULT_ENDPOINT is deprecated. Use _DEFAULT_ENDPOINT_TEMPLATE instead. DEFAULT_ENDPOINT = "logging.googleapis.com" @@ -356,7 +356,7 @@ def _get_api_endpoint( use_mtls_endpoint: str, ) -> str: """Return the API endpoint used by the client.""" - return routing.get_api_endpoint( + return client_utils.get_api_endpoint( api_override, client_cert_source, universe_domain, @@ -369,7 +369,7 @@ def _get_api_endpoint( @staticmethod def _get_universe_domain(client_universe_domain: Optional[str], universe_domain_env: Optional[str]) -> str: """Return the universe domain used by the client.""" - return routing.get_universe_domain( + return client_utils.get_universe_domain( client_universe_domain, universe_domain_env, LoggingServiceV2Client._DEFAULT_UNIVERSE, diff --git a/packages/gapic-generator/tests/integration/goldens/logging_internal/google/cloud/logging_v2/services/metrics_service_v2/client.py b/packages/gapic-generator/tests/integration/goldens/logging_internal/google/cloud/logging_v2/services/metrics_service_v2/client.py index 7105f0d4b61e..e63028e1cc34 100755 --- a/packages/gapic-generator/tests/integration/goldens/logging_internal/google/cloud/logging_v2/services/metrics_service_v2/client.py +++ b/packages/gapic-generator/tests/integration/goldens/logging_internal/google/cloud/logging_v2/services/metrics_service_v2/client.py @@ -27,7 +27,7 @@ from google.api_core import client_options as client_options_lib from google.api_core import exceptions as core_exceptions from google.api_core import gapic_v1 -from google.api_core.gapic_v1 import routing +from google.api_core.gapic_v1 import client_utils from google.api_core import retry as retries from google.auth import credentials as ga_credentials # type: ignore from google.auth.transport import mtls # type: ignore @@ -98,7 +98,7 @@ class BaseMetricsServiceV2Client(metaclass=BaseMetricsServiceV2ClientMeta): @staticmethod def _get_default_mtls_endpoint(api_endpoint: Optional[str]) -> Optional[str]: """Converts api endpoint to mTLS endpoint.""" - return routing.get_default_mtls_endpoint(api_endpoint) + return client_utils.get_default_mtls_endpoint(api_endpoint) # Note: DEFAULT_ENDPOINT is deprecated. Use _DEFAULT_ENDPOINT_TEMPLATE instead. DEFAULT_ENDPOINT = "logging.googleapis.com" @@ -357,7 +357,7 @@ def _get_api_endpoint( use_mtls_endpoint: str, ) -> str: """Return the API endpoint used by the client.""" - return routing.get_api_endpoint( + return client_utils.get_api_endpoint( api_override, client_cert_source, universe_domain, @@ -370,7 +370,7 @@ def _get_api_endpoint( @staticmethod def _get_universe_domain(client_universe_domain: Optional[str], universe_domain_env: Optional[str]) -> str: """Return the universe domain used by the client.""" - return routing.get_universe_domain( + return client_utils.get_universe_domain( client_universe_domain, universe_domain_env, BaseMetricsServiceV2Client._DEFAULT_UNIVERSE, diff --git a/packages/gapic-generator/tests/integration/goldens/redis/google/cloud/redis_v1/services/cloud_redis/client.py b/packages/gapic-generator/tests/integration/goldens/redis/google/cloud/redis_v1/services/cloud_redis/client.py index 5a302701e64e..ebbf1b587d77 100755 --- a/packages/gapic-generator/tests/integration/goldens/redis/google/cloud/redis_v1/services/cloud_redis/client.py +++ b/packages/gapic-generator/tests/integration/goldens/redis/google/cloud/redis_v1/services/cloud_redis/client.py @@ -27,7 +27,7 @@ from google.api_core import client_options as client_options_lib from google.api_core import exceptions as core_exceptions from google.api_core import gapic_v1 -from google.api_core.gapic_v1 import routing +from google.api_core.gapic_v1 import client_utils from google.api_core import retry as retries from google.auth import credentials as ga_credentials # type: ignore from google.auth.transport import mtls # type: ignore @@ -135,7 +135,7 @@ class CloudRedisClient(metaclass=CloudRedisClientMeta): @staticmethod def _get_default_mtls_endpoint(api_endpoint: Optional[str]) -> Optional[str]: """Converts api endpoint to mTLS endpoint.""" - return routing.get_default_mtls_endpoint(api_endpoint) + return client_utils.get_default_mtls_endpoint(api_endpoint) # Note: DEFAULT_ENDPOINT is deprecated. Use _DEFAULT_ENDPOINT_TEMPLATE instead. DEFAULT_ENDPOINT = "redis.googleapis.com" @@ -394,7 +394,7 @@ def _get_api_endpoint( use_mtls_endpoint: str, ) -> str: """Return the API endpoint used by the client.""" - return routing.get_api_endpoint( + return client_utils.get_api_endpoint( api_override, client_cert_source, universe_domain, @@ -407,7 +407,7 @@ def _get_api_endpoint( @staticmethod def _get_universe_domain(client_universe_domain: Optional[str], universe_domain_env: Optional[str]) -> str: """Return the universe domain used by the client.""" - return routing.get_universe_domain( + return client_utils.get_universe_domain( client_universe_domain, universe_domain_env, CloudRedisClient._DEFAULT_UNIVERSE, diff --git a/packages/gapic-generator/tests/integration/goldens/redis_selective/google/cloud/redis_v1/services/cloud_redis/client.py b/packages/gapic-generator/tests/integration/goldens/redis_selective/google/cloud/redis_v1/services/cloud_redis/client.py index fadfa3b9a781..d7b424c072cb 100755 --- a/packages/gapic-generator/tests/integration/goldens/redis_selective/google/cloud/redis_v1/services/cloud_redis/client.py +++ b/packages/gapic-generator/tests/integration/goldens/redis_selective/google/cloud/redis_v1/services/cloud_redis/client.py @@ -27,7 +27,7 @@ from google.api_core import client_options as client_options_lib from google.api_core import exceptions as core_exceptions from google.api_core import gapic_v1 -from google.api_core.gapic_v1 import routing +from google.api_core.gapic_v1 import client_utils from google.api_core import retry as retries from google.auth import credentials as ga_credentials # type: ignore from google.auth.transport import mtls # type: ignore @@ -135,7 +135,7 @@ class CloudRedisClient(metaclass=CloudRedisClientMeta): @staticmethod def _get_default_mtls_endpoint(api_endpoint: Optional[str]) -> Optional[str]: """Converts api endpoint to mTLS endpoint.""" - return routing.get_default_mtls_endpoint(api_endpoint) + return client_utils.get_default_mtls_endpoint(api_endpoint) # Note: DEFAULT_ENDPOINT is deprecated. Use _DEFAULT_ENDPOINT_TEMPLATE instead. DEFAULT_ENDPOINT = "redis.googleapis.com" @@ -394,7 +394,7 @@ def _get_api_endpoint( use_mtls_endpoint: str, ) -> str: """Return the API endpoint used by the client.""" - return routing.get_api_endpoint( + return client_utils.get_api_endpoint( api_override, client_cert_source, universe_domain, @@ -407,7 +407,7 @@ def _get_api_endpoint( @staticmethod def _get_universe_domain(client_universe_domain: Optional[str], universe_domain_env: Optional[str]) -> str: """Return the universe domain used by the client.""" - return routing.get_universe_domain( + return client_utils.get_universe_domain( client_universe_domain, universe_domain_env, CloudRedisClient._DEFAULT_UNIVERSE, diff --git a/packages/gapic-generator/tests/integration/goldens/storagebatchoperations/google/cloud/storagebatchoperations_v1/services/storage_batch_operations/client.py b/packages/gapic-generator/tests/integration/goldens/storagebatchoperations/google/cloud/storagebatchoperations_v1/services/storage_batch_operations/client.py index 8f82bcc8fda6..47948273cf72 100755 --- a/packages/gapic-generator/tests/integration/goldens/storagebatchoperations/google/cloud/storagebatchoperations_v1/services/storage_batch_operations/client.py +++ b/packages/gapic-generator/tests/integration/goldens/storagebatchoperations/google/cloud/storagebatchoperations_v1/services/storage_batch_operations/client.py @@ -28,7 +28,7 @@ from google.api_core import client_options as client_options_lib from google.api_core import exceptions as core_exceptions from google.api_core import gapic_v1 -from google.api_core.gapic_v1 import routing +from google.api_core.gapic_v1 import client_utils from google.api_core import retry as retries from google.auth import credentials as ga_credentials # type: ignore from google.auth.transport import mtls # type: ignore @@ -108,7 +108,7 @@ class StorageBatchOperationsClient(metaclass=StorageBatchOperationsClientMeta): @staticmethod def _get_default_mtls_endpoint(api_endpoint: Optional[str]) -> Optional[str]: """Converts api endpoint to mTLS endpoint.""" - return routing.get_default_mtls_endpoint(api_endpoint) + return client_utils.get_default_mtls_endpoint(api_endpoint) # Note: DEFAULT_ENDPOINT is deprecated. Use _DEFAULT_ENDPOINT_TEMPLATE instead. DEFAULT_ENDPOINT = "storagebatchoperations.googleapis.com" @@ -389,7 +389,7 @@ def _get_api_endpoint( use_mtls_endpoint: str, ) -> str: """Return the API endpoint used by the client.""" - return routing.get_api_endpoint( + return client_utils.get_api_endpoint( api_override, client_cert_source, universe_domain, @@ -402,7 +402,7 @@ def _get_api_endpoint( @staticmethod def _get_universe_domain(client_universe_domain: Optional[str], universe_domain_env: Optional[str]) -> str: """Return the universe domain used by the client.""" - return routing.get_universe_domain( + return client_utils.get_universe_domain( client_universe_domain, universe_domain_env, StorageBatchOperationsClient._DEFAULT_UNIVERSE, From 093ac26fd4904c4203b4498633b716248a878aa6 Mon Sep 17 00:00:00 2001 From: Heba Alazzeh Date: Fri, 17 Jul 2026 14:09:13 +0000 Subject: [PATCH 10/12] fix(api-core): make mTLS endpoint conversion case-insensitive --- .../google/api_core/gapic_v1/client_utils.py | 7 ++++--- .../tests/unit/gapic/test_client_utils.py | 9 +++++++++ 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/packages/google-api-core/google/api_core/gapic_v1/client_utils.py b/packages/google-api-core/google/api_core/gapic_v1/client_utils.py index 083b4eb29499..b91d873d2dfb 100644 --- a/packages/google-api-core/google/api_core/gapic_v1/client_utils.py +++ b/packages/google-api-core/google/api_core/gapic_v1/client_utils.py @@ -33,14 +33,15 @@ def get_default_mtls_endpoint(api_endpoint: Optional[str]) -> Optional[str]: Returns: Optional[str]: converted mTLS api endpoint. """ - if not api_endpoint or ".mtls." in api_endpoint: + if not api_endpoint or ".mtls." in api_endpoint.lower(): return api_endpoint - if api_endpoint.endswith(".sandbox.googleapis.com"): + lowered_endpoint = api_endpoint.lower() + if lowered_endpoint.endswith(".sandbox.googleapis.com"): # len(".sandbox.googleapis.com") == 23 return api_endpoint[:-23] + ".mtls.sandbox.googleapis.com" - if api_endpoint.endswith(".googleapis.com"): + if lowered_endpoint.endswith(".googleapis.com"): # len(".googleapis.com") == 15 return api_endpoint[:-15] + ".mtls.googleapis.com" diff --git a/packages/google-api-core/tests/unit/gapic/test_client_utils.py b/packages/google-api-core/tests/unit/gapic/test_client_utils.py index 6c6d6f945c5c..26d29612a8e7 100644 --- a/packages/google-api-core/tests/unit/gapic/test_client_utils.py +++ b/packages/google-api-core/tests/unit/gapic/test_client_utils.py @@ -39,6 +39,15 @@ def test_get_default_mtls_endpoint(): get_default_mtls_endpoint("foo.sandbox.googleapis.com") == "foo.mtls.sandbox.googleapis.com" ) + # Test case-insensitivity + assert ( + get_default_mtls_endpoint("foo.GoogleAPIs.com") + == "foo.mtls.googleapis.com" + ) + assert ( + get_default_mtls_endpoint("foo.Sandbox.GoogleAPIs.com") + == "foo.mtls.sandbox.googleapis.com" + ) # Test endpoints that shouldn't be converted assert ( From 91d39e9be6aa149ef58dd68e63e3215638852506 Mon Sep 17 00:00:00 2001 From: Heba Alazzeh Date: Fri, 17 Jul 2026 14:16:01 +0000 Subject: [PATCH 11/12] fix(api-core): satisfy lint and formatting for conftest and test_client_utils in routing branch --- packages/google-api-core/tests/conftest.py | 1 + .../google-api-core/tests/unit/gapic/test_client_utils.py | 8 ++------ 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/packages/google-api-core/tests/conftest.py b/packages/google-api-core/tests/conftest.py index 1872207be107..148896ddcaf4 100644 --- a/packages/google-api-core/tests/conftest.py +++ b/packages/google-api-core/tests/conftest.py @@ -15,6 +15,7 @@ import os from unittest import mock + import pytest diff --git a/packages/google-api-core/tests/unit/gapic/test_client_utils.py b/packages/google-api-core/tests/unit/gapic/test_client_utils.py index 26d29612a8e7..35cede6688b0 100644 --- a/packages/google-api-core/tests/unit/gapic/test_client_utils.py +++ b/packages/google-api-core/tests/unit/gapic/test_client_utils.py @@ -17,13 +17,12 @@ import pytest -from google.auth.exceptions import MutualTLSChannelError - from google.api_core.gapic_v1.client_utils import ( get_api_endpoint, get_default_mtls_endpoint, get_universe_domain, ) +from google.auth.exceptions import MutualTLSChannelError class MockClient: @@ -40,10 +39,7 @@ def test_get_default_mtls_endpoint(): == "foo.mtls.sandbox.googleapis.com" ) # Test case-insensitivity - assert ( - get_default_mtls_endpoint("foo.GoogleAPIs.com") - == "foo.mtls.googleapis.com" - ) + assert get_default_mtls_endpoint("foo.GoogleAPIs.com") == "foo.mtls.googleapis.com" assert ( get_default_mtls_endpoint("foo.Sandbox.GoogleAPIs.com") == "foo.mtls.sandbox.googleapis.com" From 1f95ebc2d280bd95f59e3838bca536c85ebac684 Mon Sep 17 00:00:00 2001 From: Heba Alazzeh Date: Mon, 20 Jul 2026 19:44:54 +0000 Subject: [PATCH 12/12] feat(generator): add _compat.py fallback for routing helpers --- .../gapic/generator/generator.py | 2 +- .../%name_%version/%sub/_compat.py.j2 | 76 +++++++++++++++++++ .../%sub/services/%service/client.py.j2 | 2 +- 3 files changed, 78 insertions(+), 2 deletions(-) create mode 100644 packages/gapic-generator/gapic/templates/%namespace/%name_%version/%sub/_compat.py.j2 diff --git a/packages/gapic-generator/gapic/generator/generator.py b/packages/gapic-generator/gapic/generator/generator.py index 9fe56aa9de1d..c1932abbe426 100644 --- a/packages/gapic-generator/gapic/generator/generator.py +++ b/packages/gapic-generator/gapic/generator/generator.py @@ -120,7 +120,7 @@ def get_response(self, api_schema: api.API, opts: Options) -> CodeGeneratorRespo for template_name in client_templates: # Quick check: Skip "private" templates. filename = template_name.split("/")[-1] - if filename.startswith("_") and filename != "__init__.py.j2": + if filename.startswith("_") and filename not in ("__init__.py.j2", "_compat.py.j2"): continue # Append to the output files dictionary. diff --git a/packages/gapic-generator/gapic/templates/%namespace/%name_%version/%sub/_compat.py.j2 b/packages/gapic-generator/gapic/templates/%namespace/%name_%version/%sub/_compat.py.j2 new file mode 100644 index 000000000000..896222c550a4 --- /dev/null +++ b/packages/gapic-generator/gapic/templates/%namespace/%name_%version/%sub/_compat.py.j2 @@ -0,0 +1,76 @@ +# {% include '_license.j2' %} + +import re +from typing import Optional, Callable, Tuple, Union +from google.auth.exceptions import MutualTLSChannelError + +try: + from google.api_core.gapic_v1.client_utils import ( + get_default_mtls_endpoint, + get_api_endpoint, + get_universe_domain, + ) +except ImportError: + # TODO: Remove these fallbacks when google-api-core >= 2.18.0 is the minimum required version. + + def get_default_mtls_endpoint(api_endpoint: Optional[str]) -> Optional[str]: + """Converts api endpoint to mTLS endpoint.""" + if not api_endpoint: + return api_endpoint + + mtls_endpoint_re = re.compile( + r"(?P[^.]+)(?P\.mtls)?(?P\.sandbox)?(?P\.googleapis\.com)?" + ) + + m = mtls_endpoint_re.match(api_endpoint) + if m is None: + # Could not parse api_endpoint; return as-is. + return api_endpoint + + name, mtls, sandbox, googledomain = m.groups() + if mtls or not googledomain: + return api_endpoint + + if sandbox: + return api_endpoint.replace( + "sandbox.googleapis.com", "mtls.sandbox.googleapis.com" + ) + + return api_endpoint.replace(".googleapis.com", ".mtls.googleapis.com") + + def get_api_endpoint( + api_override: Optional[str], + client_cert_source: Optional[Callable[[], Tuple[bytes, bytes]]], + universe_domain: str, + use_mtls_endpoint: str, + default_universe: str, + default_mtls_endpoint: str, + default_endpoint_template: str, + ) -> str: + """Return the API endpoint used by the client.""" + if api_override is not None: + api_endpoint = api_override + elif use_mtls_endpoint == "always" or (use_mtls_endpoint == "auto" and client_cert_source): + if universe_domain != default_universe: + raise MutualTLSChannelError( + f"mTLS is not supported in any universe other than {default_universe}." + ) + api_endpoint = default_mtls_endpoint + else: + api_endpoint = default_endpoint_template.format(UNIVERSE_DOMAIN=universe_domain) + return api_endpoint + + def get_universe_domain( + client_universe_domain: Optional[str], + universe_domain_env: Optional[str], + default_universe: str, + ) -> str: + """Return the universe domain used by the client.""" + universe_domain = default_universe + if client_universe_domain is not None: + universe_domain = client_universe_domain + elif universe_domain_env is not None: + universe_domain = universe_domain_env + if len(universe_domain.strip()) == 0: + raise ValueError("Universe Domain cannot be an empty string.") + return universe_domain diff --git a/packages/gapic-generator/gapic/templates/%namespace/%name_%version/%sub/services/%service/client.py.j2 b/packages/gapic-generator/gapic/templates/%namespace/%name_%version/%sub/services/%service/client.py.j2 index 3b3b9474effd..25ea84a9fb58 100644 --- a/packages/gapic-generator/gapic/templates/%namespace/%name_%version/%sub/services/%service/client.py.j2 +++ b/packages/gapic-generator/gapic/templates/%namespace/%name_%version/%sub/services/%service/client.py.j2 @@ -30,7 +30,7 @@ from google.api_core import exceptions as core_exceptions from google.api_core import extended_operation {% endif %} from google.api_core import gapic_v1 -from google.api_core.gapic_v1 import client_utils +from {{package_path}} import _compat as client_utils from google.api_core import retry as retries from google.auth import credentials as ga_credentials # type: ignore from google.auth.transport import mtls # type: ignore