Skip to content

Propagate external refs into allOf#2299

Open
mromaszewicz wants to merge 1 commit intooapi-codegen:mainfrom
mromaszewicz:fix/issue-2288
Open

Propagate external refs into allOf#2299
mromaszewicz wants to merge 1 commit intooapi-codegen:mainfrom
mromaszewicz:fix/issue-2288

Conversation

@mromaszewicz
Copy link
Member

Closes: #2288

When allOf references an external schema, valueWithPropagatedRef was only rewriting direct property $refs to include the remote file path. Nested refs — array items, additionalProperties, and sub-object properties — were left as local "#/..." refs, producing unqualified Go type names that fail to compile.

Extract the propagation logic into a recursive propagateRemoteRefs function that walks Properties, Items, and AdditionalProperties, rewriting any local ref it finds. This ensures types like *[]externalRef0.Role are correctly qualified instead of the broken *[]Role.

Extend the externalref test with a User/Role/EnrichedUser scenario that exercises the array-items case from the issue report.

@mromaszewicz mromaszewicz requested a review from a team as a code owner March 23, 2026 19:21
@mromaszewicz
Copy link
Member Author

@greptileai

@greptile-apps
Copy link

greptile-apps bot commented Mar 23, 2026

Greptile Summary

This PR fixes a bug where allOf extending an external schema would leave nested #/... refs (array items, additionalProperties, and sub-object properties) unqualified, producing broken Go type names like *[]Role instead of *[]externalRef0.Role. The fix extracts the existing per-property ref-rewriting into a new recursive propagateRemoteRefs helper and extends it to cover Items and AdditionalProperties, then adds a concrete User/Role/EnrichedUser regression test that exercises the array-items case from the reported issue.

  • pkg/codegen/merge_schemas.go: Refactors valueWithPropagatedRef to call a new propagateRemoteRefs function that recursively walks Properties, Items, and AdditionalProperties, rewriting any local #/... ref to include the remote file path.
  • internal/test/externalref/packageB/spec.yaml: Adds Role (string enum) and User (object with a roles: []Role array property) to supply the external types for the regression test.
  • internal/test/externalref/packageA/spec.yaml: Adds EnrichedUser as an allOf extension of the external User, reproducing issue allOf with cross-file does not qualify nested type references from source schema #2288.
  • Gap to be aware of: propagateRemoteRefs does not yet walk AnyOf, OneOf, AllOf, or Not sub-schemas. External types that use those composition keywords internally with local refs could trigger the same class of bug in a follow-up scenario.

Confidence Score: 4/5

  • Safe to merge; correctly fixes the reported array-items qualification bug with a targeted regression test.
  • The core logic is correct and the fix is well-scoped to the reported issue. The only gap is that AnyOf/OneOf/AllOf/Not sub-schema refs are not yet walked, which could resurface the same class of bug for schemas using those keywords — but that is a follow-up concern, not a regression from this PR.
  • pkg/codegen/merge_schemas.go — consider extending propagateRemoteRefs to cover AnyOf/OneOf/AllOf/Not in a follow-up.

Important Files Changed

Filename Overview
pkg/codegen/merge_schemas.go Extracts inline property-ref propagation into a recursive propagateRemoteRefs helper that also walks Items and AdditionalProperties; correctly fixes the reported issue but does not cover AnyOf/OneOf/AllOf/Not sub-schema refs.
internal/test/externalref/packageA/spec.yaml Adds EnrichedUser schema that extends the external User type via allOf, reproducing the cross-file array-items ref qualification bug from issue #2288; missing newline at end of file (pre-existing).
internal/test/externalref/packageB/spec.yaml Adds Role enum and User object (with a roles: []Role array) to packageB's spec, providing the external types needed to exercise the fix.

Reviews (1): Last reviewed commit: "Propagate external refs into allOf" | Re-trigger Greptile

Closes: oapi-codegen#2288

When allOf references an external schema, valueWithPropagatedRef was
only rewriting direct property $refs to include the remote file path.
Nested refs — array items, additionalProperties, and sub-object
properties — were left as local "#/..." refs, producing unqualified
Go type names that fail to compile.

Extract the propagation logic into a recursive propagateRemoteRefs
function that walks Properties, Items, and AdditionalProperties,
rewriting any local ref it finds. This ensures types like
`*[]externalRef0.Role` are correctly qualified instead of the
broken `*[]Role`.

Extend the externalref test with a User/Role/EnrichedUser scenario
that exercises the array-items case from the issue report.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@mromaszewicz mromaszewicz added the bug Something isn't working label Mar 23, 2026
@cdevienne
Copy link

cdevienne commented Mar 26, 2026

I tested your patch with the following files, which is an extract of a bigger api in which I encountered the same issue.

cfg-api.yaml:

package: api
output: api.gen.go
generate:
  models: true
output-options:
  # to make sure that all types are generated
  skip-prune: true
import-mapping:
  ./types.yaml: "types"

api.yaml:

components:
  schemas:
    CustomerStatusPostPayloadV1:
      allOf:
        - $ref: './types.yaml#/components/schemas/CustomerStatusV1'
        - type: object
          properties:
            name:
              type: string

types.yaml

components:
  schemas:
    CustomerStatusEnum:
      type: string
      enum:
        - Approved
        - Disputed
        - Suspended

    CustomerStatusV1:
      allOf:
        - type: object
          properties:
            status:
              $ref: '#/components/schemas/CustomerStatusEnum'

The generated files still doesn't reference the enum properly:

// Package api provides primitives to interact with the openapi HTTP API.
//
// Code generated by github.com/oapi-codegen/oapi-codegen/v2 version v2.6.0 DO NOT EDIT.
package api

// CustomerStatusPostPayloadV1 defines model for CustomerStatusPostPayloadV1.
type CustomerStatusPostPayloadV1 struct {
	Name   *string             `json:"name,omitempty"`
	Status *CustomerStatusEnum `json:"status,omitempty"`
}

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.

allOf with cross-file does not qualify nested type references from source schema

2 participants