Upload app/api/v1/notes.py
This commit is contained in:
@@ -0,0 +1,123 @@
|
|||||||
|
"""Note API endpoints (R-2: polymorphic parent validation)."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
from fastapi import APIRouter, Depends, HTTPException, Query, 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.note import NoteParentType
|
||||||
|
from app.models.user import User
|
||||||
|
from app.schemas.note import NoteCreate, NoteOut, NoteUpdate
|
||||||
|
from app.services.note_service import (
|
||||||
|
InvalidParent,
|
||||||
|
create_note as svc_create_note,
|
||||||
|
get_note as svc_get_note,
|
||||||
|
list_notes as svc_list_notes,
|
||||||
|
soft_delete_note as svc_soft_delete_note,
|
||||||
|
update_note as svc_update_note,
|
||||||
|
)
|
||||||
|
|
||||||
|
router = APIRouter(prefix="/notes", tags=["notes"])
|
||||||
|
|
||||||
|
|
||||||
|
@router.post(
|
||||||
|
"/",
|
||||||
|
response_model=NoteOut,
|
||||||
|
status_code=status.HTTP_201_CREATED,
|
||||||
|
summary="Create a new note (R-2: validates parent exists)",
|
||||||
|
)
|
||||||
|
async def create_note(
|
||||||
|
payload: NoteCreate,
|
||||||
|
current_user: User = Depends(get_current_user),
|
||||||
|
db: AsyncSession = Depends(get_db),
|
||||||
|
) -> NoteOut:
|
||||||
|
try:
|
||||||
|
note = await svc_create_note(
|
||||||
|
db, payload, org_id=current_user.org_id, author_id=current_user.id
|
||||||
|
)
|
||||||
|
except InvalidParent as e:
|
||||||
|
raise HTTPException(status_code=404, detail=str(e)) from e
|
||||||
|
return NoteOut.model_validate(note)
|
||||||
|
|
||||||
|
|
||||||
|
@router.get(
|
||||||
|
"/",
|
||||||
|
response_model=list[NoteOut],
|
||||||
|
summary="List notes (filterable by parent_type + parent_id)",
|
||||||
|
)
|
||||||
|
async def list_notes(
|
||||||
|
skip: int = Query(0, ge=0),
|
||||||
|
limit: int = Query(20, ge=1, le=100),
|
||||||
|
parent_type: Optional[NoteParentType] = None,
|
||||||
|
parent_id: Optional[int] = None,
|
||||||
|
current_user: User = Depends(get_current_user),
|
||||||
|
db: AsyncSession = Depends(get_db),
|
||||||
|
) -> list[NoteOut]:
|
||||||
|
notes = await svc_list_notes(
|
||||||
|
db,
|
||||||
|
org_id=current_user.org_id,
|
||||||
|
skip=skip,
|
||||||
|
limit=limit,
|
||||||
|
parent_type=parent_type.value if parent_type else None,
|
||||||
|
parent_id=parent_id,
|
||||||
|
)
|
||||||
|
return [NoteOut.model_validate(n) for n in notes]
|
||||||
|
|
||||||
|
|
||||||
|
@router.get(
|
||||||
|
"/{note_id}",
|
||||||
|
response_model=NoteOut,
|
||||||
|
summary="Get one note",
|
||||||
|
)
|
||||||
|
async def get_note(
|
||||||
|
note_id: int,
|
||||||
|
current_user: User = Depends(get_current_user),
|
||||||
|
db: AsyncSession = Depends(get_db),
|
||||||
|
) -> NoteOut:
|
||||||
|
note = await svc_get_note(db, note_id, org_id=current_user.org_id)
|
||||||
|
if note is None:
|
||||||
|
raise HTTPException(status_code=404, detail="Note not found")
|
||||||
|
return NoteOut.model_validate(note)
|
||||||
|
|
||||||
|
|
||||||
|
@router.patch(
|
||||||
|
"/{note_id}",
|
||||||
|
response_model=NoteOut,
|
||||||
|
summary="Update a note's body",
|
||||||
|
)
|
||||||
|
async def update_note(
|
||||||
|
note_id: int,
|
||||||
|
payload: NoteUpdate,
|
||||||
|
current_user: User = Depends(get_current_user),
|
||||||
|
db: AsyncSession = Depends(get_db),
|
||||||
|
) -> NoteOut:
|
||||||
|
note = await svc_get_note(db, note_id, org_id=current_user.org_id)
|
||||||
|
if note is None:
|
||||||
|
raise HTTPException(status_code=404, detail="Note not found")
|
||||||
|
updated = await svc_update_note(db, note, payload)
|
||||||
|
return NoteOut.model_validate(updated)
|
||||||
|
|
||||||
|
|
||||||
|
@router.delete(
|
||||||
|
"/{note_id}",
|
||||||
|
status_code=status.HTTP_204_NO_CONTENT,
|
||||||
|
response_class=Response,
|
||||||
|
summary="Soft-delete a note",
|
||||||
|
)
|
||||||
|
async def delete_note(
|
||||||
|
note_id: int,
|
||||||
|
current_user: User = Depends(get_current_user),
|
||||||
|
db: AsyncSession = Depends(get_db),
|
||||||
|
) -> Response:
|
||||||
|
note = await svc_get_note(db, note_id, org_id=current_user.org_id)
|
||||||
|
if note is None:
|
||||||
|
raise HTTPException(status_code=404, detail="Note not found")
|
||||||
|
await svc_soft_delete_note(db, note)
|
||||||
|
return Response(status_code=status.HTTP_204_NO_CONTENT)
|
||||||
|
|
||||||
|
|
||||||
|
__all__ = ["router"]
|
||||||
Reference in New Issue
Block a user