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
Member

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

PathPurpose
crates/aisix-core/src/bin/dump-schema.rsThe tool (~70 lines, hand-rolled static list — no clap)
schemas/README.mdRegeneration command, layout, downstream consumers
schemas/resources/api_key.schema.jsonGenerated
schemas/resources/cache_policy.schema.jsonGenerated
schemas/resources/guardrail.schema.jsonGenerated
schemas/resources/model.schema.jsonGenerated
schemas/resources/observability_exporter.schema.jsonGenerated
schemas/resources/provider_key.schema.jsonGenerated
schemas/resources/rate_limit.schema.jsonGenerated
schemas/resources/rate_limit_policy.schema.jsonGenerated
schemas/resources/routing.schema.jsonGenerated

~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).

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).
CopilotAI review requested due to automatic review settings May 17, 2026 01:01
@coderabbitai

coderabbitaiBot 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.

CopilotAI 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 mainMay 17, 2026 01:41
@moonmingmoonming reopened this May 17, 2026
@moonming
moonming merged commit 36ed90b into mainMay 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 freeto 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

@moonming
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
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
Member

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

PathPurpose
crates/aisix-core/src/bin/dump-schema.rsThe tool (~70 lines, hand-rolled static list — no clap)
schemas/README.mdRegeneration command, layout, downstream consumers
schemas/resources/api_key.schema.jsonGenerated
schemas/resources/cache_policy.schema.jsonGenerated
schemas/resources/guardrail.schema.jsonGenerated
schemas/resources/model.schema.jsonGenerated
schemas/resources/observability_exporter.schema.jsonGenerated
schemas/resources/provider_key.schema.jsonGenerated
schemas/resources/rate_limit.schema.jsonGenerated
schemas/resources/rate_limit_policy.schema.jsonGenerated
schemas/resources/routing.schema.jsonGenerated

~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).

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).
CopilotAI review requested due to automatic review settings May 17, 2026 01:01
@coderabbitai

coderabbitaiBot 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.

CopilotAI 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 mainMay 17, 2026 01:41
@moonmingmoonming reopened this May 17, 2026
@moonming
moonming merged commit 36ed90b into mainMay 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 freeto 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

@moonming
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
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
Member

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

PathPurpose
crates/aisix-core/src/bin/dump-schema.rsThe tool (~70 lines, hand-rolled static list — no clap)
schemas/README.mdRegeneration command, layout, downstream consumers
schemas/resources/api_key.schema.jsonGenerated
schemas/resources/cache_policy.schema.jsonGenerated
schemas/resources/guardrail.schema.jsonGenerated
schemas/resources/model.schema.jsonGenerated
schemas/resources/observability_exporter.schema.jsonGenerated
schemas/resources/provider_key.schema.jsonGenerated
schemas/resources/rate_limit.schema.jsonGenerated
schemas/resources/rate_limit_policy.schema.jsonGenerated
schemas/resources/routing.schema.jsonGenerated

~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).

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).
CopilotAI review requested due to automatic review settings May 17, 2026 01:01
@coderabbitai

coderabbitaiBot 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.

CopilotAI 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 mainMay 17, 2026 01:41
@moonmingmoonming reopened this May 17, 2026
@moonming
moonming merged commit 36ed90b into mainMay 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 freeto 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

@moonming
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
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
Member

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

PathPurpose
crates/aisix-core/src/bin/dump-schema.rsThe tool (~70 lines, hand-rolled static list — no clap)
schemas/README.mdRegeneration command, layout, downstream consumers
schemas/resources/api_key.schema.jsonGenerated
schemas/resources/cache_policy.schema.jsonGenerated
schemas/resources/guardrail.schema.jsonGenerated
schemas/resources/model.schema.jsonGenerated
schemas/resources/observability_exporter.schema.jsonGenerated
schemas/resources/provider_key.schema.jsonGenerated
schemas/resources/rate_limit.schema.jsonGenerated
schemas/resources/rate_limit_policy.schema.jsonGenerated
schemas/resources/routing.schema.jsonGenerated

~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).

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).
CopilotAI review requested due to automatic review settings May 17, 2026 01:01
@coderabbitai

coderabbitaiBot 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.

CopilotAI 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 mainMay 17, 2026 01:41
@moonmingmoonming reopened this May 17, 2026
@moonming
moonming merged commit 36ed90b into mainMay 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 freeto 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

