52 lines
1.6 KiB
Python
52 lines
1.6 KiB
Python
|
"""Decorator for view methods to help with data validation."""
|
||
|
import asyncio
|
||
|
from functools import wraps
|
||
|
import logging
|
||
|
|
||
|
import voluptuous as vol
|
||
|
|
||
|
_LOGGER = logging.getLogger(__name__)
|
||
|
|
||
|
|
||
|
class RequestDataValidator:
|
||
|
"""Decorator that will validate the incoming data.
|
||
|
|
||
|
Takes in a voluptuous schema and adds 'post_data' as
|
||
|
keyword argument to the function call.
|
||
|
|
||
|
Will return a 400 if no JSON provided or doesn't match schema.
|
||
|
"""
|
||
|
|
||
|
def __init__(self, schema, allow_empty=False):
|
||
|
"""Initialize the decorator."""
|
||
|
self._schema = schema
|
||
|
self._allow_empty = allow_empty
|
||
|
|
||
|
def __call__(self, method):
|
||
|
"""Decorate a function."""
|
||
|
@asyncio.coroutine
|
||
|
@wraps(method)
|
||
|
def wrapper(view, request, *args, **kwargs):
|
||
|
"""Wrap a request handler with data validation."""
|
||
|
data = None
|
||
|
try:
|
||
|
data = yield from request.json()
|
||
|
except ValueError:
|
||
|
if not self._allow_empty or \
|
||
|
(yield from request.content.read()) != b'':
|
||
|
_LOGGER.error('Invalid JSON received.')
|
||
|
return view.json_message('Invalid JSON.', 400)
|
||
|
data = {}
|
||
|
|
||
|
try:
|
||
|
kwargs['data'] = self._schema(data)
|
||
|
except vol.Invalid as err:
|
||
|
_LOGGER.error('Data does not match schema: %s', err)
|
||
|
return view.json_message(
|
||
|
'Message format incorrect: {}'.format(err), 400)
|
||
|
|
||
|
result = yield from method(view, request, *args, **kwargs)
|
||
|
return result
|
||
|
|
||
|
return wrapper
|