Files
svg-backend/backend/app/services/draft_guard.py
greebo a266f56ddd feat(backend): harden draft, pricing and publish contracts
- unify typed API errors across draft, pricing and publish flows
- add stale draft and publish-state mutation guards
- add publish readiness contract and guarded publish flow
- add sellability reason codes to test seat preview
- add pricing diagnostics and strengthen snapshot/publish lifecycle consistency
2026-03-19 20:58:14 +03:00

65 lines
2.2 KiB
Python

from app.repositories.scheme_versions import get_current_scheme_version
from app.repositories.schemes import get_scheme_record_by_scheme_id
from app.services.api_errors import raise_conflict
def build_stale_draft_version_detail(
*,
expected_scheme_version_id: str,
actual_scheme_version_id: str,
) -> dict:
return {
"code": "stale_draft_version",
"message": "Draft scheme version is stale. Reload current draft state before applying mutation.",
"expected_scheme_version_id": expected_scheme_version_id,
"actual_scheme_version_id": actual_scheme_version_id,
}
async def get_current_draft_context(
scheme_id: str,
expected_scheme_version_id: str | None = None,
):
scheme = await get_scheme_record_by_scheme_id(scheme_id)
version = await get_current_scheme_version(
scheme_id=scheme.scheme_id,
current_version_number=scheme.current_version_number,
)
if version.status != "draft" or scheme.status != "draft":
raise_conflict(
code="draft_not_editable",
message="Current scheme version is not editable because it is not in draft state",
details={
"scheme_status": scheme.status,
"scheme_version_status": version.status,
"actual_scheme_version_id": version.scheme_version_id,
},
)
if expected_scheme_version_id and expected_scheme_version_id != version.scheme_version_id:
raise_conflict(
code="stale_draft_version",
message="Draft scheme version is stale. Reload current draft state before applying mutation.",
details={
"expected_scheme_version_id": expected_scheme_version_id,
"actual_scheme_version_id": version.scheme_version_id,
},
)
return scheme, version
async def validate_expected_draft_version_if_provided(
scheme_id: str,
expected_scheme_version_id: str | None,
):
if not expected_scheme_version_id:
return None
scheme, version = await get_current_draft_context(
scheme_id=scheme_id,
expected_scheme_version_id=expected_scheme_version_id,
)
return scheme, version