"""API utilities.""" import datetime import functools from copy import deepcopy from typing import Callable import neo4j.time as neo_time import stringcase from flask import g, request from marshmallow import ValidationError from owsresponse import response from owsresponse.adaptors.flask import flaskify from permissions.constants import error def jwt_check(func: Callable): """Check for a valid jwt.""" @functools.wraps(func) def decorator(**kwargs): identity_uuid = g.request_context.jwt_identity_id if not identity_uuid: return flaskify( response.create_error_response( error.ERROR_CODE_AUTHORIZATION, message='Request context has no identity uuid.', status=401, ) ) return func(**kwargs) return decorator def validate_request_data(schema, partial=False): """Decorate requests' input data validation. Args: schema (marshmallow.Schema()): schema instance to apply partial (bool): validate only derived fields """ def validator(func): @functools.wraps(func) def wrapper(*args, **kwargs): data = {} if request.method == 'GET': # Handle query parameters for GET requests data = dict(request.args.lists()) elif request.method in ['POST', 'PUT', 'PATCH']: # Handle JSON body for POST, PUT, PATCH requests json_data = request.get_json(silent=True) or {} data = deepcopy(json_data) data.update(kwargs) try: kwargs['deserialize_schema'] = schema.load(data, partial=partial) except ValidationError as err: g.log.error(f'Marshmallow validation error: {err.messages}') return flaskify( response.create_error_response( code=error.ERROR_CODE_VALIDATION_ERROR, message=err.messages ) ) else: return func(*args, **kwargs) return wrapper return validator def to_snake(data): """Transform the keys of the payload to snake case. Args: data (dict): the payload to format the keys. Returns: dict: a new dictionary with the formatted payload """ snake_case_dict = {} for k, v in data.items(): snake_case_dict[stringcase.snakecase(k)] = v return snake_case_dict def to_serializable_dict(data): """Transform DateTime values to string in the input dict. Args: data (dict): the payload to format the keys. Returns: dict: a new dictionary with the formatted payload """ serializable_dict = {} for attribute, value in data.items(): if isinstance(value, (datetime.date, datetime.datetime, neo_time.DateTime)): serializable_dict[attribute] = value.__str__() else: serializable_dict[attribute] = value return serializable_dict