@moonming
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
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
Member

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

PathPurpose
crates/aisix-core/src/bin/dump-schema.rsThe tool (~70 lines, hand-rolled static list — no clap)
schemas/README.mdRegeneration command, layout, downstream consumers
schemas/resources/api_key.schema.jsonGenerated
schemas/resources/cache_policy.schema.jsonGenerated
schemas/resources/guardrail.schema.jsonGenerated
schemas/resources/model.schema.jsonGenerated
schemas/resources/observability_exporter.schema.jsonGenerated
schemas/resources/provider_key.schema.jsonGenerated
schemas/resources/rate_limit.schema.jsonGenerated
schemas/resources/rate_limit_policy.schema.jsonGenerated
schemas/resources/routing.schema.jsonGenerated

~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).

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).
CopilotAI review requested due to automatic review settings May 17, 2026 01:01
@coderabbitai

coderabbitaiBot 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.

CopilotAI 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 mainMay 17, 2026 01:41
@moonmingmoonming reopened this May 17, 2026
@moonming
moonming merged commit 36ed90b into mainMay 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 freeto 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

@moonming
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
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
Member

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

PathPurpose
crates/aisix-core/src/bin/dump-schema.rsThe tool (~70 lines, hand-rolled static list — no clap)
schemas/README.mdRegeneration command, layout, downstream consumers
schemas/resources/api_key.schema.jsonGenerated
schemas/resources/cache_policy.schema.jsonGenerated
schemas/resources/guardrail.schema.jsonGenerated
schemas/resources/model.schema.jsonGenerated
schemas/resources/observability_exporter.schema.jsonGenerated
schemas/resources/provider_key.schema.jsonGenerated
schemas/resources/rate_limit.schema.jsonGenerated
schemas/resources/rate_limit_policy.schema.jsonGenerated
schemas/resources/routing.schema.jsonGenerated

~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).

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).
CopilotAI review requested due to automatic review settings May 17, 2026 01:01
@coderabbitai

coderabbitaiBot 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.

CopilotAI 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 mainMay 17, 2026 01:41
@moonmingmoonming reopened this May 17, 2026
@moonming
moonming merged commit 36ed90b into mainMay 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 freeto 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

@moonming
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
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
Member

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

PathPurpose
crates/aisix-core/src/bin/dump-schema.rsThe tool (~70 lines, hand-rolled static list — no clap)
schemas/README.mdRegeneration command, layout, downstream consumers
schemas/resources/api_key.schema.jsonGenerated
schemas/resources/cache_policy.schema.jsonGenerated
schemas/resources/guardrail.schema.jsonGenerated
schemas/resources/model.schema.jsonGenerated
schemas/resources/observability_exporter.schema.jsonGenerated
schemas/resources/provider_key.schema.jsonGenerated
schemas/resources/rate_limit.schema.jsonGenerated
schemas/resources/rate_limit_policy.schema.jsonGenerated
schemas/resources/routing.schema.jsonGenerated

~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).

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).
CopilotAI review requested due to automatic review settings May 17, 2026 01:01
@coderabbitai

coderabbitaiBot 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.

CopilotAI 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 mainMay 17, 2026 01:41
@moonmingmoonming reopened this May 17, 2026
@moonming
moonming merged commit 36ed90b into mainMay 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 freeto 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

@moonming
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
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
Member

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

PathPurpose
crates/aisix-core/src/bin/dump-schema.rsThe tool (~70 lines, hand-rolled static list — no clap)
schemas/README.mdRegeneration command, layout, downstream consumers
schemas/resources/api_key.schema.jsonGenerated
schemas/resources/cache_policy.schema.jsonGenerated
schemas/resources/guardrail.schema.jsonGenerated
schemas/resources/model.schema.jsonGenerated
schemas/resources/observability_exporter.schema.jsonGenerated
schemas/resources/provider_key.schema.jsonGenerated
schemas/resources/rate_limit.schema.jsonGenerated
schemas/resources/rate_limit_policy.schema.jsonGenerated
schemas/resources/routing.schema.jsonGenerated

~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).

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).
CopilotAI review requested due to automatic review settings May 17, 2026 01:01
@coderabbitai

coderabbitaiBot 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.

CopilotAI 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 mainMay 17, 2026 01:41
@moonmingmoonming reopened this May 17, 2026
@moonming
moonming merged commit 36ed90b into mainMay 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 freeto 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

@moonming