Uh oh!
There was an error while loading. Please reload this page.
This repository was archived by the owner on Feb 23, 2026. It is now read-only.
- Notifications
You must be signed in to change notification settings - Fork 96
feat: add grpc transcoding + tests#259
Merged
Uh oh!
There was an error while loading. Please reload this page.
Merged
Changes from all commits
Commits
Show all changes
20 commits
Select commit
Hold shift + click to select a range
598ca88
feat: add grpc transcoding + tests
yon-mg a956abb
🦉 Updates from OwlBot
gcf-owl-bot[bot] 1ca321a
chore: tweak for clarity / idiomatic usage
tseaver f8725d9
chore: attempt to appease Sphinx
tseaver 79f561f
feat: add grpc transcoding + tests
yon-mg 6a9e8c0
Merge changes.
yihjenku 6b6dd7b
Merge changes.
yihjenku 01df2c6
Merge branch 'transcode-takeover' of https://github.com/yihjenku/pyth…
yihjenku 5a04dcd
Merge branch 'main' into transcode-takeover
yihjenku 1b827a2
Merge branch 'main' into transcode-takeover
yihjenku 51661d3
Add functions to properly handle subfields
yihjenku 2dfbc28
Merge branch 'transcode-takeover' of https://github.com/yihjenku/pyth…
yihjenku 12a4436
Add unit tests for get_field and delete_field.
yihjenku 28f587d
Add function docstrings and incorporate correct native dict functions.
yihjenku 928e3fb
Add function docstrings and incorporate correct native dict functions.
yihjenku 6d61e4c
Merge branch 'transcode-takeover' of https://github.com/yihjenku/pyth…
yihjenku 97c5697
Increase code coverage
yihjenku 1450b91
Increase code coverage
yihjenku 4c157a9
Increase code coverage
yihjenku c99e7bb
Reformat files
yihjenku File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Uh oh!
There was an error while loading. Please reload this page.
Jump to
Jump to file
Failed to load files.
Loading
Uh oh!
There was an error while loading. Please reload this page.
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -25,6 +25,8 @@ | ||
| from __future__ import unicode_literals | ||
| from collections import deque | ||
| import copy | ||
| import functools | ||
| import re | ||
| @@ -64,7 +66,7 @@ def _expand_variable_match(positional_vars, named_vars, match): | ||
| """Expand a matched variable with its value. | ||
| Args: | ||
| positional_vars (list): A list of positonal variables. This list will | ||
| positional_vars (list): A list of positional variables. This list will | ||
| be modified. | ||
| named_vars (dict): A dictionary of named variables. | ||
| match (re.Match): A regular expression match. | ||
| @@ -170,6 +172,46 @@ def _generate_pattern_for_template(tmpl): | ||
| return _VARIABLE_RE.sub(_replace_variable_with_pattern, tmpl) | ||
| def get_field(request, field): | ||
| """Get the value of a field from a given dictionary. | ||
| Args: | ||
| request (dict): A dictionary object. | ||
| field (str): The key to the request in dot notation. | ||
| Returns: | ||
| The value of the field. | ||
| """ | ||
| parts = field.split(".") | ||
| value = request | ||
| for part in parts: | ||
| if not isinstance(value, dict): | ||
| return | ||
| value = value.get(part) | ||
| if isinstance(value, dict): | ||
| return | ||
| return value | ||
| def delete_field(request, field): | ||
| """Delete the value of a field from a given dictionary. | ||
| Args: | ||
| request (dict): A dictionary object. | ||
| field (str): The key to the request in dot notation. | ||
| """ | ||
| parts = deque(field.split(".")) | ||
| while len(parts) > 1: | ||
| if not isinstance(request, dict): | ||
| return | ||
| part = parts.popleft() | ||
| request = request.get(part) | ||
| part = parts.popleft() | ||
| if not isinstance(request, dict): | ||
| return | ||
| request.pop(part, None) | ||
| def validate(tmpl, path): | ||
| """Validate a path against the path template. | ||
| @@ -193,3 +235,66 @@ def validate(tmpl, path): | ||
| """ | ||
| pattern = _generate_pattern_for_template(tmpl) + "$" | ||
| return True if re.match(pattern, path) is not None else False | ||
| def transcode(http_options, **request_kwargs): | ||
| """Transcodes a grpc request pattern into a proper HTTP request following the rules outlined here, | ||
| https://github.com/googleapis/googleapis/blob/master/google/api/http.proto#L44-L312 | ||
yihjenku marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| Args: | ||
| http_options (list(dict)): A list of dicts which consist of these keys, | ||
yihjenku marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| 'method' (str): The http method | ||
| 'uri' (str): The path template | ||
| 'body' (str): The body field name (optional) | ||
| (This is a simplified representation of the proto option `google.api.http`) | ||
tseaver marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| request_kwargs (dict) : A dict representing the request object | ||
| Returns: | ||
| dict: The transcoded request with these keys, | ||
yihjenku marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| 'method' (str) : The http method | ||
| 'uri' (str) : The expanded uri | ||
| 'body' (dict) : A dict representing the body (optional) | ||
| 'query_params' (dict) : A dict mapping query parameter variables and values | ||
| Raises: | ||
| ValueError: If the request does not match the given template. | ||
| """ | ||
| for http_option in http_options: | ||
yihjenku marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| request = {} | ||
| # Assign path | ||
| uri_template = http_option["uri"] | ||
| path_fields = [ | ||
| match.group("name") for match in _VARIABLE_RE.finditer(uri_template) | ||
| ] | ||
| path_args = {field: get_field(request_kwargs, field) for field in path_fields} | ||
| request["uri"] = expand(uri_template, **path_args) | ||
| # Remove fields used in uri path from request | ||
| leftovers = copy.deepcopy(request_kwargs) | ||
| for path_field in path_fields: | ||
| delete_field(leftovers, path_field) | ||
| if not validate(uri_template, request["uri"]) or not all(path_args.values()): | ||
| continue | ||
| # Assign body and query params | ||
| body = http_option.get("body") | ||
| if body: | ||
| if body == "*": | ||
| request["body"] = leftovers | ||
| request["query_params"] = {} | ||
| else: | ||
| try: | ||
| request["body"] = leftovers.pop(body) | ||
| except KeyError: | ||
| continue | ||
| request["query_params"] = leftovers | ||
| else: | ||
| request["query_params"] = leftovers | ||
| request["method"] = http_option["method"] | ||
| return request | ||
| raise ValueError("Request obj does not match any template") | ||
Oops, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.