Skip to content

Commit

Permalink
Merge extra_responses to responses and deprecate extra_responses
Browse files Browse the repository at this point in the history
  • Loading branch information
luolingchun committed Jun 2, 2023
1 parent f4e4b1b commit 7d41436
Show file tree
Hide file tree
Showing 11 changed files with 86 additions and 61 deletions.
2 changes: 1 addition & 1 deletion examples/api_blueprint_demo.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,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={"200": {"content": {"text/csv": {"schema": {"type": "string"}}}}})
def create_book(body: BookBody):
assert body.age == 3
return {"code": 0, "message": "ok"}
Expand Down
5 changes: 3 additions & 2 deletions examples/response_demo.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
from flask_openapi3 import Info
from flask_openapi3 import OpenAPI, APIBlueprint

app = OpenAPI(__name__, info=Info(title="Hello API", version="1.0.0"), )
app = OpenAPI(__name__, info=Info(title="Hello API", version="1.0.0"))

bp = APIBlueprint("Hello BP", __name__)

Expand Down Expand Up @@ -43,7 +43,8 @@ class Config:
}


@bp.get("/hello/<string:name>", responses={"200": Message})
@bp.get("/hello/<string:name>",
responses={"200": Message, "201": {"content": {"text/csv": {"schema": {"type": "string"}}}}})
def hello(path: HelloPath):
message = {"message": f"""Hello {path.name}!"""}

Expand Down
2 changes: 0 additions & 2 deletions examples/rest_demo.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,9 +95,7 @@ class BookResponse(BaseModel):
external_docs=ExternalDocumentation(
url="https://www.openapis.org/",
description="Something great got better, get excited!"),
responses={"200": BookResponse},
extra_responses={"200": {"content": {"text/csv": {"schema": {"type": "string"}}}}},
security=security,
servers=[Server(url="https://www.openapis.org/", description="openapi")]
)
Expand Down
10 changes: 5 additions & 5 deletions flask_openapi3/blueprint.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
# @Time : 2022/4/1 16:54
import re
from copy import deepcopy
from typing import Optional, List, Dict, Any, Type, Callable, Tuple
from typing import Optional, List, Dict, Any, Type, Callable, Tuple, Union

