Files
svg-backend/backend/README.md
greebo 127c5bff71 feat(backend): stabilize draft editor flow and complete smoke regression baseline
- add editor entry flow with editor context and ensure-draft bootstrap
- add draft summary read model and single-record draft read endpoints
- add typed draft, edit and publish conflicts with validation errors
- add pricing diagnostics and publish readiness endpoints
- fix Decimal serialization in seat price and test preview flows
- harden draft lifecycle guards for published vs draft current version
- update API map and smoke regression checklist
- add backend README and smoke regression script
2026-03-19 22:23:46 +03:00

8.3 KiB

svg-service backend

Backend for SVG scheme upload, draft editing, pricing, publish preview, and publish lifecycle.

Stack

  • Python 3.11
  • FastAPI
  • SQLAlchemy async
  • PostgreSQL 16
  • Docker Compose

Runtime

Default backend port: 9020

Health check:

  • GET /healthz

Main API prefix:

  • /api/v1

Auth header:

  • X-API-Key

Default local admin key:

  • admin-local-dev-key

Core lifecycle

The backend works with a scheme lifecycle:

  1. Upload SVG
  2. Normalize and persist structure
  3. Work in current draft
  4. Create / update pricing
  5. Build pricing snapshot
  6. Inspect publish preview / readiness
  7. Publish current draft
  8. If editing is needed after publish, create or ensure a new draft again

Main concepts

Scheme

Top-level business entity.

Scheme version

Concrete version of the scheme.
A version can be draft or published.

Current version

The version referenced by the scheme registry as active current.

Draft

Editable current version.
All editor mutations and draft pricing operations must target a current draft version only.

Published version

Non-editable current version.
If current version is published, editor flow must first create or ensure a new draft.

Upload artifacts

Stored technical artifacts, including:

  • original svg
  • sanitized svg
  • normalized json
  • display svg
  • publish preview json

Editor entry flow

Use this flow from frontend or operator scripts.

1. Inspect editor state

GET /api/v1/schemes/{scheme_id}/editor/context

Response tells whether:

  • current version is draft
  • editor is available
  • a new draft should be created
  • recommended action is use_current_draft or create_draft

2. Ensure editable draft

POST /api/v1/schemes/{scheme_id}/draft/ensure

Behavior:

  • if current version is already draft: returns it with created=false
  • if current version is published: clones current version into a new current draft and returns it with created=true

Returned scheme_version_id must be reused as:

  • expected_scheme_version_id

for draft reads and mutations.

Optimistic concurrency

Mutable draft flows support optimistic concurrency through query params:

  • expected_current_scheme_version_id
  • expected_scheme_version_id

These guards prevent frontend/editor from mutating a stale draft after another version switch.

Typical typed conflict payload:

  • stale_current_version
  • stale_draft_version
  • draft_not_editable
  • publish_not_ready

Main operator routes

System

  • GET /healthz
  • GET /api/v1/ping
  • GET /api/v1/db/ping
  • GET /api/v1/manifest

Uploads

  • POST /api/v1/schemes/upload
  • GET /api/v1/uploads
  • GET /api/v1/uploads/{upload_id}
  • GET /api/v1/uploads/{upload_id}/normalized

Scheme registry

  • GET /api/v1/schemes
  • GET /api/v1/schemes/{scheme_id}
  • GET /api/v1/schemes/{scheme_id}/current
  • GET /api/v1/schemes/{scheme_id}/versions
  • POST /api/v1/schemes/{scheme_id}/versions
  • GET /api/v1/schemes/{scheme_id}/publish/validation
  • GET /api/v1/schemes/{scheme_id}/draft/publish-readiness
  • POST /api/v1/schemes/{scheme_id}/publish
  • POST /api/v1/schemes/{scheme_id}/unpublish
  • POST /api/v1/schemes/{scheme_id}/rollback

Editor / draft

  • GET /api/v1/schemes/{scheme_id}/editor/context
  • POST /api/v1/schemes/{scheme_id}/draft/ensure
  • GET /api/v1/schemes/{scheme_id}/draft/summary
  • GET /api/v1/schemes/{scheme_id}/draft/structure
  • GET /api/v1/schemes/{scheme_id}/draft/validation
  • GET /api/v1/schemes/{scheme_id}/draft/compare-preview
  • GET /api/v1/schemes/{scheme_id}/draft/seats/records/{seat_record_id}
  • GET /api/v1/schemes/{scheme_id}/draft/sectors/records/{sector_record_id}
  • GET /api/v1/schemes/{scheme_id}/draft/groups/records/{group_record_id}
  • POST /api/v1/schemes/{scheme_id}/draft/sectors
  • POST /api/v1/schemes/{scheme_id}/draft/groups
  • DELETE /api/v1/schemes/{scheme_id}/draft/sectors/records/{sector_record_id}
  • DELETE /api/v1/schemes/{scheme_id}/draft/groups/records/{group_record_id}
  • PATCH /api/v1/schemes/{scheme_id}/draft/seats/records/{seat_record_id}
  • POST /api/v1/schemes/{scheme_id}/draft/seats/bulk
  • PATCH /api/v1/schemes/{scheme_id}/draft/sectors/records/{sector_record_id}
  • PATCH /api/v1/schemes/{scheme_id}/draft/groups/records/{group_record_id}
  • POST /api/v1/schemes/{scheme_id}/draft/repair-references

