diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 7e288c54..5599b01e 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -2,5 +2,5 @@ Checklist: - [ ] Run `pytest tests` and no failed. - [ ] Run `flake8 flask_openapi3 tests examples` and no failed. -- [ ] Run `mkdocs serve` and no failed. - [ ] Run `mypy flask_openapi3` and no failed. +- [ ] Run `mkdocs serve` and no failed. diff --git a/docs/Example.md b/docs/Example.md index 23a8d24c..237de032 100644 --- a/docs/Example.md +++ b/docs/Example.md @@ -127,8 +127,7 @@ class BookResponse(BaseModel): tags=[book_tag], summary='new summary', description='new description', - responses={"200": BookResponse}, - extra_responses={"200": {"content": {"text/csv": {"schema": {"type": "string"}}}}}, + responses={"200": BookResponse, "201": {"content": {"text/csv": {"schema": {"type": "string"}}}}}, security=security ) def get_book(path: BookPath): @@ -189,7 +188,6 @@ from typing import Optional from pydantic import BaseModel, Field from flask_openapi3 import APIBlueprint, OpenAPI -from flask_openapi3 import HTTPBearer from flask_openapi3 import Tag, Info info = Info(title='book API', version='1.0.0') @@ -238,7 +236,7 @@ def get_book(): return {"code": 0, "message": "ok"} -@api.post('/book', extra_responses={"200": {"content": {"text/csv": {"schema": {"type": "string"}}}}}) +@api.post('/book', responses={"201": {"content": {"text/csv": {"schema": {"type": "string"}}}}}) def create_book(body: BookBody): assert body.age == 3 return {"code": 0, "message": "ok"} diff --git a/docs/Tutorial/Openapi_extra.md b/docs/Tutorial/Openapi_extra.md new file mode 100644 index 00000000..b8a98a47 --- /dev/null +++ b/docs/Tutorial/Openapi_extra.md @@ -0,0 +1,105 @@ +*New in v2.4.0* + +The [BaseModel](https://docs.pydantic.dev/latest/usage/models/) in [Pydantic](https://github.com/pydantic/pydantic) +supports some custom configurations([Model Config](https://docs.pydantic.dev/latest/usage/model_config/)), +so we can use the `openapi_extra` to extend OpenAPI Specification. + +The `openapi_extra` will be merged with the automatically generated OpenAPI schema. + +## form + +```python +class UploadFilesForm(BaseModel): + file: FileStorage + str_list: List[str] + + class Config: + openapi_extra = { + # "example": {"a": 123}, + "examples": { + "Example 01": { + "summary": "An example", + "value": { + "file": "Example-01.jpg", + "str_list": ["a", "b", "c"] + } + }, + "Example 02": { + "summary": "Another example", + "value": { + "str_list": ["1", "2", "3"] + } + } + } + } +``` + +Effect in Redoc: + +![](../assets/Snipaste_2023-06-02_11-05-11.png) + +## body + +```python +class BookBody(BaseModel): + age: int + author: str + + class Config: + openapi_extra = { + "description": "This is post RequestBody", + "example": {"age": 12, "author": "author1"}, + "examples": { + "example1": { + "summary": "example summary1", + "description": "example description1", + "value": { + "age": 24, + "author": "author2" + } + }, + "example2": { + "summary": "example summary2", + "description": "example description2", + "value": { + "age": 48, + "author": "author3" + } + } + + }} +``` + +Effect in swagger: + +![](../assets/Snipaste_2023-06-02_11-06-59.png) + +## responses + +```python +class Message(BaseModel): + message: str = Field(..., description="The message") + + class Config: + openapi_extra = { + # "example": {"message": "aaa"}, + "examples": { + "example1": { + "summary": "example1 summary", + "value": { + "message": "bbb" + } + }, + "example2": { + "summary": "example2 summary", + "value": { + "message": "ccc" + } + } + } + } +``` + +Effect in swagger: + +![](../assets/Snipaste_2023-06-02_11-08-40.png) \ No newline at end of file diff --git a/docs/Tutorial/Operation.md b/docs/Tutorial/Operation.md index 421af14d..f8c4b113 100644 --- a/docs/Tutorial/Operation.md +++ b/docs/Tutorial/Operation.md @@ -127,6 +127,12 @@ def create_book(body: BookBody): ## extra_form +*new in v2.4.0* + +!!! Deprecated-Warning warning + + `extra_form` will be deprecated in v3.x, please use `openapi_extra` instead. + *new in v2.1.0* Extra form information can be provided using `extra_form` as in the following sample: @@ -148,6 +154,12 @@ def create_book(body: BookForm): ## extra_body +*new in v2.4.0* + +!!! Deprecated-Warning warning + + `extra_body` will be deprecated in v3.x, please use `openapi_extra` instead. + *new in v2.1.0* Extra body information can be provided using `extra_body` as in the following sample: diff --git a/docs/Tutorial/Response.md b/docs/Tutorial/Response.md index 0849a012..5fc0b9f9 100644 --- a/docs/Tutorial/Response.md +++ b/docs/Tutorial/Response.md @@ -15,7 +15,13 @@ class BookResponse(BaseModel): data: BookBodyWithID -@app.get('/book/', tags=[book_tag], responses={"200": BookResponse}) +@app.get('/book/', + tags=[book_tag], + responses={ + "200": BookResponse, + # Version 2.4.0 starts supporting response for dictionary types + "201": {"content": {"text/csv": {"schema": {"type": "string"}}}} + }) def get_book(path: BookPath, query: BookBody): """get a book get book by id, age or author @@ -26,9 +32,14 @@ def get_book(path: BookPath, query: BookBody): ![image-20210526104627124](../assets/image-20210526104627124.png) - ## extra_responses +*New in v2.4.0* + +!!! Deprecated-Warning warning + + `extra_responses` have been merged into the `responses`, and `extra_responses` will be deprecated in v3.x. + *New in v1.0.0* You can pass to your path operation decorators a parameter `extra_responses`. @@ -43,14 +54,14 @@ Like this: '/book/', tags=[book_tag], responses={"200": BookResponse}, - extra_responses={"200": {"content": {"text/csv": {"schema": {"type": "string"}}}}}, + extra_responses={"201": {"content": {"text/csv": {"schema": {"type": "string"}}}}}, security=security ) def get_book(path: BookPath): ... -@api.post('/book', extra_responses={"200": {"content": {"text/csv": {"schema": {"type": "string"}}}}}) +@api.post('/book', extra_responses={"201": {"content": {"text/csv": {"schema": {"type": "string"}}}}}) def create_book(body: BookBody): ... ``` diff --git a/docs/Tutorial/Specification.md b/docs/Tutorial/Specification.md index 65457fdd..80de9525 100644 --- a/docs/Tutorial/Specification.md +++ b/docs/Tutorial/Specification.md @@ -252,6 +252,14 @@ def endpoint(): You can also use [responses ](./Response.md#responses) and [extra_responses](./Response.md#extra_responses) in your api. +*New in v2.4.0* + +!!! Deprecated-Warning warning + + `extra_responses` have been merged into the `responses`, and `extra_responses` will be deprecated in v3.x. + + + ## doc_ui You can pass `doc_ui=False` to disable the `OpenAPI spec` when init `OpenAPI `. diff --git a/docs/assets/Snipaste_2023-06-02_11-05-11.png b/docs/assets/Snipaste_2023-06-02_11-05-11.png new file mode 100644 index 00000000..a171bcc1 Binary files /dev/null and b/docs/assets/Snipaste_2023-06-02_11-05-11.png differ diff --git a/docs/assets/Snipaste_2023-06-02_11-06-59.png b/docs/assets/Snipaste_2023-06-02_11-06-59.png new file mode 100644 index 00000000..5c698f4b Binary files /dev/null and b/docs/assets/Snipaste_2023-06-02_11-06-59.png differ diff --git a/docs/assets/Snipaste_2023-06-02_11-08-40.png b/docs/assets/Snipaste_2023-06-02_11-08-40.png new file mode 100644 index 00000000..a6aa0aa2 Binary files /dev/null and b/docs/assets/Snipaste_2023-06-02_11-08-40.png differ diff --git a/flask_openapi3/models/common.py b/flask_openapi3/models/common.py index 0f998705..3dff7441 100644 --- a/flask_openapi3/models/common.py +++ b/flask_openapi3/models/common.py @@ -1,10 +1,13 @@ # -*- coding: utf-8 -*- # @Author : llc # @Time : 2021/4/30 11:46 +import warnings from typing import Optional, List, Any, Union, Dict from pydantic import BaseModel, Field +warnings.simplefilter("once") + class ExternalDocumentation(BaseModel): description: Optional[str] = None @@ -129,3 +132,7 @@ class ExtraRequestBody(BaseModel): example: Optional[Any] = None examples: Optional[Dict[str, Union[Example, Reference]]] = None encoding: Optional[Dict[str, Encoding]] = None + + def __new__(cls, *args, **kwargs): + warnings.warn(f"""\n{cls.__name__} will be deprecated in v3.x.""", DeprecationWarning) + return super().__new__(cls) diff --git a/flask_openapi3/utils.py b/flask_openapi3/utils.py index 929b0e6c..a5a25eb0 100644 --- a/flask_openapi3/utils.py +++ b/flask_openapi3/utils.py @@ -314,7 +314,7 @@ def get_responses( _content["application/json"].examples = model_config.openapi_extra.get("examples") # type: ignore _content["application/json"].encoding = model_config.openapi_extra.get("encoding") # type: ignore if model_config.openapi_extra.get("content"): - _responses[key].content.update(model_config.openapi_extra.get("content")) + _responses[key].content.update(model_config.openapi_extra.get("content")) # type: ignore _schemas[response.__name__] = Schema(**schema) definitions = schema.get("definitions") diff --git a/mkdocs.yml b/mkdocs.yml index 10681601..13ceaae4 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -89,6 +89,7 @@ nav: - Operation: Tutorial/Operation.md - Request: Tutorial/Request.md - Response: Tutorial/Response.md + - OpenAPI Extra: Tutorial/Openapi_extra.md - UI Templates: Tutorial/UI_Templates.md - JSON: Tutorial/JSON.md - Example: Example.md