Skip to content

Commit d435c6c

Browse files
committed
feat(generator): restrict routing centralization to get_universe_domain
1 parent 0282c14 commit d435c6c

37 files changed

Lines changed: 1173 additions & 954 deletions

File tree

packages/gapic-generator/gapic/templates/%namespace/%name_%version/%sub/_compat.py.j2

Lines changed: 1 addition & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1,66 +1,14 @@
11
{% include '_license.j2' %}
22

3-
import re
4-
from typing import Optional, Callable, Tuple, Union
5-
from google.auth.exceptions import MutualTLSChannelError
3+
from typing import Optional
64

75
try:
86
from google.api_core.gapic_v1.client_utils import ( # type: ignore
9-
get_default_mtls_endpoint,
10-
get_api_endpoint,
117
get_universe_domain,
128
)
139
except ImportError: # pragma: NO COVER
1410
# TODO: Remove these fallbacks when google-api-core >= 2.18.0 is the minimum required version.
1511

16-
def get_default_mtls_endpoint(api_endpoint: Optional[str]) -> Optional[str]:
17-
"""Converts api endpoint to mTLS endpoint."""
18-
if not api_endpoint:
19-
return api_endpoint
20-
21-
mtls_endpoint_re = re.compile(
22-
r"(?P<name>[^.]+)(?P<mtls>\.mtls)?(?P<sandbox>\.sandbox)?(?P<googledomain>\.googleapis\.com)?"
23-
)
24-
25-
m = mtls_endpoint_re.match(api_endpoint)
26-
if m is None:
27-
# Could not parse api_endpoint; return as-is.
28-
return api_endpoint
29-
30-
name, mtls, sandbox, googledomain = m.groups()
31-
if mtls or not googledomain:
32-
return api_endpoint
33-
34-
if sandbox:
35-
return api_endpoint.replace(
36-
"sandbox.googleapis.com", "mtls.sandbox.googleapis.com"
37-
)
38-
39-
return api_endpoint.replace(".googleapis.com", ".mtls.googleapis.com")
40-
41-
def get_api_endpoint(
42-
api_override: Optional[str],
43-
universe_domain: str,
44-
default_universe: str,
45-
default_mtls_endpoint: Optional[str],
46-
default_endpoint_template: str,
47-
use_mtls: bool,
48-
) -> str:
49-
"""Return the API endpoint used by the client."""
50-
if api_override is not None:
51-
return api_override
52-
53-
if use_mtls:
54-
if universe_domain != default_universe:
55-
raise MutualTLSChannelError(
56-
f"mTLS is not supported in any universe other than {default_universe}."
57-
)
58-
if not default_mtls_endpoint:
59-
raise ValueError("mTLS endpoint is not available.")
60-
return default_mtls_endpoint
61-
else:
62-
return default_endpoint_template.format(UNIVERSE_DOMAIN=universe_domain)
63-
6412
def get_universe_domain(
6513
*potential_universes: Optional[str],
6614
default_universe: str,

packages/gapic-generator/gapic/templates/%namespace/%name_%version/%sub/services/%service/client.py.j2

Lines changed: 50 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -144,10 +144,44 @@ class {{ service.client_name }}(metaclass={{ service.client_name }}Meta):
144144
"""{{ service.meta.doc|rst(width=72, indent=4) }}{% if service.version|length %}
145145
This class implements API version {{ service.version }}.{% endif %}"""
146146

147+
@staticmethod
148+
def _get_default_mtls_endpoint(api_endpoint) -> Optional[str]:
149+
"""Converts api endpoint to mTLS endpoint.
150+
151+
Convert "*.sandbox.googleapis.com" and "*.googleapis.com" to
152+
"*.mtls.sandbox.googleapis.com" and "*.mtls.googleapis.com" respectively.
153+
Args:
154+
api_endpoint (Optional[str]): the api endpoint to convert.
155+
Returns:
156+
Optional[str]: converted mTLS api endpoint.
157+
"""
158+
if not api_endpoint:
159+
return api_endpoint
160+
161+
mtls_endpoint_re = re.compile(
162+
r"(?P<name>[^.]+)(?P<mtls>\.mtls)?(?P<sandbox>\.sandbox)?(?P<googledomain>\.googleapis\.com)?"
163+
)
164+
165+
m = mtls_endpoint_re.match(api_endpoint)
166+
if m is None:
167+
# Could not parse api_endpoint; return as-is.
168+
return api_endpoint
169+
170+
name, mtls, sandbox, googledomain = m.groups()
171+
if mtls or not googledomain:
172+
return api_endpoint
173+
174+
if sandbox:
175+
return api_endpoint.replace(
176+
"sandbox.googleapis.com", "mtls.sandbox.googleapis.com"
177+
)
178+
179+
return api_endpoint.replace(".googleapis.com", ".mtls.googleapis.com")
180+
147181
# Note: DEFAULT_ENDPOINT is deprecated. Use _DEFAULT_ENDPOINT_TEMPLATE instead.
148182
DEFAULT_ENDPOINT = {% if service.host %}"{{ service.host }}"{% else %}None{% endif %}
149183

150-
DEFAULT_MTLS_ENDPOINT = client_utils.get_default_mtls_endpoint(
184+
DEFAULT_MTLS_ENDPOINT = _get_default_mtls_endpoint.__func__( # type: ignore
151185
DEFAULT_ENDPOINT
152186
)
153187

@@ -309,12 +343,9 @@ class {{ service.client_name }}(metaclass={{ service.client_name }}Meta):
309343
client_cert_source = mtls.default_client_cert_source()
310344

311345
# Figure out which api endpoint to use.
312-
api_endpoint_opt = client_options.api_endpoint
313-
if api_endpoint_opt is not None:
314-
api_endpoint = api_endpoint_opt
346+
if client_options.api_endpoint is not None:
347+
api_endpoint = client_options.api_endpoint
315348
elif use_mtls_endpoint == "always" or (use_mtls_endpoint == "auto" and client_cert_source):
316-
if cls.DEFAULT_MTLS_ENDPOINT is None:
317-
raise MutualTLSChannelError("mTLS endpoint is not available.") # pragma: NO COVER
318349
api_endpoint = cls.DEFAULT_MTLS_ENDPOINT
319350
else:
320351
api_endpoint = cls.DEFAULT_ENDPOINT
@@ -362,36 +393,30 @@ class {{ service.client_name }}(metaclass={{ service.client_name }}Meta):
362393
return client_cert_source
363394

364395
@staticmethod
365-
def _get_api_endpoint(
366-
api_override: Optional[str],
367-
client_cert_source: Optional[Callable[[], Tuple[bytes, bytes]]],
368-
universe_domain: str,
369-
use_mtls_endpoint: str,
370-
) -> str:
396+
def _get_api_endpoint(api_override, client_cert_source, universe_domain, use_mtls_endpoint) -> str:
371397
"""Return the API endpoint used by the client.
372398

373399
Args:
374-
api_override (Optional[str]): The API endpoint override. If specified, this is always
400+
api_override (str): The API endpoint override. If specified, this is always
375401
the return value of this function and the other arguments are not used.
376-
client_cert_source (Union[Callable[[], Tuple[bytes, bytes]], None]): The client certificate source used by the client.
402+
client_cert_source (bytes): The client certificate source used by the client.
377403
universe_domain (str): The universe domain used by the client.
378404
use_mtls_endpoint (str): How to use the mTLS endpoint, which depends also on the other parameters.
379405
Possible values are "always", "auto", or "never".
380406

381407
Returns:
382408
str: The API endpoint to be used by the client.
383409
"""
384-
use_mtls = use_mtls_endpoint == "always" or (
385-
use_mtls_endpoint == "auto" and bool(client_cert_source)
386-
)
387-
return client_utils.get_api_endpoint(
388-
api_override,
389-
universe_domain,
390-
default_universe={{ service.client_name }}._DEFAULT_UNIVERSE,
391-
default_mtls_endpoint={{ service.client_name }}.DEFAULT_MTLS_ENDPOINT,
392-
default_endpoint_template={{ service.client_name }}._DEFAULT_ENDPOINT_TEMPLATE,
393-
use_mtls=use_mtls,
394-
)
410+
if api_override is not None:
411+
api_endpoint = api_override
412+
elif use_mtls_endpoint == "always" or (use_mtls_endpoint == "auto" and client_cert_source):
413+
_default_universe = {{ service.client_name }}._DEFAULT_UNIVERSE
414+
if universe_domain != _default_universe:
415+
raise MutualTLSChannelError(f"mTLS is not supported in any universe other than {_default_universe}.")
416+
api_endpoint = {{ service.client_name }}.DEFAULT_MTLS_ENDPOINT
417+
else:
418+
api_endpoint = {{ service.client_name }}._DEFAULT_ENDPOINT_TEMPLATE.format(UNIVERSE_DOMAIN=universe_domain)
419+
return api_endpoint
395420

396421
@staticmethod
397422
def _get_universe_domain(client_universe_domain: Optional[str], universe_domain_env: Optional[str]) -> str:

packages/gapic-generator/gapic/templates/tests/unit/gapic/%name_%version/%sub/test_%service.py.j2

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,22 @@ def set_event_loop():
156156
asyncio.set_event_loop(None)
157157

158158

159+
def test__get_default_mtls_endpoint():
160+
api_endpoint = "example.googleapis.com"
161+
api_mtls_endpoint = "example.mtls.googleapis.com"
162+
sandbox_endpoint = "example.sandbox.googleapis.com"
163+
sandbox_mtls_endpoint = "example.mtls.sandbox.googleapis.com"
164+
non_googleapi = "api.example.com"
165+
custom_endpoint = ".custom"
166+
167+
assert {{ service.client_name }}._get_default_mtls_endpoint(None) is None
168+
assert {{ service.client_name }}._get_default_mtls_endpoint(api_endpoint) == api_mtls_endpoint
169+
assert {{ service.client_name }}._get_default_mtls_endpoint(api_mtls_endpoint) == api_mtls_endpoint
170+
assert {{ service.client_name }}._get_default_mtls_endpoint(sandbox_endpoint) == sandbox_mtls_endpoint
171+
assert {{ service.client_name }}._get_default_mtls_endpoint(sandbox_mtls_endpoint) == sandbox_mtls_endpoint
172+
assert {{ service.client_name }}._get_default_mtls_endpoint(non_googleapi) == non_googleapi
173+
assert {{ service.client_name }}._get_default_mtls_endpoint(custom_endpoint) == custom_endpoint
174+
159175
def test__read_environment_variables():
160176
assert {{ service.client_name }}._read_environment_variables() == (False, "auto", None)
161177

@@ -297,7 +313,29 @@ def test__get_client_cert_source():
297313
assert {{ service.client_name }}._get_client_cert_source(None, True) is mock_default_cert_source
298314
assert {{ service.client_name }}._get_client_cert_source(mock_provided_cert_source, "true") is mock_provided_cert_source
299315

316+
@mock.patch.object({{ service.client_name }}, "_DEFAULT_ENDPOINT_TEMPLATE", modify_default_endpoint_template({{ service.client_name }}))
317+
{% if 'grpc' in opts.transport %}
318+
@mock.patch.object({{ service.async_client_name }}, "_DEFAULT_ENDPOINT_TEMPLATE", modify_default_endpoint_template({{ service.async_client_name }}))
319+
{% endif %}
320+
def test__get_api_endpoint():
321+
api_override = "foo.com"
322+
mock_client_cert_source = mock.Mock()
323+
default_universe = {{ service.client_name }}._DEFAULT_UNIVERSE
324+
default_endpoint = {{ service.client_name }}._DEFAULT_ENDPOINT_TEMPLATE.format(UNIVERSE_DOMAIN=default_universe)
325+
mock_universe = "bar.com"
326+
mock_endpoint = {{ service.client_name }}._DEFAULT_ENDPOINT_TEMPLATE.format(UNIVERSE_DOMAIN=mock_universe)
327+
328+
assert {{ service.client_name }}._get_api_endpoint(api_override, mock_client_cert_source, default_universe, "always") == api_override
329+
assert {{ service.client_name }}._get_api_endpoint(None, mock_client_cert_source, default_universe, "auto") == {{ service.client_name }}.DEFAULT_MTLS_ENDPOINT
330+
assert {{ service.client_name }}._get_api_endpoint(None, None, default_universe, "auto") == default_endpoint
331+
assert {{ service.client_name }}._get_api_endpoint(None, None, default_universe, "always") == {{ service.client_name }}.DEFAULT_MTLS_ENDPOINT
332+
assert {{ service.client_name }}._get_api_endpoint(None, mock_client_cert_source, default_universe, "always") == {{ service.client_name }}.DEFAULT_MTLS_ENDPOINT
333+
assert {{ service.client_name }}._get_api_endpoint(None, None, mock_universe, "never") == mock_endpoint
334+
assert {{ service.client_name }}._get_api_endpoint(None, None, default_universe, "never") == default_endpoint
300335

336+
with pytest.raises(MutualTLSChannelError) as excinfo:
337+
{{ service.client_name }}._get_api_endpoint(None, mock_client_cert_source, mock_universe, "auto")
338+
assert str(excinfo.value) == "mTLS is not supported in any universe other than googleapis.com."
301339

302340
{% if service.version %}
303341
{% for method in service.methods.values() %}{% with method_name = method.name|snake_case %}
@@ -346,8 +384,6 @@ def test_{{ method_name }}_api_version_header(transport_name):
346384
{% endfor %}
347385
{% endif %}{# service.version #}
348386

349-
350-
351387
@pytest.mark.parametrize("error_code,cred_info_json,show_cred_info", [
352388
(401, CRED_INFO_JSON, True),
353389
(403, CRED_INFO_JSON, True),

packages/gapic-generator/tests/integration/goldens/asset/google/cloud/asset_v1/_compat.py

100755100644
Lines changed: 2 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -12,67 +12,16 @@
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
1414
#
15-
import re
16-
from typing import Optional, Callable, Tuple, Union
17-
from google.auth.exceptions import MutualTLSChannelError
15+
16+
from typing import Optional
1817

1918
try:
2019
from google.api_core.gapic_v1.client_utils import ( # type: ignore
21-
get_default_mtls_endpoint,
22-
get_api_endpoint,
2320
get_universe_domain,
2421
)
2522
except ImportError: # pragma: NO COVER
2623
# TODO: Remove these fallbacks when google-api-core >= 2.18.0 is the minimum required version.
2724

28-
def get_default_mtls_endpoint(api_endpoint: Optional[str]) -> Optional[str]:
29-
"""Converts api endpoint to mTLS endpoint."""
30-
if not api_endpoint:
31-
return api_endpoint
32-
33-
mtls_endpoint_re = re.compile(
34-
r"(?P<name>[^.]+)(?P<mtls>\.mtls)?(?P<sandbox>\.sandbox)?(?P<googledomain>\.googleapis\.com)?"
35-
)
36-
37-
m = mtls_endpoint_re.match(api_endpoint)
38-
if m is None:
39-
# Could not parse api_endpoint; return as-is.
40-
return api_endpoint
41-
42-
name, mtls, sandbox, googledomain = m.groups()
43-
if mtls or not googledomain:
44-
return api_endpoint
45-
46-
if sandbox:
47-
return api_endpoint.replace(
48-
"sandbox.googleapis.com", "mtls.sandbox.googleapis.com"
49-
)
50-
51-
return api_endpoint.replace(".googleapis.com", ".mtls.googleapis.com")
52-
53-
def get_api_endpoint(
54-
api_override: Optional[str],
55-
universe_domain: str,
56-
default_universe: str,
57-
default_mtls_endpoint: Optional[str],
58-
default_endpoint_template: str,
59-
use_mtls: bool,
60-
) -> str:
61-
"""Return the API endpoint used by the client."""
62-
if api_override is not None:
63-
return api_override
64-
65-
if use_mtls:
66-
if universe_domain != default_universe:
67-
raise MutualTLSChannelError(
68-
f"mTLS is not supported in any universe other than {default_universe}."
69-
)
70-
if not default_mtls_endpoint:
71-
raise ValueError("mTLS endpoint is not available.")
72-
return default_mtls_endpoint
73-
else:
74-
return default_endpoint_template.format(UNIVERSE_DOMAIN=universe_domain)
75-
7625
def get_universe_domain(
7726
*potential_universes: Optional[str],
7827
default_universe: str,

0 commit comments

Comments
 (0)