Pricing

  • GET /api/v1/schemes/{scheme_id}/pricing
  • POST /api/v1/schemes/{scheme_id}/pricing/categories
  • PUT /api/v1/schemes/{scheme_id}/pricing/categories/{pricing_category_id}
  • DELETE /api/v1/schemes/{scheme_id}/pricing/categories/{pricing_category_id}
  • POST /api/v1/schemes/{scheme_id}/pricing/rules
  • PUT /api/v1/schemes/{scheme_id}/pricing/rules/{price_rule_id}
  • DELETE /api/v1/schemes/{scheme_id}/pricing/rules/{price_rule_id}

Pricing diagnostics

  • GET /api/v1/schemes/{scheme_id}/pricing/coverage
  • GET /api/v1/schemes/{scheme_id}/pricing/unpriced-seats
  • GET /api/v1/schemes/{scheme_id}/pricing/explain/{seat_id}
  • GET /api/v1/schemes/{scheme_id}/pricing/rules/diagnostics

Publish preview

  • POST /api/v1/schemes/{scheme_id}/draft/pricing/snapshot
  • GET /api/v1/schemes/{scheme_id}/draft/publish-preview
  • POST /api/v1/schemes/{scheme_id}/draft/remap/preview
  • POST /api/v1/schemes/{scheme_id}/draft/remap/apply

Structure read model

  • GET /api/v1/schemes/{scheme_id}/current/sectors
  • GET /api/v1/schemes/{scheme_id}/current/groups
  • GET /api/v1/schemes/{scheme_id}/current/seats
  • GET /api/v1/schemes/{scheme_id}/current/seats/{seat_id}/price
  • GET /api/v1/schemes/{scheme_id}/current/svg
  • GET /api/v1/schemes/{scheme_id}/current/svg/display
  • GET /api/v1/schemes/{scheme_id}/current/svg/display/meta

Test mode

  • GET /api/v1/schemes/{scheme_id}/test/seats/{seat_id}

Audit

  • GET /api/v1/schemes/{scheme_id}/audit

Admin / ops

  • GET /api/v1/admin/schemes/{scheme_id}/current/artifacts
  • GET /api/v1/admin/schemes/{scheme_id}/current/validation
  • POST /api/v1/admin/schemes/{scheme_id}/current/display/regenerate
  • POST /api/v1/admin/display/backfill
  • GET /api/v1/admin/artifacts/publish-preview/audit
  • POST /api/v1/admin/artifacts/publish-preview/cleanup

Typical local flow

1. Read current version

Use:

GET /api/v1/schemes/{scheme_id}/current

2. Ensure draft

Use:

POST /api/v1/schemes/{scheme_id}/draft/ensure

Store returned:

  • scheme_version_id

3. Read draft state

Use:

  • GET /draft/summary?expected_scheme_version_id=...
  • GET /draft/structure?expected_scheme_version_id=...
  • GET /draft/validation?expected_scheme_version_id=...
  • GET /draft/compare-preview?expected_scheme_version_id=...

4. Perform editor mutations

Pass:

  • expected_scheme_version_id={draft_scheme_version_id}

on every mutation route.

5. Inspect pricing quality

Use:

  • GET /pricing/coverage
  • GET /pricing/unpriced-seats
  • GET /pricing/explain/{seat_id}
  • GET /pricing/rules/diagnostics

6. Create pricing snapshot

Use:

POST /draft/pricing/snapshot?expected_scheme_version_id=...

7. Inspect readiness

Use:

GET /draft/publish-readiness?expected_scheme_version_id=...

8. Publish

Use:

POST /publish?expected_scheme_version_id=...

Typed error expectations

Examples of stable typed errors already used in the service:

Draft concurrency/state

  • stale_current_version
  • stale_draft_version
  • draft_not_editable

Editor validation

  • duplicate_seat_id
  • duplicate_seat_id_in_payload
  • duplicate_sector_id
  • duplicate_group_id
  • duplicate_sector_element_id
  • duplicate_group_element_id
  • unknown_sector_id
  • unknown_group_id
  • unknown_sector_ids
  • unknown_group_ids
  • unknown_target_sector_id
  • unknown_target_group_id
  • remap_filter_required

Pricing / publish

  • invalid_amount
  • publish_not_ready

Draft summary semantics

GET /draft/summary is the compact route for editor bootstrap.

It returns:

  • current draft counters
  • validation summary
  • structure diff summary
  • publish readiness summary

This route is intended for frontend side panels / header status / quick preflight.

Notes

  • Draft-only routes must not mutate a published current version.
  • Published current version should require draft/ensure before any editor mutation.
  • Publish readiness can fail even if validation passes, for example when pricing snapshot is missing.
  • api-map.md and smoke-regression.md must be updated together with route changes.