Skip to content

Add support for date format in string types #30

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Apr 25, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 14 additions & 1 deletion openapi_python_client/openapi_parser/openapi.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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")
Comment on lines +125 to +128
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I actually always import datetime in templates/model.pyi right now but only importing when needed seems better. Will you remove it from that template?

if param_dict["in"] == ParameterLocation.QUERY:
self.query_parameters.append(prop)
elif param_dict["in"] == ParameterLocation.PATH:
Expand Down
28 changes: 22 additions & 6 deletions openapi_python_client/openapi_parser/properties.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down Expand Up @@ -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":
Expand Down
7 changes: 7 additions & 0 deletions openapi_python_client/templates/date_property.pyi
Original file line number Diff line number Diff line change
@@ -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 %}
24 changes: 24 additions & 0 deletions tests/test_openapi_parser/test_properties.py
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down