From ba98aee700098a509dfe7d6d0a1bef93e216fbd0 Mon Sep 17 00:00:00 2001 From: Doug Addy Date: Sun, 20 Mar 2022 17:33:56 +0000 Subject: [PATCH 1/4] Add tests for path and query parameter handling --- tests/test_api_client.py | 52 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/tests/test_api_client.py b/tests/test_api_client.py index 24474678..f294d134 100644 --- a/tests/test_api_client.py +++ b/tests/test_api_client.py @@ -36,6 +36,58 @@ def test_repr(blank_client): assert type(blank_client).__name__ in str(blank_client) +class TestParameterHandling: + @pytest.fixture(autouse=True) + def _blank_client(self, blank_client): + self._client = blank_client + + def test_simple_path_rewrite(self): + id_ = str(uuid.uuid4()) + single_path = "/resource/{id}" + result = self._client._ApiClient__handle_path_params(single_path, {"id": id_}, None) + assert single_path.replace("{id}", id_) == result + + def test_multiple_path_rewrites(self): + id_ = str(uuid.uuid4()) + name = "TestResource" + multiple_path = "/resource/{id}/name/{name}" + result = self._client._ApiClient__handle_path_params(multiple_path, {"id": id_, "name": name}, None) + assert multiple_path.replace("{id}", id_).replace("{name}", name) == result + + def test_path_with_naughty_characters(self): + name = "\"Na,ughty!P,ath." + naughty_path = "/resource/{name}" + result = self._client._ApiClient__handle_path_params(naughty_path, {"name": name}, None) + assert "/resource/%22Na%2Cughty%21P%2Cath." == result + + def test_path_with_naughty_characters_allowed(self): + name = "" + naughty_path = "/resource/{name}" + self._client.configuration.safe_chars_for_path_param = "<>" + result = self._client._ApiClient__handle_path_params(naughty_path, {"name": name}, None) + assert "/resource/" == result + + def test_single_query(self): + query = {"name": "Spamalot"} + result = self._client._ApiClient__handle_query_params(query, None) + assert "name=Spamalot" == result + + def test_multiple_queries(self): + query = {"bird": "swallow", "type": "african"} + result = self._client._ApiClient__handle_query_params(query, None) + assert "bird=swallow&type=african" == result + + def test_simple_query_with_collection(self): + query = {"bird": "swallow", "type": ("african", "european")} + result = self._client._ApiClient__handle_query_params(query, {"type": "pipes"}) + assert "bird=swallow&type=african|european" == result + + def test_query_with_naughty_characters(self): + query = {"search": "\"A &Naughty#Qu,ery@100%"} + result = self._client._ApiClient__handle_query_params(query, None) + assert "search=\"A &Naughty#Qu,ery@100%" == result + + class TestSerialization: _test_value_list = ["foo", int(2), 2.0, True] _test_value_types = [str, int, float, bool] From b630ef917b33ac86eaad38a0bade5f96947de7a1 Mon Sep 17 00:00:00 2001 From: Doug Addy Date: Sun, 20 Mar 2022 17:34:16 +0000 Subject: [PATCH 2/4] Refactor to improve testability and fix path parameter bug --- src/ansys/openapi/common/_api_client.py | 39 ++++++++++++++----------- 1 file changed, 22 insertions(+), 17 deletions(-) diff --git a/src/ansys/openapi/common/_api_client.py b/src/ansys/openapi/common/_api_client.py index d7a8d49e..72fdf908 100644 --- a/src/ansys/openapi/common/_api_client.py +++ b/src/ansys/openapi/common/_api_client.py @@ -136,27 +136,12 @@ def __call_api( # path parameters if path_params: - path_params_sanitized = self.sanitize_for_serialization(path_params) - path_params_tuples = self.parameters_to_tuples( - path_params_sanitized, collection_formats - ) - for k, v in path_params_tuples: - # specified safe chars, encode everything - resource_path = resource_path.replace( - f"{k}", - quote(str(v), safe=self.configuration.safe_chars_for_path_param), - ) + resource_path = self.__handle_path_params(resource_path, path_params, collection_formats) # query parameters query_params_str = "" if query_params: - query_params_sanitized = self.sanitize_for_serialization(query_params) - query_params_tuples = self.parameters_to_tuples( - query_params_sanitized, collection_formats - ) - query_params_str = "&".join( - ["=".join(param) for param in query_params_tuples] - ) + query_params_str = self.__handle_query_params(query_params, collection_formats) # post parameters if post_params or files: @@ -200,6 +185,26 @@ def __call_api( else: return return_data, response_data.status_code, response_data.headers + def __handle_path_params(self, resource_path: str, path_params: Union[Dict[str, Union[str, int]], List[Tuple], None], collection_formats: Optional[Dict[str, str]]) -> str: + path_params_sanitized = self.sanitize_for_serialization(path_params) + path_params_tuples = self.parameters_to_tuples( + path_params_sanitized, collection_formats + ) + for k, v in path_params_tuples: + # specified safe chars, encode everything + resource_path = resource_path.replace( + f"{{{k}}}", + quote(str(v), safe=self.configuration.safe_chars_for_path_param), + ) + return resource_path + + def __handle_query_params(self, query_params: Union[Dict[str, Union[str, int]], List[Tuple], None], collection_formats: Optional[Dict[str, str]]) -> str: + query_params_sanitized = self.sanitize_for_serialization(query_params) + query_params_tuples = self.parameters_to_tuples( + query_params_sanitized, collection_formats + ) + return "&".join(["=".join(param) for param in query_params_tuples]) + def sanitize_for_serialization(self, obj: Any) -> Any: """Build a JSON POST object. From 95b284f8330a77d143134410db6d0c4a1e4f4ddd Mon Sep 17 00:00:00 2001 From: Doug Addy Date: Sun, 20 Mar 2022 17:57:20 +0000 Subject: [PATCH 3/4] Fixup tests --- tests/test_api_client.py | 2 +- tests/test_integration_anonymous.py | 2 +- tests/test_integration_basic.py | 2 +- tests/test_integration_negotiate.py | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/test_api_client.py b/tests/test_api_client.py index f294d134..942e3816 100644 --- a/tests/test_api_client.py +++ b/tests/test_api_client.py @@ -655,7 +655,7 @@ def content_json_matcher(request: requests.Request): json_obj = json.loads(json_body) return json_obj == expected_request - resource_path = "/models/ID" + resource_path = "/models/{ID}" method = "PATCH" record_id = str(uuid.uuid4()) path_params = {"ID": record_id} diff --git a/tests/test_integration_anonymous.py b/tests/test_integration_anonymous.py index b9a45625..fdc3d3f5 100644 --- a/tests/test_integration_anonymous.py +++ b/tests/test_integration_anonymous.py @@ -57,7 +57,7 @@ def test_patch_model(self): bool_property=False, ) - resource_path = "/models/ID" + resource_path = "/models/{ID}" method = "PATCH" path_params = {"ID": TEST_MODEL_ID} diff --git a/tests/test_integration_basic.py b/tests/test_integration_basic.py index 1c98a0e2..c0aaff77 100644 --- a/tests/test_integration_basic.py +++ b/tests/test_integration_basic.py @@ -92,7 +92,7 @@ def test_patch_model(self): bool_property=False, ) - resource_path = "/models/ID" + resource_path = "/models/{ID}" method = "PATCH" path_params = {"ID": TEST_MODEL_ID} diff --git a/tests/test_integration_negotiate.py b/tests/test_integration_negotiate.py index 4e3a1b9b..a6218bbf 100644 --- a/tests/test_integration_negotiate.py +++ b/tests/test_integration_negotiate.py @@ -89,7 +89,7 @@ def test_patch_model(self): bool_property=False, ) - resource_path = "/models/ID" + resource_path = "/models/{ID}" method = "PATCH" path_params = {"ID": TEST_MODEL_ID} From 19abccb013d4820d45a3bbb483bd799aee725cea Mon Sep 17 00:00:00 2001 From: Doug Addy Date: Mon, 21 Mar 2022 09:05:23 +0000 Subject: [PATCH 4/4] Run black and mypy --- src/ansys/openapi/common/_api_client.py | 21 +++++++++++++++++---- tests/test_api_client.py | 22 +++++++++++++++------- 2 files changed, 32 insertions(+), 11 deletions(-) diff --git a/src/ansys/openapi/common/_api_client.py b/src/ansys/openapi/common/_api_client.py index 72fdf908..129ab30c 100644 --- a/src/ansys/openapi/common/_api_client.py +++ b/src/ansys/openapi/common/_api_client.py @@ -136,12 +136,16 @@ def __call_api( # path parameters if path_params: - resource_path = self.__handle_path_params(resource_path, path_params, collection_formats) + resource_path = self.__handle_path_params( + resource_path, path_params, collection_formats + ) # query parameters query_params_str = "" if query_params: - query_params_str = self.__handle_query_params(query_params, collection_formats) + query_params_str = self.__handle_query_params( + query_params, collection_formats + ) # post parameters if post_params or files: @@ -185,7 +189,12 @@ def __call_api( else: return return_data, response_data.status_code, response_data.headers - def __handle_path_params(self, resource_path: str, path_params: Union[Dict[str, Union[str, int]], List[Tuple], None], collection_formats: Optional[Dict[str, str]]) -> str: + def __handle_path_params( + self, + resource_path: str, + path_params: Union[Dict[str, Union[str, int]], List[Tuple], None], + collection_formats: Optional[Dict[str, str]], + ) -> str: path_params_sanitized = self.sanitize_for_serialization(path_params) path_params_tuples = self.parameters_to_tuples( path_params_sanitized, collection_formats @@ -198,7 +207,11 @@ def __handle_path_params(self, resource_path: str, path_params: Union[Dict[str, ) return resource_path - def __handle_query_params(self, query_params: Union[Dict[str, Union[str, int]], List[Tuple], None], collection_formats: Optional[Dict[str, str]]) -> str: + def __handle_query_params( + self, + query_params: Union[Dict[str, Union[str, int]], List[Tuple], None], + collection_formats: Optional[Dict[str, str]], + ) -> str: query_params_sanitized = self.sanitize_for_serialization(query_params) query_params_tuples = self.parameters_to_tuples( query_params_sanitized, collection_formats diff --git a/tests/test_api_client.py b/tests/test_api_client.py index 942e3816..c4202f46 100644 --- a/tests/test_api_client.py +++ b/tests/test_api_client.py @@ -44,27 +44,35 @@ def _blank_client(self, blank_client): def test_simple_path_rewrite(self): id_ = str(uuid.uuid4()) single_path = "/resource/{id}" - result = self._client._ApiClient__handle_path_params(single_path, {"id": id_}, None) + result = self._client._ApiClient__handle_path_params( + single_path, {"id": id_}, None + ) assert single_path.replace("{id}", id_) == result def test_multiple_path_rewrites(self): id_ = str(uuid.uuid4()) name = "TestResource" multiple_path = "/resource/{id}/name/{name}" - result = self._client._ApiClient__handle_path_params(multiple_path, {"id": id_, "name": name}, None) + result = self._client._ApiClient__handle_path_params( + multiple_path, {"id": id_, "name": name}, None + ) assert multiple_path.replace("{id}", id_).replace("{name}", name) == result def test_path_with_naughty_characters(self): - name = "\"Na,ughty!P,ath." + name = '"Na,ughty!P,ath.' naughty_path = "/resource/{name}" - result = self._client._ApiClient__handle_path_params(naughty_path, {"name": name}, None) + result = self._client._ApiClient__handle_path_params( + naughty_path, {"name": name}, None + ) assert "/resource/%22Na%2Cughty%21P%2Cath." == result def test_path_with_naughty_characters_allowed(self): name = "" naughty_path = "/resource/{name}" self._client.configuration.safe_chars_for_path_param = "<>" - result = self._client._ApiClient__handle_path_params(naughty_path, {"name": name}, None) + result = self._client._ApiClient__handle_path_params( + naughty_path, {"name": name}, None + ) assert "/resource/" == result def test_single_query(self): @@ -83,9 +91,9 @@ def test_simple_query_with_collection(self): assert "bird=swallow&type=african|european" == result def test_query_with_naughty_characters(self): - query = {"search": "\"A &Naughty#Qu,ery@100%"} + query = {"search": '"A &Naughty#Qu,ery@100%'} result = self._client._ApiClient__handle_query_params(query, None) - assert "search=\"A &Naughty#Qu,ery@100%" == result + assert 'search="A &Naughty#Qu,ery@100%' == result class TestSerialization: