feat(backend): add editor context, draft flow bootstrap, and draft summary endpoints

add backend endpoints for editor context and draft summaries

ensure draft flow bootstrap for editor-driven workflows
improve draft-aware initialization and summary reads for clients
This commit is contained in:
greebo
2026-03-19 21:47:38 +03:00
parent a266f56ddd
commit 4c15f4c201
6 changed files with 390 additions and 265 deletions

View File

@@ -18,7 +18,6 @@ from app.repositories.schemes import (
rollback_scheme_to_version,
unpublish_scheme,
)
from app.schemas.publish_readiness import PublishExecutionResponse
from app.schemas.scheme_registry import (
SchemeCurrentResponse,
SchemeDetailResponse,
@@ -29,6 +28,7 @@ from app.schemas.scheme_registry import (
SchemeRollbackResponse,
)
from app.schemas.scheme_versions import (
EnsureDraftResponse,
SchemeVersionCreateResponse,
SchemeVersionListItem,
SchemeVersionListResponse,
@@ -41,6 +41,19 @@ from app.services.scheme_validation import build_scheme_validation_report
router = APIRouter()
def _build_stale_current_version_detail(
*,
expected_scheme_version_id: str,
actual_scheme_version_id: str,
) -> dict:
return {
"code": "stale_current_version",
"message": "Current scheme version changed. Reload scheme state before creating a new version.",
"expected_scheme_version_id": expected_scheme_version_id,
"actual_scheme_version_id": actual_scheme_version_id,
}
@router.get(f"{settings.api_v1_prefix}/schemes", response_model=SchemeListResponse)
async def get_schemes(
limit: int = Query(default=50, ge=1, le=200),
@@ -153,12 +166,10 @@ async def create_next_scheme_version_endpoint(
and expected_current_scheme_version_id != current_version.scheme_version_id
):
raise_conflict(
code="stale_current_version",
message="Current scheme version changed. Reload scheme state before creating a new version.",
details={
"expected_scheme_version_id": expected_current_scheme_version_id,
"actual_scheme_version_id": current_version.scheme_version_id,
},
_build_stale_current_version_detail(
expected_scheme_version_id=expected_current_scheme_version_id,
actual_scheme_version_id=current_version.scheme_version_id,
)
)
new_version = await create_next_scheme_version_from_current(scheme_id)
@@ -197,6 +208,79 @@ async def create_next_scheme_version_endpoint(
)
@router.post(f"{settings.api_v1_prefix}/schemes/{{scheme_id}}/draft/ensure", response_model=EnsureDraftResponse)
async def ensure_draft_scheme_version(
scheme_id: str,
expected_current_scheme_version_id: str | None = Query(default=None),
role: str = Depends(require_api_key),
):
scheme = await get_scheme_record_by_scheme_id(scheme_id)
current_version = await get_current_scheme_version(
scheme_id=scheme.scheme_id,
current_version_number=scheme.current_version_number,
)
if (
expected_current_scheme_version_id
and expected_current_scheme_version_id != current_version.scheme_version_id
):
raise_conflict(
_build_stale_current_version_detail(
expected_scheme_version_id=expected_current_scheme_version_id,
actual_scheme_version_id=current_version.scheme_version_id,
)
)
if scheme.status == "draft" and current_version.status == "draft":
return EnsureDraftResponse(
scheme_id=scheme.scheme_id,
scheme_version_id=current_version.scheme_version_id,
version_number=current_version.version_number,
status=current_version.status,
normalized_storage_path=current_version.normalized_storage_path,
created=False,
source_scheme_version_id=None,
)
new_version = await create_next_scheme_version_from_current(scheme_id)
await clone_scheme_version_sectors(
source_scheme_version_id=current_version.scheme_version_id,
target_scheme_version_id=new_version.scheme_version_id,
)
await clone_scheme_version_groups(
source_scheme_version_id=current_version.scheme_version_id,
target_scheme_version_id=new_version.scheme_version_id,
)
await clone_scheme_version_seats(
source_scheme_version_id=current_version.scheme_version_id,
target_scheme_version_id=new_version.scheme_version_id,
)
await create_audit_event(
scheme_id=scheme_id,
event_type="scheme.version.created",
object_type="scheme_version",
object_ref=new_version.scheme_version_id,
details={
"source_scheme_version_id": current_version.scheme_version_id,
"version_number": new_version.version_number,
"normalized_storage_path": new_version.normalized_storage_path,
"reason": "ensure_draft",
},
)
return EnsureDraftResponse(
scheme_id=new_version.scheme_id,
scheme_version_id=new_version.scheme_version_id,
version_number=new_version.version_number,
status=new_version.status,
normalized_storage_path=new_version.normalized_storage_path,
created=True,
source_scheme_version_id=current_version.scheme_version_id,
)
@router.get(f"{settings.api_v1_prefix}/schemes/{{scheme_id}}/publish/validation")
async def get_publish_validation(scheme_id: str, role: str = Depends(require_api_key)):
scheme = await get_scheme_record_by_scheme_id(scheme_id)
@@ -215,20 +299,16 @@ async def get_publish_validation(scheme_id: str, role: str = Depends(require_api
}
@router.post(
f"{settings.api_v1_prefix}/schemes/{{scheme_id}}/publish",
response_model=PublishExecutionResponse,
)
@router.post(f"{settings.api_v1_prefix}/schemes/{{scheme_id}}/publish")
async def publish_scheme_endpoint(
scheme_id: str,
expected_scheme_version_id: str | None = Query(default=None),
role: str = Depends(require_api_key),
):
result = await publish_current_draft_scheme(
return await publish_current_draft_scheme(
scheme_id=scheme_id,
expected_scheme_version_id=expected_scheme_version_id,
)
return PublishExecutionResponse(**result)
@router.post(f"{settings.api_v1_prefix}/schemes/{{scheme_id}}/unpublish", response_model=SchemePublishResponse)