Files
svg-frontend/doc/frontend-integration-contract.md
greebo 796f0f4af1 feat(frontend): add editor integration shell and draft read-model views
- add editor integration shell
- add editor context read flow
- add draft flow entry handling
- add summary, structure, validation and compare-preview views
- render backend read models for draft and published states
- verify sample-contract entities: 3 seats, 1 group, 1 sector
2026-03-20 18:51:21 +03:00

12 KiB

Backend Integration Contract

This document is the frontend handoff contract for the svg-service backend. It is written as an integration baseline, not as an internal backend README.

1. Base URL and Auth

  • Base URL: http://<host>:9020
  • API prefix: /api/v1
  • Auth header: X-API-Key

All non-/healthz routes require an API key.

Auth failure contract:

  • missing API key -> 401 with string detail: Missing API key
  • invalid API key -> 403 with string detail: Invalid API key
  • valid non-admin key on admin-only route -> 403 with string detail: Admin role required

2. Roles and Access Boundaries

  • admin
    • full access to protected routes
    • required for all /api/v1/admin/... routes
  • operator
    • allowed on non-admin protected routes
    • denied on admin-only routes
  • viewer
    • allowed on non-admin protected routes
    • denied on admin-only routes

Frontend implication:

  • admin UI must treat admin routes as optional capabilities gated by role
  • frontend must not assume operator or viewer can call cleanup, audit, backfill, or current-artifact admin routes

3. Core Entities

Upload

Represents one uploaded SVG source and its normalized/sanitized artifacts.

Important fields:

  • upload_id
  • original_filename
  • content_type
  • size_bytes
  • original_storage_path
  • sanitized_storage_path
  • normalized_storage_path
  • normalized_elements_count
  • normalized_seats_count
  • normalized_groups_count
  • normalized_sectors_count

Scheme

Top-level business object created from upload.

Important fields:

  • scheme_id
  • source_upload_id
  • name
  • status
  • current_version_number
  • published_at

Scheme Version

Versioned snapshot of the scheme structure and publish state.

Important fields:

  • scheme_version_id
  • scheme_id
  • version_number
  • status
  • normalized_storage_path
  • normalized_*_count

Sector

Structure entity in a specific scheme_version.

Important fields:

  • sector_record_id
  • sector_id
  • element_id
  • name

Business identity priority:

  • use sector_id when present
  • fallback to element_id
  • never treat sector_record_id as business identity across versions

Group

Important fields:

  • group_record_id
  • group_id
  • element_id
  • name

Business identity priority:

  • use group_id when present
  • fallback to element_id
  • never treat group_record_id as business identity across versions

Seat

Important fields:

  • seat_record_id
  • seat_id
  • element_id
  • sector_id
  • group_id
  • row_label
  • seat_number

Business identity priority:

  • use seat_id when present
  • fallback to element_id
  • never treat seat_record_id as business identity across versions

Pricing Category

Important fields:

  • pricing_category_id
  • scheme_id
  • name
  • code

Price Rule

Important fields:

  • price_rule_id
  • scheme_id
  • pricing_category_id
  • target_type
  • target_ref
  • amount
  • currency

Artifact

Artifact registry row for generated backend files.

Important fields:

  • artifact_id
  • artifact_type
  • artifact_variant
  • storage_path
  • status
  • meta_json

Important artifact types currently exercised by regression:

  • sanitized_svg
  • normalized_json
  • display_svg
  • publish_preview

4. Lifecycle State Machine

Fresh Upload

Flow:

  1. POST /api/v1/schemes/upload
  2. backend creates:
    • upload
    • scheme
    • initial scheme_version
    • structure rows
    • initial artifacts

Expected initial state:

  • scheme.status = draft
  • scheme.current_version_number = 1
  • current version status = draft

Current Draft

If current scheme/version is still draft:

  • editor works directly against current version
  • draft/ensure is idempotent
  • draft/ensure returns created=false

Ensure Draft From Published Current

If current scheme/version is published:

  • POST /api/v1/schemes/{scheme_id}/draft/ensure
  • backend creates a new draft version
  • current pointer switches to the new draft
  • version number increments

Publish

Preconditions:

  • current scheme is draft
  • current version is draft
  • publish readiness must be satisfied

Publish path:

  1. optional draft/pricing/snapshot
  2. GET draft/publish-readiness
  3. optional GET draft/publish-preview
  4. POST /api/v1/schemes/{scheme_id}/publish

Expected result:

  • scheme becomes published
  • current version becomes published

Rollback

Path:

  • POST /api/v1/schemes/{scheme_id}/rollback

Effect:

  • current pointer switches to requested historical version_number
  • scheme returns to draft
  • target version becomes current editable draft

Unpublish

Path:

  • POST /api/v1/schemes/{scheme_id}/unpublish

Effect:

  • current scheme becomes draft
  • current version becomes draft

5. Editor Flow

Entry Point

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

Use it first to decide whether:

  • current draft can be edited directly
  • or a new draft must be created from published current

Important response fields:

  • current_scheme_version_id
  • current_version_number
  • scheme_status
  • scheme_version_status
  • current_is_draft
  • create_draft_available
  • recommended_action

