Files
libopenapi/index/find_component.go
quobix ca74e83a82 cleaned up some dead code!
Signed-off-by: quobix <dave@quobix.com>
2024-02-09 10:10:15 -05:00

196 lines
5.4 KiB
Go

// Copyright 2023 Princess B33f Heavy Industries / Dave Shanley
// SPDX-License-Identifier: MIT
package index
import (
"fmt"
"net/url"
"path/filepath"
"strings"
"github.com/pb33f/libopenapi/utils"
"github.com/vmware-labs/yaml-jsonpath/pkg/yamlpath"
"gopkg.in/yaml.v3"
)
// FindComponent will locate a component by its reference, returns nil if nothing is found.
// This method will recurse through remote, local and file references. For each new external reference
// a new index will be created. These indexes can then be traversed recursively.
func (index *SpecIndex) FindComponent(componentId string) *Reference {
if index.root == nil {
return nil
}
uri := strings.Split(componentId, "#/")
if len(uri) == 2 {
if uri[0] != "" {
if index.specAbsolutePath == uri[0] {
return index.FindComponentInRoot(fmt.Sprintf("#/%s", uri[1]))
} else {
return index.lookupRolodex(uri)
}
} else {
return index.FindComponentInRoot(fmt.Sprintf("#/%s", uri[1]))
}
} else {
// does it contain a file extension?
fileExt := filepath.Ext(componentId)
if fileExt != "" {
return index.lookupRolodex(uri)
}
// root search
return index.FindComponentInRoot(componentId)
}
}
func FindComponent(root *yaml.Node, componentId, absoluteFilePath string, index *SpecIndex) *Reference {
// check component for url encoding.
if strings.Contains(componentId, "%") {
// decode the url.
componentId, _ = url.QueryUnescape(componentId)
}
name, friendlySearch := utils.ConvertComponentIdIntoFriendlyPathSearch(componentId)
if friendlySearch == "$." {
friendlySearch = "$"
}
path, err := yamlpath.NewPath(friendlySearch)
if path == nil || err != nil || root == nil {
return nil // no component found
}
res, _ := path.Find(root)
if len(res) == 1 {
resNode := res[0]
fullDef := fmt.Sprintf("%s%s", absoluteFilePath, componentId)
// extract properties
// check if we have already seen this reference and there is a parent, use it
var parentNode *yaml.Node
if index.allRefs[componentId] != nil {
parentNode = index.allRefs[componentId].ParentNode
}
if index.allRefs[fullDef] != nil {
parentNode = index.allRefs[fullDef].ParentNode
}
ref := &Reference{
FullDefinition: fullDef,
Definition: componentId,
Name: name,
Node: resNode,
Path: friendlySearch,
RemoteLocation: absoluteFilePath,
ParentNode: parentNode,
Index: index,
RequiredRefProperties: extractDefinitionRequiredRefProperties(resNode, map[string][]string{}, fullDef, index),
}
return ref
}
return nil
}
func (index *SpecIndex) FindComponentInRoot(componentId string) *Reference {
if index.root != nil {
return FindComponent(index.root, componentId, index.specAbsolutePath, index)
}
return nil
}
func (index *SpecIndex) lookupRolodex(uri []string) *Reference {
if index.rolodex == nil {
return nil
}
if len(uri) > 0 {
// split string to remove file reference
file := strings.ReplaceAll(uri[0], "file:", "")
var absoluteFileLocation, fileName string
fileName = filepath.Base(file)
absoluteFileLocation = file
// if the absolute file location has no file ext, then get the rolodex root.
ext := filepath.Ext(absoluteFileLocation)
var parsedDocument *yaml.Node
var err error
idx := index
if ext != "" {
// extract the document from the rolodex.
rFile, rError := index.rolodex.Open(absoluteFileLocation)
if rError != nil {
index.logger.Error("unable to open the rolodex file, check specification references and base path",
"file", absoluteFileLocation, "error", rError)
return nil
}
if rFile == nil {
index.logger.Error("cannot locate file in the rolodex, check specification references and base path",
"file", absoluteFileLocation)
return nil
}
if rFile.GetIndex() != nil {
idx = rFile.GetIndex()
}
parsedDocument, err = rFile.GetContentAsYAMLNode()
if err != nil {
index.logger.Error("unable to parse rolodex file", "file", absoluteFileLocation, "error", err)
return nil
}
} else {
parsedDocument = index.root
}
wholeFile := false
query := ""
if len(uri) < 2 {
wholeFile = true
} else {
query = fmt.Sprintf("#/%s", uri[1])
}
// check if there is a component we want to suck in, or if the
// entire root needs to come in.
var foundRef *Reference
if wholeFile {
if parsedDocument.Kind == yaml.DocumentNode {
parsedDocument = parsedDocument.Content[0]
}
var parentNode *yaml.Node
if index.allRefs[absoluteFileLocation] != nil {
parentNode = index.allRefs[absoluteFileLocation].ParentNode
}
foundRef = &Reference{
ParentNode: parentNode,
FullDefinition: absoluteFileLocation,
Definition: fileName,
Name: fileName,
Index: idx,
Node: parsedDocument,
IsRemote: true,
RemoteLocation: absoluteFileLocation,
Path: "$",
RequiredRefProperties: extractDefinitionRequiredRefProperties(parsedDocument, map[string][]string{}, absoluteFileLocation, index),
}
return foundRef
} else {
foundRef = FindComponent(parsedDocument, query, absoluteFileLocation, index)
if foundRef != nil {
foundRef.IsRemote = true
foundRef.RemoteLocation = absoluteFileLocation
return foundRef
}
}
}
return nil
}