Skip to content

Commit a3221a0

Browse files
committed
Merge routing into mtls and resolve conflicts
2 parents 75dc49f + 91040b3 commit a3221a0

26 files changed

Lines changed: 389 additions & 1362 deletions

File tree

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

Lines changed: 78 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,85 @@
22

33
"""A compatibility module for older versions of google-api-core."""
44

5+
import re
56
import uuid
7+
from typing import Optional, Callable, Tuple, Union
8+
from google.auth.exceptions import MutualTLSChannelError
9+
10+
try:
11+
from google.api_core.universe import (
12+
get_default_mtls_endpoint,
13+
get_api_endpoint,
14+
get_universe_domain,
15+
)
16+
except ImportError:
17+
# TODO: Remove these fallbacks when google-api-core >= 2.18.0 is the minimum required version.
18+
19+
def get_default_mtls_endpoint(api_endpoint: Optional[str]) -> Optional[str]:
20+
"""Converts api endpoint to mTLS endpoint."""
21+
if not api_endpoint:
22+
return api_endpoint
23+
24+
mtls_endpoint_re = re.compile(
25+
r"(?P<name>[^.]+)(?P<mtls>\.mtls)?(?P<sandbox>\.sandbox)?(?P<googledomain>\.googleapis\.com)?"
26+
)
27+
28+
m = mtls_endpoint_re.match(api_endpoint)
29+
if m is None:
30+
# Could not parse api_endpoint; return as-is.
31+
return api_endpoint
32+
33+
name, mtls, sandbox, googledomain = m.groups()
34+
if mtls or not googledomain:
35+
return api_endpoint
36+
37+
if sandbox:
38+
return api_endpoint.replace(
39+
"sandbox.googleapis.com", "mtls.sandbox.googleapis.com"
40+
)
41+
42+
return api_endpoint.replace(".googleapis.com", ".mtls.googleapis.com")
43+
44+
def get_api_endpoint(
45+
api_override: Optional[str],
46+
client_cert_source: Optional[Callable[[], Tuple[bytes, bytes]]],
47+
universe_domain: str,
48+
use_mtls_endpoint: str,
49+
default_universe: str,
50+
default_mtls_endpoint: Optional[str],
51+
default_endpoint_template: str,
52+
) -> Optional[str]:
53+
"""Return the API endpoint used by the client."""
54+
if api_override is not None:
55+
api_endpoint = api_override
56+
elif use_mtls_endpoint == "always" or (use_mtls_endpoint == "auto" and client_cert_source):
57+
if universe_domain != default_universe:
58+
raise MutualTLSChannelError(
59+
f"mTLS is not supported in any universe other than {default_universe}."
60+
)
61+
api_endpoint = default_mtls_endpoint
62+
else:
63+
api_endpoint = default_endpoint_template.format(UNIVERSE_DOMAIN=universe_domain)
64+
65+
return api_endpoint
66+
67+
def get_universe_domain(
68+
universe_domain: Optional[str],
69+
credentials_universe_domain: Optional[str]
70+
) -> str:
71+
"""Returns the universe domain from the user and the credentials."""
72+
if not universe_domain and not credentials_universe_domain:
73+
return "googleapis.com"
74+
if not universe_domain:
75+
return credentials_universe_domain
76+
if not credentials_universe_domain:
77+
return universe_domain
78+
if universe_domain != credentials_universe_domain:
79+
raise ValueError(
80+
f"The universe_domain in ClientOptions ({universe_domain}) and "
81+
f"credentials ({credentials_universe_domain}) must match."
82+
)
83+
return universe_domain
684

785
try:
886
from google.api_core.gapic_v1.request import setup_request_id # type: ignore
@@ -40,4 +118,3 @@ except ImportError:
40118
else:
41119
if not getattr(request, field_name, None):
42120
setattr(request, field_name, request_id_val)
43-

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

Lines changed: 25 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ from google.api_core import exceptions as core_exceptions
3535
from google.api_core import extended_operation
3636
{% endif %}
3737
from google.api_core import gapic_v1
38+
from {{package_path}} import _compat as client_utils
3839
{% if has_auto_populated_fields.value %}
3940
from {{package_path}}._compat import setup_request_id
4041
{% endif %}
@@ -151,44 +152,10 @@ class {{ service.client_name }}(metaclass={{ service.client_name }}Meta):
151152
"""{{ service.meta.doc|rst(width=72, indent=4) }}{% if service.version|length %}
152153
This class implements API version {{ service.version }}.{% endif %}"""
153154

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

