GeezerVisonThe World Wide Web, restored to 800 pixels of glory. | ||||||||||||||||||||||||||||||||||||||
| Home|About|Gaming cabinet|Archives|How it works|Starter sites | ||||||||||||||||||||||||||||||||||||||
GeezerVison is showing github.com — Map OpenAPI 3.1 multi-type unions to `any` by bendrucker · Pull Request #2522 · oapi-codegen/oapi-codegen Conversation
Contributor
OpenAPI 3.1 allows Go has no type meaning "one of these", so a union maps to The mapping is deliberately narrow so the existing error keeps catching malformed input instead of quietly widening it. #1977 asks for a better error here, and swallowing more cases would be the wrong direction. A list-valued One interaction needed handling. A union carrying an Nullable objects are unaffected. Generation is exercised across property, For motivation, Honeycomb's published 3.1 spec carries two of these for genuinely polymorphic event and query-result values. The only workaround today is an overlay that strips the
Sorry, something went wrong. Uh oh!
There was an error while loading. Please reload this page. All reactions
OpenAPI 3.1 allows `type` to be a list, so a value may be any one of several types. The primitive-type dispatch uses `Types.Is(...)`, which only matches a single-element list, so every multi-type union fell through to `unhandled Schema type` and failed generation. Go has no type matching that constraint, so map a union to `any`, the same permissive mapping a bare `type: "null"` already gets (oapi-codegen#2430), rather than rejecting an otherwise valid spec. The mapping is narrow on purpose, so the existing error still catches malformed input. A list-valued `type` is 3.1-only syntax, so a 3.0 document carrying one keeps failing. Every entry must name a JSON Schema type, so a misspelled one (`type: [strng, number]`) keeps failing too. A union carrying an `enum` is excluded from enum codegen for the reason `type: array` already is: `const X any = ...` is not a valid Go constant. It generates the plain `any` the union maps to.
|
There was an error while loading. Please reload this page.
Greptile SummaryThis PR adds OpenAPI 3.1 multi-type union support by mapping unions to
Confidence Score: 5/5The PR appears safe to merge. No blocking failure remains.
|
| Filename | Overview |
|---|---|
| pkg/codegen/schema.go | Recognizes valid OpenAPI 3.1 multi-type unions, maps them to any, and excludes them from invalid enum constant generation. |
| pkg/codegen/operations.go | Separates single schema types from union member lists and handles nil-valued any parameters. |
| pkg/codegen/schema_test.go | Adds focused coverage for union mapping, nullable unions, malformed members, and OpenAPI version gating. |
| internal/test/openapi31/spec.yaml | Adds representative OpenAPI 3.1 union schemas across generated model and operation positions. |
| internal/test/openapi31params/spec.yaml | Exercises union-valued parameters and runtime binding behavior. |
| pkg/codegen/templates/server-middleware.tmpl | Passes union member metadata through shared server parameter-binding calls. |
| pkg/codegen/templates/client-with-responses.tmpl | Passes union member metadata when parsing generated client response headers. |
Reviews (2): Last reviewed commit: "Extend OpenAPI 3.1 union type binding to..." | Re-trigger Greptile
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
@mromaszewicz I'd expect that we'd treat this as a oneOf in this case?
Sorry, something went wrong.
There was an error while loading. Please reload this page.
You know, I'm not sure. This isn't like |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
Thanks for the PR — the core mapping is well-guarded and the writeup is unusually thorough. We ran a deep analysis with Claude on this change (probing generated output for the interaction cases against a compiled runtime), and it surfaced a few issues that make the change incomplete as-is. Findings below, roughly in order of severity. 1. Parameter positions generate code that always fails at request timeA multi-type union in a parameter now generates successfully, but the endpoint is permanently broken: parameters: - name: id in: path required: true schema: type: [string, integer] The generated handler binds into an So a path param typed this way 400s on every request, and a query param 400s whenever it's supplied. Before this PR the spec failed loudly at generation time; after it, the failure moves to runtime. Unlike Relatedly, the PR body says generation is exercised across "property, additionalProperties, parameter, request body, response body, array items, and allOf positions", but the committed suite only covers property, additionalProperties, a named component, and the enum interaction. If you tested the other positions locally, could you commit those tests? The parameter case above is exactly the kind of thing that coverage would have caught. 2.
|
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
Update on the review findings above: the runtime side of finding 1 has shipped. runtime v1.7.0 adds a What that leaves on the codegen side to complete the parameter story here:
We're happy to either take these as a follow-up PR on our side, or you're welcome to fold them into this one — whichever you prefer. If you'd rather keep this PR at its current scope, we'll handle the plumbing after merge so both land in the same release. On the remaining findings: having dug further, finding 2 ( |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
A union-typed parameter (type: [string, integer]) lowers to `any`, which
the runtime's destination-driven binder rejected on every request, so
generation succeeded but the endpoint never worked. runtime v1.7.0 added a
Types option carrying the union's member list for exactly this case: the
value binds to the first member that parses (boolean, integer, number,
string — JSON number grammar), only when the destination is an `any`.
Codegen now emits that option. ParameterDefinition and
ResponseHeaderDefinition gain SchemaTypes(), returning the member list
with the "null" nullability marker stripped for genuine unions and nil
otherwise; all 31 BindStyledParameterOptions/BindQueryParameterOptions
literals across the stdlib, echo, gin, fiber and iris templates populate
Types unconditionally (via the existing toStringArray helper), rendering
[]string{} for single-type parameters. Generated code therefore requires
runtime >= v1.7.0 from the next release on — a deliberate choice over a
conditional-emission shim, which added template complexity for a
compatibility window we don't intend to support.
SchemaType() previously returned the first entry of the type list
verbatim: "string" for [string, integer], and "null" for
["null", "string"]. It now strips the null marker and returns "" for
unions. Type is unread by every released runtime, so no generated
behavior changes.
ZeroValueIsNil() now reports true for `any` parameters, so the generated
client wraps optional union parameters in the existing nil check instead
of passing a nil interface to StyleParamWithOptions, which panics. This
also fixes the same latent panic for optional typeless (schema: {}) and
type: "null" parameters.
The new internal/test/openapi31params suite exercises the full loop —
generated std-http server and client, path/query/header positions, a
nullable union, member selection, and typed round-trips — against
runtime v1.7.0. The sibling openapi31 suite stays models-only per its
charter. internal/test and examples bump runtime v1.5.0 -> v1.7.0, and
every committed .gen.go with bind calls regenerates to carry the Types
field.
Co-Authored-By: Claude Fable 5 <[email protected]>
There was an error while loading. Please reload this page.
|
Review the following changes in direct dependencies. Learn more about Socket for GitHub.
|
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
Ok, @bendrucker , what do you think of this change. I added a commit which makes it much larger, since I had to change every template to pass through the OpenAPI 3.1 types array to the runtime package. |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
The previous commit populated Types unconditionally in every generated bind call, which regenerated 54 committed .gen.go files and made all generated code — including code from plain 3.0 specs — require runtime v1.7.0. Emit the field only for multi-type union parameters instead, via the unionTypes template helper (rendering through the existing toStringArray). Single-type parameters emit no Types field at all, so code generated from specs without union parameters is byte-identical to previous releases and keeps compiling against older runtimes; only specs that actually use union parameters pick up the v1.7.0 requirement, and every previously-churned .gen.go reverts to its committed state. The examples and internal/test modules both build against runtime v1.7.0 for consistency — that is a choice of what we test against, not a requirement the generated code imposes. Co-Authored-By: Claude Fable 5 <[email protected]>
There was an error while loading. Please reload this page.
|
Just getting back to this, definitely didn't anticipate those edge cases, thanks for finishing this one for me! Will take a look back through and see if there are any follow ups, might tackle #2525 as well. |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
|
Revising my earlier comment: #2525 looks like the same bug as #1328, and #2514 already fixes it with the struct wrapper suggested in the issue. I reviewed that branch merged onto current main and verified it handles every shape from #2525, including the 3.1 unions from this PR. Details on the issue. |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
* Cover remaining OpenAPI 3.1 union positions in the conformance suites The review of #2522 asked for the untested union positions to be committed. Three surfaces had no conformance coverage: - array items: a named array component whose items are a union lowers to a []any alias and round-trips mixed member values - a union as a oneOf branch: the outer schema keeps the standard union machinery with the branch accessor typed any, whose As... conversion can never fail, so branch discrimination falls to the caller - response headers: ResponseHeaderDefinition.SchemaTypes() feeds the Types list the generated ClientWithResponses passes when binding declared response headers, previously exercised by no test The allOf position is already pinned by the spec added in #2526 (both member orderings), so it is deliberately not duplicated here. * Tighten comments
There was an error while loading. Please reload this page.
Successfully merging this pull request may close these issues.
Multi-type unions aren't handled for OpenAPI 3.1
There was an error while loading. Please reload this page.