From 64ec1c5180a57e92d04737fae78675951bbf03a0 Mon Sep 17 00:00:00 2001 From: greebo Date: Thu, 19 Mar 2026 19:51:21 +0300 Subject: [PATCH] feat(backend): add draft validation and single-record draft read endpoints add backend endpoint for draft validation add single-record read endpoints for draft entities support targeted draft inspection and version-aware validation flows --- backend/app/api/routes/editor.py | 30 +++++++++++++++++++++++++----- backend/docs/smoke-regression.md | 10 ++++++++++ 2 files changed, 35 insertions(+), 5 deletions(-) diff --git a/backend/app/api/routes/editor.py b/backend/app/api/routes/editor.py index fb45bbb..e5f8801 100644 --- a/backend/app/api/routes/editor.py +++ b/backend/app/api/routes/editor.py @@ -69,9 +69,13 @@ router = APIRouter() @router.get(f"{settings.api_v1_prefix}/schemes/{{scheme_id}}/draft/structure", response_model=DraftStructureResponse) async def get_draft_structure( scheme_id: str, + expected_scheme_version_id: str | None = Query(default=None), role: str = Depends(require_api_key), ): - scheme, version = await get_current_draft_context(scheme_id) + scheme, version = await get_current_draft_context( + scheme_id, + expected_scheme_version_id=expected_scheme_version_id, + ) seats = await list_scheme_version_seats(version.scheme_version_id) sectors = await list_scheme_version_sectors(version.scheme_version_id) groups = await list_scheme_version_groups(version.scheme_version_id) @@ -161,9 +165,13 @@ async def get_draft_validation( async def get_draft_seat_by_record_id( scheme_id: str, seat_record_id: str, + expected_scheme_version_id: str | None = Query(default=None), role: str = Depends(require_api_key), ): - _scheme, version = await get_current_draft_context(scheme_id) + _scheme, version = await get_current_draft_context( + scheme_id, + expected_scheme_version_id=expected_scheme_version_id, + ) row = await get_scheme_version_seat_by_record_id( scheme_version_id=version.scheme_version_id, seat_record_id=seat_record_id, @@ -194,9 +202,13 @@ async def get_draft_seat_by_record_id( async def get_draft_sector_by_record_id( scheme_id: str, sector_record_id: str, + expected_scheme_version_id: str | None = Query(default=None), role: str = Depends(require_api_key), ): - _scheme, version = await get_current_draft_context(scheme_id) + _scheme, version = await get_current_draft_context( + scheme_id, + expected_scheme_version_id=expected_scheme_version_id, + ) row = await get_scheme_version_sector_by_record_id( scheme_version_id=version.scheme_version_id, sector_record_id=sector_record_id, @@ -217,9 +229,13 @@ async def get_draft_sector_by_record_id( async def get_draft_group_by_record_id( scheme_id: str, group_record_id: str, + expected_scheme_version_id: str | None = Query(default=None), role: str = Depends(require_api_key), ): - _scheme, version = await get_current_draft_context(scheme_id) + _scheme, version = await get_current_draft_context( + scheme_id, + expected_scheme_version_id=expected_scheme_version_id, + ) row = await get_scheme_version_group_by_record_id( scheme_version_id=version.scheme_version_id, group_record_id=group_record_id, @@ -239,9 +255,13 @@ async def get_draft_group_by_record_id( @router.get(f"{settings.api_v1_prefix}/schemes/{{scheme_id}}/draft/compare-preview", response_model=StructureDiffResponse) async def get_draft_compare_preview( scheme_id: str, + expected_scheme_version_id: str | None = Query(default=None), role: str = Depends(require_api_key), ): - scheme, version = await get_current_draft_context(scheme_id) + scheme, version = await get_current_draft_context( + scheme_id, + expected_scheme_version_id=expected_scheme_version_id, + ) diff = await build_structure_diff( scheme_id=scheme.scheme_id, draft_scheme_version_id=version.scheme_version_id, diff --git a/backend/docs/smoke-regression.md b/backend/docs/smoke-regression.md index ce7c97f..09483f1 100644 --- a/backend/docs/smoke-regression.md +++ b/backend/docs/smoke-regression.md @@ -154,3 +154,13 @@ Validate: - returned record belongs to current draft scheme_version_id - single-entity endpoints match items visible in draft structure - missing draft record returns 404, not 500 + + +Additional draft read-side stale checks: +- GET /api/v1/schemes/{scheme_id}/draft/structure?expected_scheme_version_id={current_version_id} -> 200 +- GET /api/v1/schemes/{scheme_id}/draft/compare-preview?expected_scheme_version_id={current_version_id} -> 200 +- GET /api/v1/schemes/{scheme_id}/draft/seats/records/{seat_record_id}?expected_scheme_version_id={current_version_id} -> 200 +- GET /api/v1/schemes/{scheme_id}/draft/sectors/records/{sector_record_id}?expected_scheme_version_id={current_version_id} -> 200 +- GET /api/v1/schemes/{scheme_id}/draft/groups/records/{group_record_id}?expected_scheme_version_id={current_version_id} -> 200 +- same endpoints with stale expected_scheme_version_id -> 409 +