Skip to content

ISSUE-220 allow for response validation #225

New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 38 additions & 1 deletion docs/Usage/Response.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,46 @@ def hello(path: HelloPath):
return response
```


![image-20210526104627124](../assets/image-20210526104627124.png)

## Validate responses

By default, responses are not validated. If you need to validate responses, set validate_responses to True. Here are
several ways to achieve this:

```python
# 1. APP level
app = OpenAPI(__name__, validate_response=True)

# 2. APIBlueprint level
api = APIBlueprint(__name__, validate_response=True)

# 3. APIView level
@api_view.route("/test")
class TestAPI:
@api_view.doc(responses={201: Response}, validate_response=True)
def post(self):
...

# 4. api level
@app.post("/test", responses={201: Response}, validate_response=True)
def endpoint_test(body: BaseRequest):
...
```

You can also customize the default behavior of response validation by using a custom `validate_response_callback`.

```python

def validate_response_callback(response: Any, responses: Optional[ResponseDict] = None) -> Any:

# do something

return response

app = OpenAPI(__name__, validate_response=True, validate_response_callback=validate_response_callback)
```

## More information about OpenAPI responses

- [OpenAPI Responses Object](https://spec.openapis.org/oas/v3.1.0#responses-object), it includes the Response Object.
Expand Down
5 changes: 5 additions & 0 deletions flask_openapi3/blueprint.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ def __init__(
abp_responses: Optional[ResponseDict] = None,
doc_ui: bool = True,
operation_id_callback: Callable = get_operation_id_for_path,
validate_response: Optional[bool] = None,
**kwargs: Any
) -> None:
"""
Expand All @@ -49,6 +50,7 @@ def __init__(
operation_id_callback: Callback function for custom operation_id generation.
Receives name (str), path (str) and method (str) parameters.
Defaults to `get_operation_id_for_path` from utils
validate_response: Verify the response body.
**kwargs: Flask Blueprint kwargs
"""
super(APIBlueprint, self).__init__(name, import_name, **kwargs)
Expand All @@ -71,6 +73,9 @@ def __init__(
# Set the operation ID callback function
self.operation_id_callback: Callable = operation_id_callback

# Verify the response body
self.validate_response = validate_response

def register_api(self, api: "APIBlueprint") -> None:
"""Register a nested APIBlueprint"""

Expand Down
4 changes: 2 additions & 2 deletions flask_openapi3/models/components.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
# @Time : 2023/7/4 9:36
from typing import Optional, Union, Any

from pydantic import BaseModel, Field
from pydantic import BaseModel

from .callback import Callback
from .example import Example
Expand All @@ -23,7 +23,7 @@ class Components(BaseModel):
https://spec.openapis.org/oas/v3.1.0#components-object
"""

schemas: Optional[dict[str, Union[Reference, Schema]]] = Field(None)
schemas: Optional[dict[str, Union[Reference, Schema]]] = None
responses: Optional[dict[str, Union[Response, Reference]]] = None
parameters: Optional[dict[str, Union[Parameter, Reference]]] = None
examples: Optional[dict[str, Union[Example, Reference]]] = None
Expand Down
9 changes: 9 additions & 0 deletions flask_openapi3/openapi.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
from .utils import parse_and_store_tags
from .utils import parse_method
from .utils import parse_parameters
from .utils import run_validate_response
from .view import APIView


Expand All @@ -63,6 +64,8 @@ def __init__(
doc_ui: bool = True,
doc_prefix: str = "/openapi",
doc_url: str = "/openapi.json",
validate_response: Optional[bool] = None,
validate_response_callback: Callable = run_validate_response,
**kwargs: Any
) -> None:
"""
Expand Down Expand Up @@ -95,6 +98,8 @@ def __init__(
Defaults to "/openapi".
doc_url: URL for accessing the OpenAPI specification document in JSON format.
Defaults to "/openapi.json".
validate_response: Verify the response body.
validate_response_callback: Validation and return response.
**kwargs: Additional kwargs to be passed to Flask.
"""
super(OpenAPI, self).__init__(import_name, **kwargs)
Expand Down Expand Up @@ -144,6 +149,10 @@ def __init__(
# Add the OpenAPI command
self.cli.add_command(openapi_command) # type: ignore

# Verify the response body
self.validate_response = validate_response
self.validate_response_callback = validate_response_callback

# Initialize specification JSON
self.spec_json: dict = {}
self.spec = APISpec(
Expand Down
Loading