mirror of https://github.com/tiangolo/fastapi.git
✨ Add support for disabling the separation of input and output JSON Schemas in OpenAPI with Pydantic v2 (#10145)
* 📝 Add docs for Separate OpenAPI Schemas for Input and Output * 🔧 Add new docs page to MkDocs config * ✨ Add separate_input_output_schemas parameter to FastAPI class * 📝 Add source examples for separating OpenAPI schemas * ✅ Add tests for separated OpenAPI schemas * 📝 Add source examples for Python 3.10, 3.9, and 3.7+ * 📝 Update docs for Separate OpenAPI Schemas with new multi-version examples * ✅ Add and update tests for different Python versions * ✅ Add tests for corner cases with separate_input_output_schemas * 📝 Update tutorial to use Union instead of Optional * 🐛 Fix type annotations * 🐛 Fix correct import in test * 💄 Add CSS to simulate browser windows for screenshots * ➕ Add playwright as a dev dependency to automate generating screenshots * 🔨 Add Playwright scripts to generate screenshots for new docs * 📝 Update docs, tweak text to match screenshots * 🍱 Add screenshots for new docs
This commit is contained in:
parent
10a127ea4a
commit
ea43f227e5
|
|
@ -144,3 +144,39 @@ code {
|
|||
margin-top: 2em;
|
||||
margin-bottom: 2em;
|
||||
}
|
||||
|
||||
/* Screenshots */
|
||||
/*
|
||||
Simulate a browser window frame.
|
||||
Inspired by Termynal's CSS tricks with modifications
|
||||
*/
|
||||
|
||||
.screenshot {
|
||||
display: block;
|
||||
background-color: #d3e0de;
|
||||
border-radius: 4px;
|
||||
padding: 45px 5px 5px;
|
||||
position: relative;
|
||||
-webkit-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.screenshot img {
|
||||
display: block;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.screenshot:before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 15px;
|
||||
left: 15px;
|
||||
display: inline-block;
|
||||
width: 15px;
|
||||
height: 15px;
|
||||
border-radius: 50%;
|
||||
/* A little hack to display the window buttons in one pseudo element. */
|
||||
background: #d9515d;
|
||||
-webkit-box-shadow: 25px 0 0 #f4c025, 50px 0 0 #3ec930;
|
||||
box-shadow: 25px 0 0 #f4c025, 50px 0 0 #3ec930;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,228 @@
|
|||
# Separate OpenAPI Schemas for Input and Output or Not
|
||||
|
||||
When using **Pydantic v2**, the generated OpenAPI is a bit more exact and **correct** than before. 😎
|
||||
|
||||
In fact, in some cases, it will even have **two JSON Schemas** in OpenAPI for the same Pydantic model, for input and output, depending on if they have **default values**.
|
||||
|
||||
Let's see how that works and how to change it if you need to do that.
|
||||
|
||||
## Pydantic Models for Input and Output
|
||||
|
||||
Let's say you have a Pydantic model with default values, like this one:
|
||||
|
||||
=== "Python 3.10+"
|
||||
|
||||
```Python hl_lines="7"
|
||||
{!> ../../../docs_src/separate_openapi_schemas/tutorial001_py310.py[ln:1-7]!}
|
||||
|
||||
# Code below omitted 👇
|
||||
```
|
||||
|
||||
<details>
|
||||
<summary>👀 Full file preview</summary>
|
||||
|
||||
```Python
|
||||
{!> ../../../docs_src/separate_openapi_schemas/tutorial001_py310.py!}
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
=== "Python 3.9+"
|
||||
|
||||
```Python hl_lines="9"
|
||||
{!> ../../../docs_src/separate_openapi_schemas/tutorial001_py39.py[ln:1-9]!}
|
||||
|
||||
# Code below omitted 👇
|
||||
```
|
||||
|
||||
<details>
|
||||
<summary>👀 Full file preview</summary>
|
||||
|
||||
```Python
|
||||
{!> ../../../docs_src/separate_openapi_schemas/tutorial001_py39.py!}
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
=== "Python 3.7+"
|
||||
|
||||
```Python hl_lines="9"
|
||||
{!> ../../../docs_src/separate_openapi_schemas/tutorial001.py[ln:1-9]!}
|
||||
|
||||
# Code below omitted 👇
|
||||
```
|
||||
|
||||
<details>
|
||||
<summary>👀 Full file preview</summary>
|
||||
|
||||
```Python
|
||||
{!> ../../../docs_src/separate_openapi_schemas/tutorial001.py!}
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
### Model for Input
|
||||
|
||||
If you use this model as an input like here:
|
||||
|
||||
=== "Python 3.10+"
|
||||
|
||||
```Python hl_lines="14"
|
||||
{!> ../../../docs_src/separate_openapi_schemas/tutorial001_py310.py[ln:1-15]!}
|
||||
|
||||
# Code below omitted 👇
|
||||
```
|
||||
|
||||
<details>
|
||||
<summary>👀 Full file preview</summary>
|
||||
|
||||
```Python
|
||||
{!> ../../../docs_src/separate_openapi_schemas/tutorial001_py310.py!}
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
=== "Python 3.9+"
|
||||
|
||||
```Python hl_lines="16"
|
||||
{!> ../../../docs_src/separate_openapi_schemas/tutorial001_py39.py[ln:1-17]!}
|
||||
|
||||
# Code below omitted 👇
|
||||
```
|
||||
|
||||
<details>
|
||||
<summary>👀 Full file preview</summary>
|
||||
|
||||
```Python
|
||||
{!> ../../../docs_src/separate_openapi_schemas/tutorial001_py39.py!}
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
=== "Python 3.7+"
|
||||
|
||||
```Python hl_lines="16"
|
||||
{!> ../../../docs_src/separate_openapi_schemas/tutorial001.py[ln:1-17]!}
|
||||
|
||||
# Code below omitted 👇
|
||||
```
|
||||
|
||||
<details>
|
||||
<summary>👀 Full file preview</summary>
|
||||
|
||||
```Python
|
||||
{!> ../../../docs_src/separate_openapi_schemas/tutorial001.py!}
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
...then the `description` field will **not be required**. Because it has a default value of `None`.
|
||||
|
||||
### Input Model in Docs
|
||||
|
||||
You can confirm that in the docs, the `description` field doesn't have a **red asterisk**, it's not marked as required:
|
||||
|
||||
<div class="screenshot">
|
||||
<img src="/img/tutorial/separate-openapi-schemas/image01.png">
|
||||
</div>
|
||||
|
||||
### Model for Output
|
||||
|
||||
But if you use the same model as an output, like here:
|
||||
|
||||
=== "Python 3.10+"
|
||||
|
||||
```Python hl_lines="19"
|
||||
{!> ../../../docs_src/separate_openapi_schemas/tutorial001_py310.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.9+"
|
||||
|
||||
```Python hl_lines="21"
|
||||
{!> ../../../docs_src/separate_openapi_schemas/tutorial001_py39.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.7+"
|
||||
|
||||
```Python hl_lines="21"
|
||||
{!> ../../../docs_src/separate_openapi_schemas/tutorial001.py!}
|
||||
```
|
||||
|
||||
...then because `description` has a default value, if you **don't return anything** for that field, it will still have that **default value**.
|
||||
|
||||
### Model for Output Response Data
|
||||
|
||||
If you interact with the docs and check the response, even though the code didn't add anything in one of the `description` fields, the JSON response contains the default value (`null`):
|
||||
|
||||
<div class="screenshot">
|
||||
<img src="/img/tutorial/separate-openapi-schemas/image02.png">
|
||||
</div>
|
||||
|
||||
This means that it will **always have a value**, it's just that sometimes the value could be `None` (or `null` in JSON).
|
||||
|
||||
That means that, clients using your API don't have to check if the value exists or not, they can **asume the field will always be there**, but just that in some cases it will have the default value of `None`.
|
||||
|
||||
The way to describe this in OpenAPI, is to mark that field as **required**, because it will always be there.
|
||||
|
||||
Because of that, the JSON Schema for a model can be different depending on if it's used for **input or output**:
|
||||
|
||||
* for **input** the `description` will **not be required**
|
||||
* for **output** it will be **required** (and possibly `None`, or in JSON terms, `null`)
|
||||
|
||||
### Model for Output in Docs
|
||||
|
||||
You can check the output model in the docs too, **both** `name` and `description` are marked as **required** with a **red asterisk**:
|
||||
|
||||
<div class="screenshot">
|
||||
<img src="/img/tutorial/separate-openapi-schemas/image03.png">
|
||||
</div>
|
||||
|
||||
### Model for Input and Output in Docs
|
||||
|
||||
And if you check all the available Schemas (JSON Schemas) in OpenAPI, you will see that there are two, one `Item-Input` and one `Item-Output`.
|
||||
|
||||
For `Item-Input`, `description` is **not required**, it doesn't have a red asterisk.
|
||||
|
||||
But for `Item-Output`, `description` is **required**, it has a red asterisk.
|
||||
|
||||
<div class="screenshot">
|
||||
<img src="/img/tutorial/separate-openapi-schemas/image04.png">
|
||||
</div>
|
||||
|
||||
With this feature from **Pydantic v2**, your API documentation is more **precise**, and if you have autogenerated clients and SDKs, they will be more precise too, with a better **developer experience** and consistency. 🎉
|
||||
|
||||
## Do not Separate Schemas
|
||||
|
||||
Now, there are some cases where you might want to have the **same schema for input and output**.
|
||||
|
||||
Probably the main use case for this is if you already have some autogenerated client code/SDKs and you don't want to update all the autogenerated client code/SDKs yet, you probably will want to do it at some point, but maybe not right now.
|
||||
|
||||
In that case, you can disable this feature in **FastAPI**, with the parameter `separate_input_output_schemas=False`.
|
||||
|
||||
=== "Python 3.10+"
|
||||
|
||||
```Python hl_lines="10"
|
||||
{!> ../../../docs_src/separate_openapi_schemas/tutorial002_py310.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.9+"
|
||||
|
||||
```Python hl_lines="12"
|
||||
{!> ../../../docs_src/separate_openapi_schemas/tutorial002_py39.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.7+"
|
||||
|
||||
```Python hl_lines="12"
|
||||
{!> ../../../docs_src/separate_openapi_schemas/tutorial002.py!}
|
||||
```
|
||||
|
||||
### Same Schema for Input and Output Models in Docs
|
||||
|
||||
And now there will be one single schema for input and output for the model, only `Item`, and it will have `description` as **not required**:
|
||||
|
||||
<div class="screenshot">
|
||||
<img src="/img/tutorial/separate-openapi-schemas/image05.png">
|
||||
</div>
|
||||
|
||||
This is the same behavior as in Pydantic v1. 🤓
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 80 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 90 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 83 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 57 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 44 KiB |
|
|
@ -176,6 +176,7 @@ nav:
|
|||
- how-to/custom-request-and-route.md
|
||||
- how-to/conditional-openapi.md
|
||||
- how-to/extending-openapi.md
|
||||
- how-to/separate-openapi-schemas.md
|
||||
- how-to/custom-docs-ui-assets.md
|
||||
- how-to/configure-swagger-ui.md
|
||||
- project-generation.md
|
||||
|
|
|
|||
|
|
@ -0,0 +1,28 @@
|
|||
from typing import List, Union
|
||||
|
||||
from fastapi import FastAPI
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class Item(BaseModel):
|
||||
name: str
|
||||
description: Union[str, None] = None
|
||||
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@app.post("/items/")
|
||||
def create_item(item: Item):
|
||||
return item
|
||||
|
||||
|
||||
@app.get("/items/")
|
||||
def read_items() -> List[Item]:
|
||||
return [
|
||||
Item(
|
||||
name="Portal Gun",
|
||||
description="Device to travel through the multi-rick-verse",
|
||||
),
|
||||
Item(name="Plumbus"),
|
||||
]
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
from fastapi import FastAPI
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class Item(BaseModel):
|
||||
name: str
|
||||
description: str | None = None
|
||||
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@app.post("/items/")
|
||||
def create_item(item: Item):
|
||||
return item
|
||||
|
||||
|
||||
@app.get("/items/")
|
||||
def read_items() -> list[Item]:
|
||||
return [
|
||||
Item(
|
||||
name="Portal Gun",
|
||||
description="Device to travel through the multi-rick-verse",
|
||||
),
|
||||
Item(name="Plumbus"),
|
||||
]
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
from typing import Optional
|
||||
|
||||
from fastapi import FastAPI
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class Item(BaseModel):
|
||||
name: str
|
||||
description: Optional[str] = None
|
||||
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@app.post("/items/")
|
||||
def create_item(item: Item):
|
||||
return item
|
||||
|
||||
|
||||
@app.get("/items/")
|
||||
def read_items() -> list[Item]:
|
||||
return [
|
||||
Item(
|
||||
name="Portal Gun",
|
||||
description="Device to travel through the multi-rick-verse",
|
||||
),
|
||||
Item(name="Plumbus"),
|
||||
]
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
from typing import List, Union
|
||||
|
||||
from fastapi import FastAPI
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class Item(BaseModel):
|
||||
name: str
|
||||
description: Union[str, None] = None
|
||||
|
||||
|
||||
app = FastAPI(separate_input_output_schemas=False)
|
||||
|
||||
|
||||
@app.post("/items/")
|
||||
def create_item(item: Item):
|
||||
return item
|
||||
|
||||
|
||||
@app.get("/items/")
|
||||
def read_items() -> List[Item]:
|
||||
return [
|
||||
Item(
|
||||
name="Portal Gun",
|
||||
description="Device to travel through the multi-rick-verse",
|
||||
),
|
||||
Item(name="Plumbus"),
|
||||
]
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
from fastapi import FastAPI
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class Item(BaseModel):
|
||||
name: str
|
||||
description: str | None = None
|
||||
|
||||
|
||||
app = FastAPI(separate_input_output_schemas=False)
|
||||
|
||||
|
||||
@app.post("/items/")
|
||||
def create_item(item: Item):
|
||||
return item
|
||||
|
||||
|
||||
@app.get("/items/")
|
||||
def read_items() -> list[Item]:
|
||||
return [
|
||||
Item(
|
||||
name="Portal Gun",
|
||||
description="Device to travel through the multi-rick-verse",
|
||||
),
|
||||
Item(name="Plumbus"),
|
||||
]
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
from typing import Optional
|
||||
|
||||
from fastapi import FastAPI
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class Item(BaseModel):
|
||||
name: str
|
||||
description: Optional[str] = None
|
||||
|
||||
|
||||
app = FastAPI(separate_input_output_schemas=False)
|
||||
|
||||
|
||||
@app.post("/items/")
|
||||
def create_item(item: Item):
|
||||
return item
|
||||
|
||||
|
||||
@app.get("/items/")
|
||||
def read_items() -> list[Item]:
|
||||
return [
|
||||
Item(
|
||||
name="Portal Gun",
|
||||
description="Device to travel through the multi-rick-verse",
|
||||
),
|
||||
Item(name="Plumbus"),
|
||||
]
|
||||
|
|
@ -181,9 +181,13 @@ if PYDANTIC_V2:
|
|||
field_mapping: Dict[
|
||||
Tuple[ModelField, Literal["validation", "serialization"]], JsonSchemaValue
|
||||
],
|
||||
separate_input_output_schemas: bool = True,
|
||||
) -> Dict[str, Any]:
|
||||
override_mode: Union[Literal["validation"], None] = (
|
||||
None if separate_input_output_schemas else "validation"
|
||||
)
|
||||
# This expects that GenerateJsonSchema was already used to generate the definitions
|
||||
json_schema = field_mapping[(field, field.mode)]
|
||||
json_schema = field_mapping[(field, override_mode or field.mode)]
|
||||
if "$ref" not in json_schema:
|
||||
# TODO remove when deprecating Pydantic v1
|
||||
# Ref: https://github.com/pydantic/pydantic/blob/d61792cc42c80b13b23e3ffa74bc37ec7c77f7d1/pydantic/schema.py#L207
|
||||
|
|
@ -200,14 +204,19 @@ if PYDANTIC_V2:
|
|||
fields: List[ModelField],
|
||||
schema_generator: GenerateJsonSchema,
|
||||
model_name_map: ModelNameMap,
|
||||
separate_input_output_schemas: bool = True,
|
||||
) -> Tuple[
|
||||
Dict[
|
||||
Tuple[ModelField, Literal["validation", "serialization"]], JsonSchemaValue
|
||||
],
|
||||
Dict[str, Dict[str, Any]],
|
||||
]:
|
||||
override_mode: Union[Literal["validation"], None] = (
|
||||
None if separate_input_output_schemas else "validation"
|
||||
)
|
||||
inputs = [
|
||||
(field, field.mode, field._type_adapter.core_schema) for field in fields
|
||||
(field, override_mode or field.mode, field._type_adapter.core_schema)
|
||||
for field in fields
|
||||
]
|
||||
field_mapping, definitions = schema_generator.generate_definitions(
|
||||
inputs=inputs
|
||||
|
|
@ -429,6 +438,7 @@ else:
|
|||
field_mapping: Dict[
|
||||
Tuple[ModelField, Literal["validation", "serialization"]], JsonSchemaValue
|
||||
],
|
||||
separate_input_output_schemas: bool = True,
|
||||
) -> Dict[str, Any]:
|
||||
# This expects that GenerateJsonSchema was already used to generate the definitions
|
||||
return field_schema( # type: ignore[no-any-return]
|
||||
|
|
@ -444,6 +454,7 @@ else:
|
|||
fields: List[ModelField],
|
||||
schema_generator: GenerateJsonSchema,
|
||||
model_name_map: ModelNameMap,
|
||||
separate_input_output_schemas: bool = True,
|
||||
) -> Tuple[
|
||||
Dict[
|
||||
Tuple[ModelField, Literal["validation", "serialization"]], JsonSchemaValue
|
||||
|
|
|
|||
|
|
@ -92,6 +92,7 @@ class FastAPI(Starlette):
|
|||
generate_unique_id_function: Callable[[routing.APIRoute], str] = Default(
|
||||
generate_unique_id
|
||||
),
|
||||
separate_input_output_schemas: bool = True,
|
||||
**extra: Any,
|
||||
) -> None:
|
||||
self.debug = debug
|
||||
|
|
@ -111,6 +112,7 @@ class FastAPI(Starlette):
|
|||
self.swagger_ui_init_oauth = swagger_ui_init_oauth
|
||||
self.swagger_ui_parameters = swagger_ui_parameters
|
||||
self.servers = servers or []
|
||||
self.separate_input_output_schemas = separate_input_output_schemas
|
||||
self.extra = extra
|
||||
self.openapi_version = "3.1.0"
|
||||
self.openapi_schema: Optional[Dict[str, Any]] = None
|
||||
|
|
@ -227,6 +229,7 @@ class FastAPI(Starlette):
|
|||
webhooks=self.webhooks.routes,
|
||||
tags=self.openapi_tags,
|
||||
servers=self.servers,
|
||||
separate_input_output_schemas=self.separate_input_output_schemas,
|
||||
)
|
||||
return self.openapi_schema
|
||||
|
||||
|
|
|
|||
|
|
@ -95,6 +95,7 @@ def get_openapi_operation_parameters(
|
|||
field_mapping: Dict[
|
||||
Tuple[ModelField, Literal["validation", "serialization"]], JsonSchemaValue
|
||||
],
|
||||
separate_input_output_schemas: bool = True,
|
||||
) -> List[Dict[str, Any]]:
|
||||
parameters = []
|
||||
for param in all_route_params:
|
||||
|
|
@ -107,6 +108,7 @@ def get_openapi_operation_parameters(
|
|||
schema_generator=schema_generator,
|
||||
model_name_map=model_name_map,
|
||||
field_mapping=field_mapping,
|
||||
separate_input_output_schemas=separate_input_output_schemas,
|
||||
)
|
||||
parameter = {
|
||||
"name": param.alias,
|
||||
|
|
@ -132,6 +134,7 @@ def get_openapi_operation_request_body(
|
|||
field_mapping: Dict[
|
||||
Tuple[ModelField, Literal["validation", "serialization"]], JsonSchemaValue
|
||||
],
|
||||
separate_input_output_schemas: bool = True,
|
||||
) -> Optional[Dict[str, Any]]:
|
||||
if not body_field:
|
||||
return None
|
||||
|
|
@ -141,6 +144,7 @@ def get_openapi_operation_request_body(
|
|||
schema_generator=schema_generator,
|
||||
model_name_map=model_name_map,
|
||||
field_mapping=field_mapping,
|
||||
separate_input_output_schemas=separate_input_output_schemas,
|
||||
)
|
||||
field_info = cast(Body, body_field.field_info)
|
||||
request_media_type = field_info.media_type
|
||||
|
|
@ -211,6 +215,7 @@ def get_openapi_path(
|
|||
field_mapping: Dict[
|
||||
Tuple[ModelField, Literal["validation", "serialization"]], JsonSchemaValue
|
||||
],
|
||||
separate_input_output_schemas: bool = True,
|
||||
) -> Tuple[Dict[str, Any], Dict[str, Any], Dict[str, Any]]:
|
||||
path = {}
|
||||
security_schemes: Dict[str, Any] = {}
|
||||
|
|
@ -242,6 +247,7 @@ def get_openapi_path(
|
|||
schema_generator=schema_generator,
|
||||
model_name_map=model_name_map,
|
||||
field_mapping=field_mapping,
|
||||
separate_input_output_schemas=separate_input_output_schemas,
|
||||
)
|
||||
parameters.extend(operation_parameters)
|
||||
if parameters:
|
||||
|
|
@ -263,6 +269,7 @@ def get_openapi_path(
|
|||
schema_generator=schema_generator,
|
||||
model_name_map=model_name_map,
|
||||
field_mapping=field_mapping,
|
||||
separate_input_output_schemas=separate_input_output_schemas,
|
||||
)
|
||||
if request_body_oai:
|
||||
operation["requestBody"] = request_body_oai
|
||||
|
|
@ -280,6 +287,7 @@ def get_openapi_path(
|
|||
schema_generator=schema_generator,
|
||||
model_name_map=model_name_map,
|
||||
field_mapping=field_mapping,
|
||||
separate_input_output_schemas=separate_input_output_schemas,
|
||||
)
|
||||
callbacks[callback.name] = {callback.path: cb_path}
|
||||
operation["callbacks"] = callbacks
|
||||
|
|
@ -310,6 +318,7 @@ def get_openapi_path(
|
|||
schema_generator=schema_generator,
|
||||
model_name_map=model_name_map,
|
||||
field_mapping=field_mapping,
|
||||
separate_input_output_schemas=separate_input_output_schemas,
|
||||
)
|
||||
else:
|
||||
response_schema = {}
|
||||
|
|
@ -343,6 +352,7 @@ def get_openapi_path(
|
|||
schema_generator=schema_generator,
|
||||
model_name_map=model_name_map,
|
||||
field_mapping=field_mapping,
|
||||
separate_input_output_schemas=separate_input_output_schemas,
|
||||
)
|
||||
media_type = route_response_media_type or "application/json"
|
||||
additional_schema = (
|
||||
|
|
@ -433,6 +443,7 @@ def get_openapi(
|
|||
terms_of_service: Optional[str] = None,
|
||||
contact: Optional[Dict[str, Union[str, Any]]] = None,
|
||||
license_info: Optional[Dict[str, Union[str, Any]]] = None,
|
||||
separate_input_output_schemas: bool = True,
|
||||
) -> Dict[str, Any]:
|
||||
info: Dict[str, Any] = {"title": title, "version": version}
|
||||
if summary:
|
||||
|
|
@ -459,6 +470,7 @@ def get_openapi(
|
|||
fields=all_fields,
|
||||
schema_generator=schema_generator,
|
||||
model_name_map=model_name_map,
|
||||
separate_input_output_schemas=separate_input_output_schemas,
|
||||
)
|
||||
for route in routes or []:
|
||||
if isinstance(route, routing.APIRoute):
|
||||
|
|
@ -468,6 +480,7 @@ def get_openapi(
|
|||
schema_generator=schema_generator,
|
||||
model_name_map=model_name_map,
|
||||
field_mapping=field_mapping,
|
||||
separate_input_output_schemas=separate_input_output_schemas,
|
||||
)
|
||||
if result:
|
||||
path, security_schemes, path_definitions = result
|
||||
|
|
@ -487,6 +500,7 @@ def get_openapi(
|
|||
schema_generator=schema_generator,
|
||||
model_name_map=model_name_map,
|
||||
field_mapping=field_mapping,
|
||||
separate_input_output_schemas=separate_input_output_schemas,
|
||||
)
|
||||
if result:
|
||||
path, security_schemes, path_definitions = result
|
||||
|
|
|
|||
|
|
@ -3,3 +3,5 @@
|
|||
-r requirements-docs.txt
|
||||
uvicorn[standard] >=0.12.0,<0.23.0
|
||||
pre-commit >=2.17.0,<4.0.0
|
||||
# For generating screenshots
|
||||
playwright
|
||||
|
|
|
|||
|
|
@ -0,0 +1,29 @@
|
|||
import subprocess
|
||||
|
||||
from playwright.sync_api import Playwright, sync_playwright
|
||||
|
||||
|
||||
def run(playwright: Playwright) -> None:
|
||||
browser = playwright.chromium.launch(headless=False)
|
||||
context = browser.new_context(viewport={"width": 960, "height": 1080})
|
||||
page = context.new_page()
|
||||
page.goto("http://localhost:8000/docs")
|
||||
page.get_by_text("POST/items/Create Item").click()
|
||||
page.get_by_role("tab", name="Schema").first.click()
|
||||
page.screenshot(
|
||||
path="docs/en/docs/img/tutorial/separate-openapi-schemas/image01.png"
|
||||
)
|
||||
|
||||
# ---------------------
|
||||
context.close()
|
||||
browser.close()
|
||||
|
||||
|
||||
process = subprocess.Popen(
|
||||
["uvicorn", "docs_src.separate_openapi_schemas.tutorial001:app"]
|
||||
)
|
||||
try:
|
||||
with sync_playwright() as playwright:
|
||||
run(playwright)
|
||||
finally:
|
||||
process.terminate()
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
import subprocess
|
||||
|
||||
from playwright.sync_api import Playwright, sync_playwright
|
||||
|
||||
|
||||
def run(playwright: Playwright) -> None:
|
||||
browser = playwright.chromium.launch(headless=False)
|
||||
context = browser.new_context(viewport={"width": 960, "height": 1080})
|
||||
page = context.new_page()
|
||||
page.goto("http://localhost:8000/docs")
|
||||
page.get_by_text("GET/items/Read Items").click()
|
||||
page.get_by_role("button", name="Try it out").click()
|
||||
page.get_by_role("button", name="Execute").click()
|
||||
page.screenshot(
|
||||
path="docs/en/docs/img/tutorial/separate-openapi-schemas/image02.png"
|
||||
)
|
||||
|
||||
# ---------------------
|
||||
context.close()
|
||||
browser.close()
|
||||
|
||||
|
||||
process = subprocess.Popen(
|
||||
["uvicorn", "docs_src.separate_openapi_schemas.tutorial001:app"]
|
||||
)
|
||||
try:
|
||||
with sync_playwright() as playwright:
|
||||
run(playwright)
|
||||
finally:
|
||||
process.terminate()
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
import subprocess
|
||||
|
||||
from playwright.sync_api import Playwright, sync_playwright
|
||||
|
||||
|
||||
def run(playwright: Playwright) -> None:
|
||||
browser = playwright.chromium.launch(headless=False)
|
||||
context = browser.new_context(viewport={"width": 960, "height": 1080})
|
||||
page = context.new_page()
|
||||
page.goto("http://localhost:8000/docs")
|
||||
page.get_by_text("GET/items/Read Items").click()
|
||||
page.get_by_role("tab", name="Schema").click()
|
||||
page.get_by_label("Schema").get_by_role("button", name="Expand all").click()
|
||||
page.screenshot(
|
||||
path="docs/en/docs/img/tutorial/separate-openapi-schemas/image03.png"
|
||||
)
|
||||
|
||||
# ---------------------
|
||||
context.close()
|
||||
browser.close()
|
||||
|
||||
|
||||
process = subprocess.Popen(
|
||||
["uvicorn", "docs_src.separate_openapi_schemas.tutorial001:app"]
|
||||
)
|
||||
try:
|
||||
with sync_playwright() as playwright:
|
||||
run(playwright)
|
||||
finally:
|
||||
process.terminate()
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
import subprocess
|
||||
|
||||
from playwright.sync_api import Playwright, sync_playwright
|
||||
|
||||
|
||||
def run(playwright: Playwright) -> None:
|
||||
browser = playwright.chromium.launch(headless=False)
|
||||
context = browser.new_context(viewport={"width": 960, "height": 1080})
|
||||
page = context.new_page()
|
||||
page.goto("http://localhost:8000/docs")
|
||||
page.get_by_role("button", name="Item-Input").click()
|
||||
page.get_by_role("button", name="Item-Output").click()
|
||||
page.set_viewport_size({"width": 960, "height": 820})
|
||||
page.screenshot(
|
||||
path="docs/en/docs/img/tutorial/separate-openapi-schemas/image04.png"
|
||||
)
|
||||
# ---------------------
|
||||
context.close()
|
||||
browser.close()
|
||||
|
||||
|
||||
process = subprocess.Popen(
|
||||
["uvicorn", "docs_src.separate_openapi_schemas.tutorial001:app"]
|
||||
)
|
||||
try:
|
||||
with sync_playwright() as playwright:
|
||||
run(playwright)
|
||||
finally:
|
||||
process.terminate()
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
import subprocess
|
||||
|
||||
from playwright.sync_api import Playwright, sync_playwright
|
||||
|
||||
|
||||
def run(playwright: Playwright) -> None:
|
||||
browser = playwright.chromium.launch(headless=False)
|
||||
context = browser.new_context(viewport={"width": 960, "height": 1080})
|
||||
page = context.new_page()
|
||||
page.goto("http://localhost:8000/docs")
|
||||
page.get_by_role("button", name="Item", exact=True).click()
|
||||
page.set_viewport_size({"width": 960, "height": 700})
|
||||
page.screenshot(
|
||||
path="docs/en/docs/img/tutorial/separate-openapi-schemas/image05.png"
|
||||
)
|
||||
|
||||
# ---------------------
|
||||
context.close()
|
||||
browser.close()
|
||||
|
||||
|
||||
process = subprocess.Popen(
|
||||
["uvicorn", "docs_src.separate_openapi_schemas.tutorial002:app"]
|
||||
)
|
||||
try:
|
||||
with sync_playwright() as playwright:
|
||||
run(playwright)
|
||||
finally:
|
||||
process.terminate()
|
||||
|
|
@ -0,0 +1,490 @@
|
|||
from typing import List, Optional
|
||||
|
||||
from fastapi import FastAPI
|
||||
from fastapi.testclient import TestClient
|
||||
from pydantic import BaseModel
|
||||
|
||||
from .utils import needs_pydanticv2
|
||||
|
||||
|
||||
class SubItem(BaseModel):
|
||||
subname: str
|
||||
sub_description: Optional[str] = None
|
||||
tags: List[str] = []
|
||||
|
||||
|
||||
class Item(BaseModel):
|
||||
name: str
|
||||
description: Optional[str] = None
|
||||
sub: Optional[SubItem] = None
|
||||
|
||||
|
||||
def get_app_client(separate_input_output_schemas: bool = True) -> TestClient:
|
||||
app = FastAPI(separate_input_output_schemas=separate_input_output_schemas)
|
||||
|
||||
@app.post("/items/")
|
||||
def create_item(item: Item):
|
||||
return item
|
||||
|
||||
@app.post("/items-list/")
|
||||
def create_item_list(item: List[Item]):
|
||||
return item
|
||||
|
||||
@app.get("/items/")
|
||||
def read_items() -> List[Item]:
|
||||
return [
|
||||
Item(
|
||||
name="Portal Gun",
|
||||
description="Device to travel through the multi-rick-verse",
|
||||
sub=SubItem(subname="subname"),
|
||||
),
|
||||
Item(name="Plumbus"),
|
||||
]
|
||||
|
||||
client = TestClient(app)
|
||||
return client
|
||||
|
||||
|
||||
def test_create_item():
|
||||
client = get_app_client()
|
||||
client_no = get_app_client(separate_input_output_schemas=False)
|
||||
response = client.post("/items/", json={"name": "Plumbus"})
|
||||
response2 = client_no.post("/items/", json={"name": "Plumbus"})
|
||||
assert response.status_code == response2.status_code == 200, response.text
|
||||
assert (
|
||||
response.json()
|
||||
== response2.json()
|
||||
== {"name": "Plumbus", "description": None, "sub": None}
|
||||
)
|
||||
|
||||
|
||||
def test_create_item_with_sub():
|
||||
client = get_app_client()
|
||||
client_no = get_app_client(separate_input_output_schemas=False)
|
||||
data = {
|
||||
"name": "Plumbus",
|
||||
"sub": {"subname": "SubPlumbus", "sub_description": "Sub WTF"},
|
||||
}
|
||||
response = client.post("/items/", json=data)
|
||||
response2 = client_no.post("/items/", json=data)
|
||||
assert response.status_code == response2.status_code == 200, response.text
|
||||
assert (
|
||||
response.json()
|
||||
== response2.json()
|
||||
== {
|
||||
"name": "Plumbus",
|
||||
"description": None,
|
||||
"sub": {"subname": "SubPlumbus", "sub_description": "Sub WTF", "tags": []},
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
def test_create_item_list():
|
||||
client = get_app_client()
|
||||
client_no = get_app_client(separate_input_output_schemas=False)
|
||||
data = [
|
||||
{"name": "Plumbus"},
|
||||
{
|
||||
"name": "Portal Gun",
|
||||
"description": "Device to travel through the multi-rick-verse",
|
||||
},
|
||||
]
|
||||
response = client.post("/items-list/", json=data)
|
||||
response2 = client_no.post("/items-list/", json=data)
|
||||
assert response.status_code == response2.status_code == 200, response.text
|
||||
assert (
|
||||
response.json()
|
||||
== response2.json()
|
||||
== [
|
||||
{"name": "Plumbus", "description": None, "sub": None},
|
||||
{
|
||||
"name": "Portal Gun",
|
||||
"description": "Device to travel through the multi-rick-verse",
|
||||
"sub": None,
|
||||
},
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
def test_read_items():
|
||||
client = get_app_client()
|
||||
client_no = get_app_client(separate_input_output_schemas=False)
|
||||
response = client.get("/items/")
|
||||
response2 = client_no.get("/items/")
|
||||
assert response.status_code == response2.status_code == 200, response.text
|
||||
assert (
|
||||
response.json()
|
||||
== response2.json()
|
||||
== [
|
||||
{
|
||||
"name": "Portal Gun",
|
||||
"description": "Device to travel through the multi-rick-verse",
|
||||
"sub": {"subname": "subname", "sub_description": None, "tags": []},
|
||||
},
|
||||
{"name": "Plumbus", "description": None, "sub": None},
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
@needs_pydanticv2
|
||||
def test_openapi_schema():
|
||||
client = get_app_client()
|
||||
response = client.get("/openapi.json")
|
||||
assert response.status_code == 200, response.text
|
||||
assert response.json() == {
|
||||
"openapi": "3.1.0",
|
||||
"info": {"title": "FastAPI", "version": "0.1.0"},
|
||||
"paths": {
|
||||
"/items/": {
|
||||
"get": {
|
||||
"summary": "Read Items",
|
||||
"operationId": "read_items_items__get",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Successful Response",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/Item-Output"
|
||||
},
|
||||
"type": "array",
|
||||
"title": "Response Read Items Items Get",
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
"post": {
|
||||
"summary": "Create Item",
|
||||
"operationId": "create_item_items__post",
|
||||
"requestBody": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {"$ref": "#/components/schemas/Item-Input"}
|
||||
}
|
||||
},
|
||||
"required": True,
|
||||
},
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Successful Response",
|
||||
"content": {"application/json": {"schema": {}}},
|
||||
},
|
||||
"422": {
|
||||
"description": "Validation Error",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/HTTPValidationError"
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"/items-list/": {
|
||||
"post": {
|
||||
"summary": "Create Item List",
|
||||
"operationId": "create_item_list_items_list__post",
|
||||
"requestBody": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/Item-Input"
|
||||
},
|
||||
"type": "array",
|
||||
"title": "Item",
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": True,
|
||||
},
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Successful Response",
|
||||
"content": {"application/json": {"schema": {}}},
|
||||
},
|
||||
"422": {
|
||||
"description": "Validation Error",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/HTTPValidationError"
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
"components": {
|
||||
"schemas": {
|
||||
"HTTPValidationError": {
|
||||
"properties": {
|
||||
"detail": {
|
||||
"items": {"$ref": "#/components/schemas/ValidationError"},
|
||||
"type": "array",
|
||||
"title": "Detail",
|
||||
}
|
||||
},
|
||||
"type": "object",
|
||||
"title": "HTTPValidationError",
|
||||
},
|
||||
"Item-Input": {
|
||||
"properties": {
|
||||
"name": {"type": "string", "title": "Name"},
|
||||
"description": {
|
||||
"anyOf": [{"type": "string"}, {"type": "null"}],
|
||||
"title": "Description",
|
||||
},
|
||||
"sub": {
|
||||
"anyOf": [
|
||||
{"$ref": "#/components/schemas/SubItem-Input"},
|
||||
{"type": "null"},
|
||||
]
|
||||
},
|
||||
},
|
||||
"type": "object",
|
||||
"required": ["name"],
|
||||
"title": "Item",
|
||||
},
|
||||
"Item-Output": {
|
||||
"properties": {
|
||||
"name": {"type": "string", "title": "Name"},
|
||||
"description": {
|
||||
"anyOf": [{"type": "string"}, {"type": "null"}],
|
||||
"title": "Description",
|
||||
},
|
||||
"sub": {
|
||||
"anyOf": [
|
||||
{"$ref": "#/components/schemas/SubItem-Output"},
|
||||
{"type": "null"},
|
||||
]
|
||||
},
|
||||
},
|
||||
"type": "object",
|
||||
"required": ["name", "description", "sub"],
|
||||
"title": "Item",
|
||||
},
|
||||
"SubItem-Input": {
|
||||
"properties": {
|
||||
"subname": {"type": "string", "title": "Subname"},
|
||||
"sub_description": {
|
||||
"anyOf": [{"type": "string"}, {"type": "null"}],
|
||||
"title": "Sub Description",
|
||||
},
|
||||
"tags": {
|
||||
"items": {"type": "string"},
|
||||
"type": "array",
|
||||
"title": "Tags",
|
||||
"default": [],
|
||||
},
|
||||
},
|
||||
"type": "object",
|
||||
"required": ["subname"],
|
||||
"title": "SubItem",
|
||||
},
|
||||
"SubItem-Output": {
|
||||
"properties": {
|
||||
"subname": {"type": "string", "title": "Subname"},
|
||||
"sub_description": {
|
||||
"anyOf": [{"type": "string"}, {"type": "null"}],
|
||||
"title": "Sub Description",
|
||||
},
|
||||
"tags": {
|
||||
"items": {"type": "string"},
|
||||
"type": "array",
|
||||
"title": "Tags",
|
||||
"default": [],
|
||||
},
|
||||
},
|
||||
"type": "object",
|
||||
"required": ["subname", "sub_description", "tags"],
|
||||
"title": "SubItem",
|
||||
},
|
||||
"ValidationError": {
|
||||
"properties": {
|
||||
"loc": {
|
||||
"items": {
|
||||
"anyOf": [{"type": "string"}, {"type": "integer"}]
|
||||
},
|
||||
"type": "array",
|
||||
"title": "Location",
|
||||
},
|
||||
"msg": {"type": "string", "title": "Message"},
|
||||
"type": {"type": "string", "title": "Error Type"},
|
||||
},
|
||||
"type": "object",
|
||||
"required": ["loc", "msg", "type"],
|
||||
"title": "ValidationError",
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@needs_pydanticv2
|
||||
def test_openapi_schema_no_separate():
|
||||
client = get_app_client(separate_input_output_schemas=False)
|
||||
response = client.get("/openapi.json")
|
||||
assert response.status_code == 200, response.text
|
||||
assert response.json() == {
|
||||
"openapi": "3.1.0",
|
||||
"info": {"title": "FastAPI", "version": "0.1.0"},
|
||||
"paths": {
|
||||
"/items/": {
|
||||
"get": {
|
||||
"summary": "Read Items",
|
||||
"operationId": "read_items_items__get",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Successful Response",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"items": {"$ref": "#/components/schemas/Item"},
|
||||
"type": "array",
|
||||
"title": "Response Read Items Items Get",
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
"post": {
|
||||
"summary": "Create Item",
|
||||
"operationId": "create_item_items__post",
|
||||
"requestBody": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {"$ref": "#/components/schemas/Item"}
|
||||
}
|
||||
},
|
||||
"required": True,
|
||||
},
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Successful Response",
|
||||
"content": {"application/json": {"schema": {}}},
|
||||
},
|
||||
"422": {
|
||||
"description": "Validation Error",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/HTTPValidationError"
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"/items-list/": {
|
||||
"post": {
|
||||
"summary": "Create Item List",
|
||||
"operationId": "create_item_list_items_list__post",
|
||||
"requestBody": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"items": {"$ref": "#/components/schemas/Item"},
|
||||
"type": "array",
|
||||
"title": "Item",
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": True,
|
||||
},
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Successful Response",
|
||||
"content": {"application/json": {"schema": {}}},
|
||||
},
|
||||
"422": {
|
||||
"description": "Validation Error",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/HTTPValidationError"
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
"components": {
|
||||
"schemas": {
|
||||
"HTTPValidationError": {
|
||||
"properties": {
|
||||
"detail": {
|
||||
"items": {"$ref": "#/components/schemas/ValidationError"},
|
||||
"type": "array",
|
||||
"title": "Detail",
|
||||
}
|
||||
},
|
||||
"type": "object",
|
||||
"title": "HTTPValidationError",
|
||||
},
|
||||
"Item": {
|
||||
"properties": {
|
||||
"name": {"type": "string", "title": "Name"},
|
||||
"description": {
|
||||
"anyOf": [{"type": "string"}, {"type": "null"}],
|
||||
"title": "Description",
|
||||
},
|
||||
"sub": {
|
||||
"anyOf": [
|
||||
{"$ref": "#/components/schemas/SubItem"},
|
||||
{"type": "null"},
|
||||
]
|
||||
},
|
||||
},
|
||||
"type": "object",
|
||||
"required": ["name"],
|
||||
"title": "Item",
|
||||
},
|
||||
"SubItem": {
|
||||
"properties": {
|
||||
"subname": {"type": "string", "title": "Subname"},
|
||||
"sub_description": {
|
||||
"anyOf": [{"type": "string"}, {"type": "null"}],
|
||||
"title": "Sub Description",
|
||||
},
|
||||
"tags": {
|
||||
"items": {"type": "string"},
|
||||
"type": "array",
|
||||
"title": "Tags",
|
||||
"default": [],
|
||||
},
|
||||
},
|
||||
"type": "object",
|
||||
"required": ["subname"],
|
||||
"title": "SubItem",
|
||||
},
|
||||
"ValidationError": {
|
||||
"properties": {
|
||||
"loc": {
|
||||
"items": {
|
||||
"anyOf": [{"type": "string"}, {"type": "integer"}]
|
||||
},
|
||||
"type": "array",
|
||||
"title": "Location",
|
||||
},
|
||||
"msg": {"type": "string", "title": "Message"},
|
||||
"type": {"type": "string", "title": "Error Type"},
|
||||
},
|
||||
"type": "object",
|
||||
"required": ["loc", "msg", "type"],
|
||||
"title": "ValidationError",
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
|
|
@ -0,0 +1,147 @@
|
|||
import pytest
|
||||
from fastapi.testclient import TestClient
|
||||
|
||||
from ...utils import needs_pydanticv2
|
||||
|
||||
|
||||
@pytest.fixture(name="client")
|
||||
def get_client() -> TestClient:
|
||||
from docs_src.separate_openapi_schemas.tutorial001 import app
|
||||
|
||||
client = TestClient(app)
|
||||
return client
|
||||
|
||||
|
||||
def test_create_item(client: TestClient) -> None:
|
||||
response = client.post("/items/", json={"name": "Foo"})
|
||||
assert response.status_code == 200, response.text
|
||||
assert response.json() == {"name": "Foo", "description": None}
|
||||
|
||||
|
||||
def test_read_items(client: TestClient) -> None:
|
||||
response = client.get("/items/")
|
||||
assert response.status_code == 200, response.text
|
||||
assert response.json() == [
|
||||
{
|
||||
"name": "Portal Gun",
|
||||
"description": "Device to travel through the multi-rick-verse",
|
||||
},
|
||||
{"name": "Plumbus", "description": None},
|
||||
]
|
||||
|
||||
|
||||
@needs_pydanticv2
|
||||
def test_openapi_schema(client: TestClient) -> None:
|
||||
response = client.get("/openapi.json")
|
||||
assert response.status_code == 200, response.text
|
||||
assert response.json() == {
|
||||
"openapi": "3.1.0",
|
||||
"info": {"title": "FastAPI", "version": "0.1.0"},
|
||||
"paths": {
|
||||
"/items/": {
|
||||
"get": {
|
||||
"summary": "Read Items",
|
||||
"operationId": "read_items_items__get",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Successful Response",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/Item-Output"
|
||||
},
|
||||
"type": "array",
|
||||
"title": "Response Read Items Items Get",
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
"post": {
|
||||
"summary": "Create Item",
|
||||
"operationId": "create_item_items__post",
|
||||
"requestBody": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {"$ref": "#/components/schemas/Item-Input"}
|
||||
}
|
||||
},
|
||||
"required": True,
|
||||
},
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Successful Response",
|
||||
"content": {"application/json": {"schema": {}}},
|
||||
},
|
||||
"422": {
|
||||
"description": "Validation Error",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/HTTPValidationError"
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
"components": {
|
||||
"schemas": {
|
||||
"HTTPValidationError": {
|
||||
"properties": {
|
||||
"detail": {
|
||||
"items": {"$ref": "#/components/schemas/ValidationError"},
|
||||
"type": "array",
|
||||
"title": "Detail",
|
||||
}
|
||||
},
|
||||
"type": "object",
|
||||
"title": "HTTPValidationError",
|
||||
},
|
||||
"Item-Input": {
|
||||
"properties": {
|
||||
"name": {"type": "string", "title": "Name"},
|
||||
"description": {
|
||||
"anyOf": [{"type": "string"}, {"type": "null"}],
|
||||
"title": "Description",
|
||||
},
|
||||
},
|
||||
"type": "object",
|
||||
"required": ["name"],
|
||||
"title": "Item",
|
||||
},
|
||||
"Item-Output": {
|
||||
"properties": {
|
||||
"name": {"type": "string", "title": "Name"},
|
||||
"description": {
|
||||
"anyOf": [{"type": "string"}, {"type": "null"}],
|
||||
"title": "Description",
|
||||
},
|
||||
},
|
||||
"type": "object",
|
||||
"required": ["name", "description"],
|
||||
"title": "Item",
|
||||
},
|
||||
"ValidationError": {
|
||||
"properties": {
|
||||
"loc": {
|
||||
"items": {
|
||||
"anyOf": [{"type": "string"}, {"type": "integer"}]
|
||||
},
|
||||
"type": "array",
|
||||
"title": "Location",
|
||||
},
|
||||
"msg": {"type": "string", "title": "Message"},
|
||||
"type": {"type": "string", "title": "Error Type"},
|
||||
},
|
||||
"type": "object",
|
||||
"required": ["loc", "msg", "type"],
|
||||
"title": "ValidationError",
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
|
|
@ -0,0 +1,150 @@
|
|||
import pytest
|
||||
from fastapi.testclient import TestClient
|
||||
|
||||
from ...utils import needs_py310, needs_pydanticv2
|
||||
|
||||
|
||||
@pytest.fixture(name="client")
|
||||
def get_client() -> TestClient:
|
||||
from docs_src.separate_openapi_schemas.tutorial001_py310 import app
|
||||
|
||||
client = TestClient(app)
|
||||
return client
|
||||
|
||||
|
||||
@needs_py310
|
||||
def test_create_item(client: TestClient) -> None:
|
||||
response = client.post("/items/", json={"name": "Foo"})
|
||||
assert response.status_code == 200, response.text
|
||||
assert response.json() == {"name": "Foo", "description": None}
|
||||
|
||||
|
||||
@needs_py310
|
||||
def test_read_items(client: TestClient) -> None:
|
||||
response = client.get("/items/")
|
||||
assert response.status_code == 200, response.text
|
||||
assert response.json() == [
|
||||
{
|
||||
"name": "Portal Gun",
|
||||
"description": "Device to travel through the multi-rick-verse",
|
||||
},
|
||||
{"name": "Plumbus", "description": None},
|
||||
]
|
||||
|
||||
|
||||
@needs_py310
|
||||
@needs_pydanticv2
|
||||
def test_openapi_schema(client: TestClient) -> None:
|
||||
response = client.get("/openapi.json")
|
||||
assert response.status_code == 200, response.text
|
||||
assert response.json() == {
|
||||
"openapi": "3.1.0",
|
||||
"info": {"title": "FastAPI", "version": "0.1.0"},
|
||||
"paths": {
|
||||
"/items/": {
|
||||
"get": {
|
||||
"summary": "Read Items",
|
||||
"operationId": "read_items_items__get",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Successful Response",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/Item-Output"
|
||||
},
|
||||
"type": "array",
|
||||
"title": "Response Read Items Items Get",
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
"post": {
|
||||
"summary": "Create Item",
|
||||
"operationId": "create_item_items__post",
|
||||
"requestBody": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {"$ref": "#/components/schemas/Item-Input"}
|
||||
}
|
||||
},
|
||||
"required": True,
|
||||
},
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Successful Response",
|
||||
"content": {"application/json": {"schema": {}}},
|
||||
},
|
||||
"422": {
|
||||
"description": "Validation Error",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/HTTPValidationError"
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
"components": {
|
||||
"schemas": {
|
||||
"HTTPValidationError": {
|
||||
"properties": {
|
||||
"detail": {
|
||||
"items": {"$ref": "#/components/schemas/ValidationError"},
|
||||
"type": "array",
|
||||
"title": "Detail",
|
||||
}
|
||||
},
|
||||
"type": "object",
|
||||
"title": "HTTPValidationError",
|
||||
},
|
||||
"Item-Input": {
|
||||
"properties": {
|
||||
"name": {"type": "string", "title": "Name"},
|
||||
"description": {
|
||||
"anyOf": [{"type": "string"}, {"type": "null"}],
|
||||
"title": "Description",
|
||||
},
|
||||
},
|
||||
"type": "object",
|
||||
"required": ["name"],
|
||||
"title": "Item",
|
||||
},
|
||||
"Item-Output": {
|
||||
"properties": {
|
||||
"name": {"type": "string", "title": "Name"},
|
||||
"description": {
|
||||
"anyOf": [{"type": "string"}, {"type": "null"}],
|
||||
"title": "Description",
|
||||
},
|
||||
},
|
||||
"type": "object",
|
||||
"required": ["name", "description"],
|
||||
"title": "Item",
|
||||
},
|
||||
"ValidationError": {
|
||||
"properties": {
|
||||
"loc": {
|
||||
"items": {
|
||||
"anyOf": [{"type": "string"}, {"type": "integer"}]
|
||||
},
|
||||
"type": "array",
|
||||
"title": "Location",
|
||||
},
|
||||
"msg": {"type": "string", "title": "Message"},
|
||||
"type": {"type": "string", "title": "Error Type"},
|
||||
},
|
||||
"type": "object",
|
||||
"required": ["loc", "msg", "type"],
|
||||
"title": "ValidationError",
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
|
|
@ -0,0 +1,150 @@
|
|||
import pytest
|
||||
from fastapi.testclient import TestClient
|
||||
|
||||
from ...utils import needs_py39, needs_pydanticv2
|
||||
|
||||
|
||||
@pytest.fixture(name="client")
|
||||
def get_client() -> TestClient:
|
||||
from docs_src.separate_openapi_schemas.tutorial001_py39 import app
|
||||
|
||||
client = TestClient(app)
|
||||
return client
|
||||
|
||||
|
||||
@needs_py39
|
||||
def test_create_item(client: TestClient) -> None:
|
||||
response = client.post("/items/", json={"name": "Foo"})
|
||||
assert response.status_code == 200, response.text
|
||||
assert response.json() == {"name": "Foo", "description": None}
|
||||
|
||||
|
||||
@needs_py39
|
||||
def test_read_items(client: TestClient) -> None:
|
||||
response = client.get("/items/")
|
||||
assert response.status_code == 200, response.text
|
||||
assert response.json() == [
|
||||
{
|
||||
"name": "Portal Gun",
|
||||
"description": "Device to travel through the multi-rick-verse",
|
||||
},
|
||||
{"name": "Plumbus", "description": None},
|
||||
]
|
||||
|
||||
|
||||
@needs_py39
|
||||
@needs_pydanticv2
|
||||
def test_openapi_schema(client: TestClient) -> None:
|
||||
response = client.get("/openapi.json")
|
||||
assert response.status_code == 200, response.text
|
||||
assert response.json() == {
|
||||
"openapi": "3.1.0",
|
||||
"info": {"title": "FastAPI", "version": "0.1.0"},
|
||||
"paths": {
|
||||
"/items/": {
|
||||
"get": {
|
||||
"summary": "Read Items",
|
||||
"operationId": "read_items_items__get",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Successful Response",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/Item-Output"
|
||||
},
|
||||
"type": "array",
|
||||
"title": "Response Read Items Items Get",
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
"post": {
|
||||
"summary": "Create Item",
|
||||
"operationId": "create_item_items__post",
|
||||
"requestBody": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {"$ref": "#/components/schemas/Item-Input"}
|
||||
}
|
||||
},
|
||||
"required": True,
|
||||
},
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Successful Response",
|
||||
"content": {"application/json": {"schema": {}}},
|
||||
},
|
||||
"422": {
|
||||
"description": "Validation Error",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/HTTPValidationError"
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
"components": {
|
||||
"schemas": {
|
||||
"HTTPValidationError": {
|
||||
"properties": {
|
||||
"detail": {
|
||||
"items": {"$ref": "#/components/schemas/ValidationError"},
|
||||
"type": "array",
|
||||
"title": "Detail",
|
||||
}
|
||||
},
|
||||
"type": "object",
|
||||
"title": "HTTPValidationError",
|
||||
},
|
||||
"Item-Input": {
|
||||
"properties": {
|
||||
"name": {"type": "string", "title": "Name"},
|
||||
"description": {
|
||||
"anyOf": [{"type": "string"}, {"type": "null"}],
|
||||
"title": "Description",
|
||||
},
|
||||
},
|
||||
"type": "object",
|
||||
"required": ["name"],
|
||||
"title": "Item",
|
||||
},
|
||||
"Item-Output": {
|
||||
"properties": {
|
||||
"name": {"type": "string", "title": "Name"},
|
||||
"description": {
|
||||
"anyOf": [{"type": "string"}, {"type": "null"}],
|
||||
"title": "Description",
|
||||
},
|
||||
},
|
||||
"type": "object",
|
||||
"required": ["name", "description"],
|
||||
"title": "Item",
|
||||
},
|
||||
"ValidationError": {
|
||||
"properties": {
|
||||
"loc": {
|
||||
"items": {
|
||||
"anyOf": [{"type": "string"}, {"type": "integer"}]
|
||||
},
|
||||
"type": "array",
|
||||
"title": "Location",
|
||||
},
|
||||
"msg": {"type": "string", "title": "Message"},
|
||||
"type": {"type": "string", "title": "Error Type"},
|
||||
},
|
||||
"type": "object",
|
||||
"required": ["loc", "msg", "type"],
|
||||
"title": "ValidationError",
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
|
|
@ -0,0 +1,133 @@
|
|||
import pytest
|
||||
from fastapi.testclient import TestClient
|
||||
|
||||
from ...utils import needs_pydanticv2
|
||||
|
||||
|
||||
@pytest.fixture(name="client")
|
||||
def get_client() -> TestClient:
|
||||
from docs_src.separate_openapi_schemas.tutorial002 import app
|
||||
|
||||
client = TestClient(app)
|
||||
return client
|
||||
|
||||
|
||||
def test_create_item(client: TestClient) -> None:
|
||||
response = client.post("/items/", json={"name": "Foo"})
|
||||
assert response.status_code == 200, response.text
|
||||
assert response.json() == {"name": "Foo", "description": None}
|
||||
|
||||
|
||||
def test_read_items(client: TestClient) -> None:
|
||||
response = client.get("/items/")
|
||||
assert response.status_code == 200, response.text
|
||||
assert response.json() == [
|
||||
{
|
||||
"name": "Portal Gun",
|
||||
"description": "Device to travel through the multi-rick-verse",
|
||||
},
|
||||
{"name": "Plumbus", "description": None},
|
||||
]
|
||||
|
||||
|
||||
@needs_pydanticv2
|
||||
def test_openapi_schema(client: TestClient) -> None:
|
||||
response = client.get("/openapi.json")
|
||||
assert response.status_code == 200, response.text
|
||||
assert response.json() == {
|
||||
"openapi": "3.1.0",
|
||||
"info": {"title": "FastAPI", "version": "0.1.0"},
|
||||
"paths": {
|
||||
"/items/": {
|
||||
"get": {
|
||||
"summary": "Read Items",
|
||||
"operationId": "read_items_items__get",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Successful Response",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"items": {"$ref": "#/components/schemas/Item"},
|
||||
"type": "array",
|
||||
"title": "Response Read Items Items Get",
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
"post": {
|
||||
"summary": "Create Item",
|
||||
"operationId": "create_item_items__post",
|
||||
"requestBody": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {"$ref": "#/components/schemas/Item"}
|
||||
}
|
||||
},
|
||||
"required": True,
|
||||
},
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Successful Response",
|
||||
"content": {"application/json": {"schema": {}}},
|
||||
},
|
||||
"422": {
|
||||
"description": "Validation Error",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/HTTPValidationError"
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
"components": {
|
||||
"schemas": {
|
||||
"HTTPValidationError": {
|
||||
"properties": {
|
||||
"detail": {
|
||||
"items": {"$ref": "#/components/schemas/ValidationError"},
|
||||
"type": "array",
|
||||
"title": "Detail",
|
||||
}
|
||||
},
|
||||
"type": "object",
|
||||
"title": "HTTPValidationError",
|
||||
},
|
||||
"Item": {
|
||||
"properties": {
|
||||
"name": {"type": "string", "title": "Name"},
|
||||
"description": {
|
||||
"anyOf": [{"type": "string"}, {"type": "null"}],
|
||||
"title": "Description",
|
||||
},
|
||||
},
|
||||
"type": "object",
|
||||
"required": ["name"],
|
||||
"title": "Item",
|
||||
},
|
||||
"ValidationError": {
|
||||
"properties": {
|
||||
"loc": {
|
||||
"items": {
|
||||
"anyOf": [{"type": "string"}, {"type": "integer"}]
|
||||
},
|
||||
"type": "array",
|
||||
"title": "Location",
|
||||
},
|
||||
"msg": {"type": "string", "title": "Message"},
|
||||
"type": {"type": "string", "title": "Error Type"},
|
||||
},
|
||||
"type": "object",
|
||||
"required": ["loc", "msg", "type"],
|
||||
"title": "ValidationError",
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
|
|
@ -0,0 +1,136 @@
|
|||
import pytest
|
||||
from fastapi.testclient import TestClient
|
||||
|
||||
from ...utils import needs_py310, needs_pydanticv2
|
||||
|
||||
|
||||
@pytest.fixture(name="client")
|
||||
def get_client() -> TestClient:
|
||||
from docs_src.separate_openapi_schemas.tutorial002_py310 import app
|
||||
|
||||
client = TestClient(app)
|
||||
return client
|
||||
|
||||
|
||||
@needs_py310
|
||||
def test_create_item(client: TestClient) -> None:
|
||||
response = client.post("/items/", json={"name": "Foo"})
|
||||
assert response.status_code == 200, response.text
|
||||
assert response.json() == {"name": "Foo", "description": None}
|
||||
|
||||
|
||||
@needs_py310
|
||||
def test_read_items(client: TestClient) -> None:
|
||||
response = client.get("/items/")
|
||||
assert response.status_code == 200, response.text
|
||||
assert response.json() == [
|
||||
{
|
||||
"name": "Portal Gun",
|
||||
"description": "Device to travel through the multi-rick-verse",
|
||||
},
|
||||
{"name": "Plumbus", "description": None},
|
||||
]
|
||||
|
||||
|
||||
@needs_py310
|
||||
@needs_pydanticv2
|
||||
def test_openapi_schema(client: TestClient) -> None:
|
||||
response = client.get("/openapi.json")
|
||||
assert response.status_code == 200, response.text
|
||||
assert response.json() == {
|
||||
"openapi": "3.1.0",
|
||||
"info": {"title": "FastAPI", "version": "0.1.0"},
|
||||
"paths": {
|
||||
"/items/": {
|
||||
"get": {
|
||||
"summary": "Read Items",
|
||||
"operationId": "read_items_items__get",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Successful Response",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"items": {"$ref": "#/components/schemas/Item"},
|
||||
"type": "array",
|
||||
"title": "Response Read Items Items Get",
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
"post": {
|
||||
"summary": "Create Item",
|
||||
"operationId": "create_item_items__post",
|
||||
"requestBody": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {"$ref": "#/components/schemas/Item"}
|
||||
}
|
||||
},
|
||||
"required": True,
|
||||
},
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Successful Response",
|
||||
"content": {"application/json": {"schema": {}}},
|
||||
},
|
||||
"422": {
|
||||
"description": "Validation Error",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/HTTPValidationError"
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
"components": {
|
||||
"schemas": {
|
||||
"HTTPValidationError": {
|
||||
"properties": {
|
||||
"detail": {
|
||||
"items": {"$ref": "#/components/schemas/ValidationError"},
|
||||
"type": "array",
|
||||
"title": "Detail",
|
||||
}
|
||||
},
|
||||
"type": "object",
|
||||
"title": "HTTPValidationError",
|
||||
},
|
||||
"Item": {
|
||||
"properties": {
|
||||
"name": {"type": "string", "title": "Name"},
|
||||
"description": {
|
||||
"anyOf": [{"type": "string"}, {"type": "null"}],
|
||||
"title": "Description",
|
||||
},
|
||||
},
|
||||
"type": "object",
|
||||
"required": ["name"],
|
||||
"title": "Item",
|
||||
},
|
||||
"ValidationError": {
|
||||
"properties": {
|
||||
"loc": {
|
||||
"items": {
|
||||
"anyOf": [{"type": "string"}, {"type": "integer"}]
|
||||
},
|
||||
"type": "array",
|
||||
"title": "Location",
|
||||
},
|
||||
"msg": {"type": "string", "title": "Message"},
|
||||
"type": {"type": "string", "title": "Error Type"},
|
||||
},
|
||||
"type": "object",
|
||||
"required": ["loc", "msg", "type"],
|
||||
"title": "ValidationError",
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
|
|
@ -0,0 +1,136 @@
|
|||
import pytest
|
||||
from fastapi.testclient import TestClient
|
||||
|
||||
from ...utils import needs_py39, needs_pydanticv2
|
||||
|
||||
|
||||
@pytest.fixture(name="client")
|
||||
def get_client() -> TestClient:
|
||||
from docs_src.separate_openapi_schemas.tutorial002_py39 import app
|
||||
|
||||
client = TestClient(app)
|
||||
return client
|
||||
|
||||
|
||||
@needs_py39
|
||||
def test_create_item(client: TestClient) -> None:
|
||||
response = client.post("/items/", json={"name": "Foo"})
|
||||
assert response.status_code == 200, response.text
|
||||
assert response.json() == {"name": "Foo", "description": None}
|
||||
|
||||
|
||||
@needs_py39
|
||||
def test_read_items(client: TestClient) -> None:
|
||||
response = client.get("/items/")
|
||||
assert response.status_code == 200, response.text
|
||||
assert response.json() == [
|
||||
{
|
||||
"name": "Portal Gun",
|
||||
"description": "Device to travel through the multi-rick-verse",
|
||||
},
|
||||
{"name": "Plumbus", "description": None},
|
||||
]
|
||||
|
||||
|
||||
@needs_py39
|
||||
@needs_pydanticv2
|
||||
def test_openapi_schema(client: TestClient) -> None:
|
||||
response = client.get("/openapi.json")
|
||||
assert response.status_code == 200, response.text
|
||||
assert response.json() == {
|
||||
"openapi": "3.1.0",
|
||||
"info": {"title": "FastAPI", "version": "0.1.0"},
|
||||
"paths": {
|
||||
"/items/": {
|
||||
"get": {
|
||||
"summary": "Read Items",
|
||||
"operationId": "read_items_items__get",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Successful Response",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"items": {"$ref": "#/components/schemas/Item"},
|
||||
"type": "array",
|
||||
"title": "Response Read Items Items Get",
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
"post": {
|
||||
"summary": "Create Item",
|
||||
"operationId": "create_item_items__post",
|
||||
"requestBody": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {"$ref": "#/components/schemas/Item"}
|
||||
}
|
||||
},
|
||||
"required": True,
|
||||
},
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Successful Response",
|
||||
"content": {"application/json": {"schema": {}}},
|
||||
},
|
||||
"422": {
|
||||
"description": "Validation Error",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/HTTPValidationError"
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
"components": {
|
||||
"schemas": {
|
||||
"HTTPValidationError": {
|
||||
"properties": {
|
||||
"detail": {
|
||||
"items": {"$ref": "#/components/schemas/ValidationError"},
|
||||
"type": "array",
|
||||
"title": "Detail",
|
||||
}
|
||||
},
|
||||
"type": "object",
|
||||
"title": "HTTPValidationError",
|
||||
},
|
||||
"Item": {
|
||||
"properties": {
|
||||
"name": {"type": "string", "title": "Name"},
|
||||
"description": {
|
||||
"anyOf": [{"type": "string"}, {"type": "null"}],
|
||||
"title": "Description",
|
||||
},
|
||||
},
|
||||
"type": "object",
|
||||
"required": ["name"],
|
||||
"title": "Item",
|
||||
},
|
||||
"ValidationError": {
|
||||
"properties": {
|
||||
"loc": {
|
||||
"items": {
|
||||
"anyOf": [{"type": "string"}, {"type": "integer"}]
|
||||
},
|
||||
"type": "array",
|
||||
"title": "Location",
|
||||
},
|
||||
"msg": {"type": "string", "title": "Message"},
|
||||
"type": {"type": "string", "title": "Error Type"},
|
||||
},
|
||||
"type": "object",
|
||||
"required": ["loc", "msg", "type"],
|
||||
"title": "ValidationError",
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
Loading…
Reference in New Issue