feat(codegen): fold OpenAPI 3.1 const-based oneOf without outer type into enums - #2520
feat(codegen): fold OpenAPI 3.1 const-based oneOf without outer type into enums#2520sloudel wants to merge 2 commits into
Conversation
…into enums
OpenAPI 3.1 can express string/integer enums as a `oneOf` whose branches
each carry a scalar `const`, with the top-level schema omitting `type`
entirely (the consts imply the family). Previously detectEnumViaOneOf
required an explicit outer scalar `type`, so such schemas fell through to
the generic union path and emitted `type PetStatus struct { union
json.RawMessage }` plus a `PetStatus0 = interface{}` — losing the enum
values and names.
Now, when the outer `type` is absent, the scalar family is inferred from
the per-branch `const` values (string, or whole-number integer). Every
branch must agree and every const must be a plain scalar; otherwise the
union path is preserved. The detected type drives the Go base-type mapping
via a shallow schema copy, so a no-type const oneOf now emits e.g.
`type PetStatus string` with the named constants, instead of interface{}.
When the outer `type` is present, behaviour is unchanged (must be
string/integer to be treated as an enum). The SkipEnumViaOneOf escape
hatch and the negative paths (missing `title`/`const`, object/mixed
oneOf) continue to fall through to the union generator.
Adds a focused unit test for the inference helper and integration coverage
(PetStatus string no-type, Port integer no-type) with constants +
JSON round-trip, plus guards that the existing with-type enums still pass.
Greptile SummaryThis PR infers string or integer enum types from OpenAPI 3.1 oneOf branch const values when the outer schema omits type.
Confidence Score: 4/5The PR should not merge until inferred integer enums reject or correctly map constants that cannot be represented by their generated Go type. A valid typeless oneOf containing a sufficiently large whole-number const is now folded into a Go int enum, producing an overflowing typed constant that causes generation to fail. Files Needing Attention: pkg/codegen/schema.go
|
| Filename | Overview |
|---|---|
| pkg/codegen/schema.go | Adds typeless const-based enum inference, but accepts integer constants that can overflow the generated Go integer type and abort generation. |
| pkg/codegen/schema_test.go | Covers supported scalar families and rejected non-integral values, but omits integer representability boundaries. |
| internal/test/openapi31/spec.yaml | Adds representative string and small-integer schemas using the new no-outer-type idiom. |
| internal/test/openapi31/openapi31_test.go | Verifies generated constants and JSON round trips for the added fixtures. |
| internal/test/openapi31/openapi31.gen.go | Generated changes are scoped to the expected PetStatus and Port enum output. |
Prompt To Fix All With AI
### Issue 1
pkg/codegen/schema.go:697-700
**Integer inference permits overflowing constants**
When a typeless `oneOf` contains a whole-number const outside the generated Go integer type's range, this check still classifies it as an integer and emits it as a typed constant, causing code generation to fail while formatting the overflowing Go source.
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.Reviews (1): Last reviewed commit: "feat(codegen): fold OpenAPI 3.1 const-ba..." | Re-trigger Greptile
| case float64: | ||
| if c == math.Trunc(c) { | ||
| return "integer", true | ||
| } |
There was a problem hiding this comment.
Integer inference permits overflowing constants
When a typeless oneOf contains a whole-number const outside the generated Go integer type's range, this check still classifies it as an integer and emits it as a typed constant, causing code generation to fail while formatting the overflowing Go source.
Knowledge Base Used: Codegen Pipeline
Prompt To Fix With AI
This is a comment left during a code review.
Path: pkg/codegen/schema.go
Line: 697-700
Comment:
**Integer inference permits overflowing constants**
When a typeless `oneOf` contains a whole-number const outside the generated Go integer type's range, this check still classifies it as an integer and emits it as a typed constant, causing code generation to fail while formatting the overflowing Go source.
**Knowledge Base Used:** [Codegen Pipeline](https://app.greptile.com/oapi-codegen/-/custom-context/knowledge-base/oapi-codegen/oapi-codegen/-/docs/codegen-pipeline.md)
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.When the outer schema omits `type`, the scalar family is inferred from the per-branch `const` values. A whole-number const that exceeds the Go int64 range (e.g. 2^63 or larger) was still classified as an integer and emitted verbatim into a typed Go enum constant. Such a constant does not fit the target integer type, so formatting the generated source failed. Now the int64 bounds are enforced when inferring the integer family: - float64 numbers must be whole AND in [-2^63, 2^63) (the high end is strict because math.MaxInt64 rounds up to 2^63 when widened to float64). - integral defensive types (uint/uint64) are range-checked against int64. Out-of-range consts fall through (ok=false) to the union generator instead of producing an invalid typed enum. Adds unit coverage for the boundary values and a detectEnumViaOneOf case proving an overflowing const falls through.
Summary
OpenAPI 3.1 expresses string/integer enums as a
oneOfwhose branches each carry a scalarconst. When the outer schema omitstype(the common idiom, since the consts imply the family),detectEnumViaOneOfpreviously required an explicit outer scalartypeand fell through to the union generator, emitting:losing the enum values and names entirely.
This change lets the scalar family be inferred from the per-branch
constvalues when the outertypeis absent:now produces a typed enum:
The integer variant (
Portwith whole-number consts) likewise emitsPort int+ numeric constants.Behaviour
typepresent — unchanged (must bestring/integerto be treated as an enum).typeabsent — family inferred from theconstvalues; every branch must agree and each const must be a plain scalar (string, or whole-number integer). Mixed/fractional/bool/object/array → falls through to the standard union path.oapiSchemaToGoTypeyieldsstring/intinstead of falling into the genericinterface{}branch.SkipEnumViaOneOfescape hatch and the negative paths (missingtitle/const, object/mixedoneOf) still fall through to the union generator.Tests
TestEnumViaOneOfConstType— unit coverage of the new inference helper (string / whole-number / fractional / bool / object / array / mixed).PetStatus(string, no type) andPort(integer, no type) added tointernal/test/openapi31/spec.yaml, with named-constant + JSON round-trip assertions.Severity,Color) and the negativeMixedOneOfcase still pass.Verification
all green.