191-
DEFAULT_MTLS_ENDPOINT = _get_default_mtls_endpoint.__func__( # type: ignore
158+
DEFAULT_MTLS_ENDPOINT = client_utils.get_default_mtls_endpoint(
192159
DEFAULT_ENDPOINT
193160
)
194161

@@ -400,30 +367,34 @@ class {{ service.client_name }}(metaclass={{ service.client_name }}Meta):
400367
return client_cert_source
401368

402369
@staticmethod
403-
def _get_api_endpoint(api_override, client_cert_source, universe_domain, use_mtls_endpoint) -> str:
370+
def _get_api_endpoint(
371+
api_override: Optional[str],
372+
client_cert_source: Optional[Callable[[], Tuple[bytes, bytes]]],
373+
universe_domain: str,
374+
use_mtls_endpoint: str,
375+
) -> Optional[str]:
404376
"""Return the API endpoint used by the client.
405377

406378
Args:
407-
api_override (str): The API endpoint override. If specified, this is always
379+
api_override (Optional[str]): The API endpoint override. If specified, this is always
408380
the return value of this function and the other arguments are not used.
409-
client_cert_source (bytes): The client certificate source used by the client.
381+
client_cert_source (Union[Callable[[], Tuple[bytes, bytes]], None]): The client certificate source used by the client.
410382
universe_domain (str): The universe domain used by the client.
411383
use_mtls_endpoint (str): How to use the mTLS endpoint, which depends also on the other parameters.
412384
Possible values are "always", "auto", or "never".
413385

414386
Returns:
415-
str: The API endpoint to be used by the client.
387+
Optional[str]: The API endpoint to be used by the client.
416388
"""
417-
if api_override is not None:
418-
api_endpoint = api_override
419-
elif use_mtls_endpoint == "always" or (use_mtls_endpoint == "auto" and client_cert_source):
420-
_default_universe = {{ service.client_name }}._DEFAULT_UNIVERSE
421-
if universe_domain != _default_universe:
422-
raise MutualTLSChannelError(f"mTLS is not supported in any universe other than {_default_universe}.")
423-
api_endpoint = {{ service.client_name }}.DEFAULT_MTLS_ENDPOINT
424-
else:
425-
api_endpoint = {{ service.client_name }}._DEFAULT_ENDPOINT_TEMPLATE.format(UNIVERSE_DOMAIN=universe_domain)
426-
return api_endpoint
389+
return client_utils.get_api_endpoint(
390+
api_override,
391+
client_cert_source,
392+
universe_domain,
393+
use_mtls_endpoint,
394+
{{ service.client_name }}._DEFAULT_UNIVERSE,
395+
{{ service.client_name }}.DEFAULT_MTLS_ENDPOINT,
396+
{{ service.client_name }}._DEFAULT_ENDPOINT_TEMPLATE,
397+
)
427398

428399
@staticmethod
429400
def _get_universe_domain(client_universe_domain: Optional[str], universe_domain_env: Optional[str]) -> str:
@@ -439,14 +410,11 @@ class {{ service.client_name }}(metaclass={{ service.client_name }}Meta):
439410
Raises:
440411
ValueError: If the universe domain is an empty string.
441412
"""
442-
universe_domain = {{ service.client_name }}._DEFAULT_UNIVERSE
443-
if client_universe_domain is not None:
444-
universe_domain = client_universe_domain
445-
elif universe_domain_env is not None:
446-
universe_domain = universe_domain_env
447-
if len(universe_domain.strip()) == 0:
448-
raise ValueError("Universe Domain cannot be an empty string.")
449-
return universe_domain
413+
return client_utils.get_universe_domain(
414+
client_universe_domain,
415+
universe_domain_env,
416+
{{ service.client_name }}._DEFAULT_UNIVERSE,
417+
)
450418

451419
def _validate_universe_domain(self):
452420
"""Validates client's and credentials' universe domains are consistent.

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

Lines changed: 0 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -164,22 +164,6 @@ def set_event_loop():
164164
asyncio.set_event_loop(None)
165165

166166

167-
def test__get_default_mtls_endpoint():
168-
api_endpoint = "example.googleapis.com"
169-
api_mtls_endpoint = "example.mtls.googleapis.com"
170-
sandbox_endpoint = "example.sandbox.googleapis.com"
171-
sandbox_mtls_endpoint = "example.mtls.sandbox.googleapis.com"
172-
non_googleapi = "api.example.com"
173-
custom_endpoint = ".custom"
174-
175-
assert {{ service.client_name }}._get_default_mtls_endpoint(None) is None
176-
assert {{ service.client_name }}._get_default_mtls_endpoint(api_endpoint) == api_mtls_endpoint
177-
assert {{ service.client_name }}._get_default_mtls_endpoint(api_mtls_endpoint) == api_mtls_endpoint
178-
assert {{ service.client_name }}._get_default_mtls_endpoint(sandbox_endpoint) == sandbox_mtls_endpoint
179-
assert {{ service.client_name }}._get_default_mtls_endpoint(sandbox_mtls_endpoint) == sandbox_mtls_endpoint
180-
assert {{ service.client_name }}._get_default_mtls_endpoint(non_googleapi) == non_googleapi
181-
assert {{ service.client_name }}._get_default_mtls_endpoint(custom_endpoint) == custom_endpoint
182-
183167
def test__read_environment_variables():
184168
assert {{ service.client_name }}._read_environment_variables() == (False, "auto", None)
185169

@@ -321,29 +305,7 @@ def test__get_client_cert_source():
321305
assert {{ service.client_name }}._get_client_cert_source(None, True) is mock_default_cert_source
322306
assert {{ service.client_name }}._get_client_cert_source(mock_provided_cert_source, "true") is mock_provided_cert_source
323307

324-
@mock.patch.object({{ service.client_name }}, "_DEFAULT_ENDPOINT_TEMPLATE", modify_default_endpoint_template({{ service.client_name }}))
325-
{% if 'grpc' in opts.transport %}
326-
@mock.patch.object({{ service.async_client_name }}, "_DEFAULT_ENDPOINT_TEMPLATE", modify_default_endpoint_template({{ service.async_client_name }}))
327-
{% endif %}
328-
def test__get_api_endpoint():
329-
api_override = "foo.com"
330-
mock_client_cert_source = mock.Mock()
331-
default_universe = {{ service.client_name }}._DEFAULT_UNIVERSE
332-
default_endpoint = {{ service.client_name }}._DEFAULT_ENDPOINT_TEMPLATE.format(UNIVERSE_DOMAIN=default_universe)
333-
mock_universe = "bar.com"
334-
mock_endpoint = {{ service.client_name }}._DEFAULT_ENDPOINT_TEMPLATE.format(UNIVERSE_DOMAIN=mock_universe)
335-
336-
assert {{ service.client_name }}._get_api_endpoint(api_override, mock_client_cert_source, default_universe, "always") == api_override
337-
assert {{ service.client_name }}._get_api_endpoint(None, mock_client_cert_source, default_universe, "auto") == {{ service.client_name }}.DEFAULT_MTLS_ENDPOINT
338-
assert {{ service.client_name }}._get_api_endpoint(None, None, default_universe, "auto") == default_endpoint
339-
assert {{ service.client_name }}._get_api_endpoint(None, None, default_universe, "always") == {{ service.client_name }}.DEFAULT_MTLS_ENDPOINT
340-
assert {{ service.client_name }}._get_api_endpoint(None, mock_client_cert_source, default_universe, "always") == {{ service.client_name }}.DEFAULT_MTLS_ENDPOINT
341-
assert {{ service.client_name }}._get_api_endpoint(None, None, mock_universe, "never") == mock_endpoint
342-
assert {{ service.client_name }}._get_api_endpoint(None, None, default_universe, "never") == default_endpoint
343308

344-
with pytest.raises(MutualTLSChannelError) as excinfo:
345-
{{ service.client_name }}._get_api_endpoint(None, mock_client_cert_source, mock_universe, "auto")
346-
assert str(excinfo.value) == "mTLS is not supported in any universe other than googleapis.com."
347309

348310
{% if service.version %}
349311
{% for method in service.methods.values() %}{% with method_name = method.name|snake_case %}
@@ -392,17 +354,7 @@ def test_{{ method_name }}_api_version_header(transport_name):
392354
{% endfor %}
393355
{% endif %}{# service.version #}
394356

395-
def test__get_universe_domain():
396-
client_universe_domain = "foo.com"
397-
universe_domain_env = "bar.com"
398-
399-
assert {{ service.client_name }}._get_universe_domain(client_universe_domain, universe_domain_env) == client_universe_domain
400-
assert {{ service.client_name }}._get_universe_domain(None, universe_domain_env) == universe_domain_env
401-
assert {{ service.client_name }}._get_universe_domain(None, None) == {{ service.client_name }}._DEFAULT_UNIVERSE
402357

403-
with pytest.raises(ValueError) as excinfo:
404-
{{ service.client_name }}._get_universe_domain("", None)
405-
assert str(excinfo.value) == "Universe Domain cannot be an empty string."
406358

407359
@pytest.mark.parametrize("error_code,cred_info_json,show_cred_info", [
408360
(401, CRED_INFO_JSON, True),

0 commit comments

Comments
 (0)