mirror of
https://github.com/LukeHagar/libopenapi.git
synced 2025-12-06 20:47:49 +00:00
Removed some dead code that does not need to exist
A consequence of the old index design, now gone Signed-off-by: quobix <dave@quobix.com>
This commit is contained in:
@@ -4,393 +4,392 @@
|
|||||||
package index
|
package index
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/pb33f/libopenapi/datamodel"
|
"github.com/pb33f/libopenapi/datamodel"
|
||||||
"github.com/pb33f/libopenapi/utils"
|
"github.com/pb33f/libopenapi/utils"
|
||||||
"log/slog"
|
"log/slog"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
|
||||||
"golang.org/x/sync/syncmap"
|
"golang.org/x/sync/syncmap"
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
"io"
|
"io"
|
||||||
"io/fs"
|
"io/fs"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
type RemoteFS struct {
|
type RemoteFS struct {
|
||||||
indexConfig *SpecIndexConfig
|
indexConfig *SpecIndexConfig
|
||||||
rootURL string
|
rootURL string
|
||||||
rootURLParsed *url.URL
|
rootURLParsed *url.URL
|
||||||
RemoteHandlerFunc utils.RemoteURLHandler
|
RemoteHandlerFunc utils.RemoteURLHandler
|
||||||
Files syncmap.Map
|
Files syncmap.Map
|
||||||
ProcessingFiles syncmap.Map
|
ProcessingFiles syncmap.Map
|
||||||
FetchTime int64
|
FetchTime int64
|
||||||
FetchChannel chan *RemoteFile
|
FetchChannel chan *RemoteFile
|
||||||
remoteErrors []error
|
remoteErrors []error
|
||||||
logger *slog.Logger
|
logger *slog.Logger
|
||||||
defaultClient *http.Client
|
extractedFiles map[string]RolodexFile
|
||||||
extractedFiles map[string]RolodexFile
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type RemoteFile struct {
|
type RemoteFile struct {
|
||||||
filename string
|
filename string
|
||||||
name string
|
name string
|
||||||
extension FileExtension
|
extension FileExtension
|
||||||
data []byte
|
data []byte
|
||||||
fullPath string
|
fullPath string
|
||||||
URL *url.URL
|
URL *url.URL
|
||||||
lastModified time.Time
|
lastModified time.Time
|
||||||
seekingErrors []error
|
seekingErrors []error
|
||||||
index *SpecIndex
|
index *SpecIndex
|
||||||
parsed *yaml.Node
|
parsed *yaml.Node
|
||||||
offset int64
|
offset int64
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *RemoteFile) GetFileName() string {
|
func (f *RemoteFile) GetFileName() string {
|
||||||
return f.filename
|
return f.filename
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *RemoteFile) GetContent() string {
|
func (f *RemoteFile) GetContent() string {
|
||||||
return string(f.data)
|
return string(f.data)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *RemoteFile) GetContentAsYAMLNode() (*yaml.Node, error) {
|
func (f *RemoteFile) GetContentAsYAMLNode() (*yaml.Node, error) {
|
||||||
if f.parsed != nil {
|
if f.parsed != nil {
|
||||||
return f.parsed, nil
|
return f.parsed, nil
|
||||||
}
|
}
|
||||||
if f.index != nil && f.index.root != nil {
|
if f.index != nil && f.index.root != nil {
|
||||||
return f.index.root, nil
|
return f.index.root, nil
|
||||||
}
|
}
|
||||||
if f.data == nil {
|
if f.data == nil {
|
||||||
return nil, fmt.Errorf("no data to parse for file: %s", f.fullPath)
|
return nil, fmt.Errorf("no data to parse for file: %s", f.fullPath)
|
||||||
}
|
}
|
||||||
var root yaml.Node
|
var root yaml.Node
|
||||||
err := yaml.Unmarshal(f.data, &root)
|
err := yaml.Unmarshal(f.data, &root)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if f.index != nil && f.index.root == nil {
|
if f.index != nil && f.index.root == nil {
|
||||||
f.index.root = &root
|
f.index.root = &root
|
||||||
}
|
}
|
||||||
f.parsed = &root
|
f.parsed = &root
|
||||||
return &root, nil
|
return &root, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *RemoteFile) GetFileExtension() FileExtension {
|
func (f *RemoteFile) GetFileExtension() FileExtension {
|
||||||
return f.extension
|
return f.extension
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *RemoteFile) GetLastModified() time.Time {
|
func (f *RemoteFile) GetLastModified() time.Time {
|
||||||
return f.lastModified
|
return f.lastModified
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *RemoteFile) GetErrors() []error {
|
func (f *RemoteFile) GetErrors() []error {
|
||||||
return f.seekingErrors
|
return f.seekingErrors
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *RemoteFile) GetFullPath() string {
|
func (f *RemoteFile) GetFullPath() string {
|
||||||
return f.fullPath
|
return f.fullPath
|
||||||
}
|
}
|
||||||
|
|
||||||
// fs.FileInfo interfaces
|
// fs.FileInfo interfaces
|
||||||
|
|
||||||
func (f *RemoteFile) Name() string {
|
func (f *RemoteFile) Name() string {
|
||||||
return f.name
|
return f.name
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *RemoteFile) Size() int64 {
|
func (f *RemoteFile) Size() int64 {
|
||||||
return int64(len(f.data))
|
return int64(len(f.data))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *RemoteFile) Mode() fs.FileMode {
|
func (f *RemoteFile) Mode() fs.FileMode {
|
||||||
return fs.FileMode(0)
|
return fs.FileMode(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *RemoteFile) ModTime() time.Time {
|
func (f *RemoteFile) ModTime() time.Time {
|
||||||
return f.lastModified
|
return f.lastModified
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *RemoteFile) IsDir() bool {
|
func (f *RemoteFile) IsDir() bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// fs.File interfaces
|
// fs.File interfaces
|
||||||
|
|
||||||
func (f *RemoteFile) Sys() interface{} {
|
func (f *RemoteFile) Sys() interface{} {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *RemoteFile) Close() error {
|
func (f *RemoteFile) Close() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
func (f *RemoteFile) Stat() (fs.FileInfo, error) {
|
func (f *RemoteFile) Stat() (fs.FileInfo, error) {
|
||||||
return f, nil
|
return f, nil
|
||||||
}
|
}
|
||||||
func (f *RemoteFile) Read(b []byte) (int, error) {
|
func (f *RemoteFile) Read(b []byte) (int, error) {
|
||||||
if f.offset >= int64(len(f.data)) {
|
if f.offset >= int64(len(f.data)) {
|
||||||
return 0, io.EOF
|
return 0, io.EOF
|
||||||
}
|
}
|
||||||
if f.offset < 0 {
|
if f.offset < 0 {
|
||||||
return 0, &fs.PathError{Op: "read", Path: f.name, Err: fs.ErrInvalid}
|
return 0, &fs.PathError{Op: "read", Path: f.name, Err: fs.ErrInvalid}
|
||||||
}
|
}
|
||||||
n := copy(b, f.data[f.offset:])
|
n := copy(b, f.data[f.offset:])
|
||||||
f.offset += int64(n)
|
f.offset += int64(n)
|
||||||
return n, nil
|
return n, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *RemoteFile) Index(config *SpecIndexConfig) (*SpecIndex, error) {
|
func (f *RemoteFile) Index(config *SpecIndexConfig) (*SpecIndex, error) {
|
||||||
|
|
||||||
if f.index != nil {
|
if f.index != nil {
|
||||||
return f.index, nil
|
return f.index, nil
|
||||||
}
|
}
|
||||||
content := f.data
|
content := f.data
|
||||||
|
|
||||||
// first, we must parse the content of the file
|
// first, we must parse the content of the file
|
||||||
info, err := datamodel.ExtractSpecInfoWithDocumentCheck(content, true)
|
info, err := datamodel.ExtractSpecInfoWithDocumentCheck(content, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
index := NewSpecIndexWithConfig(info.RootNode, config)
|
index := NewSpecIndexWithConfig(info.RootNode, config)
|
||||||
|
|
||||||
index.specAbsolutePath = config.SpecAbsolutePath
|
index.specAbsolutePath = config.SpecAbsolutePath
|
||||||
f.index = index
|
f.index = index
|
||||||
return index, nil
|
return index, nil
|
||||||
}
|
}
|
||||||
func (f *RemoteFile) GetIndex() *SpecIndex {
|
func (f *RemoteFile) GetIndex() *SpecIndex {
|
||||||
return f.index
|
return f.index
|
||||||
}
|
}
|
||||||
|
|
||||||
type FileExtension int
|
type FileExtension int
|
||||||
|
|
||||||
const (
|
const (
|
||||||
YAML FileExtension = iota
|
YAML FileExtension = iota
|
||||||
JSON
|
JSON
|
||||||
UNSUPPORTED
|
UNSUPPORTED
|
||||||
)
|
)
|
||||||
|
|
||||||
func NewRemoteFSWithConfig(specIndexConfig *SpecIndexConfig) (*RemoteFS, error) {
|
func NewRemoteFSWithConfig(specIndexConfig *SpecIndexConfig) (*RemoteFS, error) {
|
||||||
if specIndexConfig == nil {
|
if specIndexConfig == nil {
|
||||||
return nil, errors.New("no spec index config provided")
|
return nil, errors.New("no spec index config provided")
|
||||||
}
|
}
|
||||||
remoteRootURL := specIndexConfig.BaseURL
|
remoteRootURL := specIndexConfig.BaseURL
|
||||||
log := specIndexConfig.Logger
|
log := specIndexConfig.Logger
|
||||||
if log == nil {
|
if log == nil {
|
||||||
log = slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{
|
log = slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{
|
||||||
Level: slog.LevelError,
|
Level: slog.LevelError,
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
rfs := &RemoteFS{
|
rfs := &RemoteFS{
|
||||||
indexConfig: specIndexConfig,
|
indexConfig: specIndexConfig,
|
||||||
logger: log,
|
logger: log,
|
||||||
rootURLParsed: remoteRootURL,
|
rootURLParsed: remoteRootURL,
|
||||||
FetchChannel: make(chan *RemoteFile),
|
FetchChannel: make(chan *RemoteFile),
|
||||||
}
|
}
|
||||||
if remoteRootURL != nil {
|
if remoteRootURL != nil {
|
||||||
rfs.rootURL = remoteRootURL.String()
|
rfs.rootURL = remoteRootURL.String()
|
||||||
}
|
}
|
||||||
if specIndexConfig.RemoteURLHandler != nil {
|
if specIndexConfig.RemoteURLHandler != nil {
|
||||||
rfs.RemoteHandlerFunc = specIndexConfig.RemoteURLHandler
|
rfs.RemoteHandlerFunc = specIndexConfig.RemoteURLHandler
|
||||||
} else {
|
} else {
|
||||||
// default http client
|
// default http client
|
||||||
client := &http.Client{
|
client := &http.Client{
|
||||||
Timeout: time.Second * 120,
|
Timeout: time.Second * 120,
|
||||||
}
|
}
|
||||||
rfs.RemoteHandlerFunc = func(url string) (*http.Response, error) {
|
rfs.RemoteHandlerFunc = func(url string) (*http.Response, error) {
|
||||||
return client.Get(url)
|
return client.Get(url)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return rfs, nil
|
return rfs, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewRemoteFSWithRootURL(rootURL string) (*RemoteFS, error) {
|
func NewRemoteFSWithRootURL(rootURL string) (*RemoteFS, error) {
|
||||||
remoteRootURL, err := url.Parse(rootURL)
|
remoteRootURL, err := url.Parse(rootURL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
config := CreateOpenAPIIndexConfig()
|
config := CreateOpenAPIIndexConfig()
|
||||||
config.BaseURL = remoteRootURL
|
config.BaseURL = remoteRootURL
|
||||||
return NewRemoteFSWithConfig(config)
|
return NewRemoteFSWithConfig(config)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *RemoteFS) SetRemoteHandlerFunc(handlerFunc utils.RemoteURLHandler) {
|
func (i *RemoteFS) SetRemoteHandlerFunc(handlerFunc utils.RemoteURLHandler) {
|
||||||
i.RemoteHandlerFunc = handlerFunc
|
i.RemoteHandlerFunc = handlerFunc
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *RemoteFS) SetIndexConfig(config *SpecIndexConfig) {
|
func (i *RemoteFS) SetIndexConfig(config *SpecIndexConfig) {
|
||||||
i.indexConfig = config
|
i.indexConfig = config
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *RemoteFS) GetFiles() map[string]RolodexFile {
|
func (i *RemoteFS) GetFiles() map[string]RolodexFile {
|
||||||
files := make(map[string]RolodexFile)
|
files := make(map[string]RolodexFile)
|
||||||
i.Files.Range(func(key, value interface{}) bool {
|
i.Files.Range(func(key, value interface{}) bool {
|
||||||
files[key.(string)] = value.(*RemoteFile)
|
files[key.(string)] = value.(*RemoteFile)
|
||||||
return true
|
return true
|
||||||
})
|
})
|
||||||
i.extractedFiles = files
|
i.extractedFiles = files
|
||||||
return files
|
return files
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *RemoteFS) GetErrors() []error {
|
func (i *RemoteFS) GetErrors() []error {
|
||||||
return i.remoteErrors
|
return i.remoteErrors
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *RemoteFS) Open(remoteURL string) (fs.File, error) {
|
func (i *RemoteFS) Open(remoteURL string) (fs.File, error) {
|
||||||
|
|
||||||
if i.indexConfig != nil && !i.indexConfig.AllowRemoteLookup {
|
if i.indexConfig != nil && !i.indexConfig.AllowRemoteLookup {
|
||||||
return nil, fmt.Errorf("remote lookup for '%s' is not allowed, please set "+
|
return nil, fmt.Errorf("remote lookup for '%s' is not allowed, please set "+
|
||||||
"AllowRemoteLookup to true as part of the index configuration", remoteURL)
|
"AllowRemoteLookup to true as part of the index configuration", remoteURL)
|
||||||
}
|
}
|
||||||
|
|
||||||
remoteParsedURL, err := url.Parse(remoteURL)
|
remoteParsedURL, err := url.Parse(remoteURL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
remoteParsedURLOriginal, _ := url.Parse(remoteURL)
|
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 {
|
||||||
return r.(*RemoteFile), nil
|
return r.(*RemoteFile), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// if we're processing, we need to block and wait for the file to be processed
|
// if we're processing, we need to block and wait for the file to be processed
|
||||||
// try path first
|
// try path first
|
||||||
if _, ok := i.ProcessingFiles.Load(remoteParsedURL.Path); ok {
|
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
|
// 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.
|
// can we block threads.
|
||||||
if runtime.GOMAXPROCS(-1) > 2 {
|
if runtime.GOMAXPROCS(-1) > 2 {
|
||||||
i.logger.Debug("waiting for existing fetch to complete", "file", remoteURL, "remoteURL", remoteParsedURL.String())
|
i.logger.Debug("waiting for existing fetch to complete", "file", remoteURL, "remoteURL", remoteParsedURL.String())
|
||||||
|
|
||||||
f := make(chan *RemoteFile)
|
f := make(chan *RemoteFile)
|
||||||
fwait := func(path string, c chan *RemoteFile) {
|
fwait := func(path string, c chan *RemoteFile) {
|
||||||
for {
|
for {
|
||||||
if wf, ko := i.Files.Load(remoteParsedURL.Path); ko {
|
if wf, ko := i.Files.Load(remoteParsedURL.Path); ko {
|
||||||
c <- wf.(*RemoteFile)
|
c <- wf.(*RemoteFile)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
go fwait(remoteParsedURL.Path, f)
|
go fwait(remoteParsedURL.Path, f)
|
||||||
return <-f, nil
|
return <-f, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// add to processing
|
// add to processing
|
||||||
i.ProcessingFiles.Store(remoteParsedURL.Path, true)
|
i.ProcessingFiles.Store(remoteParsedURL.Path, true)
|
||||||
|
|
||||||
fileExt := ExtractFileType(remoteParsedURL.Path)
|
fileExt := ExtractFileType(remoteParsedURL.Path)
|
||||||
|
|
||||||
if fileExt == UNSUPPORTED {
|
if fileExt == UNSUPPORTED {
|
||||||
return nil, &fs.PathError{Op: "open", Path: remoteURL, Err: fs.ErrInvalid}
|
return nil, &fs.PathError{Op: "open", Path: remoteURL, Err: fs.ErrInvalid}
|
||||||
}
|
}
|
||||||
|
|
||||||
// if the remote URL is absolute (http:// or https://), and we have a rootURL defined, we need to override
|
// if the remote URL is absolute (http:// or https://), and we have a rootURL defined, we need to override
|
||||||
// the host being defined by this URL, and use the rootURL instead, but keep the path.
|
// the host being defined by this URL, and use the rootURL instead, but keep the path.
|
||||||
if i.rootURLParsed != nil {
|
if i.rootURLParsed != nil {
|
||||||
remoteParsedURL.Host = i.rootURLParsed.Host
|
remoteParsedURL.Host = i.rootURLParsed.Host
|
||||||
remoteParsedURL.Scheme = i.rootURLParsed.Scheme
|
remoteParsedURL.Scheme = i.rootURLParsed.Scheme
|
||||||
if !filepath.IsAbs(remoteParsedURL.Path) {
|
if !filepath.IsAbs(remoteParsedURL.Path) {
|
||||||
remoteParsedURL.Path = filepath.Join(i.rootURLParsed.Path, remoteParsedURL.Path)
|
remoteParsedURL.Path = filepath.Join(i.rootURLParsed.Path, remoteParsedURL.Path)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
i.logger.Debug("loading remote file", "file", remoteURL, "remoteURL", remoteParsedURL.String())
|
i.logger.Debug("loading remote file", "file", remoteURL, "remoteURL", remoteParsedURL.String())
|
||||||
|
|
||||||
response, clientErr := i.RemoteHandlerFunc(remoteParsedURL.String())
|
response, clientErr := i.RemoteHandlerFunc(remoteParsedURL.String())
|
||||||
if clientErr != nil {
|
if clientErr != nil {
|
||||||
|
|
||||||
i.remoteErrors = append(i.remoteErrors, clientErr)
|
i.remoteErrors = append(i.remoteErrors, clientErr)
|
||||||
// remove from processing
|
// remove from processing
|
||||||
i.ProcessingFiles.Delete(remoteParsedURL.Path)
|
i.ProcessingFiles.Delete(remoteParsedURL.Path)
|
||||||
if response != nil {
|
if response != nil {
|
||||||
i.logger.Error("client error", "error", clientErr, "status", response.StatusCode)
|
i.logger.Error("client error", "error", clientErr, "status", response.StatusCode)
|
||||||
} else {
|
} else {
|
||||||
i.logger.Error("client error", "error", clientErr.Error())
|
i.logger.Error("client error", "error", clientErr.Error())
|
||||||
}
|
}
|
||||||
return nil, clientErr
|
return nil, clientErr
|
||||||
}
|
}
|
||||||
if response == nil {
|
if response == nil {
|
||||||
return nil, fmt.Errorf("empty response from remote URL: %s", remoteParsedURL.String())
|
return nil, fmt.Errorf("empty response from remote URL: %s", remoteParsedURL.String())
|
||||||
}
|
}
|
||||||
responseBytes, readError := io.ReadAll(response.Body)
|
responseBytes, readError := io.ReadAll(response.Body)
|
||||||
if readError != nil {
|
if readError != nil {
|
||||||
|
|
||||||
// remove from processing
|
// remove from processing
|
||||||
i.ProcessingFiles.Delete(remoteParsedURL.Path)
|
i.ProcessingFiles.Delete(remoteParsedURL.Path)
|
||||||
|
|
||||||
return nil, fmt.Errorf("error reading bytes from remote file '%s': [%s]",
|
return nil, fmt.Errorf("error reading bytes from remote file '%s': [%s]",
|
||||||
remoteParsedURL.String(), readError.Error())
|
remoteParsedURL.String(), readError.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
if response.StatusCode >= 400 {
|
if response.StatusCode >= 400 {
|
||||||
|
|
||||||
// remove from processing
|
// remove from processing
|
||||||
i.ProcessingFiles.Delete(remoteParsedURL.Path)
|
i.ProcessingFiles.Delete(remoteParsedURL.Path)
|
||||||
|
|
||||||
i.logger.Error("unable to fetch remote document",
|
i.logger.Error("unable to fetch remote document",
|
||||||
"file", remoteParsedURL.Path, "status", response.StatusCode, "resp", string(responseBytes))
|
"file", remoteParsedURL.Path, "status", response.StatusCode, "resp", string(responseBytes))
|
||||||
return nil, fmt.Errorf("unable to fetch remote document: %s", string(responseBytes))
|
return nil, fmt.Errorf("unable to fetch remote document: %s", string(responseBytes))
|
||||||
}
|
}
|
||||||
|
|
||||||
absolutePath, _ := filepath.Abs(remoteParsedURL.Path)
|
absolutePath, _ := filepath.Abs(remoteParsedURL.Path)
|
||||||
|
|
||||||
// extract last modified from response
|
// extract last modified from response
|
||||||
lastModified := response.Header.Get("Last-Modified")
|
lastModified := response.Header.Get("Last-Modified")
|
||||||
|
|
||||||
// parse the last modified date into a time object
|
// parse the last modified date into a time object
|
||||||
lastModifiedTime, parseErr := time.Parse(time.RFC1123, lastModified)
|
lastModifiedTime, parseErr := time.Parse(time.RFC1123, lastModified)
|
||||||
|
|
||||||
if parseErr != nil {
|
if parseErr != nil {
|
||||||
// can't extract last modified, so use now
|
// can't extract last modified, so use now
|
||||||
lastModifiedTime = time.Now()
|
lastModifiedTime = time.Now()
|
||||||
}
|
}
|
||||||
|
|
||||||
filename := filepath.Base(remoteParsedURL.Path)
|
filename := filepath.Base(remoteParsedURL.Path)
|
||||||
|
|
||||||
remoteFile := &RemoteFile{
|
remoteFile := &RemoteFile{
|
||||||
filename: filename,
|
filename: filename,
|
||||||
name: remoteParsedURL.Path,
|
name: remoteParsedURL.Path,
|
||||||
extension: fileExt,
|
extension: fileExt,
|
||||||
data: responseBytes,
|
data: responseBytes,
|
||||||
fullPath: absolutePath,
|
fullPath: absolutePath,
|
||||||
URL: remoteParsedURL,
|
URL: remoteParsedURL,
|
||||||
lastModified: lastModifiedTime,
|
lastModified: lastModifiedTime,
|
||||||
}
|
}
|
||||||
|
|
||||||
copiedCfg := *i.indexConfig
|
copiedCfg := *i.indexConfig
|
||||||
|
|
||||||
newBase := fmt.Sprintf("%s://%s%s", remoteParsedURLOriginal.Scheme, remoteParsedURLOriginal.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)
|
||||||
|
|
||||||
if newBaseURL != nil {
|
if newBaseURL != nil {
|
||||||
copiedCfg.BaseURL = newBaseURL
|
copiedCfg.BaseURL = newBaseURL
|
||||||
}
|
}
|
||||||
copiedCfg.SpecAbsolutePath = remoteParsedURL.String()
|
copiedCfg.SpecAbsolutePath = remoteParsedURL.String()
|
||||||
|
|
||||||
if len(remoteFile.data) > 0 {
|
if len(remoteFile.data) > 0 {
|
||||||
i.logger.Debug("successfully loaded file", "file", absolutePath)
|
i.logger.Debug("successfully loaded file", "file", absolutePath)
|
||||||
}
|
}
|
||||||
//i.seekRelatives(remoteFile)
|
//i.seekRelatives(remoteFile)
|
||||||
// remove from processing
|
// remove from processing
|
||||||
i.ProcessingFiles.Delete(remoteParsedURL.Path)
|
i.ProcessingFiles.Delete(remoteParsedURL.Path)
|
||||||
i.Files.Store(absolutePath, remoteFile)
|
i.Files.Store(absolutePath, remoteFile)
|
||||||
|
|
||||||
idx, idxError := remoteFile.Index(&copiedCfg)
|
idx, idxError := remoteFile.Index(&copiedCfg)
|
||||||
|
|
||||||
if idxError != nil && idx == nil {
|
if idxError != nil && idx == nil {
|
||||||
i.remoteErrors = append(i.remoteErrors, idxError)
|
i.remoteErrors = append(i.remoteErrors, idxError)
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
// for each index, we need a resolver
|
// for each index, we need a resolver
|
||||||
resolver := NewResolver(idx)
|
resolver := NewResolver(idx)
|
||||||
idx.resolver = resolver
|
idx.resolver = resolver
|
||||||
idx.BuildIndex()
|
idx.BuildIndex()
|
||||||
}
|
}
|
||||||
return remoteFile, errors.Join(i.remoteErrors...)
|
return remoteFile, errors.Join(i.remoteErrors...)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,383 +4,382 @@
|
|||||||
package index
|
package index
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
"net/url"
|
"net/url"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
var test_httpClient = &http.Client{Timeout: time.Duration(60) * time.Second}
|
var test_httpClient = &http.Client{Timeout: time.Duration(60) * time.Second}
|
||||||
|
|
||||||
func test_buildServer() *httptest.Server {
|
func test_buildServer() *httptest.Server {
|
||||||
return httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
|
return httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
|
||||||
if req.URL.String() == "/file1.yaml" {
|
if req.URL.String() == "/file1.yaml" {
|
||||||
rw.Header().Set("Last-Modified", "Wed, 21 Oct 2015 07:28:00 GMT")
|
rw.Header().Set("Last-Modified", "Wed, 21 Oct 2015 07:28:00 GMT")
|
||||||
_, _ = rw.Write([]byte(`"$ref": "./deeper/file2.yaml#/components/schemas/Pet"`))
|
_, _ = rw.Write([]byte(`"$ref": "./deeper/file2.yaml#/components/schemas/Pet"`))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if req.URL.String() == "/deeper/file2.yaml" {
|
if req.URL.String() == "/deeper/file2.yaml" {
|
||||||
rw.Header().Set("Last-Modified", "Wed, 21 Oct 2015 08:28:00 GMT")
|
rw.Header().Set("Last-Modified", "Wed, 21 Oct 2015 08:28:00 GMT")
|
||||||
_, _ = rw.Write([]byte(`"$ref": "/deeper/even_deeper/file3.yaml#/components/schemas/Pet"`))
|
_, _ = rw.Write([]byte(`"$ref": "/deeper/even_deeper/file3.yaml#/components/schemas/Pet"`))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if req.URL.String() == "/deeper/even_deeper/file3.yaml" {
|
if req.URL.String() == "/deeper/even_deeper/file3.yaml" {
|
||||||
rw.Header().Set("Last-Modified", "Wed, 21 Oct 2015 10:28:00 GMT")
|
rw.Header().Set("Last-Modified", "Wed, 21 Oct 2015 10:28:00 GMT")
|
||||||
_, _ = rw.Write([]byte(`"$ref": "../file2.yaml#/components/schemas/Pet"`))
|
_, _ = rw.Write([]byte(`"$ref": "../file2.yaml#/components/schemas/Pet"`))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
rw.Header().Set("Last-Modified", "Wed, 21 Oct 2015 12:28:00 GMT")
|
rw.Header().Set("Last-Modified", "Wed, 21 Oct 2015 12:28:00 GMT")
|
||||||
|
|
||||||
if req.URL.String() == "/deeper/list.yaml" {
|
if req.URL.String() == "/deeper/list.yaml" {
|
||||||
_, _ = rw.Write([]byte(`"$ref": "../file2.yaml"`))
|
_, _ = rw.Write([]byte(`"$ref": "../file2.yaml"`))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if req.URL.String() == "/bag/list.yaml" {
|
if req.URL.String() == "/bag/list.yaml" {
|
||||||
_, _ = rw.Write([]byte(`"$ref": "pocket/list.yaml"\n\n"$ref": "zip/things.yaml"`))
|
_, _ = rw.Write([]byte(`"$ref": "pocket/list.yaml"\n\n"$ref": "zip/things.yaml"`))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if req.URL.String() == "/bag/pocket/list.yaml" {
|
if req.URL.String() == "/bag/pocket/list.yaml" {
|
||||||
_, _ = rw.Write([]byte(`"$ref": "../list.yaml"\n\n"$ref": "../../file2.yaml"`))
|
_, _ = rw.Write([]byte(`"$ref": "../list.yaml"\n\n"$ref": "../../file2.yaml"`))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if req.URL.String() == "/bag/pocket/things.yaml" {
|
if req.URL.String() == "/bag/pocket/things.yaml" {
|
||||||
_, _ = rw.Write([]byte(`"$ref": "list.yaml"`))
|
_, _ = rw.Write([]byte(`"$ref": "list.yaml"`))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if req.URL.String() == "/bag/zip/things.yaml" {
|
if req.URL.String() == "/bag/zip/things.yaml" {
|
||||||
_, _ = rw.Write([]byte(`"$ref": "list.yaml"`))
|
_, _ = rw.Write([]byte(`"$ref": "list.yaml"`))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if req.URL.String() == "/bag/zip/list.yaml" {
|
if req.URL.String() == "/bag/zip/list.yaml" {
|
||||||
_, _ = rw.Write([]byte(`"$ref": "../list.yaml"\n\n"$ref": "../../file1.yaml"\n\n"$ref": "more.yaml""`))
|
_, _ = rw.Write([]byte(`"$ref": "../list.yaml"\n\n"$ref": "../../file1.yaml"\n\n"$ref": "more.yaml""`))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if req.URL.String() == "/bag/zip/more.yaml" {
|
if req.URL.String() == "/bag/zip/more.yaml" {
|
||||||
_, _ = rw.Write([]byte(`"$ref": "../../deeper/list.yaml"\n\n"$ref": "../../bad.yaml"`))
|
_, _ = rw.Write([]byte(`"$ref": "../../deeper/list.yaml"\n\n"$ref": "../../bad.yaml"`))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if req.URL.String() == "/bad.yaml" {
|
if req.URL.String() == "/bad.yaml" {
|
||||||
rw.WriteHeader(http.StatusInternalServerError)
|
rw.WriteHeader(http.StatusInternalServerError)
|
||||||
_, _ = rw.Write([]byte(`"error, cannot do the thing"`))
|
_, _ = rw.Write([]byte(`"error, cannot do the thing"`))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
_, _ = rw.Write([]byte(`OK`))
|
_, _ = rw.Write([]byte(`OK`))
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNewRemoteFS_BasicCheck(t *testing.T) {
|
func TestNewRemoteFS_BasicCheck(t *testing.T) {
|
||||||
|
|
||||||
server := test_buildServer()
|
server := test_buildServer()
|
||||||
defer server.Close()
|
defer server.Close()
|
||||||
|
|
||||||
//remoteFS := NewRemoteFS("https://raw.githubusercontent.com/digitalocean/openapi/main/specification/")
|
//remoteFS := NewRemoteFS("https://raw.githubusercontent.com/digitalocean/openapi/main/specification/")
|
||||||
remoteFS, _ := NewRemoteFSWithRootURL(server.URL)
|
remoteFS, _ := NewRemoteFSWithRootURL(server.URL)
|
||||||
remoteFS.RemoteHandlerFunc = test_httpClient.Get
|
remoteFS.RemoteHandlerFunc = test_httpClient.Get
|
||||||
|
|
||||||
file, err := remoteFS.Open("/file1.yaml")
|
file, err := remoteFS.Open("/file1.yaml")
|
||||||
|
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
bytes, rErr := io.ReadAll(file)
|
bytes, rErr := io.ReadAll(file)
|
||||||
assert.NoError(t, rErr)
|
assert.NoError(t, rErr)
|
||||||
|
|
||||||
stat, _ := file.Stat()
|
stat, _ := file.Stat()
|
||||||
|
|
||||||
assert.Equal(t, "/file1.yaml", stat.Name())
|
assert.Equal(t, "/file1.yaml", stat.Name())
|
||||||
assert.Equal(t, int64(53), stat.Size())
|
assert.Equal(t, int64(53), stat.Size())
|
||||||
assert.Len(t, bytes, 53)
|
assert.Len(t, bytes, 53)
|
||||||
|
|
||||||
lastMod := stat.ModTime()
|
lastMod := stat.ModTime()
|
||||||
assert.Equal(t, "2015-10-21 07:28:00 +0000 GMT", lastMod.String())
|
assert.Equal(t, "2015-10-21 07:28:00 +0000 GMT", lastMod.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNewRemoteFS_BasicCheck_Relative(t *testing.T) {
|
func TestNewRemoteFS_BasicCheck_Relative(t *testing.T) {
|
||||||
|
|
||||||
server := test_buildServer()
|
server := test_buildServer()
|
||||||
defer server.Close()
|
defer server.Close()
|
||||||
|
|
||||||
remoteFS, _ := NewRemoteFSWithRootURL(server.URL)
|
remoteFS, _ := NewRemoteFSWithRootURL(server.URL)
|
||||||
remoteFS.RemoteHandlerFunc = test_httpClient.Get
|
remoteFS.RemoteHandlerFunc = test_httpClient.Get
|
||||||
|
|
||||||
file, err := remoteFS.Open("/deeper/file2.yaml")
|
file, err := remoteFS.Open("/deeper/file2.yaml")
|
||||||
|
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
bytes, rErr := io.ReadAll(file)
|
bytes, rErr := io.ReadAll(file)
|
||||||
assert.NoError(t, rErr)
|
assert.NoError(t, rErr)
|
||||||
|
|
||||||
assert.Len(t, bytes, 64)
|
assert.Len(t, bytes, 64)
|
||||||
|
|
||||||
stat, _ := file.Stat()
|
stat, _ := file.Stat()
|
||||||
|
|
||||||
assert.Equal(t, "/deeper/file2.yaml", stat.Name())
|
assert.Equal(t, "/deeper/file2.yaml", stat.Name())
|
||||||
assert.Equal(t, int64(64), stat.Size())
|
assert.Equal(t, int64(64), stat.Size())
|
||||||
|
|
||||||
lastMod := stat.ModTime()
|
lastMod := stat.ModTime()
|
||||||
assert.Equal(t, "2015-10-21 08:28:00 +0000 GMT", lastMod.String())
|
assert.Equal(t, "2015-10-21 08:28:00 +0000 GMT", lastMod.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNewRemoteFS_BasicCheck_Relative_Deeper(t *testing.T) {
|
func TestNewRemoteFS_BasicCheck_Relative_Deeper(t *testing.T) {
|
||||||
|
|
||||||
server := test_buildServer()
|
server := test_buildServer()
|
||||||
defer server.Close()
|
defer server.Close()
|
||||||
|
|
||||||
cf := CreateOpenAPIIndexConfig()
|
cf := CreateOpenAPIIndexConfig()
|
||||||
u, _ := url.Parse(server.URL)
|
u, _ := url.Parse(server.URL)
|
||||||
cf.BaseURL = u
|
cf.BaseURL = u
|
||||||
|
|
||||||
remoteFS, _ := NewRemoteFSWithConfig(cf)
|
remoteFS, _ := NewRemoteFSWithConfig(cf)
|
||||||
remoteFS.RemoteHandlerFunc = test_httpClient.Get
|
remoteFS.RemoteHandlerFunc = test_httpClient.Get
|
||||||
|
|
||||||
file, err := remoteFS.Open("/deeper/even_deeper/file3.yaml")
|
file, err := remoteFS.Open("/deeper/even_deeper/file3.yaml")
|
||||||
|
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
bytes, rErr := io.ReadAll(file)
|
bytes, rErr := io.ReadAll(file)
|
||||||
assert.NoError(t, rErr)
|
assert.NoError(t, rErr)
|
||||||
|
|
||||||
assert.Len(t, bytes, 47)
|
assert.Len(t, bytes, 47)
|
||||||
|
|
||||||
stat, _ := file.Stat()
|
stat, _ := file.Stat()
|
||||||
|
|
||||||
assert.Equal(t, "/deeper/even_deeper/file3.yaml", stat.Name())
|
assert.Equal(t, "/deeper/even_deeper/file3.yaml", stat.Name())
|
||||||
assert.Equal(t, int64(47), stat.Size())
|
assert.Equal(t, int64(47), stat.Size())
|
||||||
assert.Equal(t, "/deeper/even_deeper/file3.yaml", file.(*RemoteFile).Name())
|
assert.Equal(t, "/deeper/even_deeper/file3.yaml", file.(*RemoteFile).Name())
|
||||||
assert.Equal(t, "file3.yaml", file.(*RemoteFile).GetFileName())
|
assert.Equal(t, "file3.yaml", file.(*RemoteFile).GetFileName())
|
||||||
assert.Len(t, file.(*RemoteFile).GetContent(), 47)
|
assert.Len(t, file.(*RemoteFile).GetContent(), 47)
|
||||||
assert.Equal(t, YAML, file.(*RemoteFile).GetFileExtension())
|
assert.Equal(t, YAML, file.(*RemoteFile).GetFileExtension())
|
||||||
assert.NotNil(t, file.(*RemoteFile).GetLastModified())
|
assert.NotNil(t, file.(*RemoteFile).GetLastModified())
|
||||||
assert.Len(t, file.(*RemoteFile).GetErrors(), 0)
|
assert.Len(t, file.(*RemoteFile).GetErrors(), 0)
|
||||||
assert.Equal(t, "/deeper/even_deeper/file3.yaml", file.(*RemoteFile).GetFullPath())
|
assert.Equal(t, "/deeper/even_deeper/file3.yaml", file.(*RemoteFile).GetFullPath())
|
||||||
assert.False(t, file.(*RemoteFile).IsDir())
|
assert.False(t, file.(*RemoteFile).IsDir())
|
||||||
assert.Nil(t, file.(*RemoteFile).Sys())
|
assert.Nil(t, file.(*RemoteFile).Sys())
|
||||||
assert.Nil(t, file.(*RemoteFile).Close())
|
assert.Nil(t, file.(*RemoteFile).Close())
|
||||||
|
|
||||||
lastMod := stat.ModTime()
|
lastMod := stat.ModTime()
|
||||||
assert.Equal(t, "2015-10-21 10:28:00 +0000 GMT", lastMod.String())
|
assert.Equal(t, "2015-10-21 10:28:00 +0000 GMT", lastMod.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestRemoteFile_NoContent(t *testing.T) {
|
func TestRemoteFile_NoContent(t *testing.T) {
|
||||||
|
|
||||||
rf := &RemoteFile{}
|
rf := &RemoteFile{}
|
||||||
x, y := rf.GetContentAsYAMLNode()
|
x, y := rf.GetContentAsYAMLNode()
|
||||||
assert.Nil(t, x)
|
assert.Nil(t, x)
|
||||||
assert.Error(t, y)
|
assert.Error(t, y)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestRemoteFile_BadContent(t *testing.T) {
|
func TestRemoteFile_BadContent(t *testing.T) {
|
||||||
|
|
||||||
rf := &RemoteFile{data: []byte("bad: data: on: a single: line: makes: for: unhappy: yaml"), index: &SpecIndex{}}
|
rf := &RemoteFile{data: []byte("bad: data: on: a single: line: makes: for: unhappy: yaml"), index: &SpecIndex{}}
|
||||||
x, y := rf.GetContentAsYAMLNode()
|
x, y := rf.GetContentAsYAMLNode()
|
||||||
assert.Nil(t, x)
|
assert.Nil(t, x)
|
||||||
assert.Error(t, y)
|
assert.Error(t, y)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestRemoteFile_GoodContent(t *testing.T) {
|
func TestRemoteFile_GoodContent(t *testing.T) {
|
||||||
|
|
||||||
rf := &RemoteFile{data: []byte("good: data"), index: &SpecIndex{}}
|
rf := &RemoteFile{data: []byte("good: data"), index: &SpecIndex{}}
|
||||||
x, y := rf.GetContentAsYAMLNode()
|
x, y := rf.GetContentAsYAMLNode()
|
||||||
assert.NotNil(t, x)
|
assert.NotNil(t, x)
|
||||||
assert.NoError(t, y)
|
assert.NoError(t, y)
|
||||||
assert.NotNil(t, rf.index.root)
|
assert.NotNil(t, rf.index.root)
|
||||||
|
|
||||||
// bad read
|
// bad read
|
||||||
rf.offset = -1
|
rf.offset = -1
|
||||||
d, err := io.ReadAll(rf)
|
d, err := io.ReadAll(rf)
|
||||||
assert.Empty(t, d)
|
assert.Empty(t, d)
|
||||||
assert.Error(t, err)
|
assert.Error(t, err)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestRemoteFile_Index_AlreadySet(t *testing.T) {
|
func TestRemoteFile_Index_AlreadySet(t *testing.T) {
|
||||||
|
|
||||||
rf := &RemoteFile{data: []byte("good: data"), index: &SpecIndex{}}
|
rf := &RemoteFile{data: []byte("good: data"), index: &SpecIndex{}}
|
||||||
x, y := rf.Index(&SpecIndexConfig{})
|
x, y := rf.Index(&SpecIndexConfig{})
|
||||||
assert.NotNil(t, x)
|
assert.NotNil(t, x)
|
||||||
assert.NoError(t, y)
|
assert.NoError(t, y)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestRemoteFile_Index_BadContent(t *testing.T) {
|
func TestRemoteFile_Index_BadContent(t *testing.T) {
|
||||||
|
|
||||||
rf := &RemoteFile{data: []byte("no: sleep: until: the bugs: weep")}
|
rf := &RemoteFile{data: []byte("no: sleep: until: the bugs: weep")}
|
||||||
x, y := rf.Index(&SpecIndexConfig{})
|
x, y := rf.Index(&SpecIndexConfig{})
|
||||||
assert.Nil(t, x)
|
assert.Nil(t, x)
|
||||||
assert.Error(t, y)
|
assert.Error(t, y)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestRemoteFS_NoConfig(t *testing.T) {
|
func TestRemoteFS_NoConfig(t *testing.T) {
|
||||||
|
|
||||||
x, y := NewRemoteFSWithConfig(nil)
|
x, y := NewRemoteFSWithConfig(nil)
|
||||||
assert.Nil(t, x)
|
assert.Nil(t, x)
|
||||||
assert.Error(t, y)
|
assert.Error(t, y)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestRemoteFS_SetRemoteHandler(t *testing.T) {
|
func TestRemoteFS_SetRemoteHandler(t *testing.T) {
|
||||||
|
|
||||||
h := func(url string) (*http.Response, error) {
|
h := func(url string) (*http.Response, error) {
|
||||||
return nil, errors.New("nope")
|
return nil, errors.New("nope")
|
||||||
}
|
}
|
||||||
cf := CreateClosedAPIIndexConfig()
|
cf := CreateClosedAPIIndexConfig()
|
||||||
cf.RemoteURLHandler = h
|
cf.RemoteURLHandler = h
|
||||||
|
|
||||||
x, y := NewRemoteFSWithConfig(cf)
|
x, y := NewRemoteFSWithConfig(cf)
|
||||||
assert.NotNil(t, x)
|
assert.NotNil(t, x)
|
||||||
assert.NoError(t, y)
|
assert.NoError(t, y)
|
||||||
assert.NotNil(t, x.RemoteHandlerFunc)
|
assert.NotNil(t, x.RemoteHandlerFunc)
|
||||||
|
|
||||||
cf = CreateClosedAPIIndexConfig()
|
assert.NotNil(t, x.RemoteHandlerFunc)
|
||||||
assert.NotNil(t, x.RemoteHandlerFunc)
|
|
||||||
|
|
||||||
x.SetRemoteHandlerFunc(h)
|
x.SetRemoteHandlerFunc(h)
|
||||||
assert.NotNil(t, x.RemoteHandlerFunc)
|
assert.NotNil(t, x.RemoteHandlerFunc)
|
||||||
|
|
||||||
// run the handler
|
// run the handler
|
||||||
i, n := x.RemoteHandlerFunc("http://www.google.com")
|
i, n := x.RemoteHandlerFunc("http://www.google.com")
|
||||||
assert.Nil(t, i)
|
assert.Nil(t, i)
|
||||||
assert.Error(t, n)
|
assert.Error(t, n)
|
||||||
assert.Equal(t, "nope", n.Error())
|
assert.Equal(t, "nope", n.Error())
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestRemoteFS_NoConfigBadURL(t *testing.T) {
|
func TestRemoteFS_NoConfigBadURL(t *testing.T) {
|
||||||
x, y := NewRemoteFSWithRootURL("I am not a URL. I am a potato.: no.... // no.")
|
x, y := NewRemoteFSWithRootURL("I am not a URL. I am a potato.: no.... // no.")
|
||||||
assert.Nil(t, x)
|
assert.Nil(t, x)
|
||||||
assert.Error(t, y)
|
assert.Error(t, y)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNewRemoteFS_Open_NoConfig(t *testing.T) {
|
func TestNewRemoteFS_Open_NoConfig(t *testing.T) {
|
||||||
|
|
||||||
rfs := &RemoteFS{}
|
rfs := &RemoteFS{}
|
||||||
x, y := rfs.Open("https://pb33f.io")
|
x, y := rfs.Open("https://pb33f.io")
|
||||||
assert.Nil(t, x)
|
assert.Nil(t, x)
|
||||||
assert.Error(t, y)
|
assert.Error(t, y)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNewRemoteFS_Open_ConfigNotAllowed(t *testing.T) {
|
func TestNewRemoteFS_Open_ConfigNotAllowed(t *testing.T) {
|
||||||
|
|
||||||
rfs := &RemoteFS{indexConfig: CreateClosedAPIIndexConfig()}
|
rfs := &RemoteFS{indexConfig: CreateClosedAPIIndexConfig()}
|
||||||
x, y := rfs.Open("https://pb33f.io")
|
x, y := rfs.Open("https://pb33f.io")
|
||||||
assert.Nil(t, x)
|
assert.Nil(t, x)
|
||||||
assert.Error(t, y)
|
assert.Error(t, y)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNewRemoteFS_Open_BadURL(t *testing.T) {
|
func TestNewRemoteFS_Open_BadURL(t *testing.T) {
|
||||||
|
|
||||||
rfs := &RemoteFS{indexConfig: CreateOpenAPIIndexConfig()}
|
rfs := &RemoteFS{indexConfig: CreateOpenAPIIndexConfig()}
|
||||||
x, y := rfs.Open("I am not a URL. I am a box of candy.. yum yum yum:: in my tum tum tum")
|
x, y := rfs.Open("I am not a URL. I am a box of candy.. yum yum yum:: in my tum tum tum")
|
||||||
assert.Nil(t, x)
|
assert.Nil(t, x)
|
||||||
assert.Error(t, y)
|
assert.Error(t, y)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNewRemoteFS_RemoteBaseURL_RelativeRequest(t *testing.T) {
|
func TestNewRemoteFS_RemoteBaseURL_RelativeRequest(t *testing.T) {
|
||||||
|
|
||||||
cf := CreateOpenAPIIndexConfig()
|
cf := CreateOpenAPIIndexConfig()
|
||||||
h := func(url string) (*http.Response, error) {
|
h := func(url string) (*http.Response, error) {
|
||||||
return nil, fmt.Errorf("nope, not having it %s", url)
|
return nil, fmt.Errorf("nope, not having it %s", url)
|
||||||
}
|
}
|
||||||
cf.RemoteURLHandler = h
|
cf.RemoteURLHandler = h
|
||||||
|
|
||||||
cf.BaseURL, _ = url.Parse("https://pb33f.io/the/love/machine")
|
cf.BaseURL, _ = url.Parse("https://pb33f.io/the/love/machine")
|
||||||
rfs, _ := NewRemoteFSWithConfig(cf)
|
rfs, _ := NewRemoteFSWithConfig(cf)
|
||||||
|
|
||||||
x, y := rfs.Open("gib/gab/jib/jab.yaml")
|
x, y := rfs.Open("gib/gab/jib/jab.yaml")
|
||||||
assert.Nil(t, x)
|
assert.Nil(t, x)
|
||||||
assert.Error(t, y)
|
assert.Error(t, y)
|
||||||
assert.Equal(t, "nope, not having it https://pb33f.io/the/love/machine/gib/gab/jib/jab.yaml", y.Error())
|
assert.Equal(t, "nope, not having it https://pb33f.io/the/love/machine/gib/gab/jib/jab.yaml", y.Error())
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNewRemoteFS_RemoteBaseURL_BadRequestButContainsBody(t *testing.T) {
|
func TestNewRemoteFS_RemoteBaseURL_BadRequestButContainsBody(t *testing.T) {
|
||||||
|
|
||||||
cf := CreateOpenAPIIndexConfig()
|
cf := CreateOpenAPIIndexConfig()
|
||||||
h := func(url string) (*http.Response, error) {
|
h := func(url string) (*http.Response, error) {
|
||||||
return &http.Response{}, fmt.Errorf("it's bad, but who cares %s", url)
|
return &http.Response{}, fmt.Errorf("it's bad, but who cares %s", url)
|
||||||
}
|
}
|
||||||
cf.RemoteURLHandler = h
|
cf.RemoteURLHandler = h
|
||||||
|
|
||||||
cf.BaseURL, _ = url.Parse("https://pb33f.io/the/love/machine")
|
cf.BaseURL, _ = url.Parse("https://pb33f.io/the/love/machine")
|
||||||
rfs, _ := NewRemoteFSWithConfig(cf)
|
rfs, _ := NewRemoteFSWithConfig(cf)
|
||||||
|
|
||||||
x, y := rfs.Open("/woof.yaml")
|
x, y := rfs.Open("/woof.yaml")
|
||||||
assert.Nil(t, x)
|
assert.Nil(t, x)
|
||||||
assert.Error(t, y)
|
assert.Error(t, y)
|
||||||
assert.Equal(t, "it's bad, but who cares https://pb33f.io/woof.yaml", y.Error())
|
assert.Equal(t, "it's bad, but who cares https://pb33f.io/woof.yaml", y.Error())
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNewRemoteFS_RemoteBaseURL_NoErrorNoResponse(t *testing.T) {
|
func TestNewRemoteFS_RemoteBaseURL_NoErrorNoResponse(t *testing.T) {
|
||||||
|
|
||||||
cf := CreateOpenAPIIndexConfig()
|
cf := CreateOpenAPIIndexConfig()
|
||||||
h := func(url string) (*http.Response, error) {
|
h := func(url string) (*http.Response, error) {
|
||||||
return nil, nil // useless!
|
return nil, nil // useless!
|
||||||
}
|
}
|
||||||
cf.RemoteURLHandler = h
|
cf.RemoteURLHandler = h
|
||||||
|
|
||||||
cf.BaseURL, _ = url.Parse("https://pb33f.io/the/love/machine")
|
cf.BaseURL, _ = url.Parse("https://pb33f.io/the/love/machine")
|
||||||
rfs, _ := NewRemoteFSWithConfig(cf)
|
rfs, _ := NewRemoteFSWithConfig(cf)
|
||||||
|
|
||||||
x, y := rfs.Open("/woof.yaml")
|
x, y := rfs.Open("/woof.yaml")
|
||||||
assert.Nil(t, x)
|
assert.Nil(t, x)
|
||||||
assert.Error(t, y)
|
assert.Error(t, y)
|
||||||
assert.Equal(t, "empty response from remote URL: https://pb33f.io/woof.yaml", y.Error())
|
assert.Equal(t, "empty response from remote URL: https://pb33f.io/woof.yaml", y.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNewRemoteFS_RemoteBaseURL_ReadBodyFail(t *testing.T) {
|
func TestNewRemoteFS_RemoteBaseURL_ReadBodyFail(t *testing.T) {
|
||||||
|
|
||||||
cf := CreateOpenAPIIndexConfig()
|
cf := CreateOpenAPIIndexConfig()
|
||||||
h := func(url string) (*http.Response, error) {
|
h := func(url string) (*http.Response, error) {
|
||||||
r := &http.Response{}
|
r := &http.Response{}
|
||||||
r.Body = &LocalFile{offset: -1} // read will fail.
|
r.Body = &LocalFile{offset: -1} // read will fail.
|
||||||
return r, nil
|
return r, nil
|
||||||
}
|
}
|
||||||
cf.RemoteURLHandler = h
|
cf.RemoteURLHandler = h
|
||||||
|
|
||||||
cf.BaseURL, _ = url.Parse("https://pb33f.io/the/love/machine")
|
cf.BaseURL, _ = url.Parse("https://pb33f.io/the/love/machine")
|
||||||
rfs, _ := NewRemoteFSWithConfig(cf)
|
rfs, _ := NewRemoteFSWithConfig(cf)
|
||||||
|
|
||||||
x, y := rfs.Open("/woof.yaml")
|
x, y := rfs.Open("/woof.yaml")
|
||||||
assert.Nil(t, x)
|
assert.Nil(t, x)
|
||||||
assert.Error(t, y)
|
assert.Error(t, y)
|
||||||
assert.Equal(t, "error reading bytes from remote file 'https://pb33f.io/woof.yaml': "+
|
assert.Equal(t, "error reading bytes from remote file 'https://pb33f.io/woof.yaml': "+
|
||||||
"[read : invalid argument]", y.Error())
|
"[read : invalid argument]", y.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNewRemoteFS_RemoteBaseURL_EmptySpecFailIndex(t *testing.T) {
|
func TestNewRemoteFS_RemoteBaseURL_EmptySpecFailIndex(t *testing.T) {
|
||||||
|
|
||||||
cf := CreateOpenAPIIndexConfig()
|
cf := CreateOpenAPIIndexConfig()
|
||||||
h := func(url string) (*http.Response, error) {
|
h := func(url string) (*http.Response, error) {
|
||||||
r := &http.Response{}
|
r := &http.Response{}
|
||||||
r.Body = &LocalFile{data: []byte{}} // no bytes to read.
|
r.Body = &LocalFile{data: []byte{}} // no bytes to read.
|
||||||
return r, nil
|
return r, nil
|
||||||
}
|
}
|
||||||
cf.RemoteURLHandler = h
|
cf.RemoteURLHandler = h
|
||||||
|
|
||||||
cf.BaseURL, _ = url.Parse("https://pb33f.io/the/love/machine")
|
cf.BaseURL, _ = url.Parse("https://pb33f.io/the/love/machine")
|
||||||
rfs, _ := NewRemoteFSWithConfig(cf)
|
rfs, _ := NewRemoteFSWithConfig(cf)
|
||||||
|
|
||||||
x, y := rfs.Open("/woof.yaml")
|
x, y := rfs.Open("/woof.yaml")
|
||||||
assert.NotNil(t, x)
|
assert.NotNil(t, x)
|
||||||
assert.Error(t, y)
|
assert.Error(t, y)
|
||||||
assert.Equal(t, "there is nothing in the spec, it's empty - so there is nothing to be done", y.Error())
|
assert.Equal(t, "there is nothing in the spec, it's empty - so there is nothing to be done", y.Error())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -650,29 +650,8 @@ func (index *SpecIndex) GetGlobalCallbacksCount() int {
|
|||||||
|
|
||||||
// look through method for callbacks
|
// look through method for callbacks
|
||||||
callbacks, _ := yamlpath.NewPath("$..callbacks")
|
callbacks, _ := yamlpath.NewPath("$..callbacks")
|
||||||
// Channel used to receive the result from doSomething function
|
|
||||||
ch := make(chan string, 1)
|
|
||||||
|
|
||||||
// Create a context with a timeout of 5 seconds
|
|
||||||
ctxTimeout, cancel := context.WithTimeout(context.Background(), time.Millisecond*500)
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
var res []*yaml.Node
|
var res []*yaml.Node
|
||||||
|
res, _ = callbacks.Find(m.Node)
|
||||||
doSomething := func(ctx context.Context, ch chan<- string) {
|
|
||||||
res, _ = callbacks.Find(m.Node)
|
|
||||||
ch <- m.Definition
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start the doSomething function
|
|
||||||
go doSomething(ctxTimeout, ch)
|
|
||||||
|
|
||||||
select {
|
|
||||||
case <-ctxTimeout.Done():
|
|
||||||
fmt.Printf("Callback %d: Context cancelled: %v\n", m.Node.Line, ctxTimeout.Err())
|
|
||||||
case <-ch:
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(res) > 0 {
|
if len(res) > 0 {
|
||||||
for _, callback := range res[0].Content {
|
for _, callback := range res[0].Content {
|
||||||
if utils.IsNodeMap(callback) {
|
if utils.IsNodeMap(callback) {
|
||||||
|
|||||||
Reference in New Issue
Block a user