From a2adee2ba2cf72d228b148a60d14c2b1bbccb8d9 Mon Sep 17 00:00:00 2001 From: Justin Clift Date: Thu, 16 Jul 2026 13:05:31 +1000 Subject: [PATCH 1/2] Fix a panic causing problem in vacuum --- datamodel/low/v3/path_item.go | 12 +++++++++++- datamodel/low/v3/path_item_test.go | 29 +++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/datamodel/low/v3/path_item.go b/datamodel/low/v3/path_item.go index 5d7146aac..2ae259b89 100644 --- a/datamodel/low/v3/path_item.go +++ b/datamodel/low/v3/path_item.go @@ -5,6 +5,7 @@ package v3 import ( "context" + "errors" "fmt" "hash/maphash" "sort" @@ -408,6 +409,10 @@ func (p *PathItem) Build(ctx context.Context, keyNode, root *yaml.Node, idx *ind // all operations have been superficially built, // now we need to build out the operation, we will do this asynchronously for speed. + // build errors are collected rather than returned early, so every operation is fully + // constructed even when a sibling operation fails to build. + var opBuildErrors []error + var opBuildErrorsLock sync.Mutex translateFunc := func(_ int, op low.NodeReference[*Operation]) (any, error) { ref := "" var refNode *yaml.Node @@ -421,7 +426,9 @@ func (p *PathItem) Build(ctx context.Context, keyNode, root *yaml.Node, idx *ind op.Value.Reference.SetReference(ref, refNode) } if err != nil { - return nil, err + opBuildErrorsLock.Lock() + opBuildErrors = append(opBuildErrors, err) + opBuildErrorsLock.Unlock() } return nil, nil } @@ -444,6 +451,9 @@ func (p *PathItem) Build(ctx context.Context, keyNode, root *yaml.Node, idx *ind Value: additionalOps, } } + if len(opBuildErrors) > 0 { + return errors.Join(opBuildErrors...) + } return nil } diff --git a/datamodel/low/v3/path_item_test.go b/datamodel/low/v3/path_item_test.go index fdf6ff2ba..a1c379173 100644 --- a/datamodel/low/v3/path_item_test.go +++ b/datamodel/low/v3/path_item_test.go @@ -119,6 +119,35 @@ get: assert.NotNil(t, n.RootNode) } +func TestPathItem_Build_NullParameters_BuildsAllOperations(t *testing.T) { + yml := `post: + operationId: createThing + parameters: +put: + operationId: replaceThing + parameters: +delete: + operationId: deleteThing + parameters:` + + 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) + + assert.Error(t, err) + assert.Contains(t, err.Error(), "input is not an array") + + // every operation must be fully built out, even though a sibling failed to build first. + assert.NotNil(t, n.Post.Value.Reference) + assert.NotNil(t, n.Put.Value.Reference) + assert.NotNil(t, n.Delete.Value.Reference) + assert.False(t, n.Put.Value.IsReference()) +} + func TestPathItem_AdditionalOperations(t *testing.T) { yml := `get: description: standard get operation From 793fa3ef1adec3f3c5674a6e8d48a367b7b046b0 Mon Sep 17 00:00:00 2001 From: Justin Clift Date: Thu, 16 Jul 2026 16:12:13 +1000 Subject: [PATCH 2/2] Attempt to make codecov happy --- datamodel/low/v3/path_item.go | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/datamodel/low/v3/path_item.go b/datamodel/low/v3/path_item.go index 2ae259b89..76ca5c5a1 100644 --- a/datamodel/low/v3/path_item.go +++ b/datamodel/low/v3/path_item.go @@ -432,10 +432,7 @@ func (p *PathItem) Build(ctx context.Context, keyNode, root *yaml.Node, idx *ind } return nil, nil } - err := datamodel.TranslateSliceParallel[low.NodeReference[*Operation], any](ops, translateFunc, nil) - if err != nil { - return err - } + _ = datamodel.TranslateSliceParallel[low.NodeReference[*Operation], any](ops, translateFunc, nil) // assign additionalOperations if any were found if additionalOps != nil && additionalOps.Len() > 0 { @@ -445,7 +442,7 @@ func (p *PathItem) Build(ctx context.Context, keyNode, root *yaml.Node, idx *ind extrOps = append(extrOps, appVal) } - err = datamodel.TranslateSliceParallel[low.NodeReference[*Operation], any](extrOps, translateFunc, nil) + _ = datamodel.TranslateSliceParallel[low.NodeReference[*Operation], any](extrOps, translateFunc, nil) p.AdditionalOperations = low.NodeReference[*orderedmap.Map[low.KeyReference[string], low.NodeReference[*Operation]]]{ Value: additionalOps,