22
33{% block content %}
44from collections import OrderedDict
5- from typing import Dict, {% if service .any_server_streaming %} Iterable, {% endif %}{% if service .any_client_streaming %} Iterator, {% endif %} Sequence, Tuple, Type, Union
5+ import re
6+ from typing import Callable, Dict, {% if service .any_server_streaming %} Iterable, {% endif %}{% if service .any_client_streaming %} Iterator, {% endif %} Sequence, Tuple, Type, Union
67import pkg_resources
78
89import google.api_core.client_options as ClientOptions # type: ignore
@@ -57,7 +58,39 @@ class {{ service.client_name }}Meta(type):
5758class {{ service.client_name }}(metaclass={{ service.client_name }}Meta):
5859 """{{ service.meta.doc|rst(width=72, indent=4) }}"""
5960
60- DEFAULT_OPTIONS = ClientOptions.ClientOptions({% if service .host %} api_endpoint='{{ service.host }}'{% endif %} )
61+ @staticmethod
62+ def _get_default_mtls_endpoint(api_endpoint):
63+ """Convert api endpoint to mTLS endpoint.
64+ Convert "*.sandbox.googleapis.com" and "*.googleapis.com" to
65+ "*.mtls.sandbox.googleapis.com" and "*.mtls.googleapis.com" respectively.
66+ Args:
67+ api_endpoint (Optional[str]): the api endpoint to convert.
68+ Returns:
69+ str: converted mTLS api endpoint.
70+ """
71+ if not api_endpoint:
72+ return api_endpoint
73+
74+ mtls_endpoint_re = re.compile(
75+ r"(?P<name >[^.]+)(?P<mtls >\.mtls)?(?P<sandbox >\.sandbox)?(?P<googledomain >\.googleapis\.com)?"
76+ )
77+
78+ m = mtls_endpoint_re.match(api_endpoint)
79+ name, mtls, sandbox, googledomain = m.groups()
80+ if mtls or not googledomain:
81+ return api_endpoint
82+
83+ if sandbox:
84+ return api_endpoint.replace(
85+ "sandbox.googleapis.com", "mtls.sandbox.googleapis.com"
86+ )
87+
88+ return api_endpoint.replace(".googleapis.com", ".mtls.googleapis.com")
89+
90+ DEFAULT_ENDPOINT = {% if service .host %} '{{ service.host }}'{% else %} None{% endif %}
91+ DEFAULT_MTLS_ENDPOINT = _get_default_mtls_endpoint.__func__( # type: ignore
92+ DEFAULT_ENDPOINT
93+ )
6194
6295 @classmethod
6396 def from_service_account_file(cls, filename: str, *args, **kwargs):
@@ -92,7 +125,7 @@ class {{ service.client_name }}(metaclass={{ service.client_name }}Meta):
92125 def __init__(self, *,
93126 credentials: credentials.Credentials = None,
94127 transport: Union[str, {{ service.name }}Transport] = None,
95- client_options: ClientOptions = DEFAULT_OPTIONS ,
128+ client_options: ClientOptions = None ,
96129 ) -> None:
97130 """Instantiate the {{ (service.client_name|snake_case).replace('_', ' ') }}.
98131
@@ -106,6 +139,17 @@ class {{ service.client_name }}(metaclass={{ service.client_name }}Meta):
106139 transport to use. If set to None, a transport is chosen
107140 automatically.
108141 client_options (ClientOptions): Custom options for the client.
142+ (1) The ``api_endpoint`` property can be used to override the
143+ default endpoint provided by the client.
144+ (2) If ``transport`` argument is None, ``client_options`` can be
145+ used to create a mutual TLS transport. If ``client_cert_source``
146+ is provided, mutual TLS transport will be created with the given
147+ ``api_endpoint`` or the default mTLS endpoint, and the client
148+ SSL credentials obtained from ``client_cert_source``.
149+
150+ Raises:
151+ google.auth.exceptions.MutualTlsChannelError: If mutual TLS transport
152+ creation failed for any reason.
109153 """
110154 if isinstance(client_options, dict):
111155 client_options = ClientOptions.from_dict(client_options)
@@ -114,16 +158,45 @@ class {{ service.client_name }}(metaclass={{ service.client_name }}Meta):
114158 # Ordinarily, we provide the transport, but allowing a custom transport
115159 # instance provides an extensibility point for unusual situations.
116160 if isinstance(transport, {{ service.name }}Transport):
161+ # transport is a {{ service.name }}Transport instance.
117162 if credentials:
118163 raise ValueError('When providing a transport instance, '
119164 'provide its credentials directly.')
120165 self._transport = transport
121- else:
166+ elif client_options is None or (
167+ client_options.api_endpoint == None
168+ and client_options.client_cert_source is None
169+ ):
170+ # Don't trigger mTLS if we get an empty ClientOptions.
122171 Transport = type(self).get_transport_class(transport)
123172 self._transport = Transport(
124- credentials=credentials,
125- host=client_options.api_endpoint{% if service .host %} or '{{ service.host }}'{% endif %} ,
173+ credentials=credentials, host=self.DEFAULT_ENDPOINT
126174 )
175+ else:
176+ # We have a non-empty ClientOptions. If client_cert_source is
177+ # provided, trigger mTLS with user provided endpoint or the default
178+ # mTLS endpoint.
179+ if client_options.client_cert_source:
180+ api_mtls_endpoint = (
181+ client_options.api_endpoint
182+ if client_options.api_endpoint
183+ else self.DEFAULT_MTLS_ENDPOINT
184+ )
185+ else:
186+ api_mtls_endpoint = None
187+
188+ api_endpoint = (
189+ client_options.api_endpoint
190+ if client_options.api_endpoint
191+ else self.DEFAULT_ENDPOINT
192+ )
193+
194+ self._transport = {{ service.name }}GrpcTransport(
195+ credentials=credentials,
196+ host=api_endpoint,
197+ api_mtls_endpoint=api_mtls_endpoint,
198+ client_cert_source=client_options.client_cert_source,
199+ )
127200
128201 {% for method in service .methods .values () -%}
129202 def {{ method.name|snake_case }}(self,
0 commit comments