diff --git a/openapi_python_client/parser/openapi.py b/openapi_python_client/parser/openapi.py index 37d50bca5..f5be9b747 100644 --- a/openapi_python_client/parser/openapi.py +++ b/openapi_python_client/parser/openapi.py @@ -120,6 +120,21 @@ def __call__( ... # pragma: no cover +def get_status_codes_from_string(code: Union[int, str]) -> List[HTTPStatus]: + """Interpret HTTP status code patterns and return a list of HTTPStatus instances.""" + if isinstance(code, str) and "XX" in code: + start_number = int(code[0]) + + start_range = start_number * 100 + end_range = start_range + 100 + + return [ + HTTPStatus(status) for status in range(start_range, end_range) if status in HTTPStatus._value2member_map_ + ] + + return [HTTPStatus(int(code))] + + @dataclass class Endpoint: """ @@ -149,9 +164,8 @@ def _add_responses( ) -> Tuple["Endpoint", Schemas]: endpoint = deepcopy(endpoint) for code, response_data in data.items(): - status_code: HTTPStatus try: - status_code = HTTPStatus(int(code)) + parsable_codes = get_status_codes_from_string(code) except ValueError: endpoint.errors.append( ParseError( @@ -164,30 +178,31 @@ def _add_responses( ) continue - response, schemas = response_from_data( - status_code=status_code, - data=response_data, - schemas=schemas, - parent_name=endpoint.name, - config=config, - ) - if isinstance(response, ParseError): - detail_suffix = "" if response.detail is None else f" ({response.detail})" - endpoint.errors.append( - ParseError( - detail=( - f"Cannot parse response for status code {status_code}{detail_suffix}, " - f"response will be ommitted from generated client" - ), - data=response.data, - ) + for status_code in parsable_codes: + response, schemas = response_from_data( + status_code=status_code, + data=response_data, + schemas=schemas, + parent_name=endpoint.name, + config=config, ) - continue + if isinstance(response, ParseError): + detail_suffix = "" if response.detail is None else f" ({response.detail})" + endpoint.errors.append( + ParseError( + detail=( + f"Cannot parse response for status code {status_code}{detail_suffix}, " + f"response will be ommitted from generated client" + ), + data=response.data, + ) + ) + continue - # No reasons to use lazy imports in endpoints, so add lazy imports to relative here. - endpoint.relative_imports |= response.prop.get_lazy_imports(prefix=models_relative_prefix) - endpoint.relative_imports |= response.prop.get_imports(prefix=models_relative_prefix) - endpoint.responses.append(response) + # No reasons to use lazy imports in endpoints, so add lazy imports to relative here. + endpoint.relative_imports |= response.prop.get_lazy_imports(prefix=models_relative_prefix) + endpoint.relative_imports |= response.prop.get_imports(prefix=models_relative_prefix) + endpoint.responses.append(response) return endpoint, schemas @staticmethod diff --git a/tests/test_parser/test_openapi.py b/tests/test_parser/test_openapi.py index 0bb8df491..d1431a0b2 100644 --- a/tests/test_parser/test_openapi.py +++ b/tests/test_parser/test_openapi.py @@ -151,6 +151,36 @@ def test__add_responses_status_code_error(self, response_status_code, mocker): ] response_from_data.assert_not_called() + def test__add_responses__wildcard_response(self, mocker): + from openapi_python_client.parser.openapi import Endpoint, Schemas + + schemas = Schemas() + response_1_data = mocker.MagicMock() + data = { + "2XX": response_1_data, + } + endpoint = self.make_endpoint() + parse_error = ParseError(data=mocker.MagicMock()) + response_from_data = mocker.patch(f"{MODULE_NAME}.response_from_data", return_value=(parse_error, schemas)) + config = MagicMock() + + response, schemas = Endpoint._add_responses(endpoint=endpoint, data=data, schemas=schemas, config=config) + + response_from_data.assert_has_calls( + [ + mocker.call(status_code=200, data=response_1_data, schemas=schemas, parent_name="name", config=config), + mocker.call(status_code=201, data=response_1_data, schemas=schemas, parent_name="name", config=config), + mocker.call(status_code=202, data=response_1_data, schemas=schemas, parent_name="name", config=config), + mocker.call(status_code=203, data=response_1_data, schemas=schemas, parent_name="name", config=config), + mocker.call(status_code=204, data=response_1_data, schemas=schemas, parent_name="name", config=config), + mocker.call(status_code=205, data=response_1_data, schemas=schemas, parent_name="name", config=config), + mocker.call(status_code=206, data=response_1_data, schemas=schemas, parent_name="name", config=config), + mocker.call(status_code=207, data=response_1_data, schemas=schemas, parent_name="name", config=config), + mocker.call(status_code=208, data=response_1_data, schemas=schemas, parent_name="name", config=config), + mocker.call(status_code=226, data=response_1_data, schemas=schemas, parent_name="name", config=config), + ] + ) + def test__add_responses_error(self, mocker): from openapi_python_client.parser.openapi import Endpoint, Schemas