Draft Read Models

  • 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

Frontend should treat draft/structure as the main editable read model.

Patch Operations

Supported flows:

  • single seat patch
  • bulk seat patch
  • sector create/patch/delete
  • group create/patch/delete
  • repair references
  • remap preview/apply

Frontend rule:

  • always send expected_scheme_version_id when mutating or reading draft state after editor entry

Stale Conflict Handling

If backend returns a stale or draft editability conflict:

  • stop optimistic local mutation flow
  • re-read:
    • editor/context
    • draft/summary
    • draft/structure

Do not keep editing against stale cached scheme_version_id.

6. Pricing Flow

Categories

  • 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}

Rules

  • 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}

Read Models

  • GET /api/v1/schemes/{scheme_id}/pricing
  • 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
  • GET /api/v1/schemes/{scheme_id}/current/seats/{seat_id}/price
  • GET /api/v1/schemes/{scheme_id}/test/seats/{seat_id}

Frontend rule:

  • empty pricing on a fresh upload is valid
  • do not treat categories=[] and rules=[] as backend failure

7. Publish Flow

Main endpoints:

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

Frontend sequencing rule:

  1. ensure draft
  2. mutate if needed
  3. create/refresh pricing
  4. build pricing snapshot
  5. read publish readiness
  6. read publish preview if UI needs preview surface
  7. publish

8. Admin/Ops Flow

Admin-only endpoints:

  • 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

Healthy publish-preview audit contract:

  • orphan_files_count = 0
  • missing_files_for_db_rows_count = 0
  • db_rows_count == disk_files_count

Frontend implication:

  • admin tools must not be shown as generally available functionality
  • admin cleanup/destructive flows must be role-gated on the client and still handle backend 403

9. Typed Error Catalog

Auth

  • 401 string detail: Missing API key
  • 403 string detail: Invalid API key
  • 403 string detail: Admin role required

Lifecycle / Draft / Publish

  • stale_draft_version
  • stale_current_version
  • current_version_inconsistent
  • draft_not_editable
  • publish_not_ready

Editor Uniqueness / References

  • editor_uniqueness_error
  • editor_reference_error
  • 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
  • business_identifier_nullification_forbidden

Pricing / Remap / Test

  • invalid_amount
  • remap_filter_required
  • test_preview_failed

Validation Report Codes

These appear inside validation report payloads rather than as top-level HTTP conflict codes:

  • duplicate_seat_ids
  • missing_seat_contract
  • seats_without_sector_or_group
  • seats_without_price

Frontend rule:

  • do not parse only HTTP status
  • always inspect structured detail.code when detail is an object

10. Frontend Obligations

  • always handle auth failures 401 and 403
  • always handle stale/conflict responses on draft, publish, and lifecycle operations
  • never treat *_record_id as stable cross-version business identity
  • always prefer business ids:
    • seat -> seat_id, fallback element_id
    • sector -> sector_id, fallback element_id
    • group -> group_id, fallback element_id
  • re-read current/draft state after:
    • any 409
    • publish
    • rollback
    • unpublish
    • draft/ensure returning a newly created draft
  • do not assume current version remains stable across concurrent operator sessions
  • do not assume publish-preview artifacts or display artifacts are frontend-owned resources

11. Non-Persistent Assumptions Frontend Must Avoid

The frontend must not assume that these remain stable forever:

  • scheme_version_id
  • seat_record_id
  • sector_record_id
  • group_record_id
  • artifact storage_path
  • publish-preview cache artifacts

These are safe to treat as business-stable:

  • scheme_id
  • version_number within one scheme
  • seat_id when present
  • sector_id when present
  • group_id when present

12. Known Limitations / Deferred Tech Debt

  • some lifecycle negative contracts still return mixed styles:
    • typed object conflicts for 409
    • plain string details for some 404 and auth cases
  • validation warnings and error code families are not yet unified into one single global error envelope
  • admin/ops routes are backend-internal tools, not end-user product APIs
  • corruption remediation smoke exists only for publish_preview, not for every artifact type

13. Regression Baseline Frontend Can Rely On

The frontend can rely on the following regression-backed flows:

  • fresh upload on clean DB
  • current/draft/editor read flow
  • editor mutations and stale draft protection
  • pricing setup and publish flow
  • version lifecycle:
    • publish
    • ensure draft from published current
    • rollback
    • unpublish
  • admin ops:
    • audit
    • cleanup
    • destructive pricing cleanup for safe fixture categories
  • full admin permission matrix on implemented admin endpoints
  • controlled publish_preview corruption detection and remediation
  • negative upload validation
  • negative auth matrix
  • negative lifecycle matrix

For normal editor work:

  1. authenticate
  2. upload or pick scheme_id
  3. read editor/context
  4. call draft/ensure if needed
  5. read draft/structure
  6. mutate using current scheme_version_id
  7. on 409, reload editor state before retry
  8. configure pricing if needed
  9. create pricing snapshot
  10. read publish readiness / preview
  11. publish

For admin UI:

  1. verify admin role in client auth state
  2. call admin endpoints
  3. still handle backend 403
  4. treat cleanup and remediation as explicit operator actions, not background automation