feat(backend): add operational smoke tooling and safe pricing cleanup endpoints
- add backend README and refresh API map and smoke regression docs - add full backend smoke regression script - add admin pricing cleanup preview and dry-run endpoints - add helper script for test pricing cleanup - verify typed error contracts, draft flow, publish readiness and preview flows - verify publish preview retention and clean backend startup behavior
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
# svg-service backend
|
||||
|
||||
Backend for SVG scheme upload, draft editing, pricing, publish preview, and publish lifecycle.
|
||||
Backend for SVG scheme upload, draft editing, pricing, diagnostics, publish preview, and publish lifecycle.
|
||||
|
||||
## Stack
|
||||
|
||||
@@ -32,16 +32,15 @@ Default local admin 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
|
||||
3. Enter editor flow through context + ensure draft
|
||||
4. Edit sectors / groups / seats in current draft
|
||||
5. Configure pricing and inspect diagnostics
|
||||
6. Build pricing snapshot
|
||||
7. Inspect publish readiness and publish preview
|
||||
8. Publish current draft
|
||||
9. If editing is needed after publish, create or ensure a new draft again
|
||||
|
||||
## Main concepts
|
||||
|
||||
@@ -49,23 +48,19 @@ The backend works with a scheme lifecycle:
|
||||
Top-level business entity.
|
||||
|
||||
### Scheme version
|
||||
Concrete version of the scheme.
|
||||
A version can be `draft` or `published`.
|
||||
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.
|
||||
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.
|
||||
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
|
||||
@@ -74,30 +69,23 @@ Stored technical artifacts, including:
|
||||
|
||||
## 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:
|
||||
|
||||
Returned `scheme_version_id` should be reused as:
|
||||
- `expected_scheme_version_id`
|
||||
|
||||
for draft reads and mutations.
|
||||
@@ -105,14 +93,10 @@ 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:
|
||||
|
||||
Typical typed conflicts:
|
||||
- `stale_current_version`
|
||||
- `stale_draft_version`
|
||||
- `draft_not_editable`
|
||||
@@ -120,22 +104,19 @@ Typical typed conflict payload:
|
||||
|
||||
## Main operator routes
|
||||
|
||||
## System
|
||||
|
||||
### System
|
||||
- `GET /healthz`
|
||||
- `GET /api/v1/ping`
|
||||
- `GET /api/v1/db/ping`
|
||||
- `GET /api/v1/manifest`
|
||||
|
||||
## Uploads
|
||||
|
||||
### 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
|
||||
|
||||
### Scheme registry
|
||||
- `GET /api/v1/schemes`
|
||||
- `GET /api/v1/schemes/{scheme_id}`
|
||||
- `GET /api/v1/schemes/{scheme_id}/current`
|
||||
@@ -147,8 +128,7 @@ Typical typed conflict payload:
|
||||
- `POST /api/v1/schemes/{scheme_id}/unpublish`
|
||||
- `POST /api/v1/schemes/{scheme_id}/rollback`
|
||||
|
||||
## Editor / draft
|
||||
|
||||
### 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`
|
||||
@@ -168,8 +148,7 @@ Typical typed conflict payload:
|
||||
- `PATCH /api/v1/schemes/{scheme_id}/draft/groups/records/{group_record_id}`
|
||||
- `POST /api/v1/schemes/{scheme_id}/draft/repair-references`
|
||||
|
||||
## Pricing
|
||||
|
||||
### 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}`
|
||||
@@ -178,22 +157,19 @@ Typical typed conflict payload:
|
||||
- `PUT /api/v1/schemes/{scheme_id}/pricing/rules/{price_rule_id}`
|
||||
- `DELETE /api/v1/schemes/{scheme_id}/pricing/rules/{price_rule_id}`
|
||||
|
||||
## Pricing diagnostics
|
||||
|
||||
### 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
|
||||
|
||||
### 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
|
||||
|
||||
### 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`
|
||||
@@ -202,129 +178,85 @@ Typical typed conflict payload:
|
||||
- `GET /api/v1/schemes/{scheme_id}/current/svg/display`
|
||||
- `GET /api/v1/schemes/{scheme_id}/current/svg/display/meta`
|
||||
|
||||
## Test mode
|
||||
|
||||
### Test mode
|
||||
- `GET /api/v1/schemes/{scheme_id}/test/seats/{seat_id}`
|
||||
|
||||
## Audit
|
||||
|
||||
### Audit
|
||||
- `GET /api/v1/schemes/{scheme_id}/audit`
|
||||
|
||||
## Admin / ops
|
||||
|
||||
### 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`
|
||||
- `GET /api/v1/admin/schemes/{scheme_id}/pricing/categories/cleanup-preview`
|
||||
- `POST /api/v1/admin/schemes/{scheme_id}/pricing/categories/cleanup`
|
||||
|
||||
## Cleanup of test pricing data
|
||||
|
||||
Cleanup endpoints are intended for removing diagnostic / test categories accidentally accumulated in a shared scheme.
|
||||
|
||||
Preview candidates:
|
||||
`GET /api/v1/admin/schemes/{scheme_id}/pricing/categories/cleanup-preview`
|
||||
|
||||
Execute cleanup:
|
||||
`POST /api/v1/admin/schemes/{scheme_id}/pricing/categories/cleanup`
|
||||
|
||||
Safety notes:
|
||||
- use `dry_run=true` first
|
||||
- keep `delete_only_without_rules=true` unless you intentionally want a harder cleanup
|
||||
- prefer matching by prefixes instead of raw ids for repetitive test artifacts
|
||||
|
||||
Helper script:
|
||||
- `backend/scripts/cleanup_test_pricing_data.sh`
|
||||
|
||||
Example:
|
||||
`SCHEME_ID=... DRY_RUN=true ./backend/scripts/cleanup_test_pricing_data.sh`
|
||||
|
||||
## Typical local flow
|
||||
|
||||
## 1. Read current version
|
||||
|
||||
Use:
|
||||
|
||||
### 1. Read current version
|
||||
`GET /api/v1/schemes/{scheme_id}/current`
|
||||
|
||||
## 2. Ensure draft
|
||||
|
||||
Use:
|
||||
|
||||
### 2. Ensure draft
|
||||
`POST /api/v1/schemes/{scheme_id}/draft/ensure`
|
||||
|
||||
Store returned:
|
||||
|
||||
- `scheme_version_id`
|
||||
|
||||
## 3. Read draft state
|
||||
|
||||
Use:
|
||||
|
||||
### 3. Read draft state
|
||||
- `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
|
||||
|
||||
### 4. Perform editor mutations
|
||||
Pass:
|
||||
|
||||
- `expected_scheme_version_id={draft_scheme_version_id}`
|
||||
|
||||
on every mutation route.
|
||||
|
||||
## 5. Inspect pricing quality
|
||||
|
||||
Use:
|
||||
|
||||
### 5. Inspect pricing quality
|
||||
- `GET /pricing/coverage`
|
||||
- `GET /pricing/unpriced-seats`
|
||||
- `GET /pricing/explain/{seat_id}`
|
||||
- `GET /pricing/rules/diagnostics`
|
||||
|
||||
## 6. Create pricing snapshot
|
||||
### 6. Build snapshot and inspect readiness
|
||||
- `POST /draft/pricing/snapshot`
|
||||
- `GET /draft/publish-readiness`
|
||||
- `GET /draft/publish-preview?refresh=true`
|
||||
|
||||
Use:
|
||||
### 7. Publish
|
||||
- `POST /publish?expected_scheme_version_id=...`
|
||||
|
||||
`POST /draft/pricing/snapshot?expected_scheme_version_id=...`
|
||||
## Regression
|
||||
|
||||
## 7. Inspect readiness
|
||||
Main operator regression:
|
||||
- `backend/scripts/smoke_regression.sh`
|
||||
|
||||
Use:
|
||||
Run:
|
||||
`API_URL=http://127.0.0.1:9020 API_KEY=admin-local-dev-key SCHEME_ID=... ./backend/scripts/smoke_regression.sh`
|
||||
|
||||
`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.
|
||||
|
||||
Reference in New Issue
Block a user