diff --git a/index/rolodex_remote_loader.go b/index/rolodex_remote_loader.go index 1a091bf..c419a3b 100644 --- a/index/rolodex_remote_loader.go +++ b/index/rolodex_remote_loader.go @@ -4,17 +4,16 @@ package index import ( + "context" "errors" "fmt" "github.com/pb33f/libopenapi/datamodel" "github.com/pb33f/libopenapi/utils" - "log/slog" - "runtime" - "golang.org/x/sync/syncmap" "gopkg.in/yaml.v3" "io" "io/fs" + "log/slog" "net/http" "net/url" "os" @@ -261,21 +260,26 @@ func (i *RemoteFS) Open(remoteURL string) (fs.File, error) { // if we're processing, we need to block and wait for the file to be processed // try path first if _, ok := i.ProcessingFiles.Load(remoteParsedURL.Path); ok { - // we can't block if we only have a couple of CPUs, as we'll deadlock / run super slow, only when we're running in parallel - // can we block threads. - if runtime.GOMAXPROCS(-1) > 2 { - i.logger.Debug("waiting for existing fetch to complete", "file", remoteURL, "remoteURL", remoteParsedURL.String()) - f := make(chan *RemoteFile) - fwait := func(path string, c chan *RemoteFile) { - for { - if wf, ko := i.Files.Load(remoteParsedURL.Path); ko { - c <- wf.(*RemoteFile) - } + i.logger.Debug("waiting for existing fetch to complete", "file", remoteURL, "remoteURL", remoteParsedURL.String()) + // Create a context with a timeout of 50ms + ctxTimeout, cancel := context.WithTimeout(context.Background(), time.Millisecond*50) + defer cancel() + f := make(chan *RemoteFile) + fwait := func(path string, c chan *RemoteFile) { + for { + if wf, ko := i.Files.Load(remoteParsedURL.Path); ko { + c <- wf.(*RemoteFile) } } - go fwait(remoteParsedURL.Path, f) - return <-f, nil + } + go fwait(remoteParsedURL.Path, f) + + select { + case <-ctxTimeout.Done(): + i.logger.Info("waiting for remote file timed out, trying again", "file", remoteURL, "remoteURL", remoteParsedURL.String()) + case v := <-f: + return v, nil } } diff --git a/index/spec_index.go b/index/spec_index.go index 6bda617..8a0b0d5 100644 --- a/index/spec_index.go +++ b/index/spec_index.go @@ -13,49 +13,46 @@ package index import ( - "context" "fmt" + "github.com/pb33f/libopenapi/utils" + "github.com/vmware-labs/yaml-jsonpath/pkg/yamlpath" "golang.org/x/sync/syncmap" + "gopkg.in/yaml.v3" "log/slog" "os" "sort" "strings" "sync" - "time" - - "github.com/pb33f/libopenapi/utils" - "github.com/vmware-labs/yaml-jsonpath/pkg/yamlpath" - "gopkg.in/yaml.v3" ) // NewSpecIndexWithConfig will create a new index of an OpenAPI or Swagger spec. It uses the same logic as NewSpecIndex // except it sets a base URL for resolving relative references, except it also allows for granular control over // how the index is set up. func NewSpecIndexWithConfig(rootNode *yaml.Node, config *SpecIndexConfig) *SpecIndex { - index := new(SpecIndex) - //if config != nil && config.seenRemoteSources == nil { - // config.seenRemoteSources = &syncmap.Map{} - //} - //config.remoteLock = &sync.Mutex{} - index.config = config - index.rolodex = config.Rolodex - //index.parentIndex = config.ParentIndex - index.uri = config.uri - index.specAbsolutePath = config.SpecAbsolutePath - if rootNode == nil || len(rootNode.Content) <= 0 { - return index - } + index := new(SpecIndex) + //if config != nil && config.seenRemoteSources == nil { + // config.seenRemoteSources = &syncmap.Map{} + //} + //config.remoteLock = &sync.Mutex{} + index.config = config + index.rolodex = config.Rolodex + //index.parentIndex = config.ParentIndex + index.uri = config.uri + index.specAbsolutePath = config.SpecAbsolutePath + if rootNode == nil || len(rootNode.Content) <= 0 { + return index + } - if config.Logger != nil { - index.logger = config.Logger - } else { - index.logger = slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{ - Level: slog.LevelError, - })) - } + if config.Logger != nil { + index.logger = config.Logger + } else { + index.logger = slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{ + Level: slog.LevelError, + })) + } - boostrapIndexCollections(rootNode, index) - return createNewIndex(rootNode, index, config.AvoidBuildIndex) + boostrapIndexCollections(rootNode, index) + return createNewIndex(rootNode, index, config.AvoidBuildIndex) } // NewSpecIndex will create a new index of an OpenAPI or Swagger spec. It's not resolved or converted into anything @@ -65,169 +62,169 @@ func NewSpecIndexWithConfig(rootNode *yaml.Node, config *SpecIndexConfig) *SpecI // This creates a new index using a default 'open' configuration. This means if a BaseURL or BasePath are supplied // the rolodex will automatically read those files or open those h func NewSpecIndex(rootNode *yaml.Node) *SpecIndex { - index := new(SpecIndex) - index.config = CreateOpenAPIIndexConfig() - boostrapIndexCollections(rootNode, index) - return createNewIndex(rootNode, index, false) + index := new(SpecIndex) + index.config = CreateOpenAPIIndexConfig() + boostrapIndexCollections(rootNode, index) + return createNewIndex(rootNode, index, false) } func createNewIndex(rootNode *yaml.Node, index *SpecIndex, avoidBuildOut bool) *SpecIndex { - // there is no node! return an empty index. - if rootNode == nil { - return index - } + // there is no node! return an empty index. + if rootNode == nil { + return index + } - index.cache = new(syncmap.Map) + index.cache = new(syncmap.Map) - // boot index. - results := index.ExtractRefs(index.root.Content[0], index.root, []string{}, 0, false, "") + // boot index. + results := index.ExtractRefs(index.root.Content[0], index.root, []string{}, 0, false, "") - // map poly refs - poly := make([]*Reference, len(index.polymorphicRefs)) - z := 0 - for i := range index.polymorphicRefs { - poly[z] = index.polymorphicRefs[i] - z++ - } + // map poly refs + poly := make([]*Reference, len(index.polymorphicRefs)) + z := 0 + for i := range index.polymorphicRefs { + poly[z] = index.polymorphicRefs[i] + z++ + } - // pull out references - index.ExtractComponentsFromRefs(results) - index.ExtractComponentsFromRefs(poly) + // pull out references + index.ExtractComponentsFromRefs(results) + index.ExtractComponentsFromRefs(poly) - index.ExtractExternalDocuments(index.root) - index.GetPathCount() + index.ExtractExternalDocuments(index.root) + index.GetPathCount() - // build out the index. - if !avoidBuildOut { - index.BuildIndex() - } + // build out the index. + if !avoidBuildOut { + index.BuildIndex() + } - // do a copy! - //index.config.seenRemoteSources.Range(func(k, v any) bool { - // index.seenRemoteSources[k.(string)] = v.(*yaml.Node) - // return true - //}) - return index + // do a copy! + //index.config.seenRemoteSources.Range(func(k, v any) bool { + // index.seenRemoteSources[k.(string)] = v.(*yaml.Node) + // return true + //}) + return index } // BuildIndex will run all of the count operations required to build up maps of everything. It's what makes the index // useful for looking up things, the count operations are all run in parallel and then the final calculations are run // the index is ready. func (index *SpecIndex) BuildIndex() { - if index.built { - return - } - countFuncs := []func() int{ - index.GetOperationCount, - index.GetComponentSchemaCount, - index.GetGlobalTagsCount, - index.GetComponentParameterCount, - index.GetOperationsParameterCount, - } + if index.built { + return + } + countFuncs := []func() int{ + index.GetOperationCount, + index.GetComponentSchemaCount, + index.GetGlobalTagsCount, + index.GetComponentParameterCount, + index.GetOperationsParameterCount, + } - var wg sync.WaitGroup - wg.Add(len(countFuncs)) - runIndexFunction(countFuncs, &wg) // run as fast as we can. - wg.Wait() + var wg sync.WaitGroup + wg.Add(len(countFuncs)) + runIndexFunction(countFuncs, &wg) // run as fast as we can. + wg.Wait() - // these functions are aggregate and can only run once the rest of the datamodel is ready - countFuncs = []func() int{ - index.GetInlineUniqueParamCount, - index.GetOperationTagsCount, - index.GetGlobalLinksCount, - index.GetGlobalCallbacksCount, - } + // these functions are aggregate and can only run once the rest of the datamodel is ready + countFuncs = []func() int{ + index.GetInlineUniqueParamCount, + index.GetOperationTagsCount, + index.GetGlobalLinksCount, + index.GetGlobalCallbacksCount, + } - wg.Add(len(countFuncs)) - runIndexFunction(countFuncs, &wg) // run as fast as we can. - wg.Wait() + wg.Add(len(countFuncs)) + runIndexFunction(countFuncs, &wg) // run as fast as we can. + wg.Wait() - // these have final calculation dependencies - index.GetInlineDuplicateParamCount() - index.GetAllDescriptionsCount() - index.GetTotalTagsCount() - index.built = true + // these have final calculation dependencies + index.GetInlineDuplicateParamCount() + index.GetAllDescriptionsCount() + index.GetTotalTagsCount() + index.built = true } func (index *SpecIndex) GetSpecAbsolutePath() string { - return index.specAbsolutePath + return index.specAbsolutePath } func (index *SpecIndex) GetLogger() *slog.Logger { - return index.logger + return index.logger } // GetRootNode returns document root node. func (index *SpecIndex) GetRootNode() *yaml.Node { - return index.root + return index.root } // GetGlobalTagsNode returns document root tags node. func (index *SpecIndex) GetGlobalTagsNode() *yaml.Node { - return index.tagsNode + return index.tagsNode } // SetCircularReferences is a convenience method for the resolver to pass in circular references // if the resolver is used. func (index *SpecIndex) SetCircularReferences(refs []*CircularReferenceResult) { - index.circularReferences = refs + index.circularReferences = refs } // GetCircularReferences will return any circular reference results that were found by the resolver. func (index *SpecIndex) GetCircularReferences() []*CircularReferenceResult { - return index.circularReferences + return index.circularReferences } // GetPathsNode returns document root node. func (index *SpecIndex) GetPathsNode() *yaml.Node { - return index.pathsNode + return index.pathsNode } // GetDiscoveredReferences will return all unique references found in the spec func (index *SpecIndex) GetDiscoveredReferences() map[string]*Reference { - return index.allRefs + return index.allRefs } // GetPolyReferences will return every polymorphic reference in the doc func (index *SpecIndex) GetPolyReferences() map[string]*Reference { - return index.polymorphicRefs + return index.polymorphicRefs } // GetPolyAllOfReferences will return every 'allOf' polymorphic reference in the doc func (index *SpecIndex) GetPolyAllOfReferences() []*Reference { - return index.polymorphicAllOfRefs + return index.polymorphicAllOfRefs } // GetPolyAnyOfReferences will return every 'anyOf' polymorphic reference in the doc func (index *SpecIndex) GetPolyAnyOfReferences() []*Reference { - return index.polymorphicAnyOfRefs + return index.polymorphicAnyOfRefs } // GetPolyOneOfReferences will return every 'allOf' polymorphic reference in the doc func (index *SpecIndex) GetPolyOneOfReferences() []*Reference { - return index.polymorphicOneOfRefs + return index.polymorphicOneOfRefs } // GetAllCombinedReferences will return the number of unique and polymorphic references discovered. func (index *SpecIndex) GetAllCombinedReferences() map[string]*Reference { - combined := make(map[string]*Reference) - for k, ref := range index.allRefs { - combined[k] = ref - } - for k, ref := range index.polymorphicRefs { - combined[k] = ref - } - return combined + combined := make(map[string]*Reference) + for k, ref := range index.allRefs { + combined[k] = ref + } + for k, ref := range index.polymorphicRefs { + combined[k] = ref + } + return combined } // GetRefsByLine will return all references and the lines at which they were found. func (index *SpecIndex) GetRefsByLine() map[string]map[int]bool { - return index.refsByLine + return index.refsByLine } // GetLinesWithReferences will return a map of lines that have a $ref func (index *SpecIndex) GetLinesWithReferences() map[int]bool { - return index.linesWithRefs + return index.linesWithRefs } // GetMappedReferences will return all references that were mapped successfully to actual property nodes. @@ -235,18 +232,18 @@ func (index *SpecIndex) GetLinesWithReferences() map[int]bool { // encountering circular references can change results depending on where in the collection the resolver started // its journey through the index. func (index *SpecIndex) GetMappedReferences() map[string]*Reference { - return index.allMappedRefs + return index.allMappedRefs } // GetMappedReferencesSequenced will return all references that were mapped successfully to nodes, performed in sequence // as they were read in from the document. func (index *SpecIndex) GetMappedReferencesSequenced() []*ReferenceMapped { - return index.allMappedRefsSequenced + return index.allMappedRefsSequenced } // GetOperationParameterReferences will return all references to operation parameters func (index *SpecIndex) GetOperationParameterReferences() map[string]map[string]map[string][]*Reference { - return index.paramOpRefs + return index.paramOpRefs } // GetAllSchemas will return references to all schemas found in the document both inline and those under components @@ -255,198 +252,198 @@ func (index *SpecIndex) GetOperationParameterReferences() map[string]map[string] // finally all the references that are not inline, but marked as $ref in the document are returned (using GetAllReferenceSchemas). // the results are sorted by line number. func (index *SpecIndex) GetAllSchemas() []*Reference { - componentSchemas := index.GetAllComponentSchemas() - inlineSchemas := index.GetAllInlineSchemas() - refSchemas := index.GetAllReferenceSchemas() - combined := make([]*Reference, len(inlineSchemas)+len(componentSchemas)+len(refSchemas)) - i := 0 - for x := range inlineSchemas { - combined[i] = inlineSchemas[x] - i++ - } - for x := range componentSchemas { - combined[i] = componentSchemas[x] - i++ - } - for x := range refSchemas { - combined[i] = refSchemas[x] - i++ - } - sort.Slice(combined, func(i, j int) bool { - return combined[i].Node.Line < combined[j].Node.Line - }) - return combined + componentSchemas := index.GetAllComponentSchemas() + inlineSchemas := index.GetAllInlineSchemas() + refSchemas := index.GetAllReferenceSchemas() + combined := make([]*Reference, len(inlineSchemas)+len(componentSchemas)+len(refSchemas)) + i := 0 + for x := range inlineSchemas { + combined[i] = inlineSchemas[x] + i++ + } + for x := range componentSchemas { + combined[i] = componentSchemas[x] + i++ + } + for x := range refSchemas { + combined[i] = refSchemas[x] + i++ + } + sort.Slice(combined, func(i, j int) bool { + return combined[i].Node.Line < combined[j].Node.Line + }) + return combined } // GetAllInlineSchemaObjects will return all schemas that are inline (not inside components) and that are also typed // as 'object' or 'array' (not primitives). func (index *SpecIndex) GetAllInlineSchemaObjects() []*Reference { - return index.allInlineSchemaObjectDefinitions + return index.allInlineSchemaObjectDefinitions } // GetAllInlineSchemas will return all schemas defined in the components section of the document. func (index *SpecIndex) GetAllInlineSchemas() []*Reference { - return index.allInlineSchemaDefinitions + return index.allInlineSchemaDefinitions } // GetAllReferenceSchemas will return all schemas that are not inline, but $ref'd from somewhere. func (index *SpecIndex) GetAllReferenceSchemas() []*Reference { - return index.allRefSchemaDefinitions + return index.allRefSchemaDefinitions } // GetAllComponentSchemas will return all schemas defined in the components section of the document. func (index *SpecIndex) GetAllComponentSchemas() map[string]*Reference { - return index.allComponentSchemaDefinitions + return index.allComponentSchemaDefinitions } // GetAllSecuritySchemes will return all security schemes / definitions found in the document. func (index *SpecIndex) GetAllSecuritySchemes() map[string]*Reference { - return index.allSecuritySchemes + return index.allSecuritySchemes } // GetAllHeaders will return all headers found in the document (under components) func (index *SpecIndex) GetAllHeaders() map[string]*Reference { - return index.allHeaders + return index.allHeaders } // GetAllExternalDocuments will return all external documents found func (index *SpecIndex) GetAllExternalDocuments() map[string]*Reference { - return index.allExternalDocuments + return index.allExternalDocuments } // GetAllExamples will return all examples found in the document (under components) func (index *SpecIndex) GetAllExamples() map[string]*Reference { - return index.allExamples + return index.allExamples } // GetAllDescriptions will return all descriptions found in the document func (index *SpecIndex) GetAllDescriptions() []*DescriptionReference { - return index.allDescriptions + return index.allDescriptions } // GetAllEnums will return all enums found in the document func (index *SpecIndex) GetAllEnums() []*EnumReference { - return index.allEnums + return index.allEnums } // GetAllObjectsWithProperties will return all objects with properties found in the document func (index *SpecIndex) GetAllObjectsWithProperties() []*ObjectReference { - return index.allObjectsWithProperties + return index.allObjectsWithProperties } // GetAllSummaries will return all summaries found in the document func (index *SpecIndex) GetAllSummaries() []*DescriptionReference { - return index.allSummaries + return index.allSummaries } // GetAllRequestBodies will return all requestBodies found in the document (under components) func (index *SpecIndex) GetAllRequestBodies() map[string]*Reference { - return index.allRequestBodies + return index.allRequestBodies } // GetAllLinks will return all links found in the document (under components) func (index *SpecIndex) GetAllLinks() map[string]*Reference { - return index.allLinks + return index.allLinks } // GetAllParameters will return all parameters found in the document (under components) func (index *SpecIndex) GetAllParameters() map[string]*Reference { - return index.allParameters + return index.allParameters } // GetAllResponses will return all responses found in the document (under components) func (index *SpecIndex) GetAllResponses() map[string]*Reference { - return index.allResponses + return index.allResponses } // GetAllCallbacks will return all links found in the document (under components) func (index *SpecIndex) GetAllCallbacks() map[string]*Reference { - return index.allCallbacks + return index.allCallbacks } // GetInlineOperationDuplicateParameters will return a map of duplicates located in operation parameters. func (index *SpecIndex) GetInlineOperationDuplicateParameters() map[string][]*Reference { - return index.paramInlineDuplicateNames + return index.paramInlineDuplicateNames } // GetReferencesWithSiblings will return a map of all the references with sibling nodes (illegal) func (index *SpecIndex) GetReferencesWithSiblings() map[string]Reference { - return index.refsWithSiblings + return index.refsWithSiblings } // GetAllReferences will return every reference found in the spec, after being de-duplicated. func (index *SpecIndex) GetAllReferences() map[string]*Reference { - return index.allRefs + return index.allRefs } // GetAllSequencedReferences will return every reference (in sequence) that was found (non-polymorphic) func (index *SpecIndex) GetAllSequencedReferences() []*Reference { - return index.rawSequencedRefs + return index.rawSequencedRefs } // GetSchemasNode will return the schema's node found in the spec func (index *SpecIndex) GetSchemasNode() *yaml.Node { - return index.schemasNode + return index.schemasNode } // GetParametersNode will return the schema's node found in the spec func (index *SpecIndex) GetParametersNode() *yaml.Node { - return index.parametersNode + return index.parametersNode } // GetReferenceIndexErrors will return any errors that occurred when indexing references func (index *SpecIndex) GetReferenceIndexErrors() []error { - return index.refErrors + return index.refErrors } // GetOperationParametersIndexErrors any errors that occurred when indexing operation parameters func (index *SpecIndex) GetOperationParametersIndexErrors() []error { - return index.operationParamErrors + return index.operationParamErrors } // GetAllPaths will return all paths indexed in the document func (index *SpecIndex) GetAllPaths() map[string]map[string]*Reference { - return index.pathRefs + return index.pathRefs } // GetOperationTags will return all references to all tags found in operations. func (index *SpecIndex) GetOperationTags() map[string]map[string][]*Reference { - return index.operationTagsRefs + return index.operationTagsRefs } // GetAllParametersFromOperations will return all paths indexed in the document func (index *SpecIndex) GetAllParametersFromOperations() map[string]map[string]map[string][]*Reference { - return index.paramOpRefs + return index.paramOpRefs } // GetRootSecurityReferences will return all root security settings func (index *SpecIndex) GetRootSecurityReferences() []*Reference { - return index.rootSecurity + return index.rootSecurity } // GetSecurityRequirementReferences will return all security requirement definitions found in the document func (index *SpecIndex) GetSecurityRequirementReferences() map[string]map[string][]*Reference { - return index.securityRequirementRefs + return index.securityRequirementRefs } // GetRootSecurityNode will return the root security node func (index *SpecIndex) GetRootSecurityNode() *yaml.Node { - return index.rootSecurityNode + return index.rootSecurityNode } // GetRootServersNode will return the root servers node func (index *SpecIndex) GetRootServersNode() *yaml.Node { - return index.rootServersNode + return index.rootServersNode } // GetAllRootServers will return all root servers defined func (index *SpecIndex) GetAllRootServers() []*Reference { - return index.serversRefs + return index.serversRefs } // GetAllOperationsServers will return all operation overrides for servers. func (index *SpecIndex) GetAllOperationsServers() map[string]map[string][]*Reference { - return index.opServersRefs + return index.opServersRefs } //// GetAllExternalIndexes will return all indexes for external documents @@ -457,785 +454,765 @@ func (index *SpecIndex) GetAllOperationsServers() map[string]map[string][]*Refer // SetAllowCircularReferenceResolving will flip a bit that can be used by any consumers to determine if they want // to allow or disallow circular references to be resolved or visited func (index *SpecIndex) SetAllowCircularReferenceResolving(allow bool) { - index.allowCircularReferences = allow + index.allowCircularReferences = allow } // AllowCircularReferenceResolving will return a bit that allows developers to determine what to do with circular refs. func (index *SpecIndex) AllowCircularReferenceResolving() bool { - return index.allowCircularReferences + return index.allowCircularReferences } func (index *SpecIndex) checkPolymorphicNode(name string) (bool, string) { - switch name { - case "anyOf": - return true, "anyOf" - case "allOf": - return true, "allOf" - case "oneOf": - return true, "oneOf" - } - return false, "" + switch name { + case "anyOf": + return true, "anyOf" + case "allOf": + return true, "allOf" + case "oneOf": + return true, "oneOf" + } + return false, "" } // GetPathCount will return the number of paths found in the spec func (index *SpecIndex) GetPathCount() int { - if index.root == nil { - return -1 - } + if index.root == nil { + return -1 + } - if index.pathCount > 0 { - return index.pathCount - } - pc := 0 - for i, n := range index.root.Content[0].Content { - if i%2 == 0 { - if n.Value == "paths" { - pn := index.root.Content[0].Content[i+1].Content - index.pathsNode = index.root.Content[0].Content[i+1] - pc = len(pn) / 2 - } - } - } - index.pathCount = pc - return pc + if index.pathCount > 0 { + return index.pathCount + } + pc := 0 + for i, n := range index.root.Content[0].Content { + if i%2 == 0 { + if n.Value == "paths" { + pn := index.root.Content[0].Content[i+1].Content + index.pathsNode = index.root.Content[0].Content[i+1] + pc = len(pn) / 2 + } + } + } + index.pathCount = pc + return pc } // ExtractExternalDocuments will extract the number of externalDocs nodes found in the document. func (index *SpecIndex) ExtractExternalDocuments(node *yaml.Node) []*Reference { - if node == nil { - return nil - } - var found []*Reference - if len(node.Content) > 0 { - for i, n := range node.Content { - if utils.IsNodeMap(n) || utils.IsNodeArray(n) { - found = append(found, index.ExtractExternalDocuments(n)...) - } + if node == nil { + return nil + } + var found []*Reference + if len(node.Content) > 0 { + for i, n := range node.Content { + if utils.IsNodeMap(n) || utils.IsNodeArray(n) { + found = append(found, index.ExtractExternalDocuments(n)...) + } - if i%2 == 0 && n.Value == "externalDocs" { - docNode := node.Content[i+1] - _, urlNode := utils.FindKeyNode("url", docNode.Content) - if urlNode != nil { - ref := &Reference{ - Definition: urlNode.Value, - Name: urlNode.Value, - Node: docNode, - } - index.externalDocumentsRef = append(index.externalDocumentsRef, ref) - } - } - } - } - index.externalDocumentsCount = len(index.externalDocumentsRef) - return found + if i%2 == 0 && n.Value == "externalDocs" { + docNode := node.Content[i+1] + _, urlNode := utils.FindKeyNode("url", docNode.Content) + if urlNode != nil { + ref := &Reference{ + Definition: urlNode.Value, + Name: urlNode.Value, + Node: docNode, + } + index.externalDocumentsRef = append(index.externalDocumentsRef, ref) + } + } + } + } + index.externalDocumentsCount = len(index.externalDocumentsRef) + return found } // GetGlobalTagsCount will return the number of tags found in the top level 'tags' node of the document. func (index *SpecIndex) GetGlobalTagsCount() int { - if index.root == nil { - return -1 - } + if index.root == nil { + return -1 + } - if index.globalTagsCount > 0 { - return index.globalTagsCount - } + if index.globalTagsCount > 0 { + return index.globalTagsCount + } - for i, n := range index.root.Content[0].Content { - if i%2 == 0 { - if n.Value == "tags" { - tagsNode := index.root.Content[0].Content[i+1] - if tagsNode != nil { - index.tagsNode = tagsNode - index.globalTagsCount = len(tagsNode.Content) // tags is an array, don't divide by 2. - for x, tagNode := range index.tagsNode.Content { + for i, n := range index.root.Content[0].Content { + if i%2 == 0 { + if n.Value == "tags" { + tagsNode := index.root.Content[0].Content[i+1] + if tagsNode != nil { + index.tagsNode = tagsNode + index.globalTagsCount = len(tagsNode.Content) // tags is an array, don't divide by 2. + for x, tagNode := range index.tagsNode.Content { - _, name := utils.FindKeyNode("name", tagNode.Content) - _, description := utils.FindKeyNode("description", tagNode.Content) + _, name := utils.FindKeyNode("name", tagNode.Content) + _, description := utils.FindKeyNode("description", tagNode.Content) - var desc string - if description == nil { - desc = "" - } - if name != nil { - ref := &Reference{ - Definition: desc, - Name: name.Value, - Node: tagNode, - Path: fmt.Sprintf("$.tags[%d]", x), - } - index.globalTagRefs[name.Value] = ref - } - } - } - } - } - } - return index.globalTagsCount + var desc string + if description == nil { + desc = "" + } + if name != nil { + ref := &Reference{ + Definition: desc, + Name: name.Value, + Node: tagNode, + Path: fmt.Sprintf("$.tags[%d]", x), + } + index.globalTagRefs[name.Value] = ref + } + } + } + } + } + } + return index.globalTagsCount } // GetOperationTagsCount will return the number of operation tags found (tags referenced in operations) func (index *SpecIndex) GetOperationTagsCount() int { - if index.root == nil { - return -1 - } + if index.root == nil { + return -1 + } - if index.operationTagsCount > 0 { - return index.operationTagsCount - } + if index.operationTagsCount > 0 { + return index.operationTagsCount + } - // this is an aggregate count function that can only be run after operations - // have been calculated. - seen := make(map[string]bool) - count := 0 - for _, path := range index.operationTagsRefs { - for _, method := range path { - for _, tag := range method { - if !seen[tag.Name] { - seen[tag.Name] = true - count++ - } - } - } - } - index.operationTagsCount = count - return index.operationTagsCount + // this is an aggregate count function that can only be run after operations + // have been calculated. + seen := make(map[string]bool) + count := 0 + for _, path := range index.operationTagsRefs { + for _, method := range path { + for _, tag := range method { + if !seen[tag.Name] { + seen[tag.Name] = true + count++ + } + } + } + } + index.operationTagsCount = count + return index.operationTagsCount } // GetTotalTagsCount will return the number of global and operation tags found that are unique. func (index *SpecIndex) GetTotalTagsCount() int { - if index.root == nil { - return -1 - } - if index.totalTagsCount > 0 { - return index.totalTagsCount - } + if index.root == nil { + return -1 + } + if index.totalTagsCount > 0 { + return index.totalTagsCount + } - seen := make(map[string]bool) - count := 0 + seen := make(map[string]bool) + count := 0 - for _, gt := range index.globalTagRefs { - // TODO: do we still need this? - if !seen[gt.Name] { - seen[gt.Name] = true - count++ - } - } - for _, ot := range index.operationTagsRefs { - for _, m := range ot { - for _, t := range m { - if !seen[t.Name] { - seen[t.Name] = true - count++ - } - } - } - } - index.totalTagsCount = count - return index.totalTagsCount + for _, gt := range index.globalTagRefs { + // TODO: do we still need this? + if !seen[gt.Name] { + seen[gt.Name] = true + count++ + } + } + for _, ot := range index.operationTagsRefs { + for _, m := range ot { + for _, t := range m { + if !seen[t.Name] { + seen[t.Name] = true + count++ + } + } + } + } + index.totalTagsCount = count + return index.totalTagsCount } // GetGlobalCallbacksCount for each response of each operation method, multiple callbacks can be defined func (index *SpecIndex) GetGlobalCallbacksCount() int { - if index.root == nil { - return -1 - } + if index.root == nil { + return -1 + } - if index.globalCallbacksCount > 0 { - return index.globalCallbacksCount - } + if index.globalCallbacksCount > 0 { + return index.globalCallbacksCount + } - index.pathRefsLock.RLock() - for path, p := range index.pathRefs { - for _, m := range p { + index.pathRefsLock.RLock() + for path, p := range index.pathRefs { + for _, m := range p { - // look through method for callbacks - callbacks, _ := yamlpath.NewPath("$..callbacks") - var res []*yaml.Node - res, _ = callbacks.Find(m.Node) - if len(res) > 0 { - for _, callback := range res[0].Content { - if utils.IsNodeMap(callback) { + // look through method for callbacks + callbacks, _ := yamlpath.NewPath("$..callbacks") + var res []*yaml.Node + res, _ = callbacks.Find(m.Node) + if len(res) > 0 { + for _, callback := range res[0].Content { + if utils.IsNodeMap(callback) { - ref := &Reference{ - Definition: m.Name, - Name: m.Name, - Node: callback, - } + ref := &Reference{ + Definition: m.Name, + Name: m.Name, + Node: callback, + } - if index.callbacksRefs[path] == nil { - index.callbacksRefs[path] = make(map[string][]*Reference) - } - if len(index.callbacksRefs[path][m.Name]) > 0 { - index.callbacksRefs[path][m.Name] = append(index.callbacksRefs[path][m.Name], ref) - } else { - index.callbacksRefs[path][m.Name] = []*Reference{ref} - } - index.globalCallbacksCount++ - } - } - } - } - } - index.pathRefsLock.RUnlock() - return index.globalCallbacksCount + if index.callbacksRefs[path] == nil { + index.callbacksRefs[path] = make(map[string][]*Reference) + } + if len(index.callbacksRefs[path][m.Name]) > 0 { + index.callbacksRefs[path][m.Name] = append(index.callbacksRefs[path][m.Name], ref) + } else { + index.callbacksRefs[path][m.Name] = []*Reference{ref} + } + index.globalCallbacksCount++ + } + } + } + } + } + index.pathRefsLock.RUnlock() + return index.globalCallbacksCount } // GetGlobalLinksCount for each response of each operation method, multiple callbacks can be defined func (index *SpecIndex) GetGlobalLinksCount() int { - if index.root == nil { - return -1 - } + if index.root == nil { + return -1 + } - if index.globalLinksCount > 0 { - return index.globalLinksCount - } + if index.globalLinksCount > 0 { + return index.globalLinksCount + } - // index.pathRefsLock.Lock() - for path, p := range index.pathRefs { - for _, m := range p { + // index.pathRefsLock.Lock() + for path, p := range index.pathRefs { + for _, m := range p { - // look through method for links - links, _ := yamlpath.NewPath("$..links") + // look through method for links + links, _ := yamlpath.NewPath("$..links") + var res []*yaml.Node - // Channel used to receive the result from doSomething function - ch := make(chan string, 1) + res, _ = links.Find(m.Node) - // Create a context with a timeout of 5 seconds - ctxTimeout, cancel := context.WithTimeout(context.Background(), time.Millisecond*500) - defer cancel() + if len(res) > 0 { + for _, link := range res[0].Content { + if utils.IsNodeMap(link) { - var res []*yaml.Node - - doSomething := func(ctx context.Context, ch chan<- string) { - res, _ = links.Find(m.Node) - ch <- m.Definition - } - - // Start the doSomething function - go doSomething(ctxTimeout, ch) - - select { - case <-ctxTimeout.Done(): - fmt.Printf("Global links %d ref: Context cancelled: %v\n", m.Node.Line, ctxTimeout.Err()) - case <-ch: - } - - if len(res) > 0 { - for _, link := range res[0].Content { - if utils.IsNodeMap(link) { - - ref := &Reference{ - Definition: m.Name, - Name: m.Name, - Node: link, - } - if index.linksRefs[path] == nil { - index.linksRefs[path] = make(map[string][]*Reference) - } - if len(index.linksRefs[path][m.Name]) > 0 { - index.linksRefs[path][m.Name] = append(index.linksRefs[path][m.Name], ref) - } - index.linksRefs[path][m.Name] = []*Reference{ref} - index.globalLinksCount++ - } - } - } - } - } - // index.pathRefsLock.Unlock() - return index.globalLinksCount + ref := &Reference{ + Definition: m.Name, + Name: m.Name, + Node: link, + } + if index.linksRefs[path] == nil { + index.linksRefs[path] = make(map[string][]*Reference) + } + if len(index.linksRefs[path][m.Name]) > 0 { + index.linksRefs[path][m.Name] = append(index.linksRefs[path][m.Name], ref) + } + index.linksRefs[path][m.Name] = []*Reference{ref} + index.globalLinksCount++ + } + } + } + } + } + // index.pathRefsLock.Unlock() + return index.globalLinksCount } // GetRawReferenceCount will return the number of raw references located in the document. func (index *SpecIndex) GetRawReferenceCount() int { - return len(index.rawSequencedRefs) + return len(index.rawSequencedRefs) } // GetComponentSchemaCount will return the number of schemas located in the 'components' or 'definitions' node. func (index *SpecIndex) GetComponentSchemaCount() int { - if index.root == nil { - return -1 - } + if index.root == nil { + return -1 + } - if index.schemaCount > 0 { - return index.schemaCount - } + if index.schemaCount > 0 { + return index.schemaCount + } - for i, n := range index.root.Content[0].Content { - if i%2 == 0 { + for i, n := range index.root.Content[0].Content { + if i%2 == 0 { - // servers - if n.Value == "servers" { - index.rootServersNode = index.root.Content[0].Content[i+1] - if i+1 < len(index.root.Content[0].Content) { - serverDefinitions := index.root.Content[0].Content[i+1] - for x, def := range serverDefinitions.Content { - ref := &Reference{ - Definition: "servers", - Name: "server", - Node: def, - Path: fmt.Sprintf("$.servers[%d]", x), - ParentNode: index.rootServersNode, - } - index.serversRefs = append(index.serversRefs, ref) - } - } - } + // servers + if n.Value == "servers" { + index.rootServersNode = index.root.Content[0].Content[i+1] + if i+1 < len(index.root.Content[0].Content) { + serverDefinitions := index.root.Content[0].Content[i+1] + for x, def := range serverDefinitions.Content { + ref := &Reference{ + Definition: "servers", + Name: "server", + Node: def, + Path: fmt.Sprintf("$.servers[%d]", x), + ParentNode: index.rootServersNode, + } + index.serversRefs = append(index.serversRefs, ref) + } + } + } - // root security definitions - if n.Value == "security" { - index.rootSecurityNode = index.root.Content[0].Content[i+1] - if i+1 < len(index.root.Content[0].Content) { - securityDefinitions := index.root.Content[0].Content[i+1] - for x, def := range securityDefinitions.Content { - if len(def.Content) > 0 { - name := def.Content[0] - ref := &Reference{ - Definition: name.Value, - Name: name.Value, - Node: def, - Path: fmt.Sprintf("$.security[%d]", x), - } - index.rootSecurity = append(index.rootSecurity, ref) - } - } - } - } + // root security definitions + if n.Value == "security" { + index.rootSecurityNode = index.root.Content[0].Content[i+1] + if i+1 < len(index.root.Content[0].Content) { + securityDefinitions := index.root.Content[0].Content[i+1] + for x, def := range securityDefinitions.Content { + if len(def.Content) > 0 { + name := def.Content[0] + ref := &Reference{ + Definition: name.Value, + Name: name.Value, + Node: def, + Path: fmt.Sprintf("$.security[%d]", x), + } + index.rootSecurity = append(index.rootSecurity, ref) + } + } + } + } - if n.Value == "components" { - _, schemasNode := utils.FindKeyNode("schemas", index.root.Content[0].Content[i+1].Content) + if n.Value == "components" { + _, schemasNode := utils.FindKeyNode("schemas", index.root.Content[0].Content[i+1].Content) - // while we are here, go ahead and extract everything in components. - _, parametersNode := utils.FindKeyNode("parameters", index.root.Content[0].Content[i+1].Content) - _, requestBodiesNode := utils.FindKeyNode("requestBodies", index.root.Content[0].Content[i+1].Content) - _, responsesNode := utils.FindKeyNode("responses", index.root.Content[0].Content[i+1].Content) - _, securitySchemesNode := utils.FindKeyNode("securitySchemes", index.root.Content[0].Content[i+1].Content) - _, headersNode := utils.FindKeyNode("headers", index.root.Content[0].Content[i+1].Content) - _, examplesNode := utils.FindKeyNode("examples", index.root.Content[0].Content[i+1].Content) - _, linksNode := utils.FindKeyNode("links", index.root.Content[0].Content[i+1].Content) - _, callbacksNode := utils.FindKeyNode("callbacks", index.root.Content[0].Content[i+1].Content) + // while we are here, go ahead and extract everything in components. + _, parametersNode := utils.FindKeyNode("parameters", index.root.Content[0].Content[i+1].Content) + _, requestBodiesNode := utils.FindKeyNode("requestBodies", index.root.Content[0].Content[i+1].Content) + _, responsesNode := utils.FindKeyNode("responses", index.root.Content[0].Content[i+1].Content) + _, securitySchemesNode := utils.FindKeyNode("securitySchemes", index.root.Content[0].Content[i+1].Content) + _, headersNode := utils.FindKeyNode("headers", index.root.Content[0].Content[i+1].Content) + _, examplesNode := utils.FindKeyNode("examples", index.root.Content[0].Content[i+1].Content) + _, linksNode := utils.FindKeyNode("links", index.root.Content[0].Content[i+1].Content) + _, callbacksNode := utils.FindKeyNode("callbacks", index.root.Content[0].Content[i+1].Content) - // extract schemas - if schemasNode != nil { - index.extractDefinitionsAndSchemas(schemasNode, "#/components/schemas/") - index.schemasNode = schemasNode - index.schemaCount = len(schemasNode.Content) / 2 - } + // extract schemas + if schemasNode != nil { + index.extractDefinitionsAndSchemas(schemasNode, "#/components/schemas/") + index.schemasNode = schemasNode + index.schemaCount = len(schemasNode.Content) / 2 + } - // extract parameters - if parametersNode != nil { - index.extractComponentParameters(parametersNode, "#/components/parameters/") - index.componentLock.Lock() - index.parametersNode = parametersNode - index.componentLock.Unlock() - } + // extract parameters + if parametersNode != nil { + index.extractComponentParameters(parametersNode, "#/components/parameters/") + index.componentLock.Lock() + index.parametersNode = parametersNode + index.componentLock.Unlock() + } - // extract requestBodies - if requestBodiesNode != nil { - index.extractComponentRequestBodies(requestBodiesNode, "#/components/requestBodies/") - index.requestBodiesNode = requestBodiesNode - } + // extract requestBodies + if requestBodiesNode != nil { + index.extractComponentRequestBodies(requestBodiesNode, "#/components/requestBodies/") + index.requestBodiesNode = requestBodiesNode + } - // extract responses - if responsesNode != nil { - index.extractComponentResponses(responsesNode, "#/components/responses/") - index.responsesNode = responsesNode - } + // extract responses + if responsesNode != nil { + index.extractComponentResponses(responsesNode, "#/components/responses/") + index.responsesNode = responsesNode + } - // extract security schemes - if securitySchemesNode != nil { - index.extractComponentSecuritySchemes(securitySchemesNode, "#/components/securitySchemes/") - index.securitySchemesNode = securitySchemesNode - } + // extract security schemes + if securitySchemesNode != nil { + index.extractComponentSecuritySchemes(securitySchemesNode, "#/components/securitySchemes/") + index.securitySchemesNode = securitySchemesNode + } - // extract headers - if headersNode != nil { - index.extractComponentHeaders(headersNode, "#/components/headers/") - index.headersNode = headersNode - } + // extract headers + if headersNode != nil { + index.extractComponentHeaders(headersNode, "#/components/headers/") + index.headersNode = headersNode + } - // extract examples - if examplesNode != nil { - index.extractComponentExamples(examplesNode, "#/components/examples/") - index.examplesNode = examplesNode - } + // extract examples + if examplesNode != nil { + index.extractComponentExamples(examplesNode, "#/components/examples/") + index.examplesNode = examplesNode + } - // extract links - if linksNode != nil { - index.extractComponentLinks(linksNode, "#/components/links/") - index.linksNode = linksNode - } + // extract links + if linksNode != nil { + index.extractComponentLinks(linksNode, "#/components/links/") + index.linksNode = linksNode + } - // extract callbacks - if callbacksNode != nil { - index.extractComponentCallbacks(callbacksNode, "#/components/callbacks/") - index.callbacksNode = callbacksNode - } + // extract callbacks + if callbacksNode != nil { + index.extractComponentCallbacks(callbacksNode, "#/components/callbacks/") + index.callbacksNode = callbacksNode + } - } + } - // swagger - if n.Value == "definitions" { - schemasNode := index.root.Content[0].Content[i+1] - if schemasNode != nil { + // swagger + if n.Value == "definitions" { + schemasNode := index.root.Content[0].Content[i+1] + if schemasNode != nil { - // extract schemas - index.extractDefinitionsAndSchemas(schemasNode, "#/definitions/") - index.schemasNode = schemasNode - index.schemaCount = len(schemasNode.Content) / 2 - } - } + // extract schemas + index.extractDefinitionsAndSchemas(schemasNode, "#/definitions/") + index.schemasNode = schemasNode + index.schemaCount = len(schemasNode.Content) / 2 + } + } - // swagger - if n.Value == "parameters" { - parametersNode := index.root.Content[0].Content[i+1] - if parametersNode != nil { - // extract params - index.extractComponentParameters(parametersNode, "#/parameters/") - index.componentLock.Lock() - index.parametersNode = parametersNode - index.componentLock.Unlock() - } - } + // swagger + if n.Value == "parameters" { + parametersNode := index.root.Content[0].Content[i+1] + if parametersNode != nil { + // extract params + index.extractComponentParameters(parametersNode, "#/parameters/") + index.componentLock.Lock() + index.parametersNode = parametersNode + index.componentLock.Unlock() + } + } - if n.Value == "responses" { - responsesNode := index.root.Content[0].Content[i+1] - if responsesNode != nil { + if n.Value == "responses" { + responsesNode := index.root.Content[0].Content[i+1] + if responsesNode != nil { - // extract responses - index.extractComponentResponses(responsesNode, "#/responses/") - index.responsesNode = responsesNode - } - } + // extract responses + index.extractComponentResponses(responsesNode, "#/responses/") + index.responsesNode = responsesNode + } + } - if n.Value == "securityDefinitions" { - securityDefinitionsNode := index.root.Content[0].Content[i+1] - if securityDefinitionsNode != nil { + if n.Value == "securityDefinitions" { + securityDefinitionsNode := index.root.Content[0].Content[i+1] + if securityDefinitionsNode != nil { - // extract security definitions. - index.extractComponentSecuritySchemes(securityDefinitionsNode, "#/securityDefinitions/") - index.securitySchemesNode = securityDefinitionsNode - } - } + // extract security definitions. + index.extractComponentSecuritySchemes(securityDefinitionsNode, "#/securityDefinitions/") + index.securitySchemesNode = securityDefinitionsNode + } + } - } - } - return index.schemaCount + } + } + return index.schemaCount } // GetComponentParameterCount returns the number of parameter components defined func (index *SpecIndex) GetComponentParameterCount() int { - if index.root == nil { - return -1 - } + if index.root == nil { + return -1 + } - if index.componentParamCount > 0 { - return index.componentParamCount - } + if index.componentParamCount > 0 { + return index.componentParamCount + } - for i, n := range index.root.Content[0].Content { - if i%2 == 0 { - // openapi 3 - if n.Value == "components" { - _, parametersNode := utils.FindKeyNode("parameters", index.root.Content[0].Content[i+1].Content) - if parametersNode != nil { - index.componentLock.Lock() - index.parametersNode = parametersNode - index.componentParamCount = len(parametersNode.Content) / 2 - index.componentLock.Unlock() - } - } - // openapi 2 - if n.Value == "parameters" { - parametersNode := index.root.Content[0].Content[i+1] - if parametersNode != nil { - index.componentLock.Lock() - index.parametersNode = parametersNode - index.componentParamCount = len(parametersNode.Content) / 2 - index.componentLock.Unlock() - } - } - } - } - return index.componentParamCount + for i, n := range index.root.Content[0].Content { + if i%2 == 0 { + // openapi 3 + if n.Value == "components" { + _, parametersNode := utils.FindKeyNode("parameters", index.root.Content[0].Content[i+1].Content) + if parametersNode != nil { + index.componentLock.Lock() + index.parametersNode = parametersNode + index.componentParamCount = len(parametersNode.Content) / 2 + index.componentLock.Unlock() + } + } + // openapi 2 + if n.Value == "parameters" { + parametersNode := index.root.Content[0].Content[i+1] + if parametersNode != nil { + index.componentLock.Lock() + index.parametersNode = parametersNode + index.componentParamCount = len(parametersNode.Content) / 2 + index.componentLock.Unlock() + } + } + } + } + return index.componentParamCount } // GetOperationCount returns the number of operations (for all paths) located in the document func (index *SpecIndex) GetOperationCount() int { - if index.root == nil { - return -1 - } + if index.root == nil { + return -1 + } - if index.pathsNode == nil { - return -1 - } + if index.pathsNode == nil { + return -1 + } - if index.operationCount > 0 { - return index.operationCount - } + if index.operationCount > 0 { + return index.operationCount + } - opCount := 0 + opCount := 0 - locatedPathRefs := make(map[string]map[string]*Reference) + locatedPathRefs := make(map[string]map[string]*Reference) - for x, p := range index.pathsNode.Content { - if x%2 == 0 { + for x, p := range index.pathsNode.Content { + if x%2 == 0 { - var method *yaml.Node - if utils.IsNodeArray(index.pathsNode) { - method = index.pathsNode.Content[x] - } else { - method = index.pathsNode.Content[x+1] - } + var method *yaml.Node + if utils.IsNodeArray(index.pathsNode) { + method = index.pathsNode.Content[x] + } else { + method = index.pathsNode.Content[x+1] + } - // extract methods for later use. - for y, m := range method.Content { - if y%2 == 0 { + // extract methods for later use. + for y, m := range method.Content { + if y%2 == 0 { - // check node is a valid method - valid := false - for _, methodType := range methodTypes { - if m.Value == methodType { - valid = true - } - } - if valid { - ref := &Reference{ - Definition: m.Value, - Name: m.Value, - Node: method.Content[y+1], - Path: fmt.Sprintf("$.paths.%s.%s", p.Value, m.Value), - ParentNode: m, - } - //index.pathRefsLock.Lock() - if locatedPathRefs[p.Value] == nil { - locatedPathRefs[p.Value] = make(map[string]*Reference) - } - locatedPathRefs[p.Value][ref.Name] = ref - //index.pathRefsLock.Unlock() - // update - opCount++ - } - } - } - } - } - index.pathRefsLock.Lock() - for k, v := range locatedPathRefs { - index.pathRefs[k] = v - } - index.pathRefsLock.Unlock() - index.operationCount = opCount - return opCount + // check node is a valid method + valid := false + for _, methodType := range methodTypes { + if m.Value == methodType { + valid = true + } + } + if valid { + ref := &Reference{ + Definition: m.Value, + Name: m.Value, + Node: method.Content[y+1], + Path: fmt.Sprintf("$.paths.%s.%s", p.Value, m.Value), + ParentNode: m, + } + //index.pathRefsLock.Lock() + if locatedPathRefs[p.Value] == nil { + locatedPathRefs[p.Value] = make(map[string]*Reference) + } + locatedPathRefs[p.Value][ref.Name] = ref + //index.pathRefsLock.Unlock() + // update + opCount++ + } + } + } + } + } + index.pathRefsLock.Lock() + for k, v := range locatedPathRefs { + index.pathRefs[k] = v + } + index.pathRefsLock.Unlock() + index.operationCount = opCount + return opCount } // GetOperationsParameterCount returns the number of parameters defined in paths and operations. // this method looks in top level (path level) and inside each operation (get, post etc.). Parameters can // be hiding within multiple places. func (index *SpecIndex) GetOperationsParameterCount() int { - if index.root == nil { - return -1 - } + if index.root == nil { + return -1 + } - if index.pathsNode == nil { - return -1 - } + if index.pathsNode == nil { + return -1 + } - if index.operationParamCount > 0 { - return index.operationParamCount - } + if index.operationParamCount > 0 { + return index.operationParamCount + } - // parameters are sneaky, they can be in paths, in path operations or in components. - // sometimes they are refs, sometimes they are inline definitions, just for fun. - // some authors just LOVE to mix and match them all up. - // check paths first - for x, pathItemNode := range index.pathsNode.Content { - if x%2 == 0 { + // parameters are sneaky, they can be in paths, in path operations or in components. + // sometimes they are refs, sometimes they are inline definitions, just for fun. + // some authors just LOVE to mix and match them all up. + // check paths first + for x, pathItemNode := range index.pathsNode.Content { + if x%2 == 0 { - var pathPropertyNode *yaml.Node - if utils.IsNodeArray(index.pathsNode) { - pathPropertyNode = index.pathsNode.Content[x] - } else { - pathPropertyNode = index.pathsNode.Content[x+1] - } + var pathPropertyNode *yaml.Node + if utils.IsNodeArray(index.pathsNode) { + pathPropertyNode = index.pathsNode.Content[x] + } else { + pathPropertyNode = index.pathsNode.Content[x+1] + } - // extract methods for later use. - for y, prop := range pathPropertyNode.Content { - if y%2 == 0 { + // extract methods for later use. + for y, prop := range pathPropertyNode.Content { + if y%2 == 0 { - // while we're here, lets extract any top level servers - if prop.Value == "servers" { - serversNode := pathPropertyNode.Content[y+1] - if index.opServersRefs[pathItemNode.Value] == nil { - index.opServersRefs[pathItemNode.Value] = make(map[string][]*Reference) - } - var serverRefs []*Reference - for i, serverRef := range serversNode.Content { - ref := &Reference{ - Definition: serverRef.Value, - Name: serverRef.Value, - Node: serverRef, - ParentNode: prop, - Path: fmt.Sprintf("$.paths.%s.servers[%d]", pathItemNode.Value, i), - } - serverRefs = append(serverRefs, ref) - } - index.opServersRefs[pathItemNode.Value]["top"] = serverRefs - } + // while we're here, lets extract any top level servers + if prop.Value == "servers" { + serversNode := pathPropertyNode.Content[y+1] + if index.opServersRefs[pathItemNode.Value] == nil { + index.opServersRefs[pathItemNode.Value] = make(map[string][]*Reference) + } + var serverRefs []*Reference + for i, serverRef := range serversNode.Content { + ref := &Reference{ + Definition: serverRef.Value, + Name: serverRef.Value, + Node: serverRef, + ParentNode: prop, + Path: fmt.Sprintf("$.paths.%s.servers[%d]", pathItemNode.Value, i), + } + serverRefs = append(serverRefs, ref) + } + index.opServersRefs[pathItemNode.Value]["top"] = serverRefs + } - // top level params - if prop.Value == "parameters" { + // top level params + if prop.Value == "parameters" { - // let's look at params, check if they are refs or inline. - params := pathPropertyNode.Content[y+1].Content - index.scanOperationParams(params, pathItemNode, "top") - } + // let's look at params, check if they are refs or inline. + params := pathPropertyNode.Content[y+1].Content + index.scanOperationParams(params, pathItemNode, "top") + } - // method level params. - if isHttpMethod(prop.Value) { - for z, httpMethodProp := range pathPropertyNode.Content[y+1].Content { - if z%2 == 0 { - if httpMethodProp.Value == "parameters" { - params := pathPropertyNode.Content[y+1].Content[z+1].Content - index.scanOperationParams(params, pathItemNode, prop.Value) - } + // method level params. + if isHttpMethod(prop.Value) { + for z, httpMethodProp := range pathPropertyNode.Content[y+1].Content { + if z%2 == 0 { + if httpMethodProp.Value == "parameters" { + params := pathPropertyNode.Content[y+1].Content[z+1].Content + index.scanOperationParams(params, pathItemNode, prop.Value) + } - // extract operation tags if set. - if httpMethodProp.Value == "tags" { - tags := pathPropertyNode.Content[y+1].Content[z+1] + // extract operation tags if set. + if httpMethodProp.Value == "tags" { + tags := pathPropertyNode.Content[y+1].Content[z+1] - if index.operationTagsRefs[pathItemNode.Value] == nil { - index.operationTagsRefs[pathItemNode.Value] = make(map[string][]*Reference) - } + if index.operationTagsRefs[pathItemNode.Value] == nil { + index.operationTagsRefs[pathItemNode.Value] = make(map[string][]*Reference) + } - var tagRefs []*Reference - for _, tagRef := range tags.Content { - ref := &Reference{ - Definition: tagRef.Value, - Name: tagRef.Value, - Node: tagRef, - } - tagRefs = append(tagRefs, ref) - } - index.operationTagsRefs[pathItemNode.Value][prop.Value] = tagRefs - } + var tagRefs []*Reference + for _, tagRef := range tags.Content { + ref := &Reference{ + Definition: tagRef.Value, + Name: tagRef.Value, + Node: tagRef, + } + tagRefs = append(tagRefs, ref) + } + index.operationTagsRefs[pathItemNode.Value][prop.Value] = tagRefs + } - // extract description and summaries - if httpMethodProp.Value == "description" { - desc := pathPropertyNode.Content[y+1].Content[z+1].Value - ref := &Reference{ - Definition: desc, - Name: "description", - Node: pathPropertyNode.Content[y+1].Content[z+1], - } - if index.operationDescriptionRefs[pathItemNode.Value] == nil { - index.operationDescriptionRefs[pathItemNode.Value] = make(map[string]*Reference) - } + // extract description and summaries + if httpMethodProp.Value == "description" { + desc := pathPropertyNode.Content[y+1].Content[z+1].Value + ref := &Reference{ + Definition: desc, + Name: "description", + Node: pathPropertyNode.Content[y+1].Content[z+1], + } + if index.operationDescriptionRefs[pathItemNode.Value] == nil { + index.operationDescriptionRefs[pathItemNode.Value] = make(map[string]*Reference) + } - index.operationDescriptionRefs[pathItemNode.Value][prop.Value] = ref - } - if httpMethodProp.Value == "summary" { - summary := pathPropertyNode.Content[y+1].Content[z+1].Value - ref := &Reference{ - Definition: summary, - Name: "summary", - Node: pathPropertyNode.Content[y+1].Content[z+1], - } + index.operationDescriptionRefs[pathItemNode.Value][prop.Value] = ref + } + if httpMethodProp.Value == "summary" { + summary := pathPropertyNode.Content[y+1].Content[z+1].Value + ref := &Reference{ + Definition: summary, + Name: "summary", + Node: pathPropertyNode.Content[y+1].Content[z+1], + } - if index.operationSummaryRefs[pathItemNode.Value] == nil { - index.operationSummaryRefs[pathItemNode.Value] = make(map[string]*Reference) - } + if index.operationSummaryRefs[pathItemNode.Value] == nil { + index.operationSummaryRefs[pathItemNode.Value] = make(map[string]*Reference) + } - index.operationSummaryRefs[pathItemNode.Value][prop.Value] = ref - } + index.operationSummaryRefs[pathItemNode.Value][prop.Value] = ref + } - // extract servers from method operation. - if httpMethodProp.Value == "servers" { - serversNode := pathPropertyNode.Content[y+1].Content[z+1] + // extract servers from method operation. + if httpMethodProp.Value == "servers" { + serversNode := pathPropertyNode.Content[y+1].Content[z+1] - var serverRefs []*Reference - for i, serverRef := range serversNode.Content { - ref := &Reference{ - Definition: "servers", - Name: "servers", - Node: serverRef, - ParentNode: httpMethodProp, - Path: fmt.Sprintf("$.paths.%s.%s.servers[%d]", pathItemNode.Value, prop.Value, i), - } - serverRefs = append(serverRefs, ref) - } + var serverRefs []*Reference + for i, serverRef := range serversNode.Content { + ref := &Reference{ + Definition: "servers", + Name: "servers", + Node: serverRef, + ParentNode: httpMethodProp, + Path: fmt.Sprintf("$.paths.%s.%s.servers[%d]", pathItemNode.Value, prop.Value, i), + } + serverRefs = append(serverRefs, ref) + } - if index.opServersRefs[pathItemNode.Value] == nil { - index.opServersRefs[pathItemNode.Value] = make(map[string][]*Reference) - } + if index.opServersRefs[pathItemNode.Value] == nil { + index.opServersRefs[pathItemNode.Value] = make(map[string][]*Reference) + } - index.opServersRefs[pathItemNode.Value][prop.Value] = serverRefs - } + index.opServersRefs[pathItemNode.Value][prop.Value] = serverRefs + } - } - } - } - } - } - } - } + } + } + } + } + } + } + } - // Now that all the paths and operations are processed, lets pick out everything from our pre - // mapped refs and populate our ready to roll index of component params. - for key, component := range index.allMappedRefs { - if strings.Contains(key, "/parameters/") { - index.paramCompRefs[key] = component - index.paramAllRefs[key] = component - } - } + // Now that all the paths and operations are processed, lets pick out everything from our pre + // mapped refs and populate our ready to roll index of component params. + for key, component := range index.allMappedRefs { + if strings.Contains(key, "/parameters/") { + index.paramCompRefs[key] = component + index.paramAllRefs[key] = component + } + } - // now build main index of all params by combining comp refs with inline params from operations. - // use the namespace path:::param for inline params to identify them as inline. - for path, params := range index.paramOpRefs { - for mName, mValue := range params { - for pName, pValue := range mValue { - if !strings.HasPrefix(pName, "#") { - index.paramInlineDuplicateNames[pName] = append(index.paramInlineDuplicateNames[pName], pValue...) - for i := range pValue { - if pValue[i] != nil { - _, in := utils.FindKeyNodeTop("in", pValue[i].Node.Content) - if in != nil { - index.paramAllRefs[fmt.Sprintf("%s:::%s:::%s", path, mName, in.Value)] = pValue[i] - } else { - index.paramAllRefs[fmt.Sprintf("%s:::%s", path, mName)] = pValue[i] - } - } - } - } - } - } - } + // now build main index of all params by combining comp refs with inline params from operations. + // use the namespace path:::param for inline params to identify them as inline. + for path, params := range index.paramOpRefs { + for mName, mValue := range params { + for pName, pValue := range mValue { + if !strings.HasPrefix(pName, "#") { + index.paramInlineDuplicateNames[pName] = append(index.paramInlineDuplicateNames[pName], pValue...) + for i := range pValue { + if pValue[i] != nil { + _, in := utils.FindKeyNodeTop("in", pValue[i].Node.Content) + if in != nil { + index.paramAllRefs[fmt.Sprintf("%s:::%s:::%s", path, mName, in.Value)] = pValue[i] + } else { + index.paramAllRefs[fmt.Sprintf("%s:::%s", path, mName)] = pValue[i] + } + } + } + } + } + } + } - index.operationParamCount = len(index.paramCompRefs) + len(index.paramInlineDuplicateNames) - return index.operationParamCount + index.operationParamCount = len(index.paramCompRefs) + len(index.paramInlineDuplicateNames) + return index.operationParamCount } // GetInlineDuplicateParamCount returns the number of inline duplicate parameters (operation params) func (index *SpecIndex) GetInlineDuplicateParamCount() int { - if index.componentsInlineParamDuplicateCount > 0 { - return index.componentsInlineParamDuplicateCount - } - dCount := len(index.paramInlineDuplicateNames) - index.countUniqueInlineDuplicates() - index.componentsInlineParamDuplicateCount = dCount - return dCount + if index.componentsInlineParamDuplicateCount > 0 { + return index.componentsInlineParamDuplicateCount + } + dCount := len(index.paramInlineDuplicateNames) - index.countUniqueInlineDuplicates() + index.componentsInlineParamDuplicateCount = dCount + return dCount } // GetInlineUniqueParamCount returns the number of unique inline parameters (operation params) func (index *SpecIndex) GetInlineUniqueParamCount() int { - return index.countUniqueInlineDuplicates() + return index.countUniqueInlineDuplicates() } // GetAllDescriptionsCount will collect together every single description found in the document func (index *SpecIndex) GetAllDescriptionsCount() int { - return len(index.allDescriptions) + return len(index.allDescriptions) } // GetAllSummariesCount will collect together every single summary found in the document func (index *SpecIndex) GetAllSummariesCount() int { - return len(index.allSummaries) + return len(index.allSummaries) } diff --git a/index/spec_index_test.go b/index/spec_index_test.go index d0ff277..5e7f2a6 100644 --- a/index/spec_index_test.go +++ b/index/spec_index_test.go @@ -142,7 +142,7 @@ func TestSpecIndex_DigitalOcean(t *testing.T) { cf.AllowRemoteLookup = true cf.AvoidCircularReferenceCheck = true cf.Logger = slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{ - Level: slog.LevelError, + Level: slog.LevelInfo, })) // setting this baseURL will override the base