Skip to content
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

Fix datetime object handling in OpenAPI schema generation #1134

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
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
38 changes: 29 additions & 9 deletions robyn/openapi.py
Original file line number Diff line number Diff line change
Expand Up @@ -389,25 +389,45 @@ def get_schema_object(self, parameter: str, param_type: Any) -> dict:
list: "array",
}

if param_type.__module__ == "datetime":
if param_type.__name__ == "datetime":
properties["type"] = "string"
properties["format"] = "date-time"
return properties
elif param_type.__name__ == "date":
properties["type"] = "string"
properties["format"] = "date"
return properties

for type_name in type_mapping:
if param_type is type_name:
properties["type"] = type_mapping[type_name]
return properties

# check for Optional type
# should check for Optional type first
if param_type.__module__ == "typing":
properties["anyOf"] = [{"type": self.get_openapi_type(param_type.__args__[0])}, {"type": "null"}]
from typing import Optional, Union
Copy link
Member

Choose a reason for hiding this comment

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

@Soroushsrd , thanks for the PR but we can move the imports to the top of the file, right?


if hasattr(param_type, "__origin__") and param_type.__origin__ in (Optional, Union):
types = [t for t in param_type.__args__ if t is not type(None)]
if len(types) == 1:
inner_schema = self.get_schema_object(parameter, types[0])
# title from inner schema is not needed in anyOf
if "title" in inner_schema:
del inner_schema["title"]
properties["anyOf"] = [inner_schema, {"type": "null"}]
return properties
properties["type"] = "object"
return properties
# check for custom classes and TypedDicts

# checking for custom classes and TypedDicts
elif inspect.isclass(param_type):
properties["type"] = "object"

properties["properties"] = {}

for e in param_type.__annotations__:
properties["properties"][e] = self.get_schema_object(e, param_type.__annotations__[e])

properties["type"] = "object"
if hasattr(param_type, "__annotations__"):
properties["properties"] = {}
for e in param_type.__annotations__:
properties["properties"][e] = self.get_schema_object(e, param_type.__annotations__[e])

return properties

Expand Down
69 changes: 69 additions & 0 deletions unit_tests/test_openapi.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import datetime
from dataclasses import dataclass
from typing import Optional


from robyn.openapi import OpenAPI, OpenAPIInfo
from robyn.types import JSONResponse


@dataclass
class DateResponseModel(JSONResponse):
date: datetime.datetime
optional_date: Optional[datetime.date] = None


def test_datetime_schema_object():
"""Test the schema generation for datetime types"""
openapi = OpenAPI(info=OpenAPIInfo())

schema = openapi.get_schema_object("test", datetime.datetime)
assert schema["type"] == "string"
assert schema["format"] == "date-time"

schema = openapi.get_schema_object("test", datetime.date)
assert schema["type"] == "string"
assert schema["format"] == "date"


def test_datetime_response_object():
"""Test the schema generation for response objects containing datetime fields"""
openapi = OpenAPI(info=OpenAPIInfo())

schema = openapi.get_schema_object("test", DateResponseModel)
assert schema["type"] == "object"
assert "properties" in schema
assert schema["properties"]["date"]["type"] == "string"
assert schema["properties"]["date"]["format"] == "date-time"
assert schema["properties"]["optional_date"]["anyOf"] == [{"type": "string", "format": "date"}, {"type": "null"}]


def test_datetime_path_object():
"""Test the OpenAPI path object generation for datetime return types"""
openapi = OpenAPI(info=OpenAPIInfo())

_, path_obj = openapi.get_path_obj(
endpoint="/test",
name="Test Endpoint",
description="Test description",
tags=["test"],
query_params=None,
request_body=None,
return_annotation=datetime.datetime,
)

assert path_obj["responses"]["200"]["content"]["application/json"]["schema"]["type"] == "string"
assert path_obj["responses"]["200"]["content"]["application/json"]["schema"]["format"] == "date-time"

_, path_obj = openapi.get_path_obj(
endpoint="/test",
name="Test Endpoint",
description="Test description",
tags=["test"],
query_params=None,
request_body=None,
return_annotation=datetime.date,
)

assert path_obj["responses"]["200"]["content"]["application/json"]["schema"]["type"] == "string"
assert path_obj["responses"]["200"]["content"]["application/json"]["schema"]["format"] == "date"
Loading