feat: add optimistic concurrency guards for draft editor, pricing and publish flows
add optimistic concurrency guards via expected scheme version id protect draft editor, pricing snapshot, remap and publish flows from stale mutations protect version creation from stale current version state keep backward compatibility with optional query guards verify 409 conflict behavior for stale clients and 200 for valid flows
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
from fastapi import APIRouter, Depends, Query
|
||||
from fastapi import APIRouter, Depends, HTTPException, Query, status
|
||||
|
||||
from app.core.config import settings
|
||||
from app.repositories.audit import create_audit_event
|
||||
@@ -137,6 +137,7 @@ async def get_scheme_versions(
|
||||
@router.post(f"{settings.api_v1_prefix}/schemes/{{scheme_id}}/versions", response_model=SchemeVersionCreateResponse)
|
||||
async def create_next_scheme_version_endpoint(
|
||||
scheme_id: str,
|
||||
expected_current_scheme_version_id: str | None = Query(default=None),
|
||||
role: str = Depends(require_api_key),
|
||||
):
|
||||
current_scheme = await get_scheme_record_by_scheme_id(scheme_id)
|
||||
@@ -145,6 +146,20 @@ async def create_next_scheme_version_endpoint(
|
||||
current_version_number=current_scheme.current_version_number,
|
||||
)
|
||||
|
||||
if (
|
||||
expected_current_scheme_version_id is not None
|
||||
and expected_current_scheme_version_id != current_version.scheme_version_id
|
||||
):
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_409_CONFLICT,
|
||||
detail={
|
||||
"code": "stale_current_version",
|
||||
"message": "Current scheme version changed. Reload scheme state before creating a new version.",
|
||||
"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)
|
||||
|
||||
await clone_scheme_version_sectors(
|
||||
@@ -200,8 +215,15 @@ async def get_publish_validation(scheme_id: str, role: str = Depends(require_api
|
||||
|
||||
|
||||
@router.post(f"{settings.api_v1_prefix}/schemes/{{scheme_id}}/publish")
|
||||
async def publish_scheme_endpoint(scheme_id: str, role: str = Depends(require_api_key)):
|
||||
return await publish_current_draft_scheme(scheme_id=scheme_id)
|
||||
async def publish_scheme_endpoint(
|
||||
scheme_id: str,
|
||||
expected_scheme_version_id: str | None = Query(default=None),
|
||||
role: str = Depends(require_api_key),
|
||||
):
|
||||
return await publish_current_draft_scheme(
|
||||
scheme_id=scheme_id,
|
||||
expected_scheme_version_id=expected_scheme_version_id,
|
||||
)
|
||||
|
||||
|
||||
@router.post(f"{settings.api_v1_prefix}/schemes/{{scheme_id}}/unpublish", response_model=SchemePublishResponse)
|
||||
|
||||
Reference in New Issue
Block a user