Skip to content

Commit c2c39bd

Browse files
feat: add api key support (#116)
* chore: upgrade gapic-generator-java, gax-java and gapic-generator-python PiperOrigin-RevId: 423842556 Source-Link: googleapis/googleapis@a616ca0 Source-Link: googleapis/googleapis-gen@29b938c Copy-Tag: eyJwIjoiLmdpdGh1Yi8uT3dsQm90LnlhbWwiLCJoIjoiMjliOTM4YzU4YzFlNTFkMDE5ZjJlZTUzOWQ1NWRjMGEzYzg2YTkwNSJ9 * 🦉 Updates from OwlBot See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md Co-authored-by: Owl Bot <gcf-owl-bot[bot]@users.noreply.github.com>
1 parent 9f543df commit c2c39bd

3 files changed

Lines changed: 249 additions & 44 deletions

File tree

packages/google-area120-tables/google/area120/tables_v1alpha1/services/tables_service/async_client.py

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
from collections import OrderedDict
1717
import functools
1818
import re
19-
from typing import Dict, Sequence, Tuple, Type, Union
19+
from typing import Dict, Optional, Sequence, Tuple, Type, Union
2020
import pkg_resources
2121

2222
from google.api_core.client_options import ClientOptions
@@ -125,6 +125,42 @@ def from_service_account_file(cls, filename: str, *args, **kwargs):
125125

126126
from_service_account_json = from_service_account_file
127127

128+
@classmethod
129+
def get_mtls_endpoint_and_cert_source(
130+
cls, client_options: Optional[ClientOptions] = None
131+
):
132+
"""Return the API endpoint and client cert source for mutual TLS.
133+
134+
The client cert source is determined in the following order:
135+
(1) if `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable is not "true", the
136+
client cert source is None.
137+
(2) if `client_options.client_cert_source` is provided, use the provided one; if the
138+
default client cert source exists, use the default one; otherwise the client cert
139+
source is None.
140+
141+
The API endpoint is determined in the following order:
142+
(1) if `client_options.api_endpoint` if provided, use the provided one.
143+
(2) if `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable is "always", use the
144+
default mTLS endpoint; if the environment variabel is "never", use the default API
145+
endpoint; otherwise if client cert source exists, use the default mTLS endpoint, otherwise
146+
use the default API endpoint.
147+
148+
More details can be found at https://google.aip.dev/auth/4114.
149+
150+
Args:
151+
client_options (google.api_core.client_options.ClientOptions): Custom options for the
152+
client. Only the `api_endpoint` and `client_cert_source` properties may be used
153+
in this method.
154+
155+
Returns:
156+
Tuple[str, Callable[[], Tuple[bytes, bytes]]]: returns the API endpoint and the
157+
client cert source to use.
158+
159+
Raises:
160+
google.auth.exceptions.MutualTLSChannelError: If any errors happen.
161+
"""
162+
return TablesServiceClient.get_mtls_endpoint_and_cert_source(client_options) # type: ignore
163+
128164
@property
129165
def transport(self) -> TablesServiceTransport:
130166
"""Returns the transport used by the client instance.

packages/google-area120-tables/google/area120/tables_v1alpha1/services/tables_service/client.py

Lines changed: 84 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,73 @@ def parse_common_location_path(path: str) -> Dict[str, str]:
264264
m = re.match(r"^projects/(?P<project>.+?)/locations/(?P<location>.+?)$", path)
265265
return m.groupdict() if m else {}
266266

267+
@classmethod
268+
def get_mtls_endpoint_and_cert_source(
269+
cls, client_options: Optional[client_options_lib.ClientOptions] = None
270+
):
271+
"""Return the API endpoint and client cert source for mutual TLS.
272+
273+
The client cert source is determined in the following order:
274+
(1) if `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable is not "true", the
275+
client cert source is None.
276+
(2) if `client_options.client_cert_source` is provided, use the provided one; if the
277+
default client cert source exists, use the default one; otherwise the client cert
278+
source is None.
279+
280+
The API endpoint is determined in the following order:
281+
(1) if `client_options.api_endpoint` if provided, use the provided one.
282+
(2) if `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable is "always", use the
283+
default mTLS endpoint; if the environment variabel is "never", use the default API
284+
endpoint; otherwise if client cert source exists, use the default mTLS endpoint, otherwise
285+
use the default API endpoint.
286+
287+
More details can be found at https://google.aip.dev/auth/4114.
288+
289+
Args:
290+
client_options (google.api_core.client_options.ClientOptions): Custom options for the
291+
client. Only the `api_endpoint` and `client_cert_source` properties may be used
292+
in this method.
293+
294+
Returns:
295+
Tuple[str, Callable[[], Tuple[bytes, bytes]]]: returns the API endpoint and the
296+
client cert source to use.
297+
298+
Raises:
299+
google.auth.exceptions.MutualTLSChannelError: If any errors happen.
300+
"""
301+
if client_options is None:
302+
client_options = client_options_lib.ClientOptions()
303+
use_client_cert = os.getenv("GOOGLE_API_USE_CLIENT_CERTIFICATE", "false")
304+
use_mtls_endpoint = os.getenv("GOOGLE_API_USE_MTLS_ENDPOINT", "auto")
305+
if use_client_cert not in ("true", "false"):
306+
raise ValueError(
307+
"Environment variable `GOOGLE_API_USE_CLIENT_CERTIFICATE` must be either `true` or `false`"
308+
)
309+
if use_mtls_endpoint not in ("auto", "never", "always"):
310+
raise MutualTLSChannelError(
311+
"Environment variable `GOOGLE_API_USE_MTLS_ENDPOINT` must be `never`, `auto` or `always`"
312+
)
313+
314+
# Figure out the client cert source to use.
315+
client_cert_source = None
316+
if use_client_cert == "true":
317+
if client_options.client_cert_source:
318+
client_cert_source = client_options.client_cert_source
319+
elif mtls.has_default_client_cert_source():
320+
client_cert_source = mtls.default_client_cert_source()
321+
322+
# Figure out which api endpoint to use.
323+
if client_options.api_endpoint is not None:
324+
api_endpoint = client_options.api_endpoint
325+
elif use_mtls_endpoint == "always" or (
326+
use_mtls_endpoint == "auto" and client_cert_source
327+
):
328+
api_endpoint = cls.DEFAULT_MTLS_ENDPOINT
329+
else:
330+
api_endpoint = cls.DEFAULT_ENDPOINT
331+
332+
return api_endpoint, client_cert_source
333+
267334
def __init__(
268335
self,
269336
*,
@@ -314,57 +381,22 @@ def __init__(
314381
if client_options is None:
315382
client_options = client_options_lib.ClientOptions()
316383

317-
# Create SSL credentials for mutual TLS if needed.
318-
if os.getenv("GOOGLE_API_USE_CLIENT_CERTIFICATE", "false") not in (
319-
"true",
320-
"false",
321-
):
322-
raise ValueError(
323-
"Environment variable `GOOGLE_API_USE_CLIENT_CERTIFICATE` must be either `true` or `false`"
324-
)
325-
use_client_cert = (
326-
os.getenv("GOOGLE_API_USE_CLIENT_CERTIFICATE", "false") == "true"
384+
api_endpoint, client_cert_source_func = self.get_mtls_endpoint_and_cert_source(
385+
client_options
327386
)
328387

329-
client_cert_source_func = None
330-
is_mtls = False
331-
if use_client_cert:
332-
if client_options.client_cert_source:
333-
is_mtls = True
334-
client_cert_source_func = client_options.client_cert_source
335-
else:
336-
is_mtls = mtls.has_default_client_cert_source()
337-
if is_mtls:
338-
client_cert_source_func = mtls.default_client_cert_source()
339-
else:
340-
client_cert_source_func = None
341-
342-
# Figure out which api endpoint to use.
343-
if client_options.api_endpoint is not None:
344-
api_endpoint = client_options.api_endpoint
345-
else:
346-
use_mtls_env = os.getenv("GOOGLE_API_USE_MTLS_ENDPOINT", "auto")
347-
if use_mtls_env == "never":
348-
api_endpoint = self.DEFAULT_ENDPOINT
349-
elif use_mtls_env == "always":
350-
api_endpoint = self.DEFAULT_MTLS_ENDPOINT
351-
elif use_mtls_env == "auto":
352-
if is_mtls:
353-
api_endpoint = self.DEFAULT_MTLS_ENDPOINT
354-
else:
355-
api_endpoint = self.DEFAULT_ENDPOINT
356-
else:
357-
raise MutualTLSChannelError(
358-
"Unsupported GOOGLE_API_USE_MTLS_ENDPOINT value. Accepted "
359-
"values: never, auto, always"
360-
)
388+
api_key_value = getattr(client_options, "api_key", None)
389+
if api_key_value and credentials:
390+
raise ValueError(
391+
"client_options.api_key and credentials are mutually exclusive"
392+
)
361393

362394
# Save or instantiate the transport.
363395
# Ordinarily, we provide the transport, but allowing a custom transport
364396
# instance provides an extensibility point for unusual situations.
365397
if isinstance(transport, TablesServiceTransport):
366398
# transport is a TablesServiceTransport instance.
367-
if credentials or client_options.credentials_file:
399+
if credentials or client_options.credentials_file or api_key_value:
368400
raise ValueError(
369401
"When providing a transport instance, "
370402
"provide its credentials directly."
@@ -376,6 +408,15 @@ def __init__(
376408
)
377409
self._transport = transport
378410
else:
411+
import google.auth._default # type: ignore
412+
413+
if api_key_value and hasattr(
414+
google.auth._default, "get_api_key_credentials"
415+
):
416+
credentials = google.auth._default.get_api_key_credentials(
417+
api_key_value
418+
)
419+
379420
Transport = type(self).get_transport_class(transport)
380421
self._transport = Transport(
381422
credentials=credentials,

packages/google-area120-tables/tests/unit/gapic/tables_v1alpha1/test_tables_service.py

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -399,6 +399,87 @@ def test_tables_service_client_mtls_env_auto(
399399
)
400400

401401

402+
@pytest.mark.parametrize(
403+
"client_class", [TablesServiceClient, TablesServiceAsyncClient]
404+
)
405+
@mock.patch.object(
406+
TablesServiceClient,
407+
"DEFAULT_ENDPOINT",
408+
modify_default_endpoint(TablesServiceClient),
409+
)
410+
@mock.patch.object(
411+
TablesServiceAsyncClient,
412+
"DEFAULT_ENDPOINT",
413+
modify_default_endpoint(TablesServiceAsyncClient),
414+
)
415+
def test_tables_service_client_get_mtls_endpoint_and_cert_source(client_class):
416+
mock_client_cert_source = mock.Mock()
417+
418+
# Test the case GOOGLE_API_USE_CLIENT_CERTIFICATE is "true".
419+
with mock.patch.dict(os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "true"}):
420+
mock_api_endpoint = "foo"
421+
options = client_options.ClientOptions(
422+
client_cert_source=mock_client_cert_source, api_endpoint=mock_api_endpoint
423+
)
424+
api_endpoint, cert_source = client_class.get_mtls_endpoint_and_cert_source(
425+
options
426+
)
427+
assert api_endpoint == mock_api_endpoint
428+
assert cert_source == mock_client_cert_source
429+
430+
# Test the case GOOGLE_API_USE_CLIENT_CERTIFICATE is "false".
431+
with mock.patch.dict(os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "false"}):
432+
mock_client_cert_source = mock.Mock()
433+
mock_api_endpoint = "foo"
434+
options = client_options.ClientOptions(
435+
client_cert_source=mock_client_cert_source, api_endpoint=mock_api_endpoint
436+
)
437+
api_endpoint, cert_source = client_class.get_mtls_endpoint_and_cert_source(
438+
options
439+
)
440+
assert api_endpoint == mock_api_endpoint
441+
assert cert_source is None
442+
443+
# Test the case GOOGLE_API_USE_MTLS_ENDPOINT is "never".
444+
with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "never"}):
445+
api_endpoint, cert_source = client_class.get_mtls_endpoint_and_cert_source()
446+
assert api_endpoint == client_class.DEFAULT_ENDPOINT
447+
assert cert_source is None
448+
449+
# Test the case GOOGLE_API_USE_MTLS_ENDPOINT is "always".
450+
with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "always"}):
451+
api_endpoint, cert_source = client_class.get_mtls_endpoint_and_cert_source()
452+
assert api_endpoint == client_class.DEFAULT_MTLS_ENDPOINT
453+
assert cert_source is None
454+
455+
# Test the case GOOGLE_API_USE_MTLS_ENDPOINT is "auto" and default cert doesn't exist.
456+
with mock.patch.dict(os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "true"}):
457+
with mock.patch(
458+
"google.auth.transport.mtls.has_default_client_cert_source",
459+
return_value=False,
460+
):
461+
api_endpoint, cert_source = client_class.get_mtls_endpoint_and_cert_source()
462+
assert api_endpoint == client_class.DEFAULT_ENDPOINT
463+
assert cert_source is None
464+
465+
# Test the case GOOGLE_API_USE_MTLS_ENDPOINT is "auto" and default cert exists.
466+
with mock.patch.dict(os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "true"}):
467+
with mock.patch(
468+
"google.auth.transport.mtls.has_default_client_cert_source",
469+
return_value=True,
470+
):
471+
with mock.patch(
472+
"google.auth.transport.mtls.default_client_cert_source",
473+
return_value=mock_client_cert_source,
474+
):
475+
(
476+
api_endpoint,
477+
cert_source,
478+
) = client_class.get_mtls_endpoint_and_cert_source()
479+
assert api_endpoint == client_class.DEFAULT_MTLS_ENDPOINT
480+
assert cert_source == mock_client_cert_source
481+
482+
402483
@pytest.mark.parametrize(
403484
"client_class,transport_class,transport_name",
404485
[
@@ -2835,6 +2916,23 @@ def test_credentials_transport_error():
28352916
transport=transport,
28362917
)
28372918

2919+
# It is an error to provide an api_key and a transport instance.
2920+
transport = transports.TablesServiceGrpcTransport(
2921+
credentials=ga_credentials.AnonymousCredentials(),
2922+
)
2923+
options = client_options.ClientOptions()
2924+
options.api_key = "api_key"
2925+
with pytest.raises(ValueError):
2926+
client = TablesServiceClient(client_options=options, transport=transport,)
2927+
2928+
# It is an error to provide an api_key and a credential.
2929+
options = mock.Mock()
2930+
options.api_key = "api_key"
2931+
with pytest.raises(ValueError):
2932+
client = TablesServiceClient(
2933+
client_options=options, credentials=ga_credentials.AnonymousCredentials()
2934+
)
2935+
28382936
# It is an error to provide scopes and a transport instance.
28392937
transport = transports.TablesServiceGrpcTransport(
28402938
credentials=ga_credentials.AnonymousCredentials(),
@@ -3463,3 +3561,33 @@ def test_client_ctx():
34633561
with client:
34643562
pass
34653563
close.assert_called()
3564+
3565+
3566+
@pytest.mark.parametrize(
3567+
"client_class,transport_class",
3568+
[
3569+
(TablesServiceClient, transports.TablesServiceGrpcTransport),
3570+
(TablesServiceAsyncClient, transports.TablesServiceGrpcAsyncIOTransport),
3571+
],
3572+
)
3573+
def test_api_key_credentials(client_class, transport_class):
3574+
with mock.patch.object(
3575+
google.auth._default, "get_api_key_credentials", create=True
3576+
) as get_api_key_credentials:
3577+
mock_cred = mock.Mock()
3578+
get_api_key_credentials.return_value = mock_cred
3579+
options = client_options.ClientOptions()
3580+
options.api_key = "api_key"
3581+
with mock.patch.object(transport_class, "__init__") as patched:
3582+
patched.return_value = None
3583+
client = client_class(client_options=options)
3584+
patched.assert_called_once_with(
3585+
credentials=mock_cred,
3586+
credentials_file=None,
3587+
host=client.DEFAULT_ENDPOINT,
3588+
scopes=None,
3589+
client_cert_source_for_mtls=None,
3590+
quota_project_id=None,
3591+
client_info=transports.base.DEFAULT_CLIENT_INFO,
3592+
always_use_jwt_access=True,
3593+
)

0 commit comments

Comments
 (0)