Skip to content
This repository was archived by the owner on Mar 26, 2026. It is now read-only.

Commit 9ae3e66

Browse files
feat: add api key support
1 parent 9e46031 commit 9ae3e66

2 files changed

Lines changed: 61 additions & 1 deletion

File tree

gapic/templates/%namespace/%name_%version/%sub/services/%service/client.py.j2

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -309,12 +309,16 @@ class {{ service.client_name }}(metaclass={{ service.client_name }}Meta):
309309

310310
api_endpoint, client_cert_source_func = self.get_mtls_endpoint_and_cert_source(client_options)
311311

312+
api_key_value = getattr(client_options, "api_key", None)
313+
if api_key_value and credentials:
314+
raise ValueError("client_options.api_key and credentials are mutually exclusive")
315+
312316
# Save or instantiate the transport.
313317
# Ordinarily, we provide the transport, but allowing a custom transport
314318
# instance provides an extensibility point for unusual situations.
315319
if isinstance(transport, {{ service.name }}Transport):
316320
# transport is a {{ service.name }}Transport instance.
317-
if credentials or client_options.credentials_file:
321+
if credentials or client_options.credentials_file or api_key_value:
318322
raise ValueError("When providing a transport instance, "
319323
"provide its credentials directly.")
320324
if client_options.scopes:
@@ -324,6 +328,11 @@ class {{ service.client_name }}(metaclass={{ service.client_name }}Meta):
324328
)
325329
self._transport = transport
326330
else:
331+
import google.auth._default # type: ignore
332+
333+
if api_key_value and hasattr(google.auth._default, "get_api_key_credentials"):
334+
credentials = google.auth._default.get_api_key_credentials(api_key_value)
335+
327336
Transport = type(self).get_transport_class(transport)
328337
self._transport = Transport(
329338
credentials=credentials,

gapic/templates/tests/unit/gapic/%name_%version/%sub/test_%service.py.j2

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1829,6 +1829,27 @@ def test_credentials_transport_error():
18291829
client_options={"credentials_file": "credentials.json"},
18301830
transport=transport,
18311831
)
1832+
1833+
# It is an error to provide an api_key and a transport instance.
1834+
transport = transports.{{ service.name }}{{ opts.transport[0].capitalize() }}Transport(
1835+
credentials=ga_credentials.AnonymousCredentials(),
1836+
)
1837+
options = client_options.ClientOptions()
1838+
options.api_key = "api_key"
1839+
with pytest.raises(ValueError):
1840+
client = {{ service.client_name }}(
1841+
client_options=options,
1842+
transport=transport,
1843+
)
1844+
1845+
# It is an error to provide an api_key and a credential.
1846+
options = mock.Mock()
1847+
options.api_key = "api_key"
1848+
with pytest.raises(ValueError):
1849+
client = {{ service.client_name }}(
1850+
client_options=options,
1851+
credentials=ga_credentials.AnonymousCredentials()
1852+
)
18321853

18331854
# It is an error to provide scopes and a transport instance.
18341855
transport = transports.{{ service.name }}{{ opts.transport[0].capitalize() }}Transport(
@@ -2897,4 +2918,34 @@ def test_client_ctx():
28972918
pass
28982919
close.assert_called()
28992920

2921+
@pytest.mark.parametrize("client_class,transport_class", [
2922+
{% if 'grpc' in opts.transport %}
2923+
({{ service.client_name }}, transports.{{ service.grpc_transport_name }}),
2924+
({{ service.async_client_name }}, transports.{{ service.grpc_asyncio_transport_name }}),
2925+
{% elif 'rest' in opts.transport %}
2926+
({{ service.client_name }}, transports.{{ service.rest_transport_name }}),
2927+
{% endif %}
2928+
])
2929+
def test_api_key_credentials(client_class, transport_class):
2930+
with mock.patch.object(
2931+
google.auth._default, "get_api_key_credentials", create=True
2932+
) as get_api_key_credentials:
2933+
mock_cred = mock.Mock()
2934+
get_api_key_credentials.return_value = mock_cred
2935+
options = client_options.ClientOptions()
2936+
options.api_key = "api_key"
2937+
with mock.patch.object(transport_class, "__init__") as patched:
2938+
patched.return_value = None
2939+
client = client_class(client_options=options)
2940+
patched.assert_called_once_with(
2941+
credentials=mock_cred,
2942+
credentials_file=None,
2943+
host=client.DEFAULT_ENDPOINT,
2944+
scopes=None,
2945+
client_cert_source_for_mtls=None,
2946+
quota_project_id=None,
2947+
client_info=transports.base.DEFAULT_CLIENT_INFO,
2948+
always_use_jwt_access=True,
2949+
)
2950+
29002951
{% endblock %}

0 commit comments

Comments
 (0)