"""Services API endpoints.""" from uuid import UUID from fastapi import APIRouter, Depends, HTTPException, status from pydantic import BaseModel, Field from sqlalchemy import select from sqlalchemy.ext.asyncio import AsyncSession from app.dependencies import get_db, verify_api_key from app.models.models import Service router = APIRouter() class ServiceCreate(BaseModel): name: str = Field(..., max_length=100) slug: str = Field(..., max_length=50, pattern=r"^[a-z0-9-]+$") description: str | None = None group_name: str | None = Field(None, max_length=50) position: int = 0 is_visible: bool = True class ServiceUpdate(BaseModel): name: str | None = None slug: str | None = Field(None, max_length=50, pattern=r"^[a-z0-9-]+$") description: str | None = None group_name: str | None = None position: int | None = None is_visible: bool | None = None def serialize_service(s: Service) -> dict: return { "id": s.id, "name": s.name, "slug": s.slug, "description": s.description, "group_name": s.group_name, "position": s.position, "is_visible": s.is_visible, "created_at": s.created_at.isoformat() if s.created_at else None, "updated_at": s.updated_at.isoformat() if s.updated_at else None, } @router.get("/") async def list_services(db: AsyncSession = Depends(get_db)): """List all services.""" result = await db.execute(select(Service).order_by(Service.position, Service.name)) services = result.scalars().all() return [serialize_service(s) for s in services] @router.post("/", status_code=status.HTTP_201_CREATED) async def create_service( data: ServiceCreate, db: AsyncSession = Depends(get_db), api_key: str = Depends(verify_api_key), ): """Create a new service.""" service = Service( name=data.name, slug=data.slug, description=data.description, group_name=data.group_name, position=data.position, is_visible=data.is_visible, ) db.add(service) await db.flush() await db.refresh(service) return serialize_service(service) @router.get("/{service_id}") async def get_service(service_id: UUID, db: AsyncSession = Depends(get_db)): """Get a service by ID.""" result = await db.execute(select(Service).where(Service.id == str(service_id))) service = result.scalar_one_or_none() if not service: raise HTTPException(status_code=404, detail="Service not found") return serialize_service(service) @router.patch("/{service_id}") async def update_service( service_id: UUID, data: ServiceUpdate, db: AsyncSession = Depends(get_db), api_key: str = Depends(verify_api_key), ): """Update a service.""" result = await db.execute(select(Service).where(Service.id == str(service_id))) service = result.scalar_one_or_none() if not service: raise HTTPException(status_code=404, detail="Service not found") update_data = data.model_dump(exclude_unset=True) for field, value in update_data.items(): setattr(service, field, value) await db.flush() await db.refresh(service) return serialize_service(service) @router.delete("/{service_id}", status_code=status.HTTP_204_NO_CONTENT) async def delete_service( service_id: UUID, db: AsyncSession = Depends(get_db), api_key: str = Depends(verify_api_key), ): """Delete a service.""" result = await db.execute(select(Service).where(Service.id == str(service_id))) service = result.scalar_one_or_none() if not service: raise HTTPException(status_code=404, detail="Service not found") await db.delete(service)