Skip to content

Fix ValueByDiscriminator() with external refs#2474

Merged
mromaszewicz merged 1 commit into
oapi-codegen:mainfrom
mromaszewicz:fix/issue-2470
Jul 16, 2026
Merged

Fix ValueByDiscriminator() with external refs#2474
mromaszewicz merged 1 commit into
oapi-codegen:mainfrom
mromaszewicz:fix/issue-2470

Conversation

@mromaszewicz

Copy link
Copy Markdown
Member

Closes: #2470

When a discriminated oneOf union is defined in an external file and pulled into an allOf from another package via import-mapping, the generated ValueByDiscriminator() interpolated the raw discriminator mapping value (e.g. externalRef0.GitDiffFile) directly into the method name, producing invalid Go: t.AsexternalRef0.GitDiffFile().

union.tmpl now resolves each mapping value to its matching UnionElement and emits {{ .Method }} — the same name used by the generated As*/From*/Merge* helpers — so the call becomes t.AsExternalRef0GitDiffFile(). Local-ref unions are unaffected: the element Method equals the mapping value there, so existing generated code is byte-identical.

Adds a regression fixture under
internal/test/references/multipackage/discriminated_union_allof that reproduces the two-file, import-mapped, discriminated-union-in-allOf case and exercises ValueByDiscriminator() end-to-end.

@mromaszewicz
mromaszewicz requested a review from a team as a code owner July 16, 2026 17:30
@mromaszewicz mromaszewicz added the bug Something isn't working label Jul 16, 2026
@greptile-apps

greptile-apps Bot commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

Fixes ValueByDiscriminator() generating invalid Go (t.AsexternalRef0.GitDiffFile()) when a discriminated oneOf union is defined in an external file and pulled into an allOf via import-mapping. The fix moves the mapping-value-to-method-name resolution out of the template and into a new DiscriminatorCases() Go helper, which looks each mapping value up in the union's element list and returns the correct Method() suffix.

  • pkg/codegen/schema.go — adds DiscriminatorCase struct and DiscriminatorCases() method; the helper uses SortedMapKeys for deterministic output, which also implicitly fixes non-deterministic case ordering that existed in the old range $map template loop.
  • pkg/codegen/templates/union.tmpl — replaces the raw {{$type}} interpolation in ValueByDiscriminator() with {{.Method}} from the pre-resolved DiscriminatorCases slice; unresolvable mapping values are now silently skipped (fall to default) rather than producing uncompilable output.
  • internal/test/references/multipackage/discriminated_union_allof/ — new regression fixture under the correct references/multipackage category with a two-package, import-mapped, discriminated-union-in-allOf layout and a compile+runtime test.

Confidence Score: 5/5

Safe to merge: the change is narrowly scoped to the ValueByDiscriminator() template path and is guarded by a new multi-package regression fixture that would fail to compile if the fix regressed.

The core change (resolving mapping values through DiscriminatorCases before template rendering) is correct and well-contained. Local-ref unions produce byte-identical output because UnionElement.Method() equals the raw mapping value for plain Go type names. The generated fixture confirms the external-ref case now compiles and dispatches correctly. No other template files or router backends are affected, and no configuration or public API surface is altered.

No files require special attention.

Important Files Changed

Filename Overview
pkg/codegen/schema.go Adds DiscriminatorCases() helper that resolves discriminator mapping values to UnionElement.Method() names before template rendering, fixing invalid Go for external-ref types
pkg/codegen/templates/union.tmpl Switches ValueByDiscriminator() generation from iterating the raw Mapping map to using DiscriminatorCases(); also implicitly fixes non-deterministic case ordering (Go map range was random, SortedMapKeys is used in the Go helper)
internal/test/references/multipackage/discriminated_union_allof/gen/api/api.gen.go Generated fixture for the cross-package allOf case; ValueByDiscriminator() correctly calls AsExternalRef0GitDiffFile()/AsExternalRef0AddedImageDiffFile() instead of the previously invalid t.AsexternalRef0.GitDiffFile()
internal/test/references/multipackage/discriminated_union_allof/issue_test.go Runtime regression test exercises ValueByDiscriminator() round-trip for the external-ref case; compilation of the package itself is the primary guard
internal/test/references/multipackage/discriminated_union_allof/spec.yaml New test spec defining PRFile as an allOf of an external discriminated union (DiffFile) and a local object (ViewedStatus)
internal/test/references/multipackage/discriminated_union_allof/common/spec.yaml External spec defining the discriminated oneOf union (DiffFile) with implicit discriminator mapping; no issues
internal/test/references/multipackage/discriminated_union_allof/doc.go Standard doc.go with go:generate directives for both packages; follows existing conventions
internal/test/references/multipackage/discriminated_union_allof/config.api.yaml oapi-codegen config for the api package with import-mapping for the external common spec; correctly structured
internal/test/references/multipackage/discriminated_union_allof/config.common.yaml oapi-codegen config for the common package; straightforward models-only generation
internal/test/references/multipackage/discriminated_union_allof/gen/common/common.gen.go Generated fixture for the common package; local-ref discriminator cases are byte-identical to pre-fix output (Method() == raw mapping value for local types)

Reviews (2): Last reviewed commit: "Fix ValueByDiscriminator() with external..." | Re-trigger Greptile

Comment thread pkg/codegen/templates/union.tmpl Outdated
Closes: oapi-codegen#2470

When a discriminated `oneOf` union is defined in an external file and pulled
into an `allOf` from another package via `import-mapping`, the generated
`ValueByDiscriminator()` interpolated the raw discriminator mapping value
(e.g. `externalRef0.GitDiffFile`) directly into the method name, producing
invalid Go: `t.AsexternalRef0.GitDiffFile()`.

The fix computes the switch arms in Go rather than in the template. A new
`Schema.DiscriminatorCases()` helper maps each discriminator value to its
union element's `Method()` — the same name used by the generated
`As*`/`From*`/`Merge*` helpers — so the call becomes
`t.AsExternalRef0GitDiffFile()`. `union.tmpl` now just ranges over those
precomputed `{Value, Method}` cases, replacing the nested
`range UnionElements` / `eq (print $el)` matching logic. Local-ref unions are
unaffected: the element `Method` equals the mapping value there, so existing
generated code is byte-identical.

Adds a regression fixture under
`internal/test/references/multipackage/discriminated_union_allof` that
reproduces the two-file, import-mapped, discriminated-union-in-allOf case and
exercises `ValueByDiscriminator()` end-to-end.
@mromaszewicz
mromaszewicz merged commit 1457922 into oapi-codegen:main Jul 16, 2026
14 checks passed
@mromaszewicz
mromaszewicz deleted the fix/issue-2470 branch July 16, 2026 17:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

ValueByDiscriminator() generates incorrect method call when union type is referenced via external ref in allOf

1 participant