-
-
Notifications
You must be signed in to change notification settings - Fork 95
Description
Note
AI disclosure: issue found and reported by Claude. I have verified it independently.
Root Cause Analysis
Summary
When components.parameters contains an external $ref (e.g., $ref: "./params.yaml#/FilterParam"), the bundled output is malformed because NewParameter() copies empty field values from the low-level struct without resolving the reference.
Versions Affected
| Version | Status |
|---|---|
| libopenapi v0.28.1 / vacuum v0.20.0 | ✅ Works |
| libopenapi v0.30.4+ / vacuum v0.21.0+ | ❌ Broken |
Minimal Reproduction
Directory structure:
test/
├── openapi.yaml
└── params.yaml
openapi.yaml:
openapi: 3.0.4
info:
title: Test
version: 1.0.0
paths:
/test:
get:
parameters:
- $ref: "#/components/parameters/FilterParam"
responses:
"200":
description: OK
components:
parameters:
FilterParam:
$ref: "./params.yaml#/FilterParam"params.yaml:
FilterParam:
name: filter
in: query
required: false
description: Filter expression
schema:
type: stringCommand:
cd test && vacuum bundle openapi.yaml -oExpected output:
components:
parameters:
FilterParam:
name: filter
in: query
required: false
description: Filter expression
schema:
type: stringActual output:
components:
parameters:
FilterParam:
schema:
description: Filter expression
examples:
name: {}
in: {}
required: {}
description: {}
schema: {}
content:
name: {}
in: {}
required: {}
description: {}
schema: {}Code Flow
-
Low-level parsing - When
components.parameters.FilterParamis parsed, the low-levelParameterstruct correctly identifies this as a reference:param.IsReference()→trueparam.GetReference()→"./params.yaml#/FilterParam"param.Name.Value→""(empty - actual value is in the referenced file)
-
High-level construction -
NewParameter()copies the empty values without resolving the reference:func NewParameter(param *low.Parameter) *Parameter { p := new(Parameter) p.low = param p.Name = param.Name.Value // "" (empty) p.In = param.In.Value // "" (empty) p.Description = param.Description.Value // "" (empty) // ... return p }
Note: The high-level
Parameterhas aReferencefield butNewParameter()never sets it fromparam.GetReference(). -
Bundling - When
NodeBuilder.Render()runs withResolve=true:- It sees
n.Low.IsReference() = trueandn.Resolve = true - It correctly skips rendering as a
$refnode (line 296-298) - But then it renders the high-level struct fields which are all empty
- The empty
Name,In,Required,Descriptionfields get incorrectly placed underschema,examples, andcontentkeys
- It sees
Bisects to 5620619