feat(task-013): add draft assembly API, immutable versioning, and editor preview

This commit is contained in:
2026-05-21 23:33:15 +03:00
parent 8ebb5ad623
commit c3914bf8be
19 changed files with 2416 additions and 16 deletions
@@ -0,0 +1,93 @@
from __future__ import annotations
from uuid import UUID
from fastapi import APIRouter, Depends, HTTPException, status
from src.application.drafts import assemble_draft, get_draft, list_drafts, update_draft
from src.domain.auth import EDITOR_OR_ADMIN_ROLES
from src.domain.contracts import (
CurrentUser,
DraftAssembleResponse,
DraftListResponse,
DraftResponse,
DraftUpdateRequest,
)
from src.infrastructure.repositories import BackendRepository
from src.presentation.dependencies import get_repository, require_roles
router = APIRouter(prefix="/api", tags=["drafts"])
@router.post(
"/articles/{article_id}/draft/assemble",
response_model=DraftAssembleResponse,
status_code=status.HTTP_201_CREATED,
)
def post_assemble_draft(
article_id: UUID,
_: CurrentUser = Depends(require_roles(EDITOR_OR_ADMIN_ROLES)),
repository: BackendRepository = Depends(get_repository),
) -> DraftAssembleResponse:
try:
return assemble_draft(repository, article_id=article_id)
except LookupError as error:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Not Found") from error
except (PermissionError, ValueError) as error:
raise HTTPException(status_code=status.HTTP_409_CONFLICT, detail=str(error)) from error
@router.get(
"/articles/{article_id}/drafts",
response_model=DraftListResponse,
)
def get_article_drafts(
article_id: UUID,
_: CurrentUser = Depends(require_roles(EDITOR_OR_ADMIN_ROLES)),
repository: BackendRepository = Depends(get_repository),
) -> DraftListResponse:
try:
return list_drafts(repository, article_id=article_id)
except LookupError as error:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Not Found") from error
@router.get(
"/articles/{article_id}/drafts/{draft_id}",
response_model=DraftResponse,
)
def get_article_draft(
article_id: UUID,
draft_id: UUID,
_: CurrentUser = Depends(require_roles(EDITOR_OR_ADMIN_ROLES)),
repository: BackendRepository = Depends(get_repository),
) -> DraftResponse:
try:
return get_draft(repository, article_id=article_id, draft_id=draft_id)
except LookupError as error:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Not Found") from error
@router.patch(
"/articles/{article_id}/drafts/{draft_id}",
response_model=DraftResponse,
)
def patch_article_draft(
article_id: UUID,
draft_id: UUID,
request: DraftUpdateRequest,
_: CurrentUser = Depends(require_roles(EDITOR_OR_ADMIN_ROLES)),
repository: BackendRepository = Depends(get_repository),
) -> DraftResponse:
try:
return update_draft(
repository,
article_id=article_id,
draft_id=draft_id,
request=request,
)
except LookupError as error:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Not Found") from error
except ValueError as error:
raise HTTPException(status_code=status.HTTP_409_CONFLICT, detail=str(error)) from error