"""Decorator for view methods to help with data validation.""" 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.""" @wraps(method) async def wrapper(view, request, *args, **kwargs): """Wrap a request handler with data validation.""" data = None try: data = await request.json() except ValueError: if not self._allow_empty or \ (await 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 = await method(view, request, *args, **kwargs) return result return wrapper