whacking the shit out of exploded use-cases

Before everything worked, but was completely accurate, now everything works and everything is absolute and can be resolved. Phew, what a mission!

Signed-off-by: quobix <dave@quobix.com>
This commit is contained in:
quobix
2023-10-21 14:14:49 -04:00
parent afe89454ac
commit bf270d3d2b
9 changed files with 870 additions and 756 deletions

View File

@@ -54,6 +54,10 @@ func (index *SpecIndex) ExtractRefs(node, parent *yaml.Node, seenPath []string,
definitionPath = fmt.Sprintf("#/%s", strings.Join(loc, "/")) definitionPath = fmt.Sprintf("#/%s", strings.Join(loc, "/"))
fullDefinitionPath = fmt.Sprintf("%s#/%s", index.specAbsolutePath, strings.Join(loc, "/")) fullDefinitionPath = fmt.Sprintf("%s#/%s", index.specAbsolutePath, strings.Join(loc, "/"))
_, jsonPath = utils.ConvertComponentIdIntoFriendlyPathSearch(definitionPath) _, jsonPath = utils.ConvertComponentIdIntoFriendlyPathSearch(definitionPath)
} else {
definitionPath = fmt.Sprintf("#/%s", n.Value)
fullDefinitionPath = fmt.Sprintf("%s#/%s", index.specAbsolutePath, n.Value)
_, jsonPath = utils.ConvertComponentIdIntoFriendlyPathSearch(definitionPath)
} }
ref := &Reference{ ref := &Reference{
FullDefinition: fullDefinitionPath, FullDefinition: fullDefinitionPath,
@@ -105,7 +109,12 @@ func (index *SpecIndex) ExtractRefs(node, parent *yaml.Node, seenPath []string,
definitionPath = fmt.Sprintf("#/%s", strings.Join(loc, "/")) definitionPath = fmt.Sprintf("#/%s", strings.Join(loc, "/"))
fullDefinitionPath = fmt.Sprintf("%s#/%s", index.specAbsolutePath, strings.Join(loc, "/")) fullDefinitionPath = fmt.Sprintf("%s#/%s", index.specAbsolutePath, strings.Join(loc, "/"))
_, jsonPath = utils.ConvertComponentIdIntoFriendlyPathSearch(definitionPath) _, jsonPath = utils.ConvertComponentIdIntoFriendlyPathSearch(definitionPath)
} else {
definitionPath = fmt.Sprintf("#/%s", n.Value)
fullDefinitionPath = fmt.Sprintf("%s#/%s", index.specAbsolutePath, n.Value)
_, jsonPath = utils.ConvertComponentIdIntoFriendlyPathSearch(definitionPath)
} }
ref := &Reference{ ref := &Reference{
FullDefinition: fullDefinitionPath, FullDefinition: fullDefinitionPath,
Definition: definitionPath, Definition: definitionPath,
@@ -145,7 +154,12 @@ func (index *SpecIndex) ExtractRefs(node, parent *yaml.Node, seenPath []string,
definitionPath = fmt.Sprintf("#/%s", strings.Join(loc, "/")) definitionPath = fmt.Sprintf("#/%s", strings.Join(loc, "/"))
fullDefinitionPath = fmt.Sprintf("%s#/%s", index.specAbsolutePath, strings.Join(loc, "/")) fullDefinitionPath = fmt.Sprintf("%s#/%s", index.specAbsolutePath, strings.Join(loc, "/"))
_, jsonPath = utils.ConvertComponentIdIntoFriendlyPathSearch(definitionPath) _, jsonPath = utils.ConvertComponentIdIntoFriendlyPathSearch(definitionPath)
} else {
definitionPath = fmt.Sprintf("#/%s", n.Value)
fullDefinitionPath = fmt.Sprintf("%s#/%s", index.specAbsolutePath, n.Value)
_, jsonPath = utils.ConvertComponentIdIntoFriendlyPathSearch(definitionPath)
} }
ref := &Reference{ ref := &Reference{
FullDefinition: fullDefinitionPath, FullDefinition: fullDefinitionPath,
Definition: definitionPath, Definition: definitionPath,
@@ -407,6 +421,7 @@ func (index *SpecIndex) ExtractRefs(node, parent *yaml.Node, seenPath []string,
//} else { //} else {
// index.allRefs[value] = ref // index.allRefs[value] = ref
//} //}
index.allRefs[fullDefinitionPath] = ref index.allRefs[fullDefinitionPath] = ref
found = append(found, ref) found = append(found, ref)
} }
@@ -597,13 +612,13 @@ func (index *SpecIndex) ExtractComponentsFromRefs(refs []*Reference) []*Referenc
if located != nil { if located != nil {
index.refLock.Lock() index.refLock.Lock()
// have we already mapped this? // have we already mapped this?
if index.allMappedRefs[ref.Definition] == nil { if index.allMappedRefs[ref.FullDefinition] == nil {
found = append(found, located) found = append(found, located)
if located.FullDefinition != ref.FullDefinition { if located.FullDefinition != ref.FullDefinition {
located.FullDefinition = ref.FullDefinition located.FullDefinition = ref.FullDefinition
} }
index.allMappedRefs[ref.Definition] = located index.allMappedRefs[ref.FullDefinition] = located
rm := &ReferenceMapped{ rm := &ReferenceMapped{
Reference: located, Reference: located,
Definition: ref.Definition, Definition: ref.Definition,
@@ -612,7 +627,7 @@ func (index *SpecIndex) ExtractComponentsFromRefs(refs []*Reference) []*Referenc
sequence[refIndex] = rm sequence[refIndex] = rm
} else { } else {
// it exists, but is it a component with the same ID? // it exists, but is it a component with the same ID?
d := index.allMappedRefs[ref.Definition] d := index.allMappedRefs[ref.FullDefinition]
// if the full definition matches, we're good and can skip this. // if the full definition matches, we're good and can skip this.
if d.FullDefinition != ref.FullDefinition { if d.FullDefinition != ref.FullDefinition {

View File

@@ -4,80 +4,80 @@
package index package index
import ( import (
"fmt" "fmt"
"io" "io"
"net/http" "net/http"
"net/url" "net/url"
"path/filepath" "path/filepath"
"strings" "strings"
"time" "time"
"github.com/pb33f/libopenapi/utils" "github.com/pb33f/libopenapi/utils"
"github.com/vmware-labs/yaml-jsonpath/pkg/yamlpath" "github.com/vmware-labs/yaml-jsonpath/pkg/yamlpath"
"gopkg.in/yaml.v3" "gopkg.in/yaml.v3"
) )
// FindComponent will locate a component by its reference, returns nil if nothing is found. // 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 // 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. // a new index will be created. These indexes can then be traversed recursively.
func (index *SpecIndex) FindComponent(componentId string, parent *yaml.Node) *Reference { func (index *SpecIndex) FindComponent(componentId string, parent *yaml.Node) *Reference {
if index.root == nil { if index.root == nil {
return nil return nil
} }
//remoteLookup := func(id string) (*yaml.Node, *yaml.Node, error) { //remoteLookup := func(id string) (*yaml.Node, *yaml.Node, error) {
// if index.config.AllowRemoteLookup { // if index.config.AllowRemoteLookup {
// return index.lookupRemoteReference(id) // return index.lookupRemoteReference(id)
// } else { // } else {
// return nil, nil, fmt.Errorf("remote lookups are not permitted, " + // return nil, nil, fmt.Errorf("remote lookups are not permitted, " +
// "please set AllowRemoteLookup to true in the configuration") // "please set AllowRemoteLookup to true in the configuration")
// } // }
//} //}
// //
//fileLookup := func(id string) (*yaml.Node, *yaml.Node, error) { //fileLookup := func(id string) (*yaml.Node, *yaml.Node, error) {
// if index.config.AllowFileLookup { // if index.config.AllowFileLookup {
// return index.lookupFileReference(id) // return index.lookupFileReference(id)
// } else { // } else {
// return nil, nil, fmt.Errorf("local lookups are not permitted, " + // return nil, nil, fmt.Errorf("local lookups are not permitted, " +
// "please set AllowFileLookup to true in the configuration") // "please set AllowFileLookup to true in the configuration")
// } // }
//} //}
//witch DetermineReferenceResolveType(componentId) { //witch DetermineReferenceResolveType(componentId) {
//case LocalResolve: // ideally, every single ref in every single spec is local. however, this is not the case. //case LocalResolve: // ideally, every single ref in every single spec is local. however, this is not the case.
//return index.FindComponentInRoot(componentId) //return index.FindComponentInRoot(componentId)
//case HttpResolve, FileResolve: //case HttpResolve, FileResolve:
uri := strings.Split(componentId, "#/") uri := strings.Split(componentId, "#/")
if len(uri) == 2 { if len(uri) == 2 {
if uri[0] != "" { if uri[0] != "" {
if index.specAbsolutePath == uri[0] { if index.specAbsolutePath == uri[0] {
return index.FindComponentInRoot(fmt.Sprintf("#/%s", uri[1])) return index.FindComponentInRoot(fmt.Sprintf("#/%s", uri[1]))
} else { } else {
return index.lookupRolodex(uri) return index.lookupRolodex(uri)
} }
} else { } else {
return index.FindComponentInRoot(fmt.Sprintf("#/%s", uri[1])) return index.FindComponentInRoot(fmt.Sprintf("#/%s", uri[1]))
} }
} else { } else {
if !strings.Contains(componentId, "#") { if !strings.Contains(componentId, "#") {
// does it contain a file extension? // does it contain a file extension?
fileExt := filepath.Ext(componentId) fileExt := filepath.Ext(componentId)
if fileExt != "" { if fileExt != "" {
return index.lookupRolodex(uri) return index.lookupRolodex(uri)
} }
// root search // root search
return index.FindComponentInRoot(componentId) return index.FindComponentInRoot(componentId)
} }
return index.FindComponentInRoot(fmt.Sprintf("#/%s", uri[0])) return index.FindComponentInRoot(fmt.Sprintf("#/%s", uri[0]))
} }
//} //}
//return nil //return nil
} }
var httpClient = &http.Client{Timeout: time.Duration(60) * time.Second} var httpClient = &http.Client{Timeout: time.Duration(60) * time.Second}
@@ -85,18 +85,18 @@ var httpClient = &http.Client{Timeout: time.Duration(60) * time.Second}
type RemoteURLHandler = func(url string) (*http.Response, error) type RemoteURLHandler = func(url string) (*http.Response, error)
func getRemoteDoc(g RemoteURLHandler, u string, d chan []byte, e chan error) { func getRemoteDoc(g RemoteURLHandler, u string, d chan []byte, e chan error) {
resp, err := g(u) resp, err := g(u)
if err != nil { if err != nil {
e <- err e <- err
close(e) close(e)
close(d) close(d)
return return
} }
var body []byte var body []byte
body, _ = io.ReadAll(resp.Body) body, _ = io.ReadAll(resp.Body)
d <- body d <- body
close(e) close(e)
close(d) close(d)
} }
//func (index *SpecIndex) lookupRemoteReference(ref string) (*yaml.Node, *yaml.Node, error) { //func (index *SpecIndex) lookupRemoteReference(ref string) (*yaml.Node, *yaml.Node, error) {
@@ -315,255 +315,255 @@ func getRemoteDoc(g RemoteURLHandler, u string, d chan []byte, e chan error) {
//} //}
func FindComponent(root *yaml.Node, componentId, absoluteFilePath string) *Reference { func FindComponent(root *yaml.Node, componentId, absoluteFilePath string) *Reference {
// check component for url encoding. // check component for url encoding.
if strings.Contains(componentId, "%") { if strings.Contains(componentId, "%") {
// decode the url. // decode the url.
componentId, _ = url.QueryUnescape(componentId) componentId, _ = url.QueryUnescape(componentId)
} }
name, friendlySearch := utils.ConvertComponentIdIntoFriendlyPathSearch(componentId) name, friendlySearch := utils.ConvertComponentIdIntoFriendlyPathSearch(componentId)
path, err := yamlpath.NewPath(friendlySearch) path, err := yamlpath.NewPath(friendlySearch)
if path == nil || err != nil { if path == nil || err != nil {
return nil // no component found return nil // no component found
} }
res, _ := path.Find(root) res, _ := path.Find(root)
if len(res) == 1 { if len(res) == 1 {
resNode := res[0] resNode := res[0]
if res[0].Kind == yaml.DocumentNode { if res[0].Kind == yaml.DocumentNode {
resNode = res[0].Content[0] resNode = res[0].Content[0]
} }
fullDef := fmt.Sprintf("%s%s", absoluteFilePath, componentId) fullDef := fmt.Sprintf("%s%s", absoluteFilePath, componentId)
// extract properties // extract properties
ref := &Reference{ ref := &Reference{
FullDefinition: fullDef, FullDefinition: fullDef,
Definition: componentId, Definition: componentId,
Name: name, Name: name,
Node: resNode, Node: resNode,
Path: friendlySearch, Path: friendlySearch,
RequiredRefProperties: extractDefinitionRequiredRefProperties(resNode, map[string][]string{}, fullDef), RequiredRefProperties: extractDefinitionRequiredRefProperties(resNode, map[string][]string{}, fullDef),
} }
return ref return ref
} }
return nil return nil
} }
func (index *SpecIndex) FindComponentInRoot(componentId string) *Reference { func (index *SpecIndex) FindComponentInRoot(componentId string) *Reference {
if index.root != nil { if index.root != nil {
return FindComponent(index.root, componentId, index.specAbsolutePath) return FindComponent(index.root, componentId, index.specAbsolutePath)
} }
return nil return nil
} }
func (index *SpecIndex) lookupRolodex(uri []string) *Reference { func (index *SpecIndex) lookupRolodex(uri []string) *Reference {
if len(uri) > 0 { if len(uri) > 0 {
// split string to remove file reference // split string to remove file reference
file := strings.ReplaceAll(uri[0], "file:", "") file := strings.ReplaceAll(uri[0], "file:", "")
var absoluteFileLocation, fileName string var absoluteFileLocation, fileName string
// is this a local or a remote file? // is this a local or a remote file?
fileName = filepath.Base(file) fileName = filepath.Base(file)
if filepath.IsAbs(file) || strings.HasPrefix(file, "http") { if filepath.IsAbs(file) || strings.HasPrefix(file, "http") {
absoluteFileLocation = file absoluteFileLocation = file
} else { } else {
if index.specAbsolutePath != "" { if index.specAbsolutePath != "" {
if index.config.BaseURL != nil { if index.config.BaseURL != nil {
// consider the file remote. // consider the file remote.
//if strings.Contains(file, "../../") { //if strings.Contains(file, "../../") {
// extract the base path from the specAbsolutePath for this index. // extract the base path from the specAbsolutePath for this index.
sap, _ := url.Parse(index.specAbsolutePath) sap, _ := url.Parse(index.specAbsolutePath)
newPath, _ := filepath.Abs(filepath.Join(filepath.Dir(sap.Path), file)) newPath, _ := filepath.Abs(filepath.Join(filepath.Dir(sap.Path), file))
sap.Path = newPath sap.Path = newPath
f := sap.String() f := sap.String()
absoluteFileLocation = f absoluteFileLocation = f
//} //}
//loc := fmt.Sprintf("%s%s", index.config.BaseURL.Path, file) //loc := fmt.Sprintf("%s%s", index.config.BaseURL.Path, file)
//absoluteFileLocation = loc //absoluteFileLocation = loc
} else { } else {
// consider the file local // consider the file local
dir := filepath.Dir(index.config.SpecAbsolutePath) dir := filepath.Dir(index.config.SpecAbsolutePath)
absoluteFileLocation, _ = filepath.Abs(filepath.Join(dir, file)) absoluteFileLocation, _ = filepath.Abs(filepath.Join(dir, file))
} }
} else { } else {
absoluteFileLocation = file absoluteFileLocation = file
} }
} }
// if the absolute file location has no file ext, then get the rolodex root. // if the absolute file location has no file ext, then get the rolodex root.
ext := filepath.Ext(absoluteFileLocation) ext := filepath.Ext(absoluteFileLocation)
var parsedDocument *yaml.Node var parsedDocument *yaml.Node
var err error var err error
if ext != "" { if ext != "" {
// extract the document from the rolodex. // extract the document from the rolodex.
rFile, rError := index.rolodex.Open(absoluteFileLocation) rFile, rError := index.rolodex.Open(absoluteFileLocation)
if rError != nil { if rError != nil {
logger.Error("unable to open rolodex file", "file", absoluteFileLocation, "error", rError) logger.Error("unable to open rolodex file", "file", absoluteFileLocation, "error", rError)
return nil return nil
} }
if rFile == nil { if rFile == nil {
logger.Error("rolodex file is empty!", "file", absoluteFileLocation) logger.Error("rolodex file is empty!", "file", absoluteFileLocation)
return nil return nil
} }
parsedDocument, err = rFile.GetContentAsYAMLNode() parsedDocument, err = rFile.GetContentAsYAMLNode()
if err != nil { if err != nil {
logger.Error("unable to parse rolodex file", "file", absoluteFileLocation, "error", err) logger.Error("unable to parse rolodex file", "file", absoluteFileLocation, "error", err)
return nil return nil
} }
} else { } else {
parsedDocument = index.root parsedDocument = index.root
} }
//fmt.Printf("parsedDocument: %v\n", parsedDocument) //fmt.Printf("parsedDocument: %v\n", parsedDocument)
//index.externalLock.RLock() //index.externalLock.RLock()
//externalSpecIndex := index.externalSpecIndex[uri[0]] //externalSpecIndex := index.externalSpecIndex[uri[0]]
//index.externalLock.RUnlock() //index.externalLock.RUnlock()
//if externalSpecIndex == nil { //if externalSpecIndex == nil {
// _, newRoot, err := lookupFunction(componentId) // _, newRoot, err := lookupFunction(componentId)
// if err != nil { // if err != nil {
// indexError := &IndexingError{ // indexError := &IndexingError{
// Err: err, // Err: err,
// Node: parent, // Node: parent,
// Path: componentId, // Path: componentId,
// } // }
// index.errorLock.Lock() // index.errorLock.Lock()
// index.refErrors = append(index.refErrors, indexError) // index.refErrors = append(index.refErrors, indexError)
// index.errorLock.Unlock() // index.errorLock.Unlock()
// return nil // return nil
// } // }
// //
// // cool, cool, lets index this spec also. This is a recursive action and will keep going // // cool, cool, lets index this spec also. This is a recursive action and will keep going
// // until all remote references have been found. // // until all remote references have been found.
// var bp *url.URL // var bp *url.URL
// var bd string // var bd string
// //
// if index.config.BaseURL != nil { // if index.config.BaseURL != nil {
// bp = index.config.BaseURL // bp = index.config.BaseURL
// } // }
// if index.config.BasePath != "" { // if index.config.BasePath != "" {
// bd = index.config.BasePath // bd = index.config.BasePath
// } // }
// //
// var path, newBasePath string // var path, newBasePath string
// var newUrl *url.URL // var newUrl *url.URL
// //
// if bp != nil { // if bp != nil {
// path = GenerateCleanSpecConfigBaseURL(bp, uri[0], false) // path = GenerateCleanSpecConfigBaseURL(bp, uri[0], false)
// newUrl, _ = url.Parse(path) // newUrl, _ = url.Parse(path)
// newBasePath = filepath.Dir(filepath.Join(index.config.BasePath, filepath.Dir(newUrl.Path))) // newBasePath = filepath.Dir(filepath.Join(index.config.BasePath, filepath.Dir(newUrl.Path)))
// } // }
// if bd != "" { // if bd != "" {
// if len(uri[0]) > 0 { // if len(uri[0]) > 0 {
// // if there is no base url defined, but we can know we have been requested remotely, // // if there is no base url defined, but we can know we have been requested remotely,
// // set the base url to the remote url base path. // // set the base url to the remote url base path.
// // first check if the first param is actually a URL // // first check if the first param is actually a URL
// io, er := url.ParseRequestURI(uri[0]) // io, er := url.ParseRequestURI(uri[0])
// if er != nil { // if er != nil {
// newBasePath = filepath.Dir(filepath.Join(bd, uri[0])) // newBasePath = filepath.Dir(filepath.Join(bd, uri[0]))
// } else { // } else {
// if newUrl == nil || newUrl.String() != io.String() { // if newUrl == nil || newUrl.String() != io.String() {
// newUrl, _ = url.Parse(fmt.Sprintf("%s://%s%s", io.Scheme, io.Host, filepath.Dir(io.Path))) // newUrl, _ = url.Parse(fmt.Sprintf("%s://%s%s", io.Scheme, io.Host, filepath.Dir(io.Path)))
// } // }
// newBasePath = filepath.Dir(filepath.Join(bd, uri[1])) // newBasePath = filepath.Dir(filepath.Join(bd, uri[1]))
// } // }
// } else { // } else {
// newBasePath = filepath.Dir(filepath.Join(bd, uri[0])) // newBasePath = filepath.Dir(filepath.Join(bd, uri[0]))
// } // }
// } // }
// //
// if newUrl != nil || newBasePath != "" { // if newUrl != nil || newBasePath != "" {
// newConfig := &SpecIndexConfig{ // newConfig := &SpecIndexConfig{
// BaseURL: newUrl, // BaseURL: newUrl,
// BasePath: newBasePath, // BasePath: newBasePath,
// AllowRemoteLookup: index.config.AllowRemoteLookup, // AllowRemoteLookup: index.config.AllowRemoteLookup,
// AllowFileLookup: index.config.AllowFileLookup, // AllowFileLookup: index.config.AllowFileLookup,
// ParentIndex: index, // ParentIndex: index,
// seenRemoteSources: index.config.seenRemoteSources, // seenRemoteSources: index.config.seenRemoteSources,
// remoteLock: index.config.remoteLock, // remoteLock: index.config.remoteLock,
// uri: uri, // uri: uri,
// AvoidBuildIndex: index.config.AvoidBuildIndex, // AvoidBuildIndex: index.config.AvoidBuildIndex,
// } // }
// //
// var newIndex *SpecIndex // var newIndex *SpecIndex
// seen := index.SearchAncestryForSeenURI(uri[0]) // seen := index.SearchAncestryForSeenURI(uri[0])
// if seen == nil { // if seen == nil {
// //
// newIndex = NewSpecIndexWithConfig(newRoot, newConfig) // newIndex = NewSpecIndexWithConfig(newRoot, newConfig)
// index.refLock.Lock() // index.refLock.Lock()
// index.externalLock.Lock() // index.externalLock.Lock()
// index.externalSpecIndex[uri[0]] = newIndex // index.externalSpecIndex[uri[0]] = newIndex
// index.externalLock.Unlock() // index.externalLock.Unlock()
// newIndex.relativePath = path // newIndex.relativePath = path
// newIndex.parentIndex = index // newIndex.parentIndex = index
// index.AddChild(newIndex) // index.AddChild(newIndex)
// index.refLock.Unlock() // index.refLock.Unlock()
// externalSpecIndex = newIndex // externalSpecIndex = newIndex
// } else { // } else {
// externalSpecIndex = seen // externalSpecIndex = seen
// } // }
// } // }
//} //}
wholeFile := false wholeFile := false
query := "" query := ""
if len(uri) < 2 { if len(uri) < 2 {
wholeFile = true wholeFile = true
} else { } else {
query = fmt.Sprintf("#/%s", strings.Replace(uri[1], "~1", "./", 1)) query = fmt.Sprintf("#/%s", strings.Replace(uri[1], "~1", "./", 1))
query = strings.ReplaceAll(query, "~1", "/") query = strings.ReplaceAll(query, "~1", "/")
} }
// check if there is a component we want to suck in, or if the // check if there is a component we want to suck in, or if the
// entire root needs to come in. // entire root needs to come in.
var foundRef *Reference var foundRef *Reference
if wholeFile { if wholeFile {
if parsedDocument.Kind == yaml.DocumentNode { if parsedDocument.Kind == yaml.DocumentNode {
parsedDocument = parsedDocument.Content[0] parsedDocument = parsedDocument.Content[0]
} }
// TODO: remote locations // TODO: remote locations
foundRef = &Reference{ foundRef = &Reference{
FullDefinition: absoluteFileLocation, FullDefinition: absoluteFileLocation,
Definition: fileName, Definition: fileName,
Name: fileName, Name: fileName,
Node: parsedDocument, Node: parsedDocument,
IsRemote: true, IsRemote: true,
RemoteLocation: absoluteFileLocation, RemoteLocation: absoluteFileLocation,
Path: "$", Path: "$",
RequiredRefProperties: extractDefinitionRequiredRefProperties(parsedDocument, map[string][]string{}, absoluteFileLocation), RequiredRefProperties: extractDefinitionRequiredRefProperties(parsedDocument, map[string][]string{}, absoluteFileLocation),
} }
return foundRef return foundRef
} else { } else {
foundRef = FindComponent(parsedDocument, query, absoluteFileLocation) foundRef = FindComponent(parsedDocument, query, absoluteFileLocation)
if foundRef != nil { if foundRef != nil {
foundRef.IsRemote = true foundRef.IsRemote = true
foundRef.RemoteLocation = absoluteFileLocation foundRef.RemoteLocation = absoluteFileLocation
return foundRef return foundRef
} }
} }
} }
return nil return nil
} }

View File

@@ -55,7 +55,6 @@ func NewResolver(index *SpecIndex) *Resolver {
return nil return nil
} }
r := &Resolver{ r := &Resolver{
specIndex: index, specIndex: index,
resolvedRoot: index.GetRootNode(), resolvedRoot: index.GetRootNode(),
} }
@@ -336,7 +335,7 @@ func (resolver *Resolver) isInfiniteCircularDependency(ref *Reference, visitedDe
} }
for refDefinition := range ref.RequiredRefProperties { for refDefinition := range ref.RequiredRefProperties {
r := resolver.specIndex.GetMappedReferences()[refDefinition] r := resolver.specIndex.SearchIndexForReference(refDefinition)
if initialRef != nil && initialRef.Definition == r.Definition { if initialRef != nil && initialRef.Definition == r.Definition {
return true, visitedDefinitions return true, visitedDefinitions
} }
@@ -434,11 +433,24 @@ func (resolver *Resolver) extractRelatives(ref *Reference, node, parent *yaml.No
} else { } else {
// split the full def into parts if filepath.IsAbs(exp[0]) {
fileDef := strings.Split(ref.FullDefinition, "#/") fullDef = value
// extract the location of the ref and build a full def path. } else {
fullDef = fmt.Sprintf("%s#/%s", fileDef[0], exp[1])
// split the referring ref full def into parts
fileDef := strings.Split(ref.FullDefinition, "#/")
// extract the location of the ref and build a full def path.
fullDef, _ = filepath.Abs(filepath.Join(filepath.Dir(fileDef[0]), exp[0]))
}
//// split the full def into parts
//fileDef := strings.Split(ref.FullDefinition, "#/")
//
//// extract the location of the ref and build a full def path.
//
//fullDef = fmt.Sprintf("%s#/%s", fileDef[0], exp[1])
} }
} else { } else {
@@ -470,9 +482,9 @@ func (resolver *Resolver) extractRelatives(ref *Reference, node, parent *yaml.No
fileDef := strings.Split(ref.FullDefinition, "#/") fileDef := strings.Split(ref.FullDefinition, "#/")
// extract the location of the ref and build a full def path. // extract the location of the ref and build a full def path.
loc, _ := filepath.Abs(filepath.Join(filepath.Dir(fileDef[0]), exp[0])) //loc, _ := filepath.Abs(fileDef[0]), exp[1]))
fullDef = fmt.Sprintf("%s#/%s", loc, exp[1]) fullDef = fmt.Sprintf("%s#/%s", fileDef[0], exp[1])
} }
@@ -505,7 +517,6 @@ func (resolver *Resolver) extractRelatives(ref *Reference, node, parent *yaml.No
} else { } else {
// extract the location of the ref and build a full def path.
fullDef, _ = filepath.Abs(filepath.Join(filepath.Dir(fileDef[0]), exp[0])) fullDef, _ = filepath.Abs(filepath.Join(filepath.Dir(fileDef[0]), exp[0]))
} }

File diff suppressed because it is too large Load Diff

View File

@@ -36,6 +36,7 @@ type RemoteFS struct {
remoteErrors []error remoteErrors []error
logger *slog.Logger logger *slog.Logger
defaultClient *http.Client defaultClient *http.Client
extractedFiles map[string]RolodexFile
} }
type RemoteFile struct { type RemoteFile struct {
@@ -158,7 +159,8 @@ func (f *RemoteFile) Index(config *SpecIndexConfig) (*SpecIndex, error) {
} }
index := NewSpecIndexWithConfig(info.RootNode, config) index := NewSpecIndexWithConfig(info.RootNode, config)
index.specAbsolutePath = f.fullPath
index.specAbsolutePath = config.SpecAbsolutePath
f.index = index f.index = index
return index, nil return index, nil
} }
@@ -233,6 +235,7 @@ func (i *RemoteFS) GetFiles() map[string]RolodexFile {
files[key.(string)] = value.(*RemoteFile) files[key.(string)] = value.(*RemoteFile)
return true return true
}) })
i.extractedFiles = files
return files return files
} }
@@ -302,6 +305,7 @@ func (i *RemoteFS) Open(remoteURL string) (fs.File, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
remoteParsedURLOriginal, _ := url.Parse(remoteURL)
// try path first // try path first
if r, ok := i.Files.Load(remoteParsedURL.Path); ok { if r, ok := i.Files.Load(remoteParsedURL.Path); ok {
@@ -418,7 +422,7 @@ func (i *RemoteFS) Open(remoteURL string) (fs.File, error) {
copiedCfg := *i.indexConfig copiedCfg := *i.indexConfig
newBase := fmt.Sprintf("%s://%s%s", remoteParsedURL.Scheme, remoteParsedURL.Host, newBase := fmt.Sprintf("%s://%s%s", remoteParsedURLOriginal.Scheme, remoteParsedURLOriginal.Host,
filepath.Dir(remoteParsedURL.Path)) filepath.Dir(remoteParsedURL.Path))
newBaseURL, _ := url.Parse(newBase) newBaseURL, _ := url.Parse(newBase)

View File

@@ -33,8 +33,19 @@ func (index *SpecIndex) SearchIndexForReferenceByReference(fullRef *Reference) *
roloLookup, _ = filepath.Abs(filepath.Join(absPath, uri[0])) roloLookup, _ = filepath.Abs(filepath.Join(absPath, uri[0]))
} }
} }
} else {
//roloLookup = absPath // hang on a jiffy whiffy
if filepath.Ext(uri[1]) != "" {
roloLookup = absPath
} else {
roloLookup = ""
}
ref = fmt.Sprintf("%s#/%s", absPath, uri[1])
} }
ref = fmt.Sprintf("#/%s", uri[1])
} else { } else {
if filepath.IsAbs(uri[0]) { if filepath.IsAbs(uri[0]) {
roloLookup = uri[0] roloLookup = uri[0]
@@ -65,7 +76,9 @@ func (index *SpecIndex) SearchIndexForReferenceByReference(fullRef *Reference) *
// extract the index from the rolodex file. // extract the index from the rolodex file.
idx := rFile.GetIndex() idx := rFile.GetIndex()
index.resolver.indexesVisited++ if index.resolver != nil {
index.resolver.indexesVisited++
}
if idx != nil { if idx != nil {
// check mapped refs. // check mapped refs.

View File

@@ -212,6 +212,12 @@ func TestSpecIndex_DigitalOcean_FullCheckoutLocalResolve(t *testing.T) {
assert.Len(t, index.GetMappedReferencesSequenced(), 299) assert.Len(t, index.GetMappedReferencesSequenced(), 299)
assert.Len(t, index.GetMappedReferences(), 299) assert.Len(t, index.GetMappedReferences(), 299)
assert.Len(t, fileFS.GetErrors(), 0) assert.Len(t, fileFS.GetErrors(), 0)
// check circular references
rolo.CheckForCircularReferences()
assert.Len(t, rolo.GetCaughtErrors(), 0)
assert.Len(t, rolo.GetIgnoredCircularReferences(), 0)
} }
func TestSpecIndex_DigitalOcean_LookupsNotAllowed(t *testing.T) { func TestSpecIndex_DigitalOcean_LookupsNotAllowed(t *testing.T) {

View File

@@ -6,6 +6,7 @@ package index
import ( import (
"fmt" "fmt"
"net/url" "net/url"
"path/filepath"
"strings" "strings"
"sync" "sync"
@@ -31,14 +32,14 @@ func (index *SpecIndex) extractDefinitionsAndSchemas(schemasNode *yaml.Node, pat
Node: schema, Node: schema,
Path: fmt.Sprintf("$.components.schemas.%s", name), Path: fmt.Sprintf("$.components.schemas.%s", name),
ParentNode: schemasNode, ParentNode: schemasNode,
RequiredRefProperties: extractDefinitionRequiredRefProperties(schemasNode, map[string][]string{}), RequiredRefProperties: extractDefinitionRequiredRefProperties(schemasNode, map[string][]string{}, fullDef),
} }
index.allComponentSchemaDefinitions[def] = ref index.allComponentSchemaDefinitions[def] = ref
} }
} }
// extractDefinitionRequiredRefProperties goes through the direct properties of a schema and extracts the map of required definitions from within it // extractDefinitionRequiredRefProperties goes through the direct properties of a schema and extracts the map of required definitions from within it
func extractDefinitionRequiredRefProperties(schemaNode *yaml.Node, reqRefProps map[string][]string) map[string][]string { func extractDefinitionRequiredRefProperties(schemaNode *yaml.Node, reqRefProps map[string][]string, fulldef string) map[string][]string {
if schemaNode == nil { if schemaNode == nil {
return reqRefProps return reqRefProps
} }
@@ -73,7 +74,7 @@ func extractDefinitionRequiredRefProperties(schemaNode *yaml.Node, reqRefProps m
// Check to see if the current property is directly embedded within the current schema, and handle its properties if so // Check to see if the current property is directly embedded within the current schema, and handle its properties if so
_, paramPropertiesMapNode := utils.FindKeyNodeTop("properties", param.Content) _, paramPropertiesMapNode := utils.FindKeyNodeTop("properties", param.Content)
if paramPropertiesMapNode != nil { if paramPropertiesMapNode != nil {
reqRefProps = extractDefinitionRequiredRefProperties(param, reqRefProps) reqRefProps = extractDefinitionRequiredRefProperties(param, reqRefProps, fulldef)
} }
// Check to see if the current property is polymorphic, and dive into that model if so // Check to see if the current property is polymorphic, and dive into that model if so
@@ -81,7 +82,7 @@ func extractDefinitionRequiredRefProperties(schemaNode *yaml.Node, reqRefProps m
_, ofNode := utils.FindKeyNodeTop(key, param.Content) _, ofNode := utils.FindKeyNodeTop(key, param.Content)
if ofNode != nil { if ofNode != nil {
for _, ofNodeItem := range ofNode.Content { for _, ofNodeItem := range ofNode.Content {
reqRefProps = extractRequiredReferenceProperties(ofNodeItem, name, reqRefProps) reqRefProps = extractRequiredReferenceProperties(fulldef, ofNodeItem, name, reqRefProps)
} }
} }
} }
@@ -94,14 +95,14 @@ func extractDefinitionRequiredRefProperties(schemaNode *yaml.Node, reqRefProps m
continue continue
} }
reqRefProps = extractRequiredReferenceProperties(requiredPropDefNode, requiredPropertyNode.Value, reqRefProps) reqRefProps = extractRequiredReferenceProperties(fulldef, requiredPropDefNode, requiredPropertyNode.Value, reqRefProps)
} }
return reqRefProps return reqRefProps
} }
// extractRequiredReferenceProperties returns a map of definition names to the property or properties which reference it within a node // extractRequiredReferenceProperties returns a map of definition names to the property or properties which reference it within a node
func extractRequiredReferenceProperties(requiredPropDefNode *yaml.Node, propName string, reqRefProps map[string][]string) map[string][]string { func extractRequiredReferenceProperties(fulldef string, requiredPropDefNode *yaml.Node, propName string, reqRefProps map[string][]string) map[string][]string {
isRef, _, defPath := utils.IsNodeRefValue(requiredPropDefNode) isRef, _, defPath := utils.IsNodeRefValue(requiredPropDefNode)
if !isRef { if !isRef {
_, defItems := utils.FindKeyNodeTop("items", requiredPropDefNode.Content) _, defItems := utils.FindKeyNodeTop("items", requiredPropDefNode.Content)
@@ -114,6 +115,69 @@ func extractRequiredReferenceProperties(requiredPropDefNode *yaml.Node, propName
return reqRefProps return reqRefProps
} }
// explode defpath
exp := strings.Split(defPath, "#/")
if len(exp) == 2 {
if exp[0] != "" {
if !strings.HasPrefix(exp[0], "http") {
if !filepath.IsAbs(exp[0]) {
if strings.HasPrefix(fulldef, "http") {
u, _ := url.Parse(fulldef)
p := filepath.Dir(u.Path)
abs, _ := filepath.Abs(filepath.Join(p, exp[0]))
u.Path = abs
defPath = fmt.Sprintf("%s#/%s", u.String(), exp[1])
} else {
abs, _ := filepath.Abs(filepath.Join(filepath.Dir(fulldef), exp[0]))
defPath = fmt.Sprintf("%s#/%s", abs, exp[1])
}
}
}
}
} else {
if strings.HasPrefix(exp[0], "http") {
defPath = exp[0]
} else {
// file shit again
if filepath.IsAbs(exp[0]) {
defPath = exp[0]
} else {
// check full def and decide what to do next.
if strings.HasPrefix(fulldef, "http") {
u, _ := url.Parse(fulldef)
p := filepath.Dir(u.Path)
abs, _ := filepath.Abs(filepath.Join(p, exp[0]))
u.Path = abs
defPath = u.String()
} else {
defPath, _ = filepath.Abs(filepath.Join(filepath.Dir(fulldef), exp[0]))
}
}
}
}
if _, ok := reqRefProps[defPath]; !ok { if _, ok := reqRefProps[defPath]; !ok {
reqRefProps[defPath] = []string{} reqRefProps[defPath] = []string{}
} }

View File

@@ -49,5 +49,5 @@ func TestGenerateCleanSpecConfigBaseURL_HttpStrip(t *testing.T) {
} }
func TestSpecIndex_extractDefinitionRequiredRefProperties(t *testing.T) { func TestSpecIndex_extractDefinitionRequiredRefProperties(t *testing.T) {
assert.Nil(t, extractDefinitionRequiredRefProperties(nil, nil)) assert.Nil(t, extractDefinitionRequiredRefProperties(nil, nil, ""))
} }