""" Connection schemas — search, request, respond, connection management. All input schemas use extra="forbid" to prevent mass-assignment. """ import re from typing import Literal, Optional from datetime import datetime from pydantic import BaseModel, ConfigDict, Field, field_validator _UMBRAL_NAME_RE = re.compile(r'^[a-zA-Z0-9_-]{3,50}$') class UmbralSearchRequest(BaseModel): model_config = ConfigDict(extra="forbid") umbral_name: str = Field(..., max_length=50) @field_validator('umbral_name') @classmethod def validate_umbral_name(cls, v: str) -> str: if not _UMBRAL_NAME_RE.match(v): raise ValueError('Umbral name must be 3-50 alphanumeric characters, hyphens, or underscores') return v class UmbralSearchResponse(BaseModel): found: bool class SendConnectionRequest(BaseModel): model_config = ConfigDict(extra="forbid") umbral_name: str = Field(..., max_length=50) @field_validator('umbral_name') @classmethod def validate_umbral_name(cls, v: str) -> str: if not _UMBRAL_NAME_RE.match(v): raise ValueError('Umbral name must be 3-50 alphanumeric characters, hyphens, or underscores') return v class ConnectionRequestResponse(BaseModel): id: int sender_umbral_name: str sender_preferred_name: Optional[str] = None receiver_umbral_name: str receiver_preferred_name: Optional[str] = None status: Literal["pending", "accepted", "rejected", "cancelled"] created_at: datetime class RespondRequest(BaseModel): model_config = ConfigDict(extra="forbid") action: Literal["accept", "reject"] class ConnectionResponse(BaseModel): id: int connected_user_id: int connected_umbral_name: str connected_preferred_name: Optional[str] = None person_id: Optional[int] = None created_at: datetime class RespondAcceptResponse(BaseModel): message: str connection_id: int class RespondRejectResponse(BaseModel): message: str class CancelResponse(BaseModel): message: str class SharingOverrideUpdate(BaseModel): model_config = ConfigDict(extra="forbid") first_name: Optional[bool] = None last_name: Optional[bool] = None preferred_name: Optional[bool] = None email: Optional[bool] = None phone: Optional[bool] = None mobile: Optional[bool] = None birthday: Optional[bool] = None address: Optional[bool] = None company: Optional[bool] = None job_title: Optional[bool] = None