Repository navigation
add rust axum server example, bugfixes - #92
Conversation
11b81a7 to
5203558
Compare
|
Should fix before merge
Worth doing, not blocking
Nits
|
Sorry - should have it in Draft - still working on this. Will take this into account before the next push |
36369ad to
0ecf479
Compare
|
Ready for another review @ebourgeois |
ebourgeois
left a comment
There was a problem hiding this comment.
Re-review of this branch — 9 findings, most severe first.
Blocking (generated crate does not compile / generated test fails):
needs_modelsmisses path/query param types →handlers.rsmissingmodelsimport- Created resource's key is dropped by the serde round-trip → generated
*_create_returns_a_usable_keytest panics
Also worth fixing before merge:
3. Unconditional use serde_json::json; trips the repo's own -D warnings gate
4. Scaffold-protected lib.rs desyncs when security/validations are enabled on a later regeneration
5. only_admins_may_invalidate rejects partial updates that don't touch is_valid
Lower impact: 6–9 (hard-coded test seed, format-blind int mapping, unescaped Cargo/main metadata, docs contract).
Worth noting that 1–3 all sit in blind spots of test/rust/verify.sh: its shape matrix either defaults methods.resource to all methods (so a POST body always exists) or declares no instance path, and it runs cargo clippy but never cargo test. Widening that matrix would catch all three.
Signed-off-by: Daniel Guns <danbguns@gmail.com>
0ecf479 to
7afa6f0
Compare
|
Thanks — all nine are fixed, plus both nits. Pushed. 1, 2, 3 all reproduced in the widened matrix before I touched anything, which was the useful part of your review: 2 I fixed by not claiming the key rather than by injecting harder — serde genuinely cannot carry a field the struct lacks, so 4 you are right that scaffolding 5 confirmed: a non-admin PUT that omits 6 seed dropped. 7 8 was the interesting one. Writing the test found the same bug a level up: a newline or colon in Nits: the Cargo comment lost its first half and reads properly now; 234 python tests, the gate green across all 11 shapes. |
|
Please resolve conversations. |
|
Done! Comments to address were in one above, so did not resolve with a comment. |
Signed-off-by: Daniel Guns danbguns@gmail.com
Follow-up to #91, which left the rust validation package with nothing to plug into: firestone generated a rust client CLI but no rust server, so the rules landed in a hand-written handler rather than a generated one.
What
Two generators, one document.
openapi-generator -g rust-axumturns the OpenAPI document firestone already produces into the server — the router, the typed models, per-operation authentication and request validation. That is not firestone's to duplicate. What it leaves behind is a trait with one required method per operation, 52 of them here, and nothing compiles until they all exist.firestone generate server --language rustwrites those: every method, wired to aBackendtrait, enforcing the resources' validation rules on the way past. Both halves derive from the same document, so they cannot disagree about an operation id, a model name or a response variant — unit tests pin the three naming rules (CreatePostal_code→CreatePostalCode,"Response for OK"→Status200_ResponseForOK).Firestone regenerates
handlers.rsandtests/api.rsevery time; everything else is scaffolded once and then yours, so theBackendyou implement and the tokens you accept survive a regeneration.--forceoverrides.The auth split is the part worth knowing. Which operations need a token is in the schema and the generated server enforces it; what counts as a valid token is a deployment question and lives in the
auth.rsfirestone writes. No middleware of firestone's own:router()hands back a plainaxum::Router, so rate limiting, CORS and request ids aretower-httplayers inmain.rsand a deployment decision rather than something a schema can state.On the size
Most of the diff is
api/, openapi-generator's output. It is committed on purpose so the example builds and runs withcargo run, no generator or JVM needed, the same way the python example commits itsmodels/andapis/. The half that is firestone's isapp/; the templates, module and tests come to about 2k lines, and that is the reviewable surface.Bugfixes
Generating a server that actually serves turned up several, most only reachable once something tried to answer a request:
{"default": ...}as the only response.defaultis a fallback shape, not a status, so a server generator has nothing to put on the response; rust-axum resolves it toresponse.status(0), which is not a status, and the response fails to build. All 11 HEAD operations answered 500.methods.resourceabsent, the paths were generated for every collection method while the components were generated for none, leaving a dangling$reftoCreateThing. Any server generator failed on it.config.api_key, but our schemas declarescheme: bearerand the generated client readsbearer_access_tokenfor that.--api-keywas accepted and silently dropped, so a correct token still got a 401./separating a click flag pair was missing, so--is-validbecame--is-valid--no-is-validand every create or update carrying a boolean failed with aTypeError.SyntaxError: duplicate argument 'address_key', stale output from an older generator.python -m firestonewas a no-op —if __name__ == "main":, missing dunders.Also guarded the example rule in
addressbook.yaml, which readself.is_validunguarded and so 500'd once an attribute endpoint handed it a resource that never had one.Verification
226 python tests, black / pylint / pycodestyle clean.
make verify-rustregenerates both halves into a throwaway workspace, runscargo clippy -D warningsandcargo testover them, and does the same for the committed crates plus acargo fmt --check. It also builds six different operation sets through both generators, because the addressbook happens to use every code path and a two-line schema does not: that leg is what catches an import the handlers do not use, or anauth.rsgenerated for a schema that secures nothing. CI installs openapi-generator, and the script now fails rather than skips if it is missing there, so that leg cannot quietly stop running.Ran rather than only compiled: 401 without a token, 201 with one, 201 for
bearerin lowercase, 422 onperson_must_exist, 409 onperson_is_not_in_use, 200 on HEAD, 501 on an operation firestone will not guess at, 404 through theErrorHandlerseam.Known gaps
DELETEorPATCH, and aPOSTorPATCHon an attribute all answer 501: a POST to an embedded collection appends and a PATCH merges, and neither is a replace.requestBodyfor PATCH, which is a spec bug of the same family as the HEAD one. Left for its own change, since it moves every PATCH signature.Backend.Debugon an error, so the problem document is there but escaped.