Files
gartenmanager/backend/app/api/v1/plants.py

96 lines
3.8 KiB
Python
Raw Normal View History

from typing import Annotated
from uuid import UUID
from fastapi import APIRouter, Depends, HTTPException, status
from sqlalchemy.ext.asyncio import AsyncSession
from app.core.deps import get_session, get_tenant_context, require_min_role
from app.crud.plant import crud_plant, crud_plant_family
from app.models.user import TenantRole
from app.schemas.plant import PlantCreate, PlantFamilyRead, PlantRead, PlantUpdate
router = APIRouter(tags=["Pflanzen"])
TenantCtx = Annotated[tuple, Depends(get_tenant_context)]
WriteCtx = Annotated[tuple, Depends(require_min_role(TenantRole.READ_WRITE))]
@router.get("/plant-families", response_model=list[PlantFamilyRead])
async def list_plant_families(
db: Annotated[AsyncSession, Depends(get_session)],
_: TenantCtx,
) -> list[PlantFamilyRead]:
families = await crud_plant_family.get_all(db)
return [PlantFamilyRead.model_validate(f) for f in families]
@router.get("/plants", response_model=list[PlantRead])
async def list_plants(
db: Annotated[AsyncSession, Depends(get_session)],
ctx: TenantCtx,
) -> list[PlantRead]:
_, tenant_id = ctx
plants = await crud_plant.get_multi_for_tenant(db, tenant_id=tenant_id)
return [PlantRead.model_validate(p) for p in plants]
@router.get("/plants/{plant_id}", response_model=PlantRead)
async def get_plant(
plant_id: UUID,
db: Annotated[AsyncSession, Depends(get_session)],
_: TenantCtx,
) -> PlantRead:
plant = await crud_plant.get(db, id=plant_id)
if not plant:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Pflanze nicht gefunden.")
return PlantRead.model_validate(plant)
@router.post("/plants", response_model=PlantRead, status_code=status.HTTP_201_CREATED)
async def create_plant(
body: PlantCreate,
db: Annotated[AsyncSession, Depends(get_session)],
ctx: WriteCtx,
) -> PlantRead:
_, tenant_id, _ = ctx
plant = await crud_plant.create_for_tenant(db, obj_in=body, tenant_id=tenant_id)
return PlantRead.model_validate(plant)
@router.put("/plants/{plant_id}", response_model=PlantRead)
async def update_plant(
plant_id: UUID,
body: PlantUpdate,
db: Annotated[AsyncSession, Depends(get_session)],
ctx: WriteCtx,
) -> PlantRead:
user, tenant_id, _ = ctx
plant = await crud_plant.get(db, id=plant_id)
if not plant:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Pflanze nicht gefunden.")
# Global plants: only superadmin
if plant.tenant_id is None and not user.is_superadmin:
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="Globale Pflanzen können nur von Superadmins bearbeitet werden.")
# Tenant plants: must belong to current tenant
if plant.tenant_id is not None and plant.tenant_id != tenant_id:
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="Kein Zugriff auf diese Pflanze.")
updated = await crud_plant.update(db, db_obj=plant, obj_in=body)
return PlantRead.model_validate(updated)
@router.delete("/plants/{plant_id}", status_code=status.HTTP_204_NO_CONTENT)
async def delete_plant(
plant_id: UUID,
db: Annotated[AsyncSession, Depends(get_session)],
ctx: WriteCtx,
) -> None:
user, tenant_id, _ = ctx
plant = await crud_plant.get(db, id=plant_id)
if not plant:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Pflanze nicht gefunden.")
if plant.tenant_id is None and not user.is_superadmin:
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="Globale Pflanzen können nur von Superadmins gelöscht werden.")
if plant.tenant_id is not None and plant.tenant_id != tenant_id:
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="Kein Zugriff auf diese Pflanze.")
await crud_plant.remove(db, id=plant_id)