|
| 1 | +from fastapi import APIRouter, Depends, Query, HTTPException |
| 2 | +from fastapi_pagination.limit_offset import LimitOffsetPage |
| 3 | +from fastapi_pagination import Params |
| 4 | +from sqlalchemy.ext.asyncio import AsyncSession |
| 5 | +from typing import List |
| 6 | + |
| 7 | +from app.schemas.atleta import AtletaCreate, AtletaRead |
| 8 | +from app.repositories.atleta_repo import AtletaRepository |
| 9 | +from app.services.atleta_service import AtletaService |
| 10 | +from app.api.deps import get_session |
| 11 | + |
| 12 | +router = APIRouter(prefix="/v1/atletas", tags=["Atletas"]) |
| 13 | + |
| 14 | +@router.get("/", response_model=LimitOffsetPage[AtletaRead]) |
| 15 | +async def list_atletas( |
| 16 | + nome: str | None = Query(None, description="Filtrar por nome (contains)"), |
| 17 | + cpf: str | None = Query(None, description="Filtrar por CPF exato"), |
| 18 | + limit: int = Query(10, ge=1, description="Quantidade máxima de itens (limit)"), |
| 19 | + offset: int = Query(0, ge=0, description="Offset de paginação"), |
| 20 | + session: AsyncSession = Depends(get_session), |
| 21 | +): |
| 22 | + """ |
| 23 | + Lista atletas com filtros opcionais (nome, cpf) e paginação via limit e offset. |
| 24 | + Response customizada incluirá nome, centro_treinamento e categoria. |
| 25 | + """ |
| 26 | + repo = AtletaRepository(session) |
| 27 | + service = AtletaService(repo) |
| 28 | + total = await service.count(nome=nome, cpf=cpf) |
| 29 | + items = await service.list(nome=nome, cpf=cpf, limit=limit, offset=offset) |
| 30 | + |
| 31 | + # Mapeia os objetos ORM para schema AtletaRead (Pydantic com orm_mode) |
| 32 | + # fastapi-pagination fornece create to build LimitOffsetPage |
| 33 | + return LimitOffsetPage.create(items=items, total=total, params=Params(limit=limit, offset=offset)) |
| 34 | + |
| 35 | +@router.post("/", response_model=AtletaRead, status_code=201) |
| 36 | +async def create_atleta(payload: AtletaCreate, session: AsyncSession = Depends(get_session)): |
| 37 | + """ |
| 38 | + Cria um atleta. Em caso de violação de integridade (CPF duplicado), |
| 39 | + um IntegrityError será lançado e tratado pelo handler global, retornando status 303. |
| 40 | + """ |
| 41 | + repo = AtletaRepository(session) |
| 42 | + service = AtletaService(repo) |
| 43 | + try: |
| 44 | + atleta = await service.create(payload) |
| 45 | + # prepara leitura com nome centro e categoria |
| 46 | + read = AtletaRead.from_orm(atleta) |
| 47 | + if atleta.categoria: |
| 48 | + read.categoria = atleta.categoria.nome |
| 49 | + if atleta.centro: |
| 50 | + read.centro_treinamento = atleta.centro.nome |
| 51 | + return read |
| 52 | + except Exception as exc: |
| 53 | + # IntegrityError será tratado pelo handler global; outros erros viram 400 |
| 54 | + if hasattr(exc, "__cause__") and "IntegrityError" in str(type(exc.__cause__)): |
| 55 | + raise exc.__cause__ |
| 56 | + raise HTTPException(status_code=400, detail=str(exc)) |
0 commit comments