Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
114 changes: 114 additions & 0 deletions internal/test/aggregates/allof/allof.gen.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

18 changes: 18 additions & 0 deletions internal/test/aggregates/allof/allof_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,24 @@ import (
"github.com/stretchr/testify/assert"
)

// issue #1905: a oneOf nested inside allOf-of-allOf must survive the merge.
// The generated struct must have a union field, and the two variant types must exist.
func TestIssue1905(t *testing.T) {
typ := reflect.TypeOf(NestedOneOfInAllOf{})

// The merged struct must carry the flat property from the outer allOf member.
_, hasAFoo := typ.FieldByName("AFoo")
assert.True(t, hasAFoo, "NestedOneOfInAllOf should have AFoo field")

// The union field signals that the oneOf was preserved.
_, hasUnion := typ.FieldByName("union")
assert.True(t, hasUnion, "NestedOneOfInAllOf should have a union field (oneOf was dropped)")

// The two oneOf variant types must be generated.
_ = NestedOneOfInAllOf0{}
_ = NestedOneOfInAllOf1{}
}

// issue #1219: test additionalProperties merge-precedence rules in allOf.
// In oapi-codegen, an unspecified additionalProperties is treated as false
// (unlike the OpenAPI specification default of true), so "default" and
Expand Down
24 changes: 23 additions & 1 deletion internal/test/aggregates/allof/spec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@
# Category: aggregates/allof
# Tests: allOf composition with additionalProperties on constituent schemas;
# additionalProperties merge-precedence across all combinations of
# true/false/schema/default in allOf members.
# true/false/schema/default in allOf members;
# deeply nested oneOf inside allOf-of-allOf (issue #1905).
#
# Folds in (provenance):
# - issues/issue193 (allOf + additionalProperties)
# - issues/issue-1219 (additionalProperties merge precedence)
# - issues/issue-1905 (nested oneOf lost in allOf-of-allOf merge)
# ==========================================================================
openapi: "3.0.1"
info:
Expand Down Expand Up @@ -174,3 +176,23 @@ components:
allOf:
- $ref: '#/components/schemas/DefaultAdditional1'
- $ref: '#/components/schemas/DefaultAdditional2'

# --- issue #1905: deeply nested oneOf inside allOf-of-allOf ---
# The outer allOf merges two members: an inner allOf (which itself wraps a
# oneOf with two variants) and a plain object with a_foo. The bug: the
# merge walk stops after one level, so the oneOf inside the inner allOf is
# silently dropped and neither the union field nor the variant types appear
# in the generated output.
NestedOneOfInAllOf:
allOf:
- allOf:
- oneOf:
- properties:
a_foo_one_of_0:
type: string
- properties:
a_foo_one_of_1:
type: string
- properties:
a_foo:
type: string
12 changes: 11 additions & 1 deletion pkg/codegen/merge_schemas.go
Original file line number Diff line number Diff line change
Expand Up @@ -258,7 +258,11 @@ func mergeOpenapiSchemas(s1, s2 openapi3.Schema, allOf bool, seenSchemaRef map[s
// TODO: Check for collisions
maps.Copy(result.Extensions, s2.Extensions)

result.OneOf = append(s1.OneOf, s2.OneOf...)
// Capture top-level OneOf/AnyOf before overwriting s1/s2 with transitive
// AllOf merges. The merges may surface additional OneOf/AnyOf members from
// nested allOf members (issue #1905), so we accumulate from both sources.
oneOf := append(s1.OneOf, s2.OneOf...)
anyOf := append(s1.AnyOf, s2.AnyOf...)

// We are going to make AllOf transitive, so that merging an AllOf that
// contains AllOf's will result in a flat object.
Expand All @@ -269,6 +273,8 @@ func mergeOpenapiSchemas(s1, s2 openapi3.Schema, allOf bool, seenSchemaRef map[s
if err != nil {
return openapi3.Schema{}, fmt.Errorf("error transitive merging AllOf on schema 1")
}
oneOf = append(oneOf, merged.OneOf...)
anyOf = append(anyOf, merged.AnyOf...)
s1 = merged
}
if s2.AllOf != nil {
Expand All @@ -277,9 +283,13 @@ func mergeOpenapiSchemas(s1, s2 openapi3.Schema, allOf bool, seenSchemaRef map[s
if err != nil {
return openapi3.Schema{}, fmt.Errorf("error transitive merging AllOf on schema 2")
}
oneOf = append(oneOf, merged.OneOf...)
anyOf = append(anyOf, merged.AnyOf...)
s2 = merged
}

result.OneOf = oneOf
result.AnyOf = anyOf
result.AllOf = append(s1.AllOf, s2.AllOf...)

if s1.Type.Slice() != nil && s2.Type.Slice() != nil && !equalTypes(s1.Type, s2.Type) {
Expand Down