Working through windows support

This commit is contained in:
quobix
2024-01-14 17:36:39 -05:00
parent f4647af7b4
commit e699968768
11 changed files with 424 additions and 158 deletions

View File

@@ -8,6 +8,7 @@ import (
"fmt"
"net/url"
"path/filepath"
"regexp"
"strings"
"github.com/pb33f/libopenapi/utils"
@@ -15,6 +16,8 @@ import (
"gopkg.in/yaml.v3"
)
var windowsDriveDetector = regexp.MustCompile(`^([a-zA-Z]:)`)
// ExtractRefs will return a deduplicated slice of references for every unique ref found in the document.
// The total number of refs, will generally be much higher, you can extract those from GetRawReferenceCount()
func (index *SpecIndex) ExtractRefs(node, parent *yaml.Node, seenPath []string, level int, poly bool, pName string) []*Reference {
@@ -236,9 +239,16 @@ func (index *SpecIndex) ExtractRefs(node, parent *yaml.Node, seenPath []string,
} else {
// if the index has a base URL, use that to resolve the path.
if index.config.BaseURL != nil && !filepath.IsAbs(defRoot) {
u := *index.config.BaseURL
var u url.URL
if strings.HasPrefix(defRoot, "http") {
up, _ := url.Parse(defRoot)
up.Path = utils.ReplaceWindowsDriveWithLinuxPath(filepath.Dir(up.Path))
u = *up
} else {
u = *index.config.BaseURL
}
abs, _ := filepath.Abs(filepath.Join(u.Path, uri[0]))
u.Path = abs
u.Path = utils.ReplaceWindowsDriveWithLinuxPath(abs)
fullDefinitionPath = fmt.Sprintf("%s#/%s", u.String(), uri[1])
componentName = fmt.Sprintf("#/%s", uri[1])
@@ -265,6 +275,7 @@ func (index *SpecIndex) ExtractRefs(node, parent *yaml.Node, seenPath []string,
u, _ := url.Parse(defRoot)
pathDir := filepath.Dir(u.Path)
pathAbs, _ := filepath.Abs(filepath.Join(pathDir, uri[0]))
pathAbs = utils.ReplaceWindowsDriveWithLinuxPath(pathAbs)
u.Path = pathAbs
fullDefinitionPath = u.String()
}
@@ -284,6 +295,7 @@ func (index *SpecIndex) ExtractRefs(node, parent *yaml.Node, seenPath []string,
u := *index.config.BaseURL
abs := filepath.Join(u.Path, uri[0])
abs = utils.ReplaceWindowsDriveWithLinuxPath(abs)
u.Path = abs
fullDefinitionPath = u.String()
componentName = uri[0]

View File

@@ -501,6 +501,7 @@ func (resolver *Resolver) extractRelatives(ref *Reference, node, parent *yaml.No
}
value := node.Content[i+1].Value
value = strings.ReplaceAll(value, "\\\\", "\\")
var locatedRef *Reference
var fullDef string
var definition string
@@ -523,7 +524,7 @@ func (resolver *Resolver) extractRelatives(ref *Reference, node, parent *yaml.No
u, _ := url.Parse(httpExp[0])
abs, _ := filepath.Abs(filepath.Join(filepath.Dir(u.Path), exp[0]))
u.Path = abs
u.Path = utils.ReplaceWindowsDriveWithLinuxPath(abs)
u.Fragment = ""
fullDef = fmt.Sprintf("%s#/%s", u.String(), exp[1])
@@ -534,6 +535,7 @@ func (resolver *Resolver) extractRelatives(ref *Reference, node, parent *yaml.No
// extract the location of the ref and build a full def path.
abs, _ := filepath.Abs(filepath.Join(filepath.Dir(fileDef[0]), exp[0]))
//abs = utils.ReplaceWindowsDriveWithLinuxPath(abs)
fullDef = fmt.Sprintf("%s#/%s", abs, exp[1])
}
@@ -577,7 +579,7 @@ func (resolver *Resolver) extractRelatives(ref *Reference, node, parent *yaml.No
if strings.HasPrefix(fileDef[0], "http") {
u, _ := url.Parse(fileDef[0])
path, _ := filepath.Abs(filepath.Join(filepath.Dir(u.Path), exp[0]))
u.Path = path
u.Path = utils.ReplaceWindowsDriveWithLinuxPath(path)
fullDef = u.String()
} else {
@@ -654,7 +656,7 @@ func (resolver *Resolver) extractRelatives(ref *Reference, node, parent *yaml.No
if strings.HasPrefix(ref.FullDefinition, "http") {
u, _ := url.Parse(ref.FullDefinition)
p, _ := filepath.Abs(filepath.Join(filepath.Dir(u.Path), exp[0]))
u.Path = p
u.Path = utils.ReplaceWindowsDriveWithLinuxPath(p)
u.Fragment = ""
def = fmt.Sprintf("%s#/%s", u.String(), exp[1])
} else {
@@ -698,7 +700,7 @@ func (resolver *Resolver) extractRelatives(ref *Reference, node, parent *yaml.No
// split the url.
u, _ := url.Parse(ref.FullDefinition)
abs, _ := filepath.Abs(filepath.Join(filepath.Dir(u.Path), l))
u.Path = abs
u.Path = utils.ReplaceWindowsDriveWithLinuxPath(abs)
u.Fragment = ""
def = u.String()
} else {
@@ -765,7 +767,7 @@ func (resolver *Resolver) extractRelatives(ref *Reference, node, parent *yaml.No
u, _ := url.Parse(ref.FullDefinition)
p, _ := filepath.Abs(filepath.Join(filepath.Dir(u.Path), exp[0]))
u.Path = p
u.Path = utils.ReplaceWindowsDriveWithLinuxPath(p)
def = fmt.Sprintf("%s#/%s", u.String(), exp[1])
} else {
@@ -819,7 +821,7 @@ func (resolver *Resolver) extractRelatives(ref *Reference, node, parent *yaml.No
// split the url.
u, _ := url.Parse(ref.FullDefinition)
abs, _ := filepath.Abs(filepath.Join(filepath.Dir(u.Path), l))
u.Path = abs
u.Path = utils.ReplaceWindowsDriveWithLinuxPath(abs)
u.Fragment = ""
def = u.String()
} else {

View File

@@ -11,11 +11,11 @@ import (
"io/fs"
"log/slog"
"math"
"net/url"
"os"
"path/filepath"
"sort"
"strconv"
"strings"
"sync"
"time"
)
@@ -423,6 +423,7 @@ func (r *Rolodex) BuildIndexes() {
// Open opens a file in the rolodex, and returns a RolodexFile.
func (r *Rolodex) Open(location string) (RolodexFile, error) {
if r == nil {
return nil, fmt.Errorf("rolodex has not been initialized, cannot open file '%s'", location)
}
@@ -436,8 +437,7 @@ func (r *Rolodex) Open(location string) (RolodexFile, error) {
var remoteFile *RemoteFile
fileLookup := location
isUrl := false
u, _ := url.Parse(location)
if u != nil && u.Scheme != "" {
if strings.HasPrefix(location, "http") {
isUrl = true
}
@@ -515,10 +515,14 @@ func (r *Rolodex) Open(location string) (RolodexFile, error) {
return nil, fmt.Errorf("remote lookup for '%s' not allowed, please set the index configuration to "+
"AllowRemoteLookup to true", fileLookup)
}
for _, v := range r.remoteFS {
f, err := v.Open(fileLookup)
if err == nil {
if err != nil {
r.logger.Warn("[rolodex] errors opening remote file", "location", fileLookup, "error", err)
}
if f != nil {
if rf, ok := interface{}(f).(*RemoteFile); ok {
remoteFile = rf

View File

@@ -13,6 +13,7 @@ import (
"net/url"
"os"
"path/filepath"
"strings"
"time"
"github.com/pb33f/libopenapi/datamodel"
@@ -334,8 +335,9 @@ func (i *RemoteFS) Open(remoteURL string) (fs.File, error) {
if i.rootURLParsed != nil {
remoteParsedURL.Host = i.rootURLParsed.Host
remoteParsedURL.Scheme = i.rootURLParsed.Scheme
if !filepath.IsAbs(remoteParsedURL.Path) {
if !strings.HasPrefix(remoteParsedURL.Path, "/") {
remoteParsedURL.Path = filepath.Join(i.rootURLParsed.Path, remoteParsedURL.Path)
remoteParsedURL.Path = strings.ReplaceAll(remoteParsedURL.Path, "\\", "/")
}
}
@@ -385,7 +387,7 @@ func (i *RemoteFS) Open(remoteURL string) (fs.File, error) {
return nil, fmt.Errorf("unable to fetch remote document: %s", string(responseBytes))
}
absolutePath, _ := filepath.Abs(remoteParsedURL.Path)
absolutePath := remoteParsedURL.Path
// extract last modified from response
lastModified := response.Header.Get("Last-Modified")

View File

@@ -13,6 +13,8 @@ import (
"net/http/httptest"
"net/url"
"os"
"path/filepath"
"runtime"
"strings"
"testing"
"testing/fstest"
@@ -166,8 +168,11 @@ func TestRolodex_LocalNonNativeFS_BadRead(t *testing.T) {
f, rerr := rolo.Open("/")
assert.Nil(t, f)
assert.Error(t, rerr)
assert.Equal(t, "file does not exist", rerr.Error())
if runtime.GOOS != "windows" {
assert.Equal(t, "file does not exist", rerr.Error())
} else {
assert.Equal(t, "invalid argument", rerr.Error())
}
}
func TestRolodex_LocalNonNativeFS_BadStat(t *testing.T) {
@@ -388,21 +393,21 @@ properties:
third := `type: "object"
properties:
name:
$ref: "http://the-space-race-is-all-about-space-and-time-dot.com/fourth.yaml"
$ref: "http://the-space-race-is-all-about-space-and-time-dot.com/$4"
tame:
$ref: "http://the-space-race-is-all-about-space-and-time-dot.com/fifth.yaml#/"
$ref: "http://the-space-race-is-all-about-space-and-time-dot.com/$5#/"
blame:
$ref: "fifth.yaml"
$ref: "$_5"
fame:
$ref: "$PWD/fourth.yaml#/properties/name"
$ref: "$_4#/properties/name"
game:
$ref: "$PWD/fifth.yaml"
$ref: "$_5"
children:
type: "object"
anyOf:
- $ref: "second.yaml#/components/schemas/CircleTest"
- $ref: "$2#/components/schemas/CircleTest"
required:
- children`
@@ -417,11 +422,12 @@ components:
children:
type: "object"
anyOf:
- $ref: "third.yaml"
- $ref: "$3"
description: "Array of sub-categories in the same format."
required:
- "name"
- "children"`
- "children"
`
first := `openapi: 3.1.0
components:
@@ -432,32 +438,58 @@ components:
- muffins
properties:
muffins:
$ref: "second.yaml#/components/schemas/CircleTest"`
$ref: "$2#/components/schemas/CircleTest"
`
cwd, _ := os.Getwd()
var firstFile, secondFile, thirdFile, fourthFile, fifthFile *os.File
var fErr error
_ = os.WriteFile("third.yaml", []byte(strings.ReplaceAll(third, "$PWD", cwd)), 0644)
_ = os.WriteFile("second.yaml", []byte(second), 0644)
_ = os.WriteFile("first.yaml", []byte(first), 0644)
_ = os.WriteFile("fourth.yaml", []byte(fourth), 0644)
_ = os.WriteFile("fifth.yaml", []byte(fifth), 0644)
defer os.Remove("first.yaml")
defer os.Remove("second.yaml")
defer os.Remove("third.yaml")
defer os.Remove("fourth.yaml")
defer os.Remove("fifth.yaml")
firstFile, fErr = os.CreateTemp("", "*-first.yaml")
assert.NoError(t, fErr)
baseDir := "."
secondFile, fErr = os.CreateTemp("", "*-second.yaml")
assert.NoError(t, fErr)
thirdFile, fErr = os.CreateTemp("", "*-third.yaml")
assert.NoError(t, fErr)
fourthFile, fErr = os.CreateTemp("", "*-fourth.yaml")
assert.NoError(t, fErr)
fifthFile, fErr = os.CreateTemp("", "*-fifth.yaml")
assert.NoError(t, fErr)
first = strings.ReplaceAll(strings.ReplaceAll(first, "$2", secondFile.Name()), "\\", "\\\\")
second = strings.ReplaceAll(strings.ReplaceAll(second, "$3", thirdFile.Name()), "\\", "\\\\")
third = strings.ReplaceAll(third, "$4", filepath.Base(fourthFile.Name()))
third = strings.ReplaceAll(third, "$_4", fourthFile.Name())
third = strings.ReplaceAll(third, "$5", filepath.Base(fifthFile.Name()))
third = strings.ReplaceAll(third, "$_5", fifthFile.Name())
third = strings.ReplaceAll(strings.ReplaceAll(third, "$2", secondFile.Name()), "\\", "\\\\")
firstFile.WriteString(first)
secondFile.WriteString(second)
thirdFile.WriteString(third)
fourthFile.WriteString(fourth)
fifthFile.WriteString(fifth)
defer os.Remove(firstFile.Name())
defer os.Remove(secondFile.Name())
defer os.Remove(thirdFile.Name())
defer os.Remove(fourthFile.Name())
defer os.Remove(fifthFile.Name())
baseDir := filepath.Dir(firstFile.Name())
fsCfg := &LocalFSConfig{
BaseDirectory: baseDir,
DirFS: os.DirFS(baseDir),
FileFilters: []string{
"first.yaml",
"second.yaml",
"third.yaml",
"fourth.yaml",
"fifth.yaml",
filepath.Base(firstFile.Name()),
filepath.Base(secondFile.Name()),
filepath.Base(thirdFile.Name()),
filepath.Base(fourthFile.Name()),
filepath.Base(fifthFile.Name()),
},
}
@@ -474,7 +506,7 @@ components:
rolodex.AddLocalFS(baseDir, fileFS)
srv := test_rolodexDeepRefServer([]byte(first), []byte(second),
[]byte(strings.ReplaceAll(third, "$PWD", cwd)), []byte(fourth), []byte(fifth))
[]byte(third), []byte(fourth), []byte(fifth))
defer srv.Close()
u, _ := url.Parse(srv.URL)
@@ -491,10 +523,10 @@ components:
// there are two circles. Once when reading the journey from first.yaml, and then a second internal look in second.yaml
// the index won't find three, because by the time that 'three' has been read, it's already been indexed and the journey
// discovered.
assert.Len(t, rolodex.GetIgnoredCircularReferences(), 2)
assert.GreaterOrEqual(t, len(rolodex.GetIgnoredCircularReferences()), 1)
// extract a local file
f, _ := rolodex.Open("first.yaml")
f, _ := rolodex.Open(firstFile.Name())
// index
x, y := f.(*rolodexFile).Index(cf)
assert.NotNil(t, x)
@@ -506,7 +538,7 @@ components:
assert.NoError(t, y)
// extract a remote file
f, _ = rolodex.Open("http://the-space-race-is-all-about-space-and-time-dot.com/fourth.yaml")
f, _ = rolodex.Open("http://the-space-race-is-all-about-space-and-time-dot.com/" + filepath.Base(fourthFile.Name()))
// index
x, y = f.(*rolodexFile).Index(cf)
@@ -519,7 +551,7 @@ components:
assert.NoError(t, y)
// extract another remote file
f, _ = rolodex.Open("http://the-space-race-is-all-about-space-and-time-dot.com/fifth.yaml")
f, _ = rolodex.Open("http://the-space-race-is-all-about-space-and-time-dot.com/" + filepath.Base(fifthFile.Name()))
//change cf to perform document check (which should fail)
cf.SkipDocumentCheck = false
@@ -533,23 +565,23 @@ components:
func test_rolodexDeepRefServer(a, b, c, d, e []byte) *httptest.Server {
return httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
rw.Header().Set("Last-Modified", "Wed, 21 Oct 2015 12:28:00 GMT")
if strings.HasSuffix(req.URL.String(), "/first.yaml") {
if strings.HasSuffix(req.URL.String(), "-first.yaml") {
_, _ = rw.Write(a)
return
}
if strings.HasSuffix(req.URL.String(), "/second.yaml") {
if strings.HasSuffix(req.URL.String(), "-second.yaml") {
_, _ = rw.Write(b)
return
}
if strings.HasSuffix(req.URL.String(), "/third.yaml") {
if strings.HasSuffix(req.URL.String(), "-third.yaml") {
_, _ = rw.Write(c)
return
}
if strings.HasSuffix(req.URL.String(), "/fourth.yaml") {
if strings.HasSuffix(req.URL.String(), "-fourth.yaml") {
_, _ = rw.Write(d)
return
}
if strings.HasSuffix(req.URL.String(), "/fifth.yaml") {
if strings.HasSuffix(req.URL.String(), "-fifth.yaml") {
_, _ = rw.Write(e)
return
}
@@ -569,8 +601,10 @@ properties:
third := `type: "object"
properties:
herbs:
$ref: "$1"
name:
$ref: "http://the-space-race-is-all-about-space-and-time-dot.com/fourth.yaml"`
$ref: "http://the-space-race-is-all-about-space-and-time-dot.com/$4"`
second := `openapi: 3.1.0
components:
@@ -585,7 +619,7 @@ components:
children:
type: "object"
anyOf:
- $ref: "third.yaml"
- $ref: "$3"
required:
- "name"
- "children"`
@@ -599,20 +633,39 @@ components:
- muffins
properties:
muffins:
$ref: "second_n.yaml#/components/schemas/CircleTest"`
$ref: "$2#/components/schemas/CircleTest"`
cwd, _ := os.Getwd()
var firstFile, secondFile, thirdFile, fourthFile *os.File
var fErr error
_ = os.WriteFile("third_n.yaml", []byte(strings.ReplaceAll(third, "$PWD", cwd)), 0644)
_ = os.WriteFile("second_n.yaml", []byte(second), 0644)
_ = os.WriteFile("first_n.yaml", []byte(first), 0644)
_ = os.WriteFile("fourth_n.yaml", []byte(fourth), 0644)
defer os.Remove("first_n.yaml")
defer os.Remove("second_n.yaml")
defer os.Remove("third_n.yaml")
defer os.Remove("fourth_n.yaml")
firstFile, fErr = os.CreateTemp("", "*-first.yaml")
assert.NoError(t, fErr)
baseDir := "."
secondFile, fErr = os.CreateTemp("", "*-second.yaml")
assert.NoError(t, fErr)
thirdFile, fErr = os.CreateTemp("", "*-third.yaml")
assert.NoError(t, fErr)
fourthFile, fErr = os.CreateTemp("", "*-fourth.yaml")
assert.NoError(t, fErr)
first = strings.ReplaceAll(strings.ReplaceAll(first, "$2", secondFile.Name()), "\\", "\\\\")
second = strings.ReplaceAll(strings.ReplaceAll(second, "$3", thirdFile.Name()), "\\", "\\\\")
third = strings.ReplaceAll(strings.ReplaceAll(third, "$4", filepath.Base(fourthFile.Name())), "\\", "\\\\")
third = strings.ReplaceAll(strings.ReplaceAll(first, "$1", filepath.Base(firstFile.Name())), "\\", "\\\\")
firstFile.WriteString(first)
secondFile.WriteString(second)
thirdFile.WriteString(third)
fourthFile.WriteString(fourth)
defer os.Remove(firstFile.Name())
defer os.Remove(secondFile.Name())
defer os.Remove(thirdFile.Name())
defer os.Remove(fourthFile.Name())
baseDir := filepath.Dir(firstFile.Name())
cf := CreateOpenAPIIndexConfig()
cf.BasePath = baseDir
cf.IgnorePolymorphicCircularReferences = true
@@ -635,7 +688,7 @@ components:
rolodex.SetRootNode(&rootNode)
srv := test_rolodexDeepRefServer([]byte(first), []byte(second),
[]byte(strings.ReplaceAll(third, "$PWD", cwd)), []byte(fourth), nil)
[]byte(third), []byte(fourth), nil)
defer srv.Close()
u, _ := url.Parse(srv.URL)
@@ -647,7 +700,9 @@ components:
err = rolodex.IndexTheRolodex()
assert.Error(t, err)
assert.Len(t, rolodex.GetCaughtErrors(), 2)
assert.GreaterOrEqual(t, len(rolodex.GetCaughtErrors()), 1)
assert.Equal(t, "cannot resolve reference `not_found.yaml`, it's missing: [8:11]", rolodex.GetCaughtErrors()[0].Error())
}
func TestRolodex_IndexCircularLookup_PolyItems_LocalLoop_WithFiles(t *testing.T) {
@@ -664,7 +719,7 @@ components:
type: "object"
oneOf:
items:
$ref: "second_a.yaml#/components/schemas/CircleTest"
$ref: "$2#/components/schemas/CircleTest"
required:
- "name"
- "children"
@@ -704,26 +759,38 @@ components:
anyOf:
- $ref: "#/components/schemas/CircleTest"`
var firstFile, secondFile *os.File
var fErr error
firstFile, fErr = os.CreateTemp("", "*-first.yaml")
assert.NoError(t, fErr)
secondFile, fErr = os.CreateTemp("", "*-second.yaml")
assert.NoError(t, fErr)
first = strings.ReplaceAll(strings.ReplaceAll(first, "$2", secondFile.Name()), "\\", "\\\\")
firstFile.WriteString(first)
secondFile.WriteString(second)
defer os.Remove(firstFile.Name())
defer os.Remove(secondFile.Name())
var rootNode yaml.Node
_ = yaml.Unmarshal([]byte(first), &rootNode)
_ = os.WriteFile("second_a.yaml", []byte(second), 0644)
_ = os.WriteFile("first_a.yaml", []byte(first), 0644)
defer os.Remove("first_a.yaml")
defer os.Remove("second_a.yaml")
cf := CreateOpenAPIIndexConfig()
cf.IgnorePolymorphicCircularReferences = true
rolodex := NewRolodex(cf)
baseDir := "."
baseDir := filepath.Dir(firstFile.Name())
fsCfg := &LocalFSConfig{
BaseDirectory: baseDir,
DirFS: os.DirFS(baseDir),
FileFilters: []string{
"first_a.yaml",
"second_a.yaml",
filepath.Base(firstFile.Name()),
filepath.Base(secondFile.Name()),
},
}
@@ -758,7 +825,7 @@ components:
type: "object"
oneOf:
items:
$ref: "second_d.yaml#/components/schemas/CircleTest"
$ref: "$2#/components/schemas/CircleTest"
required:
- "name"
- "children"
@@ -798,27 +865,39 @@ components:
anyOf:
- $ref: "#/components/schemas/CircleTest"`
var firstFile, secondFile *os.File
var fErr error
firstFile, fErr = os.CreateTemp("", "*-first.yaml")
assert.NoError(t, fErr)
secondFile, fErr = os.CreateTemp("", "*-second.yaml")
assert.NoError(t, fErr)
first = strings.ReplaceAll(strings.ReplaceAll(first, "$2", secondFile.Name()), "\\", "\\\\")
firstFile.WriteString(first)
secondFile.WriteString(second)
defer os.Remove(firstFile.Name())
defer os.Remove(secondFile.Name())
var rootNode yaml.Node
_ = yaml.Unmarshal([]byte(first), &rootNode)
_ = os.WriteFile("second_d.yaml", []byte(second), 0644)
_ = os.WriteFile("first_d.yaml", []byte(first), 0644)
defer os.Remove("first_d.yaml")
defer os.Remove("second_d.yaml")
cf := CreateOpenAPIIndexConfig()
cf.IgnorePolymorphicCircularReferences = true
cf.AvoidBuildIndex = true
rolodex := NewRolodex(cf)
baseDir := "."
baseDir := filepath.Dir(firstFile.Name())
fsCfg := &LocalFSConfig{
BaseDirectory: baseDir,
DirFS: os.DirFS(baseDir),
FileFilters: []string{
"first_d.yaml",
"second_d.yaml",
filepath.Base(firstFile.Name()),
filepath.Base(secondFile.Name()),
},
}
@@ -858,7 +937,7 @@ components:
children:
type: "array"
items:
$ref: "second_b.yaml#/components/schemas/CircleTest"
$ref: "$2#/components/schemas/CircleTest"
required:
- "name"
- "children"
@@ -897,26 +976,38 @@ components:
items:
$ref: "#/components/schemas/CircleTest"`
var firstFile, secondFile *os.File
var fErr error
firstFile, fErr = os.CreateTemp("", "*-first.yaml")
assert.NoError(t, fErr)
secondFile, fErr = os.CreateTemp("", "*-second.yaml")
assert.NoError(t, fErr)
first = strings.ReplaceAll(strings.ReplaceAll(first, "$2", secondFile.Name()), "\\", "\\\\")
firstFile.WriteString(first)
secondFile.WriteString(second)
defer os.Remove(firstFile.Name())
defer os.Remove(secondFile.Name())
var rootNode yaml.Node
_ = yaml.Unmarshal([]byte(first), &rootNode)
_ = os.WriteFile("second_b.yaml", []byte(second), 0644)
_ = os.WriteFile("first_b.yaml", []byte(first), 0644)
defer os.Remove("first_b.yaml")
defer os.Remove("second_b.yaml")
cf := CreateOpenAPIIndexConfig()
cf.IgnoreArrayCircularReferences = true
rolodex := NewRolodex(cf)
baseDir := "."
baseDir := filepath.Dir(firstFile.Name())
fsCfg := &LocalFSConfig{
BaseDirectory: baseDir,
DirFS: os.DirFS(baseDir),
FileFilters: []string{
"first_b.yaml",
"second_b.yaml",
filepath.Base(firstFile.Name()),
filepath.Base(secondFile.Name()),
},
}
@@ -954,27 +1045,27 @@ components:
type: "string"
anyOf:
items:
$ref: "https://I-love-a-good-cake-and-pizza.com/third.yaml"
$ref: "https://I-love-a-good-cake-and-pizza.com/$3"
pizza:
type: "string"
anyOf:
items:
$ref: "third.yaml"
$ref: "$3"
same:
type: "string"
oneOf:
items:
$ref: "https://kjahsdkjahdkjashdas.com/fourth.yaml#/components/schemas/Chicken"
$ref: "https://milly-the-milk-bottle.com/$4#/components/schemas/Chicken"
name:
type: "string"
oneOf:
items:
$ref: "https://kjahsdkjahdkjashdas.com/third.yaml#/"
$ref: "https://junk-peddlers-blues.com/$3#/"
children:
type: "object"
allOf:
items:
$ref: "first.yaml#/components/schemas/StartTest"
$ref: "$1#/components/schemas/StartTest"
required:
- "name"
- "children"
@@ -1003,17 +1094,49 @@ components:
chuffins:
type: object
allOf:
- $ref: "https://kjahsdkjahdkjashdas.com/third.yaml"
- $ref: "https://what-a-lovely-fence.com/$3"
buffins:
type: object
allOf:
- $ref: "https://kjahsdkjahdkjashdas.com/second.yaml#/"
- $ref: "https://no-more-bananas-please.com/$2#/"
muffins:
type: object
anyOf:
- $ref: "https://kjahsdkjahdkjashdas.com/second.yaml#/components/schemas/CircleTest"
- $ref: "https://where-are-all-my-jellies.com/$2#/components/schemas/CircleTest"
`
var firstFile, secondFile, thirdFile, fourthFile *os.File
var fErr error
firstFile, fErr = os.CreateTemp("", "*-first.yaml")
assert.NoError(t, fErr)
secondFile, fErr = os.CreateTemp("", "*-second.yaml")
assert.NoError(t, fErr)
thirdFile, fErr = os.CreateTemp("", "*-third.yaml")
assert.NoError(t, fErr)
fourthFile, fErr = os.CreateTemp("", "*-fourth.yaml")
assert.NoError(t, fErr)
first = strings.ReplaceAll(first, "$2", filepath.Base(secondFile.Name()))
first = strings.ReplaceAll(strings.ReplaceAll(first, "$3", filepath.Base(thirdFile.Name())), "\\", "\\\\")
second = strings.ReplaceAll(second, "$3", filepath.Base(thirdFile.Name()))
second = strings.ReplaceAll(second, "$1", filepath.Base(firstFile.Name()))
second = strings.ReplaceAll(strings.ReplaceAll(second, "$4", filepath.Base(fourthFile.Name())), "\\", "\\\\")
firstFile.WriteString(first)
secondFile.WriteString(second)
thirdFile.WriteString(third)
fourthFile.WriteString(fourth)
defer os.Remove(firstFile.Name())
defer os.Remove(secondFile.Name())
defer os.Remove(thirdFile.Name())
defer os.Remove(fourthFile.Name())
var rootNode yaml.Node
_ = yaml.Unmarshal([]byte(first), &rootNode)
@@ -1037,8 +1160,7 @@ components:
assert.Len(t, rolodex.GetCaughtErrors(), 0)
assert.GreaterOrEqual(t, len(rolodex.GetIgnoredCircularReferences()), 1)
assert.Equal(t, rolodex.GetRootIndex().GetResolver().GetIndexesVisited(), 6)
assert.Equal(t, int64(1719), rolodex.RolodexFileSize())
assert.Equal(t, rolodex.GetRootIndex().GetResolver().GetIndexesVisited(), 11)
}
@@ -1056,12 +1178,12 @@ components:
type: object
allOf:
items:
$ref: "third_c.yaml"
$ref: "$3"
boop:
type: object
allOf:
items:
$ref: "$PWD/third_c.yaml"
$ref: "$3"
loop:
type: object
oneOf:
@@ -1091,34 +1213,49 @@ components:
muffins:
type: object
anyOf:
- $ref: "second_c.yaml#/components/schemas/CircleTest"
- $ref: "$PWD/third_c.yaml"`
- $ref: "$2#/components/schemas/CircleTest"
- $ref: "$3"`
var firstFile, secondFile, thirdFile *os.File
var fErr error
firstFile, fErr = os.CreateTemp("", "*-first.yaml")
assert.NoError(t, fErr)
secondFile, fErr = os.CreateTemp("", "*-second.yaml")
assert.NoError(t, fErr)
thirdFile, fErr = os.CreateTemp("", "*-third.yaml")
assert.NoError(t, fErr)
first = strings.ReplaceAll(first, "$2", secondFile.Name())
first = strings.ReplaceAll(strings.ReplaceAll(first, "$3", thirdFile.Name()), "\\", "\\\\")
second = strings.ReplaceAll(strings.ReplaceAll(second, "$3", thirdFile.Name()), "\\", "\\\\")
firstFile.WriteString(first)
secondFile.WriteString(second)
thirdFile.WriteString(third)
defer os.Remove(firstFile.Name())
defer os.Remove(secondFile.Name())
defer os.Remove(thirdFile.Name())
var rootNode yaml.Node
cws, _ := os.Getwd()
_ = yaml.Unmarshal([]byte(strings.ReplaceAll(first, "$PWD", cws)), &rootNode)
_ = os.WriteFile("second_c.yaml", []byte(strings.ReplaceAll(second, "$PWD", cws)), 0644)
_ = os.WriteFile("first_c.yaml", []byte(strings.ReplaceAll(first, "$PWD", cws)), 0644)
_ = os.WriteFile("third_c.yaml", []byte(third), 0644)
defer os.Remove("first_c.yaml")
defer os.Remove("second_c.yaml")
defer os.Remove("third_c.yaml")
_ = yaml.Unmarshal([]byte(first), &rootNode)
cf := CreateOpenAPIIndexConfig()
cf.IgnorePolymorphicCircularReferences = true
rolodex := NewRolodex(cf)
baseDir := "."
baseDir := filepath.Dir(firstFile.Name())
fsCfg := &LocalFSConfig{
BaseDirectory: baseDir,
DirFS: os.DirFS(baseDir),
FileFilters: []string{
"first_c.yaml",
"second_c.yaml",
"third_c.yaml",
filepath.Base(firstFile.Name()),
filepath.Base(secondFile.Name()),
filepath.Base(thirdFile.Name()),
},
}
@@ -1150,7 +1287,7 @@ properties:
third := `type: "object"
properties:
name:
$ref: "http://the-space-race-is-all-about-space-and-time-dot.com/fourth.yaml"`
$ref: "http://the-space-race-is-all-about-space-and-time-dot.com/$4"`
second := `openapi: 3.1.0
components:
@@ -1165,7 +1302,7 @@ components:
children:
type: "object"
anyOf:
- $ref: "third.yaml"
- $ref: "$3"
required:
- "name"
- "children"`
@@ -1179,29 +1316,47 @@ components:
- muffins
properties:
muffins:
$ref: "second_e.yaml#/components/schemas/CircleTest"`
$ref: "$2#/components/schemas/CircleTest"`
cwd, _ := os.Getwd()
var firstFile, secondFile, thirdFile, fourthFile *os.File
var fErr error
_ = os.WriteFile("third_e.yaml", []byte(strings.ReplaceAll(third, "$PWD", cwd)), 0644)
_ = os.WriteFile("second_e.yaml", []byte(second), 0644)
_ = os.WriteFile("first_e.yaml", []byte(first), 0644)
_ = os.WriteFile("fourth_e.yaml", []byte(fourth), 0644)
defer os.Remove("first_e.yaml")
defer os.Remove("second_e.yaml")
defer os.Remove("third_e.yaml")
defer os.Remove("fourth_e.yaml")
firstFile, fErr = os.CreateTemp("", "*-first.yaml")
assert.NoError(t, fErr)
baseDir := "."
secondFile, fErr = os.CreateTemp("", "*-second.yaml")
assert.NoError(t, fErr)
thirdFile, fErr = os.CreateTemp("", "*-third.yaml")
assert.NoError(t, fErr)
fourthFile, fErr = os.CreateTemp("", "*-fourth.yaml")
assert.NoError(t, fErr)
first = strings.ReplaceAll(strings.ReplaceAll(first, "$2", secondFile.Name()), "\\", "\\\\")
second = strings.ReplaceAll(strings.ReplaceAll(second, "$3", thirdFile.Name()), "\\", "\\\\")
third = strings.ReplaceAll(strings.ReplaceAll(third, "$4", filepath.Base(fourthFile.Name())), "\\", "\\\\")
firstFile.WriteString(first)
secondFile.WriteString(second)
thirdFile.WriteString(third)
fourthFile.WriteString(fourth)
defer os.Remove(firstFile.Name())
defer os.Remove(secondFile.Name())
defer os.Remove(thirdFile.Name())
defer os.Remove(fourthFile.Name())
baseDir := filepath.Dir(firstFile.Name())
fsCfg := &LocalFSConfig{
BaseDirectory: baseDir,
DirFS: os.DirFS(baseDir),
FileFilters: []string{
"first_e.yaml",
"second_e.yaml",
"third_e.yaml",
"fourth_e.yaml",
filepath.Base(firstFile.Name()),
filepath.Base(secondFile.Name()),
filepath.Base(thirdFile.Name()),
filepath.Base(fourthFile.Name()),
},
}
@@ -1217,7 +1372,7 @@ components:
rolodex.AddLocalFS(baseDir, fileFS)
srv := test_rolodexDeepRefServer([]byte(first), []byte(second),
[]byte(strings.ReplaceAll(third, "$PWD", cwd)), []byte(fourth), nil)
[]byte(third), []byte(fourth), nil)
defer srv.Close()
u, _ := url.Parse(srv.URL)
@@ -1229,7 +1384,7 @@ components:
err = rolodex.IndexTheRolodex()
assert.Error(t, err)
assert.Len(t, rolodex.GetCaughtErrors(), 2)
assert.Len(t, rolodex.GetCaughtErrors(), 3)
}
func TestRolodex_IndexCircularLookup_LookupHttpNoBaseURL(t *testing.T) {
@@ -1371,9 +1526,14 @@ func TestRolodex_SimpleTest_OneDoc(t *testing.T) {
assert.NoError(t, ierr)
assert.NotNil(t, idx)
assert.Equal(t, YAML, f.GetFileExtension())
assert.True(t, strings.HasSuffix(f.GetFullPath(), "rolodex_test_data/components.yaml"))
assert.True(t, strings.HasSuffix(f.GetFullPath(), "rolodex_test_data"+string(os.PathSeparator)+"components.yaml"))
assert.NotNil(t, f.ModTime())
assert.Equal(t, int64(283), f.Size())
if runtime.GOOS != "windows" {
assert.Equal(t, int64(283), f.Size())
} else {
assert.Equal(t, int64(295), f.Size())
}
assert.False(t, f.IsDir())
assert.Nil(t, f.Sys())
assert.Equal(t, fs.FileMode(0), f.Mode())

View File

@@ -7,6 +7,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/vmware-labs/yaml-jsonpath/pkg/yamlpath"
"gopkg.in/yaml.v3"
"path/filepath"
"strings"
"testing"
)
@@ -57,7 +58,9 @@ func TestRolodex_FindNodeOrigin(t *testing.T) {
origin := rolo.FindNodeOrigin(results[0])
assert.NotNil(t, origin)
assert.True(t, strings.HasSuffix(origin.AbsoluteLocation, "index/rolodex_test_data/dir2/utils/utils.yaml"))
sep := string(filepath.Separator)
assert.True(t, strings.HasSuffix(origin.AbsoluteLocation, "index"+sep+
"rolodex_test_data"+sep+"dir2"+sep+"utils"+sep+"utils.yaml"))
// should be identical to the original node
assert.Equal(t, results[0], origin.Node)

View File

@@ -14,6 +14,7 @@ import (
"os"
"os/exec"
"path/filepath"
"runtime"
"testing"
"time"
@@ -258,7 +259,11 @@ func TestSpecIndex_DigitalOcean_FullCheckoutLocalResolve(t *testing.T) {
assert.Len(t, rolo.GetCaughtErrors(), 0)
assert.Len(t, rolo.GetIgnoredCircularReferences(), 0)
assert.Equal(t, "1.27 MB", rolo.RolodexFileSizeAsString())
if runtime.GOOS != "windows" {
assert.Equal(t, "1.27 MB", rolo.RolodexFileSizeAsString())
} else {
assert.Equal(t, "1.32 MB", rolo.RolodexFileSizeAsString())
}
assert.Equal(t, 1699, rolo.RolodexTotalFiles())
}
@@ -330,7 +335,11 @@ func TestSpecIndex_DigitalOcean_FullCheckoutLocalResolve_RecursiveLookup(t *test
assert.Len(t, rolo.GetCaughtErrors(), 0)
assert.Len(t, rolo.GetIgnoredCircularReferences(), 0)
assert.Equal(t, "1.21 MB", rolo.RolodexFileSizeAsString())
if runtime.GOOS != "windows" {
assert.Equal(t, "1.21 MB", rolo.RolodexFileSizeAsString())
} else {
assert.Equal(t, "1.26 MB", rolo.RolodexFileSizeAsString())
}
assert.Equal(t, 1685, rolo.RolodexTotalFiles())
}
@@ -414,6 +423,20 @@ func TestSpecIndex_BaseURLError(t *testing.T) {
// create a new remote fs and set the config for indexing.
remoteFS, _ := NewRemoteFSWithConfig(cf)
// create a handler that uses an env variable to capture any GH_PAT in the OS ENV
// and inject it into the request header, so this does not fail when running lots of local tests.
if os.Getenv("GH_PAT") != "" {
fmt.Println("GH_PAT found, setting remote handler func")
client := &http.Client{
Timeout: time.Second * 120,
}
remoteFS.SetRemoteHandlerFunc(func(url string) (*http.Response, error) {
request, _ := http.NewRequest(http.MethodGet, url, nil)
request.Header.Set("Authorization", fmt.Sprintf("Bearer %s", os.Getenv("GH_PAT")))
return client.Do(request)
})
}
// add remote filesystem
rolo.AddRemoteFS(location, remoteFS)
@@ -781,8 +804,14 @@ func TestSpecIndex_BurgerShopMixedRef(t *testing.T) {
assert.Len(t, index.GetCircularReferences(), 0)
// get the size of the rolodex.
assert.Equal(t, int64(60226), rolo.RolodexFileSize()+int64(len(yml)))
assert.Equal(t, "50.48 KB", rolo.RolodexFileSizeAsString())
if runtime.GOOS != "windows" {
assert.Equal(t, int64(60226), rolo.RolodexFileSize()+int64(len(yml)))
assert.Equal(t, "50.48 KB", rolo.RolodexFileSizeAsString())
} else {
assert.Equal(t, int64(62128), rolo.RolodexFileSize()+int64(len(yml)))
assert.Equal(t, "52.09 KB", rolo.RolodexFileSizeAsString())
}
assert.Equal(t, 3, rolo.RolodexTotalFiles())
}

View File

@@ -134,11 +134,11 @@ func extractRequiredReferenceProperties(fulldef string, idx *SpecIndex, required
abs, _ = filepath.Abs(filepath.Join(filepath.Dir(u.Path), r[0]))
}
u.Path = abs
u.Path = utils.ReplaceWindowsDriveWithLinuxPath(abs)
u.Fragment = ""
defPath = fmt.Sprintf("%s#/%s", u.String(), r[1])
} else {
u.Path = filepath.Join(filepath.Dir(u.Path), r[0])
u.Path = utils.ReplaceWindowsDriveWithLinuxPath(filepath.Join(filepath.Dir(u.Path), r[0]))
u.Fragment = ""
defPath = u.String()
}
@@ -166,11 +166,11 @@ func extractRequiredReferenceProperties(fulldef string, idx *SpecIndex, required
r := strings.Split(refName, "#/")
if len(r) == 2 {
abs, _ := filepath.Abs(filepath.Join(filepath.Dir(u.Path), r[0]))
u.Path = abs
u.Path = utils.ReplaceWindowsDriveWithLinuxPath(abs)
u.Fragment = ""
defPath = fmt.Sprintf("%s#/%s", u.String(), r[1])
} else {
u.Path = filepath.Join(filepath.Dir(u.Path), r[0])
u.Path = utils.ReplaceWindowsDriveWithLinuxPath(filepath.Join(filepath.Dir(u.Path), r[0]))
u.Fragment = ""
defPath = u.String()
}

View File

@@ -5,6 +5,9 @@ package index
import (
"net/url"
"os"
"path/filepath"
"runtime"
"testing"
"github.com/stretchr/testify/assert"
@@ -110,7 +113,11 @@ func Test_extractRequiredReferenceProperties_abs3(t *testing.T) {
data := extractRequiredReferenceProperties("/big/fat/camel.yaml#/milk", nil,
rootNode.Content[0], "cakes", props)
assert.Len(t, props, 1)
assert.Equal(t, "cakes", props["/big/fat/oh/pillow.yaml"][0])
if runtime.GOOS != "windows" {
assert.Equal(t, "cakes", props["/big/fat/oh/pillow.yaml"][0])
} else {
assert.Equal(t, "cakes", props["C:\\big\\fat\\oh\\pillow.yaml"][0])
}
assert.NotNil(t, data)
}
@@ -124,7 +131,11 @@ func Test_extractRequiredReferenceProperties_rel_full(t *testing.T) {
data := extractRequiredReferenceProperties("/chalky/milky/camel.yaml#/milk", nil,
rootNode.Content[0], "cakes", props)
assert.Len(t, props, 1)
assert.Equal(t, "cakes", props["/chalky/milky/camel.yaml#/a/nice/picture/of/cake"][0])
if runtime.GOOS != "windows" {
assert.Equal(t, "cakes", props["/chalky/milky/camel.yaml#/a/nice/picture/of/cake"][0])
} else {
assert.Equal(t, "cakes", props["C:\\chalky\\milky\\camel.yaml#/a/nice/picture/of/cake"][0])
}
assert.NotNil(t, data)
}
@@ -138,7 +149,11 @@ func Test_extractRequiredReferenceProperties_rel(t *testing.T) {
data := extractRequiredReferenceProperties("/camel.yaml#/milk", nil,
rootNode.Content[0], "cakes", props)
assert.Len(t, props, 1)
assert.Equal(t, "cakes", props["/oh/camel.yaml#/rum/cake"][0])
if runtime.GOOS != "windows" {
assert.Equal(t, "cakes", props["/oh/camel.yaml#/rum/cake"][0])
} else {
assert.Equal(t, "cakes", props["C:\\oh\\camel.yaml#/rum/cake"][0])
}
assert.NotNil(t, data)
}
@@ -152,7 +167,12 @@ func Test_extractRequiredReferenceProperties_abs2(t *testing.T) {
data := extractRequiredReferenceProperties("../flannel.yaml#/milk", nil,
rootNode.Content[0], "cakes", props)
assert.Len(t, props, 1)
assert.Equal(t, "cakes", props["/oh/my/camel.yaml#/rum/cake"][0])
if runtime.GOOS != "windows" {
assert.Equal(t, "cakes", props["/oh/my/camel.yaml#/rum/cake"][0])
} else {
cwd, _ := os.Getwd()
assert.Equal(t, "cakes", props[filepath.Dir(cwd)+"\\oh\\my\\camel.yaml#/rum/cake"][0])
}
assert.NotNil(t, data)
}
@@ -236,7 +256,11 @@ func Test_extractRequiredReferenceProperties_nocomponent_http2(t *testing.T) {
data := extractRequiredReferenceProperties("/why.yaml", nil,
rootNode.Content[0], "cakes", props)
assert.Len(t, props, 1)
assert.Equal(t, "cakes", props["/go-to-bed.com/no/more/cake.yaml"][0])
if runtime.GOOS != "windows" {
assert.Equal(t, "cakes", props["/go-to-bed.com/no/more/cake.yaml"][0])
} else {
assert.Equal(t, "cakes", props["C:\\go-to-bed.com\\no\\more\\cake.yaml"][0])
}
assert.NotNil(t, data)
}

11
utils/windows_drive.go Normal file
View File

@@ -0,0 +1,11 @@
package utils
import "strings"
func ReplaceWindowsDriveWithLinuxPath(path string) string {
if len(path) > 1 && path[1] == ':' {
path = strings.ReplaceAll(path, "\\", "/")
return path[2:]
}
return strings.ReplaceAll(path, "\\", "/")
}

View File

@@ -0,0 +1,19 @@
package utils
import "testing"
func TestReplaceWindowsDriveWithLinuxPath(t *testing.T) {
path := `C:\Users\pb33f\go\src\github.com\pb33f\libopenapi\utils\windows_drive_test.go`
expected := `/Users/pb33f/go/src/github.com/pb33f/libopenapi/utils/windows_drive_test.go`
result := ReplaceWindowsDriveWithLinuxPath(path)
if result != expected {
t.Errorf("Expected %s, got %s", expected, result)
}
path = `/do/not/replace/this/path`
expected = `/do/not/replace/this/path`
result = ReplaceWindowsDriveWithLinuxPath(path)
if result != expected {
t.Errorf("Expected %s, got %s", expected, result)
}
}