Skip to content

Commit

Permalink
feat: support user custom error (#398)
Browse files Browse the repository at this point in the history
* feat: support user custom error

Signed-off-by: Keming <[email protected]>

* legacy validator

Signed-off-by: Keming <[email protected]>

---------

Signed-off-by: Keming <[email protected]>
  • Loading branch information
kemingy authored Dec 31, 2024
1 parent bca3067 commit 2a3f8af
Show file tree
Hide file tree
Showing 25 changed files with 489 additions and 25 deletions.
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@ import_test:

test: import_test
pip install -U -e .[email,flask,quart,falcon,starlette]
pytest tests -vv -rs
pytest tests -vv -rs --disable-warnings
pip install --force-reinstall 'pydantic[email]<2'
pytest tests -vv -rs
pytest tests -vv -rs --disable-warnings

update_snapshot:
@pytest --snapshot-update
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -489,8 +489,8 @@ if __name__ == "__main__":
> ValidationError: missing field for headers
The HTTP headers' keys in Flask are capitalized, in Falcon are upper cases, in Starlette are lower cases.
You can use [`pydantic.root_validators(pre=True)`](https://pydantic-docs.helpmanual.io/usage/validators/#root-validators) to change all the keys into lower cases or upper cases.
You can use [`pydantic.model_validator(mode="before")`](https://docs.pydantic.dev/dev/concepts/validators/#model-validators) to change all the keys into lower cases or upper cases.

> ValidationError: value is not a valid list for the query
Since there is no standard for HTTP queries with multiple values, it's hard to find a way to handle this for different web frameworks. So I suggest not to use list type in query until I find a suitable way to fix it.
Since there is no standard for HTTP queries with multiple values, it's hard to find a way to handle this for different web frameworks.
2 changes: 1 addition & 1 deletion docs/source/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@
source_suffix = [".rst", ".md"]
language = "en"
html_baseurl = "https://0b01001001.github.io/spectree/"
html_extra_path = ['robots.txt']
html_extra_path = ["robots.txt"]

# -- Options for HTML output -------------------------------------------------

Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "spectree"
version = "1.4.2"
version = "1.4.3"
dynamic = []
description = "generate OpenAPI document and validate request&response with Python annotations."
readme = "README.md"
Expand Down
24 changes: 20 additions & 4 deletions spectree/plugins/falcon_plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,11 @@ def validate(
except (InternalValidationError, ValidationError) as err:
req_validation_error = err
_resp.status = f"{validation_error_status} Validation Error"
_resp.media = err.errors()
_resp.media = (
err.errors()
if isinstance(err, InternalValidationError)
else err.errors(include_context=False)
)

if self.config.annotations:
annotations = get_type_hints(func)
Expand All @@ -238,7 +242,11 @@ def validate(
except (InternalValidationError, ValidationError) as err:
resp_validation_error = err
_resp.status = HTTP_500
_resp.media = err.errors()
_resp.media = (
err.errors()
if isinstance(err, InternalValidationError)
else err.errors(include_context=False)
)
else:
_resp.media = response_validation_result.payload

Expand Down Expand Up @@ -320,7 +328,11 @@ async def validate(
except (InternalValidationError, ValidationError) as err:
req_validation_error = err
_resp.status = f"{validation_error_status} Validation Error"
_resp.media = err.errors()
_resp.media = (
err.errors()
if isinstance(err, InternalValidationError)
else err.errors(include_context=False)
)

if self.config.annotations:
annotations = get_type_hints(func)
Expand Down Expand Up @@ -348,7 +360,11 @@ async def validate(
except (InternalValidationError, ValidationError) as err:
resp_validation_error = err
_resp.status = HTTP_500
_resp.media = err.errors()
_resp.media = (
err.errors()
if isinstance(err, InternalValidationError)
else err.errors(include_context=False)
)
else:
_resp.media = response_validation_result.payload

Expand Down
12 changes: 10 additions & 2 deletions spectree/plugins/flask_plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,11 @@ def validate(
self.request_validation(request, query, json, form, headers, cookies)
except (InternalValidationError, ValidationError) as err:
req_validation_error = err
errors = err.errors() if isinstance(err, InternalValidationError) else err.errors(include_context=False)
errors = (
err.errors()
if isinstance(err, InternalValidationError)
else err.errors(include_context=False)
)
response = make_response(jsonify(errors), validation_error_status)

if self.config.annotations:
Expand Down Expand Up @@ -225,7 +229,11 @@ def validate(
response_payload=payload,
)
except (InternalValidationError, ValidationError) as err:
errors = err.errors() if isinstance(err, InternalValidationError) else err.errors(include_context=False)
errors = (
err.errors()
if isinstance(err, InternalValidationError)
else err.errors(include_context=False)
)
response = make_response(errors, 500)
resp_validation_error = err
else:
Expand Down
14 changes: 11 additions & 3 deletions spectree/plugins/quart_plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -197,9 +197,12 @@ async def validate(
)
except (InternalValidationError, ValidationError) as err:
req_validation_error = err
response = await make_response(
jsonify(err.errors()), validation_error_status
errors = (
err.errors()
if isinstance(err, InternalValidationError)
else err.errors(include_context=False)
)
response = await make_response(jsonify(errors), validation_error_status)

if self.config.annotations:
annotations = get_type_hints(func)
Expand Down Expand Up @@ -241,7 +244,12 @@ async def validate(
response_payload=payload,
)
except (InternalValidationError, ValidationError) as err:
response = await make_response(err.errors(), 500)
errors = (
err.errors()
if isinstance(err, InternalValidationError)
else err.errors(include_context=False)
)
response = await make_response(errors, 500)
resp_validation_error = err
else:
response = await make_response(
Expand Down
14 changes: 12 additions & 2 deletions spectree/plugins/starlette_plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,12 @@ async def validate(
)
except (InternalValidationError, ValidationError) as err:
req_validation_error = err
response = JSONResponse(err.errors(), validation_error_status)
response = JSONResponse(
err.errors()
if isinstance(err, InternalValidationError)
else err.errors(include_context=False),
validation_error_status,
)
except JSONDecodeError as err:
json_decode_error = err
self.logger.info(
Expand Down Expand Up @@ -162,7 +167,12 @@ async def validate(
response_payload=RawResponsePayload(payload=response.body),
)
except (InternalValidationError, ValidationError) as err:
response = JSONResponse(err.errors(), 500)
response = JSONResponse(
err.errors()
if isinstance(err, InternalValidationError)
else err.errors(include_context=False),
500,
)
resp_validation_error = err

after(request, response, resp_validation_error, instance)
Expand Down
2 changes: 1 addition & 1 deletion spectree/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -393,7 +393,7 @@ def flask_response_unpack(resp: Any) -> Tuple[Any, int, Dict[str, Any]]:
f"Invalid return tuple: {resp}, expect (body,), (body, status), "
"(body, headers), or (body, status, headers)."
)
return payload, status, headers
return payload, status, dict(headers)


def parse_resp(func: Any, naming_strategy: NamingStrategy = get_model_key):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,19 @@
"title": "Cookies",
"type": "object"
},
"CustomError.7068f62": {
"properties": {
"foo": {
"title": "Foo",
"type": "string"
}
},
"required": [
"foo"
],
"title": "CustomError",
"type": "object"
},
"FormFileUpload.7068f62": {
"properties": {
"file": {
Expand Down Expand Up @@ -167,6 +180,46 @@
},
"openapi": "3.1.0",
"paths": {
"/api/custom_error": {
"post": {
"description": "",
"operationId": "post__api_custom_error",
"parameters": [],
"requestBody": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/CustomError.7068f62"
}
}
}
},
"responses": {
"200": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/CustomError.7068f62"
}
}
},
"description": "OK"
},
"422": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ValidationError.6a07bef"
}
}
},
"description": "Unprocessable Content"
}
},
"summary": "on_post <POST>",
"tags": []
}
},
"/api/custom_serializer": {
"get": {
"description": "",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,19 @@
"title": "Cookies",
"type": "object"
},
"CustomError.7068f62": {
"properties": {
"foo": {
"title": "Foo",
"type": "string"
}
},
"required": [
"foo"
],
"title": "CustomError",
"type": "object"
},
"Form.7068f62": {
"properties": {
"limit": {
Expand Down Expand Up @@ -201,6 +214,46 @@
},
"openapi": "3.1.0",
"paths": {
"/api/custom_error": {
"post": {
"description": "",
"operationId": "post__api_custom_error",
"parameters": [],
"requestBody": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/CustomError.7068f62"
}
}
}
},
"responses": {
"200": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/CustomError.7068f62"
}
}
},
"description": "OK"
},
"422": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ValidationError.6a07bef"
}
}
},
"description": "Unprocessable Content"
}
},
"summary": "custom_error <POST>",
"tags": []
}
},
"/api/file_upload": {
"post": {
"description": "",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,19 @@
"title": "Cookies",
"type": "object"
},
"CustomError.7068f62": {
"properties": {
"foo": {
"title": "Foo",
"type": "string"
}
},
"required": [
"foo"
],
"title": "CustomError",
"type": "object"
},
"Form.7068f62": {
"properties": {
"limit": {
Expand Down Expand Up @@ -201,6 +214,46 @@
},
"openapi": "3.1.0",
"paths": {
"/api/custom_error": {
"post": {
"description": "",
"operationId": "post__api_custom_error",
"parameters": [],
"requestBody": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/CustomError.7068f62"
}
}
}
},
"responses": {
"200": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/CustomError.7068f62"
}
}
},
"description": "OK"
},
"422": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ValidationError.6a07bef"
}
}
},
"description": "Unprocessable Content"
}
},
"summary": "custom_error <POST>",
"tags": []
}
},
"/api/file_upload": {
"post": {
"description": "",
Expand Down
Loading

0 comments on commit 2a3f8af

Please sign in to comment.