Skip to content
This repository was archived by the owner on Jul 6, 2023. It is now read-only.

Commit e9014c4

Browse files
feat: add api key support (#78)
* chore: upgrade gapic-generator-java, gax-java and gapic-generator-python PiperOrigin-RevId: 423842556 Source-Link: googleapis/googleapis@a616ca0 Source-Link: https://github.com/googleapis/googleapis-gen/commit/29b938c58c1e51d019f2ee539d55dc0a3c86a905 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 4b68089 commit e9014c4

File tree

3 files changed

+260
-44
lines changed

3 files changed

+260
-44
lines changed

google/cloud/essential_contacts_v1/services/essential_contacts_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
@@ -115,6 +115,42 @@ def from_service_account_file(cls, filename: str, *args, **kwargs):
115115

116116
from_service_account_json = from_service_account_file
117117

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

google/cloud/essential_contacts_v1/services/essential_contacts_service/client.py

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

241+
@classmethod
242+
def get_mtls_endpoint_and_cert_source(
243+
cls, client_options: Optional[client_options_lib.ClientOptions] = None
244+
):
245+
"""Return the API endpoint and client cert source for mutual TLS.
246+
247+
The client cert source is determined in the following order:
248+
(1) if `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable is not "true", the
249+
client cert source is None.
250+
(2) if `client_options.client_cert_source` is provided, use the provided one; if the
251+
default client cert source exists, use the default one; otherwise the client cert
252+
source is None.
253+
254+
The API endpoint is determined in the following order:
255+
(1) if `client_options.api_endpoint` if provided, use the provided one.
256+
(2) if `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable is "always", use the
257+
default mTLS endpoint; if the environment variabel is "never", use the default API
258+
endpoint; otherwise if client cert source exists, use the default mTLS endpoint, otherwise
259+
use the default API endpoint.
260+
261+
More details can be found at https://google.aip.dev/auth/4114.
262+
263+
Args:
264+
client_options (google.api_core.client_options.ClientOptions): Custom options for the
265+
client. Only the `api_endpoint` and `client_cert_source` properties may be used
266+
in this method.
267+
268+
Returns:
269+
Tuple[str, Callable[[], Tuple[bytes, bytes]]]: returns the API endpoint and the
270+
client cert source to use.
271+
272+
Raises:
273+
google.auth.exceptions.MutualTLSChannelError: If any errors happen.
274+
"""
275+
if client_options is None:
276+
client_options = client_options_lib.ClientOptions()
277+
use_client_cert = os.getenv("GOOGLE_API_USE_CLIENT_CERTIFICATE", "false")
278+
use_mtls_endpoint = os.getenv("GOOGLE_API_USE_MTLS_ENDPOINT", "auto")
279+
if use_client_cert not in ("true", "false"):
280+
raise ValueError(
281+
"Environment variable `GOOGLE_API_USE_CLIENT_CERTIFICATE` must be either `true` or `false`"
282+
)
283+
if use_mtls_endpoint not in ("auto", "never", "always"):
284+
raise MutualTLSChannelError(
285+
"Environment variable `GOOGLE_API_USE_MTLS_ENDPOINT` must be `never`, `auto` or `always`"
286+
)
287+
288+
# Figure out the client cert source to use.
289+
client_cert_source = None
290+
if use_client_cert == "true":
291+
if client_options.client_cert_source:
292+
client_cert_source = client_options.client_cert_source
293+
elif mtls.has_default_client_cert_source():
294+
client_cert_source = mtls.default_client_cert_source()
295+
296+
# Figure out which api endpoint to use.
297+
if client_options.api_endpoint is not None:
298+
api_endpoint = client_options.api_endpoint
299+
elif use_mtls_endpoint == "always" or (
300+
use_mtls_endpoint == "auto" and client_cert_source
301+
):
302+
api_endpoint = cls.DEFAULT_MTLS_ENDPOINT
303+
else:
304+
api_endpoint = cls.DEFAULT_ENDPOINT
305+
306+
return api_endpoint, client_cert_source
307+
241308
def __init__(
242309
self,
243310
*,
@@ -288,57 +355,22 @@ def __init__(
288355
if client_options is None:
289356
client_options = client_options_lib.ClientOptions()
290357

291-
# Create SSL credentials for mutual TLS if needed.
292-
if os.getenv("GOOGLE_API_USE_CLIENT_CERTIFICATE", "false") not in (
293-
"true",
294-
"false",
295-
):
296-
raise ValueError(
297-
"Environment variable `GOOGLE_API_USE_CLIENT_CERTIFICATE` must be either `true` or `false`"
298-
)
299-
use_client_cert = (
300-
os.getenv("GOOGLE_API_USE_CLIENT_CERTIFICATE", "false") == "true"
358+
api_endpoint, client_cert_source_func = self.get_mtls_endpoint_and_cert_source(
359+
client_options
301360
)
302361

303-
client_cert_source_func = None
304-
is_mtls = False
305-
if use_client_cert:
306-
if client_options.client_cert_source:
307-
is_mtls = True
308-
client_cert_source_func = client_options.client_cert_source
309-
else:
310-
is_mtls = mtls.has_default_client_cert_source()
311-
if is_mtls:
312-
client_cert_source_func = mtls.default_client_cert_source()
313-
else:
314-
client_cert_source_func = None
315-
316-
# Figure out which api endpoint to use.
317-
if client_options.api_endpoint is not None:
318-
api_endpoint = client_options.api_endpoint
319-
else:
320-
use_mtls_env = os.getenv("GOOGLE_API_USE_MTLS_ENDPOINT", "auto")
321-
if use_mtls_env == "never":
322-
api_endpoint = self.DEFAULT_ENDPOINT
323-
elif use_mtls_env == "always":
324-
api_endpoint = self.DEFAULT_MTLS_ENDPOINT
325-
elif use_mtls_env == "auto":
326-
if is_mtls:
327-
api_endpoint = self.DEFAULT_MTLS_ENDPOINT
328-
else:
329-
api_endpoint = self.DEFAULT_ENDPOINT
330-
else:
331-
raise MutualTLSChannelError(
332-
"Unsupported GOOGLE_API_USE_MTLS_ENDPOINT value. Accepted "
333-
"values: never, auto, always"
334-
)
362+
api_key_value = getattr(client_options, "api_key", None)
363+
if api_key_value and credentials:
364+
raise ValueError(
365+
"client_options.api_key and credentials are mutually exclusive"
366+
)
335367

336368
# Save or instantiate the transport.
337369
# Ordinarily, we provide the transport, but allowing a custom transport
338370
# instance provides an extensibility point for unusual situations.
339371
if isinstance(transport, EssentialContactsServiceTransport):
340372
# transport is a EssentialContactsServiceTransport instance.
341-
if credentials or client_options.credentials_file:
373+
if credentials or client_options.credentials_file or api_key_value:
342374
raise ValueError(
343375
"When providing a transport instance, "
344376
"provide its credentials directly."
@@ -350,6 +382,15 @@ def __init__(
350382
)
351383
self._transport = transport
352384
else:
385+
import google.auth._default # type: ignore
386+
387+
if api_key_value and hasattr(
388+
google.auth._default, "get_api_key_credentials"
389+
):
390+
credentials = google.auth._default.get_api_key_credentials(
391+
api_key_value
392+
)
393+
353394
Transport = type(self).get_transport_class(transport)
354395
self._transport = Transport(
355396
credentials=credentials,

tests/unit/gapic/essential_contacts_v1/test_essential_contacts_service.py

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -427,6 +427,90 @@ def test_essential_contacts_service_client_mtls_env_auto(
427427
)
428428

429429

430+
@pytest.mark.parametrize(
431+
"client_class",
432+
[EssentialContactsServiceClient, EssentialContactsServiceAsyncClient],
433+
)
434+
@mock.patch.object(
435+
EssentialContactsServiceClient,
436+
"DEFAULT_ENDPOINT",
437+
modify_default_endpoint(EssentialContactsServiceClient),
438+
)
439+
@mock.patch.object(
440+
EssentialContactsServiceAsyncClient,
441+
"DEFAULT_ENDPOINT",
442+
modify_default_endpoint(EssentialContactsServiceAsyncClient),
443+
)
444+
def test_essential_contacts_service_client_get_mtls_endpoint_and_cert_source(
445+
client_class,
446+
):
447+
mock_client_cert_source = mock.Mock()
448+
449+
# Test the case GOOGLE_API_USE_CLIENT_CERTIFICATE is "true".
450+
with mock.patch.dict(os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "true"}):
451+
mock_api_endpoint = "foo"
452+
options = client_options.ClientOptions(
453+
client_cert_source=mock_client_cert_source, api_endpoint=mock_api_endpoint
454+
)
455+
api_endpoint, cert_source = client_class.get_mtls_endpoint_and_cert_source(
456+
options
457+
)
458+
assert api_endpoint == mock_api_endpoint
459+
assert cert_source == mock_client_cert_source
460+
461+
# Test the case GOOGLE_API_USE_CLIENT_CERTIFICATE is "false".
462+
with mock.patch.dict(os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "false"}):
463+
mock_client_cert_source = mock.Mock()
464+
mock_api_endpoint = "foo"
465+
options = client_options.ClientOptions(
466+
client_cert_source=mock_client_cert_source, api_endpoint=mock_api_endpoint
467+
)
468+
api_endpoint, cert_source = client_class.get_mtls_endpoint_and_cert_source(
469+
options
470+
)
471+
assert api_endpoint == mock_api_endpoint
472+
assert cert_source is None
473+
474+
# Test the case GOOGLE_API_USE_MTLS_ENDPOINT is "never".
475+
with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "never"}):
476+
api_endpoint, cert_source = client_class.get_mtls_endpoint_and_cert_source()
477+
assert api_endpoint == client_class.DEFAULT_ENDPOINT
478+
assert cert_source is None
479+
480+
# Test the case GOOGLE_API_USE_MTLS_ENDPOINT is "always".
481+
with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "always"}):
482+
api_endpoint, cert_source = client_class.get_mtls_endpoint_and_cert_source()
483+
assert api_endpoint == client_class.DEFAULT_MTLS_ENDPOINT
484+
assert cert_source is None
485+
486+
# Test the case GOOGLE_API_USE_MTLS_ENDPOINT is "auto" and default cert doesn't exist.
487+
with mock.patch.dict(os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "true"}):
488+
with mock.patch(
489+
"google.auth.transport.mtls.has_default_client_cert_source",
490+
return_value=False,
491+
):
492+
api_endpoint, cert_source = client_class.get_mtls_endpoint_and_cert_source()
493+
assert api_endpoint == client_class.DEFAULT_ENDPOINT
494+
assert cert_source is None
495+
496+
# Test the case GOOGLE_API_USE_MTLS_ENDPOINT is "auto" and default cert exists.
497+
with mock.patch.dict(os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "true"}):
498+
with mock.patch(
499+
"google.auth.transport.mtls.has_default_client_cert_source",
500+
return_value=True,
501+
):
502+
with mock.patch(
503+
"google.auth.transport.mtls.default_client_cert_source",
504+
return_value=mock_client_cert_source,
505+
):
506+
(
507+
api_endpoint,
508+
cert_source,
509+
) = client_class.get_mtls_endpoint_and_cert_source()
510+
assert api_endpoint == client_class.DEFAULT_MTLS_ENDPOINT
511+
assert cert_source == mock_client_cert_source
512+
513+
430514
@pytest.mark.parametrize(
431515
"client_class,transport_class,transport_name",
432516
[
@@ -2188,6 +2272,25 @@ def test_credentials_transport_error():
21882272
transport=transport,
21892273
)
21902274

2275+
# It is an error to provide an api_key and a transport instance.
2276+
transport = transports.EssentialContactsServiceGrpcTransport(
2277+
credentials=ga_credentials.AnonymousCredentials(),
2278+
)
2279+
options = client_options.ClientOptions()
2280+
options.api_key = "api_key"
2281+
with pytest.raises(ValueError):
2282+
client = EssentialContactsServiceClient(
2283+
client_options=options, transport=transport,
2284+
)
2285+
2286+
# It is an error to provide an api_key and a credential.
2287+
options = mock.Mock()
2288+
options.api_key = "api_key"
2289+
with pytest.raises(ValueError):
2290+
client = EssentialContactsServiceClient(
2291+
client_options=options, credentials=ga_credentials.AnonymousCredentials()
2292+
)
2293+
21912294
# It is an error to provide scopes and a transport instance.
21922295
transport = transports.EssentialContactsServiceGrpcTransport(
21932296
credentials=ga_credentials.AnonymousCredentials(),
@@ -2759,3 +2862,39 @@ def test_client_ctx():
27592862
with client:
27602863
pass
27612864
close.assert_called()
2865+
2866+
2867+
@pytest.mark.parametrize(
2868+
"client_class,transport_class",
2869+
[
2870+
(
2871+
EssentialContactsServiceClient,
2872+
transports.EssentialContactsServiceGrpcTransport,
2873+
),
2874+
(
2875+
EssentialContactsServiceAsyncClient,
2876+
transports.EssentialContactsServiceGrpcAsyncIOTransport,
2877+
),
2878+
],
2879+
)
2880+
def test_api_key_credentials(client_class, transport_class):
2881+
with mock.patch.object(
2882+
google.auth._default, "get_api_key_credentials", create=True
2883+
) as get_api_key_credentials:
2884+
mock_cred = mock.Mock()
2885+
get_api_key_credentials.return_value = mock_cred
2886+
options = client_options.ClientOptions()
2887+
options.api_key = "api_key"
2888+
with mock.patch.object(transport_class, "__init__") as patched:
2889+
patched.return_value = None
2890+
client = client_class(client_options=options)
2891+
patched.assert_called_once_with(
2892+
credentials=mock_cred,
2893+
credentials_file=None,
2894+
host=client.DEFAULT_ENDPOINT,
2895+
scopes=None,
2896+
client_cert_source_for_mtls=None,
2897+
quota_project_id=None,
2898+
client_info=transports.base.DEFAULT_CLIENT_INFO,
2899+
always_use_jwt_access=True,
2900+
)

0 commit comments

Comments
 (0)