cf5518c7ec
Closes the rule #8 gap at the API layer: PATCH endpoints + service updates for Tree (name/description/visibility), Source, Citation (page/detail/confidence), Relationship (qualifier/notes), and Media (title/attachment) — editor-gated and audited. Every core entity now has create/read/update/delete. Edit UIs for these land in the frontend batch. 37 tests pass. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: Justin Paul <justin@jpaul.me>
60 lines
2.2 KiB
Python
60 lines
2.2 KiB
Python
import uuid
|
|
|
|
from fastapi import APIRouter, status
|
|
|
|
from app.api.deps import CurrentUser, SessionDep
|
|
from app.schemas.tree import TreeCreate, TreeRead, TreeUpdate
|
|
from app.services import tree_service
|
|
|
|
router = APIRouter(prefix="/trees", tags=["trees"])
|
|
|
|
|
|
@router.post("", response_model=TreeRead, status_code=status.HTTP_201_CREATED)
|
|
async def create_tree(data: TreeCreate, session: SessionDep, current: CurrentUser) -> TreeRead:
|
|
tree = await tree_service.create_tree(
|
|
session,
|
|
owner=current,
|
|
name=data.name,
|
|
description=data.description,
|
|
visibility=data.visibility,
|
|
)
|
|
return TreeRead.model_validate(tree)
|
|
|
|
|
|
@router.get("", response_model=list[TreeRead])
|
|
async def list_my_trees(
|
|
session: SessionDep, current: CurrentUser, deleted: bool = False
|
|
) -> list[TreeRead]:
|
|
if deleted:
|
|
trees = await tree_service.list_deleted_trees_for_user(session, user=current)
|
|
else:
|
|
trees = await tree_service.list_trees_for_user(session, user=current)
|
|
return [TreeRead.model_validate(t) for t in trees]
|
|
|
|
|
|
@router.get("/{tree_id}", response_model=TreeRead)
|
|
async def get_tree(tree_id: uuid.UUID, session: SessionDep, current: CurrentUser) -> TreeRead:
|
|
tree = await tree_service.get_tree(session, viewer_id=current.id, tree_id=tree_id)
|
|
return TreeRead.model_validate(tree)
|
|
|
|
|
|
@router.patch("/{tree_id}", response_model=TreeRead)
|
|
async def update_tree(
|
|
tree_id: uuid.UUID, data: TreeUpdate, session: SessionDep, current: CurrentUser
|
|
) -> TreeRead:
|
|
tree = await tree_service.update_tree(
|
|
session, actor=current, tree_id=tree_id, changes=data.model_dump(exclude_unset=True)
|
|
)
|
|
return TreeRead.model_validate(tree)
|
|
|
|
|
|
@router.delete("/{tree_id}", status_code=status.HTTP_204_NO_CONTENT)
|
|
async def delete_tree(tree_id: uuid.UUID, session: SessionDep, current: CurrentUser) -> None:
|
|
await tree_service.delete_tree(session, actor=current, tree_id=tree_id)
|
|
|
|
|
|
@router.post("/{tree_id}/restore", response_model=TreeRead)
|
|
async def restore_tree(tree_id: uuid.UUID, session: SessionDep, current: CurrentUser) -> TreeRead:
|
|
tree = await tree_service.restore_tree(session, actor=current, tree_id=tree_id)
|
|
return TreeRead.model_validate(tree)
|