Browse Source

Accept a list of strings as value for JSON Schema "type"

According to the JSON Schema specification [1], the "type" keyword may
be an array of strings (with unique items). Previously, the Schema
definition did not allow this and would thus prevent users from using
custom JSON Schema definitions in their field such as `Annotated[...,
WithJsonSchema({"type": ["string", "null"]})` as an alternative to the
`anyOf` which is usually generated (a ValidationError was raised when
generated the OpenAPI definition).

We now accept this "type" variant, as illustrated in modified
test_custom_schema_fields.py.

[1]: https://json-schema.org/draft/2020-12/json-schema-validation#name-type
pull/12528/head
Denis Laxalde 6 months ago
parent
commit
53ba155f6e
  1. 2
      fastapi/openapi/models.py
  2. 27
      tests/test_custom_schema_fields.py

2
fastapi/openapi/models.py

@ -157,7 +157,7 @@ class Schema(BaseModelWithConfig):
unevaluatedProperties: Optional["SchemaOrBool"] = None
# Ref: JSON Schema Validation 2020-12: https://json-schema.org/draft/2020-12/json-schema-validation.html#name-a-vocabulary-for-structural
# A Vocabulary for Structural Validation
type: Optional[str] = None
type: Optional[Union[str, Set[str]]] = None
enum: Optional[List[Any]] = None
const: Optional[Any] = None
multipleOf: Optional[float] = Field(default=None, gt=0)

27
tests/test_custom_schema_fields.py

@ -1,7 +1,14 @@
from typing import Optional
from dirty_equals import IsList
from fastapi import FastAPI
from fastapi._compat import PYDANTIC_V2
from fastapi.testclient import TestClient
from pydantic import BaseModel
from typing_extensions import Annotated
if PYDANTIC_V2:
from pydantic.json_schema import WithJsonSchema
app = FastAPI()
@ -10,12 +17,17 @@ class Item(BaseModel):
name: str
if PYDANTIC_V2:
description: Annotated[
Optional[str], WithJsonSchema({"type": ["string", "null"]})
] = None
model_config = {
"json_schema_extra": {
"x-something-internal": {"level": 4},
}
}
else:
description: Optional[str] = None # type: ignore[no-redef]
class Config:
schema_extra = {
@ -42,7 +54,18 @@ item_schema = {
"name": {
"title": "Name",
"type": "string",
}
},
"description": (
{
"title": "Description",
"type": IsList("string", "null", check_order=False),
}
if PYDANTIC_V2
else {
"title": "Description",
"type": "string",
}
),
},
}
@ -57,4 +80,4 @@ def test_response():
# For coverage
response = client.get("/foo")
assert response.status_code == 200, response.text
assert response.json() == {"name": "Foo item"}
assert response.json() == {"name": "Foo item", "description": None}

Loading…
Cancel
Save