Files
Clarium/backend/validation.py
T
mroberts 9d7b0e805c style: drop redundant comments and apply formatting
Frontend now uses double quotes and semicolons. No behaviour change.
2026-10-02 15:06:56 -05:00

59 lines
1.9 KiB
Python

"""Request validation shared by every slice. Errors carry a status, a fix-it message, and the field."""
from flask import request
class ApiError(Exception):
def __init__(self, status: int, message: str, field: str | None = None):
super().__init__(message)
self.status = status
self.message = message
self.field = field
def json_body(allowed: set[str]) -> dict:
body = request.get_json(silent=True)
if not isinstance(body, dict):
raise ApiError(400, "Request body must be a JSON object")
unknown = sorted(set(body) - allowed)
if unknown:
raise ApiError(
400,
f"Unknown field(s): {', '.join(unknown)}. Allowed: {', '.join(sorted(allowed))}",
unknown[0],
)
return body
def require_utf8(value: str, field: str) -> None:
try:
value.encode("utf-8")
except UnicodeEncodeError:
raise ApiError(
400,
f"{field} contains unpaired surrogate characters; send valid UTF-8 text",
field,
) from None
def text(body: dict, field: str, max_len: int) -> str:
value = body.get(field)
if not isinstance(value, str) or not value.strip():
raise ApiError(400, f"{field} is required and must be non-blank text", field)
value = value.strip()
if len(value) > max_len:
raise ApiError(400, f"{field} must be at most {max_len} characters", field)
if "\x00" in value:
raise ApiError(400, f"{field} must not contain NUL characters", field)
require_utf8(value, field)
return value
def integer(body: dict, field: str, low: int, high: int) -> int:
value = body.get(field)
if isinstance(value, bool) or not isinstance(value, int):
raise ApiError(400, f"{field} must be a whole number", field)
if not low <= value <= high:
raise ApiError(400, f"{field} must be between {low} and {high}", field)
return value