-
Notifications
You must be signed in to change notification settings - Fork 69
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: add proper handling of query/path/body parameters for rest transport #702
Changes from 26 commits
c1a6f89
bd7c32d
cd3a7f1
b41db31
ce69e7d
89fa08b
86f3a9c
234cc09
35ac276
c8155a5
55a815c
ac0bdef
e79e8c7
824ad11
2d11e19
b5c6d06
221cd44
475f903
f08ca32
efa0ed6
d3e08c0
df88ef9
67c6bd1
37e3d28
c964fba
85be88d
5d1c6a3
f6c64cc
a4f34e7
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -767,6 +767,30 @@ def http_opt(self) -> Optional[Dict[str, str]]: | |
# TODO(yon-mg): enums for http verbs? | ||
return answer | ||
|
||
@property | ||
def path_params(self) -> Sequence[str]: | ||
"""Return the path parameters found in the http annotation url""" | ||
# TODO(yon-mg): fully implement grpc transcoding (currently only handles basic case) | ||
if self.http_opt is None: | ||
return [] | ||
pattern = r'\{\w+\}' | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please add a comment that this handles grpc encoding in its simples case |
||
|
||
return [x[1:-1] for x in re.findall(pattern, self.http_opt['url'])] | ||
software-dov marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
@property | ||
def query_params(self) -> Set[str]: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just curious: why can't There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I suppose they could. Just thought it's more appropriate since ordering seems important for There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As discussed. That seems reasonable, but a complication is that repeated fields map to repeated query params. In that case, a sequence may be best. (If order doesn't matter, a multiset might be enough, but we can't assume that order doesn't matter for repeated fields) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You are right set is not appropriate even if order doesn't matter but I should mention though that once query param logic is moved out, this won't matter anymore. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. True! |
||
"""Return query parameters for API call as determined by http annotation and grpc transcoding""" | ||
vchudnov-g marked this conversation as resolved.
Show resolved
Hide resolved
|
||
# TODO(yon-mg): fully implement grpc transcoding (currently only handles basic case) | ||
# TODO(yon-mg): remove this method and move logic to generated client | ||
if self.http_opt is None: | ||
return set() | ||
params = set(self.path_params) | ||
body = self.http_opt.get('body') | ||
if body: | ||
params.add(body) | ||
|
||
return set(self.input.fields) - params | ||
|
||
# TODO(yon-mg): refactor as there may be more than one method signature | ||
@utils.cached_property | ||
def flattened_fields(self) -> Mapping[str, Field]: | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -133,31 +133,56 @@ class {{ service.name }}RestTransport({{ service.name }}Transport): | |
{%- endif %} | ||
""" | ||
|
||
{%- if 'body' in method.http_opt.keys() %} | ||
# Jsonify the input | ||
data = {{ method.output.ident }}.to_json( | ||
{%- if method.http_opt['body'] == '*' %} | ||
{# TODO(yon-mg): refactor when implementing grpc transcoding | ||
- parse request pb & assign body, path params | ||
- shove leftovers into query params | ||
- make sure dotted nested fields preserved | ||
- format url and send the request | ||
#} | ||
{%- if 'body' in method.http_opt %} | ||
# Jsonify the request body | ||
{%- if method.http_opt['body'] != '*' %} | ||
body = {{ method.input.fields[method.http_opt['body']].type.ident }}.to_json( | ||
request.{{ method.http_opt['body'] }}, | ||
including_default_value_fields=False | ||
) | ||
{%- else %} | ||
body = {{ method.input.ident }}.to_json( | ||
request | ||
{%- else %} | ||
request.body | ||
{%- endif %} | ||
) | ||
{%- endif %} | ||
{%- endif %} | ||
|
||
{# TODO(yon-mg): Write helper method for handling grpc transcoding url #} | ||
# TODO(yon-mg): need to handle grpc transcoding and parse url correctly | ||
# current impl assumes simpler version of grpc transcoding | ||
# Send the request | ||
# current impl assumes basic case of grpc transcoding | ||
url = 'https://{host}{{ method.http_opt['url'] }}'.format( | ||
host=self._host, | ||
{%- for field in method.input.fields.keys() %} | ||
{%- for field in method.path_params %} | ||
{{ field }}=request.{{ field }}, | ||
{%- endfor %} | ||
) | ||
|
||
{# TODO(yon-mg): move all query param logic out of wrappers into here to handle | ||
nested fields correctly (can't just use set of top level fields | ||
#} | ||
# TODO(yon-mg): handle nested fields corerctly rather than using only top level fields | ||
# not required for GCE | ||
potentialParams = { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It would probably be clearer to s/ |
||
{%- for field in method.query_params %} | ||
'{{ field|camel_case }}': request.{{ field }}, | ||
vchudnov-g marked this conversation as resolved.
Show resolved
Hide resolved
|
||
{%- endfor %} | ||
} | ||
potentialParams = ((k, v) for k, v in potentialParams.items() if v) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It would probably be clearer to s/ There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is fine, but another approach is to
vchudnov-g marked this conversation as resolved.
Show resolved
Hide resolved
|
||
for i, (param_name, param_value) in enumerate(potentialParams): | ||
q = '?' if i == 0 else '&' | ||
url += "{q}{name}={value}".format(q=q, name=param_name, value=param_value.replace(' ', '+')) | ||
vchudnov-g marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
# Send the request | ||
{% if not method.void %}response = {% endif %}self._session.{{ method.http_opt['verb'] }}( | ||
url, | ||
{%- if 'body' in method.http_opt.keys() %} | ||
json=data, | ||
url | ||
{%- if 'body' in method.http_opt %}, | ||
json=body, | ||
{%- endif %} | ||
) | ||
{%- if not method.void %} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -45,3 +45,19 @@ def to_snake_case(s: str) -> str: | |
|
||
# Done; return the camel-cased string. | ||
return s.lower() | ||
|
||
|
||
def to_camel_case(s: str) -> str: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Are there any built-in/standard python functions which do the same? Please prefer using standard ones to custom, if there are any. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm fairly certain there is no standard library function for to camel-case. |
||
'''Convert any string to camel case. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: In this and the previous, pre-existing function, we're not touching spaces, right? Might be worth mentioning that. |
||
|
||
This is provided to templates as the ``camel_case`` filter. | ||
|
||
Args: | ||
s (str): The input string, provided in any sane case system | ||
|
||
Returns: | ||
str: The string in lower camel case. | ||
''' | ||
|
||
items = re.split(r'[_-]', to_snake_case(s)) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm surprised by the hyphen Opinion, @software-dov ? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It actually might handle it: I didn't check. As far as use, it doesn't get used but I wouldn't want a future user to attempt o try it and have it fail. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. My vote: write a test for There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I doubt it would break anything since it's additive but this is why I didn't change There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. But the generator has already produced libraries that were published, so if one of those had a hyphen somewhere, it made it through |
||
return items[0].lower() + "".join(x.capitalize() for x in items[1:]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: How about "the field paths encoded in the path parameters"?
s/"url"/"path template"/
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well it doesn't do this yet so I'll update the comment when it does.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
WDYM? You're returning the
{foo}
which is a field path, right?