Skip to content

chore(core): add dump-schema binary and commit canonical schemas - #308

Merged
moonming merged 3 commits into
mainfrom
chore/dump-schema-binary
May 17, 2026
Merged

chore(core): add dump-schema binary and commit canonical schemas#308
moonming merged 3 commits into
mainfrom
chore/dump-schema-binary

Conversation

@moonming

Copy link
Copy Markdown
Collaborator

Stacked on #307. Base will switch to main once #307 merges.

Summary

Introduces cargo run -p aisix-core --bin dump-schema, an 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/. Commits the initial generated outputs alongside
the tool.

Files added

Path Purpose
crates/aisix-core/src/bin/dump-schema.rs The tool (~70 lines, hand-rolled static list — no clap)
schemas/README.md Regeneration command, layout, downstream consumers
schemas/resources/api_key.schema.json Generated
schemas/resources/cache_policy.schema.json Generated
schemas/resources/guardrail.schema.json Generated
schemas/resources/model.schema.json Generated
schemas/resources/observability_exporter.schema.json Generated
schemas/resources/provider_key.schema.json Generated
schemas/resources/rate_limit.schema.json Generated
schemas/resources/rate_limit_policy.schema.json Generated
schemas/resources/routing.schema.json Generated

~1,350 lines of JSON across 9 files. Each file is self-contained — nested types (Adapter, RoutingTarget, TelemetryTags, etc.) live in the parent's definitions/ section, no cross-file $ref is 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 $ref these files instead of redefining the shapes.

Schema quality spot-checks

  • additionalProperties: false correctly translated from #[serde(deny_unknown_fields)]
  • required: [...] lists non-Option<> fields only
  • Doc-comments on fields land as description in the output schema
  • Adapter enum's #[serde(rename_all = "kebab-case")] produces variants "openai" / "anthropic" / "bedrock" / "vertex" / "azure-openai" (verified directly in provider_key.schema.json's definitions/Adapter)
  • Provider enum's #[serde(rename_all = "lowercase")] produces variants "openai" / "anthropic" / "google" / "deepseek" / "cohere" / "jina" (verified in model.schema.json)

Regeneration

cargo run -p aisix-core --bin dump-schema

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-schema succeeds; 9 files written
  • cargo clippy --workspace --all-targets -- -D warnings clean
  • cargo fmt --all -- --check clean
  • Generated schemas inspected by hand for shape correctness (see spot-checks above)

Scope

Pure additive. One new binary, one new top-level directory. No existing source file is modified.

Follow-ups (separate PRs)

  • CI drift check workflow (regenerate in CI, git diff --exit-code schemas/)
  • crates/aisix-admin/src/openapi.rs refactor: replace inline schemas with $ref into these files

Refs #304 (#1).

moonming added 2 commits May 17, 2026 08:53
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).
Copilot AI review requested due to automatic review settings May 17, 2026 01:01
@coderabbitai

coderabbitai Bot commented May 17, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Free

Run ID: 1c3b3d0d-6728-480a-a719-a0afb2d27fcf

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Note

🎁 Summarized by CodeRabbit Free

Your 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 @coderabbitai help to get the list of available commands and usage tips.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

…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).
@moonming
moonming changed the base branch from chore/add-schemars-derive to main May 17, 2026 01:41
@moonming moonming closed this May 17, 2026
@moonming moonming reopened this May 17, 2026
@moonming
moonming merged commit 36ed90b into main May 17, 2026
7 checks passed
moonming added a commit that referenced this pull request May 17, 2026
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).
moonming added a commit that referenced this pull request May 17, 2026
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).
@jarvis9443
jarvis9443 deleted the chore/dump-schema-binary branch June 25, 2026 06:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants