Skip to content

Commit be25f3a

Browse files
committed
added context to FindComponent
this allows for tracking of a root index when performing deeply nested reference end seeking using `seefRefEnd` in the utilities Resolves #402
1 parent 8f2dbb8 commit be25f3a

File tree

9 files changed

+62
-10
lines changed

9 files changed

+62
-10
lines changed

bundler/bundler.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"strings"
1111
"sync"
1212

13+
"context"
1314
"github.com/pb33f/libopenapi"
1415
"github.com/pb33f/libopenapi/datamodel"
1516
v3 "github.com/pb33f/libopenapi/datamodel/high/v3"
@@ -181,7 +182,7 @@ func bundle(model *v3.Document) ([]byte, error) {
181182
for _, i := range indexes {
182183
if i.GetSpecAbsolutePath() == refExp[0] {
183184
if mappedReference != nil && !mappedReference.Circular {
184-
mr := i.FindComponent(sequenced.Definition)
185+
mr := i.FindComponent(context.Background(), sequenced.Definition)
185186
if mr != nil {
186187
// found the component; this is the one we want to use.
187188
mappedReference = mr

bundler/bundler_composer.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"strings"
88
"sync"
99

10+
"context"
1011
v3 "github.com/pb33f/libopenapi/datamodel/high/v3"
1112
v3low "github.com/pb33f/libopenapi/datamodel/low/v3"
1213
"github.com/pb33f/libopenapi/index"
@@ -57,7 +58,7 @@ func handleIndex(c *handleIndexConfig) {
5758
if !strings.Contains(lookup, "#/") {
5859
lookup = sequenced.FullDefinition
5960
}
60-
mr := i.FindComponent(lookup)
61+
mr := i.FindComponent(context.Background(), lookup)
6162
if mr != nil {
6263
// found the component; this is the one we want to use.
6364
mappedReference = mr

bundler/bundler_composer_test.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,3 +116,33 @@ func TestBuildSchema(t *testing.T) {
116116
_, err := buildSchema(nil, nil)
117117
assert.Error(t, err)
118118
}
119+
120+
func TestBundlerComposed_StrangeRefs(t *testing.T) {
121+
specBytes, err := os.ReadFile("../test_specs/first.yaml")
122+
123+
doc, err := libopenapi.NewDocumentWithConfiguration(specBytes, &datamodel.DocumentConfiguration{
124+
BasePath: "../test_specs/",
125+
ExtractRefsSequentially: true,
126+
Logger: slog.Default(),
127+
})
128+
if err != nil {
129+
panic(err)
130+
}
131+
132+
v3Doc, errs := doc.BuildV3Model()
133+
if len(errs) > 0 {
134+
panic(errs)
135+
}
136+
137+
var bytes []byte
138+
139+
bytes, err = BundleDocumentComposed(&v3Doc.Model, &BundleCompositionConfig{Delimiter: "__"})
140+
if err != nil {
141+
panic(err)
142+
}
143+
144+
// windows needs a different byte count
145+
if runtime.GOOS != "windows" {
146+
assert.Len(t, bytes, 3075)
147+
}
148+
}

index/extract_refs.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212
"slices"
1313
"strings"
1414

15+
"context"
1516
"github.com/pb33f/libopenapi/utils"
1617
"gopkg.in/yaml.v3"
1718
)
@@ -668,7 +669,7 @@ func (index *SpecIndex) ExtractComponentsFromRefs(refs []*Reference) []*Referenc
668669
if unsafeAsync {
669670
index.refLock.Lock()
670671
}
671-
located := index.FindComponent(ref.FullDefinition)
672+
located := index.FindComponent(context.Background(), ref.FullDefinition)
672673
if unsafeAsync {
673674
index.refLock.Unlock()
674675
}

index/find_component.go

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111

1212
jsonpathconfig "github.com/speakeasy-api/jsonpath/pkg/jsonpath/config"
1313

14+
"context"
1415
"github.com/pb33f/libopenapi/utils"
1516
"github.com/speakeasy-api/jsonpath/pkg/jsonpath"
1617
"gopkg.in/yaml.v3"
@@ -19,7 +20,7 @@ import (
1920
// FindComponent will locate a component by its reference, returns nil if nothing is found.
2021
// This method will recurse through remote, local and file references. For each new external reference
2122
// a new index will be created. These indexes can then be traversed recursively.
22-
func (index *SpecIndex) FindComponent(componentId string) *Reference {
23+
func (index *SpecIndex) FindComponent(ctx context.Context, componentId string) *Reference {
2324
if index.root == nil {
2425
return nil
2526
}
@@ -40,6 +41,20 @@ func (index *SpecIndex) FindComponent(componentId string) *Reference {
4041
// does it contain a file extension?
4142
fileExt := filepath.Ext(componentId)
4243
if fileExt != "" {
44+
45+
// check if the context has a root index set, if so this is a deep search that has moved through multiple
46+
// indexes and we need to adjust the URI to reflect the location of the root index.
47+
if ctx.Value(RootIndexKey) != nil {
48+
rootIndex := ctx.Value(RootIndexKey).(*SpecIndex)
49+
if rootIndex != nil && rootIndex.specAbsolutePath != "" {
50+
dir := filepath.Dir(rootIndex.specAbsolutePath)
51+
// create an absolute path to the file.
52+
absoluteFilePath := filepath.Join(dir, componentId)
53+
// split into a URI.
54+
uri = []string{absoluteFilePath}
55+
}
56+
}
57+
4358
return index.lookupRolodex(uri)
4459
}
4560

index/search_index.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ type ContextKey string
1616
const (
1717
CurrentPathKey ContextKey = "currentPath"
1818
FoundIndexKey ContextKey = "foundIndex"
19+
RootIndexKey ContextKey = "currentIndex"
1920
)
2021

2122
func (index *SpecIndex) SearchIndexForReferenceByReference(fullRef *Reference) (*Reference, *SpecIndex) {
@@ -225,7 +226,7 @@ func (index *SpecIndex) SearchIndexForReferenceByReferenceWithContext(ctx contex
225226
found = FindComponent(node, compId, exp[0], idx)
226227
}
227228
if found == nil {
228-
found = idx.FindComponent(ref)
229+
found = idx.FindComponent(ctx, ref)
229230
}
230231

231232
if found != nil {

index/spec_index.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1045,6 +1045,7 @@ func (index *SpecIndex) GetOperationCount() int {
10451045
// is the path a ref?
10461046
if isRef, _, ref := utils.IsNodeRefValue(method); isRef {
10471047
ctx := context.WithValue(context.Background(), CurrentPathKey, index.specAbsolutePath)
1048+
ctx = context.WithValue(ctx, RootIndexKey, index)
10481049
pNode := seekRefEnd(ctx, index, ref)
10491050
if pNode != nil {
10501051
method = pNode.Node
@@ -1121,6 +1122,7 @@ func (index *SpecIndex) GetOperationsParameterCount() int {
11211122
// is the path a ref?
11221123
if isRef, _, ref := utils.IsNodeRefValue(pathPropertyNode); isRef {
11231124
ctx := context.WithValue(context.Background(), CurrentPathKey, index.specAbsolutePath)
1125+
ctx = context.WithValue(ctx, RootIndexKey, index)
11241126
pNode := seekRefEnd(ctx, index, ref)
11251127
if pNode != nil {
11261128
pathPropertyNode = pNode.Node

index/spec_index_test.go

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import (
1919
"testing"
2020
"time"
2121

22+
"context"
2223
"github.com/pb33f/libopenapi/utils"
2324
"github.com/stretchr/testify/assert"
2425
"gopkg.in/yaml.v3"
@@ -785,7 +786,7 @@ func TestSpecIndex_NoRoot(t *testing.T) {
785786
docs := index.ExtractExternalDocuments(nil)
786787
assert.Nil(t, docs)
787788
assert.Nil(t, refs)
788-
assert.Nil(t, index.FindComponent("nothing"))
789+
assert.Nil(t, index.FindComponent(context.Background(), "nothing"))
789790
assert.Equal(t, -1, index.GetOperationCount())
790791
assert.Equal(t, -1, index.GetPathCount())
791792
assert.Equal(t, -1, index.GetGlobalTagsCount())
@@ -1037,10 +1038,10 @@ func TestSpecIndex_FindComponent_WithACrazyAssPath(t *testing.T) {
10371038

10381039
index := NewSpecIndexWithConfig(&rootNode, CreateOpenAPIIndexConfig())
10391040
assert.Equal(t, "#/paths/~1crazy~1ass~1references/get/parameters/0",
1040-
index.FindComponent("#/paths/~1crazy~1ass~1references/get/responses/404/content/application~1xml;%20charset=utf-8/schema").Node.Content[1].Value)
1041+
index.FindComponent(context.Background(), "#/paths/~1crazy~1ass~1references/get/responses/404/content/application~1xml;%20charset=utf-8/schema").Node.Content[1].Value)
10411042

10421043
assert.Equal(t, "a param",
1043-
index.FindComponent("#/paths/~1crazy~1ass~1references/get/parameters/0").Node.Content[1].Value)
1044+
index.FindComponent(context.Background(), "#/paths/~1crazy~1ass~1references/get/parameters/0").Node.Content[1].Value)
10441045
}
10451046

10461047
func TestSpecIndex_FindComponent(t *testing.T) {
@@ -1057,7 +1058,7 @@ func TestSpecIndex_FindComponent(t *testing.T) {
10571058
_ = yaml.Unmarshal([]byte(yml), &rootNode)
10581059

10591060
index := NewSpecIndexWithConfig(&rootNode, CreateOpenAPIIndexConfig())
1060-
assert.Nil(t, index.FindComponent("I-do-not-exist"))
1061+
assert.Nil(t, index.FindComponent(context.Background(), "I-do-not-exist"))
10611062
}
10621063

10631064
func TestSpecIndex_TestPathsNodeAsArray(t *testing.T) {

index/utility_methods.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -425,7 +425,7 @@ func (index *SpecIndex) scanOperationParams(params []*yaml.Node, keyNode, pathIt
425425
// could be in the rolodex
426426
searchInIndex := findIndex(index, param.Content[1])
427427
ctx := context.WithValue(context.Background(), CurrentPathKey, searchInIndex.specAbsolutePath)
428-
428+
ctx = context.WithValue(ctx, RootIndexKey, searchInIndex)
429429
ref := seekRefEnd(ctx, searchInIndex, paramRefName)
430430
if ref != nil {
431431
paramRef = ref

0 commit comments

Comments
 (0)