fix: Improve transcoding error message (#442)
* fix: Improve transcodding error message
This fixes https://github.com/googleapis/python-api-core/issues/441 and https://github.com/googleapis/python-api-core/issues/440. Also, with this change we stop outputting the whole request message in transcodding erro message to prevent leaking any confidential information from a request message in a form of an error in a log message.
* reformat code with black
diff --git a/google/api_core/path_template.py b/google/api_core/path_template.py
index 2639459..b8ebb2a 100644
--- a/google/api_core/path_template.py
+++ b/google/api_core/path_template.py
@@ -272,15 +272,19 @@
ValueError: If the request does not match the given template.
"""
transcoded_value = message or request_kwargs
+ bindings = []
for http_option in http_options:
request = {}
# Assign path
uri_template = http_option["uri"]
- path_fields = [
- match.group("name") for match in _VARIABLE_RE.finditer(uri_template)
+ fields = [
+ (m.group("name"), m.group("template"))
+ for m in _VARIABLE_RE.finditer(uri_template)
]
- path_args = {field: get_field(transcoded_value, field) for field in path_fields}
+ bindings.append((uri_template, fields))
+
+ path_args = {field: get_field(transcoded_value, field) for field, _ in fields}
request["uri"] = expand(uri_template, **path_args)
if not validate(uri_template, request["uri"]) or not all(path_args.values()):
@@ -288,7 +292,7 @@
# Remove fields used in uri path from request
leftovers = copy.deepcopy(transcoded_value)
- for path_field in path_fields:
+ for path_field, _ in fields:
delete_field(leftovers, path_field)
# Assign body and query params
@@ -316,8 +320,27 @@
request["method"] = http_option["method"]
return request
+ bindings_description = [
+ '\n\tURI: "{}"'
+ "\n\tRequired request fields:\n\t\t{}".format(
+ uri,
+ "\n\t\t".join(
+ [
+ 'field: "{}", pattern: "{}"'.format(n, p if p else "*")
+ for n, p in fields
+ ]
+ ),
+ )
+ for uri, fields in bindings
+ ]
+
raise ValueError(
- "Request {} does not match any URL path template in available HttpRule's {}".format(
- request_kwargs, [opt["uri"] for opt in http_options]
+ "Invalid request."
+ "\nSome of the fields of the request message are either not initialized or "
+ "initialized with an invalid value."
+ "\nPlease make sure your request matches at least one accepted HTTP binding."
+ "\nTo match a binding the request message must have all the required fields "
+ "initialized with values matching their patterns as listed below:{}".format(
+ "\n".join(bindings_description)
)
)
diff --git a/tests/unit/test_path_template.py b/tests/unit/test_path_template.py
index 73d351c..808b36f 100644
--- a/tests/unit/test_path_template.py
+++ b/tests/unit/test_path_template.py
@@ -629,8 +629,9 @@
)
def test_transcode_fails(http_options, message, request_kwargs):
http_options, _ = helper_test_transcode(http_options, range(4))
- with pytest.raises(ValueError):
+ with pytest.raises(ValueError) as exc_info:
path_template.transcode(http_options, message, **request_kwargs)
+ assert str(exc_info.value).count("URI") == len(http_options)
def helper_test_transcode(http_options_list, expected_result_list):