feat(server): generate OpenAPI spec with utoipa (#241)#271
feat(server): generate OpenAPI spec with utoipa (#241)#271haseebrabbani wants to merge 3 commits into
Conversation
Integrate utoipa to auto-generate an OpenAPI 3.1 spec from the HTTP handlers and wire models, so API docs stay in sync with the code. - Annotate every HTTP handler (client, dashboard, and feature-gated EVM surfaces) with `#[utoipa::path]` and derive `ToSchema` / `IntoParams` on the request/response/query/model types. - Add `openapi::openapi()` which assembles the document and merges the EVM routes only when the `evm` feature is compiled in. - Serve the spec at `GET /api-docs/openapi.json` (unauthenticated, read-only) and add a `gen-openapi` binary to write it to a file. - Commit the generated `docs/openapi.json` (built with `evm`) and document it in `docs/OPENAPI.md`. 36 operations / 82 schemas. Purely additive: no wire-shape changes, so the Rust/TS clients need no updates. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Important Review skippedAuto incremental reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
WalkthroughThis PR integrates ChangesOpenAPI Spec Generation Integration
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Suggested labels
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Pull request overview
This PR integrates utoipa into the guardian-server crate to generate an OpenAPI 3.1 specification directly from HTTP handlers and wire models, then exposes and documents that spec for downstream tooling (docs, SDK generators).
Changes:
- Adds
#[utoipa::path]annotations to HTTP handlers andToSchema/IntoParamsderives to request/response/query models across client, dashboard, and feature-gated EVM HTTP surfaces. - Introduces a new
crates/server/src/openapi.rsaggregator module plus agen-openapibinary to emit a committeddocs/openapi.json. - Serves the OpenAPI spec at
GET /api-docs/openapi.jsonand documents regeneration/usage underdocs/OPENAPI.md.
Reviewed changes
Copilot reviewed 32 out of 34 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| docs/README.md | Adds links to the OpenAPI documentation/spec artifact. |
| docs/OPENAPI.md | Documents where the spec lives, runtime endpoint, and regeneration command. |
| docs/openapi.json | Adds the generated OpenAPI 3.1 JSON spec (built with evm). |
| crates/shared/src/lib.rs | Derives utoipa::ToSchema for shared signature types used on the wire. |
| crates/shared/Cargo.toml | Adds utoipa dependency to guardian-shared. |
| crates/server/src/state_object.rs | Derives ToSchema and marks state_json as schema-free object. |
| crates/server/src/services/unpause_account.rs | Derives ToSchema for UnpauseResponse. |
| crates/server/src/services/pause_account.rs | Derives ToSchema for PauseResponse. |
| crates/server/src/services/dashboard_pagination.rs | Derives ToSchema for PagedResult<T>. |
| crates/server/src/services/dashboard_info.rs | Derives ToSchema for dashboard info/status response models. |
| crates/server/src/services/dashboard_global_proposals.rs | Derives ToSchema for global proposal feed entries. |
| crates/server/src/services/dashboard_global_deltas.rs | Derives ToSchema for global delta feed entries. |
| crates/server/src/services/dashboard_accounts.rs | Derives ToSchema for dashboard account summary/detail types. |
| crates/server/src/services/dashboard_account_snapshot.rs | Derives ToSchema for snapshot types. |
| crates/server/src/services/dashboard_account_proposals.rs | Derives ToSchema for per-account proposal feed entries. |
| crates/server/src/services/dashboard_account_deltas.rs | Derives ToSchema for per-account delta feed types. |
| crates/server/src/services/dashboard_account_delta_detail.rs | Derives ToSchema for delta detail response type. |
| crates/server/src/services/account_status.rs | Derives ToSchema for AccountStatus. |
| crates/server/src/openapi.rs | Adds OpenAPI document aggregation, error schema, EVM merge, and unit tests. |
| crates/server/src/metadata/network.rs | Derives ToSchema for NetworkConfig and MidenNetworkType. |
| crates/server/src/metadata/auth/mod.rs | Derives ToSchema for Auth enum used in client configure requests. |
| crates/server/src/lib.rs | Exposes the new openapi module. |
| crates/server/src/evm/proposal.rs | Derives ToSchema for EVM proposal models. |
| crates/server/src/delta_summary/mod.rs | Derives ToSchema for dashboard delta metadata and decode projection types. |
| crates/server/src/delta_object.rs | Derives ToSchema for delta objects/status/signatures and schema-free payload. |
| crates/server/src/builder/handle.rs | Serves the generated spec at /api-docs/openapi.json. |
| crates/server/src/bin/gen-openapi.rs | Adds a binary to generate/write the spec JSON. |
| crates/server/src/api/http.rs | Adds utoipa annotations for client HTTP handlers + schemas/params. |
| crates/server/src/api/evm.rs | Adds utoipa annotations for EVM HTTP handlers + schemas/params. |
| crates/server/src/api/dashboard.rs | Adds utoipa annotations for dashboard auth/account/session/pause endpoints. |
| crates/server/src/api/dashboard_feeds.rs | Adds utoipa annotations for dashboard feed endpoints + query params. |
| crates/server/Cargo.toml | Adds utoipa dependency to the server crate. |
| Cargo.toml | Adds workspace utoipa dependency with axum_extras feature. |
| Cargo.lock | Locks utoipa (and transitive updates like itertools). |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| // Issue #241: serve the auto-generated OpenAPI spec. Unauthenticated | ||
| // and read-only — it documents the contract, not data. | ||
| async fn openapi_json() -> axum::Json<utoipa::openapi::OpenApi> { | ||
| axum::Json(crate::openapi::openapi()) | ||
| } |
| /// Wire shape of a Guardian error response body. Mirrors the envelope | ||
| /// produced by [`crate::error::GuardianError`]'s `IntoResponse` impl. | ||
| /// Documented as the body of every non-2xx response. Optional fields | ||
| /// are populated only for the error codes that carry them. |
| [dependencies] | ||
| miden-protocol = { version = "=0.14.5" } | ||
| serde = { workspace = true, features = ["derive"] } | ||
| serde_json = { workspace = true } | ||
| base64 = { workspace = true } | ||
| rand = "0.8" | ||
| hex = { workspace = true } | ||
| prost = { workspace = true } | ||
| utoipa = { workspace = true } |
zeljkoX
left a comment
There was a problem hiding this comment.
Thanks for working on this! Great first contribution.
I have reviewed it and would like to propose few improvements. Please let me know if they make sense to you.
-
Document auth in OpenAPI
- Add security schemes for:
- Signed client headers:
x-pubkey,x-signature,x-timestamp - Cookies:
guardian_operator_session,guardian_evm_session
- Signed client headers:
- Apply
security(...)per operation - Leave challenge/public endpoints unauthenticated
- Add security schemes for:
-
Add missing error responses to utoipa annotations
- Cross-cutting:
429rate limit,413body limit if we want them per-endpoint - Dashboard endpoints: add
404/503gaps for detail/snapshot/feed - Unpause: add
400 - EVM: add
403for signer authorization errors where applicable - Lookup: cover malformed input and storage errors
- Cross-cutting:
-
Add CI spec drift detection
- Regenerate OpenAPI to a temp file
- Diff against
docs/openapi.jsonor against more granular openapi spec files. - Fail CI when stale
-
Generate more granual specs alongside the combined one
docs/openapi.jsondocs/openapi-client.jsondocs/openapi-dashboard.jsondocs/openapi-evm.json
This maps to existing TS clients and reduces unrelated schema exposure for potential SDK generation.
-
Reduce manual endpoint docs
- Review
spec/api.md. Avoid duplicating spec in that document.
- Review
-
Update agent/contributor guidance
- Any change to HTTP handlers, DTOs, query/path params, auth behavior, or feature-gated routes must update utoipa annotations and regenerate the OpenAPI spec files
…CI drift Addresses review feedback on #271: - Document auth: add OpenAPI security schemes for the signed client headers (x-pubkey/x-signature/x-timestamp) and the operator/EVM session cookies, and apply `security(...)` per operation. Challenge/verify/pubkey stay public. - Fill missing error responses: cross-cutting 429/413 injected into every operation via a Modify addon; dashboard 404/503 gaps (detail/snapshot), unpause 400, EVM 403 signer-authorization, lookup 400/500. - Generate granular specs alongside the combined one: openapi-client.json, openapi-dashboard.json, openapi-evm.json (map to the TS client packages, scope SDK generation). gen-openapi now emits all four and gained a `--check` drift mode. - CI: new "OpenAPI Spec Drift" job fails when committed specs are stale. - Reduce duplication: replace the per-endpoint shape catalog in spec/api.md with an endpoint index + behavioral notes pointing at the OpenAPI specs as the source of truth (804 -> 479 lines). - Contributor guidance: AGENTS.md contract-change workflow + server layer guidance now require updating utoipa annotations and regenerating specs.
|
Thanks for the detailed review — all six points are addressed in 1. Document auth in OpenAPI ✅ Added security schemes: 2. Missing error responses ✅
3. CI spec drift detection ✅ New 4. Granular specs ✅ 5. Reduce manual endpoint docs ✅ Replaced the per-endpoint shape catalog in 6. Contributor guidance ✅ Combined spec is now 36 operations / 82 schemas / 5 security schemes. Local checks green: One open question on #5: I took the conservative path of keeping behavioral/semantic notes rather than deleting the section outright — let me know if you'd like a more aggressive trim of |
# Conflicts: # Cargo.lock # crates/server/src/lib.rs
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #271 +/- ##
==========================================
+ Coverage 76.26% 76.35% +0.08%
==========================================
Files 139 140 +1
Lines 25479 25621 +142
==========================================
+ Hits 19432 19563 +131
- Misses 6047 6058 +11 Continue to review full report in Codecov by Harness.
🚀 New features to boost your workflow:
|
| /// Configure (register) an account with its authorization set and | ||
| /// initial state. Requires a signed `X-Guardian-*` auth header. |
| #[utoipa::path( | ||
| post, | ||
| path = "/configure", | ||
| tag = "client", | ||
| security(("x-pubkey" = [], "x-signature" = [], "x-timestamp" = [])), | ||
| request_body = ConfigureRequest, | ||
| responses( | ||
| (status = 200, description = "Account configured", body = ConfigureResponse), | ||
| (status = 400, description = "Invalid request", body = ConfigureResponse), | ||
| ) | ||
| )] |
| - **Cross-cutting errors.** Every endpoint may also return `429` | ||
| (rate limit, with `Retry-After`) and `413` (body size limit); see | ||
| [Rate Limiting](#rate-limiting) and [Request Size Limits](#request-size-limits). | ||
| All errors use the structured `GuardianError` envelope (see [Errors](#errors)). |
| /// Wire shape of a Guardian error response body. Mirrors the envelope | ||
| /// produced by [`crate::error::GuardianError`]'s `IntoResponse` impl. | ||
| /// Documented as the body of every non-2xx response. Optional fields | ||
| /// are populated only for the error codes that carry them. |
There was a problem hiding this comment.
🧹 Nitpick comments (1)
.github/workflows/ci.yml (1)
191-192: 💤 Low valueConsider hardening checkout with
persist-credentials: false(pre-existing pattern).The zizmor static analysis flags that the checkout action doesn't set
persist-credentials: false, which prevents Git credentials from persisting in the runner environment. However, this is consistent with all other jobs in this workflow (test, clippy, fmt, build), so addressing it would be a separate hardening effort across the entire CI configuration rather than specific to this PR.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In @.github/workflows/ci.yml around lines 191 - 192, The checkout step named "Checkout code" uses actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 and should be hardened by adding the input persist-credentials: false to the step; update the "Checkout code" step (and any other identical checkout steps across jobs like test, clippy, fmt, build) to include persist-credentials: false so Git credentials are not persisted in the runner environment.Source: Linters/SAST tools
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In @.github/workflows/ci.yml:
- Around line 191-192: The checkout step named "Checkout code" uses
actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 and should be hardened
by adding the input persist-credentials: false to the step; update the "Checkout
code" step (and any other identical checkout steps across jobs like test,
clippy, fmt, build) to include persist-credentials: false so Git credentials are
not persisted in the runner environment.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 3b8a47fa-fa5a-41e6-b9e0-24c6348f7179
⛔ Files ignored due to path filters (1)
Cargo.lockis excluded by!**/*.lock
📒 Files selected for processing (17)
.github/workflows/ci.ymlAGENTS.mdCargo.tomlcrates/server/Cargo.tomlcrates/server/src/api/dashboard.rscrates/server/src/api/dashboard_feeds.rscrates/server/src/api/evm.rscrates/server/src/api/http.rscrates/server/src/bin/gen-openapi.rscrates/server/src/lib.rscrates/server/src/openapi.rsdocs/OPENAPI.mddocs/openapi-client.jsondocs/openapi-dashboard.jsondocs/openapi-evm.jsondocs/openapi.jsonspec/api.md
✅ Files skipped from review due to trivial changes (5)
- crates/server/Cargo.toml
- AGENTS.md
- spec/api.md
- docs/OPENAPI.md
- crates/server/src/api/dashboard_feeds.rs
🚧 Files skipped from review as they are similar to previous changes (5)
- Cargo.toml
- crates/server/src/lib.rs
- crates/server/src/api/evm.rs
- crates/server/src/api/http.rs
- crates/server/src/api/dashboard.rs
Summary
Resolves #241. Integrates
utoipato auto-generate an OpenAPI 3.1 specification directly from the HTTP handlers and wire models, so API docs stay in sync with the code (no manual drift).Scope covers both HTTP surfaces — the client API (
tag: client) consumed by SDKs/packages and the operator dashboard API (tag: dashboard) — plus the feature-gated EVM API (tag: evm).Per the task framing, the deliverable is the generated spec file; a bundled Swagger UI was treated as optional and is not included (consumers can point Swagger UI / ReDoc / SDK generators at the spec).
What changed
#[utoipa::path(...)]on all 36 operations acrossapi/http.rs,api/dashboard.rs,api/dashboard_feeds.rs,api/evm.rs(method, path, params, request body, per-status responses);#[derive(ToSchema)]/IntoParamson every request/response/query/model type in the graph (→ 82 component schemas), including the shared signature types.openapimodule with anApiDocOpenApiderive and a sharedApiErrorResponseschema mirroringGuardianError's envelope.openapi::openapi()merges the EVM surface only when theevmfeature is compiled in, so the spec matches the routes actually mounted.GET /api-docs/openapi.json(unauthenticated, read-only); agen-openapibinary writes the spec to a file; the generateddocs/openapi.json(built withevm) is committed and documented indocs/OPENAPI.md.Regenerating
Testing
cargo check(default) and--features evm: clean.cargo clippy --all-targets --all-features -- -D warningson the touched crates: clean.Notes
Purely additive documentation — no wire-shape changes — so the Rust/TS clients need no updates under the contract-change workflow.
Summary by CodeRabbit
Release Notes
New Features
GET /api-docs/openapi.jsonand committed underdocs/.Documentation
docs/OPENAPI.mdguide and spec files for automatic API documentation generation.Chores