Skip to content
Open
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
8 changes: 8 additions & 0 deletions datamodel/low/v3/path_item.go
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,10 @@ func (p *PathItem) Build(ctx context.Context, keyNode, root *yaml.Node, idx *ind
if err := low.BuildModel(pathNode, &op); err != nil {
return err
}
// Initialize the embedded reference up front so the operation is safe to
// inspect (e.g. IsReference()) even if a sibling operation's Build fails
// and this one is never built. See issue #616.
op.Reference = &op.reference

opRef := low.NodeReference[*Operation]{
Value: &op,
Expand Down Expand Up @@ -374,6 +378,10 @@ func (p *PathItem) Build(ctx context.Context, keyNode, root *yaml.Node, idx *ind
if err := low.BuildModel(opValueNode, &addOp); err != nil {
return err
}
// Initialize the embedded reference up front so the operation
// is safe to inspect even if a sibling operation's Build fails
// and this one is never built. See issue #616.
addOp.Reference = &addOp.reference

addOpRef := low.NodeReference[*Operation]{
Value: &addOp,
Expand Down
38 changes: 38 additions & 0 deletions datamodel/low/v3/path_item_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -392,3 +392,41 @@ func TestResolveOperationReference_EmptyTagNode(t *testing.T) {
assert.Equal(t, "from-empty-tag-node", resolvedNode.Content[1].Value)
assert.NotNil(t, foundCtx.Value(index.FoundIndexKey))
}

// TestPathItem_Build_SiblingOperationSafeAfterRefError reproduces issue #616:
// when one operation in a path item fails to build (here, a dangling $ref),
// the sibling operations must still be safe to inspect. Previously they were
// returned with a nil embedded *low.Reference, so IsReference() panicked with a
// nil pointer dereference.
func TestPathItem_Build_SiblingOperationSafeAfterRefError(t *testing.T) {
yml := `get:
responses:
'200':
description: ok
content:
application/json:
schema:
$ref: '#/components/schemas/Nope'
post:
responses:
'200':
description: ok`

var idxNode yaml.Node
_ = yaml.Unmarshal([]byte(yml), &idxNode)
idx := index.NewSpecIndex(&idxNode)

var n PathItem
_ = low.BuildModel(idxNode.Content[0], &n)
err := n.Build(context.Background(), nil, idxNode.Content[0], idx)

// the dangling $ref is expected to surface as an error...
assert.Error(t, err)

// ...but the sibling POST operation must still be safe to inspect: its
// embedded reference is initialized, so IsReference() does not panic.
assert.NotNil(t, n.Post.Value)
assert.NotPanics(t, func() {
assert.False(t, n.Post.Value.IsReference())
})
}
Loading