diff --git a/datamodel/low/extraction_functions.go b/datamodel/low/extraction_functions.go index 732eb0b..75794b7 100644 --- a/datamodel/low/extraction_functions.go +++ b/datamodel/low/extraction_functions.go @@ -90,9 +90,9 @@ func LocateRefNode(root *yaml.Node, idx *index.SpecIndex) (*yaml.Node, error) { } // perform a search for the reference in the index - foundRefs := idx.SearchIndexForReference(rv) - if len(foundRefs) > 0 { - return utils.NodeAlias(foundRefs[0].Node), nil + foundRef := idx.SearchIndexForReference(rv) + if foundRef != nil { + return utils.NodeAlias(foundRef.Node), nil } // let's try something else to find our references. diff --git a/index/extract_refs.go b/index/extract_refs.go index fe6b1e5..258ad8a 100644 --- a/index/extract_refs.go +++ b/index/extract_refs.go @@ -467,14 +467,14 @@ func (index *SpecIndex) ExtractRefs(node, parent *yaml.Node, seenPath []string, } } } - if len(seenPath) > 0 { - seenPath = seenPath[:len(seenPath)-1] - } + //if len(seenPath) > 0 { + // seenPath = seenPath[:len(seenPath)-1] + //} } - if len(seenPath) > 0 { - seenPath = seenPath[:len(seenPath)-1] - } + //if len(seenPath) > 0 { + // seenPath = seenPath[:len(seenPath)-1] + //} index.refCount = len(index.allRefs) @@ -487,7 +487,7 @@ func (index *SpecIndex) ExtractComponentsFromRefs(refs []*Reference) []*Referenc var found []*Reference // run this async because when things get recursive, it can take a while - //c := make(chan bool) + c := make(chan bool) locate := func(ref *Reference, refIndex int, sequence []*ReferenceMapped) { located := index.FindComponent(ref.FullDefinition, ref.Node) @@ -532,7 +532,7 @@ func (index *SpecIndex) ExtractComponentsFromRefs(refs []*Reference) []*Referenc index.refErrors = append(index.refErrors, indexError) index.errorLock.Unlock() } - //c <- true + c <- true } var refsToCheck []*Reference @@ -556,17 +556,17 @@ func (index *SpecIndex) ExtractComponentsFromRefs(refs []*Reference) []*Referenc for r := range refsToCheck { // expand our index of all mapped refs - //go locate(refsToCheck[r], r, mappedRefsInSequence) - locate(refsToCheck[r], r, mappedRefsInSequence) // used for sync testing. + go locate(refsToCheck[r], r, mappedRefsInSequence) + //locate(refsToCheck[r], r, mappedRefsInSequence) // used for sync testing. } - //completedRefs := 0 - //for completedRefs < len(refsToCheck) { - // select { - // case <-c: - // completedRefs++ - // } - //} + completedRefs := 0 + for completedRefs < len(refsToCheck) { + select { + case <-c: + completedRefs++ + } + } for m := range mappedRefsInSequence { if mappedRefsInSequence[m] != nil { index.allMappedRefsSequenced = append(index.allMappedRefsSequenced, mappedRefsInSequence[m]) diff --git a/index/resolver.go b/index/resolver.go index f759a04..478de77 100644 --- a/index/resolver.go +++ b/index/resolver.go @@ -7,6 +7,7 @@ import ( "fmt" "github.com/pb33f/libopenapi/utils" "gopkg.in/yaml.v3" + "strings" ) // ResolvingError represents an issue the resolver had trying to stitch the tree together. @@ -215,7 +216,6 @@ func visitIndex(res *Resolver, idx *SpecIndex) { mapped := idx.GetMappedReferencesSequenced() mappedIndex := idx.GetMappedReferences() res.indexesVisited++ - for _, ref := range mapped { seenReferences := make(map[string]bool) var journey []*Reference @@ -244,9 +244,6 @@ func visitIndex(res *Resolver, idx *SpecIndex) { } } } - //for _, c := range idx.GetChildren() { - // visitIndex(res, c) - //} } // VisitReference will visit a reference as part of a journey and will return resolved nodes. @@ -269,9 +266,9 @@ func (resolver *Resolver) VisitReference(ref *Reference, seen map[string]bool, j if j.Definition == r.Definition { var foundDup *Reference - foundRefs := resolver.specIndex.SearchIndexForReferenceByReference(r) - if len(foundRefs) > 0 { - foundDup = foundRefs[0] + foundRef := resolver.specIndex.SearchIndexForReferenceByReference(r) + if foundRef != nil { + foundDup = foundRef } var circRef *CircularReferenceResult @@ -295,10 +292,8 @@ func (resolver *Resolver) VisitReference(ref *Reference, seen map[string]bool, j } if resolver.IgnoreArray && isArray { - fmt.Printf("Ignored: %s\n", circRef.GenerateJourneyPath()) resolver.ignoredArrayReferences = append(resolver.ignoredArrayReferences, circRef) } else { - fmt.Printf("Not Ignored: %s\n", circRef.GenerateJourneyPath()) resolver.circularReferences = append(resolver.circularReferences, circRef) } @@ -311,9 +306,9 @@ func (resolver *Resolver) VisitReference(ref *Reference, seen map[string]bool, j if !skip { var original *Reference - foundRefs := resolver.specIndex.SearchIndexForReferenceByReference(r) - if len(foundRefs) > 0 { - original = foundRefs[0] + foundRef := resolver.specIndex.SearchIndexForReferenceByReference(r) + if foundRef != nil { + original = foundRef } resolved := resolver.VisitReference(original, seen, journey, resolve) if resolve && !original.Circular { @@ -408,10 +403,23 @@ func (resolver *Resolver) extractRelatives(ref *Reference, node, parent *yaml.No } value := node.Content[i+1].Value - var locatedRef []*Reference + var locatedRef *Reference + + var fullDef string + exp := strings.Split(ref.FullDefinition, "#/") + if len(exp) == 2 { + if exp[0] != "" { + fullDef = fmt.Sprintf("%s%s", exp[0], value) + } else { + fullDef = value + } + } else { + fullDef = value + } + searchRef := &Reference{ Definition: value, - FullDefinition: ref.FullDefinition, + FullDefinition: fullDef, RemoteLocation: ref.RemoteLocation, IsRemote: true, } @@ -451,10 +459,8 @@ func (resolver *Resolver) extractRelatives(ref *Reference, node, parent *yaml.No } } - locatedRef[0].ParentNodeSchemaType = schemaType - - found = append(found, locatedRef[0]) - + locatedRef.ParentNodeSchemaType = schemaType + found = append(found, locatedRef) foundRelatives[value] = true } diff --git a/index/resolver_test.go b/index/resolver_test.go index b576df8..bf96478 100644 --- a/index/resolver_test.go +++ b/index/resolver_test.go @@ -5,9 +5,9 @@ import ( "fmt" "github.com/pb33f/libopenapi/datamodel" "github.com/pb33f/libopenapi/utils" + "net/http" "net/url" "os" - "path/filepath" "testing" "github.com/stretchr/testify/assert" @@ -24,27 +24,15 @@ func Benchmark_ResolveDocumentStripe(b *testing.B) { var rootNode yaml.Node _ = yaml.Unmarshal(resolveFile, &rootNode) - fileFS, err := NewLocalFS(baseDir, os.DirFS(filepath.Dir(baseDir))) - for n := 0; n < b.N; n++ { - if err != nil { - b.Fatal(err) - } - cf := CreateOpenAPIIndexConfig() - cf.AvoidBuildIndex = true rolo := NewRolodex(cf) rolo.SetRootNode(&rootNode) - cf.Rolodex = rolo - - // TODO: pick up here. - - rolo.AddLocalFS(baseDir, fileFS) indexedErr := rolo.IndexTheRolodex() - assert.Error(b, indexedErr) + assert.Len(b, utils.UnwrapErrors(indexedErr), 3) } } @@ -407,20 +395,16 @@ func TestResolver_ResolveComponents_Stripe(t *testing.T) { resolveFile, _ := os.ReadFile(baseDir) + var stripeRoot yaml.Node + _ = yaml.Unmarshal(resolveFile, &stripeRoot) + info, _ := datamodel.ExtractSpecInfoWithDocumentCheck(resolveFile, true) - fileFS, err := NewLocalFS(baseDir, os.DirFS(filepath.Dir(baseDir))) - if err != nil { - t.Fatal(err) - } - cf := CreateOpenAPIIndexConfig() - //cf.AvoidBuildIndex = true cf.SpecInfo = info - rolo := NewRolodex(cf) - cf.Rolodex = rolo - rolo.AddLocalFS(baseDir, fileFS) + rolo := NewRolodex(cf) + rolo.SetRootNode(&stripeRoot) indexedErr := rolo.IndexTheRolodex() @@ -545,20 +529,67 @@ func TestResolver_ResolveComponents_MixedRef(t *testing.T) { var rootNode yaml.Node _ = yaml.Unmarshal(mixedref, &rootNode) - b := CreateOpenAPIIndexConfig() - idx := NewSpecIndexWithConfig(&rootNode, b) + // create a test server. + server := test_buildMixedRefServer() + defer server.Close() - resolver := NewResolver(idx) - assert.NotNil(t, resolver) + // create a new config that allows local and remote to be mixed up. + cf := CreateOpenAPIIndexConfig() + cf.AvoidBuildIndex = true + cf.AllowRemoteLookup = true + cf.AvoidCircularReferenceCheck = true + cf.BasePath = "../test_specs" - circ := resolver.Resolve() - assert.Len(t, circ, 0) - assert.Equal(t, 5, resolver.GetIndexesVisited()) + // setting this baseURL will override the base + cf.BaseURL, _ = url.Parse(server.URL) + + // create a new rolodex + rolo := NewRolodex(cf) + + // set the rolodex root node to the root node of the spec. + rolo.SetRootNode(&rootNode) + + // create a new remote fs and set the config for indexing. + remoteFS, _ := NewRemoteFSWithRootURL(server.URL) + remoteFS.SetIndexConfig(cf) + + // set our remote handler func + + c := http.Client{} + + remoteFS.RemoteHandlerFunc = c.Get + + // configure the local filesystem. + fsCfg := LocalFSConfig{ + BaseDirectory: cf.BasePath, + FileFilters: []string{"burgershop.openapi.yaml"}, + DirFS: os.DirFS(cf.BasePath), + } + + // create a new local filesystem. + fileFS, err := NewLocalFSWithConfig(&fsCfg) + assert.NoError(t, err) + + // add file systems to the rolodex + rolo.AddLocalFS(cf.BasePath, fileFS) + rolo.AddRemoteFS(server.URL, remoteFS) + + // index the rolodex. + indexedErr := rolo.IndexTheRolodex() + + assert.NoError(t, indexedErr) + + rolo.Resolve() + index := rolo.GetRootIndex + resolver := index().GetResolver() + + assert.Len(t, resolver.GetCircularErrors(), 0) + assert.Equal(t, 3, resolver.GetIndexesVisited()) // in v0.8.2 a new check was added when indexing, to prevent re-indexing the same file multiple times. - assert.Equal(t, 191, resolver.GetRelativesSeen()) - assert.Equal(t, 35, resolver.GetJourneysTaken()) - assert.Equal(t, 62, resolver.GetReferenceVisited()) + assert.Equal(t, 6, resolver.GetRelativesSeen()) + assert.Equal(t, 5, resolver.GetJourneysTaken()) + assert.Equal(t, 7, resolver.GetReferenceVisited()) } func TestResolver_ResolveComponents_k8s(t *testing.T) { diff --git a/index/rolodex.go b/index/rolodex.go index ef2e083..cf091b9 100644 --- a/index/rolodex.go +++ b/index/rolodex.go @@ -356,18 +356,18 @@ func (r *Rolodex) IndexTheRolodex() error { // now that we have indexed all the files, we can build the index. r.indexes = indexBuildQueue - if !r.indexConfig.AvoidBuildIndex { - for _, idx := range indexBuildQueue { - idx.BuildIndex() - if r.indexConfig.AvoidCircularReferenceCheck { - continue - } - errs := idx.resolver.CheckForCircularReferences() - for e := range errs { - caughtErrors = append(caughtErrors, errs[e]) - } + //if !r.indexConfig.AvoidBuildIndex { + for _, idx := range indexBuildQueue { + idx.BuildIndex() + if r.indexConfig.AvoidCircularReferenceCheck { + continue + } + errs := idx.resolver.CheckForCircularReferences() + for e := range errs { + caughtErrors = append(caughtErrors, errs[e]) } } + //} // indexed and built every supporting file, we can build the root index (our entry point) @@ -453,7 +453,7 @@ func (r *Rolodex) Open(location string) (RolodexFile, error) { var remoteFile *RemoteFile if r == nil || r.localFS == nil && r.remoteFS == nil { - panic("WHAT NO....") + return nil, fmt.Errorf("rolodex has no file systems configured, cannot open '%s'", location) } fileLookup := location diff --git a/index/rolodex_remote_loader_test.go b/index/rolodex_remote_loader_test.go index 7d1e7dd..2b3a8ea 100644 --- a/index/rolodex_remote_loader_test.go +++ b/index/rolodex_remote_loader_test.go @@ -4,192 +4,192 @@ package index import ( - "github.com/stretchr/testify/assert" - "io" - "net/http" - "net/http/httptest" - "testing" - "time" + "github.com/stretchr/testify/assert" + "io" + "net/http" + "net/http/httptest" + "testing" + "time" ) var test_httpClient = &http.Client{Timeout: time.Duration(60) * time.Second} func test_buildServer() *httptest.Server { - return httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { - if req.URL.String() == "/file1.yaml" { - rw.Header().Set("Last-Modified", "Wed, 21 Oct 2015 07:28:00 GMT") - _, _ = rw.Write([]byte(`"$ref": "./deeper/file2.yaml#/components/schemas/Pet"`)) - return - } - if req.URL.String() == "/deeper/file2.yaml" { - rw.Header().Set("Last-Modified", "Wed, 21 Oct 2015 08:28:00 GMT") - _, _ = rw.Write([]byte(`"$ref": "/deeper/even_deeper/file3.yaml#/components/schemas/Pet"`)) - return - } + return httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { + if req.URL.String() == "/file1.yaml" { + rw.Header().Set("Last-Modified", "Wed, 21 Oct 2015 07:28:00 GMT") + _, _ = rw.Write([]byte(`"$ref": "./deeper/file2.yaml#/components/schemas/Pet"`)) + return + } + if req.URL.String() == "/deeper/file2.yaml" { + rw.Header().Set("Last-Modified", "Wed, 21 Oct 2015 08:28:00 GMT") + _, _ = rw.Write([]byte(`"$ref": "/deeper/even_deeper/file3.yaml#/components/schemas/Pet"`)) + return + } - if req.URL.String() == "/deeper/even_deeper/file3.yaml" { - rw.Header().Set("Last-Modified", "Wed, 21 Oct 2015 10:28:00 GMT") - _, _ = rw.Write([]byte(`"$ref": "../file2.yaml#/components/schemas/Pet"`)) - return - } + if req.URL.String() == "/deeper/even_deeper/file3.yaml" { + rw.Header().Set("Last-Modified", "Wed, 21 Oct 2015 10:28:00 GMT") + _, _ = rw.Write([]byte(`"$ref": "../file2.yaml#/components/schemas/Pet"`)) + return + } - rw.Header().Set("Last-Modified", "Wed, 21 Oct 2015 12:28:00 GMT") + rw.Header().Set("Last-Modified", "Wed, 21 Oct 2015 12:28:00 GMT") - if req.URL.String() == "/deeper/list.yaml" { - _, _ = rw.Write([]byte(`"$ref": "../file2.yaml"`)) - return - } + if req.URL.String() == "/deeper/list.yaml" { + _, _ = rw.Write([]byte(`"$ref": "../file2.yaml"`)) + return + } - if req.URL.String() == "/bag/list.yaml" { - _, _ = rw.Write([]byte(`"$ref": "pocket/list.yaml"\n\n"$ref": "zip/things.yaml"`)) - return - } + if req.URL.String() == "/bag/list.yaml" { + _, _ = rw.Write([]byte(`"$ref": "pocket/list.yaml"\n\n"$ref": "zip/things.yaml"`)) + return + } - if req.URL.String() == "/bag/pocket/list.yaml" { - _, _ = rw.Write([]byte(`"$ref": "../list.yaml"\n\n"$ref": "../../file2.yaml"`)) - return - } + if req.URL.String() == "/bag/pocket/list.yaml" { + _, _ = rw.Write([]byte(`"$ref": "../list.yaml"\n\n"$ref": "../../file2.yaml"`)) + return + } - if req.URL.String() == "/bag/pocket/things.yaml" { - _, _ = rw.Write([]byte(`"$ref": "list.yaml"`)) - return - } + if req.URL.String() == "/bag/pocket/things.yaml" { + _, _ = rw.Write([]byte(`"$ref": "list.yaml"`)) + return + } - if req.URL.String() == "/bag/zip/things.yaml" { - _, _ = rw.Write([]byte(`"$ref": "list.yaml"`)) - return - } + if req.URL.String() == "/bag/zip/things.yaml" { + _, _ = rw.Write([]byte(`"$ref": "list.yaml"`)) + return + } - if req.URL.String() == "/bag/zip/list.yaml" { - _, _ = rw.Write([]byte(`"$ref": "../list.yaml"\n\n"$ref": "../../file1.yaml"\n\n"$ref": "more.yaml""`)) - return - } + if req.URL.String() == "/bag/zip/list.yaml" { + _, _ = rw.Write([]byte(`"$ref": "../list.yaml"\n\n"$ref": "../../file1.yaml"\n\n"$ref": "more.yaml""`)) + return + } - if req.URL.String() == "/bag/zip/more.yaml" { - _, _ = rw.Write([]byte(`"$ref": "../../deeper/list.yaml"\n\n"$ref": "../../bad.yaml"`)) - return - } + if req.URL.String() == "/bag/zip/more.yaml" { + _, _ = rw.Write([]byte(`"$ref": "../../deeper/list.yaml"\n\n"$ref": "../../bad.yaml"`)) + return + } - if req.URL.String() == "/bad.yaml" { - rw.WriteHeader(http.StatusInternalServerError) - _, _ = rw.Write([]byte(`"error, cannot do the thing"`)) - return - } + if req.URL.String() == "/bad.yaml" { + rw.WriteHeader(http.StatusInternalServerError) + _, _ = rw.Write([]byte(`"error, cannot do the thing"`)) + return + } - _, _ = rw.Write([]byte(`OK`)) - })) + _, _ = rw.Write([]byte(`OK`)) + })) } func TestNewRemoteFS_BasicCheck(t *testing.T) { - server := test_buildServer() - defer server.Close() + server := test_buildServer() + defer server.Close() - //remoteFS := NewRemoteFS("https://raw.githubusercontent.com/digitalocean/openapi/main/specification/") - remoteFS, _ := NewRemoteFSWithRootURL(server.URL) - remoteFS.RemoteHandlerFunc = test_httpClient.Get + //remoteFS := NewRemoteFS("https://raw.githubusercontent.com/digitalocean/openapi/main/specification/") + remoteFS, _ := NewRemoteFSWithRootURL(server.URL) + remoteFS.RemoteHandlerFunc = test_httpClient.Get - file, err := remoteFS.Open("/file1.yaml") + file, err := remoteFS.Open("/file1.yaml") - assert.NoError(t, err) + assert.NoError(t, err) - bytes, rErr := io.ReadAll(file) - assert.NoError(t, rErr) + bytes, rErr := io.ReadAll(file) + assert.NoError(t, rErr) - assert.Equal(t, "\"$ref\": \"\"./deeper/file2.yaml#/components/schemas/Pet\"", string(bytes)) + assert.Equal(t, "\"$ref\": \"\"./deeper/file2.yaml#/components/schemas/Pet\"", string(bytes)) - stat, _ := file.Stat() + stat, _ := file.Stat() - assert.Equal(t, "file1.yaml", stat.Name()) - assert.Equal(t, int64(54), stat.Size()) + assert.Equal(t, "file1.yaml", stat.Name()) + assert.Equal(t, int64(54), stat.Size()) - lastMod := stat.ModTime() - assert.Equal(t, "2015-10-21 07:28:00 +0000 GMT", lastMod.String()) + lastMod := stat.ModTime() + assert.Equal(t, "2015-10-21 07:28:00 +0000 GMT", lastMod.String()) } func TestNewRemoteFS_BasicCheck_Relative(t *testing.T) { - server := test_buildServer() - defer server.Close() + server := test_buildServer() + defer server.Close() - remoteFS, _ := NewRemoteFSWithRootURL(server.URL) - remoteFS.RemoteHandlerFunc = test_httpClient.Get + remoteFS, _ := NewRemoteFSWithRootURL(server.URL) + remoteFS.RemoteHandlerFunc = test_httpClient.Get - file, err := remoteFS.Open("/deeper/file2.yaml") + file, err := remoteFS.Open("/deeper/file2.yaml") - assert.NoError(t, err) + assert.NoError(t, err) - bytes, rErr := io.ReadAll(file) - assert.NoError(t, rErr) + bytes, rErr := io.ReadAll(file) + assert.NoError(t, rErr) - assert.Equal(t, "\"$ref\": \"./deeper/even_deeper/file3.yaml#/components/schemas/Pet\"", string(bytes)) + assert.Equal(t, "\"$ref\": \"./deeper/even_deeper/file3.yaml#/components/schemas/Pet\"", string(bytes)) - stat, _ := file.Stat() + stat, _ := file.Stat() - assert.Equal(t, "/deeper/file2.yaml", stat.Name()) - assert.Equal(t, int64(65), stat.Size()) + assert.Equal(t, "/deeper/file2.yaml", stat.Name()) + assert.Equal(t, int64(65), stat.Size()) - lastMod := stat.ModTime() - assert.Equal(t, "2015-10-21 08:28:00 +0000 GMT", lastMod.String()) + lastMod := stat.ModTime() + assert.Equal(t, "2015-10-21 08:28:00 +0000 GMT", lastMod.String()) } func TestNewRemoteFS_BasicCheck_Relative_Deeper(t *testing.T) { - server := test_buildServer() - defer server.Close() + server := test_buildServer() + defer server.Close() - remoteFS, _ := NewRemoteFSWithRootURL(server.URL) - remoteFS.RemoteHandlerFunc = test_httpClient.Get + remoteFS, _ := NewRemoteFSWithRootURL(server.URL) + remoteFS.RemoteHandlerFunc = test_httpClient.Get - file, err := remoteFS.Open("/deeper/even_deeper/file3.yaml") + file, err := remoteFS.Open("/deeper/even_deeper/file3.yaml") - assert.NoError(t, err) + assert.NoError(t, err) - bytes, rErr := io.ReadAll(file) - assert.NoError(t, rErr) + bytes, rErr := io.ReadAll(file) + assert.NoError(t, rErr) - assert.Equal(t, "\"$ref\": \"../file2.yaml#/components/schemas/Pet\"", string(bytes)) + assert.Equal(t, "\"$ref\": \"../file2.yaml#/components/schemas/Pet\"", string(bytes)) - stat, _ := file.Stat() + stat, _ := file.Stat() - assert.Equal(t, "/deeper/even_deeper/file3.yaml", stat.Name()) - assert.Equal(t, int64(47), stat.Size()) + assert.Equal(t, "/deeper/even_deeper/file3.yaml", stat.Name()) + assert.Equal(t, int64(47), stat.Size()) - lastMod := stat.ModTime() - assert.Equal(t, "2015-10-21 10:28:00 +0000 GMT", lastMod.String()) + lastMod := stat.ModTime() + assert.Equal(t, "2015-10-21 10:28:00 +0000 GMT", lastMod.String()) } func TestNewRemoteFS_BasicCheck_SeekRelatives(t *testing.T) { - server := test_buildServer() - defer server.Close() + server := test_buildServer() + defer server.Close() - remoteFS, _ := NewRemoteFSWithRootURL(server.URL) - remoteFS.RemoteHandlerFunc = test_httpClient.Get + remoteFS, _ := NewRemoteFSWithRootURL(server.URL) + remoteFS.RemoteHandlerFunc = test_httpClient.Get - file, err := remoteFS.Open("/bag/list.yaml") + file, err := remoteFS.Open("/bag/list.yaml") - assert.Error(t, err) + assert.Error(t, err) - bytes, rErr := io.ReadAll(file) - assert.NoError(t, rErr) + bytes, rErr := io.ReadAll(file) + assert.NoError(t, rErr) - assert.Equal(t, "\"$ref\": \"pocket/list.yaml\"\\n\\n\"$ref\": \"zip/things.yaml\"", string(bytes)) + assert.Equal(t, "\"$ref\": \"pocket/list.yaml\"\\n\\n\"$ref\": \"zip/things.yaml\"", string(bytes)) - stat, _ := file.Stat() + stat, _ := file.Stat() - assert.Equal(t, "/bag/list.yaml", stat.Name()) - assert.Equal(t, int64(55), stat.Size()) + assert.Equal(t, "/bag/list.yaml", stat.Name()) + assert.Equal(t, int64(55), stat.Size()) - lastMod := stat.ModTime() - assert.Equal(t, "2015-10-21 12:28:00 +0000 GMT", lastMod.String()) + lastMod := stat.ModTime() + assert.Equal(t, "2015-10-21 12:28:00 +0000 GMT", lastMod.String()) - files := remoteFS.GetFiles() - assert.Len(t, remoteFS.remoteErrors, 1) - assert.Len(t, files, 10) + files := remoteFS.GetFiles() + assert.Len(t, remoteFS.remoteErrors, 1) + assert.Len(t, files, 10) - // check correct files are in the cache - assert.Equal(t, "/bag/list.yaml", files["/bag/list.yaml"].GetFullPath()) - assert.Equal(t, "list.yaml", files["/bag/list.yaml"].Name()) + // check correct files are in the cache + assert.Equal(t, "/bag/list.yaml", files["/bag/list.yaml"].GetFullPath()) + assert.Equal(t, "list.yaml", files["/bag/list.yaml"].Name()) } diff --git a/index/search_index.go b/index/search_index.go index b468738..76aa785 100644 --- a/index/search_index.go +++ b/index/search_index.go @@ -9,7 +9,7 @@ import ( "strings" ) -func (index *SpecIndex) SearchIndexForReferenceByReference(fullRef *Reference) []*Reference { +func (index *SpecIndex) SearchIndexForReferenceByReference(fullRef *Reference) *Reference { ref := fullRef.FullDefinition @@ -34,28 +34,24 @@ func (index *SpecIndex) SearchIndexForReferenceByReference(fullRef *Reference) [ } if r, ok := index.allMappedRefs[ref]; ok { - return []*Reference{r} - } - - if r, ok := index.allRefs[ref]; ok { - return []*Reference{r} + return r } + // check the rolodex for the reference. if roloLookup != "" { rFile, err := index.rolodex.Open(roloLookup) if err != nil { return nil } + + // extract the index from the rolodex file. idx := rFile.GetIndex() + index.resolver.indexesVisited++ if idx != nil { // check mapped refs. if r, ok := idx.allMappedRefs[ref]; ok { - return []*Reference{r} - } - - if r, ok := index.allRefs[ref]; ok { - return []*Reference{r} + return r } // build a collection of all the inline schemas and search them @@ -66,7 +62,7 @@ func (index *SpecIndex) SearchIndexForReferenceByReference(fullRef *Reference) [ d = append(d, idx.allInlineSchemaObjectDefinitions...) for _, s := range d { if s.Definition == ref { - return []*Reference{s} + return s } } } @@ -79,10 +75,6 @@ func (index *SpecIndex) SearchIndexForReferenceByReference(fullRef *Reference) [ // SearchIndexForReference searches the index for a reference, first looking through the mapped references // and then externalSpecIndex for a match. If no match is found, it will recursively search the child indexes // extracted when parsing the OpenAPI Spec. -func (index *SpecIndex) SearchIndexForReference(ref string) []*Reference { - return index.SearchIndexForReferenceByReference(&Reference{FullDefinition: ref}) -} - -func (index *SpecIndex) SearchIndexForReferenceWithParent(ref string, reference *Reference) []*Reference { +func (index *SpecIndex) SearchIndexForReference(ref string) *Reference { return index.SearchIndexForReferenceByReference(&Reference{FullDefinition: ref}) } diff --git a/index/spec_index_test.go b/index/spec_index_test.go index 1199357..560dc5d 100644 --- a/index/spec_index_test.go +++ b/index/spec_index_test.go @@ -4,344 +4,344 @@ package index import ( - "fmt" - "log" - "net/http" - "net/http/httptest" - "net/url" - "os" - "os/exec" - "path/filepath" - "testing" + "fmt" + "log" + "net/http" + "net/http/httptest" + "net/url" + "os" + "os/exec" + "path/filepath" + "testing" - "github.com/stretchr/testify/assert" - "gopkg.in/yaml.v3" + "github.com/stretchr/testify/assert" + "gopkg.in/yaml.v3" ) func TestSpecIndex_ExtractRefsStripe(t *testing.T) { - stripe, _ := os.ReadFile("../test_specs/stripe.yaml") - var rootNode yaml.Node - _ = yaml.Unmarshal(stripe, &rootNode) + stripe, _ := os.ReadFile("../test_specs/stripe.yaml") + var rootNode yaml.Node + _ = yaml.Unmarshal(stripe, &rootNode) - index := NewSpecIndexWithConfig(&rootNode, CreateOpenAPIIndexConfig()) + index := NewSpecIndexWithConfig(&rootNode, CreateOpenAPIIndexConfig()) - assert.Len(t, index.allRefs, 385) - assert.Equal(t, 537, len(index.allMappedRefs)) - combined := index.GetAllCombinedReferences() - assert.Equal(t, 537, len(combined)) + assert.Len(t, index.allRefs, 385) + assert.Equal(t, 537, len(index.allMappedRefs)) + combined := index.GetAllCombinedReferences() + assert.Equal(t, 537, len(combined)) - assert.Len(t, index.rawSequencedRefs, 1972) - assert.Equal(t, 246, index.pathCount) - assert.Equal(t, 402, index.operationCount) - assert.Equal(t, 537, index.schemaCount) - assert.Equal(t, 0, index.globalTagsCount) - assert.Equal(t, 0, index.globalLinksCount) - assert.Equal(t, 0, index.componentParamCount) - assert.Equal(t, 143, index.operationParamCount) - assert.Equal(t, 88, index.componentsInlineParamDuplicateCount) - assert.Equal(t, 55, index.componentsInlineParamUniqueCount) - assert.Equal(t, 1516, index.enumCount) - assert.Len(t, index.GetAllEnums(), 1516) - assert.Len(t, index.GetPolyAllOfReferences(), 0) - assert.Len(t, index.GetPolyOneOfReferences(), 275) - assert.Len(t, index.GetPolyAnyOfReferences(), 553) - assert.Len(t, index.GetAllReferenceSchemas(), 1972) - assert.NotNil(t, index.GetRootServersNode()) - assert.Len(t, index.GetAllRootServers(), 1) + assert.Len(t, index.rawSequencedRefs, 1972) + assert.Equal(t, 246, index.pathCount) + assert.Equal(t, 402, index.operationCount) + assert.Equal(t, 537, index.schemaCount) + assert.Equal(t, 0, index.globalTagsCount) + assert.Equal(t, 0, index.globalLinksCount) + assert.Equal(t, 0, index.componentParamCount) + assert.Equal(t, 143, index.operationParamCount) + assert.Equal(t, 88, index.componentsInlineParamDuplicateCount) + assert.Equal(t, 55, index.componentsInlineParamUniqueCount) + assert.Equal(t, 1516, index.enumCount) + assert.Len(t, index.GetAllEnums(), 1516) + assert.Len(t, index.GetPolyAllOfReferences(), 0) + assert.Len(t, index.GetPolyOneOfReferences(), 275) + assert.Len(t, index.GetPolyAnyOfReferences(), 553) + assert.Len(t, index.GetAllReferenceSchemas(), 1972) + assert.NotNil(t, index.GetRootServersNode()) + assert.Len(t, index.GetAllRootServers(), 1) - // not required, but flip the circular result switch on and off. - assert.False(t, index.AllowCircularReferenceResolving()) - index.SetAllowCircularReferenceResolving(true) - assert.True(t, index.AllowCircularReferenceResolving()) + // not required, but flip the circular result switch on and off. + assert.False(t, index.AllowCircularReferenceResolving()) + index.SetAllowCircularReferenceResolving(true) + assert.True(t, index.AllowCircularReferenceResolving()) - // simulate setting of circular references, also pointless but needed for coverage. - assert.Nil(t, index.GetCircularReferences()) - index.SetCircularReferences([]*CircularReferenceResult{new(CircularReferenceResult)}) - assert.Len(t, index.GetCircularReferences(), 1) + // simulate setting of circular references, also pointless but needed for coverage. + assert.Nil(t, index.GetCircularReferences()) + index.SetCircularReferences([]*CircularReferenceResult{new(CircularReferenceResult)}) + assert.Len(t, index.GetCircularReferences(), 1) - assert.Len(t, index.GetRefsByLine(), 537) - assert.Len(t, index.GetLinesWithReferences(), 1972) - assert.Len(t, index.GetAllExternalDocuments(), 0) - assert.Len(t, index.GetAllExternalIndexes(), 0) + assert.Len(t, index.GetRefsByLine(), 537) + assert.Len(t, index.GetLinesWithReferences(), 1972) + assert.Len(t, index.GetAllExternalDocuments(), 0) + assert.Len(t, index.GetAllExternalIndexes(), 0) } func TestSpecIndex_Asana(t *testing.T) { - asana, _ := os.ReadFile("../test_specs/asana.yaml") - var rootNode yaml.Node - _ = yaml.Unmarshal(asana, &rootNode) + asana, _ := os.ReadFile("../test_specs/asana.yaml") + var rootNode yaml.Node + _ = yaml.Unmarshal(asana, &rootNode) - index := NewSpecIndexWithConfig(&rootNode, CreateOpenAPIIndexConfig()) + index := NewSpecIndexWithConfig(&rootNode, CreateOpenAPIIndexConfig()) - assert.Len(t, index.allRefs, 152) - assert.Len(t, index.allMappedRefs, 171) - combined := index.GetAllCombinedReferences() - assert.Equal(t, 171, len(combined)) - assert.Equal(t, 118, index.pathCount) - assert.Equal(t, 152, index.operationCount) - assert.Equal(t, 135, index.schemaCount) - assert.Equal(t, 26, index.globalTagsCount) - assert.Equal(t, 0, index.globalLinksCount) - assert.Equal(t, 30, index.componentParamCount) - assert.Equal(t, 107, index.operationParamCount) - assert.Equal(t, 8, index.componentsInlineParamDuplicateCount) - assert.Equal(t, 69, index.componentsInlineParamUniqueCount) + assert.Len(t, index.allRefs, 152) + assert.Len(t, index.allMappedRefs, 171) + combined := index.GetAllCombinedReferences() + assert.Equal(t, 171, len(combined)) + assert.Equal(t, 118, index.pathCount) + assert.Equal(t, 152, index.operationCount) + assert.Equal(t, 135, index.schemaCount) + assert.Equal(t, 26, index.globalTagsCount) + assert.Equal(t, 0, index.globalLinksCount) + assert.Equal(t, 30, index.componentParamCount) + assert.Equal(t, 107, index.operationParamCount) + assert.Equal(t, 8, index.componentsInlineParamDuplicateCount) + assert.Equal(t, 69, index.componentsInlineParamUniqueCount) } func TestSpecIndex_DigitalOcean(t *testing.T) { - do, _ := os.ReadFile("../test_specs/digitalocean.yaml") - var rootNode yaml.Node - _ = yaml.Unmarshal(do, &rootNode) + do, _ := os.ReadFile("../test_specs/digitalocean.yaml") + var rootNode yaml.Node + _ = yaml.Unmarshal(do, &rootNode) - baseURL, _ := url.Parse("https://raw.githubusercontent.com/digitalocean/openapi/main/specification") - index := NewSpecIndexWithConfig(&rootNode, &SpecIndexConfig{ - BaseURL: baseURL, - //AllowRemoteLookup: true, - //AllowFileLookup: true, - }) + baseURL, _ := url.Parse("https://raw.githubusercontent.com/digitalocean/openapi/main/specification") + index := NewSpecIndexWithConfig(&rootNode, &SpecIndexConfig{ + BaseURL: baseURL, + AllowRemoteLookup: true, + AllowFileLookup: true, + }) - assert.Len(t, index.GetAllExternalIndexes(), 291) - assert.NotNil(t, index) + assert.Len(t, index.GetAllExternalIndexes(), 291) + assert.NotNil(t, index) } func TestSpecIndex_DigitalOcean_FullCheckoutLocalResolve(t *testing.T) { - // this is a full checkout of the digitalocean API repo. - tmp, _ := os.MkdirTemp("", "openapi") - cmd := exec.Command("git", "clone", "https://github.com/digitalocean/openapi", tmp) - defer os.RemoveAll(filepath.Join(tmp, "openapi")) - err := cmd.Run() - if err != nil { - log.Fatalf("cmd.Run() failed with %s\n", err) - } - spec, _ := filepath.Abs(filepath.Join(tmp, "specification", "DigitalOcean-public.v2.yaml")) - doLocal, _ := os.ReadFile(spec) - var rootNode yaml.Node - _ = yaml.Unmarshal(doLocal, &rootNode) + // this is a full checkout of the digitalocean API repo. + tmp, _ := os.MkdirTemp("", "openapi") + cmd := exec.Command("git", "clone", "https://github.com/digitalocean/openapi", tmp) + defer os.RemoveAll(filepath.Join(tmp, "openapi")) + err := cmd.Run() + if err != nil { + log.Fatalf("cmd.Run() failed with %s\n", err) + } + spec, _ := filepath.Abs(filepath.Join(tmp, "specification", "DigitalOcean-public.v2.yaml")) + doLocal, _ := os.ReadFile(spec) + var rootNode yaml.Node + _ = yaml.Unmarshal(doLocal, &rootNode) - config := CreateOpenAPIIndexConfig() - config.BasePath = filepath.Join(tmp, "specification") + config := CreateOpenAPIIndexConfig() + config.BasePath = filepath.Join(tmp, "specification") - index := NewSpecIndexWithConfig(&rootNode, config) + index := NewSpecIndexWithConfig(&rootNode, config) - assert.NotNil(t, index) - assert.Len(t, index.GetAllExternalIndexes(), 296) + assert.NotNil(t, index) + assert.Len(t, index.GetAllExternalIndexes(), 296) - ref := index.SearchIndexForReference("resources/apps/apps_list_instanceSizes.yml") - assert.NotNil(t, ref) - assert.Equal(t, "operationId", ref[0].Node.Content[0].Value) + ref := index.SearchIndexForReference("resources/apps/apps_list_instanceSizes.yml") + assert.NotNil(t, ref) + assert.Equal(t, "operationId", ref.Node.Content[0].Value) - ref = index.SearchIndexForReference("examples/ruby/domains_create.yml") - assert.NotNil(t, ref) - assert.Equal(t, "lang", ref[0].Node.Content[0].Value) + ref = index.SearchIndexForReference("examples/ruby/domains_create.yml") + assert.NotNil(t, ref) + assert.Equal(t, "lang", ref.Node.Content[0].Value) - ref = index.SearchIndexForReference("../../shared/responses/server_error.yml") - assert.NotNil(t, ref) - assert.Equal(t, "description", ref[0].Node.Content[0].Value) + ref = index.SearchIndexForReference("../../shared/responses/server_error.yml") + assert.NotNil(t, ref) + assert.Equal(t, "description", ref.Node.Content[0].Value) - ref = index.SearchIndexForReference("../models/options.yml") - assert.NotNil(t, ref) + ref = index.SearchIndexForReference("../models/options.yml") + assert.NotNil(t, ref) } func TestSpecIndex_DigitalOcean_LookupsNotAllowed(t *testing.T) { - asana, _ := os.ReadFile("../test_specs/digitalocean.yaml") - var rootNode yaml.Node - _ = yaml.Unmarshal(asana, &rootNode) + asana, _ := os.ReadFile("../test_specs/digitalocean.yaml") + var rootNode yaml.Node + _ = yaml.Unmarshal(asana, &rootNode) - baseURL, _ := url.Parse("https://raw.githubusercontent.com/digitalocean/openapi/main/specification") - index := NewSpecIndexWithConfig(&rootNode, &SpecIndexConfig{ - BaseURL: baseURL, - }) + baseURL, _ := url.Parse("https://raw.githubusercontent.com/digitalocean/openapi/main/specification") + index := NewSpecIndexWithConfig(&rootNode, &SpecIndexConfig{ + BaseURL: baseURL, + }) - // no lookups allowed, bits have not been set, so there should just be a bunch of errors. - assert.Len(t, index.GetAllExternalIndexes(), 0) - assert.True(t, len(index.GetReferenceIndexErrors()) > 0) + // no lookups allowed, bits have not been set, so there should just be a bunch of errors. + assert.Len(t, index.GetAllExternalIndexes(), 0) + assert.True(t, len(index.GetReferenceIndexErrors()) > 0) } func TestSpecIndex_BaseURLError(t *testing.T) { - asana, _ := os.ReadFile("../test_specs/digitalocean.yaml") - var rootNode yaml.Node - _ = yaml.Unmarshal(asana, &rootNode) + asana, _ := os.ReadFile("../test_specs/digitalocean.yaml") + var rootNode yaml.Node + _ = yaml.Unmarshal(asana, &rootNode) - // this should fail because the base url is not a valid url and digital ocean won't be able to resolve - // anything. - baseURL, _ := url.Parse("https://githerbs.com/fresh/herbs/for/you") - index := NewSpecIndexWithConfig(&rootNode, &SpecIndexConfig{ - BaseURL: baseURL, - //AllowRemoteLookup: true, - //AllowFileLookup: true, - }) + // this should fail because the base url is not a valid url and digital ocean won't be able to resolve + // anything. + baseURL, _ := url.Parse("https://githerbs.com/fresh/herbs/for/you") + index := NewSpecIndexWithConfig(&rootNode, &SpecIndexConfig{ + BaseURL: baseURL, + //AllowRemoteLookup: true, + //AllowFileLookup: true, + }) - assert.Len(t, index.GetAllExternalIndexes(), 0) + assert.Len(t, index.GetAllExternalIndexes(), 0) } func TestSpecIndex_k8s(t *testing.T) { - asana, _ := os.ReadFile("../test_specs/k8s.json") - var rootNode yaml.Node - _ = yaml.Unmarshal(asana, &rootNode) + asana, _ := os.ReadFile("../test_specs/k8s.json") + var rootNode yaml.Node + _ = yaml.Unmarshal(asana, &rootNode) - index := NewSpecIndexWithConfig(&rootNode, CreateOpenAPIIndexConfig()) + index := NewSpecIndexWithConfig(&rootNode, CreateOpenAPIIndexConfig()) - assert.Len(t, index.allRefs, 558) - assert.Equal(t, 563, len(index.allMappedRefs)) - combined := index.GetAllCombinedReferences() - assert.Equal(t, 563, len(combined)) - assert.Equal(t, 436, index.pathCount) - assert.Equal(t, 853, index.operationCount) - assert.Equal(t, 563, index.schemaCount) - assert.Equal(t, 0, index.globalTagsCount) - assert.Equal(t, 58, index.operationTagsCount) - assert.Equal(t, 0, index.globalLinksCount) - assert.Equal(t, 0, index.componentParamCount) - assert.Equal(t, 36, index.operationParamCount) - assert.Equal(t, 26, index.componentsInlineParamDuplicateCount) - assert.Equal(t, 10, index.componentsInlineParamUniqueCount) - assert.Equal(t, 58, index.GetTotalTagsCount()) - assert.Equal(t, 2524, index.GetRawReferenceCount()) + assert.Len(t, index.allRefs, 558) + assert.Equal(t, 563, len(index.allMappedRefs)) + combined := index.GetAllCombinedReferences() + assert.Equal(t, 563, len(combined)) + assert.Equal(t, 436, index.pathCount) + assert.Equal(t, 853, index.operationCount) + assert.Equal(t, 563, index.schemaCount) + assert.Equal(t, 0, index.globalTagsCount) + assert.Equal(t, 58, index.operationTagsCount) + assert.Equal(t, 0, index.globalLinksCount) + assert.Equal(t, 0, index.componentParamCount) + assert.Equal(t, 36, index.operationParamCount) + assert.Equal(t, 26, index.componentsInlineParamDuplicateCount) + assert.Equal(t, 10, index.componentsInlineParamUniqueCount) + assert.Equal(t, 58, index.GetTotalTagsCount()) + assert.Equal(t, 2524, index.GetRawReferenceCount()) } func TestSpecIndex_PetstoreV2(t *testing.T) { - asana, _ := os.ReadFile("../test_specs/petstorev2.json") - var rootNode yaml.Node - _ = yaml.Unmarshal(asana, &rootNode) + asana, _ := os.ReadFile("../test_specs/petstorev2.json") + var rootNode yaml.Node + _ = yaml.Unmarshal(asana, &rootNode) - index := NewSpecIndexWithConfig(&rootNode, CreateOpenAPIIndexConfig()) + index := NewSpecIndexWithConfig(&rootNode, CreateOpenAPIIndexConfig()) - assert.Len(t, index.allRefs, 6) - assert.Len(t, index.allMappedRefs, 6) - assert.Equal(t, 14, index.pathCount) - assert.Equal(t, 20, index.operationCount) - assert.Equal(t, 6, index.schemaCount) - assert.Equal(t, 3, index.globalTagsCount) - assert.Equal(t, 3, index.operationTagsCount) - assert.Equal(t, 0, index.globalLinksCount) - assert.Equal(t, 1, index.componentParamCount) - assert.Equal(t, 1, index.GetComponentParameterCount()) - assert.Equal(t, 11, index.operationParamCount) - assert.Equal(t, 5, index.componentsInlineParamDuplicateCount) - assert.Equal(t, 6, index.componentsInlineParamUniqueCount) - assert.Equal(t, 3, index.GetTotalTagsCount()) - assert.Equal(t, 2, len(index.GetSecurityRequirementReferences())) + assert.Len(t, index.allRefs, 6) + assert.Len(t, index.allMappedRefs, 6) + assert.Equal(t, 14, index.pathCount) + assert.Equal(t, 20, index.operationCount) + assert.Equal(t, 6, index.schemaCount) + assert.Equal(t, 3, index.globalTagsCount) + assert.Equal(t, 3, index.operationTagsCount) + assert.Equal(t, 0, index.globalLinksCount) + assert.Equal(t, 1, index.componentParamCount) + assert.Equal(t, 1, index.GetComponentParameterCount()) + assert.Equal(t, 11, index.operationParamCount) + assert.Equal(t, 5, index.componentsInlineParamDuplicateCount) + assert.Equal(t, 6, index.componentsInlineParamUniqueCount) + assert.Equal(t, 3, index.GetTotalTagsCount()) + assert.Equal(t, 2, len(index.GetSecurityRequirementReferences())) } func TestSpecIndex_XSOAR(t *testing.T) { - xsoar, _ := os.ReadFile("../test_specs/xsoar.json") - var rootNode yaml.Node - _ = yaml.Unmarshal(xsoar, &rootNode) + xsoar, _ := os.ReadFile("../test_specs/xsoar.json") + var rootNode yaml.Node + _ = yaml.Unmarshal(xsoar, &rootNode) - index := NewSpecIndexWithConfig(&rootNode, CreateOpenAPIIndexConfig()) - assert.Len(t, index.allRefs, 209) - assert.Equal(t, 85, index.pathCount) - assert.Equal(t, 88, index.operationCount) - assert.Equal(t, 245, index.schemaCount) - assert.Equal(t, 207, len(index.allMappedRefs)) - assert.Equal(t, 0, index.globalTagsCount) - assert.Equal(t, 0, index.operationTagsCount) - assert.Equal(t, 0, index.globalLinksCount) - assert.Len(t, index.GetRootSecurityReferences(), 1) - assert.NotNil(t, index.GetRootSecurityNode()) + index := NewSpecIndexWithConfig(&rootNode, CreateOpenAPIIndexConfig()) + assert.Len(t, index.allRefs, 209) + assert.Equal(t, 85, index.pathCount) + assert.Equal(t, 88, index.operationCount) + assert.Equal(t, 245, index.schemaCount) + assert.Equal(t, 207, len(index.allMappedRefs)) + assert.Equal(t, 0, index.globalTagsCount) + assert.Equal(t, 0, index.operationTagsCount) + assert.Equal(t, 0, index.globalLinksCount) + assert.Len(t, index.GetRootSecurityReferences(), 1) + assert.NotNil(t, index.GetRootSecurityNode()) } func TestSpecIndex_PetstoreV3(t *testing.T) { - petstore, _ := os.ReadFile("../test_specs/petstorev3.json") - var rootNode yaml.Node - _ = yaml.Unmarshal(petstore, &rootNode) + petstore, _ := os.ReadFile("../test_specs/petstorev3.json") + var rootNode yaml.Node + _ = yaml.Unmarshal(petstore, &rootNode) - index := NewSpecIndexWithConfig(&rootNode, CreateOpenAPIIndexConfig()) + index := NewSpecIndexWithConfig(&rootNode, CreateOpenAPIIndexConfig()) - assert.Len(t, index.allRefs, 7) - assert.Len(t, index.allMappedRefs, 7) - assert.Equal(t, 13, index.pathCount) - assert.Equal(t, 19, index.operationCount) - assert.Equal(t, 8, index.schemaCount) - assert.Equal(t, 3, index.globalTagsCount) - assert.Equal(t, 3, index.operationTagsCount) - assert.Equal(t, 0, index.globalLinksCount) - assert.Equal(t, 0, index.componentParamCount) - assert.Equal(t, 9, index.operationParamCount) - assert.Equal(t, 4, index.componentsInlineParamDuplicateCount) - assert.Equal(t, 5, index.componentsInlineParamUniqueCount) - assert.Equal(t, 3, index.GetTotalTagsCount()) - assert.Equal(t, 90, index.GetAllDescriptionsCount()) - assert.Equal(t, 19, index.GetAllSummariesCount()) - assert.Len(t, index.GetAllDescriptions(), 90) - assert.Len(t, index.GetAllSummaries(), 19) + assert.Len(t, index.allRefs, 7) + assert.Len(t, index.allMappedRefs, 7) + assert.Equal(t, 13, index.pathCount) + assert.Equal(t, 19, index.operationCount) + assert.Equal(t, 8, index.schemaCount) + assert.Equal(t, 3, index.globalTagsCount) + assert.Equal(t, 3, index.operationTagsCount) + assert.Equal(t, 0, index.globalLinksCount) + assert.Equal(t, 0, index.componentParamCount) + assert.Equal(t, 9, index.operationParamCount) + assert.Equal(t, 4, index.componentsInlineParamDuplicateCount) + assert.Equal(t, 5, index.componentsInlineParamUniqueCount) + assert.Equal(t, 3, index.GetTotalTagsCount()) + assert.Equal(t, 90, index.GetAllDescriptionsCount()) + assert.Equal(t, 19, index.GetAllSummariesCount()) + assert.Len(t, index.GetAllDescriptions(), 90) + assert.Len(t, index.GetAllSummaries(), 19) } var mappedRefs = 15 func TestSpecIndex_BurgerShop(t *testing.T) { - burgershop, _ := os.ReadFile("../test_specs/burgershop.openapi.yaml") - var rootNode yaml.Node - _ = yaml.Unmarshal(burgershop, &rootNode) + burgershop, _ := os.ReadFile("../test_specs/burgershop.openapi.yaml") + var rootNode yaml.Node + _ = yaml.Unmarshal(burgershop, &rootNode) - index := NewSpecIndexWithConfig(&rootNode, CreateOpenAPIIndexConfig()) + index := NewSpecIndexWithConfig(&rootNode, CreateOpenAPIIndexConfig()) - assert.Len(t, index.allRefs, mappedRefs) - assert.Len(t, index.allMappedRefs, mappedRefs) - assert.Equal(t, mappedRefs, len(index.GetMappedReferences())) - assert.Equal(t, mappedRefs, len(index.GetMappedReferencesSequenced())) + assert.Len(t, index.allRefs, mappedRefs) + assert.Len(t, index.allMappedRefs, mappedRefs) + assert.Equal(t, mappedRefs, len(index.GetMappedReferences())) + assert.Equal(t, mappedRefs, len(index.GetMappedReferencesSequenced())) - assert.Equal(t, 6, index.pathCount) - assert.Equal(t, 6, index.GetPathCount()) + assert.Equal(t, 6, index.pathCount) + assert.Equal(t, 6, index.GetPathCount()) - assert.Equal(t, 6, len(index.GetAllComponentSchemas())) - assert.Equal(t, 56, len(index.GetAllSchemas())) + assert.Equal(t, 6, len(index.GetAllComponentSchemas())) + assert.Equal(t, 56, len(index.GetAllSchemas())) - assert.Equal(t, 34, len(index.GetAllSequencedReferences())) - assert.NotNil(t, index.GetSchemasNode()) - assert.NotNil(t, index.GetParametersNode()) + assert.Equal(t, 34, len(index.GetAllSequencedReferences())) + assert.NotNil(t, index.GetSchemasNode()) + assert.NotNil(t, index.GetParametersNode()) - assert.Equal(t, 5, index.operationCount) - assert.Equal(t, 5, index.GetOperationCount()) + assert.Equal(t, 5, index.operationCount) + assert.Equal(t, 5, index.GetOperationCount()) - assert.Equal(t, 6, index.schemaCount) - assert.Equal(t, 6, index.GetComponentSchemaCount()) + assert.Equal(t, 6, index.schemaCount) + assert.Equal(t, 6, index.GetComponentSchemaCount()) - assert.Equal(t, 2, index.globalTagsCount) - assert.Equal(t, 2, index.GetGlobalTagsCount()) - assert.Equal(t, 2, index.GetTotalTagsCount()) + assert.Equal(t, 2, index.globalTagsCount) + assert.Equal(t, 2, index.GetGlobalTagsCount()) + assert.Equal(t, 2, index.GetTotalTagsCount()) - assert.Equal(t, 2, index.operationTagsCount) - assert.Equal(t, 2, index.GetOperationTagsCount()) + assert.Equal(t, 2, index.operationTagsCount) + assert.Equal(t, 2, index.GetOperationTagsCount()) - assert.Equal(t, 3, index.globalLinksCount) - assert.Equal(t, 3, index.GetGlobalLinksCount()) + assert.Equal(t, 3, index.globalLinksCount) + assert.Equal(t, 3, index.GetGlobalLinksCount()) - assert.Equal(t, 1, index.globalCallbacksCount) - assert.Equal(t, 1, index.GetGlobalCallbacksCount()) + assert.Equal(t, 1, index.globalCallbacksCount) + assert.Equal(t, 1, index.GetGlobalCallbacksCount()) - assert.Equal(t, 2, index.componentParamCount) - assert.Equal(t, 2, index.GetComponentParameterCount()) + assert.Equal(t, 2, index.componentParamCount) + assert.Equal(t, 2, index.GetComponentParameterCount()) - assert.Equal(t, 4, index.operationParamCount) - assert.Equal(t, 4, index.GetOperationsParameterCount()) + assert.Equal(t, 4, index.operationParamCount) + assert.Equal(t, 4, index.GetOperationsParameterCount()) - assert.Equal(t, 0, index.componentsInlineParamDuplicateCount) - assert.Equal(t, 0, index.GetInlineDuplicateParamCount()) + assert.Equal(t, 0, index.componentsInlineParamDuplicateCount) + assert.Equal(t, 0, index.GetInlineDuplicateParamCount()) - assert.Equal(t, 2, index.componentsInlineParamUniqueCount) - assert.Equal(t, 2, index.GetInlineUniqueParamCount()) + assert.Equal(t, 2, index.componentsInlineParamUniqueCount) + assert.Equal(t, 2, index.GetInlineUniqueParamCount()) - assert.Equal(t, 1, len(index.GetAllRequestBodies())) - assert.NotNil(t, index.GetRootNode()) - assert.NotNil(t, index.GetGlobalTagsNode()) - assert.NotNil(t, index.GetPathsNode()) - assert.NotNil(t, index.GetDiscoveredReferences()) - assert.Equal(t, 1, len(index.GetPolyReferences())) - assert.NotNil(t, index.GetOperationParameterReferences()) - assert.Equal(t, 3, len(index.GetAllSecuritySchemes())) - assert.Equal(t, 2, len(index.GetAllParameters())) - assert.Equal(t, 1, len(index.GetAllResponses())) - assert.Equal(t, 2, len(index.GetInlineOperationDuplicateParameters())) - assert.Equal(t, 0, len(index.GetReferencesWithSiblings())) - assert.Equal(t, mappedRefs, len(index.GetAllReferences())) - assert.Equal(t, 0, len(index.GetOperationParametersIndexErrors())) - assert.Equal(t, 5, len(index.GetAllPaths())) - assert.Equal(t, 5, len(index.GetOperationTags())) - assert.Equal(t, 3, len(index.GetAllParametersFromOperations())) + assert.Equal(t, 1, len(index.GetAllRequestBodies())) + assert.NotNil(t, index.GetRootNode()) + assert.NotNil(t, index.GetGlobalTagsNode()) + assert.NotNil(t, index.GetPathsNode()) + assert.NotNil(t, index.GetDiscoveredReferences()) + assert.Equal(t, 1, len(index.GetPolyReferences())) + assert.NotNil(t, index.GetOperationParameterReferences()) + assert.Equal(t, 3, len(index.GetAllSecuritySchemes())) + assert.Equal(t, 2, len(index.GetAllParameters())) + assert.Equal(t, 1, len(index.GetAllResponses())) + assert.Equal(t, 2, len(index.GetInlineOperationDuplicateParameters())) + assert.Equal(t, 0, len(index.GetReferencesWithSiblings())) + assert.Equal(t, mappedRefs, len(index.GetAllReferences())) + assert.Equal(t, 0, len(index.GetOperationParametersIndexErrors())) + assert.Equal(t, 5, len(index.GetAllPaths())) + assert.Equal(t, 5, len(index.GetOperationTags())) + assert.Equal(t, 3, len(index.GetAllParametersFromOperations())) } func TestSpecIndex_GetAllParametersFromOperations(t *testing.T) { - yml := `openapi: 3.0.0 + yml := `openapi: 3.0.0 servers: - url: http://localhost:8080 paths: @@ -357,47 +357,47 @@ paths: schema: type: string` - var rootNode yaml.Node - _ = yaml.Unmarshal([]byte(yml), &rootNode) + var rootNode yaml.Node + _ = yaml.Unmarshal([]byte(yml), &rootNode) - index := NewSpecIndexWithConfig(&rootNode, CreateOpenAPIIndexConfig()) + index := NewSpecIndexWithConfig(&rootNode, CreateOpenAPIIndexConfig()) - assert.Equal(t, 1, len(index.GetAllParametersFromOperations())) - assert.Equal(t, 1, len(index.GetOperationParametersIndexErrors())) + assert.Equal(t, 1, len(index.GetAllParametersFromOperations())) + assert.Equal(t, 1, len(index.GetOperationParametersIndexErrors())) } func TestSpecIndex_BurgerShop_AllTheComponents(t *testing.T) { - burgershop, _ := os.ReadFile("../test_specs/all-the-components.yaml") - var rootNode yaml.Node - _ = yaml.Unmarshal(burgershop, &rootNode) + burgershop, _ := os.ReadFile("../test_specs/all-the-components.yaml") + var rootNode yaml.Node + _ = yaml.Unmarshal(burgershop, &rootNode) - index := NewSpecIndexWithConfig(&rootNode, CreateOpenAPIIndexConfig()) + index := NewSpecIndexWithConfig(&rootNode, CreateOpenAPIIndexConfig()) - assert.Equal(t, 1, len(index.GetAllHeaders())) - assert.Equal(t, 1, len(index.GetAllLinks())) - assert.Equal(t, 1, len(index.GetAllCallbacks())) - assert.Equal(t, 1, len(index.GetAllExamples())) - assert.Equal(t, 1, len(index.GetAllResponses())) - assert.Equal(t, 2, len(index.GetAllRootServers())) - assert.Equal(t, 2, len(index.GetAllOperationsServers())) + assert.Equal(t, 1, len(index.GetAllHeaders())) + assert.Equal(t, 1, len(index.GetAllLinks())) + assert.Equal(t, 1, len(index.GetAllCallbacks())) + assert.Equal(t, 1, len(index.GetAllExamples())) + assert.Equal(t, 1, len(index.GetAllResponses())) + assert.Equal(t, 2, len(index.GetAllRootServers())) + assert.Equal(t, 2, len(index.GetAllOperationsServers())) } func TestSpecIndex_SwaggerResponses(t *testing.T) { - yml := `swagger: 2.0 + yml := `swagger: 2.0 responses: niceResponse: description: hi` - var rootNode yaml.Node - _ = yaml.Unmarshal([]byte(yml), &rootNode) + var rootNode yaml.Node + _ = yaml.Unmarshal([]byte(yml), &rootNode) - index := NewSpecIndexWithConfig(&rootNode, CreateOpenAPIIndexConfig()) + index := NewSpecIndexWithConfig(&rootNode, CreateOpenAPIIndexConfig()) - assert.Equal(t, 1, len(index.GetAllResponses())) + assert.Equal(t, 1, len(index.GetAllResponses())) } func TestSpecIndex_NoNameParam(t *testing.T) { - yml := `paths: + yml := `paths: /users/{id}: parameters: - in: path @@ -409,165 +409,164 @@ func TestSpecIndex_NoNameParam(t *testing.T) { name: id - in: query` - var rootNode yaml.Node - _ = yaml.Unmarshal([]byte(yml), &rootNode) + var rootNode yaml.Node + _ = yaml.Unmarshal([]byte(yml), &rootNode) - index := NewSpecIndexWithConfig(&rootNode, CreateOpenAPIIndexConfig()) + index := NewSpecIndexWithConfig(&rootNode, CreateOpenAPIIndexConfig()) - assert.Equal(t, 2, len(index.GetOperationParametersIndexErrors())) + assert.Equal(t, 2, len(index.GetOperationParametersIndexErrors())) } func TestSpecIndex_NoRoot(t *testing.T) { - index := NewSpecIndex(nil) - refs := index.ExtractRefs(nil, nil, nil, 0, false, "") - docs := index.ExtractExternalDocuments(nil) - assert.Nil(t, docs) - assert.Nil(t, refs) - assert.Nil(t, index.FindComponent("nothing", nil)) - assert.Equal(t, -1, index.GetOperationCount()) - assert.Equal(t, -1, index.GetPathCount()) - assert.Equal(t, -1, index.GetGlobalTagsCount()) - assert.Equal(t, -1, index.GetOperationTagsCount()) - assert.Equal(t, -1, index.GetTotalTagsCount()) - assert.Equal(t, -1, index.GetOperationsParameterCount()) - assert.Equal(t, -1, index.GetComponentParameterCount()) - assert.Equal(t, -1, index.GetComponentSchemaCount()) - assert.Equal(t, -1, index.GetGlobalLinksCount()) + index := NewSpecIndex(nil) + refs := index.ExtractRefs(nil, nil, nil, 0, false, "") + docs := index.ExtractExternalDocuments(nil) + assert.Nil(t, docs) + assert.Nil(t, refs) + assert.Nil(t, index.FindComponent("nothing", nil)) + assert.Equal(t, -1, index.GetOperationCount()) + assert.Equal(t, -1, index.GetPathCount()) + assert.Equal(t, -1, index.GetGlobalTagsCount()) + assert.Equal(t, -1, index.GetOperationTagsCount()) + assert.Equal(t, -1, index.GetTotalTagsCount()) + assert.Equal(t, -1, index.GetOperationsParameterCount()) + assert.Equal(t, -1, index.GetComponentParameterCount()) + assert.Equal(t, -1, index.GetComponentSchemaCount()) + assert.Equal(t, -1, index.GetGlobalLinksCount()) } func test_buildMixedRefServer() *httptest.Server { - bs, _ := os.ReadFile("../test_specs/burgershop.openapi.yaml") - return httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { - if req.URL.String() == "/daveshanley/vacuum/main/model/test_files/burgershop.openapi.yaml" { - rw.Header().Set("Last-Modified", "Wed, 21 Oct 2015 07:28:00 GMT") - _, _ = rw.Write(bs) - return - } + bs, _ := os.ReadFile("../test_specs/burgershop.openapi.yaml") + return httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { + if req.URL.String() == "/daveshanley/vacuum/main/model/test_files/burgershop.openapi.yaml" { + rw.Header().Set("Last-Modified", "Wed, 21 Oct 2015 07:28:00 GMT") + _, _ = rw.Write(bs) + return + } - _, _ = rw.Write([]byte(`OK`)) - })) + _, _ = rw.Write([]byte(`OK`)) + })) } func TestSpecIndex_BurgerShopMixedRef(t *testing.T) { - // create a test server. - server := test_buildMixedRefServer() - defer server.Close() + // create a test server. + server := test_buildMixedRefServer() + defer server.Close() - // create a new config that allows local and remote to be mixed up. - cf := CreateOpenAPIIndexConfig() - cf.AvoidBuildIndex = true - cf.AllowRemoteLookup = true - cf.AvoidCircularReferenceCheck = true - cf.BasePath = "../test_specs" + // create a new config that allows local and remote to be mixed up. + cf := CreateOpenAPIIndexConfig() + cf.AvoidBuildIndex = true + cf.AllowRemoteLookup = true + cf.AvoidCircularReferenceCheck = true + cf.BasePath = "../test_specs" - // setting this baseURL will override the base - //cf.BaseURL, _ = url.Parse(server.URL) + // setting this baseURL will override the base + cf.BaseURL, _ = url.Parse(server.URL) - cFile := "../test_specs/mixedref-burgershop.openapi.yaml" - yml, _ := os.ReadFile(cFile) - var rootNode yaml.Node - _ = yaml.Unmarshal([]byte(yml), &rootNode) + cFile := "../test_specs/mixedref-burgershop.openapi.yaml" + yml, _ := os.ReadFile(cFile) + var rootNode yaml.Node + _ = yaml.Unmarshal([]byte(yml), &rootNode) - // create a new rolodex - rolo := NewRolodex(cf) + // create a new rolodex + rolo := NewRolodex(cf) - // set the rolodex root node to the root node of the spec. - rolo.SetRootNode(&rootNode) + // set the rolodex root node to the root node of the spec. + rolo.SetRootNode(&rootNode) - // create a new remote fs and set the config for indexing. - //remoteFS, _ := NewRemoteFSWithRootURL(server.URL) - remoteFS, _ := NewRemoteFS() - remoteFS.SetIndexConfig(cf) + // create a new remote fs and set the config for indexing. + remoteFS, _ := NewRemoteFSWithRootURL(server.URL) + remoteFS.SetIndexConfig(cf) - // set our remote handler func + // set our remote handler func - c := http.Client{} + c := http.Client{} - remoteFS.RemoteHandlerFunc = c.Get + remoteFS.RemoteHandlerFunc = c.Get - // configure the local filesystem. - fsCfg := LocalFSConfig{ - BaseDirectory: cf.BasePath, - FileFilters: []string{"burgershop.openapi.yaml"}, - DirFS: os.DirFS(cf.BasePath), - } + // configure the local filesystem. + fsCfg := LocalFSConfig{ + BaseDirectory: cf.BasePath, + FileFilters: []string{"burgershop.openapi.yaml"}, + DirFS: os.DirFS(cf.BasePath), + } - // create a new local filesystem. - fileFS, err := NewLocalFSWithConfig(&fsCfg) - assert.NoError(t, err) + // create a new local filesystem. + fileFS, err := NewLocalFSWithConfig(&fsCfg) + assert.NoError(t, err) - // add file systems to the rolodex - rolo.AddLocalFS(cf.BasePath, fileFS) - rolo.AddRemoteFS(server.URL, remoteFS) + // add file systems to the rolodex + rolo.AddLocalFS(cf.BasePath, fileFS) + rolo.AddRemoteFS(server.URL, remoteFS) - // index the rolodex. - indexedErr := rolo.IndexTheRolodex() - rolo.BuildIndexes() + // index the rolodex. + indexedErr := rolo.IndexTheRolodex() + rolo.BuildIndexes() - assert.NoError(t, indexedErr) + assert.NoError(t, indexedErr) - index := rolo.GetRootIndex() - rolo.CheckForCircularReferences() + index := rolo.GetRootIndex() + rolo.CheckForCircularReferences() - assert.Len(t, index.allRefs, 5) - assert.Len(t, index.allMappedRefs, 5) - assert.Equal(t, 5, index.GetPathCount()) - assert.Equal(t, 5, index.GetOperationCount()) - assert.Equal(t, 1, index.GetComponentSchemaCount()) - assert.Equal(t, 2, index.GetGlobalTagsCount()) - assert.Equal(t, 3, index.GetTotalTagsCount()) - assert.Equal(t, 2, index.GetOperationTagsCount()) - assert.Equal(t, 0, index.GetGlobalLinksCount()) - assert.Equal(t, 0, index.GetComponentParameterCount()) - assert.Equal(t, 2, index.GetOperationsParameterCount()) - assert.Equal(t, 1, index.GetInlineDuplicateParamCount()) - assert.Equal(t, 1, index.GetInlineUniqueParamCount()) - assert.Len(t, index.refErrors, 0) - assert.Len(t, index.GetCircularReferences(), 0) + assert.Len(t, index.allRefs, 5) + assert.Len(t, index.allMappedRefs, 5) + assert.Equal(t, 5, index.GetPathCount()) + assert.Equal(t, 5, index.GetOperationCount()) + assert.Equal(t, 1, index.GetComponentSchemaCount()) + assert.Equal(t, 2, index.GetGlobalTagsCount()) + assert.Equal(t, 3, index.GetTotalTagsCount()) + assert.Equal(t, 2, index.GetOperationTagsCount()) + assert.Equal(t, 0, index.GetGlobalLinksCount()) + assert.Equal(t, 0, index.GetComponentParameterCount()) + assert.Equal(t, 2, index.GetOperationsParameterCount()) + assert.Equal(t, 1, index.GetInlineDuplicateParamCount()) + assert.Equal(t, 1, index.GetInlineUniqueParamCount()) + assert.Len(t, index.refErrors, 0) + assert.Len(t, index.GetCircularReferences(), 0) } func TestSpecIndex_TestEmptyBrokenReferences(t *testing.T) { - asana, _ := os.ReadFile("../test_specs/badref-burgershop.openapi.yaml") - var rootNode yaml.Node - _ = yaml.Unmarshal(asana, &rootNode) + badref, _ := os.ReadFile("../test_specs/badref-burgershop.openapi.yaml") + var rootNode yaml.Node + _ = yaml.Unmarshal(badref, &rootNode) - index := NewSpecIndexWithConfig(&rootNode, CreateOpenAPIIndexConfig()) - assert.Equal(t, 5, index.GetPathCount()) - assert.Equal(t, 5, index.GetOperationCount()) - assert.Equal(t, 5, index.GetComponentSchemaCount()) - assert.Equal(t, 2, index.GetGlobalTagsCount()) - assert.Equal(t, 3, index.GetTotalTagsCount()) - assert.Equal(t, 2, index.GetOperationTagsCount()) - assert.Equal(t, 2, index.GetGlobalLinksCount()) - assert.Equal(t, 0, index.GetComponentParameterCount()) - assert.Equal(t, 2, index.GetOperationsParameterCount()) - assert.Equal(t, 1, index.GetInlineDuplicateParamCount()) - assert.Equal(t, 1, index.GetInlineUniqueParamCount()) - assert.Len(t, index.refErrors, 7) + index := NewSpecIndexWithConfig(&rootNode, CreateOpenAPIIndexConfig()) + assert.Equal(t, 5, index.GetPathCount()) + assert.Equal(t, 5, index.GetOperationCount()) + assert.Equal(t, 5, index.GetComponentSchemaCount()) + assert.Equal(t, 2, index.GetGlobalTagsCount()) + assert.Equal(t, 3, index.GetTotalTagsCount()) + assert.Equal(t, 2, index.GetOperationTagsCount()) + assert.Equal(t, 2, index.GetGlobalLinksCount()) + assert.Equal(t, 0, index.GetComponentParameterCount()) + assert.Equal(t, 2, index.GetOperationsParameterCount()) + assert.Equal(t, 1, index.GetInlineDuplicateParamCount()) + assert.Equal(t, 1, index.GetInlineUniqueParamCount()) + assert.Len(t, index.refErrors, 5) } func TestTagsNoDescription(t *testing.T) { - yml := `tags: + yml := `tags: - name: one - name: two - three: three` - var rootNode yaml.Node - _ = yaml.Unmarshal([]byte(yml), &rootNode) + var rootNode yaml.Node + _ = yaml.Unmarshal([]byte(yml), &rootNode) - index := NewSpecIndexWithConfig(&rootNode, CreateOpenAPIIndexConfig()) - assert.Equal(t, 3, index.GetGlobalTagsCount()) + index := NewSpecIndexWithConfig(&rootNode, CreateOpenAPIIndexConfig()) + assert.Equal(t, 3, index.GetGlobalTagsCount()) } func TestGlobalCallbacksNoIndexTest(t *testing.T) { - idx := new(SpecIndex) - assert.Equal(t, -1, idx.GetGlobalCallbacksCount()) + idx := new(SpecIndex) + assert.Equal(t, -1, idx.GetGlobalCallbacksCount()) } func TestMultipleCallbacksPerOperationVerb(t *testing.T) { - yml := `components: + yml := `components: callbacks: callbackA: "{$request.query.queryUrl}": @@ -596,15 +595,15 @@ paths: callbackA: $ref: '#/components/callbacks/CallbackA'` - var rootNode yaml.Node - _ = yaml.Unmarshal([]byte(yml), &rootNode) + var rootNode yaml.Node + _ = yaml.Unmarshal([]byte(yml), &rootNode) - index := NewSpecIndexWithConfig(&rootNode, CreateOpenAPIIndexConfig()) - assert.Equal(t, 4, index.GetGlobalCallbacksCount()) + index := NewSpecIndexWithConfig(&rootNode, CreateOpenAPIIndexConfig()) + assert.Equal(t, 4, index.GetGlobalCallbacksCount()) } func TestSpecIndex_ExtractComponentsFromRefs(t *testing.T) { - yml := `components: + yml := `components: schemas: pizza: properties: @@ -613,15 +612,15 @@ func TestSpecIndex_ExtractComponentsFromRefs(t *testing.T) { something: description: something` - var rootNode yaml.Node - _ = yaml.Unmarshal([]byte(yml), &rootNode) + var rootNode yaml.Node + _ = yaml.Unmarshal([]byte(yml), &rootNode) - index := NewSpecIndexWithConfig(&rootNode, CreateOpenAPIIndexConfig()) - assert.Len(t, index.GetReferenceIndexErrors(), 1) + index := NewSpecIndexWithConfig(&rootNode, CreateOpenAPIIndexConfig()) + assert.Len(t, index.GetReferenceIndexErrors(), 1) } func TestSpecIndex_FindComponent_WithACrazyAssPath(t *testing.T) { - yml := `paths: + yml := `paths: /crazy/ass/references: get: parameters: @@ -655,19 +654,19 @@ func TestSpecIndex_FindComponent_WithACrazyAssPath(t *testing.T) { $ref: "#/paths/~1crazy~1ass~1references/get/responses/404/content/application~1xml;%20charset=utf-8/schema" description: Not Found.` - var rootNode yaml.Node - _ = yaml.Unmarshal([]byte(yml), &rootNode) + var rootNode yaml.Node + _ = yaml.Unmarshal([]byte(yml), &rootNode) - index := NewSpecIndexWithConfig(&rootNode, CreateOpenAPIIndexConfig()) - assert.Equal(t, "#/paths/~1crazy~1ass~1references/get/parameters/0", - index.FindComponent("#/paths/~1crazy~1ass~1references/get/responses/404/content/application~1xml;%20charset=utf-8/schema", nil).Node.Content[1].Value) + index := NewSpecIndexWithConfig(&rootNode, CreateOpenAPIIndexConfig()) + assert.Equal(t, "#/paths/~1crazy~1ass~1references/get/parameters/0", + index.FindComponent("#/paths/~1crazy~1ass~1references/get/responses/404/content/application~1xml;%20charset=utf-8/schema", nil).Node.Content[1].Value) - assert.Equal(t, "a param", - index.FindComponent("#/paths/~1crazy~1ass~1references/get/parameters/0", nil).Node.Content[1].Value) + assert.Equal(t, "a param", + index.FindComponent("#/paths/~1crazy~1ass~1references/get/parameters/0", nil).Node.Content[1].Value) } func TestSpecIndex_FindComponenth(t *testing.T) { - yml := `components: + yml := `components: schemas: pizza: properties: @@ -676,15 +675,15 @@ func TestSpecIndex_FindComponenth(t *testing.T) { something: description: something` - var rootNode yaml.Node - _ = yaml.Unmarshal([]byte(yml), &rootNode) + var rootNode yaml.Node + _ = yaml.Unmarshal([]byte(yml), &rootNode) - index := NewSpecIndexWithConfig(&rootNode, CreateOpenAPIIndexConfig()) - assert.Nil(t, index.FindComponent("I-do-not-exist", nil)) + index := NewSpecIndexWithConfig(&rootNode, CreateOpenAPIIndexConfig()) + assert.Nil(t, index.FindComponent("I-do-not-exist", nil)) } func TestSpecIndex_TestPathsNodeAsArray(t *testing.T) { - yml := `components: + yml := `components: schemas: pizza: properties: @@ -693,187 +692,229 @@ func TestSpecIndex_TestPathsNodeAsArray(t *testing.T) { something: description: something` - var rootNode yaml.Node - _ = yaml.Unmarshal([]byte(yml), &rootNode) + var rootNode yaml.Node + _ = yaml.Unmarshal([]byte(yml), &rootNode) - index := NewSpecIndexWithConfig(&rootNode, CreateOpenAPIIndexConfig()) - assert.Nil(t, index.lookupRolodex(nil)) + index := NewSpecIndexWithConfig(&rootNode, CreateOpenAPIIndexConfig()) + assert.Nil(t, index.lookupRolodex(nil)) } func TestSpecIndex_lookupRemoteReference_SeenSourceSimulation_Error(t *testing.T) { - index := new(SpecIndex) - index.seenRemoteSources = make(map[string]*yaml.Node) - index.seenRemoteSources["https://no-hope-for-a-dope.com"] = &yaml.Node{} - _, _, err := index.lookupRemoteReference("https://no-hope-for-a-dope.com#/$.....#[;]something") - assert.Error(t, err) + index := new(SpecIndex) + index.seenRemoteSources = make(map[string]*yaml.Node) + index.seenRemoteSources["https://no-hope-for-a-dope.com"] = &yaml.Node{} + _, _, err := index.lookupRemoteReference("https://no-hope-for-a-dope.com#/$.....#[;]something") + assert.Error(t, err) } func TestSpecIndex_lookupRemoteReference_SeenSourceSimulation_BadFind(t *testing.T) { - index := new(SpecIndex) - index.seenRemoteSources = make(map[string]*yaml.Node) - index.seenRemoteSources["https://no-hope-for-a-dope.com"] = &yaml.Node{} - a, b, err := index.lookupRemoteReference("https://no-hope-for-a-dope.com#/hey") - assert.Error(t, err) - assert.Nil(t, a) - assert.Nil(t, b) + index := new(SpecIndex) + index.seenRemoteSources = make(map[string]*yaml.Node) + index.seenRemoteSources["https://no-hope-for-a-dope.com"] = &yaml.Node{} + a, b, err := index.lookupRemoteReference("https://no-hope-for-a-dope.com#/hey") + assert.Error(t, err) + assert.Nil(t, a) + assert.Nil(t, b) } // Discovered in issue https://github.com/pb33f/libopenapi/issues/37 func TestSpecIndex_lookupRemoteReference_NoComponent(t *testing.T) { - index := new(SpecIndex) - index.seenRemoteSources = make(map[string]*yaml.Node) - index.seenRemoteSources["https://api.rest.sh/schemas/ErrorModel.json"] = &yaml.Node{} - a, b, err := index.lookupRemoteReference("https://api.rest.sh/schemas/ErrorModel.json") - assert.NoError(t, err) - assert.NotNil(t, a) - assert.NotNil(t, b) + index := new(SpecIndex) + index.seenRemoteSources = make(map[string]*yaml.Node) + index.seenRemoteSources["https://api.rest.sh/schemas/ErrorModel.json"] = &yaml.Node{} + a, b, err := index.lookupRemoteReference("https://api.rest.sh/schemas/ErrorModel.json") + assert.NoError(t, err) + assert.NotNil(t, a) + assert.NotNil(t, b) } // Discovered in issue https://github.com/daveshanley/vacuum/issues/225 func TestSpecIndex_lookupFileReference_NoComponent(t *testing.T) { - cwd, _ := os.Getwd() - index := new(SpecIndex) - index.config = &SpecIndexConfig{BasePath: cwd} + cwd, _ := os.Getwd() + index := new(SpecIndex) + index.config = &SpecIndexConfig{BasePath: cwd} - _ = os.WriteFile("coffee-time.yaml", []byte("time: for coffee"), 0o664) - defer os.Remove("coffee-time.yaml") + _ = os.WriteFile("coffee-time.yaml", []byte("time: for coffee"), 0o664) + defer os.Remove("coffee-time.yaml") - index.seenRemoteSources = make(map[string]*yaml.Node) - a, b, err := index.lookupFileReference("coffee-time.yaml") - assert.NoError(t, err) - assert.NotNil(t, a) - assert.NotNil(t, b) + index.seenRemoteSources = make(map[string]*yaml.Node) + a, b, err := index.lookupFileReference("coffee-time.yaml") + assert.NoError(t, err) + assert.NotNil(t, a) + assert.NotNil(t, b) } func TestSpecIndex_CheckBadURLRef(t *testing.T) { - yml := `openapi: 3.1.0 + yml := `openapi: 3.1.0 paths: /cakes: post: parameters: - $ref: 'httpsss://badurl'` - var rootNode yaml.Node - _ = yaml.Unmarshal([]byte(yml), &rootNode) + var rootNode yaml.Node + _ = yaml.Unmarshal([]byte(yml), &rootNode) - index := NewSpecIndexWithConfig(&rootNode, CreateOpenAPIIndexConfig()) + index := NewSpecIndexWithConfig(&rootNode, CreateOpenAPIIndexConfig()) - assert.Len(t, index.refErrors, 2) + assert.Len(t, index.refErrors, 2) } func TestSpecIndex_CheckBadURLRefNoRemoteAllowed(t *testing.T) { - yml := `openapi: 3.1.0 + yml := `openapi: 3.1.0 paths: /cakes: post: parameters: - $ref: 'httpsss://badurl'` - var rootNode yaml.Node - _ = yaml.Unmarshal([]byte(yml), &rootNode) + var rootNode yaml.Node + _ = yaml.Unmarshal([]byte(yml), &rootNode) - c := CreateClosedAPIIndexConfig() - idx := NewSpecIndexWithConfig(&rootNode, c) + c := CreateClosedAPIIndexConfig() + idx := NewSpecIndexWithConfig(&rootNode, c) - assert.Len(t, idx.refErrors, 2) - assert.Equal(t, "remote lookups are not permitted, "+ - "please set AllowRemoteLookup to true in the configuration", idx.refErrors[0].Error()) + assert.Len(t, idx.refErrors, 2) + assert.Equal(t, "remote lookups are not permitted, "+ + "please set AllowRemoteLookup to true in the configuration", idx.refErrors[0].Error()) } func TestSpecIndex_CheckIndexDiscoversNoComponentLocalFileReference(t *testing.T) { - _ = os.WriteFile("coffee-time.yaml", []byte("name: time for coffee"), 0o664) - defer os.Remove("coffee-time.yaml") + _ = os.WriteFile("coffee-time.yaml", []byte("name: time for coffee"), 0o664) + defer os.Remove("coffee-time.yaml") - yml := `openapi: 3.0.3 + yml := `openapi: 3.0.3 paths: /cakes: post: parameters: - $ref: 'coffee-time.yaml'` - var rootNode yaml.Node - _ = yaml.Unmarshal([]byte(yml), &rootNode) + var rootNode yaml.Node + _ = yaml.Unmarshal([]byte(yml), &rootNode) - index := NewSpecIndexWithConfig(&rootNode, CreateOpenAPIIndexConfig()) + index := NewSpecIndexWithConfig(&rootNode, CreateOpenAPIIndexConfig()) - assert.NotNil(t, index.GetAllParametersFromOperations()["/cakes"]["post"]["coffee-time.yaml"][0].Node) + assert.NotNil(t, index.GetAllParametersFromOperations()["/cakes"]["post"]["coffee-time.yaml"][0].Node) } func TestSpecIndex_lookupRemoteReference_SeenSourceSimulation_BadJSON(t *testing.T) { - index := NewSpecIndexWithConfig(nil, &SpecIndexConfig{ - //AllowRemoteLookup: true, - }) - index.seenRemoteSources = make(map[string]*yaml.Node) - a, b, err := index.lookupRemoteReference("https://google.com//logos/doodles/2022/labor-day-2022-6753651837109490.3-l.png#/hey") - assert.Error(t, err) - assert.Nil(t, a) - assert.Nil(t, b) + index := NewSpecIndexWithConfig(nil, &SpecIndexConfig{ + //AllowRemoteLookup: true, + }) + index.seenRemoteSources = make(map[string]*yaml.Node) + a, b, err := index.lookupRemoteReference("https://google.com//logos/doodles/2022/labor-day-2022-6753651837109490.3-l.png#/hey") + assert.Error(t, err) + assert.Nil(t, a) + assert.Nil(t, b) } func TestSpecIndex_lookupFileReference_BadFileName(t *testing.T) { - index := NewSpecIndexWithConfig(nil, CreateOpenAPIIndexConfig()) - _, _, err := index.lookupFileReference("not-a-reference") - assert.Error(t, err) + index := NewSpecIndexWithConfig(nil, CreateOpenAPIIndexConfig()) + _, _, err := index.lookupFileReference("not-a-reference") + assert.Error(t, err) } func TestSpecIndex_lookupFileReference_SeenSourceSimulation_Error(t *testing.T) { - index := NewSpecIndexWithConfig(nil, CreateOpenAPIIndexConfig()) - index.seenRemoteSources = make(map[string]*yaml.Node) - index.seenRemoteSources["magic-money-file.json"] = &yaml.Node{} - _, _, err := index.lookupFileReference("magic-money-file.json#something") - assert.Error(t, err) + index := NewSpecIndexWithConfig(nil, CreateOpenAPIIndexConfig()) + index.seenRemoteSources = make(map[string]*yaml.Node) + index.seenRemoteSources["magic-money-file.json"] = &yaml.Node{} + _, _, err := index.lookupFileReference("magic-money-file.json#something") + assert.Error(t, err) } func TestSpecIndex_lookupFileReference_BadFile(t *testing.T) { - index := NewSpecIndexWithConfig(nil, CreateOpenAPIIndexConfig()) - _, _, err := index.lookupFileReference("chickers.json#no-rice") - assert.Error(t, err) + index := NewSpecIndexWithConfig(nil, CreateOpenAPIIndexConfig()) + _, _, err := index.lookupFileReference("chickers.json#no-rice") + assert.Error(t, err) } func TestSpecIndex_lookupFileReference_BadFileDataRead(t *testing.T) { - _ = os.WriteFile("chickers.yaml", []byte("broke: the: thing: [again]"), 0o664) - defer os.Remove("chickers.yaml") - var root yaml.Node - index := NewSpecIndexWithConfig(&root, CreateOpenAPIIndexConfig()) - _, _, err := index.lookupFileReference("chickers.yaml#no-rice") - assert.Error(t, err) + _ = os.WriteFile("chickers.yaml", []byte("broke: the: thing: [again]"), 0o664) + defer os.Remove("chickers.yaml") + var root yaml.Node + index := NewSpecIndexWithConfig(&root, CreateOpenAPIIndexConfig()) + _, _, err := index.lookupFileReference("chickers.yaml#no-rice") + assert.Error(t, err) } func TestSpecIndex_lookupFileReference_MultiRes(t *testing.T) { - _ = os.WriteFile("embie.yaml", []byte("naughty:\n - puppy: dog\n - puppy: naughty\npuppy:\n - naughty: puppy"), 0o664) - defer os.Remove("embie.yaml") + _ = os.WriteFile("embie.yaml", []byte("naughty:\n - puppy: dog\n - puppy: naughty\npuppy:\n - naughty: puppy"), 0o664) + defer os.Remove("embie.yaml") - index := NewSpecIndexWithConfig(nil, CreateOpenAPIIndexConfig()) - index.seenRemoteSources = make(map[string]*yaml.Node) - k, doc, err := index.lookupFileReference("embie.yaml#/.naughty") - assert.NoError(t, err) - assert.NotNil(t, doc) - assert.Nil(t, k) + index := NewSpecIndexWithConfig(nil, CreateOpenAPIIndexConfig()) + index.seenRemoteSources = make(map[string]*yaml.Node) + k, doc, err := index.lookupFileReference("embie.yaml#/.naughty") + assert.NoError(t, err) + assert.NotNil(t, doc) + assert.Nil(t, k) } func TestSpecIndex_lookupFileReference(t *testing.T) { - _ = os.WriteFile("fox.yaml", []byte("good:\n - puppy: dog\n - puppy: forever-more"), 0o664) - defer os.Remove("fox.yaml") - index := NewSpecIndexWithConfig(nil, CreateOpenAPIIndexConfig()) - index.seenRemoteSources = make(map[string]*yaml.Node) - k, doc, err := index.lookupFileReference("fox.yaml#/good") - assert.NoError(t, err) - assert.NotNil(t, doc) - assert.NotNil(t, k) + pup := []byte("good:\n - puppy: dog\n - puppy: forever-more") + + var myPuppy yaml.Node + _ = yaml.Unmarshal(pup, &myPuppy) + + _ = os.WriteFile("fox.yaml", pup, 0o664) + defer os.Remove("fox.yaml") + + // create a new config that allows local and remote to be mixed up. + cf := CreateOpenAPIIndexConfig() + cf.AvoidBuildIndex = true + cf.AllowRemoteLookup = true + cf.AvoidCircularReferenceCheck = true + cf.BasePath = "." + + // create a new rolodex + rolo := NewRolodex(cf) + + // set the rolodex root node to the root node of the spec. + rolo.SetRootNode(&myPuppy) + + // configure the local filesystem. + fsCfg := LocalFSConfig{ + BaseDirectory: cf.BasePath, + FileFilters: []string{"fox.yaml"}, + DirFS: os.DirFS(cf.BasePath), + } + + // create a new local filesystem. + fileFS, err := NewLocalFSWithConfig(&fsCfg) + assert.NoError(t, err) + + rolo.AddLocalFS(cf.BasePath, fileFS) + rErr := rolo.IndexTheRolodex() + + assert.NoError(t, rErr) + + fox, fErr := rolo.Open("fox.yaml") + assert.NoError(t, fErr) + assert.Equal(t, "fox.yaml", fox.Name()) + assert.Equal(t, "good:\n - puppy: dog\n - puppy: forever-more", string(fox.GetContent())) + } func TestSpecIndex_parameterReferencesHavePaths(t *testing.T) { - _ = os.WriteFile("paramour.yaml", []byte(`components: + + _ = os.WriteFile("paramour.yaml", []byte(`components: parameters: param3: name: param3 in: query schema: type: string`), 0o664) - defer os.Remove("paramour.yaml") + defer os.Remove("paramour.yaml") - yml := `paths: + // create a new config that allows local and remote to be mixed up. + cf := CreateOpenAPIIndexConfig() + cf.AvoidBuildIndex = true + cf.AllowRemoteLookup = true + cf.AvoidCircularReferenceCheck = true + cf.BasePath = "." + + yml := `paths: /: parameters: - $ref: '#/components/parameters/param1' @@ -900,35 +941,60 @@ components: schema: type: string` - var rootNode yaml.Node - _ = yaml.Unmarshal([]byte(yml), &rootNode) + var rootNode yaml.Node + _ = yaml.Unmarshal([]byte(yml), &rootNode) - index := NewSpecIndexWithConfig(&rootNode, CreateOpenAPIIndexConfig()) + // create a new rolodex + rolo := NewRolodex(cf) - params := index.GetAllParametersFromOperations() + // set the rolodex root node to the root node of the spec. + rolo.SetRootNode(&rootNode) - if assert.Contains(t, params, "/") { - if assert.Contains(t, params["/"], "top") { - if assert.Contains(t, params["/"]["top"], "#/components/parameters/param1") { - assert.Equal(t, "$.components.parameters.param1", params["/"]["top"]["#/components/parameters/param1"][0].Path) - } - if assert.Contains(t, params["/"]["top"], "paramour.yaml#/components/parameters/param3") { - assert.Equal(t, "$.components.parameters.param3", params["/"]["top"]["paramour.yaml#/components/parameters/param3"][0].Path) - } - } - if assert.Contains(t, params["/"], "get") { - if assert.Contains(t, params["/"]["get"], "#/components/parameters/param2") { - assert.Equal(t, "$.components.parameters.param2", params["/"]["get"]["#/components/parameters/param2"][0].Path) - } - if assert.Contains(t, params["/"]["get"], "test") { - assert.Equal(t, "$.paths./.get.parameters[2]", params["/"]["get"]["test"][0].Path) - } - } - } + // configure the local filesystem. + fsCfg := LocalFSConfig{ + BaseDirectory: cf.BasePath, + FileFilters: []string{"paramour.yaml"}, + DirFS: os.DirFS(cf.BasePath), + } + + // create a new local filesystem. + fileFS, err := NewLocalFSWithConfig(&fsCfg) + assert.NoError(t, err) + + // add file system + rolo.AddLocalFS(cf.BasePath, fileFS) + + // index the rolodex. + indexedErr := rolo.IndexTheRolodex() + assert.NoError(t, indexedErr) + rolo.BuildIndexes() + + index := rolo.GetRootIndex() + + params := index.GetAllParametersFromOperations() + + if assert.Contains(t, params, "/") { + if assert.Contains(t, params["/"], "top") { + if assert.Contains(t, params["/"]["top"], "#/components/parameters/param1") { + assert.Equal(t, "$.components.parameters.param1", params["/"]["top"]["#/components/parameters/param1"][0].Path) + } + if assert.Contains(t, params["/"]["top"], "paramour.yaml#/components/parameters/param3") { + assert.Equal(t, "$.components.parameters.param3", params["/"]["top"]["paramour.yaml#/components/parameters/param3"][0].Path) + } + } + if assert.Contains(t, params["/"], "get") { + if assert.Contains(t, params["/"]["get"], "#/components/parameters/param2") { + assert.Equal(t, "$.components.parameters.param2", params["/"]["get"]["#/components/parameters/param2"][0].Path) + } + if assert.Contains(t, params["/"]["get"], "test") { + assert.Equal(t, "$.paths./.get.parameters[2]", params["/"]["get"]["test"][0].Path) + } + } + } } func TestSpecIndex_serverReferencesHaveParentNodesAndPaths(t *testing.T) { - yml := `servers: + yml := `servers: - url: https://api.example.com/v1 paths: /: @@ -938,59 +1004,59 @@ paths: servers: - url: https://api.example.com/v3` - var rootNode yaml.Node - _ = yaml.Unmarshal([]byte(yml), &rootNode) + var rootNode yaml.Node + _ = yaml.Unmarshal([]byte(yml), &rootNode) - index := NewSpecIndexWithConfig(&rootNode, CreateOpenAPIIndexConfig()) + index := NewSpecIndexWithConfig(&rootNode, CreateOpenAPIIndexConfig()) - rootServers := index.GetAllRootServers() + rootServers := index.GetAllRootServers() - for i, server := range rootServers { - assert.NotNil(t, server.ParentNode) - assert.Equal(t, fmt.Sprintf("$.servers[%d]", i), server.Path) - } + for i, server := range rootServers { + assert.NotNil(t, server.ParentNode) + assert.Equal(t, fmt.Sprintf("$.servers[%d]", i), server.Path) + } - opServers := index.GetAllOperationsServers() + opServers := index.GetAllOperationsServers() - for path, ops := range opServers { - for op, servers := range ops { - for i, server := range servers { - assert.NotNil(t, server.ParentNode) + for path, ops := range opServers { + for op, servers := range ops { + for i, server := range servers { + assert.NotNil(t, server.ParentNode) - opPath := fmt.Sprintf(".%s", op) - if op == "top" { - opPath = "" - } + opPath := fmt.Sprintf(".%s", op) + if op == "top" { + opPath = "" + } - assert.Equal(t, fmt.Sprintf("$.paths.%s%s.servers[%d]", path, opPath, i), server.Path) - } - } - } + assert.Equal(t, fmt.Sprintf("$.paths.%s%s.servers[%d]", path, opPath, i), server.Path) + } + } + } } func TestSpecIndex_schemaComponentsHaveParentsAndPaths(t *testing.T) { - yml := `components: + yml := `components: schemas: Pet: type: object Dog: type: object` - var rootNode yaml.Node - _ = yaml.Unmarshal([]byte(yml), &rootNode) + var rootNode yaml.Node + _ = yaml.Unmarshal([]byte(yml), &rootNode) - index := NewSpecIndexWithConfig(&rootNode, CreateOpenAPIIndexConfig()) + index := NewSpecIndexWithConfig(&rootNode, CreateOpenAPIIndexConfig()) - schemas := index.GetAllSchemas() + schemas := index.GetAllSchemas() - for _, schema := range schemas { - assert.NotNil(t, schema.ParentNode) - assert.Equal(t, fmt.Sprintf("$.components.schemas.%s", schema.Name), schema.Path) - } + for _, schema := range schemas { + assert.NotNil(t, schema.ParentNode) + assert.Equal(t, fmt.Sprintf("$.components.schemas.%s", schema.Name), schema.Path) + } } func TestSpecIndex_ParamsWithDuplicateNamesButUniqueInTypes(t *testing.T) { - yml := `openapi: 3.1.0 + yml := `openapi: 3.1.0 info: title: Test version: 0.0.1 @@ -1026,19 +1092,19 @@ paths: "200": description: OK` - var rootNode yaml.Node - _ = yaml.Unmarshal([]byte(yml), &rootNode) + var rootNode yaml.Node + _ = yaml.Unmarshal([]byte(yml), &rootNode) - idx := NewSpecIndexWithConfig(&rootNode, CreateOpenAPIIndexConfig()) + idx := NewSpecIndexWithConfig(&rootNode, CreateOpenAPIIndexConfig()) - assert.Len(t, idx.paramAllRefs, 4) - assert.Len(t, idx.paramInlineDuplicateNames, 2) - assert.Len(t, idx.operationParamErrors, 0) - assert.Len(t, idx.refErrors, 0) + assert.Len(t, idx.paramAllRefs, 4) + assert.Len(t, idx.paramInlineDuplicateNames, 2) + assert.Len(t, idx.operationParamErrors, 0) + assert.Len(t, idx.refErrors, 0) } func TestSpecIndex_ParamsWithDuplicateNamesAndSameInTypes(t *testing.T) { - yml := `openapi: 3.1.0 + yml := `openapi: 3.1.0 info: title: Test version: 0.0.1 @@ -1074,19 +1140,19 @@ paths: "200": description: OK` - var rootNode yaml.Node - _ = yaml.Unmarshal([]byte(yml), &rootNode) + var rootNode yaml.Node + _ = yaml.Unmarshal([]byte(yml), &rootNode) - idx := NewSpecIndexWithConfig(&rootNode, CreateOpenAPIIndexConfig()) + idx := NewSpecIndexWithConfig(&rootNode, CreateOpenAPIIndexConfig()) - assert.Len(t, idx.paramAllRefs, 3) - assert.Len(t, idx.paramInlineDuplicateNames, 2) - assert.Len(t, idx.operationParamErrors, 1) - assert.Len(t, idx.refErrors, 0) + assert.Len(t, idx.paramAllRefs, 3) + assert.Len(t, idx.paramInlineDuplicateNames, 2) + assert.Len(t, idx.operationParamErrors, 1) + assert.Len(t, idx.refErrors, 0) } func TestSpecIndex_foundObjectsWithProperties(t *testing.T) { - yml := `paths: + yml := `paths: /test: get: responses: @@ -1114,64 +1180,64 @@ components: type: object additionalProperties: true` - var rootNode yaml.Node - yaml.Unmarshal([]byte(yml), &rootNode) + var rootNode yaml.Node + yaml.Unmarshal([]byte(yml), &rootNode) - index := NewSpecIndexWithConfig(&rootNode, CreateOpenAPIIndexConfig()) + index := NewSpecIndexWithConfig(&rootNode, CreateOpenAPIIndexConfig()) - objects := index.GetAllObjectsWithProperties() - assert.Len(t, objects, 3) + objects := index.GetAllObjectsWithProperties() + assert.Len(t, objects, 3) } // Example of how to load in an OpenAPI Specification and index it. func ExampleNewSpecIndex() { - // define a rootNode to hold our raw spec AST. - var rootNode yaml.Node + // define a rootNode to hold our raw spec AST. + var rootNode yaml.Node - // load in the stripe OpenAPI specification into bytes (it's pretty meaty) - stripeSpec, _ := os.ReadFile("../test_specs/stripe.yaml") + // load in the stripe OpenAPI specification into bytes (it's pretty meaty) + stripeSpec, _ := os.ReadFile("../test_specs/stripe.yaml") - // unmarshal spec into our rootNode - _ = yaml.Unmarshal(stripeSpec, &rootNode) + // unmarshal spec into our rootNode + _ = yaml.Unmarshal(stripeSpec, &rootNode) - // create a new specification index. - index := NewSpecIndexWithConfig(&rootNode, CreateOpenAPIIndexConfig()) + // create a new specification index. + index := NewSpecIndexWithConfig(&rootNode, CreateOpenAPIIndexConfig()) - // print out some statistics - fmt.Printf("There are %d references\n"+ - "%d paths\n"+ - "%d operations\n"+ - "%d component schemas\n"+ - "%d reference schemas\n"+ - "%d inline schemas\n"+ - "%d inline schemas that are objects or arrays\n"+ - "%d total schemas\n"+ - "%d enums\n"+ - "%d polymorphic references", - len(index.GetAllCombinedReferences()), - len(index.GetAllPaths()), - index.GetOperationCount(), - len(index.GetAllComponentSchemas()), - len(index.GetAllReferenceSchemas()), - len(index.GetAllInlineSchemas()), - len(index.GetAllInlineSchemaObjects()), - len(index.GetAllSchemas()), - len(index.GetAllEnums()), - len(index.GetPolyOneOfReferences())+len(index.GetPolyAnyOfReferences())) - // Output: There are 537 references - // 246 paths - // 402 operations - // 537 component schemas - // 1972 reference schemas - // 11749 inline schemas - // 2612 inline schemas that are objects or arrays - // 14258 total schemas - // 1516 enums - // 828 polymorphic references + // print out some statistics + fmt.Printf("There are %d references\n"+ + "%d paths\n"+ + "%d operations\n"+ + "%d component schemas\n"+ + "%d reference schemas\n"+ + "%d inline schemas\n"+ + "%d inline schemas that are objects or arrays\n"+ + "%d total schemas\n"+ + "%d enums\n"+ + "%d polymorphic references", + len(index.GetAllCombinedReferences()), + len(index.GetAllPaths()), + index.GetOperationCount(), + len(index.GetAllComponentSchemas()), + len(index.GetAllReferenceSchemas()), + len(index.GetAllInlineSchemas()), + len(index.GetAllInlineSchemaObjects()), + len(index.GetAllSchemas()), + len(index.GetAllEnums()), + len(index.GetPolyOneOfReferences())+len(index.GetPolyAnyOfReferences())) + // Output: There are 537 references + // 246 paths + // 402 operations + // 537 component schemas + // 1972 reference schemas + // 11749 inline schemas + // 2612 inline schemas that are objects or arrays + // 14258 total schemas + // 1516 enums + // 828 polymorphic references } func TestSpecIndex_GetAllPathsHavePathAndParent(t *testing.T) { - yml := `openapi: 3.1.0 + yml := `openapi: 3.1.0 info: title: Test version: 0.0.1 @@ -1197,19 +1263,19 @@ paths: "200": description: OK` - var rootNode yaml.Node - _ = yaml.Unmarshal([]byte(yml), &rootNode) + var rootNode yaml.Node + _ = yaml.Unmarshal([]byte(yml), &rootNode) - idx := NewSpecIndexWithConfig(&rootNode, CreateOpenAPIIndexConfig()) + idx := NewSpecIndexWithConfig(&rootNode, CreateOpenAPIIndexConfig()) - paths := idx.GetAllPaths() + paths := idx.GetAllPaths() - assert.Equal(t, "$.paths./test.get", paths["/test"]["get"].Path) - assert.Equal(t, 9, paths["/test"]["get"].ParentNode.Line) - assert.Equal(t, "$.paths./test.post", paths["/test"]["post"].Path) - assert.Equal(t, 13, paths["/test"]["post"].ParentNode.Line) - assert.Equal(t, "$.paths./test2.delete", paths["/test2"]["delete"].Path) - assert.Equal(t, 18, paths["/test2"]["delete"].ParentNode.Line) - assert.Equal(t, "$.paths./test2.put", paths["/test2"]["put"].Path) - assert.Equal(t, 22, paths["/test2"]["put"].ParentNode.Line) + assert.Equal(t, "$.paths./test.get", paths["/test"]["get"].Path) + assert.Equal(t, 9, paths["/test"]["get"].ParentNode.Line) + assert.Equal(t, "$.paths./test.post", paths["/test"]["post"].Path) + assert.Equal(t, 13, paths["/test"]["post"].ParentNode.Line) + assert.Equal(t, "$.paths./test2.delete", paths["/test2"]["delete"].Path) + assert.Equal(t, 18, paths["/test2"]["delete"].ParentNode.Line) + assert.Equal(t, "$.paths./test2.put", paths["/test2"]["put"].Path) + assert.Equal(t, 22, paths["/test2"]["put"].ParentNode.Line) } diff --git a/index/utility_methods.go b/index/utility_methods.go index 133c304..1c74c44 100644 --- a/index/utility_methods.go +++ b/index/utility_methods.go @@ -22,7 +22,10 @@ func (index *SpecIndex) extractDefinitionsAndSchemas(schemasNode *yaml.Node, pat } def := fmt.Sprintf("%s%s", pathPrefix, name) + fullDef := fmt.Sprintf("%s%s", index.specAbsolutePath, def) + ref := &Reference{ + FullDefinition: fullDef, Definition: def, Name: name, Node: schema, @@ -278,6 +281,13 @@ func (index *SpecIndex) scanOperationParams(params []*yaml.Node, pathItemNode *y paramRefName := param.Content[1].Value paramRef := index.allMappedRefs[paramRefName] + if paramRef == nil { + // could be in the rolodex + ref := index.SearchIndexForReference(paramRefName) + if ref != nil { + paramRef = ref + } + } if index.paramOpRefs[pathItemNode.Value] == nil { index.paramOpRefs[pathItemNode.Value] = make(map[string]map[string][]*Reference)