from pydantic import BaseModel, constr, field_validator, model_validator from fastapi import HTTPException, status from typing import Optional from datetime import datetime # Funções utilitárias para sanitização de entradas (evitar XSS, SQLi etc.) # É necessário importar a função de sanitização se for utilizada nos validadores from actions.validations.text import Text # ---------------------------------------------------- # Schema base - Representa a estrutura completa do Cliente (usado em Show e Index) # ---------------------------------------------------- class ClientSchema(BaseModel): # Campos da DDL, todos opcionais para o Schema base (principalmente para leitura) client_id: Optional[int] = None cns: Optional[str] = None name: Optional[str] = None date_register: Optional[datetime] = None state: Optional[str] = None city: Optional[str] = None responsible: Optional[str] = None consultant: Optional[str] = None type_contract: Optional[str] = None class Config: # Permite que o Pydantic mapeie campos vindos do banco (ex: via ORM) from_attributes = True # ---------------------------------------------------- # Schema para operações que requerem apenas o ID # ---------------------------------------------------- class ClientIdSchema(BaseModel): client_id: Optional[int] = None # Valida se o ID não está vazio @model_validator(mode='after') def validate_client_id(self): if not self.client_id or self.client_id <= 0: raise HTTPException( status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, detail='O ID do cliente é obrigatório para esta operação.' ) return self # ---------------------------------------------------- # Schema para localizar cliente pelo CNS # ---------------------------------------------------- class ClientCNSSchema(BaseModel): cns: Optional[str] = None # Sanitiza o input @field_validator('cns') def sanitize_cns(cls, v): if v: return Text.sanitize_input(v) # Mantendo o padrão de sanitização return v # Valida se o campo não está vazio @model_validator(mode='after') def validate_cns(self): if not self.cns or len(self.cns.strip()) == 0: raise HTTPException( status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, detail='Informe um CNS para a busca.' ) return self # ---------------------------------------------------- # Schema para cadastrar (SAVE) um novo cliente (name é NOT NULL) # ---------------------------------------------------- class ClientSaveSchema(BaseModel): # Opcional, pois é AUTO_INCREMENT, mas pode ser usado para Upsert client_id: Optional[int] = None # name é NOT NULL na DDL, então é obrigatório no save name: constr(min_length=1, max_length=550) # Os demais são NULL DEFAULT ou com DEFAULT, então podem ser Optional na entrada cns: Optional[str] = None state: Optional[str] = None city: Optional[str] = None responsible: Optional[str] = None consultant: Optional[str] = None type_contract: Optional[str] = None # Sanitiza os inputs de string @field_validator('cns', 'name', 'state', 'city', 'responsible', 'consultant', 'type_contract') def validate_and_sanitize_fields(cls, v): if v is not None: # Assumindo que Text.sanitize_input existe e faz a sanitização return Text.sanitize_input(v) return v # ---------------------------------------------------- # Schema para atualizar (UPDATE) um cliente (tudo opcional) # ---------------------------------------------------- class ClientUpdateSchema(BaseModel): # Todos os campos que podem ser atualizados são opcionais cns: Optional[str] = None name: Optional[constr(min_length=1, max_length=550)] = None state: Optional[str] = None city: Optional[str] = None responsible: Optional[str] = None consultant: Optional[str] = None type_contract: Optional[str] = None # Sanitiza os inputs de string @field_validator('cns', 'name', 'state', 'city', 'responsible', 'consultant', 'type_contract') def validate_and_sanitize_fields(cls, v): if v is not None: # Assumindo que Text.sanitize_input existe e faz a sanitização return Text.sanitize_input(v) return v