Upload app/api/v1/tags.py
This commit is contained in:
@@ -0,0 +1,98 @@
|
||||
"""Tag API endpoints (R-2: polymorphic parent validation)."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException, Response, status
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from app.core.db import get_db
|
||||
from app.core.deps import get_current_user
|
||||
from app.models.user import User
|
||||
from app.schemas.tag import TagCreate, TagLinkCreate, TagLinkOut, TagOut
|
||||
from app.services.tag_service import (
|
||||
DuplicateTagLink,
|
||||
InvalidParent,
|
||||
TagNotFound,
|
||||
create_tag as svc_create_tag,
|
||||
link_tag as svc_link_tag,
|
||||
list_tags as svc_list_tags,
|
||||
unlink_tag as svc_unlink_tag,
|
||||
)
|
||||
|
||||
router = APIRouter(prefix="/tags", tags=["tags"])
|
||||
|
||||
|
||||
@router.post(
|
||||
"/",
|
||||
response_model=TagOut,
|
||||
status_code=status.HTTP_201_CREATED,
|
||||
summary="Create a new tag",
|
||||
)
|
||||
async def create_tag(
|
||||
payload: TagCreate,
|
||||
current_user: User = Depends(get_current_user),
|
||||
db: AsyncSession = Depends(get_db),
|
||||
) -> TagOut:
|
||||
tag = await svc_create_tag(db, payload, org_id=current_user.org_id)
|
||||
return TagOut.model_validate(tag)
|
||||
|
||||
|
||||
@router.get(
|
||||
"/",
|
||||
response_model=list[TagOut],
|
||||
summary="List all tags in the org",
|
||||
)
|
||||
async def list_tags(
|
||||
current_user: User = Depends(get_current_user),
|
||||
db: AsyncSession = Depends(get_db),
|
||||
) -> list[TagOut]:
|
||||
tags = await svc_list_tags(db, org_id=current_user.org_id)
|
||||
return [TagOut.model_validate(t) for t in tags]
|
||||
|
||||
|
||||
@router.post(
|
||||
"/link",
|
||||
response_model=TagLinkOut,
|
||||
status_code=status.HTTP_201_CREATED,
|
||||
summary="Link a tag to an entity (R-2: validates parent exists)",
|
||||
)
|
||||
async def link_tag_endpoint(
|
||||
payload: TagLinkCreate,
|
||||
current_user: User = Depends(get_current_user),
|
||||
db: AsyncSession = Depends(get_db),
|
||||
) -> TagLinkOut:
|
||||
try:
|
||||
link = await svc_link_tag(db, payload, org_id=current_user.org_id)
|
||||
except InvalidParent as e:
|
||||
raise HTTPException(status_code=404, detail=str(e)) from e
|
||||
except TagNotFound as e:
|
||||
raise HTTPException(status_code=404, detail=str(e)) from e
|
||||
except DuplicateTagLink as e:
|
||||
raise HTTPException(status_code=409, detail=str(e)) from e
|
||||
return TagLinkOut.model_validate(link)
|
||||
|
||||
|
||||
@router.delete(
|
||||
"/link",
|
||||
status_code=status.HTTP_204_NO_CONTENT,
|
||||
response_class=Response,
|
||||
summary="Unlink a tag from an entity",
|
||||
)
|
||||
async def unlink_tag_endpoint(
|
||||
payload: TagLinkCreate,
|
||||
current_user: User = Depends(get_current_user),
|
||||
db: AsyncSession = Depends(get_db),
|
||||
) -> Response:
|
||||
deleted = await svc_unlink_tag(
|
||||
db,
|
||||
tag_id=payload.tag_id,
|
||||
parent_type=payload.parent_type,
|
||||
parent_id=payload.parent_id,
|
||||
org_id=current_user.org_id,
|
||||
)
|
||||
if not deleted:
|
||||
raise HTTPException(status_code=404, detail="Tag link not found")
|
||||
return Response(status_code=status.HTTP_204_NO_CONTENT)
|
||||
|
||||
|
||||
__all__ = ["router"]
|
||||
Reference in New Issue
Block a user