Skip to content

Commit

Permalink
Register a validation error response
Browse files Browse the repository at this point in the history
Add new config:

VALIDATION_ERROR_CODE (defaults to 422)
VALIDATION_ERROR_DESCRIPTION (defaults to 'Validation error')
  • Loading branch information
greyli committed Feb 20, 2021
1 parent f992d99 commit c3d7c3b
Show file tree
Hide file tree
Showing 4 changed files with 86 additions and 3 deletions.
16 changes: 16 additions & 0 deletions apiflask/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
HTTPTokenAuth = None

from .exceptions import ValidationError
from .schemas import validation_error_response_schema


class APIFlask(Flask):
Expand All @@ -34,6 +35,8 @@ class APIFlask(Flask):
'OPENAPI_TAGS': None,
'200_RESPONSE_DESCRIPTION': 'Successful response',
'204_RESPONSE_DESCRIPTION': 'Empty response',
'VALIDATION_ERROR_CODE': 400,
'VALIDATION_ERROR_DESCRIPTION': 'Validation error'
}
)

Expand Down Expand Up @@ -356,6 +359,19 @@ def resolver(schema):
security[view_func._spec['auth']]: view_func._spec[
'roles']
}]

if view_func._spec.get('body') or view_func._spec.get('args'):
code = self.config['VALIDATION_ERROR_CODE']
operation['responses'][code] = {
'content': {
'application/json': {
'schema': validation_error_response_schema
}
}
}
operation['responses'][code]['description'] = \
self.config['VALIDATION_ERROR_DESCRIPTION']

operations[method.lower()] = operation

path_arguments = re.findall(r'<(([^:]+:)?([^>]+))>', rule.rule)
Expand Down
5 changes: 2 additions & 3 deletions apiflask/decorators.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,17 @@
from functools import wraps

from flask import Response, jsonify
from flask import Response, jsonify, current_app
from webargs.flaskparser import FlaskParser as BaseFlaskParser

from apiflask.exceptions import ValidationError


class FlaskParser(BaseFlaskParser):
DEFAULT_VALIDATION_STATUS = 400

def handle_error(self, error, req, schema, *, error_status_code,
error_headers):
raise ValidationError(
error_status_code or self.DEFAULT_VALIDATION_STATUS,
error_status_code or current_app.config['VALIDATION_ERROR_CODE'],
error.messages)


Expand Down
23 changes: 23 additions & 0 deletions apiflask/schemas.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,28 @@
from flask_marshmallow.schema import Schema # noqa: F401


validation_error_response_schema = {
"type": "object",
"properties": {
"messages": {
"type": "object",
"properties": {
"<location>": {
"type": "object",
"properties": {
"<field_name>": {
"type": "array",
"items": {
"type": "string"
}
}
}
}
}
}
}
}


class PaginationSchema(Schema):
pass
45 changes: 45 additions & 0 deletions tests/test_apiflask.py
Original file line number Diff line number Diff line change
Expand Up @@ -532,3 +532,48 @@ def no_schema():
'204']['description'] == 'Nothing'
assert rv.json['paths']['/bar']['get']['responses'][
'200']['description'] == 'It works'

def test_register_validation_error_response(self):
app = self.create_app()
error_code = str(app.config['VALIDATION_ERROR_CODE'])

@app.post('/foo')
@body(Schema)
def foo():
pass

@app.get('/bar')
@arguments(Schema)
def bar():
pass

client = app.test_client()
rv = client.get('/openapi.json')
assert rv.status_code == 200
validate_spec(rv.json)
assert rv.json['paths']['/foo']['post']['responses'][
error_code] is not None
assert rv.json['paths']['/foo']['post']['responses'][
error_code]['description'] == 'Validation error'
assert rv.json['paths']['/bar']['get']['responses'][
error_code] is not None
assert rv.json['paths']['/bar']['get']['responses'][
error_code]['description'] == 'Validation error'

def test_validation_error_config(self):
app = self.create_app()
app.config['VALIDATION_ERROR_CODE'] = 422
app.config['VALIDATION_ERROR_DESCRIPTION'] = 'Bad'

@app.post('/foo')
@body(Schema)
def foo():
pass

client = app.test_client()
rv = client.get('/openapi.json')
assert rv.status_code == 200
validate_spec(rv.json)
assert rv.json['paths']['/foo']['post']['responses']['422'] is not None
assert rv.json['paths']['/foo']['post']['responses'][
'422']['description'] == 'Bad'

0 comments on commit c3d7c3b

Please sign in to comment.