118 lines
3.4 KiB
Python
118 lines
3.4 KiB
Python
|
# mypy: ignore-errors
|
||
|
|
||
|
import sys
|
||
|
from typing import Any, Optional
|
||
|
|
||
|
import pytest
|
||
|
from openapi_spec_validator import validate
|
||
|
from pydantic import BaseModel
|
||
|
|
||
|
from openapi_pydantic import (
|
||
|
DataType,
|
||
|
Header,
|
||
|
Info,
|
||
|
MediaType,
|
||
|
OpenAPI,
|
||
|
Operation,
|
||
|
PathItem,
|
||
|
RequestBody,
|
||
|
Response,
|
||
|
Schema,
|
||
|
)
|
||
|
from openapi_pydantic.compat import PYDANTIC_V2
|
||
|
from openapi_pydantic.util import PydanticSchema, construct_open_api_with_schema_class
|
||
|
|
||
|
if sys.version_info < (3, 9):
|
||
|
from typing_extensions import Annotated, Literal
|
||
|
else:
|
||
|
from typing import Annotated, Literal
|
||
|
|
||
|
|
||
|
def test_basic_schema() -> None:
|
||
|
class SampleModel(BaseModel):
|
||
|
required: bool
|
||
|
optional: Optional[bool] = None
|
||
|
one_literal_choice: Literal["only_choice"]
|
||
|
multiple_literal_choices: Literal["choice1", "choice2"]
|
||
|
|
||
|
part_api = construct_sample_api(SampleModel)
|
||
|
|
||
|
api = construct_open_api_with_schema_class(part_api)
|
||
|
assert api.components is not None
|
||
|
assert api.components.schemas is not None
|
||
|
|
||
|
if PYDANTIC_V2:
|
||
|
json_api: Any = api.model_dump(mode="json", by_alias=True, exclude_none=True)
|
||
|
else:
|
||
|
json_api: Any = api.dict(by_alias=True, exclude_none=True)
|
||
|
validate(json_api)
|
||
|
|
||
|
|
||
|
@pytest.mark.skipif(
|
||
|
not PYDANTIC_V2,
|
||
|
reason="Field-level JSON examples not supported in Pydantic V1",
|
||
|
)
|
||
|
def test_field_json_schema_example() -> None:
|
||
|
from pydantic import WithJsonSchema
|
||
|
|
||
|
Thing = Annotated[str, WithJsonSchema({"examples": ["thing1"]})]
|
||
|
|
||
|
class SampleModel(BaseModel):
|
||
|
a: Thing
|
||
|
|
||
|
part_api = construct_sample_api(SampleModel)
|
||
|
|
||
|
api = construct_open_api_with_schema_class(part_api)
|
||
|
assert api.components is not None
|
||
|
assert api.components.schemas is not None
|
||
|
|
||
|
json_api: Any = api.model_dump(mode="json", by_alias=True, exclude_none=True)
|
||
|
props = json_api["components"]["schemas"]["SampleRequest"]["properties"]
|
||
|
assert props["a"]["examples"] == ["thing1"]
|
||
|
|
||
|
validate(json_api)
|
||
|
|
||
|
|
||
|
def construct_sample_api(SampleModel) -> OpenAPI:
|
||
|
class SampleRequest(SampleModel):
|
||
|
model_config = {"json_schema_mode": "validation"}
|
||
|
|
||
|
class SampleResponse(SampleModel):
|
||
|
model_config = {"json_schema_mode": "serialization"}
|
||
|
|
||
|
return OpenAPI(
|
||
|
info=Info(
|
||
|
title="Sample API",
|
||
|
version="v0.0.1",
|
||
|
),
|
||
|
paths={
|
||
|
"/callme": PathItem(
|
||
|
post=Operation(
|
||
|
requestBody=RequestBody(
|
||
|
content={
|
||
|
"application/json": MediaType(
|
||
|
schema=PydanticSchema(schema_class=SampleRequest)
|
||
|
)
|
||
|
}
|
||
|
),
|
||
|
responses={
|
||
|
"200": Response(
|
||
|
description="resp",
|
||
|
headers={
|
||
|
"WWW-Authenticate": Header(
|
||
|
description="Indicate how to authenticate",
|
||
|
schema=Schema(type=DataType.STRING),
|
||
|
)
|
||
|
},
|
||
|
content={
|
||
|
"application/json": MediaType(
|
||
|
schema=PydanticSchema(schema_class=SampleResponse)
|
||
|
)
|
||
|
},
|
||
|
)
|
||
|
},
|
||
|
)
|
||
|
)
|
||
|
},
|
||
|
)
|