chore(core): add dump-schema binary and commit canonical schemas - #308
Conversation
Adds `schemars::JsonSchema` derive to every public resource struct and enum in `aisix-core::models`: - ApiKey - CacheBackend, CachePolicy, AppliesTo - GuardrailHookPoint, KeywordPattern, KeywordConfig, BedrockAWSCredentials, BedrockLatencyMode, BedrockConfig, GuardrailKind, Guardrail - Provider, Adapter, ModelCost, BackgroundModelCheck, CooldownConfig, Model - ExporterKind, OtlpHttpConfig, ObservabilityExporter - ProviderKey, TelemetryTags, RequestOverrides, ParamConstraints, ResponseOverrides, StreamDoneMarker - RateLimit - RateLimitPolicy - RoutingStrategy, RoutingTarget, OnAllFilteredPolicy, Routing Wires `schemars.workspace = true` into `aisix-core` (the workspace already pinned `schemars = "0.8"` but no crate consumed it). ## Why Refs #304 item #1: canonical JSON Schema as config source of truth. This PR is the foundational derive pass — no schema files are emitted yet (that comes in the follow-up `dump-schema` binary PR). Adding the derives in isolation lets the compiler validate serde-schemars compatibility across every resource without mixing in a tooling change. ## Scope Zero behavior change. `JsonSchema` is a pure compile-time additional trait impl; it does not affect serde paths, dispatch, etcd loader, or any runtime behavior. All existing serde annotations (`deny_unknown_fields`, `rename_all`, `default`, `skip_serializing_if`, `skip`) are honored verbatim by `schemars` 0.8. ## Verification - `cargo check --workspace` - `cargo clippy --workspace --all-targets -- -D warnings` - `cargo fmt --all -- --check` - `cargo test -p aisix-core --lib` (168 passed)
Introduces `cargo run -p aisix-core --bin dump-schema`, a small
in-tree code-generation tool that walks `aisix-core`'s nine top-level
resource types and writes one JSON Schema draft-07 document per type
into `schemas/resources/`. Each file is self-contained — nested types
(Adapter, RoutingTarget, TelemetryTags, …) live in the parent's
`definitions/` section, no cross-file `$ref` is emitted.
## Files
- `crates/aisix-core/src/bin/dump-schema.rs` (66 lines, hand-written
to avoid leaning on a clap-style harness for a 9-line static list)
- `schemas/README.md` — regeneration command + downstream consumers
- `schemas/resources/{api_key, cache_policy, guardrail, model,
observability_exporter, provider_key, rate_limit, rate_limit_policy,
routing}.schema.json` — 9 generated files (~1350 lines of JSON)
## Why
Refs #304 item #1. This is the first time the
in-tree Rust resource shapes are published as a language-agnostic
contract artifact. Downstream consumers (cp-api request validation
in `api7/AISIX-Cloud`, dashboard form rendering, the DP admin OpenAPI
doc) can now `$ref` these files instead of redefining the shapes.
## Verification
- `cargo run -p aisix-core --bin dump-schema` succeeds and writes all
nine files (printed paths captured in PR description)
- Schema output validated against expected shape:
- `additionalProperties: false` correctly translated from
`#[serde(deny_unknown_fields)]`
- `required: [...]` lists non-Option fields only
- Doc-comments on fields land as `description` in the schema
- Adapter enum's `kebab-case` rename serializes variants as
`"openai" / "anthropic" / "bedrock" / "vertex" / "azure-openai"`
- `cargo clippy --workspace --all-targets -- -D warnings` clean
- `cargo fmt --all -- --check` clean
## Scope
Pure additive: one new binary, one new top-level directory. No
existing source file is modified. The generated schemas are not yet
consumed anywhere — that comes in two follow-ups:
- CI drift check (regenerate in CI, fail if `git diff schemas/` is
non-empty)
- `crates/aisix-admin/src/openapi.rs` refactor: replace inline schemas
with `$ref` into `schemas/resources/*.schema.json`
Stacked on #307 (`chore(core): derive JsonSchema on
resource types`). Merging requires #307 first.
Refs #304 (#1).
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Free Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
Note 🎁 Summarized by CodeRabbit FreeYour organization has reached its limit of developer seats under the Pro Plan. For new users, CodeRabbit will generate a high-level summary and a walkthrough for each pull request. For a comprehensive line-by-line review, please add seats to your subscription by visiting https://app.coderabbit.ai/login.If you believe this is a mistake and have available seats, please assign one to the pull request author through the subscription management page using the link above. Comment |
…ptions Two README clarifications surfaced during independent audit of the canonical-schemas PR: 1. **Naming namespace** — file names use the snake_case singular form of the Rust type (`api_key.schema.json`); the etcd key prefix uses the plural `Resource::kind()` value (`api_keys`). The two conventions are deliberately distinct (per-type artifact vs. collection prefix). Spelling it out keeps downstream tooling authors from assuming one when the other applies. 2. **Forward-compat exceptions** — three resources intentionally omit `additionalProperties: false` in their generated schemas: `guardrail` (serde flatten + tag incompatibility), `cache_policy` (cp-api may ship fields ahead of DP rollout), and `observability_exporter` (same forward-compat reason). Downstream consumers that default to strict validation should know to relax the check on these three. Both notes are documentation-only; the underlying schemas (and the Rust types) are unchanged. Refs #304 (#1).
Adds a new `schema-drift` job to the CI workflow that runs `cargo run -p aisix-core --bin dump-schema` and asserts `git diff --exit-code schemas/` is clean. PRs that modify resource struct in `crates/aisix-core/src/models/` but forget to regenerate the schema files now fail CI with a fix instruction in the error message. ## Why Refs #304 item #1. The `dump-schema` tool and `schemas/resources/*.schema.json` files were introduced in #308; without an enforcement mechanism the committed schemas can silently diverge from the Rust types as the resource graph evolves (especially during issue #302 Phase A, which is actively mutating ProviderKey / Model). This job is that enforcement. ## Job placement Sits as a peer to `lint` — fast, independent, no service deps. Runs in parallel with `lint` / `rust-unit` / `build-bin`. Not a `needs:` target of any downstream job, so a drift failure does not block the e2e or coverage signals. ## Verification - Positive path: `cargo run -p aisix-core --bin dump-schema` on the HEAD of this PR succeeds and `git diff --exit-code schemas/` is empty (no drift in tree) - Negative path: locally introduced a synthetic drift by truncating `schemas/resources/api_key.schema.json` to `{}`. `git diff --exit-code schemas/` returned non-zero — the check fires as expected. Reverted with `git checkout schemas/resources/api_key.schema.json`. - YAML parses with `python3 -c "import yaml; yaml.safe_load(open(...))"`. ## Stack Builds on #308 (which adds the binary + initial schemas). Base will switch to `main` once #308 merges. Refs #304 (#1).
The hand-written OpenAPI 3.1 document in `crates/aisix-admin/src/openapi.rs` previously inlined its own copy of every resource schema (`Model`, `ApiKey`, `ProviderKey`, `Guardrail`, `CachePolicy`, `ObservabilityExporter`, `RateLimit`, `Routing`, plus the nested `ModelCost` / `BackgroundModelCheck`). That left three places to keep in sync whenever a resource field changed: the Rust struct, the inline OpenAPI schema, and the cp-api / dashboard side. This PR cuts the duplication. The Rust struct is now the single source of truth; `dump-schema` (PR #308) writes canonical draft-07 JSON Schemas into `schemas/resources/*.schema.json`; CI (PR #309) enforces those files match the structs. This commit: 1. Removes the ten inlined resource schemas from `OPENAPI_JSON_BASE` (the const formerly named `OPENAPI_JSON`). 2. Embeds the eight canonical schema files at compile time via `include_str!` into a new `RESOURCE_SCHEMAS` const. 3. Adds `merged_openapi()` — runs once on first request, parses the base spec, parses each embedded schema, hoists `definitions/*` into top-level `components.schemas`, rewrites `$ref: #/definitions/X` to `$ref: #/components/schemas/X` (JSON Schema draft-07 → OpenAPI 3.1), and caches the result in an `OnceLock<String>`. 4. Changes `openapi_json()` to serve the merged doc instead of the raw `OPENAPI_JSON_BASE`. 5. Updates the three openapi unit tests to parse `merged_openapi()`. ## What this means for `/admin/openapi.json` The served document keeps the same wrapper schemas (`ModelEntry`, `ApiKeyEntry`, `ModelStatusView`, `ModelKind`, `RuntimeStatus`, `SystemTime`, `AdminError`) and gains 16 new top-level component schemas hoisted from the resource definitions (`Adapter`, `BedrockConfig`, `CacheBackend`, `CooldownConfig`, `GuardrailHookPoint`, `KeywordPattern`, `OnAllFilteredPolicy`, `ParamConstraints`, `Provider`, `RequestOverrides`, `ResponseOverrides`, `RoutingStrategy`, `RoutingTarget`, `StreamDoneMarker`, `TelemetryTags`, etc.). The resource schemas themselves are now precise reflections of the Rust types — e.g. `Guardrail` uses a proper `oneOf` discriminator on `kind` instead of the previous flat `additionalProperties: true` hand-wave; `Provider` lists its 6 variants from the actual enum; `Adapter` lists the 5 wire-shape kebab-case values from #302 Phase A. ## Verification - `cargo check -p aisix-admin` clean - `cargo clippy --workspace --all-targets -- -D warnings` clean - `cargo fmt --all -- --check` clean - `cargo test -p aisix-admin --lib` — all 7 openapi tests pass, including the regression test `openapi_apikey_schema_excludes_max_budget_usd` - External validation: parsed the merged doc, collected 43 `$ref` references across 32 distinct targets, all resolve inside `#/components/schemas/*` (0 unresolved) ## Why nested `if let` instead of let-chains Workspace is on `edition = "2021"`. The merge logic uses one level of nesting in two spots; not pretty, but `edition = "2024"` is a separate decision not in this PR's scope. ## Stack Builds on: - #307 (JsonSchema derives on resource structs) - #308 (dump-schema binary + initial schema files) - #309 (CI drift enforcement) Merge order: 307 → 308 → 309 → this PR. Base will switch to `main` once #308 merges. Refs #304 (#1).
Summary
Introduces
cargo run -p aisix-core --bin dump-schema, an in-treecode-generation tool that walks
aisix-core's nine top-level resourcetypes and writes one JSON Schema draft-07 document per type into
schemas/resources/. Commits the initial generated outputs alongsidethe tool.
Files added
crates/aisix-core/src/bin/dump-schema.rsschemas/README.mdschemas/resources/api_key.schema.jsonschemas/resources/cache_policy.schema.jsonschemas/resources/guardrail.schema.jsonschemas/resources/model.schema.jsonschemas/resources/observability_exporter.schema.jsonschemas/resources/provider_key.schema.jsonschemas/resources/rate_limit.schema.jsonschemas/resources/rate_limit_policy.schema.jsonschemas/resources/routing.schema.json~1,350 lines of JSON across 9 files. Each file is self-contained — nested types (
Adapter,RoutingTarget,TelemetryTags, etc.) live in the parent'sdefinitions/section, no cross-file$refis emitted.Why
Refs #304 item #1. First publication of in-tree Rust resource shapes as a language-agnostic contract artifact. Downstream consumers (cp-api request validation in
api7/AISIX-Cloud, dashboard form rendering with RJSF, DP admin OpenAPI doc) can now$refthese files instead of redefining the shapes.Schema quality spot-checks
additionalProperties: falsecorrectly translated from#[serde(deny_unknown_fields)]required: [...]lists non-Option<>fields onlydescriptionin the output schemaAdapterenum's#[serde(rename_all = "kebab-case")]produces variants"openai" / "anthropic" / "bedrock" / "vertex" / "azure-openai"(verified directly inprovider_key.schema.json'sdefinitions/Adapter)Providerenum's#[serde(rename_all = "lowercase")]produces variants"openai" / "anthropic" / "google" / "deepseek" / "cohere" / "jina"(verified inmodel.schema.json)Regeneration
Writes the same files (idempotent). Drift will be enforced by a CI workflow in the follow-up PR.
Verification
cargo run -p aisix-core --bin dump-schemasucceeds; 9 files writtencargo clippy --workspace --all-targets -- -D warningscleancargo fmt --all -- --checkcleanScope
Pure additive. One new binary, one new top-level directory. No existing source file is modified.
Follow-ups (separate PRs)
git diff --exit-code schemas/)crates/aisix-admin/src/openapi.rsrefactor: replace inline schemas with$refinto these filesRefs #304 (#1).