Name path-level parameter helper types per operation#2466
Conversation
Greptile SummaryThis PR fixes a compile-time redeclaration bug (issue #2090) where a path-level
Confidence Score: 5/5Safe to merge. The fix is well-scoped and the generated output for the new test fixture compiles and reflects the intended behavior. The core fix correctly deduplicates shared parameter helper types across all three generation sites (paths, webhooks, callbacks), the component-parameter fix aligns naming with union accessors, and the pre-generation validator catches the remaining cross-path collision case. The two open points are non-blocking quality issues, not defects in the generated output. No files require special attention beyond the noted improvements to validate_spec.go error messaging and the corresponding test fixture coverage for webhook/callback collision scenarios.
|
| Filename | Overview |
|---|---|
| pkg/codegen/operations.go | Adds Shared flag to ParameterDefinition, markShared/sharedParameterTypeDefs helpers, and per-path-item emit-once logic for path-level shared parameter helper types. Logic is sound for regular paths, webhooks, and callbacks. |
| pkg/codegen/codegen.go | Fixes component-level parameters: passes []string{goTypeName} to paramToGoType so helper types are named after the component type (e.g. WidgetId0), and explicitly appends AdditionalTypes to the component types slice so they are declared once with the component rather than per-operation. |
| pkg/codegen/validate_spec.go | Adds pre-generation collision detection for path-level anyOf/oneOf parameters across path items. All three domains (paths, webhooks, callbacks) are covered in code, but the error message wording does not account for webhooks/callbacks. |
| internal/test/parameters/shared_anyof/shared_anyof.gen.go | New regression fixture. Id0/Id1 declared once; Whid0/Whid1 once for webhooks; Cbid0/Cbid1 once for callbacks. WidgetId0/WidgetId1 appear with the component, not per-operation. |
| internal/test/parameters/shared_anyof/spec.yaml | Test spec covers multi-method path-level anyOf param, webhook, callback, and component parameter. Distinct parameter names deliberately avoid cross-domain collision. |
| internal/test/spec_validation/spec_validation_test.go | Adds rejection test for two regular paths with same-named anyOf path-level parameter. No tests for webhook-to-webhook or path-to-webhook collision scenarios. |
| internal/test/spec_validation/testdata/shared_path_param_type_collision.yaml | New rejection fixture: two regular paths each with an inline anyOf id path parameter, correctly exercising the new collision validator. |
| internal/test/parameters/shared_anyof/shared_anyof_test.go | Regression guard asserting Id0/Id1, Whid0, Cbid0, WidgetId0/WidgetId1 are each declared exactly once. |
Reviews (2): Last reviewed commit: "Generate shared path-level parameter hel..." | Re-trigger Greptile
|
This isn't the right fix. The parameters should be defined once globally, not specialized once per operation. There's a problem with that, though. if there are two parameters named Id on two different pathItems and each of those is an |
bea2a5e to
ea399ff
Compare
Closes: oapi-codegen#2090 A parameter declared at the path-item level is inherited by every method on the path. Its helper types — the member types of an inline anyOf/oneOf, named from the parameter alone (Id0, Id1, ...) — were generated once per operation, so a path with more than one method declared them several times and the output failed to compile ("Id0 redeclared in this block"). Fix the root cause: mark path-item-level parameters as shared and skip their helper types when collecting an operation's type definitions, emitting them once for the path item (attributed to its first operation) instead. Applied to the regular-path, webhook, and callback operation processors. A shared parameter used by several methods on one path now just works. Also fix referencing a shared parameter from #/components/parameters, which was independently broken for anyOf: the component's union member types were named from an empty path (N0/N1) and never declared, while each referencing operation redeclared its own inline members. Now GenerateTypesForParameters roots the schema path at the parameter's type name and declares its helper types once, and a $ref parameter drops the inline schema it would otherwise redeclare. Referencing a component parameter is a clean way to share one. Two different path items that each declare a shared parameter of the same name still collide (both emit the same type once). The pre-generation validation pass detects this and rejects it, naming the paths and the fixes: define the parameter under #/components/parameters and $ref it, rename it with x-go-type-name or a spec overlay, or move it into each operation. Adds internal/test/parameters/shared_anyof covering a shared anyOf parameter across multiple methods on a regular path, a webhook, a callback, and a component parameter referenced from two paths; plus a spec_validation testdata case for the cross-path rejection and a multi-method valid.yaml case for the accepted shared case. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
ea399ff to
bce67cf
Compare
|
Hi, This PR introduced an unintended breaking change in parameter type naming. Before this PR, when a component parameter's schema is a $ref to a named schema (e.g. $ref: "#/components/schemas/Status"), oapi-codegen generated a dedicated helper type named after the operation — e.g. GetItemsParamsStatus. After this PR, pd.Schema.AdditionalTypes = nil clears those inline types, and the referenced schema name is used directly — e.g. Status. This silently renames generated types, breaking existing code without any indication in the changelog. Is this intentional? If so, it should be documented as a breaking change. |
|
No, it's not intentional. Thanks for pointing it out. |
…isions (#2476) * Revert "Generate shared path-level parameter helper types once (#2466)" This reverts commit a07731d. * Name shared path parameter helper types once, hashing cross-path collisions Closes: #2090 A parameter declared at the path-item level is inherited by every method on the path. Its hoisted helper types -- the members of an inline anyOf/oneOf and similar -- were named from the parameter alone and declared once per operation, so a path with more than one method redeclared them ("Id0 redeclared in this block"). The same bare names also collided across different paths that reuse the same parameter, which is the common REST case of an {id} shared by sibling paths. Resolve path-item-level parameters in a single pre-pass (resolveSharedParameters): describe each scope's shared parameters once, count how many scopes hoist a helper type under each name, and emit each scope's helper types once for its path item rather than once per operation. A name produced by two or more scopes is disambiguated by prefixing that scope's parameters with a short, stable FNV hash of the scope, extended to the full hash only if two scopes' short hashes clash. A parameter that does not collide keeps its historical undecorated name, so existing generated code is unaffected -- the only outputs that change are specs that previously failed to compile. Applied uniformly to regular paths, webhooks, and callbacks, which share the one global Go type namespace. Disambiguated types carry a doc comment explaining the hash prefix and pointing back to the source path. Adds internal/test/parameters/shared_collision covering a shared anyOf parameter across multiple methods on a path (bare, emitted once), the same parameter reused across sibling paths (hash-disambiguated), and a non-colliding single-method parameter (unchanged). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Closes: #2090
A path-level parameter (declared under a path item's
parameters, shared by every method on the path) was described once with no operation prefix, so any helper types generated for its schema — e.g. the member types of ananyOf— were named with only the parameter name (Id0, Id1). Because the path-level parameter is folded into every method on the path, those bare types were declared once per method, producing "Id0 redeclared in this block" and failing to compile when a path had more than one method.Describe path-level parameters inside the per-operation loop with the same
<OperationId>Paramsprefix already used for method-level parameters, so their helper types are named per operation (GetResourceParamsId0, PostResourceParamsId0, ...) and no longer collide. The same fix is applied to the three sites that share this pattern: regular paths, webhooks, and callbacks. Parameters with no generated helper type (plain strings, integers, etc.) are unaffected.Adds internal/test/parameters/shared_anyof, exercising a path-level anyOf parameter shared across multiple methods on a regular path, a webhook, and a callback; the generated file compiling as part of the test module is the regression guard.