"""Helper functions to use with handlers.""" from copy import deepcopy import datetime from functools import wraps import json from flask import request from marshmallow import ValidationError from oto import response from oto.adaptors.flask import flaskify from ows_product_physical.constant 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. """ json_data = request.get_json(force=True, silent=True) or dict() # Marshmallow doesn't return validation errors unless the passed # in data type is a dict. if not isinstance(json_data, dict): return flaskify(response.create_error_response( error.BAD_REQUEST_ERROR, 'Request data must be a JSON object and include proper ' 'Content-Type header parameter.')) params = request.args or dict() # from url params data = deepcopy(json_data) data.update(kwargs) data.update(params) try: result = schema().load(data) kwargs['json_data'] = result return function(*args, **kwargs) except ValidationError as e: return flaskify(response.create_error_response( code=error.VALIDATION_ERROR, message=e.messages, status=400)) 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)