"""Helper functions to use with handlers.""" import datetime from functools import wraps import json from flask import request import marshmallow from oto import response from oto.adaptors.flask import flaskify from werkzeug.exceptions import BadRequest, UnsupportedMediaType from conflict_manager.constants import error def parse_and_validate_request_json(schema): """Use to decorate handler with request JSON data. Used to parse, deserialize, and validate JSON request data with. Args: schema: Marshallow Schema to validate request json data with. Returns: callable: the wrapped function. """ def decorator(function): @wraps(function) def wrapper(*args, **kwargs): """Validate the request body against a validator. Returns: Response: error HTTP response, or continue to the calling function if successful. """ try: json = request.get_json() except BadRequest as e: return flaskify(response.create_error_response( error.ERROR_CODE_BAD_REQUEST, e.description)) except UnsupportedMediaType as e: return flaskify(response.create_error_response( error.ERROR_CODE_BAD_REQUEST, e.description)) # Marshmallow doesn't return validation errors unless the passed # in data type is a dict. if not isinstance(json, dict): return flaskify(response.create_error_response( error.ERROR_CODE_BAD_REQUEST, 'Request data must be a JSON object and include proper ' 'Content-Type header parameter.')) try: kwargs['json_data'] = schema().load(json) except marshmallow.ValidationError as err: return flaskify(response.create_error_response( code=error.ERROR_CODE_VALIDATION, message=err.messages, status=400)) return function(*args, **kwargs) return wrapper return decorator class DateJSONEncoder(json.JSONEncoder): """Custom JSON encoder for handling datetime objects.""" def default(self, obj): """Override the object seralizer.""" if isinstance(obj, (datetime.datetime, datetime.date)): return obj.isoformat() return super().default(obj)