refactor(ergo-api): split server.rs, types.rs, batch.rs (1/2) - #216
Conversation
Mechanical, behavior-preserving file split of the 2,524-line
ergo-api/src/server.rs into a server/ directory module:
- server/mod.rs — ServerCtx, bind/serve wrappers (bind, serve_on*,
serve, realtime_handle), the utxo_reads_supported
gated subtree (scala_utxo_subtree,
utxo_lookup_unsupported_in_digest_mode,
scala_utxo_snapshots_info_handler), all router
builders incl. the master
router_with_mempool_and_wallet_and_security,
wallet_ui redirects, SPA security headers
- server/handlers.rs — the 18 native operator handlers (info, identity,
host, status, votes, set_votes, tip, sync, peers,
recent_blocks, events, difficulty_history,
miner_stats, votes_history, metrics, shutdown,
peers_connect, health)
- server/shared.rs — submit_via_node + map_submit_error (already
pub(crate); re-exported at crate::server::* so the
compat::{blocks,transactions} call sites are
untouched)
- server/openapi.rs — NativeOpenApi derive, SecurityAddon + Modify impl,
native_openapi_yaml, and the cfg(test) OpenAPI
emission test
- server/assets.rs — static-asset handlers (index, swagger*, openapi
yaml/json emitters, fonts, css, js helper)
All function bodies moved verbatim. The only non-move edits:
- import headers per file + module docs for the new files
- NativeOpenApi paths() entries re-pathed to super::handlers::* (the
handlers now live in a sibling module)
- visibility promotions, all private -> pub(super), for true cross-file
entry points only: the 18 handler fns and the 15 asset fns (used by
mod.rs route registration and, for handlers, the NativeOpenApi derive)
Public/crate surface unchanged: crate::server::{bind, realtime_handle,
serve*, router*, ServerCtx, native_openapi_yaml, NativeOpenApi,
submit_via_node, map_submit_error} all resolve as before via mod.rs
re-exports.
Verification: cargo test -p ergo-api 1041 passed / 0 failed / 5 ignored
(identical to baseline); openapi_native_matches_snapshot golden held;
cargo clippy -p ergo-api --all-targets --all-features -D warnings clean;
cargo fmt clean.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Kw7uCwqmiGna8fpg6TvxGi
Mechanical, behavior-preserving file split of the 1,876-line
ergo-api/src/types.rs (flat wire-DTO list + pinning-test tail) into a
types/ directory module organized by domain:
- types/mod.rs — module doc, private mod decls + glob re-exports
(surface stays ergo_api::types::X exactly), and
the shared hex32() helper (used by ergo-node's
snapshot builder via the crate-root glob)
- types/identity.rs — ApiInfo (+default_block_interval_ms), ApiIdentity,
ApiStateType, ApiHistoryMode, ApiHost
- types/status.rs — ApiStatus, ApiBlockApplyError, ApiSyncWedged,
ApiShadowStatus/Divergence, ApiBootstrapStatus/
Phase, ApiPopowPhase, ApiHeaderAvailability,
SyncStateLabel, ApiSyncStatus, ApiHealth,
HealthStatus
- types/voting.rs — ApiVotes, ApiVotableParam, ApiConfiguredVote,
ApiSetVotesRequest, ApiVoteTarget, ApiVotesHistory,
ApiVoteChangeEvent, ApiParamChange
- types/chain_refs.rs — ApiTip, ApiHeaderRef, ApiFullBlockRef,
ApiRecentBlock, ApiDifficultyPoint/Series,
ApiMinerStat/Stats
- types/peers.rs — ApiPeer, ApiPeerDirection, ApiPeerState
- types/mempool.rs — ApiMempoolSummary/Transactions/Transaction,
ApiIoBox, ApiAsset, ApiTxDetail, ApiWeightFunction
(+UnknownWeightFunction error + TryFrom<&str>,
moved verbatim), ApiTxSource, SubmitMode,
SubmitError, ApiSubmitResponse, ApiSubmitError,
ApiNativeSubmitError (+From<ApiSubmitError>, moved
verbatim), RawTransactionBytes
- types/events.rs — ApiNodeEvent/Events, ApiReorgRecord/History
- types/indexer.rs — ApiIndexerRepair/Totals/Status
The ~700-line wire-shape/round-trip pinning-test tail is partitioned
alongside its owning domain file (identity/status/chain_refs/peers/
mempool/events); every test moved verbatim, so the wire-format pins
themselves prove serde attributes and field order are unchanged.
No visibility promotions: every DTO was already pub, hex32 stays pub,
default_block_interval_ms stays private beside ApiInfo. No cross-file
imports were needed (each domain's field types land in the same file).
Verification: cargo test -p ergo-api 1041 passed / 0 failed / 5 ignored
(identical to baseline, all pinning tests included); clippy --all-targets
--all-features -D warnings clean; fmt clean.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Kw7uCwqmiGna8fpg6TvxGi
Mechanical, behavior-preserving file split of the 1,125-line
ergo-api/src/v1/routes/batch.rs:
- batch/mod.rs — module doc, the two public policy caps
(MAX_BATCH_ITEMS, MAX_BATCH_WEIGHT), and the wire
types (BatchRequest, BatchItemRequest,
BatchItemResult + its ok/error constructors,
BatchResponse); re-exports dispatch::batch_router
so crate::v1::routes::batch::batch_router (and the
chain up to crate::v1::batch_router) is unchanged
- batch/allowlist.rs — the route! macro and the ~500-line allowed_routes()
manifest, kept together as one self-contained unit
(the macro registers each entry on BOTH the
dispatch Router and the classification table, the
anti-drift invariant), plus the
allowed_routes_table_excludes_mutating_submit_domain
test
- batch/dispatch.rs — AllowedRoute, template_matches, specificity,
classify, BatchState, parse_method, dispatch_one,
batch_handler, batch_router, the private buffering
caps (MAX_BUFFERED_BODY_BYTES, UNKNOWN_IP), and the
template/classify/parse_method tests
All function bodies moved verbatim. Non-move edits:
- import headers + module docs for the new files
- v1/openapi.rs paths() entry re-pathed
crate::v1::routes::batch::batch_handler ->
crate::v1::routes::batch::dispatch::batch_handler (utoipa resolves the
hidden __path struct beside the fn definition, which a plain re-export
does not carry; dispatch is pub(crate) for exactly this reference)
Visibility promotions (minimum set):
- AllowedRoute struct + its three fields: private -> pub(super)
(constructed by allowlist.rs's route! table, consumed by dispatch.rs)
- allowed_routes(): private -> pub(super) (called by batch_router in
dispatch.rs)
- mod dispatch is pub(crate) (openapi.rs derive reference above)
Verification: cargo test -p ergo-api 1041 passed / 0 failed / 5 ignored
(identical to baseline); openapi v1 + native golden snapshots held;
clippy --all-targets --all-features -D warnings clean; fmt clean.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Kw7uCwqmiGna8fpg6TvxGi
|
Warning Review limit reached
Next review available in: 50 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (2)
✨ 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.
Actionable comments posted: 1
🧹 Nitpick comments (1)
ergo-api/src/v1/routes/batch/dispatch.rs (1)
96-102: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick winIgnore query strings during route classification.
If a client includes a query string in the
pathfield instead of using thequeryfield, it currently results in an inconsistency. A path like/api/v1/boxes/abcd?limit=10will successfully pass classification (asabcd?limit=10satisfies the wildcard:box_idcheck) and route correctly. However, a path like/api/v1/boxes?limit=10will fail classification because"boxes?limit=10"does not literally match"boxes", resulting in aForbiddenTargeterror.Stripping the query string during classification resolves this inconsistency and safely allows Axum to parse and route the URI correctly in both scenarios.
♻️ Proposed refactor
fn classify(table: &[AllowedRoute], method: &Method, path: &str) -> Option<RouteClass> { + let path_only = path.split_once('?').map_or(path, |(p, _)| p); table .iter() - .filter(|r| r.method == *method && template_matches(r.template, path)) + .filter(|r| r.method == *method && template_matches(r.template, path_only)) .max_by_key(|r| specificity(r.template)) .map(|r| r.class) }🤖 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 `@ergo-api/src/v1/routes/batch/dispatch.rs` around lines 96 - 102, Update classify to remove the query-string suffix from path before calling template_matches and specificity, while preserving the existing method filtering and route-selection behavior. Use only the portion before the first '?' so paths with query parameters classify consistently.
🤖 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.
Inline comments:
In `@ergo-api/src/types/voting.rs`:
- Line 79: Update the voting cadence documentation near the “Desired value”
comment to state that the node votes one step per voting epoch, matching the
behavior described in the surrounding voting documentation. Do not describe the
cadence as occurring per block.
---
Nitpick comments:
In `@ergo-api/src/v1/routes/batch/dispatch.rs`:
- Around line 96-102: Update classify to remove the query-string suffix from
path before calling template_matches and specificity, while preserving the
existing method filtering and route-selection behavior. Use only the portion
before the first '?' so paths with query parameters classify consistently.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 79a26637-da06-4c1f-978b-6f9b68eab737
📒 Files selected for processing (20)
ergo-api/src/server/assets.rsergo-api/src/server/handlers.rsergo-api/src/server/mod.rsergo-api/src/server/openapi.rsergo-api/src/server/shared.rsergo-api/src/types.rsergo-api/src/types/chain_refs.rsergo-api/src/types/events.rsergo-api/src/types/identity.rsergo-api/src/types/indexer.rsergo-api/src/types/mempool.rsergo-api/src/types/mod.rsergo-api/src/types/peers.rsergo-api/src/types/status.rsergo-api/src/types/voting.rsergo-api/src/v1/openapi.rsergo-api/src/v1/routes/batch.rsergo-api/src/v1/routes/batch/allowlist.rsergo-api/src/v1/routes/batch/dispatch.rsergo-api/src/v1/routes/batch/mod.rs
💤 Files with no reviewable changes (2)
- ergo-api/src/types.rs
- ergo-api/src/v1/routes/batch.rs
… block The `target` doc said "one step per block", contradicting `ApiVotableParam` (one step per voting epoch) and the per-epoch history rows. Ergo advances a votable parameter at most one step per voting epoch (1024 blocks on mainnet), not per block; correct the comment. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Kw7uCwqmiGna8fpg6TvxGi
… fix The `ApiVoteTarget.target` doc-comment change (per-block → per-voting-epoch) flows into the utoipa `ToSchema` `description`, so the native OpenAPI snapshot drifted. Regenerate the checked-in golden fixture to match; no schema shape change, only the corrected description text. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Kw7uCwqmiGna8fpg6TvxGi
Mechanical, behavior-preserving file-split refactor of the three largest
ergo-apiroot/route files. First of a 2-PR stack (part 2:refactor/ergo-api-split, splittingwallet/native/dto.rs,v1/routes/dto.rs,v1/routes/tx_intel.rs). One commit per split file; every function body moved verbatim (only import headers, new-file module docs, and the minimum visibility promotions listed below).Split map
src/server.rs(2,524 lines) →src/server/mod.rs—ServerCtx, bind/serve wrappers (bind,serve_on*,serve,realtime_handle), theutxo_reads_supported-gated subtree (scala_utxo_subtree+ 503 handler + snapshots-info handler), all router builders incl. the ~920-linerouter_with_mempool_and_wallet_and_security, wallet-ui redirects, SPA security headershandlers.rs— the 18 native operator handlers (info/identity/host/status/votes/set_votes/tip/sync/peers/recent_blocks/events/difficulty_history/miner_stats/votes_history/metrics/shutdown/peers_connect/health)shared.rs—submit_via_node+map_submit_error(alreadypub(crate); re-exported atcrate::server::*socompat::{blocks,transactions}call sites are untouched)openapi.rs—NativeOpenApiderive +SecurityAddon+native_openapi_yaml+ the cfg(test) emission testassets.rs— static-asset handlers (index, swagger pages, spec emitters, fonts, css,jshelper)src/types.rs(1,876 lines) →src/types/mod.rs(mod decls + glob re-exports + sharedhex32),identity.rs,status.rs,voting.rs,chain_refs.rs,peers.rs,mempool.rs,events.rs,indexer.rs— flat DTO list partitioned by domain; the ~700-line wire-shape/round-trip pinning-test tail was split alongside each owning domain file (no monolithic test file left behind).ApiWeightFunction's realTryFrom<&str>+ error type andApiNativeSubmitError'sFrom<ApiSubmitError>moved verbatim.src/v1/routes/batch.rs(1,125 lines) →src/v1/routes/batch/mod.rs— module doc, public policy caps (MAX_BATCH_ITEMS,MAX_BATCH_WEIGHT), wire types (BatchRequest/BatchItemRequest/BatchItemResult/BatchResponse)allowlist.rs— theroute!macro and the ~500-lineallowed_routes()manifest kept together as one self-contained unit, so the macro's dispatch-router/classification-table anti-drift invariant is preserved structurally (dispatch.rsconsumes only the resulting data, never the macro); the existing table-invariant test (allowed_routes_table_excludes_mutating_submit_domain) lives here and passesdispatch.rs—AllowedRoute,template_matches/specificity/classify,BatchState,parse_method,dispatch_one,batch_handler,batch_routerHonored "do NOT split" call-outs
router_with_mempool_and_wallet_and_securitykept whole inserver/mod.rsdispatch.rsroute!macro + table kept together inallowlist.rs(spec's preferred option)Visibility promotions (complete list, with reasons)
server/handlers.rs: 18 handler fns private →pub(super)(used by mod.rs route registration and by theNativeOpenApipaths()derive)server/assets.rs: 15 asset fns private →pub(super)(used by mod.rs route registration)batch/dispatch.rs:AllowedRoutestruct + its 3 fields private →pub(super)(constructed byallowlist.rs'sroute!table)batch/allowlist.rs:allowed_routes()private →pub(super)(called bybatch_router)mod dispatchispub(crate):v1/openapi.rs'spaths()entry was re-pathed tobatch::dispatch::batch_handlerbecause utoipa resolves the hidden__path_*struct beside the fn definition, which a plain re-export does not carrysubmit_via_node/map_submit_errorstaypub(crate), alltypesDTOs were alreadypub)Verification
cargo test -p ergo-api= 1041 passed / 0 failed / 5 ignored (66 targets); clippy cleanopenapi_native_snapshot,openapi_v1_snapshot) pass unchanged — the generated specs are byte-identicalcargo clippy -p ergo-api --all-targets --all-features -- -D warningsclean;cargo fmt --all --checkclean;cargo check --workspace --all-targetsclean🤖 Generated with Claude Code
https://claude.ai/code/session_01Kw7uCwqmiGna8fpg6TvxGi
Summary by CodeRabbit