"""API utilities.""" import functools from typing import Callable, Literal from flask import g, request from marshmallow import ValidationError from owsresponse import response from owsresponse.adaptors.flask import flaskify import stringcase from users import constants from users.connectors.sentry import sentry_client 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( constants.ERROR_CODE_AUTHORIZATION_ERROR, message='Request context has no identity uuid.', status=401, ) ) return func(**kwargs) return decorator def validate_request_data( schema, partial: bool = False, add_url_params: bool = True, source: Literal['json', 'args'] = 'json', ): """Decorate requests' input data validation. Args: schema (marshmallow.Schema()): schema instance to apply partial (bool): validate only derived fields add_url_params (bool): add url params to data source (str): data source to validate json or args """ def validator(func): @functools.wraps(func) def wrapper(*args, **kwargs): if request.is_json and source == 'json': raw_data = request.get_json() or {} elif source == 'args': raw_data = request.args or {} else: raw_data = {} data = dict(**raw_data) if add_url_params: data.update(kwargs) try: result = schema.load(data, partial=partial) setattr(request, f'validated_{source}', result) except ValidationError as err: sentry_client.capture_exception() return flaskify( response.create_error_response( code=constants.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_camel(data): """Transform the keys of the payload to camel case. Args: data (dict): the payload to format the keys. Returns: dict: a new dictionary with the formatted payload """ camel_case_dict = {} for k, v in data.items(): camel_case_dict[stringcase.camelcase(k)] = v return camel_case_dict