from flask import Blueprint
from pydantic import BaseModel
Expand All @@ -25,7 +25,7 @@ def __init__(
*,
abp_tags: Optional[List[Tag]] = None,
abp_security: Optional[List[Dict[str, List[str]]]] = None,
abp_responses: Optional[Dict[str, Optional[Type[BaseModel]]]] = None,
abp_responses: Optional[Dict[str, Union[Type[BaseModel], Dict[Any, Any], None]]] = None,
doc_ui: bool = True,
operation_id_callback: Callable = get_operation_id_for_path,
**kwargs: Any
Expand All @@ -39,7 +39,7 @@ def __init__(
``__name__``. This helps locate the ``root_path`` for the blueprint.
abp_tags: APIBlueprint tags for every api
abp_security: APIBlueprint security for every api
abp_responses: APIBlueprint response model
abp_responses: API responses, should be BaseModel, dict or None.
doc_ui: Add openapi document UI(swagger, rapidoc and redoc). Defaults to True.
operation_id_callback: Callback function for custom operation_id generation.
Receives name (str), path (str) and method (str) parameters.
Expand Down Expand Up @@ -92,7 +92,7 @@ def _do_decorator(
operation_id: Optional[str] = None,
extra_form: Optional[ExtraRequestBody] = None,
extra_body: Optional[ExtraRequestBody] = None,
responses: Optional[Dict[str, Optional[Type[BaseModel]]]] = None,
responses: Optional[Dict[str, Union[Type[BaseModel], Dict[Any, Any], None]]] = None,
extra_responses: Optional[Dict[str, Dict]] = None,
deprecated: Optional[bool] = None,
security: Optional[List[Dict[str, List[Any]]]] = None,
Expand All @@ -113,7 +113,7 @@ def _do_decorator(
operation_id: Unique string used to identify the operation.
extra_form: Extra information describing the request body(application/form).
extra_body: Extra information describing the request body(application/json).
responses: response's model must be pydantic BaseModel.
responses: API responses, should be BaseModel, dict or None.
extra_responses: Extra information for responses.
deprecated: Declares this operation to be deprecated.
security: A declaration of which security mechanisms can be used for this operation.
Expand Down
8 changes: 4 additions & 4 deletions flask_openapi3/openapi.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ def __init__(
info: Optional[Info] = None,
security_schemes: Optional[Dict[str, Union[SecurityScheme, Dict[str, Any]]]] = None,
oauth_config: Optional[OAuthConfig] = None,
responses: Optional[Dict[str, Optional[Type[BaseModel]]]] = None,
responses: Optional[Dict[str, Union[Type[BaseModel], Dict[Any, Any], None]]] = None,
doc_ui: bool = True,
doc_expansion: str = "list",
doc_prefix: str = "/openapi",
Expand All @@ -56,7 +56,7 @@ def __init__(
security_schemes: See https://spec.openapis.org/oas/v3.0.3#security-scheme-object
oauth_config: OAuth 2.0 configuration,
see https://github.com/swagger-api/swagger-ui/blob/master/docs/usage/oauth2.md
responses: OpenAPI response model
responses: API responses, should be BaseModel, dict or None.
doc_ui: Add openapi document UI(swagger and redoc). Defaults to True.
doc_expansion: String=["list"*, "full", "none"].
Controls the default expansion setting for the operations and tags.
Expand Down Expand Up @@ -220,7 +220,7 @@ def _do_decorator(
operation_id: Optional[str] = None,
extra_form: Optional[ExtraRequestBody] = None,
extra_body: Optional[ExtraRequestBody] = None,
responses: Optional[Dict[str, Optional[Type[BaseModel]]]] = None,
responses: Optional[Dict[str, Union[Type[BaseModel], Dict[Any, Any], None]]] = None,
extra_responses: Optional[Dict[str, Dict]] = None,
deprecated: Optional[bool] = None,
security: Optional[List[Dict[str, List[Any]]]] = None,
Expand All @@ -241,7 +241,7 @@ def _do_decorator(
operation_id: Unique string used to identify the operation.
extra_form: Extra information describing the request body(application/form).
extra_body: Extra information describing the request body(application/json).
responses: response's model must be pydantic BaseModel.
responses: API responses, should be BaseModel, dict or None.
extra_responses: Extra information for responses.
deprecated: Declares this operation to be deprecated.
security: A declaration of which security mechanisms can be used for this operation.
Expand Down
44 changes: 32 additions & 12 deletions flask_openapi3/scaffold.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import warnings
from abc import ABC
from functools import wraps
from typing import Callable, List, Optional, Dict, Type, Any, Tuple
from typing import Callable, List, Optional, Dict, Type, Any, Tuple, Union

from flask.scaffold import Scaffold
from flask.wrappers import Response
Expand Down Expand Up @@ -48,7 +48,7 @@ def _do_decorator(
operation_id: Optional[str] = None,
extra_form: Optional[ExtraRequestBody] = None,
extra_body: Optional[ExtraRequestBody] = None,
responses: Optional[Dict[str, Optional[Type[BaseModel]]]] = None,
responses: Optional[Dict[str, Union[Type[BaseModel], Dict[Any, Any], None]]] = None,
extra_responses: Optional[Dict[str, dict]] = None,
deprecated: Optional[bool] = None,
security: Optional[List[Dict[str, List[Any]]]] = None,
Expand Down Expand Up @@ -146,7 +146,7 @@ def get(
operation_id: Optional[str] = None,
extra_form: Optional[ExtraRequestBody] = None,
extra_body: Optional[ExtraRequestBody] = None,
responses: Optional[Dict[str, Optional[Type[BaseModel]]]] = None,
responses: Optional[Dict[str, Union[Type[BaseModel], Dict[Any, Any], None]]] = None,
extra_responses: Optional[Dict[str, dict]] = None,
deprecated: Optional[bool] = None,
security: Optional[List[Dict[str, List[Any]]]] = None,
Expand All @@ -168,7 +168,7 @@ def get(
operation_id: Unique string used to identify the operation.
extra_form: Extra information describing the request body(application/form).
extra_body: Extra information describing the request body(application/json).
responses: response's model must be pydantic BaseModel.
responses: API responses, should be BaseModel, dict or None.
extra_responses: Extra information for responses.
deprecated: Declares this operation to be deprecated.
security: A declaration of which security mechanisms can be used for this operation.
Expand All @@ -185,6 +185,10 @@ def get(
warnings.warn(
"""`extra_body` will be deprecated in v3.x, please use `openapi_extra` instead.""",
DeprecationWarning)
if extra_responses is not None:
warnings.warn(
"""`extra_responses` will be deprecated in v3.x, please use `responses` instead.""",
DeprecationWarning)

def decorator(func) -> Callable:
header, cookie, path, query, form, body = \
Expand Down Expand Up @@ -227,7 +231,7 @@ def post(
operation_id: Optional[str] = None,
extra_form: Optional[ExtraRequestBody] = None,
extra_body: Optional[ExtraRequestBody] = None,
responses: Optional[Dict[str, Optional[Type[BaseModel]]]] = None,
responses: Optional[Dict[str, Union[Type[BaseModel], Dict[Any, Any], None]]] = None,
extra_responses: Optional[Dict[str, dict]] = None,
deprecated: Optional[bool] = None,
security: Optional[List[Dict[str, List[Any]]]] = None,
Expand All @@ -249,7 +253,7 @@ def post(
operation_id: Unique string used to identify the operation.
extra_form: Extra information describing the request body(application/form).
extra_body: Extra information describing the request body(application/json).
responses: response's model must be pydantic BaseModel.
responses: API responses, should be BaseModel, dict or None.
extra_responses: Extra information for responses.
deprecated: Declares this operation to be deprecated.
security: A declaration of which security mechanisms can be used for this operation.
Expand All @@ -265,6 +269,10 @@ def post(
warnings.warn(
"""`extra_body` will be deprecated in v3.x, please use `openapi_extra` instead.""",
DeprecationWarning)
if extra_responses is not None:
warnings.warn(
"""`extra_responses` will be deprecated in v3.x, please use `responses` instead.""",
DeprecationWarning)

def decorator(func) -> Callable:
header, cookie, path, query, form, body = \
Expand Down Expand Up @@ -307,7 +315,7 @@ def put(
operation_id: Optional[str] = None,
extra_form: Optional[ExtraRequestBody] = None,
extra_body: Optional[ExtraRequestBody] = None,
responses: Optional[Dict[str, Optional[Type[BaseModel]]]] = None,
responses: Optional[Dict[str, Union[Type[BaseModel], Dict[Any, Any], None]]] = None,
extra_responses: Optional[Dict[str, dict]] = None,
deprecated: Optional[bool] = None,
security: Optional[List[Dict[str, List[Any]]]] = None,
Expand All @@ -329,7 +337,7 @@ def put(
operation_id: Unique string used to identify the operation.
extra_form: Extra information describing the request body(application/form).
extra_body: Extra information describing the request body(application/json).
responses: response's model must be pydantic BaseModel.
responses: API responses, should be BaseModel, dict or None.
extra_responses: Extra information for responses.
deprecated: Declares this operation to be deprecated.
security: A declaration of which security mechanisms can be used for this operation.
Expand All @@ -345,6 +353,10 @@ def put(
warnings.warn(
"""`extra_body` will be deprecated in v3.x, please use `openapi_extra` instead.""",
DeprecationWarning)
if extra_responses is not None:
warnings.warn(
"""`extra_responses` will be deprecated in v3.x, please use `responses` instead.""",
DeprecationWarning)

def decorator(func) -> Callable:
header, cookie, path, query, form, body = \
Expand Down Expand Up @@ -387,7 +399,7 @@ def delete(
operation_id: Optional[str] = None,
extra_form: Optional[ExtraRequestBody] = None,
extra_body: Optional[ExtraRequestBody] = None,
responses: Optional[Dict[str, Optional[Type[BaseModel]]]] = None,
responses: Optional[Dict[str, Union[Type[BaseModel], Dict[Any, Any], None]]] = None,
extra_responses: Optional[Dict[str, dict]] = None,
deprecated: Optional[bool] = None,
security: Optional[List[Dict[str, List[Any]]]] = None,
Expand All @@ -409,7 +421,7 @@ def delete(
operation_id: Unique string used to identify the operation.
extra_form: Extra information describing the request body(application/form).
extra_body: Extra information describing the request body(application/json).
responses: response's model must be pydantic BaseModel.
responses: API responses, should be BaseModel, dict or None.
extra_responses: Extra information for responses.
deprecated: Declares this operation to be deprecated.
security: A declaration of which security mechanisms can be used for this operation.
Expand All @@ -425,6 +437,10 @@ def delete(
warnings.warn(
"""`extra_body` will be deprecated in v3.x, please use `openapi_extra` instead.""",
DeprecationWarning)
if extra_responses is not None:
warnings.warn(
"""`extra_responses` will be deprecated in v3.x, please use `responses` instead.""",
DeprecationWarning)

def decorator(func) -> Callable:
header, cookie, path, query, form, body = \
Expand Down Expand Up @@ -467,7 +483,7 @@ def patch(
operation_id: Optional[str] = None,
extra_form: Optional[ExtraRequestBody] = None,
extra_body: Optional[ExtraRequestBody] = None,
responses: Optional[Dict[str, Optional[Type[BaseModel]]]] = None,
responses: Optional[Dict[str, Union[Type[BaseModel], Dict[Any, Any], None]]] = None,
extra_responses: Optional[Dict[str, dict]] = None,
deprecated: Optional[bool] = None,
security: Optional[List[Dict[str, List[Any]]]] = None,
Expand All @@ -489,7 +505,7 @@ def patch(
operation_id: Unique string used to identify the operation.
extra_form: Extra information describing the request body(application/form).
extra_body: Extra information describing the request body(application/json).
responses: response's model must be pydantic BaseModel.
responses: API responses, should be BaseModel, dict or None.
extra_responses: Extra information for responses.
deprecated: Declares this operation to be deprecated.
security: A declaration of which security mechanisms can be used for this operation.
Expand All @@ -505,6 +521,10 @@ def patch(
warnings.warn(
"""`extra_body` will be deprecated in v3.x, please use `openapi_extra` instead.""",
DeprecationWarning)
if extra_responses is not None:
warnings.warn(
"""`extra_responses` will be deprecated in v3.x, please use `responses` instead.""",
DeprecationWarning)

def decorator(func) -> Callable:
header, cookie, path, query, form, body = \
Expand Down
25 changes: 13 additions & 12 deletions flask_openapi3/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

import inspect
import re
from typing import get_type_hints, Dict, Type, Callable, List, Tuple, Optional, Any
from typing import get_type_hints, Dict, Type, Callable, List, Tuple, Optional, Any, Union

from pydantic import BaseModel

Expand Down Expand Up @@ -248,13 +248,13 @@ def parse_body(


def get_responses(
responses: dict,
responses: Optional[Dict[str, Union[Type[BaseModel], Dict[Any, Any], None]]],
extra_responses: Dict[str, dict],
components_schemas: dict,
operation: Operation
) -> None:
"""
:param responses: Dict[str, BaseModel]
:param responses: API responses, should be BaseModel, dict or None.
:param extra_responses: Dict[str, dict]
:param components_schemas: `models.component.py` `Components.schemas`
:param operation: `models.path.py` Operation
Expand All @@ -281,8 +281,13 @@ def get_responses(
)
_schemas[UnprocessableEntity.__name__] = Schema(**UnprocessableEntity.schema())
for key, response in responses.items():
# Verify that the response is a class and that class is a subclass of `pydantic.BaseModel`
if inspect.isclass(response) and issubclass(response, BaseModel):
if response is None:
# Verify that if the response is None, because http status code "204" means return "No Content"
_responses[key] = Response(description=HTTP_STATUS.get(key, ""))
continue
if isinstance(response, dict):
_responses[key] = response # type: ignore
else:
schema = response.schema(ref_template=OPENAPI3_REF_TEMPLATE)
_responses[key] = Response(
description=HTTP_STATUS.get(key, ""),
Expand All @@ -308,19 +313,15 @@ def get_responses(
_content["application/json"].example = model_config.openapi_extra.get("example") # type: ignore
_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"))

_schemas[response.__name__] = Schema(**schema)
definitions = schema.get("definitions")
if definitions:
for name, value in definitions.items():
_schemas[name] = Schema(**value)
# Verify that if the response is None, because http status code "204" means return "No Content"
elif response is None:
_responses[key] = Response(
description=HTTP_STATUS.get(key, ""),
)
else:
raise TypeError(f"{response} is invalid `pydantic.BaseModel`.")

# handle extra_responses
for key, value in extra_responses.items():
# key "200" value {"content":{"text/csv":{"schema":{"type": "string"}}}}
Expand Down
Loading

0 comments on commit 7d41436

Please sign in to comment.