Fix: Respect parent properties in case of merged schemas by allOf#1415
Conversation
|
@jamietanna Would you take a look at this PR? Thanks. |
|
Thank you for this, I hope it gets reviewed soon |
Signed-off-by: Kotaro Inoue <k.musaino@gmail.com>
… schema Signed-off-by: Kotaro Inoue <k.musaino@gmail.com>
Signed-off-by: Kotaro Inoue <k.musaino@gmail.com>
# Conflicts: # internal/test/all_of/v1/openapi.gen.go # internal/test/all_of/v2/openapi.gen.go
|
Thanks for this PR. I'm patching it up, and it actually closes quite a few related bugs. I will push a couple of fix-up commits to your branch. |
…tural
The unconditional injection of the parent (with AllOf cleared) as a
final allOf member correctly merges structural siblings like
properties/required, but it also forces n>=2 in mergeSchemas for pure
wrappers like `{allOf: [$ref: X]}`, which previously took the n==1
fast-path and returned the referenced type. Without the fast-path
those wrappers regenerate as inlined anonymous structs and lose their
named-type identity (regression visible in
examples/output-options/preferskipoptionalpointer).
Add hasStructuralSiblings() so injection runs only when the parent has
Properties, Required, or AdditionalProperties. Pure wrappers keep the
fast-path. Also copy the parent's Description into the merged result —
the merge does not carry Description, so it was being silently dropped.
Addresses oapi-codegen#697, oapi-codegen#931, oapi-codegen#1710, oapi-codegen#1960, oapi-codegen#2102.
Partial fix for oapi-codegen#1139 (anonymous-schema hoisting requires further work).
Greptile SummaryThis PR fixes the long-standing issue (#697) where Confidence Score: 5/5Safe to merge; the breaking change is intentional, properly gated behind an opt-out flag, and the implementation is logically sound. No P0 or P1 issues found. The two P2 findings are a documentation gap (the flag silently also controls description propagation) and a latent shallow-copy hazard in Extensions that is safe under current mergeOpenapiSchemas behaviour. Both are minor and do not affect correctness of the generated output. pkg/codegen/schema.go — shallow copy of Extensions in the injected stub; pkg/codegen/configuration.go — flag comment omits the description-propagation side-effect.
|
| Filename | Overview |
|---|---|
| pkg/codegen/schema.go | Core fix: injects parent schema (AllOf cleared) as final allOf member so structural siblings are merged; gated on OldAllOfSiblingMerging flag; description propagation bundled in same flag. |
| pkg/codegen/configuration.go | Adds OldAllOfSiblingMerging CompatibilityOptions field with appropriate YAML tag and documentation. |
| configuration-schema.json | Adds old-allof-sibling-merging entry to the JSON schema, kept in sync with configuration.go as required. |
| internal/test/all_of/openapi.yaml | Adds PersonWithMorePropertiesOutsideOfAllOf regression test schema exercising the allOf+sibling-properties bug from issue #697. |
| internal/test/all_of/v1/openapi.gen.go | Regenerated: adds PersonWithMorePropertiesOutsideOfAllOf struct embedding Person; description comments now populated for existing types. |
| internal/test/all_of/v2/openapi.gen.go | Regenerated: adds flattened PersonWithMorePropertiesOutsideOfAllOf struct (v2 merges all fields inline); description comments updated. |
| internal/test/components/components.gen.go | Description comments updated for EnumUnion, EnumUnion2, and OneOfObject12 — driven by the description-propagation fix (issue #1960) bundled in this PR. |
| internal/test/issues/issue-1087/deps/deps.gen.go | Breaking change: Error type alias (= BaseError) replaced with a distinct struct that adds the reason field. This is the new default behavior; existing users must set OldAllOfSiblingMerging: true to opt out. |
Reviews (2): Last reviewed commit: "fix(codegen): gate allOf sibling merge b..." | Re-trigger Greptile
Address Greptile P1 review on the previous commit:
1. append(schema.AllOf, ...) could mutate kin-openapi's backing array
if the parsed slice carried spare capacity. Allocate a fresh slice
with make() before appending.
2. Merging the parent's structural siblings into an allOf result is a
breaking change to generated Go: a schema like
{allOf: [Base], properties: {extra}} previously produced
`type X = Base` (interchangeable with Base) and now produces a
distinct struct (not interchangeable). Per project practice
(cf. OldMergeSchemas, OldEnumConflicts, OldAliasing) gate behavior
changes behind a compatibility flag. Add
Compatibility.OldAllOfSiblingMerging (yaml: old-allof-sibling-merging,
default false). Update configuration-schema.json with a matching
description so users hit by downstream breakage can find the knob.
The Description copy is gated on the same flag for consistency.
Greptile also suggested narrowing hasStructuralSiblings to drop the
pure-required case. Keeping Required in the trigger because that is the
exact scenario issue oapi-codegen#931 reports: a parent that adds `required: [foo]`
to an allOf chain currently produces a Go type whose `Foo` field is
`*string` with omitempty, ignoring the spec's required constraint.
Required is structural in OAS terms — it changes field type in the
generated struct — and the compatibility flag is the safety net for
users whose downstream code depended on the alias output.
|
@greptileai comments addressed, review again, make sure to look at latest commit. |
…pi-codegen#1415) * Respect parent properties in merging allOf schemas Signed-off-by: Kotaro Inoue <k.musaino@gmail.com> * Add test case to check if parent propeties are included in the merged schema Signed-off-by: Kotaro Inoue <k.musaino@gmail.com> * Make generate Signed-off-by: Kotaro Inoue <k.musaino@gmail.com> * fix(codegen): only inject parent siblings into allOf merge when structural The unconditional injection of the parent (with AllOf cleared) as a final allOf member correctly merges structural siblings like properties/required, but it also forces n>=2 in mergeSchemas for pure wrappers like `{allOf: [$ref: X]}`, which previously took the n==1 fast-path and returned the referenced type. Without the fast-path those wrappers regenerate as inlined anonymous structs and lose their named-type identity (regression visible in examples/output-options/preferskipoptionalpointer). Add hasStructuralSiblings() so injection runs only when the parent has Properties, Required, or AdditionalProperties. Pure wrappers keep the fast-path. Also copy the parent's Description into the merged result — the merge does not carry Description, so it was being silently dropped. Addresses oapi-codegen#697, oapi-codegen#931, oapi-codegen#1710, oapi-codegen#1960, oapi-codegen#2102. Partial fix for oapi-codegen#1139 (anonymous-schema hoisting requires further work). * fix(codegen): gate allOf sibling merge behind compatibility flag Address Greptile P1 review on the previous commit: 1. append(schema.AllOf, ...) could mutate kin-openapi's backing array if the parsed slice carried spare capacity. Allocate a fresh slice with make() before appending. 2. Merging the parent's structural siblings into an allOf result is a breaking change to generated Go: a schema like {allOf: [Base], properties: {extra}} previously produced `type X = Base` (interchangeable with Base) and now produces a distinct struct (not interchangeable). Per project practice (cf. OldMergeSchemas, OldEnumConflicts, OldAliasing) gate behavior changes behind a compatibility flag. Add Compatibility.OldAllOfSiblingMerging (yaml: old-allof-sibling-merging, default false). Update configuration-schema.json with a matching description so users hit by downstream breakage can find the knob. The Description copy is gated on the same flag for consistency. Greptile also suggested narrowing hasStructuralSiblings to drop the pure-required case. Keeping Required in the trigger because that is the exact scenario issue oapi-codegen#931 reports: a parent that adds `required: [foo]` to an allOf chain currently produces a Go type whose `Foo` field is `*string` with omitempty, ignoring the spec's required constraint. Required is structural in OAS terms — it changes field type in the generated struct — and the compatibility flag is the safety net for users whose downstream code depended on the alias output. --------- Signed-off-by: Kotaro Inoue <k.musaino@gmail.com> Co-authored-by: Marcin Romaszewicz <marcinr@gmail.com>
fixes #697
Currently, oapi-codegen ignores original (parent) fields if
allOfis defined. OpenAPI schema doesn't seem to refuse a combination of general fields (properties) andallOf.For example, the following schema
will be converted into the following type definition, which seems to be wrong result.
The expected result would be like:
The known approach is normalization as proposed in other similar project like
REFACTOR_ALLOF_WITH_PROPERTIES_ONLYinopneapi-generator. This PR is inspired by the idea.@oapi-codegen/maintainers edit to PR message:
Closes #697
Closes #931
Closes #1710
Closes #1960
Closes #2102
Partially improves ##1139
Supersedes #717