"""Request bodies for the exception ledger's four manual transitions. Every field that maps to a closed vocabulary is validated against the taxonomy here rather than in the router, so an invalid value is a 422 with a field name instead of a 500 from a downstream ``ValueError``. """ from __future__ import annotations import uuid from pydantic import BaseModel, Field, field_validator from app.domains.automation_exception.taxonomy import ROOT_CAUSES class AcknowledgeRequest(BaseModel): note: str | None = Field(default=None, max_length=2000) class SetRootCauseRequest(BaseModel): root_cause: str note: str | None = Field(default=None, max_length=2000) @field_validator("root_cause") @classmethod def _known_cause(cls, value: str) -> str: if value not in ROOT_CAUSES: raise ValueError( f"root_cause must be one of: {', '.join(ROOT_CAUSES)}", ) return value class CloseWontFixRequest(BaseModel): #: Mandatory and non-blank. This transition silences a still-failing #: problem; the reason is the only thing that explains it later. reason: str = Field(min_length=1, max_length=2000) @field_validator("reason") @classmethod def _non_blank(cls, value: str) -> str: if not value.strip(): raise ValueError("reason must not be blank") return value.strip() class ReopenRequest(BaseModel): note: str | None = Field(default=None, max_length=2000) class AssignOwnerRequest(BaseModel): #: ``None`` clears the assignment, returning the row to unowned so #: automatic resolution may pick it up again. owner_person_id: uuid.UUID | None = None