diff --git a/openapi_python_client/openapi_parser/openapi.py b/openapi_python_client/openapi_parser/openapi.py index 23ffb0c5a..2352a2db6 100644 --- a/openapi_python_client/openapi_parser/openapi.py +++ b/openapi_python_client/openapi_parser/openapi.py @@ -4,7 +4,16 @@ from enum import Enum from typing import Any, Dict, Generator, Iterable, List, Optional, Set, Union -from .properties import EnumListProperty, EnumProperty, Property, ReferenceListProperty, RefProperty, property_from_dict +from .properties import ( + EnumListProperty, + EnumProperty, + Property, + ReferenceListProperty, + RefProperty, + property_from_dict, + DateTimeProperty, + DateProperty, +) from .reference import Reference from .responses import ListRefResponse, RefResponse, Response, response_from_dict @@ -113,6 +122,10 @@ def _add_parameters(self, data: Dict[str, Any]) -> None: and prop.reference ): self.relative_imports.add(import_string_from_reference(prop.reference, prefix="..models")) + if isinstance(prop, DateProperty): + self.relative_imports.add("from datetime import date") + if isinstance(prop, DateTimeProperty): + self.relative_imports.add("from datetime import datetime") if param_dict["in"] == ParameterLocation.QUERY: self.query_parameters.append(prop) elif param_dict["in"] == ParameterLocation.PATH: diff --git a/openapi_python_client/openapi_parser/properties.py b/openapi_python_client/openapi_parser/properties.py index fc092b1a6..e269b9ac3 100644 --- a/openapi_python_client/openapi_parser/properties.py +++ b/openapi_python_client/openapi_parser/properties.py @@ -68,6 +68,20 @@ class DateTimeProperty(Property): _type_string: ClassVar[str] = "datetime" constructor_template: ClassVar[str] = "datetime_property.pyi" + def transform(self) -> str: + return f"{self.python_name()}.isoformat()" + + +@dataclass +class DateProperty(Property): + """ A property of type datetime.date """ + + _type_string: ClassVar[str] = "date" + constructor_template: ClassVar[str] = "date_property.pyi" + + def transform(self) -> str: + return f"{self.python_name()}.isoformat()" + @dataclass class FloatProperty(Property): @@ -232,12 +246,14 @@ def property_from_dict(name: str, required: bool, data: Dict[str, Any]) -> Prope if "$ref" in data: return RefProperty(name=name, required=required, reference=Reference.from_ref(data["$ref"]), default=None) if data["type"] == "string": - if "format" not in data: - return StringProperty( - name=name, default=data.get("default"), required=required, pattern=data.get("pattern"), - ) - elif data["format"] == "date-time": - return DateTimeProperty(name=name, required=required, default=data.get("default")) + if "format" in data: + if data.get("format") == "date-time": + return DateTimeProperty(name=name, required=required, default=data.get("default")) + elif data.get("format") == "date": + return DateProperty(name=name, required=required, default=data.get("default")) + else: + raise ValueError(f'Unsupported string format:{data["format"]}') + return StringProperty(name=name, default=data.get("default"), required=required, pattern=data.get("pattern"),) elif data["type"] == "number": return FloatProperty(name=name, default=data.get("default"), required=required) elif data["type"] == "integer": diff --git a/openapi_python_client/templates/date_property.pyi b/openapi_python_client/templates/date_property.pyi new file mode 100644 index 000000000..ec0fd1aa8 --- /dev/null +++ b/openapi_python_client/templates/date_property.pyi @@ -0,0 +1,7 @@ +{% if property.required %} + {{ property.name }} = date.fromisoformat(d["{{ property.name }}"]) +{% else %} + {{ property.name }} = None + if ({{ property.name }}_string := d.get("{{ property.name }}")) is not None: + {{ property.name }} = date.fromisoformat(cast(str, {{ property.name }}_string)) +{% endif %} diff --git a/tests/test_openapi_parser/test_properties.py b/tests/test_openapi_parser/test_properties.py index ead21aed0..d86c72869 100644 --- a/tests/test_openapi_parser/test_properties.py +++ b/tests/test_openapi_parser/test_properties.py @@ -306,6 +306,30 @@ def test_property_from_dict_string_datetime_format(self, mocker): ) DateTimeProperty.assert_called_once_with(name=name, required=required, default=data["default"]) + def test_property_from_dict_string_date_format(self, mocker): + name = mocker.MagicMock() + required = mocker.MagicMock() + data = { + "type": "string", + "format": "date", + } + DateProperty = mocker.patch(f"{MODULE_NAME}.DateProperty") + + from openapi_python_client.openapi_parser.properties import property_from_dict + + p = property_from_dict(name=name, required=required, data=data) + DateProperty.assert_called_once_with(name=name, required=required, default=None) + assert p == DateProperty() + + # Test optional values + DateProperty.reset_mock() + data["default"] = mocker.MagicMock() + + property_from_dict( + name=name, required=required, data=data, + ) + DateProperty.assert_called_once_with(name=name, required=required, default=data["default"]) + def test_property_from_dict_string_unsupported_format(self, mocker): name = mocker.MagicMock() required = mocker.MagicMock()