Skip to content

Commit deb19e4

Browse files
feat: add api key support (#63)
* 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 68db995 commit deb19e4

3 files changed

Lines changed: 245 additions & 44 deletions

File tree

packages/google-cloud-data-fusion/google/cloud/data_fusion_v1/services/data_fusion/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
@@ -110,6 +110,42 @@ def from_service_account_file(cls, filename: str, *args, **kwargs):
110110

111111
from_service_account_json = from_service_account_file
112112

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

packages/google-cloud-data-fusion/google/cloud/data_fusion_v1/services/data_fusion/client.py

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

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

314-
# Create SSL credentials for mutual TLS if needed.
315-
if os.getenv("GOOGLE_API_USE_CLIENT_CERTIFICATE", "false") not in (
316-
"true",
317-
"false",
318-
):
319-
raise ValueError(
320-
"Environment variable `GOOGLE_API_USE_CLIENT_CERTIFICATE` must be either `true` or `false`"
321-
)
322-
use_client_cert = (
323-
os.getenv("GOOGLE_API_USE_CLIENT_CERTIFICATE", "false") == "true"
381+
api_endpoint, client_cert_source_func = self.get_mtls_endpoint_and_cert_source(
382+
client_options
324383
)
325384

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

359391
# Save or instantiate the transport.
360392
# Ordinarily, we provide the transport, but allowing a custom transport
361393
# instance provides an extensibility point for unusual situations.
362394
if isinstance(transport, DataFusionTransport):
363395
# transport is a DataFusionTransport instance.
364-
if credentials or client_options.credentials_file:
396+
if credentials or client_options.credentials_file or api_key_value:
365397
raise ValueError(
366398
"When providing a transport instance, "
367399
"provide its credentials directly."
@@ -373,6 +405,15 @@ def __init__(
373405
)
374406
self._transport = transport
375407
else:
408+
import google.auth._default # type: ignore
409+
410+
if api_key_value and hasattr(
411+
google.auth._default, "get_api_key_credentials"
412+
):
413+
credentials = google.auth._default.get_api_key_credentials(
414+
api_key_value
415+
)
416+
376417
Transport = type(self).get_transport_class(transport)
377418
self._transport = Transport(
378419
credentials=credentials,

packages/google-cloud-data-fusion/tests/unit/gapic/data_fusion_v1/test_data_fusion.py

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -390,6 +390,83 @@ def test_data_fusion_client_mtls_env_auto(
390390
)
391391

392392

393+
@pytest.mark.parametrize("client_class", [DataFusionClient, DataFusionAsyncClient])
394+
@mock.patch.object(
395+
DataFusionClient, "DEFAULT_ENDPOINT", modify_default_endpoint(DataFusionClient)
396+
)
397+
@mock.patch.object(
398+
DataFusionAsyncClient,
399+
"DEFAULT_ENDPOINT",
400+
modify_default_endpoint(DataFusionAsyncClient),
401+
)
402+
def test_data_fusion_client_get_mtls_endpoint_and_cert_source(client_class):
403+
mock_client_cert_source = mock.Mock()
404+
405+
# Test the case GOOGLE_API_USE_CLIENT_CERTIFICATE is "true".
406+
with mock.patch.dict(os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "true"}):
407+
mock_api_endpoint = "foo"
408+
options = client_options.ClientOptions(
409+
client_cert_source=mock_client_cert_source, api_endpoint=mock_api_endpoint
410+
)
411+
api_endpoint, cert_source = client_class.get_mtls_endpoint_and_cert_source(
412+
options
413+
)
414+
assert api_endpoint == mock_api_endpoint
415+
assert cert_source == mock_client_cert_source
416+
417+
# Test the case GOOGLE_API_USE_CLIENT_CERTIFICATE is "false".
418+
with mock.patch.dict(os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "false"}):
419+
mock_client_cert_source = mock.Mock()
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 is None
429+
430+
# Test the case GOOGLE_API_USE_MTLS_ENDPOINT is "never".
431+
with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "never"}):
432+
api_endpoint, cert_source = client_class.get_mtls_endpoint_and_cert_source()
433+
assert api_endpoint == client_class.DEFAULT_ENDPOINT
434+
assert cert_source is None
435+
436+
# Test the case GOOGLE_API_USE_MTLS_ENDPOINT is "always".
437+
with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "always"}):
438+
api_endpoint, cert_source = client_class.get_mtls_endpoint_and_cert_source()
439+
assert api_endpoint == client_class.DEFAULT_MTLS_ENDPOINT
440+
assert cert_source is None
441+
442+
# Test the case GOOGLE_API_USE_MTLS_ENDPOINT is "auto" and default cert doesn't exist.
443+
with mock.patch.dict(os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "true"}):
444+
with mock.patch(
445+
"google.auth.transport.mtls.has_default_client_cert_source",
446+
return_value=False,
447+
):
448+
api_endpoint, cert_source = client_class.get_mtls_endpoint_and_cert_source()
449+
assert api_endpoint == client_class.DEFAULT_ENDPOINT
450+
assert cert_source is None
451+
452+
# Test the case GOOGLE_API_USE_MTLS_ENDPOINT is "auto" and default cert exists.
453+
with mock.patch.dict(os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "true"}):
454+
with mock.patch(
455+
"google.auth.transport.mtls.has_default_client_cert_source",
456+
return_value=True,
457+
):
458+
with mock.patch(
459+
"google.auth.transport.mtls.default_client_cert_source",
460+
return_value=mock_client_cert_source,
461+
):
462+
(
463+
api_endpoint,
464+
cert_source,
465+
) = client_class.get_mtls_endpoint_and_cert_source()
466+
assert api_endpoint == client_class.DEFAULT_MTLS_ENDPOINT
467+
assert cert_source == mock_client_cert_source
468+
469+
393470
@pytest.mark.parametrize(
394471
"client_class,transport_class,transport_name",
395472
[
@@ -2099,6 +2176,23 @@ def test_credentials_transport_error():
20992176
transport=transport,
21002177
)
21012178

2179+
# It is an error to provide an api_key and a transport instance.
2180+
transport = transports.DataFusionGrpcTransport(
2181+
credentials=ga_credentials.AnonymousCredentials(),
2182+
)
2183+
options = client_options.ClientOptions()
2184+
options.api_key = "api_key"
2185+
with pytest.raises(ValueError):
2186+
client = DataFusionClient(client_options=options, transport=transport,)
2187+
2188+
# It is an error to provide an api_key and a credential.
2189+
options = mock.Mock()
2190+
options.api_key = "api_key"
2191+
with pytest.raises(ValueError):
2192+
client = DataFusionClient(
2193+
client_options=options, credentials=ga_credentials.AnonymousCredentials()
2194+
)
2195+
21022196
# It is an error to provide scopes and a transport instance.
21032197
transport = transports.DataFusionGrpcTransport(
21042198
credentials=ga_credentials.AnonymousCredentials(),
@@ -2704,3 +2798,33 @@ def test_client_ctx():
27042798
with client:
27052799
pass
27062800
close.assert_called()
2801+
2802+
2803+
@pytest.mark.parametrize(
2804+
"client_class,transport_class",
2805+
[
2806+
(DataFusionClient, transports.DataFusionGrpcTransport),
2807+
(DataFusionAsyncClient, transports.DataFusionGrpcAsyncIOTransport),
2808+
],
2809+
)
2810+
def test_api_key_credentials(client_class, transport_class):
2811+
with mock.patch.object(
2812+
google.auth._default, "get_api_key_credentials", create=True
2813+
) as get_api_key_credentials:
2814+
mock_cred = mock.Mock()
2815+
get_api_key_credentials.return_value = mock_cred
2816+
options = client_options.ClientOptions()
2817+
options.api_key = "api_key"
2818+
with mock.patch.object(transport_class, "__init__") as patched:
2819+
patched.return_value = None
2820+
client = client_class(client_options=options)
2821+
patched.assert_called_once_with(
2822+
credentials=mock_cred,
2823+
credentials_file=None,
2824+
host=client.DEFAULT_ENDPOINT,
2825+
scopes=None,
2826+
client_cert_source_for_mtls=None,
2827+
quota_project_id=None,
2828+
client_info=transports.base.DEFAULT_CLIENT_INFO,
2829+
always_use_jwt_access=True,
2830+
)

0 commit comments

Comments
 (0)