mirror of
https://github.com/LukeHagar/libopenapi.git
synced 2025-12-06 04:20:11 +00:00
196 lines
5.4 KiB
Go
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
|
|
}
|