From 538df80ed6d21f43b512a73853935f7a7b9bdf52 Mon Sep 17 00:00:00 2001 From: Vadym Matsishevskyi <25311427+vam-google@users.noreply.github.com> Date: Wed, 14 Sep 2022 14:47:55 +0000 Subject: [PATCH] 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 --- google/api_core/path_template.py | 35 ++++++++++++++++++++++++++------ tests/unit/test_path_template.py | 3 ++- 2 files changed, 31 insertions(+), 7 deletions(-) diff --git a/google/api_core/path_template.py b/google/api_core/path_template.py index 2639459a..b8ebb2af 100644 --- a/google/api_core/path_template.py +++ b/google/api_core/path_template.py @@ -272,15 +272,19 @@ def transcode(http_options, message=None, **request_kwargs): 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 @@ def transcode(http_options, message=None, **request_kwargs): # 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 @@ def transcode(http_options, message=None, **request_kwargs): 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 73d351c0..808b36f3 100644 --- a/tests/unit/test_path_template.py +++ b/tests/unit/test_path_template.py @@ -629,8 +629,9 @@ def test_transcode_with_additional_bindings( ) 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):