2025-12-07 12:00:00 +00:00
|
|
|
"""Common schemas used across the API."""
|
|
|
|
|
|
|
|
|
|
from pydantic import BaseModel, Field
|
|
|
|
|
|
|
|
|
|
|
2026-01-07 20:51:13 -05:00
|
|
|
class ErrorDetail(BaseModel):
|
|
|
|
|
"""Individual error detail for validation errors."""
|
|
|
|
|
|
|
|
|
|
loc: list[str | int] = Field(description="Location of the error (field path)")
|
|
|
|
|
msg: str = Field(description="Error message")
|
|
|
|
|
type: str = Field(description="Error type identifier")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class ErrorResponse(BaseModel):
|
|
|
|
|
"""Structured error response returned by all error handlers."""
|
|
|
|
|
|
|
|
|
|
error: str = Field(description="Error type (e.g., 'not_found', 'validation_error')")
|
|
|
|
|
message: str = Field(description="Human-readable error message")
|
|
|
|
|
details: list[ErrorDetail] | None = Field(
|
|
|
|
|
default=None, description="Additional error details for validation errors"
|
|
|
|
|
)
|
|
|
|
|
request_id: str | None = Field(
|
|
|
|
|
default=None, description="Request trace ID for debugging"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
model_config = {
|
|
|
|
|
"json_schema_extra": {
|
|
|
|
|
"examples": [
|
|
|
|
|
{
|
|
|
|
|
"error": "not_found",
|
|
|
|
|
"message": "Incident not found",
|
|
|
|
|
"request_id": "abc123def456",
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"error": "validation_error",
|
|
|
|
|
"message": "Request validation failed",
|
|
|
|
|
"details": [
|
|
|
|
|
{"loc": ["body", "title"], "msg": "Field required", "type": "missing"}
|
|
|
|
|
],
|
|
|
|
|
"request_id": "abc123def456",
|
|
|
|
|
},
|
|
|
|
|
]
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2025-12-07 12:00:00 +00:00
|
|
|
class CursorParams(BaseModel):
|
|
|
|
|
"""Pagination parameters using cursor-based pagination."""
|
|
|
|
|
|
|
|
|
|
cursor: str | None = Field(default=None, description="Cursor for pagination")
|
|
|
|
|
limit: int = Field(default=20, ge=1, le=100, description="Number of items per page")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class PaginatedResponse[T](BaseModel):
|
|
|
|
|
"""Generic paginated response wrapper."""
|
|
|
|
|
|
|
|
|
|
items: list[T]
|
|
|
|
|
next_cursor: str | None = Field(
|
|
|
|
|
default=None, description="Cursor for next page, null if no more items"
|
|
|
|
|
)
|
|
|
|
|
has_more: bool = Field(description="Whether there are more items")
|