Skip to content

Commit c33085a

Browse files
Theodore HoffTJ Hoffprovinzkraut
authored
fix: sort openapi type schema (#4239)
Co-authored-by: TJ Hoff <[email protected]> Co-authored-by: Janek Nouvertné <[email protected]>
1 parent 1daf8ef commit c33085a

File tree

3 files changed

+40
-2
lines changed

3 files changed

+40
-2
lines changed
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
from typing import Literal, Union
2+
3+
from litestar import Litestar, post
4+
5+
6+
@post("/")
7+
async def query_type_test(param: Union[Literal["1"], None]) -> None:
8+
return None
9+
10+
11+
app = Litestar(route_handlers=[query_type_test])

litestar/_openapi/schema_generation/schema.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,9 @@ def _types_in_list(lst: list[Any]) -> list[OpenAPIType] | OpenAPIType:
154154
schema_types.append(schema_type)
155155
else:
156156
raise RuntimeError("Unexpected type for schema item") # pragma: no cover
157-
schema_types = list(set(schema_types))
157+
158+
# Ensure our schema types are sorted alphabetically for any downstream usage sensitive to order changes.
159+
schema_types = sorted(set(schema_types))
158160
return schema_types[0] if len(schema_types) == 1 else schema_types
159161

160162

tests/examples/test_openapi/test_openapi.py

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from docs.examples.openapi import customize_pydantic_model_name
1+
from docs.examples.openapi import customize_openapi_types, customize_pydantic_model_name
22

33
from litestar.testing import TestClient
44

@@ -44,3 +44,28 @@ def test_customize_path() -> None:
4444
with TestClient(app=app) as client:
4545
resp = client.get("/docs/openapi.json")
4646
assert resp.status_code == 200
47+
48+
49+
def test_schema_types_sorted() -> None:
50+
"""
51+
Handles an edge case where types are not `oneOf`; instead a bare list of types considered as an enum.
52+
53+
The following is what the OpenAPI 3.0 spec provides as a consistent way of describing types.
54+
These types appear to be sorted consistently for other models.
55+
56+
`{'oneOf': [{'type': 'string', 'const': '1'}, {'type': 'null'}]}`
57+
58+
The following is what a `Literal | None` type is converted to in the spec. The type field is not sorted
59+
deterministically, which can result in CI failures due to changed spec generation.
60+
61+
`{'type': ['null', 'string'], 'enum': ['1', None]}`
62+
63+
Without this change, the 'type' key above may display `['string', 'null']` depending on the system.
64+
"""
65+
with TestClient(app=customize_openapi_types.app) as client:
66+
schema = client.app.openapi_schema.to_schema()
67+
68+
nested_query_type = schema["paths"]["/"]["post"]["parameters"][0]["schema"]["type"]
69+
70+
# Types should be sorted alphabetically.
71+
assert nested_query_type == ["null", "string"]

0 commit comments

Comments
 (0)