Skip to content

Commit 1af97f1

Browse files
committed
feat(generator): add _compat.py fallback for transcoding rest_helpers
1 parent 53ded7b commit 1af97f1

4 files changed

Lines changed: 110 additions & 3 deletions

File tree

packages/gapic-generator/gapic/generator/generator.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ def get_response(self, api_schema: api.API, opts: Options) -> CodeGeneratorRespo
120120
for template_name in client_templates:
121121
# Quick check: Skip "private" templates.
122122
filename = template_name.split("/")[-1]
123-
if filename.startswith("_") and filename != "__init__.py.j2":
123+
if filename.startswith("_") and filename not in ("__init__.py.j2", "_compat.py.j2"):
124124
continue
125125

126126
# Append to the output files dictionary.
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
# {% include '_license.j2' %}
2+
3+
from typing import Any, Dict, List, Optional, Tuple
4+
5+
try:
6+
from google.api_core import rest_helpers
7+
# Trigger fallback if rest_helpers is an older version missing 'transcode'
8+
if not hasattr(rest_helpers, "transcode"):
9+
raise ImportError
10+
except ImportError:
11+
# TODO: Remove these fallbacks when google-api-core >= 2.18.0 is the minimum required version.
12+
import functools
13+
import json
14+
import operator
15+
from google.protobuf import json_format # type: ignore
16+
from google.api_core import path_template # type: ignore
17+
18+
class _FallbackRestHelpers:
19+
@staticmethod
20+
def flatten_query_params(obj, strict=False):
21+
if obj is not None and not isinstance(obj, dict):
22+
raise TypeError("flatten_query_params must be called with dict object")
23+
return _FallbackRestHelpers._flatten(obj, key_path=[], strict=strict)
24+
25+
@staticmethod
26+
def _flatten(obj, key_path, strict=False):
27+
if obj is None:
28+
return []
29+
if isinstance(obj, dict):
30+
return _FallbackRestHelpers._flatten_dict(obj, key_path=key_path, strict=strict)
31+
if isinstance(obj, list):
32+
return _FallbackRestHelpers._flatten_list(obj, key_path=key_path, strict=strict)
33+
return _FallbackRestHelpers._flatten_value(obj, key_path=key_path, strict=strict)
34+
35+
@staticmethod
36+
def _is_primitive_value(obj):
37+
if obj is None:
38+
return False
39+
if isinstance(obj, (list, dict)):
40+
raise ValueError("query params may not contain repeated dicts or lists")
41+
return True
42+
43+
@staticmethod
44+
def _flatten_value(obj, key_path, strict=False):
45+
return [(".".join(key_path), _FallbackRestHelpers._canonicalize(obj, strict=strict))]
46+
47+
@staticmethod
48+
def _flatten_dict(obj, key_path, strict=False):
49+
items = (
50+
_FallbackRestHelpers._flatten(value, key_path=key_path + [key], strict=strict)
51+
for key, value in obj.items()
52+
)
53+
return functools.reduce(operator.concat, items, [])
54+
55+
@staticmethod
56+
def _flatten_list(elems, key_path, strict=False):
57+
items = (
58+
_FallbackRestHelpers._flatten_value(elem, key_path=key_path, strict=strict)
59+
for elem in elems
60+
if _FallbackRestHelpers._is_primitive_value(elem)
61+
)
62+
return functools.reduce(operator.concat, items, [])
63+
64+
@staticmethod
65+
def _canonicalize(obj, strict=False):
66+
if strict:
67+
value = str(obj)
68+
if isinstance(obj, bool):
69+
value = value.lower()
70+
return value
71+
return obj
72+
73+
@staticmethod
74+
def transcode(
75+
http_options: List[Dict[str, str]],
76+
request: Any,
77+
required_fields_default_values: Optional[Dict[str, Any]] = None,
78+
rest_numeric_enums: bool = False,
79+
) -> Tuple[Dict[str, Any], Optional[str], Dict[str, Any]]:
80+
pb_request = getattr(request, "_pb", request)
81+
transcoded_request = path_template.transcode(http_options, pb_request)
82+
83+
body_json = None
84+
if transcoded_request.get("body") is not None:
85+
body_json = json_format.MessageToJson(
86+
transcoded_request["body"],
87+
use_integers_for_enums=rest_numeric_enums,
88+
)
89+
90+
query_params_json = {}
91+
if transcoded_request.get("query_params") is not None:
92+
query_params_json = json.loads(json_format.MessageToJson(
93+
transcoded_request["query_params"],
94+
use_integers_for_enums=rest_numeric_enums,
95+
))
96+
97+
if required_fields_default_values:
98+
for k, v in required_fields_default_values.items():
99+
if k not in query_params_json:
100+
query_params_json[k] = v
101+
102+
if rest_numeric_enums:
103+
query_params_json["$alt"] = "json;enum-encoding=int"
104+
105+
return transcoded_request, body_json, query_params_json
106+
107+
rest_helpers = _FallbackRestHelpers

packages/gapic-generator/gapic/templates/%namespace/%name_%version/%sub/services/%service/transports/rest.py.j2

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ from google.auth.transport.requests import AuthorizedSession # type: ignore
1010
from google.auth import credentials as ga_credentials # type: ignore
1111
from google.api_core import exceptions as core_exceptions
1212
from google.api_core import retry as retries
13-
from google.api_core import rest_helpers
13+
from .. import _compat as rest_helpers
1414
from google.api_core import rest_streaming
1515
from google.api_core import gapic_v1
1616
import google.protobuf

packages/gapic-generator/gapic/templates/%namespace/%name_%version/%sub/services/%service/transports/rest_asyncio.py.j2

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ from google.iam.v1 import policy_pb2 # type: ignore
3232
from google.cloud.location import locations_pb2 # type: ignore
3333
{% endif %}
3434
from google.api_core import retry_async as retries
35-
from google.api_core import rest_helpers
35+
from .. import _compat as rest_helpers
3636
from google.api_core import rest_streaming_async # type: ignore
3737
import google.protobuf
3838

0 commit comments

Comments
 (0)