diff --git a/src/ansys/openapi/common/_api_client.py b/src/ansys/openapi/common/_api_client.py index d7a8d49e..129ab30c 100644 --- a/src/ansys/openapi/common/_api_client.py +++ b/src/ansys/openapi/common/_api_client.py @@ -136,26 +136,15 @@ 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 + resource_path = self.__handle_path_params( + resource_path, path_params, 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), - ) # 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 @@ -200,6 +189,35 @@ 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. diff --git a/tests/test_api_client.py b/tests/test_api_client.py index 24474678..c4202f46 100644 --- a/tests/test_api_client.py +++ b/tests/test_api_client.py @@ -36,6 +36,66 @@ 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] @@ -603,7 +663,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}