Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 1f56604

Browse filesBrowse files
partheavchudnov-g
andauthored
feat: add support for reading google.api.api_version (#1999)
Co-authored-by: Victor Chudnovsky <vchudnov@google.com>
1 parent 8d81ae0 commit 1f56604
Copy full SHA for 1f56604

File tree

Expand file treeCollapse file tree

33 files changed

+501
-140
lines changed
Open diff view settings
Filter options
Expand file treeCollapse file tree

33 files changed

+501
-140
lines changed
Open diff view settings
Collapse file

‎packages/gapic-generator/.github/workflows/tests.yaml‎

Copy file name to clipboardExpand all lines: packages/gapic-generator/.github/workflows/tests.yaml
+1-1Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ concurrency:
1414
cancel-in-progress: true
1515

1616
env:
17-
SHOWCASE_VERSION: 0.32.0
17+
SHOWCASE_VERSION: 0.35.0
1818
PROTOC_VERSION: 3.20.2
1919

2020
jobs:
Collapse file
+76Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
{#
2+
# Copyright (C) 2024 Google LLC
3+
#
4+
# Licensed under the Apache License, Version 2.0 (the "License");
5+
# you may not use this file except in compliance with the License.
6+
# You may obtain a copy of the License at
7+
#
8+
# http://www.apache.org/licenses/LICENSE-2.0
9+
#
10+
# Unless required by applicable law or agreed to in writing, software
11+
# distributed under the License is distributed on an "AS IS" BASIS,
12+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
# See the License for the specific language governing permissions and
14+
# limitations under the License.
15+
#
16+
# This file is a copy of `_shared_macros.j2` in standard templates located at
17+
# `gapic/templates/%namespace/%name_%version/%sub/services/%service/_shared_macros.j2`
18+
# It is intended to be a symlink.
19+
# See https://github.com/googleapis/gapic-generator-python/issues/2028
20+
# which contains follow up work to convert it to a symlink.
21+
# Do not diverge from the copy of `_shared_macros.j2` in standard templates.
22+
#}
23+
24+
{% macro auto_populate_uuid4_fields(api, method) %}
25+
{#
26+
Automatically populate UUID4 fields according to
27+
https://google.aip.dev/client-libraries/4235 when the
28+
field satisfies either of:
29+
- The field supports explicit presence and has not been set by the user.
30+
- The field doesn't support explicit presence, and its value is the empty
31+
string (i.e. the default value).
32+
When using this macro, ensure the calling template generates a line `import uuid`
33+
#}
34+
{% with method_settings = api.all_method_settings.get(method.meta.address.proto) %}
35+
{% if method_settings is not none %}
36+
{% for auto_populated_field in method_settings.auto_populated_fields %}
37+
{% if method.input.fields[auto_populated_field].proto3_optional %}
38+
if '{{ auto_populated_field }}' not in request:
39+
{% else %}
40+
if not request.{{ auto_populated_field }}:
41+
{% endif %}
42+
request.{{ auto_populated_field }} = str(uuid.uuid4())
43+
{% endfor %}
44+
{% endif %}{# if method_settings is not none #}
45+
{% endwith %}{# method_settings #}
46+
{% endmacro %}
47+
48+
{% macro add_google_api_core_version_header_import(service_version) %}
49+
{#
50+
The `version_header` module was added to `google-api-core`
51+
in version 2.19.0.
52+
https://github.com/googleapis/python-api-core/releases/tag/v2.19.0
53+
The `try/except` below can be removed once the minimum version of
54+
`google-api-core` is 2.19.0 or newer.
55+
#}
56+
{% if service_version %}
57+
try:
58+
from google.api_core import version_header
59+
HAS_GOOGLE_API_CORE_VERSION_HEADER = True # pragma: NO COVER
60+
except ImportError: # pragma: NO COVER
61+
HAS_GOOGLE_API_CORE_VERSION_HEADER = False
62+
{% endif %}{# service_version #}
63+
{% endmacro %}
64+
{% macro add_api_version_header_to_metadata(service_version) %}
65+
{#
66+
Add API Version to metadata as per https://github.com/aip-dev/google.aip.dev/pull/1331.
67+
When using this macro, ensure the calling template also calls macro
68+
`add_google_api_core_version_header_import` to add the necessary import statements.
69+
#}
70+
{% if service_version %}
71+
if HAS_GOOGLE_API_CORE_VERSION_HEADER: # pragma: NO COVER
72+
metadata = tuple(metadata) + (
73+
version_header.to_api_version_header("{{ service_version }}"),
74+
)
75+
{% endif %}{# service_version #}
76+
{% endmacro %}
Collapse file

‎packages/gapic-generator/gapic/ads-templates/%namespace/%name/%version/%sub/services/%service/client.py.j2‎

Copy file name to clipboardExpand all lines: packages/gapic-generator/gapic/ads-templates/%namespace/%name/%version/%sub/services/%service/client.py.j2
+6-22Lines changed: 6 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
{% extends '_base.py.j2' %}
22

33
{% block content %}
4+
{% import "%namespace/%name/%version/%sub/services/%service/_shared_macros.j2" as shared_macros %}
45

56
from collections import OrderedDict
67
import os
@@ -23,6 +24,7 @@ from google.auth.transport.grpc import SslCredentials # type: ignore
2324
from google.auth.exceptions import MutualTLSChannelError # type: ignore
2425
from google.oauth2 import service_account # type: ignore
2526

27+
{{ shared_macros.add_google_api_core_version_header_import(service.version) }}
2628
{% set package_path = api.naming.module_namespace|join('.') + "." + api.naming.versioned_module_name %}
2729
from {{package_path}} import gapic_version as package_version
2830

@@ -94,7 +96,8 @@ class {{ service.client_name }}Meta(type):
9496

9597

9698
class {{ service.client_name }}(metaclass={{ service.client_name }}Meta):
97-
"""{{ service.meta.doc|rst(width=72, indent=4) }}"""
99+
"""{{ service.meta.doc|rst(width=72, indent=4) }}{% if service.version|length %}
100+
This class implements API version {{ service.version }}.{% endif %}"""
98101

99102
@staticmethod
100103
def _get_default_mtls_endpoint(api_endpoint):
@@ -475,27 +478,8 @@ class {{ service.client_name }}(metaclass={{ service.client_name }}Meta):
475478
)),
476479
)
477480
{% endif %}
478-
479-
{#
480-
Automatically populate UUID4 fields according to
481-
https://google.aip.dev/client-libraries/4235 when the
482-
field satisfies either of:
483-
- The field supports explicit presence and has not been set by the user.
484-
- The field doesn't support explicit presence, and its value is the empty
485-
string (i.e. the default value).
486-
#}
487-
{% with method_settings = api.all_method_settings.get(method.meta.address.proto) %}
488-
{% if method_settings is not none %}
489-
{% for auto_populated_field in method_settings.auto_populated_fields %}
490-
{% if method.input.fields[auto_populated_field].proto3_optional %}
491-
if '{{ auto_populated_field }}' not in request:
492-
{% else %}
493-
if not request.{{ auto_populated_field }}:
494-
{% endif %}
495-
request.{{ auto_populated_field }} = str(uuid.uuid4())
496-
{% endfor %}
497-
{% endif %}{# if method_settings is not none #}
498-
{% endwith %}{# method_settings #}
481+
{{ shared_macros.add_api_version_header_to_metadata(service.version) }}
482+
{{ shared_macros.auto_populate_uuid4_fields(api, method) }}
499483

500484
# Send the request.
501485
{%+ if not method.void %}response = {% endif %}rpc(
Collapse file

‎packages/gapic-generator/gapic/ads-templates/tests/unit/gapic/%name_%version/%sub/test_%service.py.j2‎

Copy file name to clipboardExpand all lines: packages/gapic-generator/gapic/ads-templates/tests/unit/gapic/%name_%version/%sub/test_%service.py.j2
+42-3Lines changed: 42 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
{% extends "_base.py.j2" %}
22

33
{% block content %}
4+
{% import "%namespace/%name/%version/%sub/services/%service/_shared_macros.j2" as shared_macros %}
45

56
import os
67
{% if api.all_method_settings.values()|map(attribute="auto_populated_fields", default=[])|list %}
@@ -39,6 +40,7 @@ from google.oauth2 import service_account
3940
from {{ (api.naming.module_namespace + (api.naming.versioned_module_name,) + service.meta.address.subpackage)|join(".") }}.services.{{ service.name|snake_case }} import {{ service.client_name }}
4041
from {{ (api.naming.module_namespace + (api.naming.versioned_module_name,) + service.meta.address.subpackage)|join(".") }}.services.{{ service.name|snake_case }} import transports
4142

43+
from google.api_core import api_core_version
4244
from google.api_core import client_options
4345
from google.api_core import exceptions as core_exceptions
4446
from google.api_core import grpc_helpers
@@ -69,6 +71,8 @@ from google.iam.v1 import options_pb2 # type: ignore
6971
from google.iam.v1 import policy_pb2 # type: ignore
7072
{% endif %}
7173
{% endfilter %}
74+
{{ shared_macros.add_google_api_core_version_header_import(service.version) }}
75+
7276

7377
{% with uuid4_re = "[a-f0-9]{8}-?[a-f0-9]{4}-?4[a-f0-9]{3}-?[89ab][a-f0-9]{3}-?[a-f0-9]{12}" %}
7478

@@ -636,6 +640,35 @@ def test_{{ method_name }}(request_type, transport: str = 'grpc'):
636640
{% endwith %}{# auto_populated_field_sample_value #}
637641

638642

643+
{% if service.version %}
644+
@pytest.mark.parametrize("transport_name", [
645+
{% if 'grpc' in opts.transport %}
646+
("grpc"),
647+
{% endif %}
648+
{% if 'rest' in opts.transport %}
649+
("rest"),
650+
{% endif %}
651+
])
652+
def test_{{ method_name }}_api_version_header(transport_name):
653+
# TODO: Make this test unconditional once the minimum supported version of
654+
# google-api-core becomes 2.19.0 or higher.
655+
api_core_major, api_core_minor = [int(part) for part in api_core_version.__version__.split(".")[0:2]]
656+
if api_core_major > 2 or (api_core_major == 2 and api_core_minor >= 19):
657+
client = {{ service.client_name }}(credentials=ga_credentials.AnonymousCredentials(), transport=transport_name)
658+
# Mock the actual call within the gRPC stub, and fake the request.
659+
with mock.patch.object(
660+
type(client.transport.{{ method.transport_safe_name|snake_case }}),
661+
'__call__'
662+
) as call:
663+
client.{{ method_name }}()
664+
665+
# Establish that the api version header was sent.
666+
_, _, kw = call.mock_calls[0]
667+
assert kw['metadata'][0] == (version_header.API_VERSION_METADATA_KEY, "{{ service.version }}")
668+
else:
669+
pytest.skip("google-api-core>=2.19.0 is required for `google.api_core.version_header`")
670+
{% endif %}{# service.version #}
671+
639672
{% if not method.client_streaming %}
640673
def test_{{ method_name }}_empty_call():
641674
# This test is a coverage failsafe to make sure that totally empty calls,
@@ -904,9 +937,9 @@ def test_{{ method_name }}_pager(transport_name: str = "grpc"):
904937
RuntimeError,
905938
)
906939

907-
metadata = ()
940+
expected_metadata = ()
908941
{% if method.field_headers %}
909-
metadata = tuple(metadata) + (
942+
expected_metadata = tuple(expected_metadata) + (
910943
gapic_v1.routing_header.to_grpc_metadata((
911944
{% for field_header in method.field_headers %}
912945
{% if not method.client_streaming %}
@@ -918,7 +951,13 @@ def test_{{ method_name }}_pager(transport_name: str = "grpc"):
918951
{% endif %}
919952
pager = client.{{ method_name }}(request={})
920953

921-
assert pager._metadata == metadata
954+
{% if service.version %}
955+
if HAS_GOOGLE_API_CORE_VERSION_HEADER:
956+
expected_metadata = tuple(expected_metadata) + (
957+
version_header.to_api_version_header("{{ service.version }}"),
958+
)
959+
{% endif %}
960+
assert pager._metadata == expected_metadata
922961

923962
results = list(pager)
924963
assert len(results) == 6
Collapse file

‎packages/gapic-generator/gapic/schema/wrappers.py‎

Copy file name to clipboardExpand all lines: packages/gapic-generator/gapic/schema/wrappers.py
+11Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1734,6 +1734,17 @@ def host(self) -> str:
17341734
return self.options.Extensions[client_pb2.default_host]
17351735
return ''
17361736

1737+
@property
1738+
def version(self) -> str:
1739+
"""Return the API version for this service, if specified.
1740+
1741+
Returns:
1742+
str: The API version for this service.
1743+
"""
1744+
if self.options.Extensions[client_pb2.api_version]:
1745+
return self.options.Extensions[client_pb2.api_version]
1746+
return ''
1747+
17371748
@property
17381749
def shortname(self) -> str:
17391750
"""Return the API short name. DRIFT uses this to identify
Collapse file

‎packages/gapic-generator/gapic/templates/%namespace/%name_%version/%sub/services/%service/_client_macros.j2‎

Copy file name to clipboardExpand all lines: packages/gapic-generator/gapic/templates/%namespace/%name_%version/%sub/services/%service/_client_macros.j2
+4-25Lines changed: 4 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
# limitations under the License.
1515
#}
1616

17+
{% import "%namespace/%name_%version/%sub/services/%service/_shared_macros.j2" as shared_macros %}
18+
1719
{% macro client_method(method, name, snippet_index, api, service, full_extended_lro=False) %}
1820
def {{ name }}(self,
1921
{% if not method.client_streaming %}
@@ -181,7 +183,8 @@
181183
)
182184
{% endif %} {# method.explicit_routing #}
183185

184-
{{ auto_populate_uuid4_fields(api, method) }}
186+
{{ shared_macros.add_api_version_header_to_metadata(service.version) }}
187+
{{ shared_macros.auto_populate_uuid4_fields(api, method) }}
185188

186189
# Validate the universe domain.
187190
self._validate_universe_domain()
@@ -265,27 +268,3 @@
265268

266269
{% macro define_extended_operation_subclass(extended_operation) %}
267270
{% endmacro %}
268-
269-
{% macro auto_populate_uuid4_fields(api, method) %}
270-
{#
271-
Automatically populate UUID4 fields according to
272-
https://google.aip.dev/client-libraries/4235 when the
273-
field satisfies either of:
274-
- The field supports explicit presence and has not been set by the user.
275-
- The field doesn't support explicit presence, and its value is the empty
276-
string (i.e. the default value).
277-
When using this macro, ensure the calling template generates a line `import uuid`
278-
#}
279-
{% with method_settings = api.all_method_settings.get(method.meta.address.proto) %}
280-
{% if method_settings is not none %}
281-
{% for auto_populated_field in method_settings.auto_populated_fields %}
282-
{% if method.input.fields[auto_populated_field].proto3_optional %}
283-
if '{{ auto_populated_field }}' not in request:
284-
{% else %}
285-
if not request.{{ auto_populated_field }}:
286-
{% endif %}
287-
request.{{ auto_populated_field }} = str(uuid.uuid4())
288-
{% endfor %}
289-
{% endif %}{# if method_settings is not none #}
290-
{% endwith %}{# method_settings #}
291-
{% endmacro %}
Collapse file
+70Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
{#
2+
# Copyright (C) 2024 Google LLC
3+
#
4+
# Licensed under the Apache License, Version 2.0 (the "License");
5+
# you may not use this file except in compliance with the License.
6+
# You may obtain a copy of the License at
7+
#
8+
# http://www.apache.org/licenses/LICENSE-2.0
9+
#
10+
# Unless required by applicable law or agreed to in writing, software
11+
# distributed under the License is distributed on an "AS IS" BASIS,
12+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
# See the License for the specific language governing permissions and
14+
# limitations under the License.
15+
#}
16+
17+
{% macro auto_populate_uuid4_fields(api, method) %}
18+
{#
19+
Automatically populate UUID4 fields according to
20+
https://google.aip.dev/client-libraries/4235 when the
21+
field satisfies either of:
22+
- The field supports explicit presence and has not been set by the user.
23+
- The field doesn't support explicit presence, and its value is the empty
24+
string (i.e. the default value).
25+
When using this macro, ensure the calling template generates a line `import uuid`
26+
#}
27+
{% with method_settings = api.all_method_settings.get(method.meta.address.proto) %}
28+
{% if method_settings is not none %}
29+
{% for auto_populated_field in method_settings.auto_populated_fields %}
30+
{% if method.input.fields[auto_populated_field].proto3_optional %}
31+
if '{{ auto_populated_field }}' not in request:
32+
{% else %}
33+
if not request.{{ auto_populated_field }}:
34+
{% endif %}
35+
request.{{ auto_populated_field }} = str(uuid.uuid4())
36+
{% endfor %}
37+
{% endif %}{# if method_settings is not none #}
38+
{% endwith %}{# method_settings #}
39+
{% endmacro %}
40+
41+
{% macro add_google_api_core_version_header_import(service_version) %}
42+
{#
43+
The `version_header` module was added to `google-api-core`
44+
in version 2.19.0.
45+
https://github.com/googleapis/python-api-core/releases/tag/v2.19.0
46+
The `try/except` below can be removed once the minimum version of
47+
`google-api-core` is 2.19.0 or newer.
48+
#}
49+
{% if service_version %}
50+
try:
51+
from google.api_core import version_header
52+
HAS_GOOGLE_API_CORE_VERSION_HEADER = True # pragma: NO COVER
53+
except ImportError: # pragma: NO COVER
54+
HAS_GOOGLE_API_CORE_VERSION_HEADER = False
55+
{% endif %}{# service_version #}
56+
{% endmacro %}
57+
58+
{% macro add_api_version_header_to_metadata(service_version) %}
59+
{#
60+
Add API Version to metadata as per https://github.com/aip-dev/google.aip.dev/pull/1331.
61+
When using this macro, ensure the calling template also calls macro
62+
`add_google_api_core_version_header_import` to add the necessary import statements.
63+
#}
64+
{% if service_version %}
65+
if HAS_GOOGLE_API_CORE_VERSION_HEADER: # pragma: NO COVER
66+
metadata = tuple(metadata) + (
67+
version_header.to_api_version_header("{{ service_version }}"),
68+
)
69+
{% endif %}{# service_version #}
70+
{% endmacro %}

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.