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

Commit 4230050

Browse files
feat: add api key support (#68)
* 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 070992a commit 4230050

3 files changed

Lines changed: 256 additions & 44 deletions

File tree

google/cloud/lifesciences_v2beta/services/workflows_service_v2_beta/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
@@ -109,6 +109,42 @@ def from_service_account_file(cls, filename: str, *args, **kwargs):
109109

110110
from_service_account_json = from_service_account_file
111111

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

google/cloud/lifesciences_v2beta/services/workflows_service_v2_beta/client.py

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

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

276-
# Create SSL credentials for mutual TLS if needed.
277-
if os.getenv("GOOGLE_API_USE_CLIENT_CERTIFICATE", "false") not in (
278-
"true",
279-
"false",
280-
):
281-
raise ValueError(
282-
"Environment variable `GOOGLE_API_USE_CLIENT_CERTIFICATE` must be either `true` or `false`"
283-
)
284-
use_client_cert = (
285-
os.getenv("GOOGLE_API_USE_CLIENT_CERTIFICATE", "false") == "true"
343+
api_endpoint, client_cert_source_func = self.get_mtls_endpoint_and_cert_source(
344+
client_options
286345
)
287346

288-
client_cert_source_func = None
289-
is_mtls = False
290-
if use_client_cert:
291-
if client_options.client_cert_source:
292-
is_mtls = True
293-
client_cert_source_func = client_options.client_cert_source
294-
else:
295-
is_mtls = mtls.has_default_client_cert_source()
296-
if is_mtls:
297-
client_cert_source_func = mtls.default_client_cert_source()
298-
else:
299-
client_cert_source_func = None
300-
301-
# Figure out which api endpoint to use.
302-
if client_options.api_endpoint is not None:
303-
api_endpoint = client_options.api_endpoint
304-
else:
305-
use_mtls_env = os.getenv("GOOGLE_API_USE_MTLS_ENDPOINT", "auto")
306-
if use_mtls_env == "never":
307-
api_endpoint = self.DEFAULT_ENDPOINT
308-
elif use_mtls_env == "always":
309-
api_endpoint = self.DEFAULT_MTLS_ENDPOINT
310-
elif use_mtls_env == "auto":
311-
if is_mtls:
312-
api_endpoint = self.DEFAULT_MTLS_ENDPOINT
313-
else:
314-
api_endpoint = self.DEFAULT_ENDPOINT
315-
else:
316-
raise MutualTLSChannelError(
317-
"Unsupported GOOGLE_API_USE_MTLS_ENDPOINT value. Accepted "
318-
"values: never, auto, always"
319-
)
347+
api_key_value = getattr(client_options, "api_key", None)
348+
if api_key_value and credentials:
349+
raise ValueError(
350+
"client_options.api_key and credentials are mutually exclusive"
351+
)
320352

321353
# Save or instantiate the transport.
322354
# Ordinarily, we provide the transport, but allowing a custom transport
323355
# instance provides an extensibility point for unusual situations.
324356
if isinstance(transport, WorkflowsServiceV2BetaTransport):
325357
# transport is a WorkflowsServiceV2BetaTransport instance.
326-
if credentials or client_options.credentials_file:
358+
if credentials or client_options.credentials_file or api_key_value:
327359
raise ValueError(
328360
"When providing a transport instance, "
329361
"provide its credentials directly."
@@ -335,6 +367,15 @@ def __init__(
335367
)
336368
self._transport = transport
337369
else:
370+
import google.auth._default # type: ignore
371+
372+
if api_key_value and hasattr(
373+
google.auth._default, "get_api_key_credentials"
374+
):
375+
credentials = google.auth._default.get_api_key_credentials(
376+
api_key_value
377+
)
378+
338379
Transport = type(self).get_transport_class(transport)
339380
self._transport = Transport(
340381
credentials=credentials,

tests/unit/gapic/lifesciences_v2beta/test_workflows_service_v2_beta.py

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -420,6 +420,89 @@ def test_workflows_service_v2_beta_client_mtls_env_auto(
420420
)
421421

422422

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

745+
# It is an error to provide an api_key and a transport instance.
746+
transport = transports.WorkflowsServiceV2BetaGrpcTransport(
747+
credentials=ga_credentials.AnonymousCredentials(),
748+
)
749+
options = client_options.ClientOptions()
750+
options.api_key = "api_key"
751+
with pytest.raises(ValueError):
752+
client = WorkflowsServiceV2BetaClient(
753+
client_options=options, transport=transport,
754+
)
755+
756+
# It is an error to provide an api_key and a credential.
757+
options = mock.Mock()
758+
options.api_key = "api_key"
759+
with pytest.raises(ValueError):
760+
client = WorkflowsServiceV2BetaClient(
761+
client_options=options, credentials=ga_credentials.AnonymousCredentials()
762+
)
763+
662764
# It is an error to provide scopes and a transport instance.
663765
transport = transports.WorkflowsServiceV2BetaGrpcTransport(
664766
credentials=ga_credentials.AnonymousCredentials(),
@@ -1229,3 +1331,36 @@ def test_client_ctx():
12291331
with client:
12301332
pass
12311333
close.assert_called()
1334+
1335+
1336+
@pytest.mark.parametrize(
1337+
"client_class,transport_class",
1338+
[
1339+
(WorkflowsServiceV2BetaClient, transports.WorkflowsServiceV2BetaGrpcTransport),
1340+
(
1341+
WorkflowsServiceV2BetaAsyncClient,
1342+
transports.WorkflowsServiceV2BetaGrpcAsyncIOTransport,
1343+
),
1344+
],
1345+
)
1346+
def test_api_key_credentials(client_class, transport_class):
1347+
with mock.patch.object(
1348+
google.auth._default, "get_api_key_credentials", create=True
1349+
) as get_api_key_credentials:
1350+
mock_cred = mock.Mock()
1351+
get_api_key_credentials.return_value = mock_cred
1352+
options = client_options.ClientOptions()
1353+
options.api_key = "api_key"
1354+
with mock.patch.object(transport_class, "__init__") as patched:
1355+
patched.return_value = None
1356+
client = client_class(client_options=options)
1357+
patched.assert_called_once_with(
1358+
credentials=mock_cred,
1359+
credentials_file=None,
1360+
host=client.DEFAULT_ENDPOINT,
1361+
scopes=None,
1362+
client_cert_source_for_mtls=None,
1363+
quota_project_id=None,
1364+
client_info=transports.base.DEFAULT_CLIENT_INFO,
1365+
always_use_jwt_access=True,
1366+
)

0 commit comments

Comments
 (0)