"""Utility Functions for Handlers.""" import re from typing import Any from flask import request from marshmallow import Schema from marshmallow.exceptions import ValidationError from werkzeug.exceptions import BadRequest, UnsupportedMediaType from vectororder.constants import search from vectororder.exceptions import RequestError def parse_request_json( schema: type[Schema], partial: bool = False, request_data: dict[str, Any] | None = None, ) -> dict[str, Any]: """Parse and optionally validate request JSON. Args: schema (type): Marshmallow schema to validate request. partial (bool): Partial validation flag. request_data (dict): Request data. Returns: dict: Validated request. Raises: RequestError: Request JSON is not an object (but a list, for example). ValidationError: Request data did not pass the schema validation. """ if not request_data: try: request_data = request.get_json() except (UnsupportedMediaType, BadRequest) as exc: raise RequestError("Invalid JSON") from exc # Verify request data is wrapped in dict (Marshmallow doesn't catch this). if not isinstance(request_data, dict): raise RequestError("Request envelope must be an object.") if not schema: return request_data return schema().load(request_data, partial=partial) def get_order_by(order_by_string: str) -> list[str]: """Parse and validate request 'order by' parameter. Args: order_by_string (str): A comma-delimited string of fields optionally prefixed by '-' to indicate a descending order. Returns: list: A list of fields to order by. Raises: ValidationError: An exception is raised on invalid input string. """ if not order_by_string: return [] order_by = [field.strip() for field in order_by_string.split(",")] order_by_re = re.compile(search.ORDER_BY_RE, re.IGNORECASE) for order_by_field in order_by: if not order_by_re.fullmatch(order_by_field): raise ValidationError( "Bad value '{}' in 'order by' field.".format(order_by_field) ) return order_by