3.3 KiB
Canonical OpenAPI
GET /openapi.json serves the OpenAPI 3 document bundled with the backend release.
The Swagger UI and its Swagger 2 export remain available at /.
The source is the actual DRF serializers and view annotations. build.mjs converts
Swagger 2 to OpenAPI 3, adds the source-record discriminated variants and the named
ordering enum with comma-separated ordering, then validates the resulting document.
Do not edit src/core/openapi.json or generated TypeScript by hand.
From the backend root (Python dependencies installed with uv):
PYTHONPATH=src uv run python src/manage.py generate_swagger tools/openapi/swagger.json --overwrite --format=json --settings=settings.test
cd tools/openapi
bun install --frozen-lockfile --ignore-scripts
bun build.mjs
bun run apigen
bun run type-check
The runtime gate also validates rendered responses from the real DRF list/detail views against Orval's generated Zod schemas, including every documented HTTP status. Generate its synthetic fixtures in the isolated test database, then run:
# From the backend root. The parent directory of this explicit path must exist.
OPENAPI_RUNTIME_FIXTURES=/tmp/mostovik-openapi-runtime.json PYTHONPATH=src uv run pytest tests/apps/organizations/test_openapi_runtime_fixtures.py -q
cd tools/openapi
bun run apigen
bun run contract-check /tmp/mostovik-openapi-runtime.json
Zod is pinned to 4.3.6. The gate covers a full Budget payload with unknown raw
fields, all five SME size units, nullable SRO admission dates, mixed/filtered lists,
empty arrays and zero counts, and 400/404 errors. Parsed responses must equal the
original JSON so that an apparently successful parser cannot silently discard
raw keys. Ordinary pytest runs write no fixture file; writing is enabled only by
OPENAPI_RUNTIME_FIXTURES. No live upstream requests or business databases are
used. The generated validators are disposable build output, never handwritten.
Bun is pinned to 1.3.9 in CI; the tool dependencies are locked in bun.lock.
CI regenerates the source Swagger document, runs bun build.mjs --check, generates
a client and compiles a strict TypeScript fixture. It fails if the bundled contract
is stale, invalid, loses discrimination, or removes the named ordering enum.
For the frontend, copy the bundled document to its openapi.json, then run its own
bun run apigen and bun run type-check. Keep source-group checks before reading
source-specific payloads. Multipart upload operations now have one accurately
documented content type, so Orval generates ...Mutation rather than the former
multi-content-type ...MutationWithFormData name. The generated function still
constructs FormData and sends multipart data.
Before accepting an upgraded live dataset:
python src/manage.py migrate --noinput
python src/manage.py normalize_published_registry_payloads
python src/manage.py normalize_published_registry_payloads --apply
python src/manage.py build_source_record_exports
The first normalization command is a dry run. Applying it preserves record UIDs,
publication timestamps and artifact IDs, but atomically increments the affected
artifact content revisions. New export tickets return 503 until a matching export
generation is built; already issued tickets keep their pinned generation.
SRO access remains gated as described in docs/sro-membership-source.md.