fix: error handling and vendor
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
This commit is contained in:
41
vendor/github.com/go-openapi/analysis/internal/debug/debug.go
generated
vendored
Normal file
41
vendor/github.com/go-openapi/analysis/internal/debug/debug.go
generated
vendored
Normal file
@ -0,0 +1,41 @@
|
||||
// Copyright 2015 go-swagger maintainers
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package debug
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
)
|
||||
|
||||
var (
|
||||
output = os.Stdout
|
||||
)
|
||||
|
||||
// GetLogger provides a prefix debug logger
|
||||
func GetLogger(prefix string, debug bool) func(string, ...interface{}) {
|
||||
if debug {
|
||||
logger := log.New(output, prefix+":", log.LstdFlags)
|
||||
|
||||
return func(msg string, args ...interface{}) {
|
||||
_, file1, pos1, _ := runtime.Caller(1)
|
||||
logger.Printf("%s:%d: %s", filepath.Base(file1), pos1, fmt.Sprintf(msg, args...))
|
||||
}
|
||||
}
|
||||
|
||||
return func(_ string, _ ...interface{}) {}
|
||||
}
|
87
vendor/github.com/go-openapi/analysis/internal/flatten/normalize/normalize.go
generated
vendored
Normal file
87
vendor/github.com/go-openapi/analysis/internal/flatten/normalize/normalize.go
generated
vendored
Normal file
@ -0,0 +1,87 @@
|
||||
package normalize
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/go-openapi/spec"
|
||||
)
|
||||
|
||||
// RebaseRef rebases a remote ref relative to a base ref.
|
||||
//
|
||||
// NOTE: does not support JSONschema ID for $ref (we assume we are working with swagger specs here).
|
||||
//
|
||||
// NOTE(windows):
|
||||
// * refs are assumed to have been normalized with drive letter lower cased (from go-openapi/spec)
|
||||
// * "/ in paths may appear as escape sequences
|
||||
func RebaseRef(baseRef string, ref string) string {
|
||||
baseRef, _ = url.PathUnescape(baseRef)
|
||||
ref, _ = url.PathUnescape(ref)
|
||||
|
||||
if baseRef == "" || baseRef == "." || strings.HasPrefix(baseRef, "#") {
|
||||
return ref
|
||||
}
|
||||
|
||||
parts := strings.Split(ref, "#")
|
||||
|
||||
baseParts := strings.Split(baseRef, "#")
|
||||
baseURL, _ := url.Parse(baseParts[0])
|
||||
if strings.HasPrefix(ref, "#") {
|
||||
if baseURL.Host == "" {
|
||||
return strings.Join([]string{baseParts[0], parts[1]}, "#")
|
||||
}
|
||||
|
||||
return strings.Join([]string{baseParts[0], parts[1]}, "#")
|
||||
}
|
||||
|
||||
refURL, _ := url.Parse(parts[0])
|
||||
if refURL.Host != "" || filepath.IsAbs(parts[0]) {
|
||||
// not rebasing an absolute path
|
||||
return ref
|
||||
}
|
||||
|
||||
// there is a relative path
|
||||
var basePath string
|
||||
if baseURL.Host != "" {
|
||||
// when there is a host, standard URI rules apply (with "/")
|
||||
baseURL.Path = path.Dir(baseURL.Path)
|
||||
baseURL.Path = path.Join(baseURL.Path, "/"+parts[0])
|
||||
|
||||
return baseURL.String()
|
||||
}
|
||||
|
||||
// this is a local relative path
|
||||
// basePart[0] and parts[0] are local filesystem directories/files
|
||||
basePath = filepath.Dir(baseParts[0])
|
||||
relPath := filepath.Join(basePath, string(filepath.Separator)+parts[0])
|
||||
if len(parts) > 1 {
|
||||
return strings.Join([]string{relPath, parts[1]}, "#")
|
||||
}
|
||||
|
||||
return relPath
|
||||
}
|
||||
|
||||
// Path renders absolute path on remote file refs
|
||||
//
|
||||
// NOTE(windows):
|
||||
// * refs are assumed to have been normalized with drive letter lower cased (from go-openapi/spec)
|
||||
// * "/ in paths may appear as escape sequences
|
||||
func Path(ref spec.Ref, basePath string) string {
|
||||
uri, _ := url.PathUnescape(ref.String())
|
||||
if ref.HasFragmentOnly || filepath.IsAbs(uri) {
|
||||
return uri
|
||||
}
|
||||
|
||||
refURL, _ := url.Parse(uri)
|
||||
if refURL.Host != "" {
|
||||
return uri
|
||||
}
|
||||
|
||||
parts := strings.Split(uri, "#")
|
||||
// BasePath, parts[0] are local filesystem directories, guaranteed to be absolute at this stage
|
||||
parts[0] = filepath.Join(filepath.Dir(basePath), parts[0])
|
||||
|
||||
return strings.Join(parts, "#")
|
||||
}
|
90
vendor/github.com/go-openapi/analysis/internal/flatten/operations/operations.go
generated
vendored
Normal file
90
vendor/github.com/go-openapi/analysis/internal/flatten/operations/operations.go
generated
vendored
Normal file
@ -0,0 +1,90 @@
|
||||
package operations
|
||||
|
||||
import (
|
||||
"path"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/go-openapi/jsonpointer"
|
||||
"github.com/go-openapi/spec"
|
||||
"github.com/go-openapi/swag"
|
||||
)
|
||||
|
||||
// AllOpRefsByRef returns an index of sortable operations
|
||||
func AllOpRefsByRef(specDoc Provider, operationIDs []string) map[string]OpRef {
|
||||
return OpRefsByRef(GatherOperations(specDoc, operationIDs))
|
||||
}
|
||||
|
||||
// OpRefsByRef indexes a map of sortable operations
|
||||
func OpRefsByRef(oprefs map[string]OpRef) map[string]OpRef {
|
||||
result := make(map[string]OpRef, len(oprefs))
|
||||
for _, v := range oprefs {
|
||||
result[v.Ref.String()] = v
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// OpRef is an indexable, sortable operation
|
||||
type OpRef struct {
|
||||
Method string
|
||||
Path string
|
||||
Key string
|
||||
ID string
|
||||
Op *spec.Operation
|
||||
Ref spec.Ref
|
||||
}
|
||||
|
||||
// OpRefs is a sortable collection of operations
|
||||
type OpRefs []OpRef
|
||||
|
||||
func (o OpRefs) Len() int { return len(o) }
|
||||
func (o OpRefs) Swap(i, j int) { o[i], o[j] = o[j], o[i] }
|
||||
func (o OpRefs) Less(i, j int) bool { return o[i].Key < o[j].Key }
|
||||
|
||||
// Provider knows how to collect operations from a spec
|
||||
type Provider interface {
|
||||
Operations() map[string]map[string]*spec.Operation
|
||||
}
|
||||
|
||||
// GatherOperations builds a map of sorted operations from a spec
|
||||
func GatherOperations(specDoc Provider, operationIDs []string) map[string]OpRef {
|
||||
var oprefs OpRefs
|
||||
|
||||
for method, pathItem := range specDoc.Operations() {
|
||||
for pth, operation := range pathItem {
|
||||
vv := *operation
|
||||
oprefs = append(oprefs, OpRef{
|
||||
Key: swag.ToGoName(strings.ToLower(method) + " " + pth),
|
||||
Method: method,
|
||||
Path: pth,
|
||||
ID: vv.ID,
|
||||
Op: &vv,
|
||||
Ref: spec.MustCreateRef("#" + path.Join("/paths", jsonpointer.Escape(pth), method)),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
sort.Sort(oprefs)
|
||||
|
||||
operations := make(map[string]OpRef)
|
||||
for _, opr := range oprefs {
|
||||
nm := opr.ID
|
||||
if nm == "" {
|
||||
nm = opr.Key
|
||||
}
|
||||
|
||||
oo, found := operations[nm]
|
||||
if found && oo.Method != opr.Method && oo.Path != opr.Path {
|
||||
nm = opr.Key
|
||||
}
|
||||
|
||||
if len(operationIDs) == 0 || swag.ContainsStrings(operationIDs, opr.ID) || swag.ContainsStrings(operationIDs, nm) {
|
||||
opr.ID = nm
|
||||
opr.Op.ID = nm
|
||||
operations[nm] = opr
|
||||
}
|
||||
}
|
||||
|
||||
return operations
|
||||
}
|
458
vendor/github.com/go-openapi/analysis/internal/flatten/replace/replace.go
generated
vendored
Normal file
458
vendor/github.com/go-openapi/analysis/internal/flatten/replace/replace.go
generated
vendored
Normal file
@ -0,0 +1,458 @@
|
||||
package replace
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"os"
|
||||
"path"
|
||||
"strconv"
|
||||
|
||||
"github.com/go-openapi/analysis/internal/debug"
|
||||
"github.com/go-openapi/jsonpointer"
|
||||
"github.com/go-openapi/spec"
|
||||
)
|
||||
|
||||
const definitionsPath = "#/definitions"
|
||||
|
||||
var debugLog = debug.GetLogger("analysis/flatten/replace", os.Getenv("SWAGGER_DEBUG") != "")
|
||||
|
||||
// RewriteSchemaToRef replaces a schema with a Ref
|
||||
func RewriteSchemaToRef(sp *spec.Swagger, key string, ref spec.Ref) error {
|
||||
debugLog("rewriting schema to ref for %s with %s", key, ref.String())
|
||||
_, value, err := getPointerFromKey(sp, key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
switch refable := value.(type) {
|
||||
case *spec.Schema:
|
||||
return rewriteParentRef(sp, key, ref)
|
||||
|
||||
case spec.Schema:
|
||||
return rewriteParentRef(sp, key, ref)
|
||||
|
||||
case *spec.SchemaOrArray:
|
||||
if refable.Schema != nil {
|
||||
refable.Schema = &spec.Schema{SchemaProps: spec.SchemaProps{Ref: ref}}
|
||||
}
|
||||
|
||||
case *spec.SchemaOrBool:
|
||||
if refable.Schema != nil {
|
||||
refable.Schema = &spec.Schema{SchemaProps: spec.SchemaProps{Ref: ref}}
|
||||
}
|
||||
case map[string]interface{}: // this happens e.g. if a schema points to an extension unmarshaled as map[string]interface{}
|
||||
return rewriteParentRef(sp, key, ref)
|
||||
default:
|
||||
return fmt.Errorf("no schema with ref found at %s for %T", key, value)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func rewriteParentRef(sp *spec.Swagger, key string, ref spec.Ref) error {
|
||||
parent, entry, pvalue, err := getParentFromKey(sp, key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
debugLog("rewriting holder for %T", pvalue)
|
||||
switch container := pvalue.(type) {
|
||||
case spec.Response:
|
||||
if err := rewriteParentRef(sp, "#"+parent, ref); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
case *spec.Response:
|
||||
container.Schema = &spec.Schema{SchemaProps: spec.SchemaProps{Ref: ref}}
|
||||
|
||||
case *spec.Responses:
|
||||
statusCode, err := strconv.Atoi(entry)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s not a number: %w", key[1:], err)
|
||||
}
|
||||
resp := container.StatusCodeResponses[statusCode]
|
||||
resp.Schema = &spec.Schema{SchemaProps: spec.SchemaProps{Ref: ref}}
|
||||
container.StatusCodeResponses[statusCode] = resp
|
||||
|
||||
case map[string]spec.Response:
|
||||
resp := container[entry]
|
||||
resp.Schema = &spec.Schema{SchemaProps: spec.SchemaProps{Ref: ref}}
|
||||
container[entry] = resp
|
||||
|
||||
case spec.Parameter:
|
||||
if err := rewriteParentRef(sp, "#"+parent, ref); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
case map[string]spec.Parameter:
|
||||
param := container[entry]
|
||||
param.Schema = &spec.Schema{SchemaProps: spec.SchemaProps{Ref: ref}}
|
||||
container[entry] = param
|
||||
|
||||
case []spec.Parameter:
|
||||
idx, err := strconv.Atoi(entry)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s not a number: %w", key[1:], err)
|
||||
}
|
||||
param := container[idx]
|
||||
param.Schema = &spec.Schema{SchemaProps: spec.SchemaProps{Ref: ref}}
|
||||
container[idx] = param
|
||||
|
||||
case spec.Definitions:
|
||||
container[entry] = spec.Schema{SchemaProps: spec.SchemaProps{Ref: ref}}
|
||||
|
||||
case map[string]spec.Schema:
|
||||
container[entry] = spec.Schema{SchemaProps: spec.SchemaProps{Ref: ref}}
|
||||
|
||||
case []spec.Schema:
|
||||
idx, err := strconv.Atoi(entry)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s not a number: %w", key[1:], err)
|
||||
}
|
||||
container[idx] = spec.Schema{SchemaProps: spec.SchemaProps{Ref: ref}}
|
||||
|
||||
case *spec.SchemaOrArray:
|
||||
// NOTE: this is necessarily an array - otherwise, the parent would be *Schema
|
||||
idx, err := strconv.Atoi(entry)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s not a number: %w", key[1:], err)
|
||||
}
|
||||
container.Schemas[idx] = spec.Schema{SchemaProps: spec.SchemaProps{Ref: ref}}
|
||||
|
||||
case spec.SchemaProperties:
|
||||
container[entry] = spec.Schema{SchemaProps: spec.SchemaProps{Ref: ref}}
|
||||
|
||||
case *interface{}:
|
||||
*container = spec.Schema{SchemaProps: spec.SchemaProps{Ref: ref}}
|
||||
|
||||
// NOTE: can't have case *spec.SchemaOrBool = parent in this case is *Schema
|
||||
|
||||
default:
|
||||
return fmt.Errorf("unhandled parent schema rewrite %s (%T)", key, pvalue)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// getPointerFromKey retrieves the content of the JSON pointer "key"
|
||||
func getPointerFromKey(sp interface{}, key string) (string, interface{}, error) {
|
||||
switch sp.(type) {
|
||||
case *spec.Schema:
|
||||
case *spec.Swagger:
|
||||
default:
|
||||
panic("unexpected type used in getPointerFromKey")
|
||||
}
|
||||
if key == "#/" {
|
||||
return "", sp, nil
|
||||
}
|
||||
// unescape chars in key, e.g. "{}" from path params
|
||||
pth, _ := url.PathUnescape(key[1:])
|
||||
ptr, err := jsonpointer.New(pth)
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
|
||||
value, _, err := ptr.Get(sp)
|
||||
if err != nil {
|
||||
debugLog("error when getting key: %s with path: %s", key, pth)
|
||||
|
||||
return "", nil, err
|
||||
}
|
||||
|
||||
return pth, value, nil
|
||||
}
|
||||
|
||||
// getParentFromKey retrieves the container of the JSON pointer "key"
|
||||
func getParentFromKey(sp interface{}, key string) (string, string, interface{}, error) {
|
||||
switch sp.(type) {
|
||||
case *spec.Schema:
|
||||
case *spec.Swagger:
|
||||
default:
|
||||
panic("unexpected type used in getPointerFromKey")
|
||||
}
|
||||
// unescape chars in key, e.g. "{}" from path params
|
||||
pth, _ := url.PathUnescape(key[1:])
|
||||
|
||||
parent, entry := path.Dir(pth), path.Base(pth)
|
||||
debugLog("getting schema holder at: %s, with entry: %s", parent, entry)
|
||||
|
||||
pptr, err := jsonpointer.New(parent)
|
||||
if err != nil {
|
||||
return "", "", nil, err
|
||||
}
|
||||
pvalue, _, err := pptr.Get(sp)
|
||||
if err != nil {
|
||||
return "", "", nil, fmt.Errorf("can't get parent for %s: %w", parent, err)
|
||||
}
|
||||
|
||||
return parent, entry, pvalue, nil
|
||||
}
|
||||
|
||||
// UpdateRef replaces a ref by another one
|
||||
func UpdateRef(sp interface{}, key string, ref spec.Ref) error {
|
||||
switch sp.(type) {
|
||||
case *spec.Schema:
|
||||
case *spec.Swagger:
|
||||
default:
|
||||
panic("unexpected type used in getPointerFromKey")
|
||||
}
|
||||
debugLog("updating ref for %s with %s", key, ref.String())
|
||||
pth, value, err := getPointerFromKey(sp, key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
switch refable := value.(type) {
|
||||
case *spec.Schema:
|
||||
refable.Ref = ref
|
||||
case *spec.SchemaOrArray:
|
||||
if refable.Schema != nil {
|
||||
refable.Schema.Ref = ref
|
||||
}
|
||||
case *spec.SchemaOrBool:
|
||||
if refable.Schema != nil {
|
||||
refable.Schema.Ref = ref
|
||||
}
|
||||
case spec.Schema:
|
||||
debugLog("rewriting holder for %T", refable)
|
||||
_, entry, pvalue, erp := getParentFromKey(sp, key)
|
||||
if erp != nil {
|
||||
return err
|
||||
}
|
||||
switch container := pvalue.(type) {
|
||||
case spec.Definitions:
|
||||
container[entry] = spec.Schema{SchemaProps: spec.SchemaProps{Ref: ref}}
|
||||
|
||||
case map[string]spec.Schema:
|
||||
container[entry] = spec.Schema{SchemaProps: spec.SchemaProps{Ref: ref}}
|
||||
|
||||
case []spec.Schema:
|
||||
idx, err := strconv.Atoi(entry)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s not a number: %w", pth, err)
|
||||
}
|
||||
container[idx] = spec.Schema{SchemaProps: spec.SchemaProps{Ref: ref}}
|
||||
|
||||
case *spec.SchemaOrArray:
|
||||
// NOTE: this is necessarily an array - otherwise, the parent would be *Schema
|
||||
idx, err := strconv.Atoi(entry)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s not a number: %w", pth, err)
|
||||
}
|
||||
container.Schemas[idx] = spec.Schema{SchemaProps: spec.SchemaProps{Ref: ref}}
|
||||
|
||||
case spec.SchemaProperties:
|
||||
container[entry] = spec.Schema{SchemaProps: spec.SchemaProps{Ref: ref}}
|
||||
|
||||
// NOTE: can't have case *spec.SchemaOrBool = parent in this case is *Schema
|
||||
|
||||
default:
|
||||
return fmt.Errorf("unhandled container type at %s: %T", key, value)
|
||||
}
|
||||
|
||||
default:
|
||||
return fmt.Errorf("no schema with ref found at %s for %T", key, value)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpdateRefWithSchema replaces a ref with a schema (i.e. re-inline schema)
|
||||
func UpdateRefWithSchema(sp *spec.Swagger, key string, sch *spec.Schema) error {
|
||||
debugLog("updating ref for %s with schema", key)
|
||||
pth, value, err := getPointerFromKey(sp, key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
switch refable := value.(type) {
|
||||
case *spec.Schema:
|
||||
*refable = *sch
|
||||
case spec.Schema:
|
||||
_, entry, pvalue, erp := getParentFromKey(sp, key)
|
||||
if erp != nil {
|
||||
return err
|
||||
}
|
||||
switch container := pvalue.(type) {
|
||||
case spec.Definitions:
|
||||
container[entry] = *sch
|
||||
|
||||
case map[string]spec.Schema:
|
||||
container[entry] = *sch
|
||||
|
||||
case []spec.Schema:
|
||||
idx, err := strconv.Atoi(entry)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s not a number: %w", pth, err)
|
||||
}
|
||||
container[idx] = *sch
|
||||
|
||||
case *spec.SchemaOrArray:
|
||||
// NOTE: this is necessarily an array - otherwise, the parent would be *Schema
|
||||
idx, err := strconv.Atoi(entry)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s not a number: %w", pth, err)
|
||||
}
|
||||
container.Schemas[idx] = *sch
|
||||
|
||||
case spec.SchemaProperties:
|
||||
container[entry] = *sch
|
||||
|
||||
// NOTE: can't have case *spec.SchemaOrBool = parent in this case is *Schema
|
||||
|
||||
default:
|
||||
return fmt.Errorf("unhandled type for parent of [%s]: %T", key, value)
|
||||
}
|
||||
case *spec.SchemaOrArray:
|
||||
*refable.Schema = *sch
|
||||
// NOTE: can't have case *spec.SchemaOrBool = parent in this case is *Schema
|
||||
case *spec.SchemaOrBool:
|
||||
*refable.Schema = *sch
|
||||
default:
|
||||
return fmt.Errorf("no schema with ref found at %s for %T", key, value)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeepestRefResult holds the results from DeepestRef analysis
|
||||
type DeepestRefResult struct {
|
||||
Ref spec.Ref
|
||||
Schema *spec.Schema
|
||||
Warnings []string
|
||||
}
|
||||
|
||||
// DeepestRef finds the first definition ref, from a cascade of nested refs which are not definitions.
|
||||
// - if no definition is found, returns the deepest ref.
|
||||
// - pointers to external files are expanded
|
||||
//
|
||||
// NOTE: all external $ref's are assumed to be already expanded at this stage.
|
||||
func DeepestRef(sp *spec.Swagger, opts *spec.ExpandOptions, ref spec.Ref) (*DeepestRefResult, error) {
|
||||
if !ref.HasFragmentOnly {
|
||||
// we found an external $ref, which is odd at this stage:
|
||||
// do nothing on external $refs
|
||||
return &DeepestRefResult{Ref: ref}, nil
|
||||
}
|
||||
|
||||
currentRef := ref
|
||||
visited := make(map[string]bool, 64)
|
||||
warnings := make([]string, 0, 2)
|
||||
|
||||
DOWNREF:
|
||||
for currentRef.String() != "" {
|
||||
if path.Dir(currentRef.String()) == definitionsPath {
|
||||
// this is a top-level definition: stop here and return this ref
|
||||
return &DeepestRefResult{Ref: currentRef}, nil
|
||||
}
|
||||
|
||||
if _, beenThere := visited[currentRef.String()]; beenThere {
|
||||
return nil,
|
||||
fmt.Errorf("cannot resolve cyclic chain of pointers under %s", currentRef.String())
|
||||
}
|
||||
|
||||
visited[currentRef.String()] = true
|
||||
value, _, err := currentRef.GetPointer().Get(sp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
switch refable := value.(type) {
|
||||
case *spec.Schema:
|
||||
if refable.Ref.String() == "" {
|
||||
break DOWNREF
|
||||
}
|
||||
currentRef = refable.Ref
|
||||
|
||||
case spec.Schema:
|
||||
if refable.Ref.String() == "" {
|
||||
break DOWNREF
|
||||
}
|
||||
currentRef = refable.Ref
|
||||
|
||||
case *spec.SchemaOrArray:
|
||||
if refable.Schema == nil || refable.Schema != nil && refable.Schema.Ref.String() == "" {
|
||||
break DOWNREF
|
||||
}
|
||||
currentRef = refable.Schema.Ref
|
||||
|
||||
case *spec.SchemaOrBool:
|
||||
if refable.Schema == nil || refable.Schema != nil && refable.Schema.Ref.String() == "" {
|
||||
break DOWNREF
|
||||
}
|
||||
currentRef = refable.Schema.Ref
|
||||
|
||||
case spec.Response:
|
||||
// a pointer points to a schema initially marshalled in responses section...
|
||||
// Attempt to convert this to a schema. If this fails, the spec is invalid
|
||||
asJSON, _ := refable.MarshalJSON()
|
||||
var asSchema spec.Schema
|
||||
|
||||
err := asSchema.UnmarshalJSON(asJSON)
|
||||
if err != nil {
|
||||
return nil,
|
||||
fmt.Errorf("invalid type for resolved JSON pointer %s. Expected a schema a, got: %T (%v)",
|
||||
currentRef.String(), value, err,
|
||||
)
|
||||
}
|
||||
warnings = append(warnings, fmt.Sprintf("found $ref %q (response) interpreted as schema", currentRef.String()))
|
||||
|
||||
if asSchema.Ref.String() == "" {
|
||||
break DOWNREF
|
||||
}
|
||||
currentRef = asSchema.Ref
|
||||
|
||||
case spec.Parameter:
|
||||
// a pointer points to a schema initially marshalled in parameters section...
|
||||
// Attempt to convert this to a schema. If this fails, the spec is invalid
|
||||
asJSON, _ := refable.MarshalJSON()
|
||||
var asSchema spec.Schema
|
||||
if err := asSchema.UnmarshalJSON(asJSON); err != nil {
|
||||
return nil,
|
||||
fmt.Errorf("invalid type for resolved JSON pointer %s. Expected a schema a, got: %T (%v)",
|
||||
currentRef.String(), value, err,
|
||||
)
|
||||
}
|
||||
|
||||
warnings = append(warnings, fmt.Sprintf("found $ref %q (parameter) interpreted as schema", currentRef.String()))
|
||||
|
||||
if asSchema.Ref.String() == "" {
|
||||
break DOWNREF
|
||||
}
|
||||
currentRef = asSchema.Ref
|
||||
|
||||
default:
|
||||
// fallback: attempts to resolve the pointer as a schema
|
||||
if refable == nil {
|
||||
break DOWNREF
|
||||
}
|
||||
|
||||
asJSON, _ := json.Marshal(refable)
|
||||
var asSchema spec.Schema
|
||||
if err := asSchema.UnmarshalJSON(asJSON); err != nil {
|
||||
return nil,
|
||||
fmt.Errorf("unhandled type to resolve JSON pointer %s. Expected a Schema, got: %T (%v)",
|
||||
currentRef.String(), value, err,
|
||||
)
|
||||
}
|
||||
warnings = append(warnings, fmt.Sprintf("found $ref %q (%T) interpreted as schema", currentRef.String(), refable))
|
||||
|
||||
if asSchema.Ref.String() == "" {
|
||||
break DOWNREF
|
||||
}
|
||||
currentRef = asSchema.Ref
|
||||
}
|
||||
}
|
||||
|
||||
// assess what schema we're ending with
|
||||
sch, erv := spec.ResolveRefWithBase(sp, ¤tRef, opts)
|
||||
if erv != nil {
|
||||
return nil, erv
|
||||
}
|
||||
|
||||
if sch == nil {
|
||||
return nil, fmt.Errorf("no schema found at %s", currentRef.String())
|
||||
}
|
||||
|
||||
return &DeepestRefResult{Ref: currentRef, Schema: sch, Warnings: warnings}, nil
|
||||
}
|
29
vendor/github.com/go-openapi/analysis/internal/flatten/schutils/flatten_schema.go
generated
vendored
Normal file
29
vendor/github.com/go-openapi/analysis/internal/flatten/schutils/flatten_schema.go
generated
vendored
Normal file
@ -0,0 +1,29 @@
|
||||
// Package schutils provides tools to save or clone a schema
|
||||
// when flattening a spec.
|
||||
package schutils
|
||||
|
||||
import (
|
||||
"github.com/go-openapi/spec"
|
||||
"github.com/go-openapi/swag"
|
||||
)
|
||||
|
||||
// Save registers a schema as an entry in spec #/definitions
|
||||
func Save(sp *spec.Swagger, name string, schema *spec.Schema) {
|
||||
if schema == nil {
|
||||
return
|
||||
}
|
||||
|
||||
if sp.Definitions == nil {
|
||||
sp.Definitions = make(map[string]spec.Schema, 150)
|
||||
}
|
||||
|
||||
sp.Definitions[name] = *schema
|
||||
}
|
||||
|
||||
// Clone deep-clones a schema
|
||||
func Clone(schema *spec.Schema) *spec.Schema {
|
||||
var sch spec.Schema
|
||||
_ = swag.FromDynamicJSON(schema, &sch)
|
||||
|
||||
return &sch
|
||||
}
|
201
vendor/github.com/go-openapi/analysis/internal/flatten/sortref/keys.go
generated
vendored
Normal file
201
vendor/github.com/go-openapi/analysis/internal/flatten/sortref/keys.go
generated
vendored
Normal file
@ -0,0 +1,201 @@
|
||||
package sortref
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"path"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/go-openapi/jsonpointer"
|
||||
"github.com/go-openapi/spec"
|
||||
)
|
||||
|
||||
const (
|
||||
paths = "paths"
|
||||
responses = "responses"
|
||||
parameters = "parameters"
|
||||
definitions = "definitions"
|
||||
)
|
||||
|
||||
var (
|
||||
ignoredKeys map[string]struct{}
|
||||
validMethods map[string]struct{}
|
||||
)
|
||||
|
||||
func init() {
|
||||
ignoredKeys = map[string]struct{}{
|
||||
"schema": {},
|
||||
"properties": {},
|
||||
"not": {},
|
||||
"anyOf": {},
|
||||
"oneOf": {},
|
||||
}
|
||||
|
||||
validMethods = map[string]struct{}{
|
||||
"GET": {},
|
||||
"HEAD": {},
|
||||
"OPTIONS": {},
|
||||
"PATCH": {},
|
||||
"POST": {},
|
||||
"PUT": {},
|
||||
"DELETE": {},
|
||||
}
|
||||
}
|
||||
|
||||
// Key represent a key item constructed from /-separated segments
|
||||
type Key struct {
|
||||
Segments int
|
||||
Key string
|
||||
}
|
||||
|
||||
// Keys is a sortable collable collection of Keys
|
||||
type Keys []Key
|
||||
|
||||
func (k Keys) Len() int { return len(k) }
|
||||
func (k Keys) Swap(i, j int) { k[i], k[j] = k[j], k[i] }
|
||||
func (k Keys) Less(i, j int) bool {
|
||||
return k[i].Segments > k[j].Segments || (k[i].Segments == k[j].Segments && k[i].Key < k[j].Key)
|
||||
}
|
||||
|
||||
// KeyParts construct a SplitKey with all its /-separated segments decomposed. It is sortable.
|
||||
func KeyParts(key string) SplitKey {
|
||||
var res []string
|
||||
for _, part := range strings.Split(key[1:], "/") {
|
||||
if part != "" {
|
||||
res = append(res, jsonpointer.Unescape(part))
|
||||
}
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
// SplitKey holds of the parts of a /-separated key, so that their location may be determined.
|
||||
type SplitKey []string
|
||||
|
||||
// IsDefinition is true when the split key is in the #/definitions section of a spec
|
||||
func (s SplitKey) IsDefinition() bool {
|
||||
return len(s) > 1 && s[0] == definitions
|
||||
}
|
||||
|
||||
// DefinitionName yields the name of the definition
|
||||
func (s SplitKey) DefinitionName() string {
|
||||
if !s.IsDefinition() {
|
||||
return ""
|
||||
}
|
||||
|
||||
return s[1]
|
||||
}
|
||||
|
||||
func (s SplitKey) isKeyName(i int) bool {
|
||||
if i <= 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
count := 0
|
||||
for idx := i - 1; idx > 0; idx-- {
|
||||
if s[idx] != "properties" {
|
||||
break
|
||||
}
|
||||
count++
|
||||
}
|
||||
|
||||
return count%2 != 0
|
||||
}
|
||||
|
||||
// PartAdder know how to construct the components of a new name
|
||||
type PartAdder func(string) []string
|
||||
|
||||
// BuildName builds a name from segments
|
||||
func (s SplitKey) BuildName(segments []string, startIndex int, adder PartAdder) string {
|
||||
for i, part := range s[startIndex:] {
|
||||
if _, ignored := ignoredKeys[part]; !ignored || s.isKeyName(startIndex+i) {
|
||||
segments = append(segments, adder(part)...)
|
||||
}
|
||||
}
|
||||
|
||||
return strings.Join(segments, " ")
|
||||
}
|
||||
|
||||
// IsOperation is true when the split key is in the operations section
|
||||
func (s SplitKey) IsOperation() bool {
|
||||
return len(s) > 1 && s[0] == paths
|
||||
}
|
||||
|
||||
// IsSharedOperationParam is true when the split key is in the parameters section of a path
|
||||
func (s SplitKey) IsSharedOperationParam() bool {
|
||||
return len(s) > 2 && s[0] == paths && s[2] == parameters
|
||||
}
|
||||
|
||||
// IsSharedParam is true when the split key is in the #/parameters section of a spec
|
||||
func (s SplitKey) IsSharedParam() bool {
|
||||
return len(s) > 1 && s[0] == parameters
|
||||
}
|
||||
|
||||
// IsOperationParam is true when the split key is in the parameters section of an operation
|
||||
func (s SplitKey) IsOperationParam() bool {
|
||||
return len(s) > 3 && s[0] == paths && s[3] == parameters
|
||||
}
|
||||
|
||||
// IsOperationResponse is true when the split key is in the responses section of an operation
|
||||
func (s SplitKey) IsOperationResponse() bool {
|
||||
return len(s) > 3 && s[0] == paths && s[3] == responses
|
||||
}
|
||||
|
||||
// IsSharedResponse is true when the split key is in the #/responses section of a spec
|
||||
func (s SplitKey) IsSharedResponse() bool {
|
||||
return len(s) > 1 && s[0] == responses
|
||||
}
|
||||
|
||||
// IsDefaultResponse is true when the split key is the default response for an operation
|
||||
func (s SplitKey) IsDefaultResponse() bool {
|
||||
return len(s) > 4 && s[0] == paths && s[3] == responses && s[4] == "default"
|
||||
}
|
||||
|
||||
// IsStatusCodeResponse is true when the split key is an operation response with a status code
|
||||
func (s SplitKey) IsStatusCodeResponse() bool {
|
||||
isInt := func() bool {
|
||||
_, err := strconv.Atoi(s[4])
|
||||
|
||||
return err == nil
|
||||
}
|
||||
|
||||
return len(s) > 4 && s[0] == paths && s[3] == responses && isInt()
|
||||
}
|
||||
|
||||
// ResponseName yields either the status code or "Default" for a response
|
||||
func (s SplitKey) ResponseName() string {
|
||||
if s.IsStatusCodeResponse() {
|
||||
code, _ := strconv.Atoi(s[4])
|
||||
|
||||
return http.StatusText(code)
|
||||
}
|
||||
|
||||
if s.IsDefaultResponse() {
|
||||
return "Default"
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
// PathItemRef constructs a $ref object from a split key of the form /{path}/{method}
|
||||
func (s SplitKey) PathItemRef() spec.Ref {
|
||||
if len(s) < 3 {
|
||||
return spec.Ref{}
|
||||
}
|
||||
|
||||
pth, method := s[1], s[2]
|
||||
if _, isValidMethod := validMethods[strings.ToUpper(method)]; !isValidMethod && !strings.HasPrefix(method, "x-") {
|
||||
return spec.Ref{}
|
||||
}
|
||||
|
||||
return spec.MustCreateRef("#" + path.Join("/", paths, jsonpointer.Escape(pth), strings.ToUpper(method)))
|
||||
}
|
||||
|
||||
// PathRef constructs a $ref object from a split key of the form /paths/{reference}
|
||||
func (s SplitKey) PathRef() spec.Ref {
|
||||
if !s.IsOperation() {
|
||||
return spec.Ref{}
|
||||
}
|
||||
|
||||
return spec.MustCreateRef("#" + path.Join("/", paths, jsonpointer.Escape(s[1])))
|
||||
}
|
141
vendor/github.com/go-openapi/analysis/internal/flatten/sortref/sort_ref.go
generated
vendored
Normal file
141
vendor/github.com/go-openapi/analysis/internal/flatten/sortref/sort_ref.go
generated
vendored
Normal file
@ -0,0 +1,141 @@
|
||||
package sortref
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/go-openapi/analysis/internal/flatten/normalize"
|
||||
"github.com/go-openapi/spec"
|
||||
)
|
||||
|
||||
var depthGroupOrder = []string{
|
||||
"sharedParam", "sharedResponse", "sharedOpParam", "opParam", "codeResponse", "defaultResponse", "definition",
|
||||
}
|
||||
|
||||
type mapIterator struct {
|
||||
len int
|
||||
mapIter *reflect.MapIter
|
||||
}
|
||||
|
||||
func (i *mapIterator) Next() bool {
|
||||
return i.mapIter.Next()
|
||||
}
|
||||
|
||||
func (i *mapIterator) Len() int {
|
||||
return i.len
|
||||
}
|
||||
|
||||
func (i *mapIterator) Key() string {
|
||||
return i.mapIter.Key().String()
|
||||
}
|
||||
|
||||
func mustMapIterator(anyMap interface{}) *mapIterator {
|
||||
val := reflect.ValueOf(anyMap)
|
||||
|
||||
return &mapIterator{mapIter: val.MapRange(), len: val.Len()}
|
||||
}
|
||||
|
||||
// DepthFirst sorts a map of anything. It groups keys by category
|
||||
// (shared params, op param, statuscode response, default response, definitions)
|
||||
// sort groups internally by number of parts in the key and lexical names
|
||||
// flatten groups into a single list of keys
|
||||
func DepthFirst(in interface{}) []string {
|
||||
iterator := mustMapIterator(in)
|
||||
sorted := make([]string, 0, iterator.Len())
|
||||
grouped := make(map[string]Keys, iterator.Len())
|
||||
|
||||
for iterator.Next() {
|
||||
k := iterator.Key()
|
||||
split := KeyParts(k)
|
||||
var pk string
|
||||
|
||||
if split.IsSharedOperationParam() {
|
||||
pk = "sharedOpParam"
|
||||
}
|
||||
if split.IsOperationParam() {
|
||||
pk = "opParam"
|
||||
}
|
||||
if split.IsStatusCodeResponse() {
|
||||
pk = "codeResponse"
|
||||
}
|
||||
if split.IsDefaultResponse() {
|
||||
pk = "defaultResponse"
|
||||
}
|
||||
if split.IsDefinition() {
|
||||
pk = "definition"
|
||||
}
|
||||
if split.IsSharedParam() {
|
||||
pk = "sharedParam"
|
||||
}
|
||||
if split.IsSharedResponse() {
|
||||
pk = "sharedResponse"
|
||||
}
|
||||
grouped[pk] = append(grouped[pk], Key{Segments: len(split), Key: k})
|
||||
}
|
||||
|
||||
for _, pk := range depthGroupOrder {
|
||||
res := grouped[pk]
|
||||
sort.Sort(res)
|
||||
|
||||
for _, v := range res {
|
||||
sorted = append(sorted, v.Key)
|
||||
}
|
||||
}
|
||||
|
||||
return sorted
|
||||
}
|
||||
|
||||
// topMostRefs is able to sort refs by hierarchical then lexicographic order,
|
||||
// yielding refs ordered breadth-first.
|
||||
type topmostRefs []string
|
||||
|
||||
func (k topmostRefs) Len() int { return len(k) }
|
||||
func (k topmostRefs) Swap(i, j int) { k[i], k[j] = k[j], k[i] }
|
||||
func (k topmostRefs) Less(i, j int) bool {
|
||||
li, lj := len(strings.Split(k[i], "/")), len(strings.Split(k[j], "/"))
|
||||
if li == lj {
|
||||
return k[i] < k[j]
|
||||
}
|
||||
|
||||
return li < lj
|
||||
}
|
||||
|
||||
// TopmostFirst sorts references by depth
|
||||
func TopmostFirst(refs []string) []string {
|
||||
res := topmostRefs(refs)
|
||||
sort.Sort(res)
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
// RefRevIdx is a reverse index for references
|
||||
type RefRevIdx struct {
|
||||
Ref spec.Ref
|
||||
Keys []string
|
||||
}
|
||||
|
||||
// ReverseIndex builds a reverse index for references in schemas
|
||||
func ReverseIndex(schemas map[string]spec.Ref, basePath string) map[string]RefRevIdx {
|
||||
collected := make(map[string]RefRevIdx)
|
||||
for key, schRef := range schemas {
|
||||
// normalize paths before sorting,
|
||||
// so we get together keys that are from the same external file
|
||||
normalizedPath := normalize.Path(schRef, basePath)
|
||||
|
||||
entry, ok := collected[normalizedPath]
|
||||
if ok {
|
||||
entry.Keys = append(entry.Keys, key)
|
||||
collected[normalizedPath] = entry
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
collected[normalizedPath] = RefRevIdx{
|
||||
Ref: schRef,
|
||||
Keys: []string{key},
|
||||
}
|
||||
}
|
||||
|
||||
return collected
|
||||
}
|
Reference in New Issue
Block a user