Register handlers in spec order#2465
Conversation
Closes: oapi-codegen#1887 Route registration was emitted in sorted path order, so reordering paths in the spec had no effect. On routers that match in registration order (Fiber, Gorilla/mux) this left users no way to disambiguate overlapping paths — e.g. a request for `/templates/privates/shortcuts` always went to `/templates/privates/{id}` rather than `/templates/{visibility}/shortcuts`. kin-openapi stores paths in a map, losing declaration order at parse time, so enable its origin tracking (`Loader.IncludeOrigin`) to recover each path's source line onto the OperationDefinition (SpecOrder). Each GenerateXServer now renders its registration template with operations sorted by SpecOrder, via a new GenerateTemplatesIntoBuffer that composes passes into one buffer (GenerateTemplates wraps it). Model and interface generation are intentionally left in the old sorted order to preserve any existing ordering behavior — in particular the order-dependent fallback in name-collision resolution. Only handler registration follows spec order. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Greptile SummaryThis PR fixes issue #1887 by making route registration follow the path declaration order in the OpenAPI spec rather than sorted alphabetical order. On routers that match in registration order (Fiber, Gorilla/mux) this gives users direct control over precedence between overlapping paths.
Confidence Score: 4/5Safe to merge with minor follow-up; the core logic is sound and well-tested, but the route-registration order change is unconditional and will cause all existing generated files to diff on the next make generate run. The implementation is correct — sortOperationsBySpecOrder makes a defensive copy, uses a stable sort, and handles the all-zero fallback cleanly. All nine router backends are updated in parallel, the new test directory follows the prescribed layout, and the generated fixtures show the expected ordering. The two open questions are: a misplaced godoc and whether the unconditional ordering change should be gated behind a compat flag. pkg/codegen/operations.go — the doc comment placement and the compat-flag question both live here.
|
| Filename | Overview |
|---|---|
| pkg/codegen/operations.go | Core logic change: adds SpecOrder field to OperationDefinition, pathItemSourceLine helper, sortOperationsBySpecOrder, and GenerateTemplatesIntoBuffer. All GenerateXServer functions updated to pass spec-ordered ops to their registration template. One doc comment is misplaced and the unconditional behavior change warrants a compat-flag discussion. |
| pkg/util/loader.go | Adds loader.IncludeOrigin = true to both LoadSwagger and the inner loader in LoadSwaggerWithOverlay to enable per-path source-line tracking. Straightforward and correct. |
| pkg/codegen/operations_spec_order_test.go | New unit tests for sortOperationsBySpecOrder covering spec-line ordering, stability within a shared path, all-zero fallback, and no-mutation guarantee. |
| internal/test/servers/routeorder/routeorder_test.go | New Fiber integration test verifying spec-order routing fix. Correctly placed under internal/test/servers/. |
| internal/test/servers/routeorder/spec.yaml | New minimal spec with two overlapping paths ordered to exercise spec-declaration-order routing. |
| internal/test/servers/routeorder/routeorder.gen.go | Generated Fiber server; RegisterHandlersWithOptions correctly registers TemplateShortcutGetAll before PrivateTemplateGet, matching spec declaration order. |
Prompt To Fix All With AI
Fix the following 2 code review issues. Work through them one at a time, proposing concise fixes.
---
### Issue 1 of 2
pkg/codegen/operations.go:1069-1074
**Misplaced godoc comment**
The line `// OperationDefinitions returns all operations for a swagger definition.` (originally the godoc for `OperationDefinitions`) is now the *first line* of `pathItemSourceLine`'s doc comment block. As a result, `go doc` will show `pathItemSourceLine` as starting with "OperationDefinitions returns all operations for a swagger definition.", and `OperationDefinitions` itself (line 1081) has no godoc at all. The `OperationDefinitions` comment line should be moved back to immediately precede `OperationDefinitions`, with a blank line separating the two function doc blocks.
### Issue 2 of 2
pkg/codegen/operations.go:1917-1924
**Unconditional behavior change — consider a compat flag**
Route-registration order now follows spec declaration order for all backends and all users, with no opt-out. The project's stated practice is that behavior-changing features be opt-in via a configuration flag (`compatibility` or `output-options`) rather than silent breaking changes. Users on order-dependent routers (Fiber, Gorilla/mux) who previously worked around the alphabetical ordering by arranging their spec paths alphabetically will silently get different runtime routing on the next code-gen run. It is worth asking whether this should be gated behind a flag (e.g. `compatibility.register-routes-in-spec-order`) so the change is opt-in rather than unconditional.
Reviews (1): Last reviewed commit: "Register handlers in spec order" | Re-trigger Greptile
|
IMO, I think this may be an option we want a compatibility config option for @mromaszewicz, especially if it changes intent for everyone's specs |
|
This only affects fiber and gorilla, and only in the case where a parameterized shallow path comes before a deeper path. For example, if you have:
The first is going to shadow the second. In every other router, path registration is order independent, so it's a no-op change, just a bit of generated code churn. I think I want this default-on, but I can add an opt out flag (even though spec editing can accomplish the same thing) |
…tion PR oapi-codegen#2465 (issue oapi-codegen#1887) changed generated servers to register route handlers in spec-declaration order, so that routers which match in registration order (Fiber, Gorilla/mux) can disambiguate overlapping paths by ordering them in the spec. This makes that behavior opt-out: the new sort-handler-registrations compatibility flag restores the historical lexicographic (by path, then method) registration order for users who relied on it. The dispatch is a single seam -- operationsInRegistrationOrder picks sortOperationsLexicographically when the flag is set, otherwise the existing sortOperationsBySpecOrder -- and all nine GenerateXServer functions route through it. The lexicographic sort reproduces the historical gather order (SortedMapKeys over paths then methods), so with the flag unset the default output is unchanged. Adds TestSortHandlerRegistrations and documents the flag in configuration-schema.json. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…tion (#2477) PR #2465 (issue #1887) changed generated servers to register route handlers in spec-declaration order, so that routers which match in registration order (Fiber, Gorilla/mux) can disambiguate overlapping paths by ordering them in the spec. This makes that behavior opt-out: the new sort-handler-registrations compatibility flag restores the historical lexicographic (by path, then method) registration order for users who relied on it. The dispatch is a single seam -- operationsInRegistrationOrder picks sortOperationsLexicographically when the flag is set, otherwise the existing sortOperationsBySpecOrder -- and all nine GenerateXServer functions route through it. The lexicographic sort reproduces the historical gather order (SortedMapKeys over paths then methods), so with the flag unset the default output is unchanged. Adds TestSortHandlerRegistrations and documents the flag in configuration-schema.json. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Closes: #1887
Route registration was emitted in sorted path order, so reordering paths in the spec had no effect. On routers that match in registration order (Fiber, Gorilla/mux) this left users no way to disambiguate overlapping paths — e.g. a request for
/templates/privates/shortcutsalways went to/templates/privates/{id}rather than/templates/{visibility}/shortcuts.kin-openapi stores paths in a map, losing declaration order at parse time, so enable its origin tracking (
Loader.IncludeOrigin) to recover each path's source line onto the OperationDefinition (SpecOrder). Each GenerateXServer now renders its registration template with operations sorted by SpecOrder, via a new GenerateTemplatesIntoBuffer that composes passes into one buffer (GenerateTemplates wraps it).Model and interface generation are intentionally left in the old sorted order to preserve any existing ordering behavior — in particular the order-dependent fallback in name-collision resolution. Only handler registration follows spec order.