Update dependencies and tests
This commit updates the dependencies, vendor files and updates tests to take into account changes to the DB driver. Signed-off-by: Gabriel Adrian Samfira <gsamfira@cloudbasesolutions.com>
This commit is contained in:
parent
069bdd8b6b
commit
97d03dd38d
693 changed files with 86307 additions and 28214 deletions
17
vendor/github.com/go-openapi/analysis/flatten.go
generated
vendored
17
vendor/github.com/go-openapi/analysis/flatten.go
generated
vendored
|
|
@ -267,6 +267,12 @@ func nameInlinedSchemas(opts *FlattenOpts) error {
|
|||
}
|
||||
|
||||
func removeUnused(opts *FlattenOpts) {
|
||||
for removeUnusedSinglePass(opts) {
|
||||
// continue until no unused definition remains
|
||||
}
|
||||
}
|
||||
|
||||
func removeUnusedSinglePass(opts *FlattenOpts) (hasRemoved bool) {
|
||||
expected := make(map[string]struct{})
|
||||
for k := range opts.Swagger().Definitions {
|
||||
expected[path.Join(definitionsPath, jsonpointer.Escape(k))] = struct{}{}
|
||||
|
|
@ -277,6 +283,7 @@ func removeUnused(opts *FlattenOpts) {
|
|||
}
|
||||
|
||||
for k := range expected {
|
||||
hasRemoved = true
|
||||
debugLog("removing unused definition %s", path.Base(k))
|
||||
if opts.Verbose {
|
||||
log.Printf("info: removing unused definition: %s", path.Base(k))
|
||||
|
|
@ -285,6 +292,8 @@ func removeUnused(opts *FlattenOpts) {
|
|||
}
|
||||
|
||||
opts.Spec.reload() // re-analyze
|
||||
|
||||
return hasRemoved
|
||||
}
|
||||
|
||||
func importKnownRef(entry sortref.RefRevIdx, refStr, newName string, opts *FlattenOpts) error {
|
||||
|
|
@ -331,7 +340,7 @@ func importNewRef(entry sortref.RefRevIdx, refStr string, opts *FlattenOpts) err
|
|||
}
|
||||
|
||||
// generate a unique name - isOAIGen means that a naming conflict was resolved by changing the name
|
||||
newName, isOAIGen = uniqifyName(opts.Swagger().Definitions, nameFromRef(entry.Ref))
|
||||
newName, isOAIGen = uniqifyName(opts.Swagger().Definitions, nameFromRef(entry.Ref, opts))
|
||||
debugLog("new name for [%s]: %s - with name conflict:%t", strings.Join(entry.Keys, ", "), newName, isOAIGen)
|
||||
|
||||
opts.flattenContext.resolved[refStr] = newName
|
||||
|
|
@ -649,6 +658,7 @@ func namePointers(opts *FlattenOpts) error {
|
|||
|
||||
refsToReplace := make(map[string]SchemaRef, len(opts.Spec.references.schemas))
|
||||
for k, ref := range opts.Spec.references.allRefs {
|
||||
debugLog("name pointers: %q => %#v", k, ref)
|
||||
if path.Dir(ref.String()) == definitionsPath {
|
||||
// this a ref to a top-level definition: ok
|
||||
continue
|
||||
|
|
@ -766,6 +776,10 @@ func flattenAnonPointer(key string, v SchemaRef, refsToReplace map[string]Schema
|
|||
|
||||
// identifying edge case when the namer did nothing because we point to a non-schema object
|
||||
// no definition is created and we expand the $ref for all callers
|
||||
debugLog("decide what to do with the schema pointed to: asch.IsSimpleSchema=%t, len(callers)=%d, parts.IsSharedParam=%t, parts.IsSharedResponse=%t",
|
||||
asch.IsSimpleSchema, len(callers), parts.IsSharedParam(), parts.IsSharedResponse(),
|
||||
)
|
||||
|
||||
if (!asch.IsSimpleSchema || len(callers) > 1) && !parts.IsSharedParam() && !parts.IsSharedResponse() {
|
||||
debugLog("replace JSON pointer at [%s] by definition: %s", key, v.Ref.String())
|
||||
if err := namer.Name(v.Ref.String(), v.Schema, asch); err != nil {
|
||||
|
|
@ -788,6 +802,7 @@ func flattenAnonPointer(key string, v SchemaRef, refsToReplace map[string]Schema
|
|||
return nil
|
||||
}
|
||||
|
||||
// everything that is a simple schema and not factorizable is expanded
|
||||
debugLog("expand JSON pointer for key=%s", key)
|
||||
|
||||
if err := replace.UpdateRefWithSchema(opts.Swagger(), key, v.Schema); err != nil {
|
||||
|
|
|
|||
37
vendor/github.com/go-openapi/analysis/flatten_name.go
generated
vendored
37
vendor/github.com/go-openapi/analysis/flatten_name.go
generated
vendored
|
|
@ -33,12 +33,14 @@ func (isn *InlineSchemaNamer) Name(key string, schema *spec.Schema, aschema *Ana
|
|||
}
|
||||
|
||||
// create unique name
|
||||
newName, isOAIGen := uniqifyName(isn.Spec.Definitions, swag.ToJSONName(name))
|
||||
mangle := mangler(isn.opts)
|
||||
newName, isOAIGen := uniqifyName(isn.Spec.Definitions, mangle(name))
|
||||
|
||||
// clone schema
|
||||
sch := schutils.Clone(schema)
|
||||
|
||||
// replace values on schema
|
||||
debugLog("rewriting schema to ref: key=%s with new name: %s", key, newName)
|
||||
if err := replace.RewriteSchemaToRef(isn.Spec, key,
|
||||
spec.MustCreateRef(path.Join(definitionsPath, newName))); err != nil {
|
||||
return fmt.Errorf("error while creating definition %q from inline schema: %w", newName, err)
|
||||
|
|
@ -149,13 +151,15 @@ func namesFromKey(parts sortref.SplitKey, aschema *AnalyzedSchema, operations ma
|
|||
startIndex int
|
||||
)
|
||||
|
||||
if parts.IsOperation() {
|
||||
switch {
|
||||
case parts.IsOperation():
|
||||
baseNames, startIndex = namesForOperation(parts, operations)
|
||||
}
|
||||
|
||||
// definitions
|
||||
if parts.IsDefinition() {
|
||||
case parts.IsDefinition():
|
||||
baseNames, startIndex = namesForDefinition(parts)
|
||||
default:
|
||||
// this a non-standard pointer: build a name by concatenating its parts
|
||||
baseNames = [][]string{parts}
|
||||
startIndex = len(baseNames) + 1
|
||||
}
|
||||
|
||||
result := make([]string, 0, len(baseNames))
|
||||
|
|
@ -169,6 +173,7 @@ func namesFromKey(parts sortref.SplitKey, aschema *AnalyzedSchema, operations ma
|
|||
}
|
||||
sort.Strings(result)
|
||||
|
||||
debugLog("names from parts: %v => %v", parts, result)
|
||||
return result
|
||||
}
|
||||
|
||||
|
|
@ -256,10 +261,20 @@ func partAdder(aschema *AnalyzedSchema) sortref.PartAdder {
|
|||
}
|
||||
}
|
||||
|
||||
func nameFromRef(ref spec.Ref) string {
|
||||
func mangler(o *FlattenOpts) func(string) string {
|
||||
if o.KeepNames {
|
||||
return func(in string) string { return in }
|
||||
}
|
||||
|
||||
return swag.ToJSONName
|
||||
}
|
||||
|
||||
func nameFromRef(ref spec.Ref, o *FlattenOpts) string {
|
||||
mangle := mangler(o)
|
||||
|
||||
u := ref.GetURL()
|
||||
if u.Fragment != "" {
|
||||
return swag.ToJSONName(path.Base(u.Fragment))
|
||||
return mangle(path.Base(u.Fragment))
|
||||
}
|
||||
|
||||
if u.Path != "" {
|
||||
|
|
@ -267,14 +282,14 @@ func nameFromRef(ref spec.Ref) string {
|
|||
if bn != "" && bn != "/" {
|
||||
ext := path.Ext(bn)
|
||||
if ext != "" {
|
||||
return swag.ToJSONName(bn[:len(bn)-len(ext)])
|
||||
return mangle(bn[:len(bn)-len(ext)])
|
||||
}
|
||||
|
||||
return swag.ToJSONName(bn)
|
||||
return mangle(bn)
|
||||
}
|
||||
}
|
||||
|
||||
return swag.ToJSONName(strings.ReplaceAll(u.Host, ".", " "))
|
||||
return mangle(strings.ReplaceAll(u.Host, ".", " "))
|
||||
}
|
||||
|
||||
// GenLocation indicates from which section of the specification (models or operations) a definition has been created.
|
||||
|
|
|
|||
1
vendor/github.com/go-openapi/analysis/flatten_options.go
generated
vendored
1
vendor/github.com/go-openapi/analysis/flatten_options.go
generated
vendored
|
|
@ -26,6 +26,7 @@ type FlattenOpts struct {
|
|||
Verbose bool // enable some reporting on possible name conflicts detected
|
||||
RemoveUnused bool // When true, remove unused parameters, responses and definitions after expansion/flattening
|
||||
ContinueOnError bool // Continue when spec expansion issues are found
|
||||
KeepNames bool // Do not attempt to jsonify names from references when flattening
|
||||
|
||||
/* Extra keys */
|
||||
_ struct{} // require keys
|
||||
|
|
|
|||
4
vendor/github.com/go-openapi/analysis/internal/debug/debug.go
generated
vendored
4
vendor/github.com/go-openapi/analysis/internal/debug/debug.go
generated
vendored
|
|
@ -29,7 +29,7 @@ var (
|
|||
// GetLogger provides a prefix debug logger
|
||||
func GetLogger(prefix string, debug bool) func(string, ...interface{}) {
|
||||
if debug {
|
||||
logger := log.New(output, fmt.Sprintf("%s:", prefix), log.LstdFlags)
|
||||
logger := log.New(output, prefix+":", log.LstdFlags)
|
||||
|
||||
return func(msg string, args ...interface{}) {
|
||||
_, file1, pos1, _ := runtime.Caller(1)
|
||||
|
|
@ -37,5 +37,5 @@ func GetLogger(prefix string, debug bool) func(string, ...interface{}) {
|
|||
}
|
||||
}
|
||||
|
||||
return func(msg string, args ...interface{}) {}
|
||||
return func(_ string, _ ...interface{}) {}
|
||||
}
|
||||
|
|
|
|||
38
vendor/github.com/go-openapi/analysis/internal/flatten/replace/replace.go
generated
vendored
38
vendor/github.com/go-openapi/analysis/internal/flatten/replace/replace.go
generated
vendored
|
|
@ -1,6 +1,7 @@
|
|||
package replace
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"os"
|
||||
|
|
@ -40,6 +41,8 @@ func RewriteSchemaToRef(sp *spec.Swagger, key string, ref spec.Ref) error {
|
|||
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)
|
||||
}
|
||||
|
|
@ -120,6 +123,9 @@ func rewriteParentRef(sp *spec.Swagger, key string, ref spec.Ref) error {
|
|||
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:
|
||||
|
|
@ -385,8 +391,9 @@ DOWNREF:
|
|||
err := asSchema.UnmarshalJSON(asJSON)
|
||||
if err != nil {
|
||||
return nil,
|
||||
fmt.Errorf("invalid type for resolved JSON pointer %s. Expected a schema a, got: %T",
|
||||
currentRef.String(), value)
|
||||
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()))
|
||||
|
||||
|
|
@ -402,8 +409,9 @@ DOWNREF:
|
|||
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",
|
||||
currentRef.String(), value)
|
||||
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()))
|
||||
|
|
@ -414,9 +422,25 @@ DOWNREF:
|
|||
currentRef = asSchema.Ref
|
||||
|
||||
default:
|
||||
return nil,
|
||||
fmt.Errorf("unhandled type to resolve JSON pointer %s. Expected a Schema, got: %T",
|
||||
currentRef.String(), value)
|
||||
// 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
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
2
vendor/github.com/go-openapi/analysis/internal/flatten/sortref/keys.go
generated
vendored
2
vendor/github.com/go-openapi/analysis/internal/flatten/sortref/keys.go
generated
vendored
|
|
@ -69,7 +69,7 @@ func KeyParts(key string) SplitKey {
|
|||
return res
|
||||
}
|
||||
|
||||
// SplitKey holds of the parts of a /-separated key, soi that their location may be determined.
|
||||
// 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
|
||||
|
|
|
|||
14
vendor/github.com/go-openapi/analysis/mixin.go
generated
vendored
14
vendor/github.com/go-openapi/analysis/mixin.go
generated
vendored
|
|
@ -53,7 +53,7 @@ import (
|
|||
// collisions.
|
||||
func Mixin(primary *spec.Swagger, mixins ...*spec.Swagger) []string {
|
||||
skipped := make([]string, 0, len(mixins))
|
||||
opIds := getOpIds(primary)
|
||||
opIDs := getOpIDs(primary)
|
||||
initPrimary(primary)
|
||||
|
||||
for i, m := range mixins {
|
||||
|
|
@ -74,7 +74,7 @@ func Mixin(primary *spec.Swagger, mixins ...*spec.Swagger) []string {
|
|||
skipped = append(skipped, mergeDefinitions(primary, m)...)
|
||||
|
||||
// merging paths requires a map of operationIDs to work with
|
||||
skipped = append(skipped, mergePaths(primary, m, opIds, i)...)
|
||||
skipped = append(skipped, mergePaths(primary, m, opIDs, i)...)
|
||||
|
||||
skipped = append(skipped, mergeParameters(primary, m)...)
|
||||
|
||||
|
|
@ -84,9 +84,9 @@ func Mixin(primary *spec.Swagger, mixins ...*spec.Swagger) []string {
|
|||
return skipped
|
||||
}
|
||||
|
||||
// getOpIds extracts all the paths.<path>.operationIds from the given
|
||||
// getOpIDs extracts all the paths.<path>.operationIds from the given
|
||||
// spec and returns them as the keys in a map with 'true' values.
|
||||
func getOpIds(s *spec.Swagger) map[string]bool {
|
||||
func getOpIDs(s *spec.Swagger) map[string]bool {
|
||||
rv := make(map[string]bool)
|
||||
if s.Paths == nil {
|
||||
return rv
|
||||
|
|
@ -179,7 +179,7 @@ func mergeDefinitions(primary *spec.Swagger, m *spec.Swagger) (skipped []string)
|
|||
return
|
||||
}
|
||||
|
||||
func mergePaths(primary *spec.Swagger, m *spec.Swagger, opIds map[string]bool, mixIndex int) (skipped []string) {
|
||||
func mergePaths(primary *spec.Swagger, m *spec.Swagger, opIDs map[string]bool, mixIndex int) (skipped []string) {
|
||||
if m.Paths != nil {
|
||||
for k, v := range m.Paths.Paths {
|
||||
if _, exists := primary.Paths.Paths[k]; exists {
|
||||
|
|
@ -198,10 +198,10 @@ func mergePaths(primary *spec.Swagger, m *spec.Swagger, opIds map[string]bool, m
|
|||
// all the proivded specs are already unique.
|
||||
piops := pathItemOps(v)
|
||||
for _, piop := range piops {
|
||||
if opIds[piop.ID] {
|
||||
if opIDs[piop.ID] {
|
||||
piop.ID = fmt.Sprintf("%v%v%v", piop.ID, "Mixin", mixIndex)
|
||||
}
|
||||
opIds[piop.ID] = true
|
||||
opIDs[piop.ID] = true
|
||||
}
|
||||
primary.Paths.Paths[k] = v
|
||||
}
|
||||
|
|
|
|||
4
vendor/github.com/go-openapi/analysis/schema.go
generated
vendored
4
vendor/github.com/go-openapi/analysis/schema.go
generated
vendored
|
|
@ -1,7 +1,7 @@
|
|||
package analysis
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"errors"
|
||||
|
||||
"github.com/go-openapi/spec"
|
||||
"github.com/go-openapi/strfmt"
|
||||
|
|
@ -19,7 +19,7 @@ type SchemaOpts struct {
|
|||
// patterns.
|
||||
func Schema(opts SchemaOpts) (*AnalyzedSchema, error) {
|
||||
if opts.Schema == nil {
|
||||
return nil, fmt.Errorf("no schema to analyze")
|
||||
return nil, errors.New("no schema to analyze")
|
||||
}
|
||||
|
||||
a := &AnalyzedSchema{
|
||||
|
|
|
|||
26
vendor/github.com/go-openapi/jsonpointer/pointer.go
generated
vendored
26
vendor/github.com/go-openapi/jsonpointer/pointer.go
generated
vendored
|
|
@ -110,16 +110,36 @@ func SetForToken(document any, decodedToken string, value any) (any, error) {
|
|||
return document, setSingleImpl(document, value, decodedToken, swag.DefaultJSONNameProvider)
|
||||
}
|
||||
|
||||
func isNil(input any) bool {
|
||||
if input == nil {
|
||||
return true
|
||||
}
|
||||
|
||||
kind := reflect.TypeOf(input).Kind()
|
||||
switch kind { //nolint:exhaustive
|
||||
case reflect.Ptr, reflect.Map, reflect.Slice, reflect.Chan:
|
||||
return reflect.ValueOf(input).IsNil()
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func getSingleImpl(node any, decodedToken string, nameProvider *swag.NameProvider) (any, reflect.Kind, error) {
|
||||
rValue := reflect.Indirect(reflect.ValueOf(node))
|
||||
kind := rValue.Kind()
|
||||
if isNil(node) {
|
||||
return nil, kind, fmt.Errorf("nil value has not field %q", decodedToken)
|
||||
}
|
||||
|
||||
if rValue.Type().Implements(jsonPointableType) {
|
||||
r, err := node.(JSONPointable).JSONLookup(decodedToken)
|
||||
switch typed := node.(type) {
|
||||
case JSONPointable:
|
||||
r, err := typed.JSONLookup(decodedToken)
|
||||
if err != nil {
|
||||
return nil, kind, err
|
||||
}
|
||||
return r, kind, nil
|
||||
case *any: // case of a pointer to interface, that is not resolved by reflect.Indirect
|
||||
return getSingleImpl(*typed, decodedToken, nameProvider)
|
||||
}
|
||||
|
||||
switch kind { //nolint:exhaustive
|
||||
|
|
@ -244,7 +264,7 @@ func (p *Pointer) set(node, data any, nameProvider *swag.NameProvider) error {
|
|||
knd := reflect.ValueOf(node).Kind()
|
||||
|
||||
if knd != reflect.Ptr && knd != reflect.Struct && knd != reflect.Map && knd != reflect.Slice && knd != reflect.Array {
|
||||
return fmt.Errorf("only structs, pointers, maps and slices are supported for setting values")
|
||||
return errors.New("only structs, pointers, maps and slices are supported for setting values")
|
||||
}
|
||||
|
||||
if nameProvider == nil {
|
||||
|
|
|
|||
2
vendor/github.com/go-openapi/loads/README.md
generated
vendored
2
vendor/github.com/go-openapi/loads/README.md
generated
vendored
|
|
@ -1,4 +1,4 @@
|
|||
# Loads OAI specs [](https://github.com/go-openapi/loads/actions?query=workflow%3A"go+test") [](https://codecov.io/gh/go-openapi/lods)
|
||||
# Loads OAI specs [](https://github.com/go-openapi/loads/actions?query=workflow%3A"go+test") [](https://codecov.io/gh/go-openapi/loads)
|
||||
|
||||
[](https://raw.githubusercontent.com/go-openapi/loads/master/LICENSE) [](http://godoc.org/github.com/go-openapi/loads)
|
||||
[](https://goreportcard.com/report/github.com/go-openapi/loads)
|
||||
|
|
|
|||
3
vendor/github.com/go-openapi/loads/TODO.md
generated
vendored
3
vendor/github.com/go-openapi/loads/TODO.md
generated
vendored
|
|
@ -1,3 +0,0 @@
|
|||
[x] why not filepath in JSONDoc()
|
||||
[] integration tests package
|
||||
[] relint
|
||||
2
vendor/github.com/go-openapi/loads/loaders.go
generated
vendored
2
vendor/github.com/go-openapi/loads/loaders.go
generated
vendored
|
|
@ -21,7 +21,7 @@ var (
|
|||
func init() {
|
||||
jsonLoader := &loader{
|
||||
DocLoaderWithMatch: DocLoaderWithMatch{
|
||||
Match: func(pth string) bool {
|
||||
Match: func(_ string) bool {
|
||||
return true
|
||||
},
|
||||
Fn: JSONDoc,
|
||||
|
|
|
|||
3
vendor/github.com/go-openapi/loads/spec.go
generated
vendored
3
vendor/github.com/go-openapi/loads/spec.go
generated
vendored
|
|
@ -248,7 +248,8 @@ func (d *Document) ResetDefinitions() *Document {
|
|||
|
||||
// Pristine creates a new pristine document instance based on the input data
|
||||
func (d *Document) Pristine() *Document {
|
||||
dd, _ := Analyzed(d.Raw(), d.Version())
|
||||
raw, _ := json.Marshal(d.Spec())
|
||||
dd, _ := Analyzed(raw, d.Version())
|
||||
dd.pathLoader = d.pathLoader
|
||||
dd.specFilePath = d.specFilePath
|
||||
|
||||
|
|
|
|||
131
vendor/github.com/go-openapi/runtime/bytestream.go
generated
vendored
131
vendor/github.com/go-openapi/runtime/bytestream.go
generated
vendored
|
|
@ -38,9 +38,16 @@ type byteStreamOpts struct {
|
|||
Close bool
|
||||
}
|
||||
|
||||
// ByteStreamConsumer creates a consumer for byte streams,
|
||||
// takes a Writer/BinaryUnmarshaler interface or binary slice by reference,
|
||||
// and reads from the provided reader
|
||||
// ByteStreamConsumer creates a consumer for byte streams.
|
||||
//
|
||||
// The consumer consumes from a provided reader into the data passed by reference.
|
||||
//
|
||||
// Supported output underlying types and interfaces, prioritized in this order:
|
||||
// - io.ReaderFrom (for maximum control)
|
||||
// - io.Writer (performs io.Copy)
|
||||
// - encoding.BinaryUnmarshaler
|
||||
// - *string
|
||||
// - *[]byte
|
||||
func ByteStreamConsumer(opts ...byteStreamOpt) Consumer {
|
||||
var vals byteStreamOpts
|
||||
for _, opt := range opts {
|
||||
|
|
@ -51,10 +58,13 @@ func ByteStreamConsumer(opts ...byteStreamOpt) Consumer {
|
|||
if reader == nil {
|
||||
return errors.New("ByteStreamConsumer requires a reader") // early exit
|
||||
}
|
||||
if data == nil {
|
||||
return errors.New("nil destination for ByteStreamConsumer")
|
||||
}
|
||||
|
||||
closer := defaultCloser
|
||||
if vals.Close {
|
||||
if cl, ok := reader.(io.Closer); ok {
|
||||
if cl, isReaderCloser := reader.(io.Closer); isReaderCloser {
|
||||
closer = cl.Close
|
||||
}
|
||||
}
|
||||
|
|
@ -62,34 +72,56 @@ func ByteStreamConsumer(opts ...byteStreamOpt) Consumer {
|
|||
_ = closer()
|
||||
}()
|
||||
|
||||
if wrtr, ok := data.(io.Writer); ok {
|
||||
_, err := io.Copy(wrtr, reader)
|
||||
if readerFrom, isReaderFrom := data.(io.ReaderFrom); isReaderFrom {
|
||||
_, err := readerFrom.ReadFrom(reader)
|
||||
return err
|
||||
}
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
if writer, isDataWriter := data.(io.Writer); isDataWriter {
|
||||
_, err := io.Copy(writer, reader)
|
||||
return err
|
||||
}
|
||||
|
||||
// buffers input before writing to data
|
||||
var buf bytes.Buffer
|
||||
_, err := buf.ReadFrom(reader)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
b := buf.Bytes()
|
||||
|
||||
if bu, ok := data.(encoding.BinaryUnmarshaler); ok {
|
||||
return bu.UnmarshalBinary(b)
|
||||
}
|
||||
switch destinationPointer := data.(type) {
|
||||
case encoding.BinaryUnmarshaler:
|
||||
return destinationPointer.UnmarshalBinary(b)
|
||||
case *any:
|
||||
switch (*destinationPointer).(type) {
|
||||
case string:
|
||||
*destinationPointer = string(b)
|
||||
|
||||
return nil
|
||||
|
||||
case []byte:
|
||||
*destinationPointer = b
|
||||
|
||||
if data != nil {
|
||||
if str, ok := data.(*string); ok {
|
||||
*str = string(b)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
default:
|
||||
// check for the underlying type to be pointer to []byte or string,
|
||||
if ptr := reflect.TypeOf(data); ptr.Kind() != reflect.Ptr {
|
||||
return errors.New("destination must be a pointer")
|
||||
}
|
||||
|
||||
if t := reflect.TypeOf(data); data != nil && t.Kind() == reflect.Ptr {
|
||||
v := reflect.Indirect(reflect.ValueOf(data))
|
||||
if t = v.Type(); t.Kind() == reflect.Slice && t.Elem().Kind() == reflect.Uint8 {
|
||||
t := v.Type()
|
||||
|
||||
switch {
|
||||
case t.Kind() == reflect.Slice && t.Elem().Kind() == reflect.Uint8:
|
||||
v.SetBytes(b)
|
||||
return nil
|
||||
|
||||
case t.Kind() == reflect.String:
|
||||
v.SetString(string(b))
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -98,21 +130,35 @@ func ByteStreamConsumer(opts ...byteStreamOpt) Consumer {
|
|||
})
|
||||
}
|
||||
|
||||
// ByteStreamProducer creates a producer for byte streams,
|
||||
// takes a Reader/BinaryMarshaler interface or binary slice,
|
||||
// and writes to a writer (essentially a pipe)
|
||||
// ByteStreamProducer creates a producer for byte streams.
|
||||
//
|
||||
// The producer takes input data then writes to an output writer (essentially as a pipe).
|
||||
//
|
||||
// Supported input underlying types and interfaces, prioritized in this order:
|
||||
// - io.WriterTo (for maximum control)
|
||||
// - io.Reader (performs io.Copy). A ReadCloser is closed before exiting.
|
||||
// - encoding.BinaryMarshaler
|
||||
// - error (writes as a string)
|
||||
// - []byte
|
||||
// - string
|
||||
// - struct, other slices: writes as JSON
|
||||
func ByteStreamProducer(opts ...byteStreamOpt) Producer {
|
||||
var vals byteStreamOpts
|
||||
for _, opt := range opts {
|
||||
opt(&vals)
|
||||
}
|
||||
|
||||
return ProducerFunc(func(writer io.Writer, data interface{}) error {
|
||||
if writer == nil {
|
||||
return errors.New("ByteStreamProducer requires a writer") // early exit
|
||||
}
|
||||
if data == nil {
|
||||
return errors.New("nil data for ByteStreamProducer")
|
||||
}
|
||||
|
||||
closer := defaultCloser
|
||||
if vals.Close {
|
||||
if cl, ok := writer.(io.Closer); ok {
|
||||
if cl, isWriterCloser := writer.(io.Closer); isWriterCloser {
|
||||
closer = cl.Close
|
||||
}
|
||||
}
|
||||
|
|
@ -120,46 +166,51 @@ func ByteStreamProducer(opts ...byteStreamOpt) Producer {
|
|||
_ = closer()
|
||||
}()
|
||||
|
||||
if rc, ok := data.(io.ReadCloser); ok {
|
||||
if rc, isDataCloser := data.(io.ReadCloser); isDataCloser {
|
||||
defer rc.Close()
|
||||
}
|
||||
|
||||
if rdr, ok := data.(io.Reader); ok {
|
||||
_, err := io.Copy(writer, rdr)
|
||||
switch origin := data.(type) {
|
||||
case io.WriterTo:
|
||||
_, err := origin.WriteTo(writer)
|
||||
return err
|
||||
}
|
||||
|
||||
if bm, ok := data.(encoding.BinaryMarshaler); ok {
|
||||
bytes, err := bm.MarshalBinary()
|
||||
case io.Reader:
|
||||
_, err := io.Copy(writer, origin)
|
||||
return err
|
||||
|
||||
case encoding.BinaryMarshaler:
|
||||
bytes, err := origin.MarshalBinary()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = writer.Write(bytes)
|
||||
return err
|
||||
}
|
||||
|
||||
if data != nil {
|
||||
if str, ok := data.(string); ok {
|
||||
_, err := writer.Write([]byte(str))
|
||||
return err
|
||||
}
|
||||
|
||||
if e, ok := data.(error); ok {
|
||||
_, err := writer.Write([]byte(e.Error()))
|
||||
return err
|
||||
}
|
||||
case error:
|
||||
_, err := writer.Write([]byte(origin.Error()))
|
||||
return err
|
||||
|
||||
default:
|
||||
v := reflect.Indirect(reflect.ValueOf(data))
|
||||
if t := v.Type(); t.Kind() == reflect.Slice && t.Elem().Kind() == reflect.Uint8 {
|
||||
t := v.Type()
|
||||
|
||||
switch {
|
||||
case t.Kind() == reflect.Slice && t.Elem().Kind() == reflect.Uint8:
|
||||
_, err := writer.Write(v.Bytes())
|
||||
return err
|
||||
}
|
||||
if t := v.Type(); t.Kind() == reflect.Struct || t.Kind() == reflect.Slice {
|
||||
|
||||
case t.Kind() == reflect.String:
|
||||
_, err := writer.Write([]byte(v.String()))
|
||||
return err
|
||||
|
||||
case t.Kind() == reflect.Struct || t.Kind() == reflect.Slice:
|
||||
b, err := swag.WriteJSON(data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = writer.Write(b)
|
||||
return err
|
||||
}
|
||||
|
|
|
|||
4
vendor/github.com/go-openapi/runtime/client/request.go
generated
vendored
4
vendor/github.com/go-openapi/runtime/client/request.go
generated
vendored
|
|
@ -36,7 +36,7 @@ import (
|
|||
)
|
||||
|
||||
// NewRequest creates a new swagger http client request
|
||||
func newRequest(method, pathPattern string, writer runtime.ClientRequestWriter) (*request, error) {
|
||||
func newRequest(method, pathPattern string, writer runtime.ClientRequestWriter) *request {
|
||||
return &request{
|
||||
pathPattern: pathPattern,
|
||||
method: method,
|
||||
|
|
@ -45,7 +45,7 @@ func newRequest(method, pathPattern string, writer runtime.ClientRequestWriter)
|
|||
query: make(url.Values),
|
||||
timeout: DefaultTimeout,
|
||||
getBody: getRequestBuffer,
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
// Request represents a swagger client request.
|
||||
|
|
|
|||
56
vendor/github.com/go-openapi/runtime/client/runtime.go
generated
vendored
56
vendor/github.com/go-openapi/runtime/client/runtime.go
generated
vendored
|
|
@ -22,6 +22,7 @@ import (
|
|||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
"fmt"
|
||||
"mime"
|
||||
"net/http"
|
||||
|
|
@ -31,12 +32,13 @@ import (
|
|||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/go-openapi/strfmt"
|
||||
"github.com/opentracing/opentracing-go"
|
||||
|
||||
"github.com/go-openapi/runtime"
|
||||
"github.com/go-openapi/runtime/logger"
|
||||
"github.com/go-openapi/runtime/middleware"
|
||||
"github.com/go-openapi/runtime/yamlpc"
|
||||
"github.com/go-openapi/strfmt"
|
||||
"github.com/opentracing/opentracing-go"
|
||||
)
|
||||
|
||||
const (
|
||||
|
|
@ -142,7 +144,7 @@ func TLSClientAuth(opts TLSClientOptions) (*tls.Config, error) {
|
|||
return nil, fmt.Errorf("tls client priv key: %v", err)
|
||||
}
|
||||
default:
|
||||
return nil, fmt.Errorf("tls client priv key: unsupported key type")
|
||||
return nil, errors.New("tls client priv key: unsupported key type")
|
||||
}
|
||||
|
||||
block = pem.Block{Type: "PRIVATE KEY", Bytes: keyBytes}
|
||||
|
|
@ -378,14 +380,11 @@ func (r *Runtime) EnableConnectionReuse() {
|
|||
func (r *Runtime) createHttpRequest(operation *runtime.ClientOperation) (*request, *http.Request, error) { //nolint:revive,stylecheck
|
||||
params, _, auth := operation.Params, operation.Reader, operation.AuthInfo
|
||||
|
||||
request, err := newRequest(operation.Method, operation.PathPattern, params)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
request := newRequest(operation.Method, operation.PathPattern, params)
|
||||
|
||||
var accept []string
|
||||
accept = append(accept, operation.ProducesMediaTypes...)
|
||||
if err = request.SetHeaderParam(runtime.HeaderAccept, accept...); err != nil {
|
||||
if err := request.SetHeaderParam(runtime.HeaderAccept, accept...); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
|
|
@ -457,27 +456,36 @@ func (r *Runtime) Submit(operation *runtime.ClientOperation) (interface{}, error
|
|||
r.logger.Debugf("%s\n", string(b))
|
||||
}
|
||||
|
||||
var hasTimeout bool
|
||||
pctx := operation.Context
|
||||
if pctx == nil {
|
||||
pctx = r.Context
|
||||
} else {
|
||||
hasTimeout = true
|
||||
var parentCtx context.Context
|
||||
switch {
|
||||
case operation.Context != nil:
|
||||
parentCtx = operation.Context
|
||||
case r.Context != nil:
|
||||
parentCtx = r.Context
|
||||
default:
|
||||
parentCtx = context.Background()
|
||||
}
|
||||
if pctx == nil {
|
||||
pctx = context.Background()
|
||||
}
|
||||
var ctx context.Context
|
||||
var cancel context.CancelFunc
|
||||
if hasTimeout {
|
||||
ctx, cancel = context.WithCancel(pctx)
|
||||
|
||||
var (
|
||||
ctx context.Context
|
||||
cancel context.CancelFunc
|
||||
)
|
||||
if request.timeout == 0 {
|
||||
// There may be a deadline in the context passed to the operation.
|
||||
// Otherwise, there is no timeout set.
|
||||
ctx, cancel = context.WithCancel(parentCtx)
|
||||
} else {
|
||||
ctx, cancel = context.WithTimeout(pctx, request.timeout)
|
||||
// Sets the timeout passed from request params (by default runtime.DefaultTimeout).
|
||||
// If there is already a deadline in the parent context, the shortest will
|
||||
// apply.
|
||||
ctx, cancel = context.WithTimeout(parentCtx, request.timeout)
|
||||
}
|
||||
defer cancel()
|
||||
|
||||
client := operation.Client
|
||||
if client == nil {
|
||||
var client *http.Client
|
||||
if operation.Client != nil {
|
||||
client = operation.Client
|
||||
} else {
|
||||
client = r.client
|
||||
}
|
||||
req = req.WithContext(ctx)
|
||||
|
|
|
|||
337
vendor/github.com/go-openapi/runtime/csv.go
generated
vendored
337
vendor/github.com/go-openapi/runtime/csv.go
generated
vendored
|
|
@ -16,62 +16,335 @@ package runtime
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding"
|
||||
"encoding/csv"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"reflect"
|
||||
|
||||
"golang.org/x/sync/errgroup"
|
||||
)
|
||||
|
||||
// CSVConsumer creates a new CSV consumer
|
||||
func CSVConsumer() Consumer {
|
||||
// CSVConsumer creates a new CSV consumer.
|
||||
//
|
||||
// The consumer consumes CSV records from a provided reader into the data passed by reference.
|
||||
//
|
||||
// CSVOpts options may be specified to alter the default CSV behavior on the reader and the writer side (e.g. separator, skip header, ...).
|
||||
// The defaults are those of the standard library's csv.Reader and csv.Writer.
|
||||
//
|
||||
// Supported output underlying types and interfaces, prioritized in this order:
|
||||
// - *csv.Writer
|
||||
// - CSVWriter (writer options are ignored)
|
||||
// - io.Writer (as raw bytes)
|
||||
// - io.ReaderFrom (as raw bytes)
|
||||
// - encoding.BinaryUnmarshaler (as raw bytes)
|
||||
// - *[][]string (as a collection of records)
|
||||
// - *[]byte (as raw bytes)
|
||||
// - *string (a raw bytes)
|
||||
//
|
||||
// The consumer prioritizes situations where buffering the input is not required.
|
||||
func CSVConsumer(opts ...CSVOpt) Consumer {
|
||||
o := csvOptsWithDefaults(opts)
|
||||
|
||||
return ConsumerFunc(func(reader io.Reader, data interface{}) error {
|
||||
if reader == nil {
|
||||
return errors.New("CSVConsumer requires a reader")
|
||||
}
|
||||
if data == nil {
|
||||
return errors.New("nil destination for CSVConsumer")
|
||||
}
|
||||
|
||||
csvReader := csv.NewReader(reader)
|
||||
writer, ok := data.(io.Writer)
|
||||
if !ok {
|
||||
return errors.New("data type must be io.Writer")
|
||||
}
|
||||
csvWriter := csv.NewWriter(writer)
|
||||
records, err := csvReader.ReadAll()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, r := range records {
|
||||
if err := csvWriter.Write(r); err != nil {
|
||||
return err
|
||||
o.applyToReader(csvReader)
|
||||
closer := defaultCloser
|
||||
if o.closeStream {
|
||||
if cl, isReaderCloser := reader.(io.Closer); isReaderCloser {
|
||||
closer = cl.Close
|
||||
}
|
||||
}
|
||||
defer func() {
|
||||
_ = closer()
|
||||
}()
|
||||
|
||||
switch destination := data.(type) {
|
||||
case *csv.Writer:
|
||||
csvWriter := destination
|
||||
o.applyToWriter(csvWriter)
|
||||
|
||||
return pipeCSV(csvWriter, csvReader, o)
|
||||
|
||||
case CSVWriter:
|
||||
csvWriter := destination
|
||||
// no writer options available
|
||||
|
||||
return pipeCSV(csvWriter, csvReader, o)
|
||||
|
||||
case io.Writer:
|
||||
csvWriter := csv.NewWriter(destination)
|
||||
o.applyToWriter(csvWriter)
|
||||
|
||||
return pipeCSV(csvWriter, csvReader, o)
|
||||
|
||||
case io.ReaderFrom:
|
||||
var buf bytes.Buffer
|
||||
csvWriter := csv.NewWriter(&buf)
|
||||
o.applyToWriter(csvWriter)
|
||||
if err := bufferedCSV(csvWriter, csvReader, o); err != nil {
|
||||
return err
|
||||
}
|
||||
_, err := destination.ReadFrom(&buf)
|
||||
|
||||
return err
|
||||
|
||||
case encoding.BinaryUnmarshaler:
|
||||
var buf bytes.Buffer
|
||||
csvWriter := csv.NewWriter(&buf)
|
||||
o.applyToWriter(csvWriter)
|
||||
if err := bufferedCSV(csvWriter, csvReader, o); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return destination.UnmarshalBinary(buf.Bytes())
|
||||
|
||||
default:
|
||||
// support *[][]string, *[]byte, *string
|
||||
if ptr := reflect.TypeOf(data); ptr.Kind() != reflect.Ptr {
|
||||
return errors.New("destination must be a pointer")
|
||||
}
|
||||
|
||||
v := reflect.Indirect(reflect.ValueOf(data))
|
||||
t := v.Type()
|
||||
|
||||
switch {
|
||||
case t.Kind() == reflect.Slice && t.Elem().Kind() == reflect.Slice && t.Elem().Elem().Kind() == reflect.String:
|
||||
csvWriter := &csvRecordsWriter{}
|
||||
// writer options are ignored
|
||||
if err := pipeCSV(csvWriter, csvReader, o); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
v.Grow(len(csvWriter.records))
|
||||
v.SetCap(len(csvWriter.records)) // in case Grow was unnessary, trim down the capacity
|
||||
v.SetLen(len(csvWriter.records))
|
||||
reflect.Copy(v, reflect.ValueOf(csvWriter.records))
|
||||
|
||||
return nil
|
||||
|
||||
case t.Kind() == reflect.Slice && t.Elem().Kind() == reflect.Uint8:
|
||||
var buf bytes.Buffer
|
||||
csvWriter := csv.NewWriter(&buf)
|
||||
o.applyToWriter(csvWriter)
|
||||
if err := bufferedCSV(csvWriter, csvReader, o); err != nil {
|
||||
return err
|
||||
}
|
||||
v.SetBytes(buf.Bytes())
|
||||
|
||||
return nil
|
||||
|
||||
case t.Kind() == reflect.String:
|
||||
var buf bytes.Buffer
|
||||
csvWriter := csv.NewWriter(&buf)
|
||||
o.applyToWriter(csvWriter)
|
||||
if err := bufferedCSV(csvWriter, csvReader, o); err != nil {
|
||||
return err
|
||||
}
|
||||
v.SetString(buf.String())
|
||||
|
||||
return nil
|
||||
|
||||
default:
|
||||
return fmt.Errorf("%v (%T) is not supported by the CSVConsumer, %s",
|
||||
data, data, "can be resolved by supporting CSVWriter/Writer/BinaryUnmarshaler interface",
|
||||
)
|
||||
}
|
||||
}
|
||||
csvWriter.Flush()
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// CSVProducer creates a new CSV producer
|
||||
func CSVProducer() Producer {
|
||||
// CSVProducer creates a new CSV producer.
|
||||
//
|
||||
// The producer takes input data then writes as CSV to an output writer (essentially as a pipe).
|
||||
//
|
||||
// Supported input underlying types and interfaces, prioritized in this order:
|
||||
// - *csv.Reader
|
||||
// - CSVReader (reader options are ignored)
|
||||
// - io.Reader
|
||||
// - io.WriterTo
|
||||
// - encoding.BinaryMarshaler
|
||||
// - [][]string
|
||||
// - []byte
|
||||
// - string
|
||||
//
|
||||
// The producer prioritizes situations where buffering the input is not required.
|
||||
func CSVProducer(opts ...CSVOpt) Producer {
|
||||
o := csvOptsWithDefaults(opts)
|
||||
|
||||
return ProducerFunc(func(writer io.Writer, data interface{}) error {
|
||||
if writer == nil {
|
||||
return errors.New("CSVProducer requires a writer")
|
||||
}
|
||||
|
||||
dataBytes, ok := data.([]byte)
|
||||
if !ok {
|
||||
return errors.New("data type must be byte array")
|
||||
if data == nil {
|
||||
return errors.New("nil data for CSVProducer")
|
||||
}
|
||||
|
||||
csvReader := csv.NewReader(bytes.NewBuffer(dataBytes))
|
||||
records, err := csvReader.ReadAll()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
csvWriter := csv.NewWriter(writer)
|
||||
for _, r := range records {
|
||||
if err := csvWriter.Write(r); err != nil {
|
||||
return err
|
||||
o.applyToWriter(csvWriter)
|
||||
closer := defaultCloser
|
||||
if o.closeStream {
|
||||
if cl, isWriterCloser := writer.(io.Closer); isWriterCloser {
|
||||
closer = cl.Close
|
||||
}
|
||||
}
|
||||
defer func() {
|
||||
_ = closer()
|
||||
}()
|
||||
|
||||
if rc, isDataCloser := data.(io.ReadCloser); isDataCloser {
|
||||
defer rc.Close()
|
||||
}
|
||||
|
||||
switch origin := data.(type) {
|
||||
case *csv.Reader:
|
||||
csvReader := origin
|
||||
o.applyToReader(csvReader)
|
||||
|
||||
return pipeCSV(csvWriter, csvReader, o)
|
||||
|
||||
case CSVReader:
|
||||
csvReader := origin
|
||||
// no reader options available
|
||||
|
||||
return pipeCSV(csvWriter, csvReader, o)
|
||||
|
||||
case io.Reader:
|
||||
csvReader := csv.NewReader(origin)
|
||||
o.applyToReader(csvReader)
|
||||
|
||||
return pipeCSV(csvWriter, csvReader, o)
|
||||
|
||||
case io.WriterTo:
|
||||
// async piping of the writes performed by WriteTo
|
||||
r, w := io.Pipe()
|
||||
csvReader := csv.NewReader(r)
|
||||
o.applyToReader(csvReader)
|
||||
|
||||
pipe, _ := errgroup.WithContext(context.Background())
|
||||
pipe.Go(func() error {
|
||||
_, err := origin.WriteTo(w)
|
||||
_ = w.Close()
|
||||
return err
|
||||
})
|
||||
|
||||
pipe.Go(func() error {
|
||||
defer func() {
|
||||
_ = r.Close()
|
||||
}()
|
||||
|
||||
return pipeCSV(csvWriter, csvReader, o)
|
||||
})
|
||||
|
||||
return pipe.Wait()
|
||||
|
||||
case encoding.BinaryMarshaler:
|
||||
buf, err := origin.MarshalBinary()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rdr := bytes.NewBuffer(buf)
|
||||
csvReader := csv.NewReader(rdr)
|
||||
|
||||
return bufferedCSV(csvWriter, csvReader, o)
|
||||
|
||||
default:
|
||||
// support [][]string, []byte, string (or pointers to those)
|
||||
v := reflect.Indirect(reflect.ValueOf(data))
|
||||
t := v.Type()
|
||||
|
||||
switch {
|
||||
case t.Kind() == reflect.Slice && t.Elem().Kind() == reflect.Slice && t.Elem().Elem().Kind() == reflect.String:
|
||||
csvReader := &csvRecordsWriter{
|
||||
records: make([][]string, v.Len()),
|
||||
}
|
||||
reflect.Copy(reflect.ValueOf(csvReader.records), v)
|
||||
|
||||
return pipeCSV(csvWriter, csvReader, o)
|
||||
|
||||
case t.Kind() == reflect.Slice && t.Elem().Kind() == reflect.Uint8:
|
||||
buf := bytes.NewBuffer(v.Bytes())
|
||||
csvReader := csv.NewReader(buf)
|
||||
o.applyToReader(csvReader)
|
||||
|
||||
return bufferedCSV(csvWriter, csvReader, o)
|
||||
|
||||
case t.Kind() == reflect.String:
|
||||
buf := bytes.NewBufferString(v.String())
|
||||
csvReader := csv.NewReader(buf)
|
||||
o.applyToReader(csvReader)
|
||||
|
||||
return bufferedCSV(csvWriter, csvReader, o)
|
||||
|
||||
default:
|
||||
return fmt.Errorf("%v (%T) is not supported by the CSVProducer, %s",
|
||||
data, data, "can be resolved by supporting CSVReader/Reader/BinaryMarshaler interface",
|
||||
)
|
||||
}
|
||||
}
|
||||
csvWriter.Flush()
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// pipeCSV copies CSV records from a CSV reader to a CSV writer
|
||||
func pipeCSV(csvWriter CSVWriter, csvReader CSVReader, opts csvOpts) error {
|
||||
for ; opts.skippedLines > 0; opts.skippedLines-- {
|
||||
_, err := csvReader.Read()
|
||||
if err != nil {
|
||||
if errors.Is(err, io.EOF) {
|
||||
return nil
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
for {
|
||||
record, err := csvReader.Read()
|
||||
if err != nil {
|
||||
if errors.Is(err, io.EOF) {
|
||||
break
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
if err := csvWriter.Write(record); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
csvWriter.Flush()
|
||||
|
||||
return csvWriter.Error()
|
||||
}
|
||||
|
||||
// bufferedCSV copies CSV records from a CSV reader to a CSV writer,
|
||||
// by first reading all records then writing them at once.
|
||||
func bufferedCSV(csvWriter *csv.Writer, csvReader *csv.Reader, opts csvOpts) error {
|
||||
for ; opts.skippedLines > 0; opts.skippedLines-- {
|
||||
_, err := csvReader.Read()
|
||||
if err != nil {
|
||||
if errors.Is(err, io.EOF) {
|
||||
return nil
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
records, err := csvReader.ReadAll()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return csvWriter.WriteAll(records)
|
||||
}
|
||||
|
|
|
|||
121
vendor/github.com/go-openapi/runtime/csv_options.go
generated
vendored
Normal file
121
vendor/github.com/go-openapi/runtime/csv_options.go
generated
vendored
Normal file
|
|
@ -0,0 +1,121 @@
|
|||
package runtime
|
||||
|
||||
import (
|
||||
"encoding/csv"
|
||||
"io"
|
||||
)
|
||||
|
||||
// CSVOpts alter the behavior of the CSV consumer or producer.
|
||||
type CSVOpt func(*csvOpts)
|
||||
|
||||
type csvOpts struct {
|
||||
csvReader csv.Reader
|
||||
csvWriter csv.Writer
|
||||
skippedLines int
|
||||
closeStream bool
|
||||
}
|
||||
|
||||
// WithCSVReaderOpts specifies the options to csv.Reader
|
||||
// when reading CSV.
|
||||
func WithCSVReaderOpts(reader csv.Reader) CSVOpt {
|
||||
return func(o *csvOpts) {
|
||||
o.csvReader = reader
|
||||
}
|
||||
}
|
||||
|
||||
// WithCSVWriterOpts specifies the options to csv.Writer
|
||||
// when writing CSV.
|
||||
func WithCSVWriterOpts(writer csv.Writer) CSVOpt {
|
||||
return func(o *csvOpts) {
|
||||
o.csvWriter = writer
|
||||
}
|
||||
}
|
||||
|
||||
// WithCSVSkipLines will skip header lines.
|
||||
func WithCSVSkipLines(skipped int) CSVOpt {
|
||||
return func(o *csvOpts) {
|
||||
o.skippedLines = skipped
|
||||
}
|
||||
}
|
||||
|
||||
func WithCSVClosesStream() CSVOpt {
|
||||
return func(o *csvOpts) {
|
||||
o.closeStream = true
|
||||
}
|
||||
}
|
||||
|
||||
func (o csvOpts) applyToReader(in *csv.Reader) {
|
||||
if o.csvReader.Comma != 0 {
|
||||
in.Comma = o.csvReader.Comma
|
||||
}
|
||||
if o.csvReader.Comment != 0 {
|
||||
in.Comment = o.csvReader.Comment
|
||||
}
|
||||
if o.csvReader.FieldsPerRecord != 0 {
|
||||
in.FieldsPerRecord = o.csvReader.FieldsPerRecord
|
||||
}
|
||||
|
||||
in.LazyQuotes = o.csvReader.LazyQuotes
|
||||
in.TrimLeadingSpace = o.csvReader.TrimLeadingSpace
|
||||
in.ReuseRecord = o.csvReader.ReuseRecord
|
||||
}
|
||||
|
||||
func (o csvOpts) applyToWriter(in *csv.Writer) {
|
||||
if o.csvWriter.Comma != 0 {
|
||||
in.Comma = o.csvWriter.Comma
|
||||
}
|
||||
in.UseCRLF = o.csvWriter.UseCRLF
|
||||
}
|
||||
|
||||
func csvOptsWithDefaults(opts []CSVOpt) csvOpts {
|
||||
var o csvOpts
|
||||
for _, apply := range opts {
|
||||
apply(&o)
|
||||
}
|
||||
|
||||
return o
|
||||
}
|
||||
|
||||
type CSVWriter interface {
|
||||
Write([]string) error
|
||||
Flush()
|
||||
Error() error
|
||||
}
|
||||
|
||||
type CSVReader interface {
|
||||
Read() ([]string, error)
|
||||
}
|
||||
|
||||
var (
|
||||
_ CSVWriter = &csvRecordsWriter{}
|
||||
_ CSVReader = &csvRecordsWriter{}
|
||||
)
|
||||
|
||||
// csvRecordsWriter is an internal container to move CSV records back and forth
|
||||
type csvRecordsWriter struct {
|
||||
i int
|
||||
records [][]string
|
||||
}
|
||||
|
||||
func (w *csvRecordsWriter) Write(record []string) error {
|
||||
w.records = append(w.records, record)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w *csvRecordsWriter) Read() ([]string, error) {
|
||||
if w.i >= len(w.records) {
|
||||
return nil, io.EOF
|
||||
}
|
||||
defer func() {
|
||||
w.i++
|
||||
}()
|
||||
|
||||
return w.records[w.i], nil
|
||||
}
|
||||
|
||||
func (w *csvRecordsWriter) Flush() {}
|
||||
|
||||
func (w *csvRecordsWriter) Error() error {
|
||||
return nil
|
||||
}
|
||||
2
vendor/github.com/go-openapi/runtime/logger/standard.go
generated
vendored
2
vendor/github.com/go-openapi/runtime/logger/standard.go
generated
vendored
|
|
@ -5,6 +5,8 @@ import (
|
|||
"os"
|
||||
)
|
||||
|
||||
var _ Logger = StandardLogger{}
|
||||
|
||||
type StandardLogger struct{}
|
||||
|
||||
func (StandardLogger) Printf(format string, args ...interface{}) {
|
||||
|
|
|
|||
161
vendor/github.com/go-openapi/runtime/middleware/context.go
generated
vendored
161
vendor/github.com/go-openapi/runtime/middleware/context.go
generated
vendored
|
|
@ -18,6 +18,8 @@ import (
|
|||
stdContext "context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"path"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
|
|
@ -35,12 +37,21 @@ import (
|
|||
|
||||
// Debug when true turns on verbose logging
|
||||
var Debug = logger.DebugEnabled()
|
||||
|
||||
// Logger is the standard libray logger used for printing debug messages
|
||||
var Logger logger.Logger = logger.StandardLogger{}
|
||||
|
||||
func debugLog(format string, args ...interface{}) { //nolint:goprintffuncname
|
||||
if Debug {
|
||||
Logger.Printf(format, args...)
|
||||
func debugLogfFunc(lg logger.Logger) func(string, ...any) {
|
||||
if logger.DebugEnabled() {
|
||||
if lg == nil {
|
||||
return Logger.Debugf
|
||||
}
|
||||
|
||||
return lg.Debugf
|
||||
}
|
||||
|
||||
// muted logger
|
||||
return func(_ string, _ ...any) {}
|
||||
}
|
||||
|
||||
// A Builder can create middlewares
|
||||
|
|
@ -73,10 +84,11 @@ func (fn ResponderFunc) WriteResponse(rw http.ResponseWriter, pr runtime.Produce
|
|||
// used throughout to store request context with the standard context attached
|
||||
// to the http.Request
|
||||
type Context struct {
|
||||
spec *loads.Document
|
||||
analyzer *analysis.Spec
|
||||
api RoutableAPI
|
||||
router Router
|
||||
spec *loads.Document
|
||||
analyzer *analysis.Spec
|
||||
api RoutableAPI
|
||||
router Router
|
||||
debugLogf func(string, ...any) // a logging function to debug context and all components using it
|
||||
}
|
||||
|
||||
type routableUntypedAPI struct {
|
||||
|
|
@ -189,7 +201,9 @@ func (r *routableUntypedAPI) DefaultConsumes() string {
|
|||
return r.defaultConsumes
|
||||
}
|
||||
|
||||
// NewRoutableContext creates a new context for a routable API
|
||||
// NewRoutableContext creates a new context for a routable API.
|
||||
//
|
||||
// If a nil Router is provided, the DefaultRouter (denco-based) will be used.
|
||||
func NewRoutableContext(spec *loads.Document, routableAPI RoutableAPI, routes Router) *Context {
|
||||
var an *analysis.Spec
|
||||
if spec != nil {
|
||||
|
|
@ -199,26 +213,40 @@ func NewRoutableContext(spec *loads.Document, routableAPI RoutableAPI, routes Ro
|
|||
return NewRoutableContextWithAnalyzedSpec(spec, an, routableAPI, routes)
|
||||
}
|
||||
|
||||
// NewRoutableContextWithAnalyzedSpec is like NewRoutableContext but takes in input the analysed spec too
|
||||
// NewRoutableContextWithAnalyzedSpec is like NewRoutableContext but takes as input an already analysed spec.
|
||||
//
|
||||
// If a nil Router is provided, the DefaultRouter (denco-based) will be used.
|
||||
func NewRoutableContextWithAnalyzedSpec(spec *loads.Document, an *analysis.Spec, routableAPI RoutableAPI, routes Router) *Context {
|
||||
// Either there are no spec doc and analysis, or both of them.
|
||||
if !((spec == nil && an == nil) || (spec != nil && an != nil)) {
|
||||
panic(errors.New(http.StatusInternalServerError, "routable context requires either both spec doc and analysis, or none of them"))
|
||||
}
|
||||
|
||||
ctx := &Context{spec: spec, api: routableAPI, analyzer: an, router: routes}
|
||||
return ctx
|
||||
return &Context{
|
||||
spec: spec,
|
||||
api: routableAPI,
|
||||
analyzer: an,
|
||||
router: routes,
|
||||
debugLogf: debugLogfFunc(nil),
|
||||
}
|
||||
}
|
||||
|
||||
// NewContext creates a new context wrapper
|
||||
// NewContext creates a new context wrapper.
|
||||
//
|
||||
// If a nil Router is provided, the DefaultRouter (denco-based) will be used.
|
||||
func NewContext(spec *loads.Document, api *untyped.API, routes Router) *Context {
|
||||
var an *analysis.Spec
|
||||
if spec != nil {
|
||||
an = analysis.New(spec.Spec())
|
||||
}
|
||||
ctx := &Context{spec: spec, analyzer: an}
|
||||
ctx := &Context{
|
||||
spec: spec,
|
||||
analyzer: an,
|
||||
router: routes,
|
||||
debugLogf: debugLogfFunc(nil),
|
||||
}
|
||||
ctx.api = newRoutableUntypedAPI(spec, api, ctx)
|
||||
ctx.router = routes
|
||||
|
||||
return ctx
|
||||
}
|
||||
|
||||
|
|
@ -282,6 +310,13 @@ func (c *Context) BasePath() string {
|
|||
return c.spec.BasePath()
|
||||
}
|
||||
|
||||
// SetLogger allows for injecting a logger to catch debug entries.
|
||||
//
|
||||
// The logger is enabled in DEBUG mode only.
|
||||
func (c *Context) SetLogger(lg logger.Logger) {
|
||||
c.debugLogf = debugLogfFunc(lg)
|
||||
}
|
||||
|
||||
// RequiredProduces returns the accepted content types for responses
|
||||
func (c *Context) RequiredProduces() []string {
|
||||
return c.analyzer.RequiredProduces()
|
||||
|
|
@ -299,6 +334,7 @@ func (c *Context) BindValidRequest(request *http.Request, route *MatchedRoute, b
|
|||
if err != nil {
|
||||
res = append(res, err)
|
||||
} else {
|
||||
c.debugLogf("validating content type for %q against [%s]", ct, strings.Join(route.Consumes, ", "))
|
||||
if err := validateContentType(route.Consumes, ct); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
|
@ -397,16 +433,16 @@ func (c *Context) ResponseFormat(r *http.Request, offers []string) (string, *htt
|
|||
var rCtx = r.Context()
|
||||
|
||||
if v, ok := rCtx.Value(ctxResponseFormat).(string); ok {
|
||||
debugLog("[%s %s] found response format %q in context", r.Method, r.URL.Path, v)
|
||||
c.debugLogf("[%s %s] found response format %q in context", r.Method, r.URL.Path, v)
|
||||
return v, r
|
||||
}
|
||||
|
||||
format := NegotiateContentType(r, offers, "")
|
||||
if format != "" {
|
||||
debugLog("[%s %s] set response format %q in context", r.Method, r.URL.Path, format)
|
||||
c.debugLogf("[%s %s] set response format %q in context", r.Method, r.URL.Path, format)
|
||||
r = r.WithContext(stdContext.WithValue(rCtx, ctxResponseFormat, format))
|
||||
}
|
||||
debugLog("[%s %s] negotiated response format %q", r.Method, r.URL.Path, format)
|
||||
c.debugLogf("[%s %s] negotiated response format %q", r.Method, r.URL.Path, format)
|
||||
return format, r
|
||||
}
|
||||
|
||||
|
|
@ -469,7 +505,7 @@ func (c *Context) BindAndValidate(request *http.Request, matched *MatchedRoute)
|
|||
var rCtx = request.Context()
|
||||
|
||||
if v, ok := rCtx.Value(ctxBoundParams).(*validation); ok {
|
||||
debugLog("got cached validation (valid: %t)", len(v.result) == 0)
|
||||
c.debugLogf("got cached validation (valid: %t)", len(v.result) == 0)
|
||||
if len(v.result) > 0 {
|
||||
return v.bound, request, errors.CompositeValidationError(v.result...)
|
||||
}
|
||||
|
|
@ -481,7 +517,7 @@ func (c *Context) BindAndValidate(request *http.Request, matched *MatchedRoute)
|
|||
if len(result.result) > 0 {
|
||||
return result.bound, request, errors.CompositeValidationError(result.result...)
|
||||
}
|
||||
debugLog("no validation errors found")
|
||||
c.debugLogf("no validation errors found")
|
||||
return result.bound, request, nil
|
||||
}
|
||||
|
||||
|
|
@ -492,7 +528,7 @@ func (c *Context) NotFound(rw http.ResponseWriter, r *http.Request) {
|
|||
|
||||
// Respond renders the response after doing some content negotiation
|
||||
func (c *Context) Respond(rw http.ResponseWriter, r *http.Request, produces []string, route *MatchedRoute, data interface{}) {
|
||||
debugLog("responding to %s %s with produces: %v", r.Method, r.URL.Path, produces)
|
||||
c.debugLogf("responding to %s %s with produces: %v", r.Method, r.URL.Path, produces)
|
||||
offers := []string{}
|
||||
for _, mt := range produces {
|
||||
if mt != c.api.DefaultProduces() {
|
||||
|
|
@ -501,7 +537,7 @@ func (c *Context) Respond(rw http.ResponseWriter, r *http.Request, produces []st
|
|||
}
|
||||
// the default producer is last so more specific producers take precedence
|
||||
offers = append(offers, c.api.DefaultProduces())
|
||||
debugLog("offers: %v", offers)
|
||||
c.debugLogf("offers: %v", offers)
|
||||
|
||||
var format string
|
||||
format, r = c.ResponseFormat(r, offers)
|
||||
|
|
@ -584,45 +620,92 @@ func (c *Context) Respond(rw http.ResponseWriter, r *http.Request, produces []st
|
|||
c.api.ServeErrorFor(route.Operation.ID)(rw, r, errors.New(http.StatusInternalServerError, "can't produce response"))
|
||||
}
|
||||
|
||||
func (c *Context) APIHandlerSwaggerUI(builder Builder) http.Handler {
|
||||
// APIHandlerSwaggerUI returns a handler to serve the API.
|
||||
//
|
||||
// This handler includes a swagger spec, router and the contract defined in the swagger spec.
|
||||
//
|
||||
// A spec UI (SwaggerUI) is served at {API base path}/docs and the spec document at /swagger.json
|
||||
// (these can be modified with uiOptions).
|
||||
func (c *Context) APIHandlerSwaggerUI(builder Builder, opts ...UIOption) http.Handler {
|
||||
b := builder
|
||||
if b == nil {
|
||||
b = PassthroughBuilder
|
||||
}
|
||||
|
||||
var title string
|
||||
sp := c.spec.Spec()
|
||||
if sp != nil && sp.Info != nil && sp.Info.Title != "" {
|
||||
title = sp.Info.Title
|
||||
}
|
||||
specPath, uiOpts, specOpts := c.uiOptionsForHandler(opts)
|
||||
var swaggerUIOpts SwaggerUIOpts
|
||||
fromCommonToAnyOptions(uiOpts, &swaggerUIOpts)
|
||||
|
||||
swaggerUIOpts := SwaggerUIOpts{
|
||||
BasePath: c.BasePath(),
|
||||
Title: title,
|
||||
}
|
||||
|
||||
return Spec("", c.spec.Raw(), SwaggerUI(swaggerUIOpts, c.RoutesHandler(b)))
|
||||
return Spec(specPath, c.spec.Raw(), SwaggerUI(swaggerUIOpts, c.RoutesHandler(b)), specOpts...)
|
||||
}
|
||||
|
||||
// APIHandler returns a handler to serve the API, this includes a swagger spec, router and the contract defined in the swagger spec
|
||||
func (c *Context) APIHandler(builder Builder) http.Handler {
|
||||
// APIHandlerRapiDoc returns a handler to serve the API.
|
||||
//
|
||||
// This handler includes a swagger spec, router and the contract defined in the swagger spec.
|
||||
//
|
||||
// A spec UI (RapiDoc) is served at {API base path}/docs and the spec document at /swagger.json
|
||||
// (these can be modified with uiOptions).
|
||||
func (c *Context) APIHandlerRapiDoc(builder Builder, opts ...UIOption) http.Handler {
|
||||
b := builder
|
||||
if b == nil {
|
||||
b = PassthroughBuilder
|
||||
}
|
||||
|
||||
specPath, uiOpts, specOpts := c.uiOptionsForHandler(opts)
|
||||
var rapidocUIOpts RapiDocOpts
|
||||
fromCommonToAnyOptions(uiOpts, &rapidocUIOpts)
|
||||
|
||||
return Spec(specPath, c.spec.Raw(), RapiDoc(rapidocUIOpts, c.RoutesHandler(b)), specOpts...)
|
||||
}
|
||||
|
||||
// APIHandler returns a handler to serve the API.
|
||||
//
|
||||
// This handler includes a swagger spec, router and the contract defined in the swagger spec.
|
||||
//
|
||||
// A spec UI (Redoc) is served at {API base path}/docs and the spec document at /swagger.json
|
||||
// (these can be modified with uiOptions).
|
||||
func (c *Context) APIHandler(builder Builder, opts ...UIOption) http.Handler {
|
||||
b := builder
|
||||
if b == nil {
|
||||
b = PassthroughBuilder
|
||||
}
|
||||
|
||||
specPath, uiOpts, specOpts := c.uiOptionsForHandler(opts)
|
||||
var redocOpts RedocOpts
|
||||
fromCommonToAnyOptions(uiOpts, &redocOpts)
|
||||
|
||||
return Spec(specPath, c.spec.Raw(), Redoc(redocOpts, c.RoutesHandler(b)), specOpts...)
|
||||
}
|
||||
|
||||
func (c Context) uiOptionsForHandler(opts []UIOption) (string, uiOptions, []SpecOption) {
|
||||
var title string
|
||||
sp := c.spec.Spec()
|
||||
if sp != nil && sp.Info != nil && sp.Info.Title != "" {
|
||||
title = sp.Info.Title
|
||||
}
|
||||
|
||||
redocOpts := RedocOpts{
|
||||
BasePath: c.BasePath(),
|
||||
Title: title,
|
||||
// default options (may be overridden)
|
||||
optsForContext := []UIOption{
|
||||
WithUIBasePath(c.BasePath()),
|
||||
WithUITitle(title),
|
||||
}
|
||||
optsForContext = append(optsForContext, opts...)
|
||||
uiOpts := uiOptionsWithDefaults(optsForContext)
|
||||
|
||||
// If spec URL is provided, there is a non-default path to serve the spec.
|
||||
// This makes sure that the UI middleware is aligned with the Spec middleware.
|
||||
u, _ := url.Parse(uiOpts.SpecURL)
|
||||
var specPath string
|
||||
if u != nil {
|
||||
specPath = u.Path
|
||||
}
|
||||
|
||||
return Spec("", c.spec.Raw(), Redoc(redocOpts, c.RoutesHandler(b)))
|
||||
pth, doc := path.Split(specPath)
|
||||
if pth == "." {
|
||||
pth = ""
|
||||
}
|
||||
|
||||
return pth, uiOpts, []SpecOption{WithSpecDocument(doc)}
|
||||
}
|
||||
|
||||
// RoutesHandler returns a handler to serve the API, just the routes and the contract defined in the swagger spec
|
||||
|
|
|
|||
30
vendor/github.com/go-openapi/runtime/middleware/denco/router.go
generated
vendored
30
vendor/github.com/go-openapi/runtime/middleware/denco/router.go
generated
vendored
|
|
@ -2,6 +2,7 @@
|
|||
package denco
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
|
|
@ -29,13 +30,13 @@ const (
|
|||
|
||||
// Router represents a URL router.
|
||||
type Router struct {
|
||||
param *doubleArray
|
||||
// SizeHint expects the maximum number of path parameters in records to Build.
|
||||
// SizeHint will be used to determine the capacity of the memory to allocate.
|
||||
// By default, SizeHint will be determined from given records to Build.
|
||||
SizeHint int
|
||||
|
||||
static map[string]interface{}
|
||||
param *doubleArray
|
||||
}
|
||||
|
||||
// New returns a new Router.
|
||||
|
|
@ -71,7 +72,7 @@ func (rt *Router) Lookup(path string) (data interface{}, params Params, found bo
|
|||
func (rt *Router) Build(records []Record) error {
|
||||
statics, params := makeRecords(records)
|
||||
if len(params) > MaxSize {
|
||||
return fmt.Errorf("denco: too many records")
|
||||
return errors.New("denco: too many records")
|
||||
}
|
||||
if rt.SizeHint < 0 {
|
||||
rt.SizeHint = 0
|
||||
|
|
@ -197,24 +198,29 @@ func (da *doubleArray) lookup(path string, params []Param, idx int) (*node, []Pa
|
|||
if next := nextIndex(da.bc[idx].Base(), TerminationCharacter); next < len(da.bc) && da.bc[next].Check() == TerminationCharacter {
|
||||
return da.node[da.bc[next].Base()], params, true
|
||||
}
|
||||
|
||||
BACKTRACKING:
|
||||
for j := len(indices) - 1; j >= 0; j-- {
|
||||
i, idx := int(indices[j]>>32), int(indices[j]&0xffffffff)
|
||||
if da.bc[idx].IsSingleParam() {
|
||||
idx := nextIndex(da.bc[idx].Base(), ParamCharacter) //nolint:govet
|
||||
if idx >= len(da.bc) {
|
||||
nextIdx := nextIndex(da.bc[idx].Base(), ParamCharacter)
|
||||
if nextIdx >= len(da.bc) {
|
||||
break
|
||||
}
|
||||
|
||||
next := NextSeparator(path, i)
|
||||
params := append(params, Param{Value: path[i:next]}) //nolint:govet
|
||||
if nd, params, found := da.lookup(path[next:], params, idx); found { //nolint:govet
|
||||
return nd, params, true
|
||||
nextParams := params
|
||||
nextParams = append(nextParams, Param{Value: path[i:next]})
|
||||
if nd, nextNextParams, found := da.lookup(path[next:], nextParams, nextIdx); found {
|
||||
return nd, nextNextParams, true
|
||||
}
|
||||
}
|
||||
|
||||
if da.bc[idx].IsWildcardParam() {
|
||||
idx := nextIndex(da.bc[idx].Base(), WildcardCharacter) //nolint:govet
|
||||
params := append(params, Param{Value: path[i:]}) //nolint:govet
|
||||
return da.node[da.bc[idx].Base()], params, true
|
||||
nextIdx := nextIndex(da.bc[idx].Base(), WildcardCharacter)
|
||||
nextParams := params
|
||||
nextParams = append(nextParams, Param{Value: path[i:]})
|
||||
return da.node[da.bc[nextIdx].Base()], nextParams, true
|
||||
}
|
||||
}
|
||||
return nil, nil, false
|
||||
|
|
@ -326,7 +332,7 @@ func (da *doubleArray) arrange(records []*record, idx, depth int, usedBase map[i
|
|||
}
|
||||
base = da.findBase(siblings, idx, usedBase)
|
||||
if base > MaxSize {
|
||||
return -1, nil, nil, fmt.Errorf("denco: too many elements of internal slice")
|
||||
return -1, nil, nil, errors.New("denco: too many elements of internal slice")
|
||||
}
|
||||
da.setBase(idx, base)
|
||||
return base, siblings, leaf, err
|
||||
|
|
@ -387,7 +393,7 @@ func makeSiblings(records []*record, depth int) (sib []sibling, leaf *record, er
|
|||
case pc == c:
|
||||
continue
|
||||
default:
|
||||
return nil, nil, fmt.Errorf("denco: BUG: routing table hasn't been sorted")
|
||||
return nil, nil, errors.New("denco: BUG: routing table hasn't been sorted")
|
||||
}
|
||||
if n > 0 {
|
||||
sib[n-1].end = i
|
||||
|
|
|
|||
10
vendor/github.com/go-openapi/runtime/middleware/go18.go
generated
vendored
10
vendor/github.com/go-openapi/runtime/middleware/go18.go
generated
vendored
|
|
@ -1,10 +0,0 @@
|
|||
//go:build go1.8
|
||||
// +build go1.8
|
||||
|
||||
package middleware
|
||||
|
||||
import "net/url"
|
||||
|
||||
func pathUnescape(path string) (string, error) {
|
||||
return url.PathUnescape(path)
|
||||
}
|
||||
9
vendor/github.com/go-openapi/runtime/middleware/pre_go18.go
generated
vendored
9
vendor/github.com/go-openapi/runtime/middleware/pre_go18.go
generated
vendored
|
|
@ -1,9 +0,0 @@
|
|||
// +build !go1.8
|
||||
|
||||
package middleware
|
||||
|
||||
import "net/url"
|
||||
|
||||
func pathUnescape(path string) (string, error) {
|
||||
return url.QueryUnescape(path)
|
||||
}
|
||||
70
vendor/github.com/go-openapi/runtime/middleware/rapidoc.go
generated
vendored
70
vendor/github.com/go-openapi/runtime/middleware/rapidoc.go
generated
vendored
|
|
@ -1,4 +1,3 @@
|
|||
//nolint:dupl
|
||||
package middleware
|
||||
|
||||
import (
|
||||
|
|
@ -11,66 +10,57 @@ import (
|
|||
|
||||
// RapiDocOpts configures the RapiDoc middlewares
|
||||
type RapiDocOpts struct {
|
||||
// BasePath for the UI path, defaults to: /
|
||||
// BasePath for the UI, defaults to: /
|
||||
BasePath string
|
||||
// Path combines with BasePath for the full UI path, defaults to: docs
|
||||
|
||||
// Path combines with BasePath to construct the path to the UI, defaults to: "docs".
|
||||
Path string
|
||||
// SpecURL the url to find the spec for
|
||||
|
||||
// SpecURL is the URL of the spec document.
|
||||
//
|
||||
// Defaults to: /swagger.json
|
||||
SpecURL string
|
||||
// RapiDocURL for the js that generates the rapidoc site, defaults to: https://cdn.jsdelivr.net/npm/rapidoc/bundles/rapidoc.standalone.js
|
||||
RapiDocURL string
|
||||
|
||||
// Title for the documentation site, default to: API documentation
|
||||
Title string
|
||||
|
||||
// Template specifies a custom template to serve the UI
|
||||
Template string
|
||||
|
||||
// RapiDocURL points to the js asset that generates the rapidoc site.
|
||||
//
|
||||
// Defaults to https://unpkg.com/rapidoc/dist/rapidoc-min.js
|
||||
RapiDocURL string
|
||||
}
|
||||
|
||||
// EnsureDefaults in case some options are missing
|
||||
func (r *RapiDocOpts) EnsureDefaults() {
|
||||
if r.BasePath == "" {
|
||||
r.BasePath = "/"
|
||||
}
|
||||
if r.Path == "" {
|
||||
r.Path = defaultDocsPath
|
||||
}
|
||||
if r.SpecURL == "" {
|
||||
r.SpecURL = defaultDocsURL
|
||||
}
|
||||
common := toCommonUIOptions(r)
|
||||
common.EnsureDefaults()
|
||||
fromCommonToAnyOptions(common, r)
|
||||
|
||||
// rapidoc-specifics
|
||||
if r.RapiDocURL == "" {
|
||||
r.RapiDocURL = rapidocLatest
|
||||
}
|
||||
if r.Title == "" {
|
||||
r.Title = defaultDocsTitle
|
||||
if r.Template == "" {
|
||||
r.Template = rapidocTemplate
|
||||
}
|
||||
}
|
||||
|
||||
// RapiDoc creates a middleware to serve a documentation site for a swagger spec.
|
||||
//
|
||||
// This allows for altering the spec before starting the http listener.
|
||||
func RapiDoc(opts RapiDocOpts, next http.Handler) http.Handler {
|
||||
opts.EnsureDefaults()
|
||||
|
||||
pth := path.Join(opts.BasePath, opts.Path)
|
||||
tmpl := template.Must(template.New("rapidoc").Parse(rapidocTemplate))
|
||||
tmpl := template.Must(template.New("rapidoc").Parse(opts.Template))
|
||||
assets := bytes.NewBuffer(nil)
|
||||
if err := tmpl.Execute(assets, opts); err != nil {
|
||||
panic(fmt.Errorf("cannot execute template: %w", err))
|
||||
}
|
||||
|
||||
buf := bytes.NewBuffer(nil)
|
||||
_ = tmpl.Execute(buf, opts)
|
||||
b := buf.Bytes()
|
||||
|
||||
return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
|
||||
if r.URL.Path == pth {
|
||||
rw.Header().Set("Content-Type", "text/html; charset=utf-8")
|
||||
rw.WriteHeader(http.StatusOK)
|
||||
|
||||
_, _ = rw.Write(b)
|
||||
return
|
||||
}
|
||||
|
||||
if next == nil {
|
||||
rw.Header().Set("Content-Type", "text/plain")
|
||||
rw.WriteHeader(http.StatusNotFound)
|
||||
_, _ = rw.Write([]byte(fmt.Sprintf("%q not found", pth)))
|
||||
return
|
||||
}
|
||||
next.ServeHTTP(rw, r)
|
||||
})
|
||||
return serveUI(pth, assets.Bytes(), next)
|
||||
}
|
||||
|
||||
const (
|
||||
|
|
|
|||
69
vendor/github.com/go-openapi/runtime/middleware/redoc.go
generated
vendored
69
vendor/github.com/go-openapi/runtime/middleware/redoc.go
generated
vendored
|
|
@ -1,4 +1,3 @@
|
|||
//nolint:dupl
|
||||
package middleware
|
||||
|
||||
import (
|
||||
|
|
@ -11,66 +10,58 @@ import (
|
|||
|
||||
// RedocOpts configures the Redoc middlewares
|
||||
type RedocOpts struct {
|
||||
// BasePath for the UI path, defaults to: /
|
||||
// BasePath for the UI, defaults to: /
|
||||
BasePath string
|
||||
// Path combines with BasePath for the full UI path, defaults to: docs
|
||||
|
||||
// Path combines with BasePath to construct the path to the UI, defaults to: "docs".
|
||||
Path string
|
||||
// SpecURL the url to find the spec for
|
||||
|
||||
// SpecURL is the URL of the spec document.
|
||||
//
|
||||
// Defaults to: /swagger.json
|
||||
SpecURL string
|
||||
// RedocURL for the js that generates the redoc site, defaults to: https://cdn.jsdelivr.net/npm/redoc/bundles/redoc.standalone.js
|
||||
RedocURL string
|
||||
|
||||
// Title for the documentation site, default to: API documentation
|
||||
Title string
|
||||
|
||||
// Template specifies a custom template to serve the UI
|
||||
Template string
|
||||
|
||||
// RedocURL points to the js that generates the redoc site.
|
||||
//
|
||||
// Defaults to: https://cdn.jsdelivr.net/npm/redoc/bundles/redoc.standalone.js
|
||||
RedocURL string
|
||||
}
|
||||
|
||||
// EnsureDefaults in case some options are missing
|
||||
func (r *RedocOpts) EnsureDefaults() {
|
||||
if r.BasePath == "" {
|
||||
r.BasePath = "/"
|
||||
}
|
||||
if r.Path == "" {
|
||||
r.Path = defaultDocsPath
|
||||
}
|
||||
if r.SpecURL == "" {
|
||||
r.SpecURL = defaultDocsURL
|
||||
}
|
||||
common := toCommonUIOptions(r)
|
||||
common.EnsureDefaults()
|
||||
fromCommonToAnyOptions(common, r)
|
||||
|
||||
// redoc-specifics
|
||||
if r.RedocURL == "" {
|
||||
r.RedocURL = redocLatest
|
||||
}
|
||||
if r.Title == "" {
|
||||
r.Title = defaultDocsTitle
|
||||
if r.Template == "" {
|
||||
r.Template = redocTemplate
|
||||
}
|
||||
}
|
||||
|
||||
// Redoc creates a middleware to serve a documentation site for a swagger spec.
|
||||
//
|
||||
// This allows for altering the spec before starting the http listener.
|
||||
func Redoc(opts RedocOpts, next http.Handler) http.Handler {
|
||||
opts.EnsureDefaults()
|
||||
|
||||
pth := path.Join(opts.BasePath, opts.Path)
|
||||
tmpl := template.Must(template.New("redoc").Parse(redocTemplate))
|
||||
tmpl := template.Must(template.New("redoc").Parse(opts.Template))
|
||||
assets := bytes.NewBuffer(nil)
|
||||
if err := tmpl.Execute(assets, opts); err != nil {
|
||||
panic(fmt.Errorf("cannot execute template: %w", err))
|
||||
}
|
||||
|
||||
buf := bytes.NewBuffer(nil)
|
||||
_ = tmpl.Execute(buf, opts)
|
||||
b := buf.Bytes()
|
||||
|
||||
return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
|
||||
if r.URL.Path == pth {
|
||||
rw.Header().Set("Content-Type", "text/html; charset=utf-8")
|
||||
rw.WriteHeader(http.StatusOK)
|
||||
|
||||
_, _ = rw.Write(b)
|
||||
return
|
||||
}
|
||||
|
||||
if next == nil {
|
||||
rw.Header().Set("Content-Type", "text/plain")
|
||||
rw.WriteHeader(http.StatusNotFound)
|
||||
_, _ = rw.Write([]byte(fmt.Sprintf("%q not found", pth)))
|
||||
return
|
||||
}
|
||||
next.ServeHTTP(rw, r)
|
||||
})
|
||||
return serveUI(pth, assets.Bytes(), next)
|
||||
}
|
||||
|
||||
const (
|
||||
|
|
|
|||
21
vendor/github.com/go-openapi/runtime/middleware/request.go
generated
vendored
21
vendor/github.com/go-openapi/runtime/middleware/request.go
generated
vendored
|
|
@ -19,10 +19,10 @@ import (
|
|||
"reflect"
|
||||
|
||||
"github.com/go-openapi/errors"
|
||||
"github.com/go-openapi/runtime"
|
||||
"github.com/go-openapi/runtime/logger"
|
||||
"github.com/go-openapi/spec"
|
||||
"github.com/go-openapi/strfmt"
|
||||
|
||||
"github.com/go-openapi/runtime"
|
||||
)
|
||||
|
||||
// UntypedRequestBinder binds and validates the data from a http request
|
||||
|
|
@ -31,6 +31,7 @@ type UntypedRequestBinder struct {
|
|||
Parameters map[string]spec.Parameter
|
||||
Formats strfmt.Registry
|
||||
paramBinders map[string]*untypedParamBinder
|
||||
debugLogf func(string, ...any) // a logging function to debug context and all components using it
|
||||
}
|
||||
|
||||
// NewUntypedRequestBinder creates a new binder for reading a request.
|
||||
|
|
@ -44,6 +45,7 @@ func NewUntypedRequestBinder(parameters map[string]spec.Parameter, spec *spec.Sw
|
|||
paramBinders: binders,
|
||||
Spec: spec,
|
||||
Formats: formats,
|
||||
debugLogf: debugLogfFunc(nil),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -52,10 +54,10 @@ func (o *UntypedRequestBinder) Bind(request *http.Request, routeParams RoutePara
|
|||
val := reflect.Indirect(reflect.ValueOf(data))
|
||||
isMap := val.Kind() == reflect.Map
|
||||
var result []error
|
||||
debugLog("binding %d parameters for %s %s", len(o.Parameters), request.Method, request.URL.EscapedPath())
|
||||
o.debugLogf("binding %d parameters for %s %s", len(o.Parameters), request.Method, request.URL.EscapedPath())
|
||||
for fieldName, param := range o.Parameters {
|
||||
binder := o.paramBinders[fieldName]
|
||||
debugLog("binding parameter %s for %s %s", fieldName, request.Method, request.URL.EscapedPath())
|
||||
o.debugLogf("binding parameter %s for %s %s", fieldName, request.Method, request.URL.EscapedPath())
|
||||
var target reflect.Value
|
||||
if !isMap {
|
||||
binder.Name = fieldName
|
||||
|
|
@ -102,3 +104,14 @@ func (o *UntypedRequestBinder) Bind(request *http.Request, routeParams RoutePara
|
|||
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetLogger allows for injecting a logger to catch debug entries.
|
||||
//
|
||||
// The logger is enabled in DEBUG mode only.
|
||||
func (o *UntypedRequestBinder) SetLogger(lg logger.Logger) {
|
||||
o.debugLogf = debugLogfFunc(lg)
|
||||
}
|
||||
|
||||
func (o *UntypedRequestBinder) setDebugLogf(fn func(string, ...any)) {
|
||||
o.debugLogf = fn
|
||||
}
|
||||
|
|
|
|||
103
vendor/github.com/go-openapi/runtime/middleware/router.go
generated
vendored
103
vendor/github.com/go-openapi/runtime/middleware/router.go
generated
vendored
|
|
@ -17,10 +17,12 @@ package middleware
|
|||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
fpath "path"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/go-openapi/runtime/logger"
|
||||
"github.com/go-openapi/runtime/security"
|
||||
"github.com/go-openapi/swag"
|
||||
|
||||
|
|
@ -67,10 +69,10 @@ func (r RouteParams) GetOK(name string) ([]string, bool, bool) {
|
|||
return nil, false, false
|
||||
}
|
||||
|
||||
// NewRouter creates a new context aware router middleware
|
||||
// NewRouter creates a new context-aware router middleware
|
||||
func NewRouter(ctx *Context, next http.Handler) http.Handler {
|
||||
if ctx.router == nil {
|
||||
ctx.router = DefaultRouter(ctx.spec, ctx.api)
|
||||
ctx.router = DefaultRouter(ctx.spec, ctx.api, WithDefaultRouterLoggerFunc(ctx.debugLogf))
|
||||
}
|
||||
|
||||
return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
|
||||
|
|
@ -103,41 +105,75 @@ type RoutableAPI interface {
|
|||
DefaultConsumes() string
|
||||
}
|
||||
|
||||
// Router represents a swagger aware router
|
||||
// Router represents a swagger-aware router
|
||||
type Router interface {
|
||||
Lookup(method, path string) (*MatchedRoute, bool)
|
||||
OtherMethods(method, path string) []string
|
||||
}
|
||||
|
||||
type defaultRouteBuilder struct {
|
||||
spec *loads.Document
|
||||
analyzer *analysis.Spec
|
||||
api RoutableAPI
|
||||
records map[string][]denco.Record
|
||||
spec *loads.Document
|
||||
analyzer *analysis.Spec
|
||||
api RoutableAPI
|
||||
records map[string][]denco.Record
|
||||
debugLogf func(string, ...any) // a logging function to debug context and all components using it
|
||||
}
|
||||
|
||||
type defaultRouter struct {
|
||||
spec *loads.Document
|
||||
routers map[string]*denco.Router
|
||||
spec *loads.Document
|
||||
routers map[string]*denco.Router
|
||||
debugLogf func(string, ...any) // a logging function to debug context and all components using it
|
||||
}
|
||||
|
||||
func newDefaultRouteBuilder(spec *loads.Document, api RoutableAPI) *defaultRouteBuilder {
|
||||
func newDefaultRouteBuilder(spec *loads.Document, api RoutableAPI, opts ...DefaultRouterOpt) *defaultRouteBuilder {
|
||||
var o defaultRouterOpts
|
||||
for _, apply := range opts {
|
||||
apply(&o)
|
||||
}
|
||||
if o.debugLogf == nil {
|
||||
o.debugLogf = debugLogfFunc(nil) // defaults to standard logger
|
||||
}
|
||||
|
||||
return &defaultRouteBuilder{
|
||||
spec: spec,
|
||||
analyzer: analysis.New(spec.Spec()),
|
||||
api: api,
|
||||
records: make(map[string][]denco.Record),
|
||||
spec: spec,
|
||||
analyzer: analysis.New(spec.Spec()),
|
||||
api: api,
|
||||
records: make(map[string][]denco.Record),
|
||||
debugLogf: o.debugLogf,
|
||||
}
|
||||
}
|
||||
|
||||
// DefaultRouter creates a default implemenation of the router
|
||||
func DefaultRouter(spec *loads.Document, api RoutableAPI) Router {
|
||||
builder := newDefaultRouteBuilder(spec, api)
|
||||
// DefaultRouterOpt allows to inject optional behavior to the default router.
|
||||
type DefaultRouterOpt func(*defaultRouterOpts)
|
||||
|
||||
type defaultRouterOpts struct {
|
||||
debugLogf func(string, ...any)
|
||||
}
|
||||
|
||||
// WithDefaultRouterLogger sets the debug logger for the default router.
|
||||
//
|
||||
// This is enabled only in DEBUG mode.
|
||||
func WithDefaultRouterLogger(lg logger.Logger) DefaultRouterOpt {
|
||||
return func(o *defaultRouterOpts) {
|
||||
o.debugLogf = debugLogfFunc(lg)
|
||||
}
|
||||
}
|
||||
|
||||
// WithDefaultRouterLoggerFunc sets a logging debug method for the default router.
|
||||
func WithDefaultRouterLoggerFunc(fn func(string, ...any)) DefaultRouterOpt {
|
||||
return func(o *defaultRouterOpts) {
|
||||
o.debugLogf = fn
|
||||
}
|
||||
}
|
||||
|
||||
// DefaultRouter creates a default implementation of the router
|
||||
func DefaultRouter(spec *loads.Document, api RoutableAPI, opts ...DefaultRouterOpt) Router {
|
||||
builder := newDefaultRouteBuilder(spec, api, opts...)
|
||||
if spec != nil {
|
||||
for method, paths := range builder.analyzer.Operations() {
|
||||
for path, operation := range paths {
|
||||
fp := fpath.Join(spec.BasePath(), path)
|
||||
debugLog("adding route %s %s %q", method, fp, operation.ID)
|
||||
builder.debugLogf("adding route %s %s %q", method, fp, operation.ID)
|
||||
builder.AddRoute(method, fp, operation)
|
||||
}
|
||||
}
|
||||
|
|
@ -319,24 +355,24 @@ func (m *MatchedRoute) NeedsAuth() bool {
|
|||
|
||||
func (d *defaultRouter) Lookup(method, path string) (*MatchedRoute, bool) {
|
||||
mth := strings.ToUpper(method)
|
||||
debugLog("looking up route for %s %s", method, path)
|
||||
d.debugLogf("looking up route for %s %s", method, path)
|
||||
if Debug {
|
||||
if len(d.routers) == 0 {
|
||||
debugLog("there are no known routers")
|
||||
d.debugLogf("there are no known routers")
|
||||
}
|
||||
for meth := range d.routers {
|
||||
debugLog("got a router for %s", meth)
|
||||
d.debugLogf("got a router for %s", meth)
|
||||
}
|
||||
}
|
||||
if router, ok := d.routers[mth]; ok {
|
||||
if m, rp, ok := router.Lookup(fpath.Clean(path)); ok && m != nil {
|
||||
if entry, ok := m.(*routeEntry); ok {
|
||||
debugLog("found a route for %s %s with %d parameters", method, path, len(entry.Parameters))
|
||||
d.debugLogf("found a route for %s %s with %d parameters", method, path, len(entry.Parameters))
|
||||
var params RouteParams
|
||||
for _, p := range rp {
|
||||
v, err := pathUnescape(p.Value)
|
||||
v, err := url.PathUnescape(p.Value)
|
||||
if err != nil {
|
||||
debugLog("failed to escape %q: %v", p.Value, err)
|
||||
d.debugLogf("failed to escape %q: %v", p.Value, err)
|
||||
v = p.Value
|
||||
}
|
||||
// a workaround to handle fragment/composing parameters until they are supported in denco router
|
||||
|
|
@ -356,10 +392,10 @@ func (d *defaultRouter) Lookup(method, path string) (*MatchedRoute, bool) {
|
|||
return &MatchedRoute{routeEntry: *entry, Params: params}, true
|
||||
}
|
||||
} else {
|
||||
debugLog("couldn't find a route by path for %s %s", method, path)
|
||||
d.debugLogf("couldn't find a route by path for %s %s", method, path)
|
||||
}
|
||||
} else {
|
||||
debugLog("couldn't find a route by method for %s %s", method, path)
|
||||
d.debugLogf("couldn't find a route by method for %s %s", method, path)
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
|
@ -378,6 +414,10 @@ func (d *defaultRouter) OtherMethods(method, path string) []string {
|
|||
return methods
|
||||
}
|
||||
|
||||
func (d *defaultRouter) SetLogger(lg logger.Logger) {
|
||||
d.debugLogf = debugLogfFunc(lg)
|
||||
}
|
||||
|
||||
// convert swagger parameters per path segment into a denco parameter as multiple parameters per segment are not supported in denco
|
||||
var pathConverter = regexp.MustCompile(`{(.+?)}([^/]*)`)
|
||||
|
||||
|
|
@ -413,7 +453,7 @@ func (d *defaultRouteBuilder) AddRoute(method, path string, operation *spec.Oper
|
|||
bp = bp[:len(bp)-1]
|
||||
}
|
||||
|
||||
debugLog("operation: %#v", *operation)
|
||||
d.debugLogf("operation: %#v", *operation)
|
||||
if handler, ok := d.api.HandlerFor(method, strings.TrimPrefix(path, bp)); ok {
|
||||
consumes := d.analyzer.ConsumesFor(operation)
|
||||
produces := d.analyzer.ProducesFor(operation)
|
||||
|
|
@ -428,6 +468,8 @@ func (d *defaultRouteBuilder) AddRoute(method, path string, operation *spec.Oper
|
|||
produces = append(produces, defProduces)
|
||||
}
|
||||
|
||||
requestBinder := NewUntypedRequestBinder(parameters, d.spec.Spec(), d.api.Formats())
|
||||
requestBinder.setDebugLogf(d.debugLogf)
|
||||
record := denco.NewRecord(pathConverter.ReplaceAllString(path, ":$1"), &routeEntry{
|
||||
BasePath: bp,
|
||||
PathPattern: path,
|
||||
|
|
@ -439,7 +481,7 @@ func (d *defaultRouteBuilder) AddRoute(method, path string, operation *spec.Oper
|
|||
Producers: d.api.ProducersFor(normalizeOffers(produces)),
|
||||
Parameters: parameters,
|
||||
Formats: d.api.Formats(),
|
||||
Binder: NewUntypedRequestBinder(parameters, d.spec.Spec(), d.api.Formats()),
|
||||
Binder: requestBinder,
|
||||
Authenticators: d.buildAuthenticators(operation),
|
||||
Authorizer: d.api.Authorizer(),
|
||||
})
|
||||
|
|
@ -482,7 +524,8 @@ func (d *defaultRouteBuilder) Build() *defaultRouter {
|
|||
routers[method] = router
|
||||
}
|
||||
return &defaultRouter{
|
||||
spec: d.spec,
|
||||
routers: routers,
|
||||
spec: d.spec,
|
||||
routers: routers,
|
||||
debugLogf: d.debugLogf,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
77
vendor/github.com/go-openapi/runtime/middleware/spec.go
generated
vendored
77
vendor/github.com/go-openapi/runtime/middleware/spec.go
generated
vendored
|
|
@ -19,29 +19,84 @@ import (
|
|||
"path"
|
||||
)
|
||||
|
||||
// Spec creates a middleware to serve a swagger spec.
|
||||
const (
|
||||
contentTypeHeader = "Content-Type"
|
||||
applicationJSON = "application/json"
|
||||
)
|
||||
|
||||
// SpecOption can be applied to the Spec serving middleware
|
||||
type SpecOption func(*specOptions)
|
||||
|
||||
var defaultSpecOptions = specOptions{
|
||||
Path: "",
|
||||
Document: "swagger.json",
|
||||
}
|
||||
|
||||
type specOptions struct {
|
||||
Path string
|
||||
Document string
|
||||
}
|
||||
|
||||
func specOptionsWithDefaults(opts []SpecOption) specOptions {
|
||||
o := defaultSpecOptions
|
||||
for _, apply := range opts {
|
||||
apply(&o)
|
||||
}
|
||||
|
||||
return o
|
||||
}
|
||||
|
||||
// Spec creates a middleware to serve a swagger spec as a JSON document.
|
||||
//
|
||||
// This allows for altering the spec before starting the http listener.
|
||||
// This can be useful if you want to serve the swagger spec from another path than /swagger.json
|
||||
func Spec(basePath string, b []byte, next http.Handler) http.Handler {
|
||||
//
|
||||
// The basePath argument indicates the path of the spec document (defaults to "/").
|
||||
// Additional SpecOption can be used to change the name of the document (defaults to "swagger.json").
|
||||
func Spec(basePath string, b []byte, next http.Handler, opts ...SpecOption) http.Handler {
|
||||
if basePath == "" {
|
||||
basePath = "/"
|
||||
}
|
||||
pth := path.Join(basePath, "swagger.json")
|
||||
o := specOptionsWithDefaults(opts)
|
||||
pth := path.Join(basePath, o.Path, o.Document)
|
||||
|
||||
return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
|
||||
if r.URL.Path == pth {
|
||||
rw.Header().Set("Content-Type", "application/json")
|
||||
if path.Clean(r.URL.Path) == pth {
|
||||
rw.Header().Set(contentTypeHeader, applicationJSON)
|
||||
rw.WriteHeader(http.StatusOK)
|
||||
//#nosec
|
||||
_, _ = rw.Write(b)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if next == nil {
|
||||
rw.Header().Set("Content-Type", "application/json")
|
||||
rw.WriteHeader(http.StatusNotFound)
|
||||
if next != nil {
|
||||
next.ServeHTTP(rw, r)
|
||||
|
||||
return
|
||||
}
|
||||
next.ServeHTTP(rw, r)
|
||||
|
||||
rw.Header().Set(contentTypeHeader, applicationJSON)
|
||||
rw.WriteHeader(http.StatusNotFound)
|
||||
})
|
||||
}
|
||||
|
||||
// WithSpecPath sets the path to be joined to the base path of the Spec middleware.
|
||||
//
|
||||
// This is empty by default.
|
||||
func WithSpecPath(pth string) SpecOption {
|
||||
return func(o *specOptions) {
|
||||
o.Path = pth
|
||||
}
|
||||
}
|
||||
|
||||
// WithSpecDocument sets the name of the JSON document served as a spec.
|
||||
//
|
||||
// By default, this is "swagger.json"
|
||||
func WithSpecDocument(doc string) SpecOption {
|
||||
return func(o *specOptions) {
|
||||
if doc == "" {
|
||||
return
|
||||
}
|
||||
|
||||
o.Document = doc
|
||||
}
|
||||
}
|
||||
|
|
|
|||
87
vendor/github.com/go-openapi/runtime/middleware/swaggerui.go
generated
vendored
87
vendor/github.com/go-openapi/runtime/middleware/swaggerui.go
generated
vendored
|
|
@ -8,40 +8,65 @@ import (
|
|||
"path"
|
||||
)
|
||||
|
||||
// SwaggerUIOpts configures the Swaggerui middlewares
|
||||
// SwaggerUIOpts configures the SwaggerUI middleware
|
||||
type SwaggerUIOpts struct {
|
||||
// BasePath for the UI path, defaults to: /
|
||||
// BasePath for the API, defaults to: /
|
||||
BasePath string
|
||||
// Path combines with BasePath for the full UI path, defaults to: docs
|
||||
|
||||
// Path combines with BasePath to construct the path to the UI, defaults to: "docs".
|
||||
Path string
|
||||
// SpecURL the url to find the spec for
|
||||
|
||||
// SpecURL is the URL of the spec document.
|
||||
//
|
||||
// Defaults to: /swagger.json
|
||||
SpecURL string
|
||||
|
||||
// Title for the documentation site, default to: API documentation
|
||||
Title string
|
||||
|
||||
// Template specifies a custom template to serve the UI
|
||||
Template string
|
||||
|
||||
// OAuthCallbackURL the url called after OAuth2 login
|
||||
OAuthCallbackURL string
|
||||
|
||||
// The three components needed to embed swagger-ui
|
||||
SwaggerURL string
|
||||
|
||||
// SwaggerURL points to the js that generates the SwaggerUI site.
|
||||
//
|
||||
// Defaults to: https://unpkg.com/swagger-ui-dist/swagger-ui-bundle.js
|
||||
SwaggerURL string
|
||||
|
||||
SwaggerPresetURL string
|
||||
SwaggerStylesURL string
|
||||
|
||||
Favicon32 string
|
||||
Favicon16 string
|
||||
|
||||
// Title for the documentation site, default to: API documentation
|
||||
Title string
|
||||
}
|
||||
|
||||
// EnsureDefaults in case some options are missing
|
||||
func (r *SwaggerUIOpts) EnsureDefaults() {
|
||||
if r.BasePath == "" {
|
||||
r.BasePath = "/"
|
||||
r.ensureDefaults()
|
||||
|
||||
if r.Template == "" {
|
||||
r.Template = swaggeruiTemplate
|
||||
}
|
||||
if r.Path == "" {
|
||||
r.Path = defaultDocsPath
|
||||
}
|
||||
if r.SpecURL == "" {
|
||||
r.SpecURL = defaultDocsURL
|
||||
}
|
||||
|
||||
func (r *SwaggerUIOpts) EnsureDefaultsOauth2() {
|
||||
r.ensureDefaults()
|
||||
|
||||
if r.Template == "" {
|
||||
r.Template = swaggerOAuthTemplate
|
||||
}
|
||||
}
|
||||
|
||||
func (r *SwaggerUIOpts) ensureDefaults() {
|
||||
common := toCommonUIOptions(r)
|
||||
common.EnsureDefaults()
|
||||
fromCommonToAnyOptions(common, r)
|
||||
|
||||
// swaggerui-specifics
|
||||
if r.OAuthCallbackURL == "" {
|
||||
r.OAuthCallbackURL = path.Join(r.BasePath, r.Path, "oauth2-callback")
|
||||
}
|
||||
|
|
@ -60,40 +85,22 @@ func (r *SwaggerUIOpts) EnsureDefaults() {
|
|||
if r.Favicon32 == "" {
|
||||
r.Favicon32 = swaggerFavicon32Latest
|
||||
}
|
||||
if r.Title == "" {
|
||||
r.Title = defaultDocsTitle
|
||||
}
|
||||
}
|
||||
|
||||
// SwaggerUI creates a middleware to serve a documentation site for a swagger spec.
|
||||
//
|
||||
// This allows for altering the spec before starting the http listener.
|
||||
func SwaggerUI(opts SwaggerUIOpts, next http.Handler) http.Handler {
|
||||
opts.EnsureDefaults()
|
||||
|
||||
pth := path.Join(opts.BasePath, opts.Path)
|
||||
tmpl := template.Must(template.New("swaggerui").Parse(swaggeruiTemplate))
|
||||
tmpl := template.Must(template.New("swaggerui").Parse(opts.Template))
|
||||
assets := bytes.NewBuffer(nil)
|
||||
if err := tmpl.Execute(assets, opts); err != nil {
|
||||
panic(fmt.Errorf("cannot execute template: %w", err))
|
||||
}
|
||||
|
||||
buf := bytes.NewBuffer(nil)
|
||||
_ = tmpl.Execute(buf, &opts)
|
||||
b := buf.Bytes()
|
||||
|
||||
return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
|
||||
if path.Join(r.URL.Path) == pth {
|
||||
rw.Header().Set("Content-Type", "text/html; charset=utf-8")
|
||||
rw.WriteHeader(http.StatusOK)
|
||||
|
||||
_, _ = rw.Write(b)
|
||||
return
|
||||
}
|
||||
|
||||
if next == nil {
|
||||
rw.Header().Set("Content-Type", "text/plain")
|
||||
rw.WriteHeader(http.StatusNotFound)
|
||||
_, _ = rw.Write([]byte(fmt.Sprintf("%q not found", pth)))
|
||||
return
|
||||
}
|
||||
next.ServeHTTP(rw, r)
|
||||
})
|
||||
return serveUI(pth, assets.Bytes(), next)
|
||||
}
|
||||
|
||||
const (
|
||||
|
|
|
|||
31
vendor/github.com/go-openapi/runtime/middleware/swaggerui_oauth2.go
generated
vendored
31
vendor/github.com/go-openapi/runtime/middleware/swaggerui_oauth2.go
generated
vendored
|
|
@ -4,37 +4,20 @@ import (
|
|||
"bytes"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"path"
|
||||
"text/template"
|
||||
)
|
||||
|
||||
func SwaggerUIOAuth2Callback(opts SwaggerUIOpts, next http.Handler) http.Handler {
|
||||
opts.EnsureDefaults()
|
||||
opts.EnsureDefaultsOauth2()
|
||||
|
||||
pth := opts.OAuthCallbackURL
|
||||
tmpl := template.Must(template.New("swaggeroauth").Parse(swaggerOAuthTemplate))
|
||||
tmpl := template.Must(template.New("swaggeroauth").Parse(opts.Template))
|
||||
assets := bytes.NewBuffer(nil)
|
||||
if err := tmpl.Execute(assets, opts); err != nil {
|
||||
panic(fmt.Errorf("cannot execute template: %w", err))
|
||||
}
|
||||
|
||||
buf := bytes.NewBuffer(nil)
|
||||
_ = tmpl.Execute(buf, &opts)
|
||||
b := buf.Bytes()
|
||||
|
||||
return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
|
||||
if path.Join(r.URL.Path) == pth {
|
||||
rw.Header().Set("Content-Type", "text/html; charset=utf-8")
|
||||
rw.WriteHeader(http.StatusOK)
|
||||
|
||||
_, _ = rw.Write(b)
|
||||
return
|
||||
}
|
||||
|
||||
if next == nil {
|
||||
rw.Header().Set("Content-Type", "text/plain")
|
||||
rw.WriteHeader(http.StatusNotFound)
|
||||
_, _ = rw.Write([]byte(fmt.Sprintf("%q not found", pth)))
|
||||
return
|
||||
}
|
||||
next.ServeHTTP(rw, r)
|
||||
})
|
||||
return serveUI(pth, assets.Bytes(), next)
|
||||
}
|
||||
|
||||
const (
|
||||
|
|
|
|||
8
vendor/github.com/go-openapi/runtime/middleware/ui_defaults.go
generated
vendored
8
vendor/github.com/go-openapi/runtime/middleware/ui_defaults.go
generated
vendored
|
|
@ -1,8 +0,0 @@
|
|||
package middleware
|
||||
|
||||
const (
|
||||
// constants that are common to all UI-serving middlewares
|
||||
defaultDocsPath = "docs"
|
||||
defaultDocsURL = "/swagger.json"
|
||||
defaultDocsTitle = "API Documentation"
|
||||
)
|
||||
173
vendor/github.com/go-openapi/runtime/middleware/ui_options.go
generated
vendored
Normal file
173
vendor/github.com/go-openapi/runtime/middleware/ui_options.go
generated
vendored
Normal file
|
|
@ -0,0 +1,173 @@
|
|||
package middleware
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/gob"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"path"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
// constants that are common to all UI-serving middlewares
|
||||
defaultDocsPath = "docs"
|
||||
defaultDocsURL = "/swagger.json"
|
||||
defaultDocsTitle = "API Documentation"
|
||||
)
|
||||
|
||||
// uiOptions defines common options for UI serving middlewares.
|
||||
type uiOptions struct {
|
||||
// BasePath for the UI, defaults to: /
|
||||
BasePath string
|
||||
|
||||
// Path combines with BasePath to construct the path to the UI, defaults to: "docs".
|
||||
Path string
|
||||
|
||||
// SpecURL is the URL of the spec document.
|
||||
//
|
||||
// Defaults to: /swagger.json
|
||||
SpecURL string
|
||||
|
||||
// Title for the documentation site, default to: API documentation
|
||||
Title string
|
||||
|
||||
// Template specifies a custom template to serve the UI
|
||||
Template string
|
||||
}
|
||||
|
||||
// toCommonUIOptions converts any UI option type to retain the common options.
|
||||
//
|
||||
// This uses gob encoding/decoding to convert common fields from one struct to another.
|
||||
func toCommonUIOptions(opts interface{}) uiOptions {
|
||||
var buf bytes.Buffer
|
||||
enc := gob.NewEncoder(&buf)
|
||||
dec := gob.NewDecoder(&buf)
|
||||
var o uiOptions
|
||||
err := enc.Encode(opts)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
err = dec.Decode(&o)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return o
|
||||
}
|
||||
|
||||
func fromCommonToAnyOptions[T any](source uiOptions, target *T) {
|
||||
var buf bytes.Buffer
|
||||
enc := gob.NewEncoder(&buf)
|
||||
dec := gob.NewDecoder(&buf)
|
||||
err := enc.Encode(source)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
err = dec.Decode(target)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
// UIOption can be applied to UI serving middleware, such as Context.APIHandler or
|
||||
// Context.APIHandlerSwaggerUI to alter the defaut behavior.
|
||||
type UIOption func(*uiOptions)
|
||||
|
||||
func uiOptionsWithDefaults(opts []UIOption) uiOptions {
|
||||
var o uiOptions
|
||||
for _, apply := range opts {
|
||||
apply(&o)
|
||||
}
|
||||
|
||||
return o
|
||||
}
|
||||
|
||||
// WithUIBasePath sets the base path from where to serve the UI assets.
|
||||
//
|
||||
// By default, Context middleware sets this value to the API base path.
|
||||
func WithUIBasePath(base string) UIOption {
|
||||
return func(o *uiOptions) {
|
||||
if !strings.HasPrefix(base, "/") {
|
||||
base = "/" + base
|
||||
}
|
||||
o.BasePath = base
|
||||
}
|
||||
}
|
||||
|
||||
// WithUIPath sets the path from where to serve the UI assets (i.e. /{basepath}/{path}.
|
||||
func WithUIPath(pth string) UIOption {
|
||||
return func(o *uiOptions) {
|
||||
o.Path = pth
|
||||
}
|
||||
}
|
||||
|
||||
// WithUISpecURL sets the path from where to serve swagger spec document.
|
||||
//
|
||||
// This may be specified as a full URL or a path.
|
||||
//
|
||||
// By default, this is "/swagger.json"
|
||||
func WithUISpecURL(specURL string) UIOption {
|
||||
return func(o *uiOptions) {
|
||||
o.SpecURL = specURL
|
||||
}
|
||||
}
|
||||
|
||||
// WithUITitle sets the title of the UI.
|
||||
//
|
||||
// By default, Context middleware sets this value to the title found in the API spec.
|
||||
func WithUITitle(title string) UIOption {
|
||||
return func(o *uiOptions) {
|
||||
o.Title = title
|
||||
}
|
||||
}
|
||||
|
||||
// WithTemplate allows to set a custom template for the UI.
|
||||
//
|
||||
// UI middleware will panic if the template does not parse or execute properly.
|
||||
func WithTemplate(tpl string) UIOption {
|
||||
return func(o *uiOptions) {
|
||||
o.Template = tpl
|
||||
}
|
||||
}
|
||||
|
||||
// EnsureDefaults in case some options are missing
|
||||
func (r *uiOptions) EnsureDefaults() {
|
||||
if r.BasePath == "" {
|
||||
r.BasePath = "/"
|
||||
}
|
||||
if r.Path == "" {
|
||||
r.Path = defaultDocsPath
|
||||
}
|
||||
if r.SpecURL == "" {
|
||||
r.SpecURL = defaultDocsURL
|
||||
}
|
||||
if r.Title == "" {
|
||||
r.Title = defaultDocsTitle
|
||||
}
|
||||
}
|
||||
|
||||
// serveUI creates a middleware that serves a templated asset as text/html.
|
||||
func serveUI(pth string, assets []byte, next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
|
||||
if path.Clean(r.URL.Path) == pth {
|
||||
rw.Header().Set(contentTypeHeader, "text/html; charset=utf-8")
|
||||
rw.WriteHeader(http.StatusOK)
|
||||
_, _ = rw.Write(assets)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if next != nil {
|
||||
next.ServeHTTP(rw, r)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
rw.Header().Set(contentTypeHeader, "text/plain")
|
||||
rw.WriteHeader(http.StatusNotFound)
|
||||
_, _ = rw.Write([]byte(fmt.Sprintf("%q not found", pth)))
|
||||
})
|
||||
}
|
||||
12
vendor/github.com/go-openapi/runtime/middleware/validation.go
generated
vendored
12
vendor/github.com/go-openapi/runtime/middleware/validation.go
generated
vendored
|
|
@ -35,7 +35,6 @@ type validation struct {
|
|||
|
||||
// ContentType validates the content type of a request
|
||||
func validateContentType(allowed []string, actual string) error {
|
||||
debugLog("validating content type for %q against [%s]", actual, strings.Join(allowed, ", "))
|
||||
if len(allowed) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
|
@ -57,13 +56,13 @@ func validateContentType(allowed []string, actual string) error {
|
|||
}
|
||||
|
||||
func validateRequest(ctx *Context, request *http.Request, route *MatchedRoute) *validation {
|
||||
debugLog("validating request %s %s", request.Method, request.URL.EscapedPath())
|
||||
validate := &validation{
|
||||
context: ctx,
|
||||
request: request,
|
||||
route: route,
|
||||
bound: make(map[string]interface{}),
|
||||
}
|
||||
validate.debugLogf("validating request %s %s", request.Method, request.URL.EscapedPath())
|
||||
|
||||
validate.contentType()
|
||||
if len(validate.result) == 0 {
|
||||
|
|
@ -76,8 +75,12 @@ func validateRequest(ctx *Context, request *http.Request, route *MatchedRoute) *
|
|||
return validate
|
||||
}
|
||||
|
||||
func (v *validation) debugLogf(format string, args ...any) {
|
||||
v.context.debugLogf(format, args...)
|
||||
}
|
||||
|
||||
func (v *validation) parameters() {
|
||||
debugLog("validating request parameters for %s %s", v.request.Method, v.request.URL.EscapedPath())
|
||||
v.debugLogf("validating request parameters for %s %s", v.request.Method, v.request.URL.EscapedPath())
|
||||
if result := v.route.Binder.Bind(v.request, v.route.Params, v.route.Consumer, v.bound); result != nil {
|
||||
if result.Error() == "validation failure list" {
|
||||
for _, e := range result.(*errors.Validation).Value.([]interface{}) {
|
||||
|
|
@ -91,7 +94,7 @@ func (v *validation) parameters() {
|
|||
|
||||
func (v *validation) contentType() {
|
||||
if len(v.result) == 0 && runtime.HasBody(v.request) {
|
||||
debugLog("validating body content type for %s %s", v.request.Method, v.request.URL.EscapedPath())
|
||||
v.debugLogf("validating body content type for %s %s", v.request.Method, v.request.URL.EscapedPath())
|
||||
ct, _, req, err := v.context.ContentType(v.request)
|
||||
if err != nil {
|
||||
v.result = append(v.result, err)
|
||||
|
|
@ -100,6 +103,7 @@ func (v *validation) contentType() {
|
|||
}
|
||||
|
||||
if len(v.result) == 0 {
|
||||
v.debugLogf("validating content type for %q against [%s]", ct, strings.Join(v.route.Consumes, ", "))
|
||||
if err := validateContentType(v.route.Consumes, ct); err != nil {
|
||||
v.result = append(v.result, err)
|
||||
}
|
||||
|
|
|
|||
23
vendor/github.com/go-openapi/spec/README.md
generated
vendored
23
vendor/github.com/go-openapi/spec/README.md
generated
vendored
|
|
@ -29,3 +29,26 @@ The object model for OpenAPI specification documents.
|
|||
> This [discussion thread](https://github.com/go-openapi/spec/issues/21) relates the full story.
|
||||
>
|
||||
> An early attempt to support Swagger 3 may be found at: https://github.com/go-openapi/spec3
|
||||
|
||||
* Does the unmarshaling support YAML?
|
||||
|
||||
> Not directly. The exposed types know only how to unmarshal from JSON.
|
||||
>
|
||||
> In order to load a YAML document as a Swagger spec, you need to use the loaders provided by
|
||||
> github.com/go-openapi/loads
|
||||
>
|
||||
> Take a look at the example there: https://pkg.go.dev/github.com/go-openapi/loads#example-Spec
|
||||
>
|
||||
> See also https://github.com/go-openapi/spec/issues/164
|
||||
|
||||
* How can I validate a spec?
|
||||
|
||||
> Validation is provided by [the validate package](http://github.com/go-openapi/validate)
|
||||
|
||||
* Why do we have an `ID` field for `Schema` which is not part of the swagger spec?
|
||||
|
||||
> We found jsonschema compatibility more important: since `id` in jsonschema influences
|
||||
> how `$ref` are resolved.
|
||||
> This `id` does not conflict with any property named `id`.
|
||||
>
|
||||
> See also https://github.com/go-openapi/spec/issues/23
|
||||
|
|
|
|||
58
vendor/github.com/go-openapi/spec/expander.go
generated
vendored
58
vendor/github.com/go-openapi/spec/expander.go
generated
vendored
|
|
@ -57,7 +57,7 @@ func ExpandSpec(spec *Swagger, options *ExpandOptions) error {
|
|||
if !options.SkipSchemas {
|
||||
for key, definition := range spec.Definitions {
|
||||
parentRefs := make([]string, 0, 10)
|
||||
parentRefs = append(parentRefs, fmt.Sprintf("#/definitions/%s", key))
|
||||
parentRefs = append(parentRefs, "#/definitions/"+key)
|
||||
|
||||
def, err := expandSchema(definition, parentRefs, resolver, specBasePath)
|
||||
if resolver.shouldStopOnError(err) {
|
||||
|
|
@ -213,7 +213,19 @@ func expandSchema(target Schema, parentRefs []string, resolver *schemaLoader, ba
|
|||
}
|
||||
|
||||
if target.Ref.String() != "" {
|
||||
return expandSchemaRef(target, parentRefs, resolver, basePath)
|
||||
if !resolver.options.SkipSchemas {
|
||||
return expandSchemaRef(target, parentRefs, resolver, basePath)
|
||||
}
|
||||
|
||||
// when "expand" with SkipSchema, we just rebase the existing $ref without replacing
|
||||
// the full schema.
|
||||
rebasedRef, err := NewRef(normalizeURI(target.Ref.String(), basePath))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
target.Ref = denormalizeRef(&rebasedRef, resolver.context.basePath, resolver.context.rootID)
|
||||
|
||||
return &target, nil
|
||||
}
|
||||
|
||||
for k := range target.Definitions {
|
||||
|
|
@ -525,21 +537,25 @@ func getRefAndSchema(input interface{}) (*Ref, *Schema, error) {
|
|||
}
|
||||
|
||||
func expandParameterOrResponse(input interface{}, resolver *schemaLoader, basePath string) error {
|
||||
ref, _, err := getRefAndSchema(input)
|
||||
ref, sch, err := getRefAndSchema(input)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if ref == nil {
|
||||
if ref == nil && sch == nil { // nothing to do
|
||||
return nil
|
||||
}
|
||||
|
||||
parentRefs := make([]string, 0, 10)
|
||||
if err = resolver.deref(input, parentRefs, basePath); resolver.shouldStopOnError(err) {
|
||||
return err
|
||||
if ref != nil {
|
||||
// dereference this $ref
|
||||
if err = resolver.deref(input, parentRefs, basePath); resolver.shouldStopOnError(err) {
|
||||
return err
|
||||
}
|
||||
|
||||
ref, sch, _ = getRefAndSchema(input)
|
||||
}
|
||||
|
||||
ref, sch, _ := getRefAndSchema(input)
|
||||
if ref.String() != "" {
|
||||
transitiveResolver := resolver.transitiveResolver(basePath, *ref)
|
||||
basePath = resolver.updateBasePath(transitiveResolver, basePath)
|
||||
|
|
@ -551,6 +567,7 @@ func expandParameterOrResponse(input interface{}, resolver *schemaLoader, basePa
|
|||
if ref != nil {
|
||||
*ref = Ref{}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -560,38 +577,29 @@ func expandParameterOrResponse(input interface{}, resolver *schemaLoader, basePa
|
|||
return ern
|
||||
}
|
||||
|
||||
switch {
|
||||
case resolver.isCircular(&rebasedRef, basePath, parentRefs...):
|
||||
if resolver.isCircular(&rebasedRef, basePath, parentRefs...) {
|
||||
// this is a circular $ref: stop expansion
|
||||
if !resolver.options.AbsoluteCircularRef {
|
||||
sch.Ref = denormalizeRef(&rebasedRef, resolver.context.basePath, resolver.context.rootID)
|
||||
} else {
|
||||
sch.Ref = rebasedRef
|
||||
}
|
||||
case !resolver.options.SkipSchemas:
|
||||
// schema expanded to a $ref in another root
|
||||
sch.Ref = rebasedRef
|
||||
debugLog("rebased to: %s", sch.Ref.String())
|
||||
default:
|
||||
// skip schema expansion but rebase $ref to schema
|
||||
sch.Ref = denormalizeRef(&rebasedRef, resolver.context.basePath, resolver.context.rootID)
|
||||
}
|
||||
}
|
||||
|
||||
// $ref expansion or rebasing is performed by expandSchema below
|
||||
if ref != nil {
|
||||
*ref = Ref{}
|
||||
}
|
||||
|
||||
// expand schema
|
||||
if !resolver.options.SkipSchemas {
|
||||
s, err := expandSchema(*sch, parentRefs, resolver, basePath)
|
||||
if resolver.shouldStopOnError(err) {
|
||||
return err
|
||||
}
|
||||
if s == nil {
|
||||
// guard for when continuing on error
|
||||
return nil
|
||||
}
|
||||
// yes, we do it even if options.SkipSchema is true: we have to go down that rabbit hole and rebase nested $ref)
|
||||
s, err := expandSchema(*sch, parentRefs, resolver, basePath)
|
||||
if resolver.shouldStopOnError(err) {
|
||||
return err
|
||||
}
|
||||
|
||||
if s != nil { // guard for when continuing on error
|
||||
*sch = *s
|
||||
}
|
||||
|
||||
|
|
|
|||
56
vendor/github.com/go-openapi/strfmt/default.go
generated
vendored
56
vendor/github.com/go-openapi/strfmt/default.go
generated
vendored
|
|
@ -25,6 +25,7 @@ import (
|
|||
"strings"
|
||||
|
||||
"github.com/asaskevich/govalidator"
|
||||
"github.com/google/uuid"
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
)
|
||||
|
||||
|
|
@ -57,24 +58,35 @@ const (
|
|||
// - long top-level domain names (e.g. example.london) are permitted
|
||||
// - symbol unicode points are permitted (e.g. emoji) (not for top-level domain)
|
||||
HostnamePattern = `^([a-zA-Z0-9\p{S}\p{L}]((-?[a-zA-Z0-9\p{S}\p{L}]{0,62})?)|([a-zA-Z0-9\p{S}\p{L}](([a-zA-Z0-9-\p{S}\p{L}]{0,61}[a-zA-Z0-9\p{S}\p{L}])?)(\.)){1,}([a-zA-Z\p{L}]){2,63})$`
|
||||
// UUIDPattern Regex for UUID that allows uppercase
|
||||
UUIDPattern = `(?i)^[0-9a-f]{8}-?[0-9a-f]{4}-?[0-9a-f]{4}-?[0-9a-f]{4}-?[0-9a-f]{12}$`
|
||||
// UUID3Pattern Regex for UUID3 that allows uppercase
|
||||
UUID3Pattern = `(?i)^[0-9a-f]{8}-?[0-9a-f]{4}-?3[0-9a-f]{3}-?[0-9a-f]{4}-?[0-9a-f]{12}$`
|
||||
// UUID4Pattern Regex for UUID4 that allows uppercase
|
||||
UUID4Pattern = `(?i)^[0-9a-f]{8}-?[0-9a-f]{4}-?4[0-9a-f]{3}-?[89ab][0-9a-f]{3}-?[0-9a-f]{12}$`
|
||||
// UUID5Pattern Regex for UUID5 that allows uppercase
|
||||
UUID5Pattern = `(?i)^[0-9a-f]{8}-?[0-9a-f]{4}-?5[0-9a-f]{3}-?[89ab][0-9a-f]{3}-?[0-9a-f]{12}$`
|
||||
|
||||
// json null type
|
||||
jsonNull = "null"
|
||||
)
|
||||
|
||||
const (
|
||||
// UUIDPattern Regex for UUID that allows uppercase
|
||||
//
|
||||
// Deprecated: strfmt no longer uses regular expressions to validate UUIDs.
|
||||
UUIDPattern = `(?i)(^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$)|(^[0-9a-f]{32}$)`
|
||||
|
||||
// UUID3Pattern Regex for UUID3 that allows uppercase
|
||||
//
|
||||
// Deprecated: strfmt no longer uses regular expressions to validate UUIDs.
|
||||
UUID3Pattern = `(?i)(^[0-9a-f]{8}-[0-9a-f]{4}-3[0-9a-f]{3}-[0-9a-f]{4}-[0-9a-f]{12}$)|(^[0-9a-f]{12}3[0-9a-f]{3}?[0-9a-f]{16}$)`
|
||||
|
||||
// UUID4Pattern Regex for UUID4 that allows uppercase
|
||||
//
|
||||
// Deprecated: strfmt no longer uses regular expressions to validate UUIDs.
|
||||
UUID4Pattern = `(?i)(^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$)|(^[0-9a-f]{12}4[0-9a-f]{3}[89ab][0-9a-f]{15}$)`
|
||||
|
||||
// UUID5Pattern Regex for UUID5 that allows uppercase
|
||||
//
|
||||
// Deprecated: strfmt no longer uses regular expressions to validate UUIDs.
|
||||
UUID5Pattern = `(?i)(^[0-9a-f]{8}-[0-9a-f]{4}-5[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$)|(^[0-9a-f]{12}5[0-9a-f]{3}[89ab][0-9a-f]{15}$)`
|
||||
)
|
||||
|
||||
var (
|
||||
rxHostname = regexp.MustCompile(HostnamePattern)
|
||||
rxUUID = regexp.MustCompile(UUIDPattern)
|
||||
rxUUID3 = regexp.MustCompile(UUID3Pattern)
|
||||
rxUUID4 = regexp.MustCompile(UUID4Pattern)
|
||||
rxUUID5 = regexp.MustCompile(UUID5Pattern)
|
||||
)
|
||||
|
||||
// IsHostname returns true when the string is a valid hostname
|
||||
|
|
@ -99,24 +111,28 @@ func IsHostname(str string) bool {
|
|||
return valid
|
||||
}
|
||||
|
||||
// IsUUID returns true is the string matches a UUID, upper case is allowed
|
||||
// IsUUID returns true is the string matches a UUID (in any version, including v6 and v7), upper case is allowed
|
||||
func IsUUID(str string) bool {
|
||||
return rxUUID.MatchString(str)
|
||||
_, err := uuid.Parse(str)
|
||||
return err == nil
|
||||
}
|
||||
|
||||
// IsUUID3 returns true is the string matches a UUID, upper case is allowed
|
||||
// IsUUID3 returns true is the string matches a UUID v3, upper case is allowed
|
||||
func IsUUID3(str string) bool {
|
||||
return rxUUID3.MatchString(str)
|
||||
id, err := uuid.Parse(str)
|
||||
return err == nil && id.Version() == uuid.Version(3)
|
||||
}
|
||||
|
||||
// IsUUID4 returns true is the string matches a UUID, upper case is allowed
|
||||
// IsUUID4 returns true is the string matches a UUID v4, upper case is allowed
|
||||
func IsUUID4(str string) bool {
|
||||
return rxUUID4.MatchString(str)
|
||||
id, err := uuid.Parse(str)
|
||||
return err == nil && id.Version() == uuid.Version(4)
|
||||
}
|
||||
|
||||
// IsUUID5 returns true is the string matches a UUID, upper case is allowed
|
||||
// IsUUID5 returns true is the string matches a UUID v5, upper case is allowed
|
||||
func IsUUID5(str string) bool {
|
||||
return rxUUID5.MatchString(str)
|
||||
id, err := uuid.Parse(str)
|
||||
return err == nil && id.Version() == uuid.Version(5)
|
||||
}
|
||||
|
||||
// IsEmail validates an email address.
|
||||
|
|
|
|||
3
vendor/github.com/go-openapi/strfmt/format.go
generated
vendored
3
vendor/github.com/go-openapi/strfmt/format.go
generated
vendored
|
|
@ -16,6 +16,7 @@ package strfmt
|
|||
|
||||
import (
|
||||
"encoding"
|
||||
stderrors "errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
|
@ -117,7 +118,7 @@ func (f *defaultFormats) MapStructureHookFunc() mapstructure.DecodeHookFunc {
|
|||
case "datetime":
|
||||
input := data
|
||||
if len(input) == 0 {
|
||||
return nil, fmt.Errorf("empty string is an invalid datetime format")
|
||||
return nil, stderrors.New("empty string is an invalid datetime format")
|
||||
}
|
||||
return ParseDateTime(input)
|
||||
case "duration":
|
||||
|
|
|
|||
52
vendor/github.com/go-openapi/swag/BENCHMARK.md
generated
vendored
Normal file
52
vendor/github.com/go-openapi/swag/BENCHMARK.md
generated
vendored
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
# Benchmarks
|
||||
|
||||
## Name mangling utilities
|
||||
|
||||
```bash
|
||||
go test -bench XXX -run XXX -benchtime 30s
|
||||
```
|
||||
|
||||
### Benchmarks at b3e7a5386f996177e4808f11acb2aa93a0f660df
|
||||
|
||||
```
|
||||
goos: linux
|
||||
goarch: amd64
|
||||
pkg: github.com/go-openapi/swag
|
||||
cpu: Intel(R) Core(TM) i5-6200U CPU @ 2.30GHz
|
||||
BenchmarkToXXXName/ToGoName-4 862623 44101 ns/op 10450 B/op 732 allocs/op
|
||||
BenchmarkToXXXName/ToVarName-4 853656 40728 ns/op 10468 B/op 734 allocs/op
|
||||
BenchmarkToXXXName/ToFileName-4 1268312 27813 ns/op 9785 B/op 617 allocs/op
|
||||
BenchmarkToXXXName/ToCommandName-4 1276322 27903 ns/op 9785 B/op 617 allocs/op
|
||||
BenchmarkToXXXName/ToHumanNameLower-4 895334 40354 ns/op 10472 B/op 731 allocs/op
|
||||
BenchmarkToXXXName/ToHumanNameTitle-4 882441 40678 ns/op 10566 B/op 749 allocs/op
|
||||
```
|
||||
|
||||
### Benchmarks after PR #79
|
||||
|
||||
~ x10 performance improvement and ~ /100 memory allocations.
|
||||
|
||||
```
|
||||
goos: linux
|
||||
goarch: amd64
|
||||
pkg: github.com/go-openapi/swag
|
||||
cpu: Intel(R) Core(TM) i5-6200U CPU @ 2.30GHz
|
||||
BenchmarkToXXXName/ToGoName-4 9595830 3991 ns/op 42 B/op 5 allocs/op
|
||||
BenchmarkToXXXName/ToVarName-4 9194276 3984 ns/op 62 B/op 7 allocs/op
|
||||
BenchmarkToXXXName/ToFileName-4 17002711 2123 ns/op 147 B/op 7 allocs/op
|
||||
BenchmarkToXXXName/ToCommandName-4 16772926 2111 ns/op 147 B/op 7 allocs/op
|
||||
BenchmarkToXXXName/ToHumanNameLower-4 9788331 3749 ns/op 92 B/op 6 allocs/op
|
||||
BenchmarkToXXXName/ToHumanNameTitle-4 9188260 3941 ns/op 104 B/op 6 allocs/op
|
||||
```
|
||||
|
||||
```
|
||||
goos: linux
|
||||
goarch: amd64
|
||||
pkg: github.com/go-openapi/swag
|
||||
cpu: AMD Ryzen 7 5800X 8-Core Processor
|
||||
BenchmarkToXXXName/ToGoName-16 18527378 1972 ns/op 42 B/op 5 allocs/op
|
||||
BenchmarkToXXXName/ToVarName-16 15552692 2093 ns/op 62 B/op 7 allocs/op
|
||||
BenchmarkToXXXName/ToFileName-16 32161176 1117 ns/op 147 B/op 7 allocs/op
|
||||
BenchmarkToXXXName/ToCommandName-16 32256634 1137 ns/op 147 B/op 7 allocs/op
|
||||
BenchmarkToXXXName/ToHumanNameLower-16 18599661 1946 ns/op 92 B/op 6 allocs/op
|
||||
BenchmarkToXXXName/ToHumanNameTitle-16 17581353 2054 ns/op 105 B/op 6 allocs/op
|
||||
```
|
||||
139
vendor/github.com/go-openapi/swag/initialism_index.go
generated
vendored
139
vendor/github.com/go-openapi/swag/initialism_index.go
generated
vendored
|
|
@ -16,9 +16,130 @@ package swag
|
|||
|
||||
import (
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
var (
|
||||
// commonInitialisms are common acronyms that are kept as whole uppercased words.
|
||||
commonInitialisms *indexOfInitialisms
|
||||
|
||||
// initialisms is a slice of sorted initialisms
|
||||
initialisms []string
|
||||
|
||||
// a copy of initialisms pre-baked as []rune
|
||||
initialismsRunes [][]rune
|
||||
initialismsUpperCased [][]rune
|
||||
|
||||
isInitialism func(string) bool
|
||||
|
||||
maxAllocMatches int
|
||||
)
|
||||
|
||||
func init() {
|
||||
// Taken from https://github.com/golang/lint/blob/3390df4df2787994aea98de825b964ac7944b817/lint.go#L732-L769
|
||||
configuredInitialisms := map[string]bool{
|
||||
"ACL": true,
|
||||
"API": true,
|
||||
"ASCII": true,
|
||||
"CPU": true,
|
||||
"CSS": true,
|
||||
"DNS": true,
|
||||
"EOF": true,
|
||||
"GUID": true,
|
||||
"HTML": true,
|
||||
"HTTPS": true,
|
||||
"HTTP": true,
|
||||
"ID": true,
|
||||
"IP": true,
|
||||
"IPv4": true,
|
||||
"IPv6": true,
|
||||
"JSON": true,
|
||||
"LHS": true,
|
||||
"OAI": true,
|
||||
"QPS": true,
|
||||
"RAM": true,
|
||||
"RHS": true,
|
||||
"RPC": true,
|
||||
"SLA": true,
|
||||
"SMTP": true,
|
||||
"SQL": true,
|
||||
"SSH": true,
|
||||
"TCP": true,
|
||||
"TLS": true,
|
||||
"TTL": true,
|
||||
"UDP": true,
|
||||
"UI": true,
|
||||
"UID": true,
|
||||
"UUID": true,
|
||||
"URI": true,
|
||||
"URL": true,
|
||||
"UTF8": true,
|
||||
"VM": true,
|
||||
"XML": true,
|
||||
"XMPP": true,
|
||||
"XSRF": true,
|
||||
"XSS": true,
|
||||
}
|
||||
|
||||
// a thread-safe index of initialisms
|
||||
commonInitialisms = newIndexOfInitialisms().load(configuredInitialisms)
|
||||
initialisms = commonInitialisms.sorted()
|
||||
initialismsRunes = asRunes(initialisms)
|
||||
initialismsUpperCased = asUpperCased(initialisms)
|
||||
maxAllocMatches = maxAllocHeuristic(initialismsRunes)
|
||||
|
||||
// a test function
|
||||
isInitialism = commonInitialisms.isInitialism
|
||||
}
|
||||
|
||||
func asRunes(in []string) [][]rune {
|
||||
out := make([][]rune, len(in))
|
||||
for i, initialism := range in {
|
||||
out[i] = []rune(initialism)
|
||||
}
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
func asUpperCased(in []string) [][]rune {
|
||||
out := make([][]rune, len(in))
|
||||
|
||||
for i, initialism := range in {
|
||||
out[i] = []rune(upper(trim(initialism)))
|
||||
}
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
func maxAllocHeuristic(in [][]rune) int {
|
||||
heuristic := make(map[rune]int)
|
||||
for _, initialism := range in {
|
||||
heuristic[initialism[0]]++
|
||||
}
|
||||
|
||||
var maxAlloc int
|
||||
for _, val := range heuristic {
|
||||
if val > maxAlloc {
|
||||
maxAlloc = val
|
||||
}
|
||||
}
|
||||
|
||||
return maxAlloc
|
||||
}
|
||||
|
||||
// AddInitialisms add additional initialisms
|
||||
func AddInitialisms(words ...string) {
|
||||
for _, word := range words {
|
||||
// commonInitialisms[upper(word)] = true
|
||||
commonInitialisms.add(upper(word))
|
||||
}
|
||||
// sort again
|
||||
initialisms = commonInitialisms.sorted()
|
||||
initialismsRunes = asRunes(initialisms)
|
||||
initialismsUpperCased = asUpperCased(initialisms)
|
||||
}
|
||||
|
||||
// indexOfInitialisms is a thread-safe implementation of the sorted index of initialisms.
|
||||
// Since go1.9, this may be implemented with sync.Map.
|
||||
type indexOfInitialisms struct {
|
||||
|
|
@ -55,7 +176,7 @@ func (m *indexOfInitialisms) add(key string) *indexOfInitialisms {
|
|||
func (m *indexOfInitialisms) sorted() (result []string) {
|
||||
m.sortMutex.Lock()
|
||||
defer m.sortMutex.Unlock()
|
||||
m.index.Range(func(key, value interface{}) bool {
|
||||
m.index.Range(func(key, _ interface{}) bool {
|
||||
k := key.(string)
|
||||
result = append(result, k)
|
||||
return true
|
||||
|
|
@ -63,3 +184,19 @@ func (m *indexOfInitialisms) sorted() (result []string) {
|
|||
sort.Sort(sort.Reverse(byInitialism(result)))
|
||||
return
|
||||
}
|
||||
|
||||
type byInitialism []string
|
||||
|
||||
func (s byInitialism) Len() int {
|
||||
return len(s)
|
||||
}
|
||||
func (s byInitialism) Swap(i, j int) {
|
||||
s[i], s[j] = s[j], s[i]
|
||||
}
|
||||
func (s byInitialism) Less(i, j int) bool {
|
||||
if len(s[i]) != len(s[j]) {
|
||||
return len(s[i]) < len(s[j])
|
||||
}
|
||||
|
||||
return strings.Compare(s[i], s[j]) > 0
|
||||
}
|
||||
|
|
|
|||
72
vendor/github.com/go-openapi/swag/name_lexem.go
generated
vendored
72
vendor/github.com/go-openapi/swag/name_lexem.go
generated
vendored
|
|
@ -14,74 +14,80 @@
|
|||
|
||||
package swag
|
||||
|
||||
import "unicode"
|
||||
import (
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
type (
|
||||
nameLexem interface {
|
||||
GetUnsafeGoName() string
|
||||
GetOriginal() string
|
||||
IsInitialism() bool
|
||||
}
|
||||
lexemKind uint8
|
||||
|
||||
initialismNameLexem struct {
|
||||
nameLexem struct {
|
||||
original string
|
||||
matchedInitialism string
|
||||
}
|
||||
|
||||
casualNameLexem struct {
|
||||
original string
|
||||
kind lexemKind
|
||||
}
|
||||
)
|
||||
|
||||
func newInitialismNameLexem(original, matchedInitialism string) *initialismNameLexem {
|
||||
return &initialismNameLexem{
|
||||
const (
|
||||
lexemKindCasualName lexemKind = iota
|
||||
lexemKindInitialismName
|
||||
)
|
||||
|
||||
func newInitialismNameLexem(original, matchedInitialism string) nameLexem {
|
||||
return nameLexem{
|
||||
kind: lexemKindInitialismName,
|
||||
original: original,
|
||||
matchedInitialism: matchedInitialism,
|
||||
}
|
||||
}
|
||||
|
||||
func newCasualNameLexem(original string) *casualNameLexem {
|
||||
return &casualNameLexem{
|
||||
func newCasualNameLexem(original string) nameLexem {
|
||||
return nameLexem{
|
||||
kind: lexemKindCasualName,
|
||||
original: original,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *initialismNameLexem) GetUnsafeGoName() string {
|
||||
return l.matchedInitialism
|
||||
}
|
||||
func (l nameLexem) GetUnsafeGoName() string {
|
||||
if l.kind == lexemKindInitialismName {
|
||||
return l.matchedInitialism
|
||||
}
|
||||
|
||||
var (
|
||||
first rune
|
||||
rest string
|
||||
)
|
||||
|
||||
func (l *casualNameLexem) GetUnsafeGoName() string {
|
||||
var first rune
|
||||
var rest string
|
||||
for i, orig := range l.original {
|
||||
if i == 0 {
|
||||
first = orig
|
||||
continue
|
||||
}
|
||||
|
||||
if i > 0 {
|
||||
rest = l.original[i:]
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if len(l.original) > 1 {
|
||||
return string(unicode.ToUpper(first)) + lower(rest)
|
||||
b := poolOfBuffers.BorrowBuffer(utf8.UTFMax + len(rest))
|
||||
defer func() {
|
||||
poolOfBuffers.RedeemBuffer(b)
|
||||
}()
|
||||
b.WriteRune(unicode.ToUpper(first))
|
||||
b.WriteString(lower(rest))
|
||||
return b.String()
|
||||
}
|
||||
|
||||
return l.original
|
||||
}
|
||||
|
||||
func (l *initialismNameLexem) GetOriginal() string {
|
||||
func (l nameLexem) GetOriginal() string {
|
||||
return l.original
|
||||
}
|
||||
|
||||
func (l *casualNameLexem) GetOriginal() string {
|
||||
return l.original
|
||||
}
|
||||
|
||||
func (l *initialismNameLexem) IsInitialism() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (l *casualNameLexem) IsInitialism() bool {
|
||||
return false
|
||||
func (l nameLexem) IsInitialism() bool {
|
||||
return l.kind == lexemKindInitialismName
|
||||
}
|
||||
|
|
|
|||
490
vendor/github.com/go-openapi/swag/split.go
generated
vendored
490
vendor/github.com/go-openapi/swag/split.go
generated
vendored
|
|
@ -15,124 +15,269 @@
|
|||
package swag
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"sync"
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
var nameReplaceTable = map[rune]string{
|
||||
'@': "At ",
|
||||
'&': "And ",
|
||||
'|': "Pipe ",
|
||||
'$': "Dollar ",
|
||||
'!': "Bang ",
|
||||
'-': "",
|
||||
'_': "",
|
||||
}
|
||||
|
||||
type (
|
||||
splitter struct {
|
||||
postSplitInitialismCheck bool
|
||||
initialisms []string
|
||||
initialismsRunes [][]rune
|
||||
initialismsUpperCased [][]rune // initialisms cached in their trimmed, upper-cased version
|
||||
postSplitInitialismCheck bool
|
||||
}
|
||||
|
||||
splitterOption func(*splitter) *splitter
|
||||
splitterOption func(*splitter)
|
||||
|
||||
initialismMatch struct {
|
||||
body []rune
|
||||
start, end int
|
||||
complete bool
|
||||
}
|
||||
initialismMatches []initialismMatch
|
||||
)
|
||||
|
||||
// split calls the splitter; splitter provides more control and post options
|
||||
func split(str string) []string {
|
||||
lexems := newSplitter().split(str)
|
||||
result := make([]string, 0, len(lexems))
|
||||
type (
|
||||
// memory pools of temporary objects.
|
||||
//
|
||||
// These are used to recycle temporarily allocated objects
|
||||
// and relieve the GC from undue pressure.
|
||||
|
||||
for _, lexem := range lexems {
|
||||
matchesPool struct {
|
||||
*sync.Pool
|
||||
}
|
||||
|
||||
buffersPool struct {
|
||||
*sync.Pool
|
||||
}
|
||||
|
||||
lexemsPool struct {
|
||||
*sync.Pool
|
||||
}
|
||||
|
||||
splittersPool struct {
|
||||
*sync.Pool
|
||||
}
|
||||
)
|
||||
|
||||
var (
|
||||
// poolOfMatches holds temporary slices for recycling during the initialism match process
|
||||
poolOfMatches = matchesPool{
|
||||
Pool: &sync.Pool{
|
||||
New: func() any {
|
||||
s := make(initialismMatches, 0, maxAllocMatches)
|
||||
|
||||
return &s
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
poolOfBuffers = buffersPool{
|
||||
Pool: &sync.Pool{
|
||||
New: func() any {
|
||||
return new(bytes.Buffer)
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
poolOfLexems = lexemsPool{
|
||||
Pool: &sync.Pool{
|
||||
New: func() any {
|
||||
s := make([]nameLexem, 0, maxAllocMatches)
|
||||
|
||||
return &s
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
poolOfSplitters = splittersPool{
|
||||
Pool: &sync.Pool{
|
||||
New: func() any {
|
||||
s := newSplitter()
|
||||
|
||||
return &s
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
// nameReplaceTable finds a word representation for special characters.
|
||||
func nameReplaceTable(r rune) (string, bool) {
|
||||
switch r {
|
||||
case '@':
|
||||
return "At ", true
|
||||
case '&':
|
||||
return "And ", true
|
||||
case '|':
|
||||
return "Pipe ", true
|
||||
case '$':
|
||||
return "Dollar ", true
|
||||
case '!':
|
||||
return "Bang ", true
|
||||
case '-':
|
||||
return "", true
|
||||
case '_':
|
||||
return "", true
|
||||
default:
|
||||
return "", false
|
||||
}
|
||||
}
|
||||
|
||||
// split calls the splitter.
|
||||
//
|
||||
// Use newSplitter for more control and options
|
||||
func split(str string) []string {
|
||||
s := poolOfSplitters.BorrowSplitter()
|
||||
lexems := s.split(str)
|
||||
result := make([]string, 0, len(*lexems))
|
||||
|
||||
for _, lexem := range *lexems {
|
||||
result = append(result, lexem.GetOriginal())
|
||||
}
|
||||
poolOfLexems.RedeemLexems(lexems)
|
||||
poolOfSplitters.RedeemSplitter(s)
|
||||
|
||||
return result
|
||||
|
||||
}
|
||||
|
||||
func (s *splitter) split(str string) []nameLexem {
|
||||
return s.toNameLexems(str)
|
||||
}
|
||||
|
||||
func newSplitter(options ...splitterOption) *splitter {
|
||||
splitter := &splitter{
|
||||
func newSplitter(options ...splitterOption) splitter {
|
||||
s := splitter{
|
||||
postSplitInitialismCheck: false,
|
||||
initialisms: initialisms,
|
||||
initialismsRunes: initialismsRunes,
|
||||
initialismsUpperCased: initialismsUpperCased,
|
||||
}
|
||||
|
||||
for _, option := range options {
|
||||
splitter = option(splitter)
|
||||
option(&s)
|
||||
}
|
||||
|
||||
return splitter
|
||||
}
|
||||
|
||||
// withPostSplitInitialismCheck allows to catch initialisms after main split process
|
||||
func withPostSplitInitialismCheck(s *splitter) *splitter {
|
||||
s.postSplitInitialismCheck = true
|
||||
return s
|
||||
}
|
||||
|
||||
type (
|
||||
initialismMatch struct {
|
||||
start, end int
|
||||
body []rune
|
||||
complete bool
|
||||
}
|
||||
initialismMatches []*initialismMatch
|
||||
)
|
||||
// withPostSplitInitialismCheck allows to catch initialisms after main split process
|
||||
func withPostSplitInitialismCheck(s *splitter) {
|
||||
s.postSplitInitialismCheck = true
|
||||
}
|
||||
|
||||
func (s *splitter) toNameLexems(name string) []nameLexem {
|
||||
func (p matchesPool) BorrowMatches() *initialismMatches {
|
||||
s := p.Get().(*initialismMatches)
|
||||
*s = (*s)[:0] // reset slice, keep allocated capacity
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
func (p buffersPool) BorrowBuffer(size int) *bytes.Buffer {
|
||||
s := p.Get().(*bytes.Buffer)
|
||||
s.Reset()
|
||||
|
||||
if s.Cap() < size {
|
||||
s.Grow(size)
|
||||
}
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
func (p lexemsPool) BorrowLexems() *[]nameLexem {
|
||||
s := p.Get().(*[]nameLexem)
|
||||
*s = (*s)[:0] // reset slice, keep allocated capacity
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
func (p splittersPool) BorrowSplitter(options ...splitterOption) *splitter {
|
||||
s := p.Get().(*splitter)
|
||||
s.postSplitInitialismCheck = false // reset options
|
||||
for _, apply := range options {
|
||||
apply(s)
|
||||
}
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
func (p matchesPool) RedeemMatches(s *initialismMatches) {
|
||||
p.Put(s)
|
||||
}
|
||||
|
||||
func (p buffersPool) RedeemBuffer(s *bytes.Buffer) {
|
||||
p.Put(s)
|
||||
}
|
||||
|
||||
func (p lexemsPool) RedeemLexems(s *[]nameLexem) {
|
||||
p.Put(s)
|
||||
}
|
||||
|
||||
func (p splittersPool) RedeemSplitter(s *splitter) {
|
||||
p.Put(s)
|
||||
}
|
||||
|
||||
func (m initialismMatch) isZero() bool {
|
||||
return m.start == 0 && m.end == 0
|
||||
}
|
||||
|
||||
func (s splitter) split(name string) *[]nameLexem {
|
||||
nameRunes := []rune(name)
|
||||
matches := s.gatherInitialismMatches(nameRunes)
|
||||
if matches == nil {
|
||||
return poolOfLexems.BorrowLexems()
|
||||
}
|
||||
|
||||
return s.mapMatchesToNameLexems(nameRunes, matches)
|
||||
}
|
||||
|
||||
func (s *splitter) gatherInitialismMatches(nameRunes []rune) initialismMatches {
|
||||
matches := make(initialismMatches, 0)
|
||||
func (s splitter) gatherInitialismMatches(nameRunes []rune) *initialismMatches {
|
||||
var matches *initialismMatches
|
||||
|
||||
for currentRunePosition, currentRune := range nameRunes {
|
||||
newMatches := make(initialismMatches, 0, len(matches))
|
||||
// recycle these allocations as we loop over runes
|
||||
// with such recycling, only 2 slices should be allocated per call
|
||||
// instead of o(n).
|
||||
newMatches := poolOfMatches.BorrowMatches()
|
||||
|
||||
// check current initialism matches
|
||||
for _, match := range matches {
|
||||
if keepCompleteMatch := match.complete; keepCompleteMatch {
|
||||
newMatches = append(newMatches, match)
|
||||
continue
|
||||
}
|
||||
|
||||
// drop failed match
|
||||
currentMatchRune := match.body[currentRunePosition-match.start]
|
||||
if !s.initialismRuneEqual(currentMatchRune, currentRune) {
|
||||
continue
|
||||
}
|
||||
|
||||
// try to complete ongoing match
|
||||
if currentRunePosition-match.start == len(match.body)-1 {
|
||||
// we are close; the next step is to check the symbol ahead
|
||||
// if it is a small letter, then it is not the end of match
|
||||
// but beginning of the next word
|
||||
|
||||
if currentRunePosition < len(nameRunes)-1 {
|
||||
nextRune := nameRunes[currentRunePosition+1]
|
||||
if newWord := unicode.IsLower(nextRune); newWord {
|
||||
// oh ok, it was the start of a new word
|
||||
continue
|
||||
}
|
||||
if matches != nil { // skip first iteration
|
||||
for _, match := range *matches {
|
||||
if keepCompleteMatch := match.complete; keepCompleteMatch {
|
||||
*newMatches = append(*newMatches, match)
|
||||
continue
|
||||
}
|
||||
|
||||
match.complete = true
|
||||
match.end = currentRunePosition
|
||||
}
|
||||
// drop failed match
|
||||
currentMatchRune := match.body[currentRunePosition-match.start]
|
||||
if currentMatchRune != currentRune {
|
||||
continue
|
||||
}
|
||||
|
||||
newMatches = append(newMatches, match)
|
||||
// try to complete ongoing match
|
||||
if currentRunePosition-match.start == len(match.body)-1 {
|
||||
// we are close; the next step is to check the symbol ahead
|
||||
// if it is a small letter, then it is not the end of match
|
||||
// but beginning of the next word
|
||||
|
||||
if currentRunePosition < len(nameRunes)-1 {
|
||||
nextRune := nameRunes[currentRunePosition+1]
|
||||
if newWord := unicode.IsLower(nextRune); newWord {
|
||||
// oh ok, it was the start of a new word
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
match.complete = true
|
||||
match.end = currentRunePosition
|
||||
}
|
||||
|
||||
*newMatches = append(*newMatches, match)
|
||||
}
|
||||
}
|
||||
|
||||
// check for new initialism matches
|
||||
for _, initialism := range s.initialisms {
|
||||
initialismRunes := []rune(initialism)
|
||||
if s.initialismRuneEqual(initialismRunes[0], currentRune) {
|
||||
newMatches = append(newMatches, &initialismMatch{
|
||||
for i := range s.initialisms {
|
||||
initialismRunes := s.initialismsRunes[i]
|
||||
if initialismRunes[0] == currentRune {
|
||||
*newMatches = append(*newMatches, initialismMatch{
|
||||
start: currentRunePosition,
|
||||
body: initialismRunes,
|
||||
complete: false,
|
||||
|
|
@ -140,24 +285,28 @@ func (s *splitter) gatherInitialismMatches(nameRunes []rune) initialismMatches {
|
|||
}
|
||||
}
|
||||
|
||||
if matches != nil {
|
||||
poolOfMatches.RedeemMatches(matches)
|
||||
}
|
||||
matches = newMatches
|
||||
}
|
||||
|
||||
// up to the caller to redeem this last slice
|
||||
return matches
|
||||
}
|
||||
|
||||
func (s *splitter) mapMatchesToNameLexems(nameRunes []rune, matches initialismMatches) []nameLexem {
|
||||
nameLexems := make([]nameLexem, 0)
|
||||
func (s splitter) mapMatchesToNameLexems(nameRunes []rune, matches *initialismMatches) *[]nameLexem {
|
||||
nameLexems := poolOfLexems.BorrowLexems()
|
||||
|
||||
var lastAcceptedMatch *initialismMatch
|
||||
for _, match := range matches {
|
||||
var lastAcceptedMatch initialismMatch
|
||||
for _, match := range *matches {
|
||||
if !match.complete {
|
||||
continue
|
||||
}
|
||||
|
||||
if firstMatch := lastAcceptedMatch == nil; firstMatch {
|
||||
nameLexems = append(nameLexems, s.breakCasualString(nameRunes[:match.start])...)
|
||||
nameLexems = append(nameLexems, s.breakInitialism(string(match.body)))
|
||||
if firstMatch := lastAcceptedMatch.isZero(); firstMatch {
|
||||
s.appendBrokenDownCasualString(nameLexems, nameRunes[:match.start])
|
||||
*nameLexems = append(*nameLexems, s.breakInitialism(string(match.body)))
|
||||
|
||||
lastAcceptedMatch = match
|
||||
|
||||
|
|
@ -169,63 +318,66 @@ func (s *splitter) mapMatchesToNameLexems(nameRunes []rune, matches initialismMa
|
|||
}
|
||||
|
||||
middle := nameRunes[lastAcceptedMatch.end+1 : match.start]
|
||||
nameLexems = append(nameLexems, s.breakCasualString(middle)...)
|
||||
nameLexems = append(nameLexems, s.breakInitialism(string(match.body)))
|
||||
s.appendBrokenDownCasualString(nameLexems, middle)
|
||||
*nameLexems = append(*nameLexems, s.breakInitialism(string(match.body)))
|
||||
|
||||
lastAcceptedMatch = match
|
||||
}
|
||||
|
||||
// we have not found any accepted matches
|
||||
if lastAcceptedMatch == nil {
|
||||
return s.breakCasualString(nameRunes)
|
||||
if lastAcceptedMatch.isZero() {
|
||||
*nameLexems = (*nameLexems)[:0]
|
||||
s.appendBrokenDownCasualString(nameLexems, nameRunes)
|
||||
} else if lastAcceptedMatch.end+1 != len(nameRunes) {
|
||||
rest := nameRunes[lastAcceptedMatch.end+1:]
|
||||
s.appendBrokenDownCasualString(nameLexems, rest)
|
||||
}
|
||||
|
||||
if lastAcceptedMatch.end+1 != len(nameRunes) {
|
||||
rest := nameRunes[lastAcceptedMatch.end+1:]
|
||||
nameLexems = append(nameLexems, s.breakCasualString(rest)...)
|
||||
}
|
||||
poolOfMatches.RedeemMatches(matches)
|
||||
|
||||
return nameLexems
|
||||
}
|
||||
|
||||
func (s *splitter) initialismRuneEqual(a, b rune) bool {
|
||||
return a == b
|
||||
}
|
||||
|
||||
func (s *splitter) breakInitialism(original string) nameLexem {
|
||||
func (s splitter) breakInitialism(original string) nameLexem {
|
||||
return newInitialismNameLexem(original, original)
|
||||
}
|
||||
|
||||
func (s *splitter) breakCasualString(str []rune) []nameLexem {
|
||||
segments := make([]nameLexem, 0)
|
||||
currentSegment := ""
|
||||
func (s splitter) appendBrokenDownCasualString(segments *[]nameLexem, str []rune) {
|
||||
currentSegment := poolOfBuffers.BorrowBuffer(len(str)) // unlike strings.Builder, bytes.Buffer initial storage can reused
|
||||
defer func() {
|
||||
poolOfBuffers.RedeemBuffer(currentSegment)
|
||||
}()
|
||||
|
||||
addCasualNameLexem := func(original string) {
|
||||
segments = append(segments, newCasualNameLexem(original))
|
||||
*segments = append(*segments, newCasualNameLexem(original))
|
||||
}
|
||||
|
||||
addInitialismNameLexem := func(original, match string) {
|
||||
segments = append(segments, newInitialismNameLexem(original, match))
|
||||
*segments = append(*segments, newInitialismNameLexem(original, match))
|
||||
}
|
||||
|
||||
addNameLexem := func(original string) {
|
||||
if s.postSplitInitialismCheck {
|
||||
for _, initialism := range s.initialisms {
|
||||
if upper(initialism) == upper(original) {
|
||||
addInitialismNameLexem(original, initialism)
|
||||
var addNameLexem func(string)
|
||||
if s.postSplitInitialismCheck {
|
||||
addNameLexem = func(original string) {
|
||||
for i := range s.initialisms {
|
||||
if isEqualFoldIgnoreSpace(s.initialismsUpperCased[i], original) {
|
||||
addInitialismNameLexem(original, s.initialisms[i])
|
||||
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
addCasualNameLexem(original)
|
||||
addCasualNameLexem(original)
|
||||
}
|
||||
} else {
|
||||
addNameLexem = addCasualNameLexem
|
||||
}
|
||||
|
||||
for _, rn := range string(str) {
|
||||
if replace, found := nameReplaceTable[rn]; found {
|
||||
if currentSegment != "" {
|
||||
addNameLexem(currentSegment)
|
||||
currentSegment = ""
|
||||
for _, rn := range str {
|
||||
if replace, found := nameReplaceTable(rn); found {
|
||||
if currentSegment.Len() > 0 {
|
||||
addNameLexem(currentSegment.String())
|
||||
currentSegment.Reset()
|
||||
}
|
||||
|
||||
if replace != "" {
|
||||
|
|
@ -236,27 +388,121 @@ func (s *splitter) breakCasualString(str []rune) []nameLexem {
|
|||
}
|
||||
|
||||
if !unicode.In(rn, unicode.L, unicode.M, unicode.N, unicode.Pc) {
|
||||
if currentSegment != "" {
|
||||
addNameLexem(currentSegment)
|
||||
currentSegment = ""
|
||||
if currentSegment.Len() > 0 {
|
||||
addNameLexem(currentSegment.String())
|
||||
currentSegment.Reset()
|
||||
}
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
if unicode.IsUpper(rn) {
|
||||
if currentSegment != "" {
|
||||
addNameLexem(currentSegment)
|
||||
if currentSegment.Len() > 0 {
|
||||
addNameLexem(currentSegment.String())
|
||||
}
|
||||
currentSegment = ""
|
||||
currentSegment.Reset()
|
||||
}
|
||||
|
||||
currentSegment += string(rn)
|
||||
currentSegment.WriteRune(rn)
|
||||
}
|
||||
|
||||
if currentSegment != "" {
|
||||
addNameLexem(currentSegment)
|
||||
if currentSegment.Len() > 0 {
|
||||
addNameLexem(currentSegment.String())
|
||||
}
|
||||
|
||||
return segments
|
||||
}
|
||||
|
||||
// isEqualFoldIgnoreSpace is the same as strings.EqualFold, but
|
||||
// it ignores leading and trailing blank spaces in the compared
|
||||
// string.
|
||||
//
|
||||
// base is assumed to be composed of upper-cased runes, and be already
|
||||
// trimmed.
|
||||
//
|
||||
// This code is heavily inspired from strings.EqualFold.
|
||||
func isEqualFoldIgnoreSpace(base []rune, str string) bool {
|
||||
var i, baseIndex int
|
||||
// equivalent to b := []byte(str), but without data copy
|
||||
b := hackStringBytes(str)
|
||||
|
||||
for i < len(b) {
|
||||
if c := b[i]; c < utf8.RuneSelf {
|
||||
// fast path for ASCII
|
||||
if c != ' ' && c != '\t' {
|
||||
break
|
||||
}
|
||||
i++
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
// unicode case
|
||||
r, size := utf8.DecodeRune(b[i:])
|
||||
if !unicode.IsSpace(r) {
|
||||
break
|
||||
}
|
||||
i += size
|
||||
}
|
||||
|
||||
if i >= len(b) {
|
||||
return len(base) == 0
|
||||
}
|
||||
|
||||
for _, baseRune := range base {
|
||||
if i >= len(b) {
|
||||
break
|
||||
}
|
||||
|
||||
if c := b[i]; c < utf8.RuneSelf {
|
||||
// single byte rune case (ASCII)
|
||||
if baseRune >= utf8.RuneSelf {
|
||||
return false
|
||||
}
|
||||
|
||||
baseChar := byte(baseRune)
|
||||
if c != baseChar &&
|
||||
!('a' <= c && c <= 'z' && c-'a'+'A' == baseChar) {
|
||||
return false
|
||||
}
|
||||
|
||||
baseIndex++
|
||||
i++
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
// unicode case
|
||||
r, size := utf8.DecodeRune(b[i:])
|
||||
if unicode.ToUpper(r) != baseRune {
|
||||
return false
|
||||
}
|
||||
baseIndex++
|
||||
i += size
|
||||
}
|
||||
|
||||
if baseIndex != len(base) {
|
||||
return false
|
||||
}
|
||||
|
||||
// all passed: now we should only have blanks
|
||||
for i < len(b) {
|
||||
if c := b[i]; c < utf8.RuneSelf {
|
||||
// fast path for ASCII
|
||||
if c != ' ' && c != '\t' {
|
||||
return false
|
||||
}
|
||||
i++
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
// unicode case
|
||||
r, size := utf8.DecodeRune(b[i:])
|
||||
if !unicode.IsSpace(r) {
|
||||
return false
|
||||
}
|
||||
|
||||
i += size
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
|
|
|||
8
vendor/github.com/go-openapi/swag/string_bytes.go
generated
vendored
Normal file
8
vendor/github.com/go-openapi/swag/string_bytes.go
generated
vendored
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
package swag
|
||||
|
||||
import "unsafe"
|
||||
|
||||
// hackStringBytes returns the (unsafe) underlying bytes slice of a string.
|
||||
func hackStringBytes(str string) []byte {
|
||||
return unsafe.Slice(unsafe.StringData(str), len(str))
|
||||
}
|
||||
206
vendor/github.com/go-openapi/swag/util.go
generated
vendored
206
vendor/github.com/go-openapi/swag/util.go
generated
vendored
|
|
@ -18,76 +18,25 @@ import (
|
|||
"reflect"
|
||||
"strings"
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
// commonInitialisms are common acronyms that are kept as whole uppercased words.
|
||||
var commonInitialisms *indexOfInitialisms
|
||||
|
||||
// initialisms is a slice of sorted initialisms
|
||||
var initialisms []string
|
||||
|
||||
var isInitialism func(string) bool
|
||||
|
||||
// GoNamePrefixFunc sets an optional rule to prefix go names
|
||||
// which do not start with a letter.
|
||||
//
|
||||
// The prefix function is assumed to return a string that starts with an upper case letter.
|
||||
//
|
||||
// e.g. to help convert "123" into "{prefix}123"
|
||||
//
|
||||
// The default is to prefix with "X"
|
||||
var GoNamePrefixFunc func(string) string
|
||||
|
||||
func init() {
|
||||
// Taken from https://github.com/golang/lint/blob/3390df4df2787994aea98de825b964ac7944b817/lint.go#L732-L769
|
||||
var configuredInitialisms = map[string]bool{
|
||||
"ACL": true,
|
||||
"API": true,
|
||||
"ASCII": true,
|
||||
"CPU": true,
|
||||
"CSS": true,
|
||||
"DNS": true,
|
||||
"EOF": true,
|
||||
"GUID": true,
|
||||
"HTML": true,
|
||||
"HTTPS": true,
|
||||
"HTTP": true,
|
||||
"ID": true,
|
||||
"IP": true,
|
||||
"IPv4": true,
|
||||
"IPv6": true,
|
||||
"JSON": true,
|
||||
"LHS": true,
|
||||
"OAI": true,
|
||||
"QPS": true,
|
||||
"RAM": true,
|
||||
"RHS": true,
|
||||
"RPC": true,
|
||||
"SLA": true,
|
||||
"SMTP": true,
|
||||
"SQL": true,
|
||||
"SSH": true,
|
||||
"TCP": true,
|
||||
"TLS": true,
|
||||
"TTL": true,
|
||||
"UDP": true,
|
||||
"UI": true,
|
||||
"UID": true,
|
||||
"UUID": true,
|
||||
"URI": true,
|
||||
"URL": true,
|
||||
"UTF8": true,
|
||||
"VM": true,
|
||||
"XML": true,
|
||||
"XMPP": true,
|
||||
"XSRF": true,
|
||||
"XSS": true,
|
||||
func prefixFunc(name, in string) string {
|
||||
if GoNamePrefixFunc == nil {
|
||||
return "X" + in
|
||||
}
|
||||
|
||||
// a thread-safe index of initialisms
|
||||
commonInitialisms = newIndexOfInitialisms().load(configuredInitialisms)
|
||||
initialisms = commonInitialisms.sorted()
|
||||
|
||||
// a test function
|
||||
isInitialism = commonInitialisms.isInitialism
|
||||
return GoNamePrefixFunc(name) + in
|
||||
}
|
||||
|
||||
const (
|
||||
|
|
@ -156,25 +105,9 @@ func SplitByFormat(data, format string) []string {
|
|||
return result
|
||||
}
|
||||
|
||||
type byInitialism []string
|
||||
|
||||
func (s byInitialism) Len() int {
|
||||
return len(s)
|
||||
}
|
||||
func (s byInitialism) Swap(i, j int) {
|
||||
s[i], s[j] = s[j], s[i]
|
||||
}
|
||||
func (s byInitialism) Less(i, j int) bool {
|
||||
if len(s[i]) != len(s[j]) {
|
||||
return len(s[i]) < len(s[j])
|
||||
}
|
||||
|
||||
return strings.Compare(s[i], s[j]) > 0
|
||||
}
|
||||
|
||||
// Removes leading whitespaces
|
||||
func trim(str string) string {
|
||||
return strings.Trim(str, " ")
|
||||
return strings.TrimSpace(str)
|
||||
}
|
||||
|
||||
// Shortcut to strings.ToUpper()
|
||||
|
|
@ -188,15 +121,20 @@ func lower(str string) string {
|
|||
}
|
||||
|
||||
// Camelize an uppercased word
|
||||
func Camelize(word string) (camelized string) {
|
||||
func Camelize(word string) string {
|
||||
camelized := poolOfBuffers.BorrowBuffer(len(word))
|
||||
defer func() {
|
||||
poolOfBuffers.RedeemBuffer(camelized)
|
||||
}()
|
||||
|
||||
for pos, ru := range []rune(word) {
|
||||
if pos > 0 {
|
||||
camelized += string(unicode.ToLower(ru))
|
||||
camelized.WriteRune(unicode.ToLower(ru))
|
||||
} else {
|
||||
camelized += string(unicode.ToUpper(ru))
|
||||
camelized.WriteRune(unicode.ToUpper(ru))
|
||||
}
|
||||
}
|
||||
return
|
||||
return camelized.String()
|
||||
}
|
||||
|
||||
// ToFileName lowercases and underscores a go type name
|
||||
|
|
@ -224,33 +162,40 @@ func ToCommandName(name string) string {
|
|||
|
||||
// ToHumanNameLower represents a code name as a human series of words
|
||||
func ToHumanNameLower(name string) string {
|
||||
in := newSplitter(withPostSplitInitialismCheck).split(name)
|
||||
out := make([]string, 0, len(in))
|
||||
s := poolOfSplitters.BorrowSplitter(withPostSplitInitialismCheck)
|
||||
in := s.split(name)
|
||||
poolOfSplitters.RedeemSplitter(s)
|
||||
out := make([]string, 0, len(*in))
|
||||
|
||||
for _, w := range in {
|
||||
for _, w := range *in {
|
||||
if !w.IsInitialism() {
|
||||
out = append(out, lower(w.GetOriginal()))
|
||||
} else {
|
||||
out = append(out, w.GetOriginal())
|
||||
out = append(out, trim(w.GetOriginal()))
|
||||
}
|
||||
}
|
||||
poolOfLexems.RedeemLexems(in)
|
||||
|
||||
return strings.Join(out, " ")
|
||||
}
|
||||
|
||||
// ToHumanNameTitle represents a code name as a human series of words with the first letters titleized
|
||||
func ToHumanNameTitle(name string) string {
|
||||
in := newSplitter(withPostSplitInitialismCheck).split(name)
|
||||
s := poolOfSplitters.BorrowSplitter(withPostSplitInitialismCheck)
|
||||
in := s.split(name)
|
||||
poolOfSplitters.RedeemSplitter(s)
|
||||
|
||||
out := make([]string, 0, len(in))
|
||||
for _, w := range in {
|
||||
original := w.GetOriginal()
|
||||
out := make([]string, 0, len(*in))
|
||||
for _, w := range *in {
|
||||
original := trim(w.GetOriginal())
|
||||
if !w.IsInitialism() {
|
||||
out = append(out, Camelize(original))
|
||||
} else {
|
||||
out = append(out, original)
|
||||
}
|
||||
}
|
||||
poolOfLexems.RedeemLexems(in)
|
||||
|
||||
return strings.Join(out, " ")
|
||||
}
|
||||
|
||||
|
|
@ -264,7 +209,7 @@ func ToJSONName(name string) string {
|
|||
out = append(out, lower(w))
|
||||
continue
|
||||
}
|
||||
out = append(out, Camelize(w))
|
||||
out = append(out, Camelize(trim(w)))
|
||||
}
|
||||
return strings.Join(out, "")
|
||||
}
|
||||
|
|
@ -283,35 +228,70 @@ func ToVarName(name string) string {
|
|||
|
||||
// ToGoName translates a swagger name which can be underscored or camel cased to a name that golint likes
|
||||
func ToGoName(name string) string {
|
||||
lexems := newSplitter(withPostSplitInitialismCheck).split(name)
|
||||
s := poolOfSplitters.BorrowSplitter(withPostSplitInitialismCheck)
|
||||
lexems := s.split(name)
|
||||
poolOfSplitters.RedeemSplitter(s)
|
||||
defer func() {
|
||||
poolOfLexems.RedeemLexems(lexems)
|
||||
}()
|
||||
lexemes := *lexems
|
||||
|
||||
result := ""
|
||||
for _, lexem := range lexems {
|
||||
if len(lexemes) == 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
result := poolOfBuffers.BorrowBuffer(len(name))
|
||||
defer func() {
|
||||
poolOfBuffers.RedeemBuffer(result)
|
||||
}()
|
||||
|
||||
// check if not starting with a letter, upper case
|
||||
firstPart := lexemes[0].GetUnsafeGoName()
|
||||
if lexemes[0].IsInitialism() {
|
||||
firstPart = upper(firstPart)
|
||||
}
|
||||
|
||||
if c := firstPart[0]; c < utf8.RuneSelf {
|
||||
// ASCII
|
||||
switch {
|
||||
case 'A' <= c && c <= 'Z':
|
||||
result.WriteString(firstPart)
|
||||
case 'a' <= c && c <= 'z':
|
||||
result.WriteByte(c - 'a' + 'A')
|
||||
result.WriteString(firstPart[1:])
|
||||
default:
|
||||
result.WriteString(prefixFunc(name, firstPart))
|
||||
// NOTE: no longer check if prefixFunc returns a string that starts with uppercase:
|
||||
// assume this is always the case
|
||||
}
|
||||
} else {
|
||||
// unicode
|
||||
firstRune, _ := utf8.DecodeRuneInString(firstPart)
|
||||
switch {
|
||||
case !unicode.IsLetter(firstRune):
|
||||
result.WriteString(prefixFunc(name, firstPart))
|
||||
case !unicode.IsUpper(firstRune):
|
||||
result.WriteString(prefixFunc(name, firstPart))
|
||||
/*
|
||||
result.WriteRune(unicode.ToUpper(firstRune))
|
||||
result.WriteString(firstPart[offset:])
|
||||
*/
|
||||
default:
|
||||
result.WriteString(firstPart)
|
||||
}
|
||||
}
|
||||
|
||||
for _, lexem := range lexemes[1:] {
|
||||
goName := lexem.GetUnsafeGoName()
|
||||
|
||||
// to support old behavior
|
||||
if lexem.IsInitialism() {
|
||||
goName = upper(goName)
|
||||
}
|
||||
result += goName
|
||||
result.WriteString(goName)
|
||||
}
|
||||
|
||||
if len(result) > 0 {
|
||||
// Only prefix with X when the first character isn't an ascii letter
|
||||
first := []rune(result)[0]
|
||||
if !unicode.IsLetter(first) || (first > unicode.MaxASCII && !unicode.IsUpper(first)) {
|
||||
if GoNamePrefixFunc == nil {
|
||||
return "X" + result
|
||||
}
|
||||
result = GoNamePrefixFunc(name) + result
|
||||
}
|
||||
first = []rune(result)[0]
|
||||
if unicode.IsLetter(first) && !unicode.IsUpper(first) {
|
||||
result = string(append([]rune{unicode.ToUpper(first)}, []rune(result)[1:]...))
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
return result.String()
|
||||
}
|
||||
|
||||
// ContainsStrings searches a slice of strings for a case-sensitive match
|
||||
|
|
@ -376,16 +356,6 @@ func IsZero(data interface{}) bool {
|
|||
}
|
||||
}
|
||||
|
||||
// AddInitialisms add additional initialisms
|
||||
func AddInitialisms(words ...string) {
|
||||
for _, word := range words {
|
||||
// commonInitialisms[upper(word)] = true
|
||||
commonInitialisms.add(upper(word))
|
||||
}
|
||||
// sort again
|
||||
initialisms = commonInitialisms.sorted()
|
||||
}
|
||||
|
||||
// CommandLineOptionsGroup represents a group of user-defined command line options
|
||||
type CommandLineOptionsGroup struct {
|
||||
ShortDescription string
|
||||
|
|
|
|||
38
vendor/github.com/go-openapi/swag/yaml.go
generated
vendored
38
vendor/github.com/go-openapi/swag/yaml.go
generated
vendored
|
|
@ -16,8 +16,11 @@ package swag
|
|||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strconv"
|
||||
|
||||
"github.com/mailru/easyjson/jlexer"
|
||||
|
|
@ -48,7 +51,7 @@ func BytesToYAMLDoc(data []byte) (interface{}, error) {
|
|||
return nil, err
|
||||
}
|
||||
if document.Kind != yaml.DocumentNode || len(document.Content) != 1 || document.Content[0].Kind != yaml.MappingNode {
|
||||
return nil, fmt.Errorf("only YAML documents that are objects are supported")
|
||||
return nil, errors.New("only YAML documents that are objects are supported")
|
||||
}
|
||||
return &document, nil
|
||||
}
|
||||
|
|
@ -245,7 +248,27 @@ func (s JSONMapSlice) MarshalYAML() (interface{}, error) {
|
|||
return yaml.Marshal(&n)
|
||||
}
|
||||
|
||||
func isNil(input interface{}) bool {
|
||||
if input == nil {
|
||||
return true
|
||||
}
|
||||
kind := reflect.TypeOf(input).Kind()
|
||||
switch kind { //nolint:exhaustive
|
||||
case reflect.Ptr, reflect.Map, reflect.Slice, reflect.Chan:
|
||||
return reflect.ValueOf(input).IsNil()
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func json2yaml(item interface{}) (*yaml.Node, error) {
|
||||
if isNil(item) {
|
||||
return &yaml.Node{
|
||||
Kind: yaml.ScalarNode,
|
||||
Value: "null",
|
||||
}, nil
|
||||
}
|
||||
|
||||
switch val := item.(type) {
|
||||
case JSONMapSlice:
|
||||
var n yaml.Node
|
||||
|
|
@ -265,7 +288,14 @@ func json2yaml(item interface{}) (*yaml.Node, error) {
|
|||
case map[string]interface{}:
|
||||
var n yaml.Node
|
||||
n.Kind = yaml.MappingNode
|
||||
for k, v := range val {
|
||||
keys := make([]string, 0, len(val))
|
||||
for k := range val {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
|
||||
for _, k := range keys {
|
||||
v := val[k]
|
||||
childNode, err := json2yaml(v)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
@ -318,9 +348,9 @@ func json2yaml(item interface{}) (*yaml.Node, error) {
|
|||
Tag: yamlBoolScalar,
|
||||
Value: strconv.FormatBool(val),
|
||||
}, nil
|
||||
default:
|
||||
return nil, fmt.Errorf("unhandled type: %T", val)
|
||||
}
|
||||
|
||||
return nil, nil //nolint:nilnil
|
||||
}
|
||||
|
||||
// JSONMapItem represents the value of a key in a JSON object held by JSONMapSlice
|
||||
|
|
|
|||
31
vendor/github.com/go-openapi/validate/BENCHMARK.md
generated
vendored
Normal file
31
vendor/github.com/go-openapi/validate/BENCHMARK.md
generated
vendored
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
# Benchmark
|
||||
|
||||
Validating the Kubernetes Swagger API
|
||||
|
||||
## v0.22.6: 60,000,000 allocs
|
||||
```
|
||||
goos: linux
|
||||
goarch: amd64
|
||||
pkg: github.com/go-openapi/validate
|
||||
cpu: AMD Ryzen 7 5800X 8-Core Processor
|
||||
Benchmark_KubernetesSpec/validating_kubernetes_API-16 1 8549863982 ns/op 7067424936 B/op 59583275 allocs/op
|
||||
```
|
||||
|
||||
## After refact PR: minor but noticable improvements: 25,000,000 allocs
|
||||
```
|
||||
go test -bench Spec
|
||||
goos: linux
|
||||
goarch: amd64
|
||||
pkg: github.com/go-openapi/validate
|
||||
cpu: AMD Ryzen 7 5800X 8-Core Processor
|
||||
Benchmark_KubernetesSpec/validating_kubernetes_API-16 1 4064535557 ns/op 3379715592 B/op 25320330 allocs/op
|
||||
```
|
||||
|
||||
## After reduce GC pressure PR: 17,000,000 allocs
|
||||
```
|
||||
goos: linux
|
||||
goarch: amd64
|
||||
pkg: github.com/go-openapi/validate
|
||||
cpu: AMD Ryzen 7 5800X 8-Core Processor
|
||||
Benchmark_KubernetesSpec/validating_kubernetes_API-16 1 3758414145 ns/op 2593881496 B/op 17111373 allocs/op
|
||||
```
|
||||
106
vendor/github.com/go-openapi/validate/default_validator.go
generated
vendored
106
vendor/github.com/go-openapi/validate/default_validator.go
generated
vendored
|
|
@ -25,48 +25,55 @@ import (
|
|||
// According to Swagger spec, default values MUST validate their schema.
|
||||
type defaultValidator struct {
|
||||
SpecValidator *SpecValidator
|
||||
visitedSchemas map[string]bool
|
||||
visitedSchemas map[string]struct{}
|
||||
schemaOptions *SchemaValidatorOptions
|
||||
}
|
||||
|
||||
// resetVisited resets the internal state of visited schemas
|
||||
func (d *defaultValidator) resetVisited() {
|
||||
d.visitedSchemas = map[string]bool{}
|
||||
if d.visitedSchemas == nil {
|
||||
d.visitedSchemas = make(map[string]struct{})
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// TODO(go1.21): clear(ex.visitedSchemas)
|
||||
for k := range d.visitedSchemas {
|
||||
delete(d.visitedSchemas, k)
|
||||
}
|
||||
}
|
||||
|
||||
func isVisited(path string, visitedSchemas map[string]bool) bool {
|
||||
found := visitedSchemas[path]
|
||||
if !found {
|
||||
// search for overlapping paths
|
||||
frags := strings.Split(path, ".")
|
||||
if len(frags) < 2 {
|
||||
// shortcut exit on smaller paths
|
||||
return found
|
||||
func isVisited(path string, visitedSchemas map[string]struct{}) bool {
|
||||
_, found := visitedSchemas[path]
|
||||
if found {
|
||||
return true
|
||||
}
|
||||
|
||||
// search for overlapping paths
|
||||
var (
|
||||
parent string
|
||||
suffix string
|
||||
)
|
||||
for i := len(path) - 2; i >= 0; i-- {
|
||||
r := path[i]
|
||||
if r != '.' {
|
||||
continue
|
||||
}
|
||||
last := len(frags) - 1
|
||||
var currentFragStr, parent string
|
||||
for i := range frags {
|
||||
if i == 0 {
|
||||
currentFragStr = frags[last]
|
||||
} else {
|
||||
currentFragStr = strings.Join([]string{frags[last-i], currentFragStr}, ".")
|
||||
}
|
||||
if i < last {
|
||||
parent = strings.Join(frags[0:last-i], ".")
|
||||
} else {
|
||||
parent = ""
|
||||
}
|
||||
if strings.HasSuffix(parent, currentFragStr) {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
|
||||
parent = path[0:i]
|
||||
suffix = path[i+1:]
|
||||
|
||||
if strings.HasSuffix(parent, suffix) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return found
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// beingVisited asserts a schema is being visited
|
||||
func (d *defaultValidator) beingVisited(path string) {
|
||||
d.visitedSchemas[path] = true
|
||||
d.visitedSchemas[path] = struct{}{}
|
||||
}
|
||||
|
||||
// isVisited tells if a path has already been visited
|
||||
|
|
@ -75,8 +82,9 @@ func (d *defaultValidator) isVisited(path string) bool {
|
|||
}
|
||||
|
||||
// Validate validates the default values declared in the swagger spec
|
||||
func (d *defaultValidator) Validate() (errs *Result) {
|
||||
errs = new(Result)
|
||||
func (d *defaultValidator) Validate() *Result {
|
||||
errs := pools.poolOfResults.BorrowResult() // will redeem when merged
|
||||
|
||||
if d == nil || d.SpecValidator == nil {
|
||||
return errs
|
||||
}
|
||||
|
|
@ -89,7 +97,7 @@ func (d *defaultValidator) validateDefaultValueValidAgainstSchema() *Result {
|
|||
// every default value that is specified must validate against the schema for that property
|
||||
// headers, items, parameters, schema
|
||||
|
||||
res := new(Result)
|
||||
res := pools.poolOfResults.BorrowResult() // will redeem when merged
|
||||
s := d.SpecValidator
|
||||
|
||||
for method, pathItem := range s.expandedAnalyzer().Operations() {
|
||||
|
|
@ -107,10 +115,12 @@ func (d *defaultValidator) validateDefaultValueValidAgainstSchema() *Result {
|
|||
// default values provided must validate against their inline definition (no explicit schema)
|
||||
if param.Default != nil && param.Schema == nil {
|
||||
// check param default value is valid
|
||||
red := NewParamValidator(¶m, s.KnownFormats).Validate(param.Default) //#nosec
|
||||
red := newParamValidator(¶m, s.KnownFormats, d.schemaOptions).Validate(param.Default) //#nosec
|
||||
if red.HasErrorsOrWarnings() {
|
||||
res.AddErrors(defaultValueDoesNotValidateMsg(param.Name, param.In))
|
||||
res.Merge(red)
|
||||
} else if red.wantsRedeemOnMerge {
|
||||
pools.poolOfResults.RedeemResult(red)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -120,6 +130,8 @@ func (d *defaultValidator) validateDefaultValueValidAgainstSchema() *Result {
|
|||
if red.HasErrorsOrWarnings() {
|
||||
res.AddErrors(defaultValueItemsDoesNotValidateMsg(param.Name, param.In))
|
||||
res.Merge(red)
|
||||
} else if red.wantsRedeemOnMerge {
|
||||
pools.poolOfResults.RedeemResult(red)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -129,6 +141,8 @@ func (d *defaultValidator) validateDefaultValueValidAgainstSchema() *Result {
|
|||
if red.HasErrorsOrWarnings() {
|
||||
res.AddErrors(defaultValueDoesNotValidateMsg(param.Name, param.In))
|
||||
res.Merge(red)
|
||||
} else if red.wantsRedeemOnMerge {
|
||||
pools.poolOfResults.RedeemResult(red)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -154,7 +168,7 @@ func (d *defaultValidator) validateDefaultValueValidAgainstSchema() *Result {
|
|||
// reset explored schemas to get depth-first recursive-proof exploration
|
||||
d.resetVisited()
|
||||
for nm, sch := range s.spec.Spec().Definitions {
|
||||
res.Merge(d.validateDefaultValueSchemaAgainstSchema(fmt.Sprintf("definitions.%s", nm), "body", &sch)) //#nosec
|
||||
res.Merge(d.validateDefaultValueSchemaAgainstSchema("definitions."+nm, "body", &sch)) //#nosec
|
||||
}
|
||||
}
|
||||
return res
|
||||
|
|
@ -176,10 +190,12 @@ func (d *defaultValidator) validateDefaultInResponse(resp *spec.Response, respon
|
|||
d.resetVisited()
|
||||
|
||||
if h.Default != nil {
|
||||
red := NewHeaderValidator(nm, &h, s.KnownFormats).Validate(h.Default) //#nosec
|
||||
red := newHeaderValidator(nm, &h, s.KnownFormats, d.schemaOptions).Validate(h.Default) //#nosec
|
||||
if red.HasErrorsOrWarnings() {
|
||||
res.AddErrors(defaultValueHeaderDoesNotValidateMsg(operationID, nm, responseName))
|
||||
res.Merge(red)
|
||||
} else if red.wantsRedeemOnMerge {
|
||||
pools.poolOfResults.RedeemResult(red)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -189,6 +205,8 @@ func (d *defaultValidator) validateDefaultInResponse(resp *spec.Response, respon
|
|||
if red.HasErrorsOrWarnings() {
|
||||
res.AddErrors(defaultValueHeaderItemsDoesNotValidateMsg(operationID, nm, responseName))
|
||||
res.Merge(red)
|
||||
} else if red.wantsRedeemOnMerge {
|
||||
pools.poolOfResults.RedeemResult(red)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -208,6 +226,8 @@ func (d *defaultValidator) validateDefaultInResponse(resp *spec.Response, respon
|
|||
// Additional message to make sure the context of the error is not lost
|
||||
res.AddErrors(defaultValueInDoesNotValidateMsg(operationID, responseName))
|
||||
res.Merge(red)
|
||||
} else if red.wantsRedeemOnMerge {
|
||||
pools.poolOfResults.RedeemResult(red)
|
||||
}
|
||||
}
|
||||
return res
|
||||
|
|
@ -219,11 +239,13 @@ func (d *defaultValidator) validateDefaultValueSchemaAgainstSchema(path, in stri
|
|||
return nil
|
||||
}
|
||||
d.beingVisited(path)
|
||||
res := new(Result)
|
||||
res := pools.poolOfResults.BorrowResult()
|
||||
s := d.SpecValidator
|
||||
|
||||
if schema.Default != nil {
|
||||
res.Merge(NewSchemaValidator(schema, s.spec.Spec(), path+".default", s.KnownFormats, SwaggerSchema(true)).Validate(schema.Default))
|
||||
res.Merge(
|
||||
newSchemaValidator(schema, s.spec.Spec(), path+".default", s.KnownFormats, d.schemaOptions).Validate(schema.Default),
|
||||
)
|
||||
}
|
||||
if schema.Items != nil {
|
||||
if schema.Items.Schema != nil {
|
||||
|
|
@ -241,7 +263,7 @@ func (d *defaultValidator) validateDefaultValueSchemaAgainstSchema(path, in stri
|
|||
}
|
||||
if schema.AdditionalItems != nil && schema.AdditionalItems.Schema != nil {
|
||||
// NOTE: we keep validating values, even though additionalItems is not supported by Swagger 2.0 (and 3.0 as well)
|
||||
res.Merge(d.validateDefaultValueSchemaAgainstSchema(fmt.Sprintf("%s.additionalItems", path), in, schema.AdditionalItems.Schema))
|
||||
res.Merge(d.validateDefaultValueSchemaAgainstSchema(path+".additionalItems", in, schema.AdditionalItems.Schema))
|
||||
}
|
||||
for propName, prop := range schema.Properties {
|
||||
res.Merge(d.validateDefaultValueSchemaAgainstSchema(path+"."+propName, in, &prop)) //#nosec
|
||||
|
|
@ -250,7 +272,7 @@ func (d *defaultValidator) validateDefaultValueSchemaAgainstSchema(path, in stri
|
|||
res.Merge(d.validateDefaultValueSchemaAgainstSchema(path+"."+propName, in, &prop)) //#nosec
|
||||
}
|
||||
if schema.AdditionalProperties != nil && schema.AdditionalProperties.Schema != nil {
|
||||
res.Merge(d.validateDefaultValueSchemaAgainstSchema(fmt.Sprintf("%s.additionalProperties", path), in, schema.AdditionalProperties.Schema))
|
||||
res.Merge(d.validateDefaultValueSchemaAgainstSchema(path+".additionalProperties", in, schema.AdditionalProperties.Schema))
|
||||
}
|
||||
if schema.AllOf != nil {
|
||||
for i, aoSch := range schema.AllOf {
|
||||
|
|
@ -263,11 +285,13 @@ func (d *defaultValidator) validateDefaultValueSchemaAgainstSchema(path, in stri
|
|||
// TODO: Temporary duplicated code. Need to refactor with examples
|
||||
|
||||
func (d *defaultValidator) validateDefaultValueItemsAgainstSchema(path, in string, root interface{}, items *spec.Items) *Result {
|
||||
res := new(Result)
|
||||
res := pools.poolOfResults.BorrowResult()
|
||||
s := d.SpecValidator
|
||||
if items != nil {
|
||||
if items.Default != nil {
|
||||
res.Merge(newItemsValidator(path, in, items, root, s.KnownFormats).Validate(0, items.Default))
|
||||
res.Merge(
|
||||
newItemsValidator(path, in, items, root, s.KnownFormats, d.schemaOptions).Validate(0, items.Default),
|
||||
)
|
||||
}
|
||||
if items.Items != nil {
|
||||
res.Merge(d.validateDefaultValueItemsAgainstSchema(path+"[0].default", in, root, items.Items))
|
||||
|
|
|
|||
62
vendor/github.com/go-openapi/validate/example_validator.go
generated
vendored
62
vendor/github.com/go-openapi/validate/example_validator.go
generated
vendored
|
|
@ -23,17 +23,27 @@ import (
|
|||
// ExampleValidator validates example values defined in a spec
|
||||
type exampleValidator struct {
|
||||
SpecValidator *SpecValidator
|
||||
visitedSchemas map[string]bool
|
||||
visitedSchemas map[string]struct{}
|
||||
schemaOptions *SchemaValidatorOptions
|
||||
}
|
||||
|
||||
// resetVisited resets the internal state of visited schemas
|
||||
func (ex *exampleValidator) resetVisited() {
|
||||
ex.visitedSchemas = map[string]bool{}
|
||||
if ex.visitedSchemas == nil {
|
||||
ex.visitedSchemas = make(map[string]struct{})
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// TODO(go1.21): clear(ex.visitedSchemas)
|
||||
for k := range ex.visitedSchemas {
|
||||
delete(ex.visitedSchemas, k)
|
||||
}
|
||||
}
|
||||
|
||||
// beingVisited asserts a schema is being visited
|
||||
func (ex *exampleValidator) beingVisited(path string) {
|
||||
ex.visitedSchemas[path] = true
|
||||
ex.visitedSchemas[path] = struct{}{}
|
||||
}
|
||||
|
||||
// isVisited tells if a path has already been visited
|
||||
|
|
@ -48,8 +58,9 @@ func (ex *exampleValidator) isVisited(path string) bool {
|
|||
// - schemas
|
||||
// - individual property
|
||||
// - responses
|
||||
func (ex *exampleValidator) Validate() (errs *Result) {
|
||||
errs = new(Result)
|
||||
func (ex *exampleValidator) Validate() *Result {
|
||||
errs := pools.poolOfResults.BorrowResult()
|
||||
|
||||
if ex == nil || ex.SpecValidator == nil {
|
||||
return errs
|
||||
}
|
||||
|
|
@ -64,7 +75,7 @@ func (ex *exampleValidator) validateExampleValueValidAgainstSchema() *Result {
|
|||
// in: schemas, properties, object, items
|
||||
// not in: headers, parameters without schema
|
||||
|
||||
res := new(Result)
|
||||
res := pools.poolOfResults.BorrowResult()
|
||||
s := ex.SpecValidator
|
||||
|
||||
for method, pathItem := range s.expandedAnalyzer().Operations() {
|
||||
|
|
@ -82,10 +93,12 @@ func (ex *exampleValidator) validateExampleValueValidAgainstSchema() *Result {
|
|||
// default values provided must validate against their inline definition (no explicit schema)
|
||||
if param.Example != nil && param.Schema == nil {
|
||||
// check param default value is valid
|
||||
red := NewParamValidator(¶m, s.KnownFormats).Validate(param.Example) //#nosec
|
||||
red := newParamValidator(¶m, s.KnownFormats, ex.schemaOptions).Validate(param.Example) //#nosec
|
||||
if red.HasErrorsOrWarnings() {
|
||||
res.AddWarnings(exampleValueDoesNotValidateMsg(param.Name, param.In))
|
||||
res.MergeAsWarnings(red)
|
||||
} else if red.wantsRedeemOnMerge {
|
||||
pools.poolOfResults.RedeemResult(red)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -95,6 +108,8 @@ func (ex *exampleValidator) validateExampleValueValidAgainstSchema() *Result {
|
|||
if red.HasErrorsOrWarnings() {
|
||||
res.AddWarnings(exampleValueItemsDoesNotValidateMsg(param.Name, param.In))
|
||||
res.Merge(red)
|
||||
} else if red.wantsRedeemOnMerge {
|
||||
pools.poolOfResults.RedeemResult(red)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -104,6 +119,8 @@ func (ex *exampleValidator) validateExampleValueValidAgainstSchema() *Result {
|
|||
if red.HasErrorsOrWarnings() {
|
||||
res.AddWarnings(exampleValueDoesNotValidateMsg(param.Name, param.In))
|
||||
res.Merge(red)
|
||||
} else if red.wantsRedeemOnMerge {
|
||||
pools.poolOfResults.RedeemResult(red)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -129,7 +146,7 @@ func (ex *exampleValidator) validateExampleValueValidAgainstSchema() *Result {
|
|||
// reset explored schemas to get depth-first recursive-proof exploration
|
||||
ex.resetVisited()
|
||||
for nm, sch := range s.spec.Spec().Definitions {
|
||||
res.Merge(ex.validateExampleValueSchemaAgainstSchema(fmt.Sprintf("definitions.%s", nm), "body", &sch)) //#nosec
|
||||
res.Merge(ex.validateExampleValueSchemaAgainstSchema("definitions."+nm, "body", &sch)) //#nosec
|
||||
}
|
||||
}
|
||||
return res
|
||||
|
|
@ -151,10 +168,12 @@ func (ex *exampleValidator) validateExampleInResponse(resp *spec.Response, respo
|
|||
ex.resetVisited()
|
||||
|
||||
if h.Example != nil {
|
||||
red := NewHeaderValidator(nm, &h, s.KnownFormats).Validate(h.Example) //#nosec
|
||||
red := newHeaderValidator(nm, &h, s.KnownFormats, ex.schemaOptions).Validate(h.Example) //#nosec
|
||||
if red.HasErrorsOrWarnings() {
|
||||
res.AddWarnings(exampleValueHeaderDoesNotValidateMsg(operationID, nm, responseName))
|
||||
res.MergeAsWarnings(red)
|
||||
} else if red.wantsRedeemOnMerge {
|
||||
pools.poolOfResults.RedeemResult(red)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -164,6 +183,8 @@ func (ex *exampleValidator) validateExampleInResponse(resp *spec.Response, respo
|
|||
if red.HasErrorsOrWarnings() {
|
||||
res.AddWarnings(exampleValueHeaderItemsDoesNotValidateMsg(operationID, nm, responseName))
|
||||
res.MergeAsWarnings(red)
|
||||
} else if red.wantsRedeemOnMerge {
|
||||
pools.poolOfResults.RedeemResult(red)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -183,13 +204,17 @@ func (ex *exampleValidator) validateExampleInResponse(resp *spec.Response, respo
|
|||
// Additional message to make sure the context of the error is not lost
|
||||
res.AddWarnings(exampleValueInDoesNotValidateMsg(operationID, responseName))
|
||||
res.Merge(red)
|
||||
} else if red.wantsRedeemOnMerge {
|
||||
pools.poolOfResults.RedeemResult(red)
|
||||
}
|
||||
}
|
||||
|
||||
if response.Examples != nil {
|
||||
if response.Schema != nil {
|
||||
if example, ok := response.Examples["application/json"]; ok {
|
||||
res.MergeAsWarnings(NewSchemaValidator(response.Schema, s.spec.Spec(), path+".examples", s.KnownFormats, SwaggerSchema(true)).Validate(example))
|
||||
res.MergeAsWarnings(
|
||||
newSchemaValidator(response.Schema, s.spec.Spec(), path+".examples", s.KnownFormats, s.schemaOptions).Validate(example),
|
||||
)
|
||||
} else {
|
||||
// TODO: validate other media types too
|
||||
res.AddWarnings(examplesMimeNotSupportedMsg(operationID, responseName))
|
||||
|
|
@ -208,10 +233,12 @@ func (ex *exampleValidator) validateExampleValueSchemaAgainstSchema(path, in str
|
|||
}
|
||||
ex.beingVisited(path)
|
||||
s := ex.SpecValidator
|
||||
res := new(Result)
|
||||
res := pools.poolOfResults.BorrowResult()
|
||||
|
||||
if schema.Example != nil {
|
||||
res.MergeAsWarnings(NewSchemaValidator(schema, s.spec.Spec(), path+".example", s.KnownFormats, SwaggerSchema(true)).Validate(schema.Example))
|
||||
res.MergeAsWarnings(
|
||||
newSchemaValidator(schema, s.spec.Spec(), path+".example", s.KnownFormats, ex.schemaOptions).Validate(schema.Example),
|
||||
)
|
||||
}
|
||||
if schema.Items != nil {
|
||||
if schema.Items.Schema != nil {
|
||||
|
|
@ -229,7 +256,7 @@ func (ex *exampleValidator) validateExampleValueSchemaAgainstSchema(path, in str
|
|||
}
|
||||
if schema.AdditionalItems != nil && schema.AdditionalItems.Schema != nil {
|
||||
// NOTE: we keep validating values, even though additionalItems is unsupported in Swagger 2.0 (and 3.0 as well)
|
||||
res.Merge(ex.validateExampleValueSchemaAgainstSchema(fmt.Sprintf("%s.additionalItems", path), in, schema.AdditionalItems.Schema))
|
||||
res.Merge(ex.validateExampleValueSchemaAgainstSchema(path+".additionalItems", in, schema.AdditionalItems.Schema))
|
||||
}
|
||||
for propName, prop := range schema.Properties {
|
||||
res.Merge(ex.validateExampleValueSchemaAgainstSchema(path+"."+propName, in, &prop)) //#nosec
|
||||
|
|
@ -238,7 +265,7 @@ func (ex *exampleValidator) validateExampleValueSchemaAgainstSchema(path, in str
|
|||
res.Merge(ex.validateExampleValueSchemaAgainstSchema(path+"."+propName, in, &prop)) //#nosec
|
||||
}
|
||||
if schema.AdditionalProperties != nil && schema.AdditionalProperties.Schema != nil {
|
||||
res.Merge(ex.validateExampleValueSchemaAgainstSchema(fmt.Sprintf("%s.additionalProperties", path), in, schema.AdditionalProperties.Schema))
|
||||
res.Merge(ex.validateExampleValueSchemaAgainstSchema(path+".additionalProperties", in, schema.AdditionalProperties.Schema))
|
||||
}
|
||||
if schema.AllOf != nil {
|
||||
for i, aoSch := range schema.AllOf {
|
||||
|
|
@ -252,11 +279,13 @@ func (ex *exampleValidator) validateExampleValueSchemaAgainstSchema(path, in str
|
|||
//
|
||||
|
||||
func (ex *exampleValidator) validateExampleValueItemsAgainstSchema(path, in string, root interface{}, items *spec.Items) *Result {
|
||||
res := new(Result)
|
||||
res := pools.poolOfResults.BorrowResult()
|
||||
s := ex.SpecValidator
|
||||
if items != nil {
|
||||
if items.Example != nil {
|
||||
res.MergeAsWarnings(newItemsValidator(path, in, items, root, s.KnownFormats).Validate(0, items.Example))
|
||||
res.MergeAsWarnings(
|
||||
newItemsValidator(path, in, items, root, s.KnownFormats, ex.schemaOptions).Validate(0, items.Example),
|
||||
)
|
||||
}
|
||||
if items.Items != nil {
|
||||
res.Merge(ex.validateExampleValueItemsAgainstSchema(path+"[0].example", in, root, items.Items))
|
||||
|
|
@ -265,5 +294,6 @@ func (ex *exampleValidator) validateExampleValueItemsAgainstSchema(path, in stri
|
|||
res.AddErrors(invalidPatternInMsg(path, in, items.Pattern))
|
||||
}
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
|
|
|
|||
78
vendor/github.com/go-openapi/validate/formats.go
generated
vendored
78
vendor/github.com/go-openapi/validate/formats.go
generated
vendored
|
|
@ -22,10 +22,32 @@ import (
|
|||
)
|
||||
|
||||
type formatValidator struct {
|
||||
Format string
|
||||
Path string
|
||||
In string
|
||||
Format string
|
||||
KnownFormats strfmt.Registry
|
||||
Options *SchemaValidatorOptions
|
||||
}
|
||||
|
||||
func newFormatValidator(path, in, format string, formats strfmt.Registry, opts *SchemaValidatorOptions) *formatValidator {
|
||||
if opts == nil {
|
||||
opts = new(SchemaValidatorOptions)
|
||||
}
|
||||
|
||||
var f *formatValidator
|
||||
if opts.recycleValidators {
|
||||
f = pools.poolOfFormatValidators.BorrowValidator()
|
||||
} else {
|
||||
f = new(formatValidator)
|
||||
}
|
||||
|
||||
f.Path = path
|
||||
f.In = in
|
||||
f.Format = format
|
||||
f.KnownFormats = formats
|
||||
f.Options = opts
|
||||
|
||||
return f
|
||||
}
|
||||
|
||||
func (f *formatValidator) SetPath(path string) {
|
||||
|
|
@ -33,37 +55,45 @@ func (f *formatValidator) SetPath(path string) {
|
|||
}
|
||||
|
||||
func (f *formatValidator) Applies(source interface{}, kind reflect.Kind) bool {
|
||||
doit := func() bool {
|
||||
if source == nil {
|
||||
return false
|
||||
}
|
||||
switch source := source.(type) {
|
||||
case *spec.Items:
|
||||
return kind == reflect.String && f.KnownFormats.ContainsName(source.Format)
|
||||
case *spec.Parameter:
|
||||
return kind == reflect.String && f.KnownFormats.ContainsName(source.Format)
|
||||
case *spec.Schema:
|
||||
return kind == reflect.String && f.KnownFormats.ContainsName(source.Format)
|
||||
case *spec.Header:
|
||||
return kind == reflect.String && f.KnownFormats.ContainsName(source.Format)
|
||||
}
|
||||
if source == nil || f.KnownFormats == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
switch source := source.(type) {
|
||||
case *spec.Items:
|
||||
return kind == reflect.String && f.KnownFormats.ContainsName(source.Format)
|
||||
case *spec.Parameter:
|
||||
return kind == reflect.String && f.KnownFormats.ContainsName(source.Format)
|
||||
case *spec.Schema:
|
||||
return kind == reflect.String && f.KnownFormats.ContainsName(source.Format)
|
||||
case *spec.Header:
|
||||
return kind == reflect.String && f.KnownFormats.ContainsName(source.Format)
|
||||
default:
|
||||
return false
|
||||
}
|
||||
r := doit()
|
||||
debugLog("format validator for %q applies %t for %T (kind: %v)\n", f.Path, r, source, kind)
|
||||
return r
|
||||
}
|
||||
|
||||
func (f *formatValidator) Validate(val interface{}) *Result {
|
||||
result := new(Result)
|
||||
debugLog("validating \"%v\" against format: %s", val, f.Format)
|
||||
if f.Options.recycleValidators {
|
||||
defer func() {
|
||||
f.redeem()
|
||||
}()
|
||||
}
|
||||
|
||||
var result *Result
|
||||
if f.Options.recycleResult {
|
||||
result = pools.poolOfResults.BorrowResult()
|
||||
} else {
|
||||
result = new(Result)
|
||||
}
|
||||
|
||||
if err := FormatOf(f.Path, f.In, f.Format, val.(string), f.KnownFormats); err != nil {
|
||||
result.AddErrors(err)
|
||||
}
|
||||
|
||||
if result.HasErrors() {
|
||||
return result
|
||||
}
|
||||
return nil
|
||||
return result
|
||||
}
|
||||
|
||||
func (f *formatValidator) redeem() {
|
||||
pools.poolOfFormatValidators.RedeemValidator(f)
|
||||
}
|
||||
|
|
|
|||
15
vendor/github.com/go-openapi/validate/helpers.go
generated
vendored
15
vendor/github.com/go-openapi/validate/helpers.go
generated
vendored
|
|
@ -101,9 +101,17 @@ type errorHelper struct {
|
|||
// A collection of unexported helpers for error construction
|
||||
}
|
||||
|
||||
func (h *errorHelper) sErr(err errors.Error) *Result {
|
||||
func (h *errorHelper) sErr(err errors.Error, recycle bool) *Result {
|
||||
// Builds a Result from standard errors.Error
|
||||
return &Result{Errors: []error{err}}
|
||||
var result *Result
|
||||
if recycle {
|
||||
result = pools.poolOfResults.BorrowResult()
|
||||
} else {
|
||||
result = new(Result)
|
||||
}
|
||||
result.Errors = []error{err}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func (h *errorHelper) addPointerError(res *Result, err error, ref string, fromPath string) *Result {
|
||||
|
|
@ -225,7 +233,7 @@ func (h *paramHelper) safeExpandedParamsFor(path, method, operationID string, re
|
|||
operation.Parameters = resolvedParams
|
||||
|
||||
for _, ppr := range s.expandedAnalyzer().SafeParamsFor(method, path,
|
||||
func(p spec.Parameter, err error) bool {
|
||||
func(_ spec.Parameter, err error) bool {
|
||||
// since params have already been expanded, there are few causes for error
|
||||
res.AddErrors(someParametersBrokenMsg(path, method, operationID))
|
||||
// original error from analyzer
|
||||
|
|
@ -306,6 +314,7 @@ func (r *responseHelper) expandResponseRef(
|
|||
errorHelp.addPointerError(res, err, response.Ref.String(), path)
|
||||
return nil, res
|
||||
}
|
||||
|
||||
return response, res
|
||||
}
|
||||
|
||||
|
|
|
|||
480
vendor/github.com/go-openapi/validate/object_validator.go
generated
vendored
480
vendor/github.com/go-openapi/validate/object_validator.go
generated
vendored
|
|
@ -15,8 +15,8 @@
|
|||
package validate
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/go-openapi/errors"
|
||||
|
|
@ -35,62 +35,116 @@ type objectValidator struct {
|
|||
PatternProperties map[string]spec.Schema
|
||||
Root interface{}
|
||||
KnownFormats strfmt.Registry
|
||||
Options SchemaValidatorOptions
|
||||
Options *SchemaValidatorOptions
|
||||
splitPath []string
|
||||
}
|
||||
|
||||
func newObjectValidator(path, in string,
|
||||
maxProperties, minProperties *int64, required []string, properties spec.SchemaProperties,
|
||||
additionalProperties *spec.SchemaOrBool, patternProperties spec.SchemaProperties,
|
||||
root interface{}, formats strfmt.Registry, opts *SchemaValidatorOptions) *objectValidator {
|
||||
if opts == nil {
|
||||
opts = new(SchemaValidatorOptions)
|
||||
}
|
||||
|
||||
var v *objectValidator
|
||||
if opts.recycleValidators {
|
||||
v = pools.poolOfObjectValidators.BorrowValidator()
|
||||
} else {
|
||||
v = new(objectValidator)
|
||||
}
|
||||
|
||||
v.Path = path
|
||||
v.In = in
|
||||
v.MaxProperties = maxProperties
|
||||
v.MinProperties = minProperties
|
||||
v.Required = required
|
||||
v.Properties = properties
|
||||
v.AdditionalProperties = additionalProperties
|
||||
v.PatternProperties = patternProperties
|
||||
v.Root = root
|
||||
v.KnownFormats = formats
|
||||
v.Options = opts
|
||||
v.splitPath = strings.Split(v.Path, ".")
|
||||
|
||||
return v
|
||||
}
|
||||
|
||||
func (o *objectValidator) SetPath(path string) {
|
||||
o.Path = path
|
||||
o.splitPath = strings.Split(path, ".")
|
||||
}
|
||||
|
||||
func (o *objectValidator) Applies(source interface{}, kind reflect.Kind) bool {
|
||||
// TODO: this should also work for structs
|
||||
// there is a problem in the type validator where it will be unhappy about null values
|
||||
// so that requires more testing
|
||||
r := reflect.TypeOf(source) == specSchemaType && (kind == reflect.Map || kind == reflect.Struct)
|
||||
debugLog("object validator for %q applies %t for %T (kind: %v)\n", o.Path, r, source, kind)
|
||||
return r
|
||||
_, isSchema := source.(*spec.Schema)
|
||||
return isSchema && (kind == reflect.Map || kind == reflect.Struct)
|
||||
}
|
||||
|
||||
func (o *objectValidator) isProperties() bool {
|
||||
p := strings.Split(o.Path, ".")
|
||||
p := o.splitPath
|
||||
return len(p) > 1 && p[len(p)-1] == jsonProperties && p[len(p)-2] != jsonProperties
|
||||
}
|
||||
|
||||
func (o *objectValidator) isDefault() bool {
|
||||
p := strings.Split(o.Path, ".")
|
||||
p := o.splitPath
|
||||
return len(p) > 1 && p[len(p)-1] == jsonDefault && p[len(p)-2] != jsonDefault
|
||||
}
|
||||
|
||||
func (o *objectValidator) isExample() bool {
|
||||
p := strings.Split(o.Path, ".")
|
||||
p := o.splitPath
|
||||
return len(p) > 1 && (p[len(p)-1] == swaggerExample || p[len(p)-1] == swaggerExamples) && p[len(p)-2] != swaggerExample
|
||||
}
|
||||
|
||||
func (o *objectValidator) checkArrayMustHaveItems(res *Result, val map[string]interface{}) {
|
||||
// for swagger 2.0 schemas, there is an additional constraint to have array items defined explicitly.
|
||||
// with pure jsonschema draft 4, one may have arrays with undefined items (i.e. any type).
|
||||
if t, typeFound := val[jsonType]; typeFound {
|
||||
if tpe, ok := t.(string); ok && tpe == arrayType {
|
||||
if item, itemsKeyFound := val[jsonItems]; !itemsKeyFound {
|
||||
res.AddErrors(errors.Required(jsonItems, o.Path, item))
|
||||
}
|
||||
}
|
||||
if val == nil {
|
||||
return
|
||||
}
|
||||
|
||||
t, typeFound := val[jsonType]
|
||||
if !typeFound {
|
||||
return
|
||||
}
|
||||
|
||||
tpe, isString := t.(string)
|
||||
if !isString || tpe != arrayType {
|
||||
return
|
||||
}
|
||||
|
||||
item, itemsKeyFound := val[jsonItems]
|
||||
if itemsKeyFound {
|
||||
return
|
||||
}
|
||||
|
||||
res.AddErrors(errors.Required(jsonItems, o.Path, item))
|
||||
}
|
||||
|
||||
func (o *objectValidator) checkItemsMustBeTypeArray(res *Result, val map[string]interface{}) {
|
||||
if !o.isProperties() && !o.isDefault() && !o.isExample() {
|
||||
if _, itemsKeyFound := val[jsonItems]; itemsKeyFound {
|
||||
t, typeFound := val[jsonType]
|
||||
if typeFound {
|
||||
if tpe, ok := t.(string); !ok || tpe != arrayType {
|
||||
res.AddErrors(errors.InvalidType(o.Path, o.In, arrayType, nil))
|
||||
}
|
||||
} else {
|
||||
// there is no type
|
||||
res.AddErrors(errors.Required(jsonType, o.Path, t))
|
||||
}
|
||||
}
|
||||
if val == nil {
|
||||
return
|
||||
}
|
||||
|
||||
if o.isProperties() || o.isDefault() || o.isExample() {
|
||||
return
|
||||
}
|
||||
|
||||
_, itemsKeyFound := val[jsonItems]
|
||||
if !itemsKeyFound {
|
||||
return
|
||||
}
|
||||
|
||||
t, typeFound := val[jsonType]
|
||||
if !typeFound {
|
||||
// there is no type
|
||||
res.AddErrors(errors.Required(jsonType, o.Path, t))
|
||||
}
|
||||
|
||||
if tpe, isString := t.(string); !isString || tpe != arrayType {
|
||||
res.AddErrors(errors.InvalidType(o.Path, o.In, arrayType, nil))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -104,176 +158,274 @@ func (o *objectValidator) precheck(res *Result, val map[string]interface{}) {
|
|||
}
|
||||
|
||||
func (o *objectValidator) Validate(data interface{}) *Result {
|
||||
val := data.(map[string]interface{})
|
||||
// TODO: guard against nil data
|
||||
if o.Options.recycleValidators {
|
||||
defer func() {
|
||||
o.redeem()
|
||||
}()
|
||||
}
|
||||
|
||||
var val map[string]interface{}
|
||||
if data != nil {
|
||||
var ok bool
|
||||
val, ok = data.(map[string]interface{})
|
||||
if !ok {
|
||||
return errorHelp.sErr(invalidObjectMsg(o.Path, o.In), o.Options.recycleResult)
|
||||
}
|
||||
}
|
||||
numKeys := int64(len(val))
|
||||
|
||||
if o.MinProperties != nil && numKeys < *o.MinProperties {
|
||||
return errorHelp.sErr(errors.TooFewProperties(o.Path, o.In, *o.MinProperties))
|
||||
return errorHelp.sErr(errors.TooFewProperties(o.Path, o.In, *o.MinProperties), o.Options.recycleResult)
|
||||
}
|
||||
if o.MaxProperties != nil && numKeys > *o.MaxProperties {
|
||||
return errorHelp.sErr(errors.TooManyProperties(o.Path, o.In, *o.MaxProperties))
|
||||
return errorHelp.sErr(errors.TooManyProperties(o.Path, o.In, *o.MaxProperties), o.Options.recycleResult)
|
||||
}
|
||||
|
||||
res := new(Result)
|
||||
var res *Result
|
||||
if o.Options.recycleResult {
|
||||
res = pools.poolOfResults.BorrowResult()
|
||||
} else {
|
||||
res = new(Result)
|
||||
}
|
||||
|
||||
o.precheck(res, val)
|
||||
|
||||
// check validity of field names
|
||||
if o.AdditionalProperties != nil && !o.AdditionalProperties.Allows {
|
||||
// Case: additionalProperties: false
|
||||
for k := range val {
|
||||
_, regularProperty := o.Properties[k]
|
||||
matched := false
|
||||
|
||||
for pk := range o.PatternProperties {
|
||||
if matches, _ := regexp.MatchString(pk, k); matches {
|
||||
matched = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !regularProperty && k != "$schema" && k != "id" && !matched {
|
||||
// Special properties "$schema" and "id" are ignored
|
||||
res.AddErrors(errors.PropertyNotAllowed(o.Path, o.In, k))
|
||||
|
||||
// BUG(fredbi): This section should move to a part dedicated to spec validation as
|
||||
// it will conflict with regular schemas where a property "headers" is defined.
|
||||
|
||||
//
|
||||
// Croaks a more explicit message on top of the standard one
|
||||
// on some recognized cases.
|
||||
//
|
||||
// NOTE: edge cases with invalid type assertion are simply ignored here.
|
||||
// NOTE: prefix your messages here by "IMPORTANT!" so there are not filtered
|
||||
// by higher level callers (the IMPORTANT! tag will be eventually
|
||||
// removed).
|
||||
if k == "headers" && val[k] != nil {
|
||||
// $ref is forbidden in header
|
||||
if headers, mapOk := val[k].(map[string]interface{}); mapOk {
|
||||
for headerKey, headerBody := range headers {
|
||||
if headerBody != nil {
|
||||
if headerSchema, mapOfMapOk := headerBody.(map[string]interface{}); mapOfMapOk {
|
||||
if _, found := headerSchema["$ref"]; found {
|
||||
var msg string
|
||||
if refString, stringOk := headerSchema["$ref"].(string); stringOk {
|
||||
msg = strings.Join([]string{", one may not use $ref=\":", refString, "\""}, "")
|
||||
}
|
||||
res.AddErrors(refNotAllowedInHeaderMsg(o.Path, headerKey, msg))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
case "$ref":
|
||||
if val[k] != nil {
|
||||
// TODO: check context of that ref: warn about siblings, check against invalid context
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
||||
}
|
||||
o.validateNoAdditionalProperties(val, res)
|
||||
} else {
|
||||
// Cases: no additionalProperties (implying: true), or additionalProperties: true, or additionalProperties: { <<schema>> }
|
||||
for key, value := range val {
|
||||
_, regularProperty := o.Properties[key]
|
||||
|
||||
// Validates property against "patternProperties" if applicable
|
||||
// BUG(fredbi): succeededOnce is always false
|
||||
|
||||
// NOTE: how about regular properties which do not match patternProperties?
|
||||
matched, succeededOnce, _ := o.validatePatternProperty(key, value, res)
|
||||
|
||||
if !(regularProperty || matched || succeededOnce) {
|
||||
|
||||
// Cases: properties which are not regular properties and have not been matched by the PatternProperties validator
|
||||
if o.AdditionalProperties != nil && o.AdditionalProperties.Schema != nil {
|
||||
// AdditionalProperties as Schema
|
||||
r := NewSchemaValidator(o.AdditionalProperties.Schema, o.Root, o.Path+"."+key, o.KnownFormats, o.Options.Options()...).Validate(value)
|
||||
res.mergeForField(data.(map[string]interface{}), key, r)
|
||||
} else if regularProperty && !(matched || succeededOnce) {
|
||||
// TODO: this is dead code since regularProperty=false here
|
||||
res.AddErrors(errors.FailedAllPatternProperties(o.Path, o.In, key))
|
||||
}
|
||||
}
|
||||
}
|
||||
// Valid cases: additionalProperties: true or undefined
|
||||
// Cases: empty additionalProperties (implying: true), or additionalProperties: true, or additionalProperties: { <<schema>> }
|
||||
o.validateAdditionalProperties(val, res)
|
||||
}
|
||||
|
||||
createdFromDefaults := map[string]bool{}
|
||||
|
||||
// Property types:
|
||||
// - regular Property
|
||||
for pName := range o.Properties {
|
||||
pSchema := o.Properties[pName] // one instance per iteration
|
||||
rName := pName
|
||||
if o.Path != "" {
|
||||
rName = o.Path + "." + pName
|
||||
}
|
||||
|
||||
// Recursively validates each property against its schema
|
||||
if v, ok := val[pName]; ok {
|
||||
r := NewSchemaValidator(&pSchema, o.Root, rName, o.KnownFormats, o.Options.Options()...).Validate(v)
|
||||
res.mergeForField(data.(map[string]interface{}), pName, r)
|
||||
} else if pSchema.Default != nil {
|
||||
// If a default value is defined, creates the property from defaults
|
||||
// NOTE: JSON schema does not enforce default values to be valid against schema. Swagger does.
|
||||
createdFromDefaults[pName] = true
|
||||
res.addPropertySchemata(data.(map[string]interface{}), pName, &pSchema)
|
||||
}
|
||||
}
|
||||
|
||||
// Check required properties
|
||||
if len(o.Required) > 0 {
|
||||
for _, k := range o.Required {
|
||||
if v, ok := val[k]; !ok && !createdFromDefaults[k] {
|
||||
res.AddErrors(errors.Required(o.Path+"."+k, o.In, v))
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
o.validatePropertiesSchema(val, res)
|
||||
|
||||
// Check patternProperties
|
||||
// TODO: it looks like we have done that twice in many cases
|
||||
for key, value := range val {
|
||||
_, regularProperty := o.Properties[key]
|
||||
matched, _ /*succeededOnce*/, patterns := o.validatePatternProperty(key, value, res)
|
||||
if !regularProperty && (matched /*|| succeededOnce*/) {
|
||||
for _, pName := range patterns {
|
||||
if v, ok := o.PatternProperties[pName]; ok {
|
||||
r := NewSchemaValidator(&v, o.Root, o.Path+"."+key, o.KnownFormats, o.Options.Options()...).Validate(value)
|
||||
res.mergeForField(data.(map[string]interface{}), key, r)
|
||||
}
|
||||
matched, _, patterns := o.validatePatternProperty(key, value, res) // applies to regular properties as well
|
||||
if regularProperty || !matched {
|
||||
continue
|
||||
}
|
||||
|
||||
for _, pName := range patterns {
|
||||
if v, ok := o.PatternProperties[pName]; ok {
|
||||
r := newSchemaValidator(&v, o.Root, o.Path+"."+key, o.KnownFormats, o.Options).Validate(value)
|
||||
res.mergeForField(data.(map[string]interface{}), key, r)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
func (o *objectValidator) validateNoAdditionalProperties(val map[string]interface{}, res *Result) {
|
||||
for k := range val {
|
||||
if k == "$schema" || k == "id" {
|
||||
// special properties "$schema" and "id" are ignored
|
||||
continue
|
||||
}
|
||||
|
||||
_, regularProperty := o.Properties[k]
|
||||
if regularProperty {
|
||||
continue
|
||||
}
|
||||
|
||||
matched := false
|
||||
for pk := range o.PatternProperties {
|
||||
re, err := compileRegexp(pk)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
if matches := re.MatchString(k); matches {
|
||||
matched = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if matched {
|
||||
continue
|
||||
}
|
||||
|
||||
res.AddErrors(errors.PropertyNotAllowed(o.Path, o.In, k))
|
||||
|
||||
// BUG(fredbi): This section should move to a part dedicated to spec validation as
|
||||
// it will conflict with regular schemas where a property "headers" is defined.
|
||||
|
||||
//
|
||||
// Croaks a more explicit message on top of the standard one
|
||||
// on some recognized cases.
|
||||
//
|
||||
// NOTE: edge cases with invalid type assertion are simply ignored here.
|
||||
// NOTE: prefix your messages here by "IMPORTANT!" so there are not filtered
|
||||
// by higher level callers (the IMPORTANT! tag will be eventually
|
||||
// removed).
|
||||
if k != "headers" || val[k] == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
// $ref is forbidden in header
|
||||
headers, mapOk := val[k].(map[string]interface{})
|
||||
if !mapOk {
|
||||
continue
|
||||
}
|
||||
|
||||
for headerKey, headerBody := range headers {
|
||||
if headerBody == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
headerSchema, mapOfMapOk := headerBody.(map[string]interface{})
|
||||
if !mapOfMapOk {
|
||||
continue
|
||||
}
|
||||
|
||||
_, found := headerSchema["$ref"]
|
||||
if !found {
|
||||
continue
|
||||
}
|
||||
|
||||
refString, stringOk := headerSchema["$ref"].(string)
|
||||
if !stringOk {
|
||||
continue
|
||||
}
|
||||
|
||||
msg := strings.Join([]string{", one may not use $ref=\":", refString, "\""}, "")
|
||||
res.AddErrors(refNotAllowedInHeaderMsg(o.Path, headerKey, msg))
|
||||
/*
|
||||
case "$ref":
|
||||
if val[k] != nil {
|
||||
// TODO: check context of that ref: warn about siblings, check against invalid context
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (o *objectValidator) validateAdditionalProperties(val map[string]interface{}, res *Result) {
|
||||
for key, value := range val {
|
||||
_, regularProperty := o.Properties[key]
|
||||
if regularProperty {
|
||||
continue
|
||||
}
|
||||
|
||||
// Validates property against "patternProperties" if applicable
|
||||
// BUG(fredbi): succeededOnce is always false
|
||||
|
||||
// NOTE: how about regular properties which do not match patternProperties?
|
||||
matched, succeededOnce, _ := o.validatePatternProperty(key, value, res)
|
||||
if matched || succeededOnce {
|
||||
continue
|
||||
}
|
||||
|
||||
if o.AdditionalProperties == nil || o.AdditionalProperties.Schema == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
// Cases: properties which are not regular properties and have not been matched by the PatternProperties validator
|
||||
// AdditionalProperties as Schema
|
||||
r := newSchemaValidator(o.AdditionalProperties.Schema, o.Root, o.Path+"."+key, o.KnownFormats, o.Options).Validate(value)
|
||||
res.mergeForField(val, key, r)
|
||||
}
|
||||
// Valid cases: additionalProperties: true or undefined
|
||||
}
|
||||
|
||||
func (o *objectValidator) validatePropertiesSchema(val map[string]interface{}, res *Result) {
|
||||
createdFromDefaults := map[string]struct{}{}
|
||||
|
||||
// Property types:
|
||||
// - regular Property
|
||||
pSchema := pools.poolOfSchemas.BorrowSchema() // recycle a spec.Schema object which lifespan extends only to the validation of properties
|
||||
defer func() {
|
||||
pools.poolOfSchemas.RedeemSchema(pSchema)
|
||||
}()
|
||||
|
||||
for pName := range o.Properties {
|
||||
*pSchema = o.Properties[pName]
|
||||
var rName string
|
||||
if o.Path == "" {
|
||||
rName = pName
|
||||
} else {
|
||||
rName = o.Path + "." + pName
|
||||
}
|
||||
|
||||
// Recursively validates each property against its schema
|
||||
v, ok := val[pName]
|
||||
if ok {
|
||||
r := newSchemaValidator(pSchema, o.Root, rName, o.KnownFormats, o.Options).Validate(v)
|
||||
res.mergeForField(val, pName, r)
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
if pSchema.Default != nil {
|
||||
// if a default value is defined, creates the property from defaults
|
||||
// NOTE: JSON schema does not enforce default values to be valid against schema. Swagger does.
|
||||
createdFromDefaults[pName] = struct{}{}
|
||||
if !o.Options.skipSchemataResult {
|
||||
res.addPropertySchemata(val, pName, pSchema) // this shallow-clones the content of the pSchema pointer
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(o.Required) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
// Check required properties
|
||||
for _, k := range o.Required {
|
||||
v, ok := val[k]
|
||||
if ok {
|
||||
continue
|
||||
}
|
||||
_, isCreatedFromDefaults := createdFromDefaults[k]
|
||||
if isCreatedFromDefaults {
|
||||
continue
|
||||
}
|
||||
|
||||
res.AddErrors(errors.Required(fmt.Sprintf("%s.%s", o.Path, k), o.In, v))
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: succeededOnce is not used anywhere
|
||||
func (o *objectValidator) validatePatternProperty(key string, value interface{}, result *Result) (bool, bool, []string) {
|
||||
matched := false
|
||||
succeededOnce := false
|
||||
var patterns []string
|
||||
|
||||
for k, schema := range o.PatternProperties {
|
||||
sch := schema
|
||||
if match, _ := regexp.MatchString(k, key); match {
|
||||
patterns = append(patterns, k)
|
||||
matched = true
|
||||
validator := NewSchemaValidator(&sch, o.Root, o.Path+"."+key, o.KnownFormats, o.Options.Options()...)
|
||||
|
||||
res := validator.Validate(value)
|
||||
result.Merge(res)
|
||||
}
|
||||
if len(o.PatternProperties) == 0 {
|
||||
return false, false, nil
|
||||
}
|
||||
|
||||
// BUG(fredbi): can't get to here. Should remove dead code (commented out).
|
||||
matched := false
|
||||
succeededOnce := false
|
||||
patterns := make([]string, 0, len(o.PatternProperties))
|
||||
|
||||
// if succeededOnce {
|
||||
// result.Inc()
|
||||
// }
|
||||
schema := pools.poolOfSchemas.BorrowSchema()
|
||||
defer func() {
|
||||
pools.poolOfSchemas.RedeemSchema(schema)
|
||||
}()
|
||||
|
||||
for k := range o.PatternProperties {
|
||||
re, err := compileRegexp(k)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
match := re.MatchString(key)
|
||||
if !match {
|
||||
continue
|
||||
}
|
||||
|
||||
*schema = o.PatternProperties[k]
|
||||
patterns = append(patterns, k)
|
||||
matched = true
|
||||
validator := newSchemaValidator(schema, o.Root, fmt.Sprintf("%s.%s", o.Path, key), o.KnownFormats, o.Options)
|
||||
|
||||
res := validator.Validate(value)
|
||||
result.Merge(res)
|
||||
}
|
||||
|
||||
return matched, succeededOnce, patterns
|
||||
}
|
||||
|
||||
func (o *objectValidator) redeem() {
|
||||
pools.poolOfObjectValidators.RedeemValidator(o)
|
||||
}
|
||||
|
|
|
|||
1
vendor/github.com/go-openapi/validate/options.go
generated
vendored
1
vendor/github.com/go-openapi/validate/options.go
generated
vendored
|
|
@ -31,6 +31,7 @@ type Opts struct {
|
|||
// GET:/v1/{shelve} and GET:/v1/{book}, where the IDs are "shelve/*" and
|
||||
// /"shelve/*/book/*" respectively.
|
||||
StrictPathParamUniqueness bool
|
||||
SkipSchemataResult bool
|
||||
}
|
||||
|
||||
var (
|
||||
|
|
|
|||
366
vendor/github.com/go-openapi/validate/pools.go
generated
vendored
Normal file
366
vendor/github.com/go-openapi/validate/pools.go
generated
vendored
Normal file
|
|
@ -0,0 +1,366 @@
|
|||
//go:build !validatedebug
|
||||
|
||||
package validate
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/go-openapi/spec"
|
||||
)
|
||||
|
||||
var pools allPools
|
||||
|
||||
func init() {
|
||||
resetPools()
|
||||
}
|
||||
|
||||
func resetPools() {
|
||||
// NOTE: for testing purpose, we might want to reset pools after calling Validate twice.
|
||||
// The pool is corrupted in that case: calling Put twice inserts a duplicate in the pool
|
||||
// and further calls to Get are mishandled.
|
||||
|
||||
pools = allPools{
|
||||
poolOfSchemaValidators: schemaValidatorsPool{
|
||||
Pool: &sync.Pool{
|
||||
New: func() any {
|
||||
s := &SchemaValidator{}
|
||||
|
||||
return s
|
||||
},
|
||||
},
|
||||
},
|
||||
poolOfObjectValidators: objectValidatorsPool{
|
||||
Pool: &sync.Pool{
|
||||
New: func() any {
|
||||
s := &objectValidator{}
|
||||
|
||||
return s
|
||||
},
|
||||
},
|
||||
},
|
||||
poolOfSliceValidators: sliceValidatorsPool{
|
||||
Pool: &sync.Pool{
|
||||
New: func() any {
|
||||
s := &schemaSliceValidator{}
|
||||
|
||||
return s
|
||||
},
|
||||
},
|
||||
},
|
||||
poolOfItemsValidators: itemsValidatorsPool{
|
||||
Pool: &sync.Pool{
|
||||
New: func() any {
|
||||
s := &itemsValidator{}
|
||||
|
||||
return s
|
||||
},
|
||||
},
|
||||
},
|
||||
poolOfBasicCommonValidators: basicCommonValidatorsPool{
|
||||
Pool: &sync.Pool{
|
||||
New: func() any {
|
||||
s := &basicCommonValidator{}
|
||||
|
||||
return s
|
||||
},
|
||||
},
|
||||
},
|
||||
poolOfHeaderValidators: headerValidatorsPool{
|
||||
Pool: &sync.Pool{
|
||||
New: func() any {
|
||||
s := &HeaderValidator{}
|
||||
|
||||
return s
|
||||
},
|
||||
},
|
||||
},
|
||||
poolOfParamValidators: paramValidatorsPool{
|
||||
Pool: &sync.Pool{
|
||||
New: func() any {
|
||||
s := &ParamValidator{}
|
||||
|
||||
return s
|
||||
},
|
||||
},
|
||||
},
|
||||
poolOfBasicSliceValidators: basicSliceValidatorsPool{
|
||||
Pool: &sync.Pool{
|
||||
New: func() any {
|
||||
s := &basicSliceValidator{}
|
||||
|
||||
return s
|
||||
},
|
||||
},
|
||||
},
|
||||
poolOfNumberValidators: numberValidatorsPool{
|
||||
Pool: &sync.Pool{
|
||||
New: func() any {
|
||||
s := &numberValidator{}
|
||||
|
||||
return s
|
||||
},
|
||||
},
|
||||
},
|
||||
poolOfStringValidators: stringValidatorsPool{
|
||||
Pool: &sync.Pool{
|
||||
New: func() any {
|
||||
s := &stringValidator{}
|
||||
|
||||
return s
|
||||
},
|
||||
},
|
||||
},
|
||||
poolOfSchemaPropsValidators: schemaPropsValidatorsPool{
|
||||
Pool: &sync.Pool{
|
||||
New: func() any {
|
||||
s := &schemaPropsValidator{}
|
||||
|
||||
return s
|
||||
},
|
||||
},
|
||||
},
|
||||
poolOfFormatValidators: formatValidatorsPool{
|
||||
Pool: &sync.Pool{
|
||||
New: func() any {
|
||||
s := &formatValidator{}
|
||||
|
||||
return s
|
||||
},
|
||||
},
|
||||
},
|
||||
poolOfTypeValidators: typeValidatorsPool{
|
||||
Pool: &sync.Pool{
|
||||
New: func() any {
|
||||
s := &typeValidator{}
|
||||
|
||||
return s
|
||||
},
|
||||
},
|
||||
},
|
||||
poolOfSchemas: schemasPool{
|
||||
Pool: &sync.Pool{
|
||||
New: func() any {
|
||||
s := &spec.Schema{}
|
||||
|
||||
return s
|
||||
},
|
||||
},
|
||||
},
|
||||
poolOfResults: resultsPool{
|
||||
Pool: &sync.Pool{
|
||||
New: func() any {
|
||||
s := &Result{}
|
||||
|
||||
return s
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
type (
|
||||
allPools struct {
|
||||
// memory pools for all validator objects.
|
||||
//
|
||||
// Each pool can be borrowed from and redeemed to.
|
||||
poolOfSchemaValidators schemaValidatorsPool
|
||||
poolOfObjectValidators objectValidatorsPool
|
||||
poolOfSliceValidators sliceValidatorsPool
|
||||
poolOfItemsValidators itemsValidatorsPool
|
||||
poolOfBasicCommonValidators basicCommonValidatorsPool
|
||||
poolOfHeaderValidators headerValidatorsPool
|
||||
poolOfParamValidators paramValidatorsPool
|
||||
poolOfBasicSliceValidators basicSliceValidatorsPool
|
||||
poolOfNumberValidators numberValidatorsPool
|
||||
poolOfStringValidators stringValidatorsPool
|
||||
poolOfSchemaPropsValidators schemaPropsValidatorsPool
|
||||
poolOfFormatValidators formatValidatorsPool
|
||||
poolOfTypeValidators typeValidatorsPool
|
||||
poolOfSchemas schemasPool
|
||||
poolOfResults resultsPool
|
||||
}
|
||||
|
||||
schemaValidatorsPool struct {
|
||||
*sync.Pool
|
||||
}
|
||||
|
||||
objectValidatorsPool struct {
|
||||
*sync.Pool
|
||||
}
|
||||
|
||||
sliceValidatorsPool struct {
|
||||
*sync.Pool
|
||||
}
|
||||
|
||||
itemsValidatorsPool struct {
|
||||
*sync.Pool
|
||||
}
|
||||
|
||||
basicCommonValidatorsPool struct {
|
||||
*sync.Pool
|
||||
}
|
||||
|
||||
headerValidatorsPool struct {
|
||||
*sync.Pool
|
||||
}
|
||||
|
||||
paramValidatorsPool struct {
|
||||
*sync.Pool
|
||||
}
|
||||
|
||||
basicSliceValidatorsPool struct {
|
||||
*sync.Pool
|
||||
}
|
||||
|
||||
numberValidatorsPool struct {
|
||||
*sync.Pool
|
||||
}
|
||||
|
||||
stringValidatorsPool struct {
|
||||
*sync.Pool
|
||||
}
|
||||
|
||||
schemaPropsValidatorsPool struct {
|
||||
*sync.Pool
|
||||
}
|
||||
|
||||
formatValidatorsPool struct {
|
||||
*sync.Pool
|
||||
}
|
||||
|
||||
typeValidatorsPool struct {
|
||||
*sync.Pool
|
||||
}
|
||||
|
||||
schemasPool struct {
|
||||
*sync.Pool
|
||||
}
|
||||
|
||||
resultsPool struct {
|
||||
*sync.Pool
|
||||
}
|
||||
)
|
||||
|
||||
func (p schemaValidatorsPool) BorrowValidator() *SchemaValidator {
|
||||
return p.Get().(*SchemaValidator)
|
||||
}
|
||||
|
||||
func (p schemaValidatorsPool) RedeemValidator(s *SchemaValidator) {
|
||||
// NOTE: s might be nil. In that case, Put is a noop.
|
||||
p.Put(s)
|
||||
}
|
||||
|
||||
func (p objectValidatorsPool) BorrowValidator() *objectValidator {
|
||||
return p.Get().(*objectValidator)
|
||||
}
|
||||
|
||||
func (p objectValidatorsPool) RedeemValidator(s *objectValidator) {
|
||||
p.Put(s)
|
||||
}
|
||||
|
||||
func (p sliceValidatorsPool) BorrowValidator() *schemaSliceValidator {
|
||||
return p.Get().(*schemaSliceValidator)
|
||||
}
|
||||
|
||||
func (p sliceValidatorsPool) RedeemValidator(s *schemaSliceValidator) {
|
||||
p.Put(s)
|
||||
}
|
||||
|
||||
func (p itemsValidatorsPool) BorrowValidator() *itemsValidator {
|
||||
return p.Get().(*itemsValidator)
|
||||
}
|
||||
|
||||
func (p itemsValidatorsPool) RedeemValidator(s *itemsValidator) {
|
||||
p.Put(s)
|
||||
}
|
||||
|
||||
func (p basicCommonValidatorsPool) BorrowValidator() *basicCommonValidator {
|
||||
return p.Get().(*basicCommonValidator)
|
||||
}
|
||||
|
||||
func (p basicCommonValidatorsPool) RedeemValidator(s *basicCommonValidator) {
|
||||
p.Put(s)
|
||||
}
|
||||
|
||||
func (p headerValidatorsPool) BorrowValidator() *HeaderValidator {
|
||||
return p.Get().(*HeaderValidator)
|
||||
}
|
||||
|
||||
func (p headerValidatorsPool) RedeemValidator(s *HeaderValidator) {
|
||||
p.Put(s)
|
||||
}
|
||||
|
||||
func (p paramValidatorsPool) BorrowValidator() *ParamValidator {
|
||||
return p.Get().(*ParamValidator)
|
||||
}
|
||||
|
||||
func (p paramValidatorsPool) RedeemValidator(s *ParamValidator) {
|
||||
p.Put(s)
|
||||
}
|
||||
|
||||
func (p basicSliceValidatorsPool) BorrowValidator() *basicSliceValidator {
|
||||
return p.Get().(*basicSliceValidator)
|
||||
}
|
||||
|
||||
func (p basicSliceValidatorsPool) RedeemValidator(s *basicSliceValidator) {
|
||||
p.Put(s)
|
||||
}
|
||||
|
||||
func (p numberValidatorsPool) BorrowValidator() *numberValidator {
|
||||
return p.Get().(*numberValidator)
|
||||
}
|
||||
|
||||
func (p numberValidatorsPool) RedeemValidator(s *numberValidator) {
|
||||
p.Put(s)
|
||||
}
|
||||
|
||||
func (p stringValidatorsPool) BorrowValidator() *stringValidator {
|
||||
return p.Get().(*stringValidator)
|
||||
}
|
||||
|
||||
func (p stringValidatorsPool) RedeemValidator(s *stringValidator) {
|
||||
p.Put(s)
|
||||
}
|
||||
|
||||
func (p schemaPropsValidatorsPool) BorrowValidator() *schemaPropsValidator {
|
||||
return p.Get().(*schemaPropsValidator)
|
||||
}
|
||||
|
||||
func (p schemaPropsValidatorsPool) RedeemValidator(s *schemaPropsValidator) {
|
||||
p.Put(s)
|
||||
}
|
||||
|
||||
func (p formatValidatorsPool) BorrowValidator() *formatValidator {
|
||||
return p.Get().(*formatValidator)
|
||||
}
|
||||
|
||||
func (p formatValidatorsPool) RedeemValidator(s *formatValidator) {
|
||||
p.Put(s)
|
||||
}
|
||||
|
||||
func (p typeValidatorsPool) BorrowValidator() *typeValidator {
|
||||
return p.Get().(*typeValidator)
|
||||
}
|
||||
|
||||
func (p typeValidatorsPool) RedeemValidator(s *typeValidator) {
|
||||
p.Put(s)
|
||||
}
|
||||
|
||||
func (p schemasPool) BorrowSchema() *spec.Schema {
|
||||
return p.Get().(*spec.Schema)
|
||||
}
|
||||
|
||||
func (p schemasPool) RedeemSchema(s *spec.Schema) {
|
||||
p.Put(s)
|
||||
}
|
||||
|
||||
func (p resultsPool) BorrowResult() *Result {
|
||||
return p.Get().(*Result).cleared()
|
||||
}
|
||||
|
||||
func (p resultsPool) RedeemResult(s *Result) {
|
||||
if s == emptyResult {
|
||||
return
|
||||
}
|
||||
p.Put(s)
|
||||
}
|
||||
1012
vendor/github.com/go-openapi/validate/pools_debug.go
generated
vendored
Normal file
1012
vendor/github.com/go-openapi/validate/pools_debug.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
123
vendor/github.com/go-openapi/validate/result.go
generated
vendored
123
vendor/github.com/go-openapi/validate/result.go
generated
vendored
|
|
@ -15,7 +15,7 @@
|
|||
package validate
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
stderrors "errors"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
|
|
@ -23,6 +23,8 @@ import (
|
|||
"github.com/go-openapi/spec"
|
||||
)
|
||||
|
||||
var emptyResult = &Result{MatchCount: 1}
|
||||
|
||||
// Result represents a validation result set, composed of
|
||||
// errors and warnings.
|
||||
//
|
||||
|
|
@ -50,8 +52,10 @@ type Result struct {
|
|||
// Schemata for slice items
|
||||
itemSchemata []itemSchemata
|
||||
|
||||
cachedFieldSchemta map[FieldKey][]*spec.Schema
|
||||
cachedItemSchemata map[ItemKey][]*spec.Schema
|
||||
cachedFieldSchemata map[FieldKey][]*spec.Schema
|
||||
cachedItemSchemata map[ItemKey][]*spec.Schema
|
||||
|
||||
wantsRedeemOnMerge bool
|
||||
}
|
||||
|
||||
// FieldKey is a pair of an object and a field, usable as a key for a map.
|
||||
|
|
@ -116,6 +120,9 @@ func (r *Result) Merge(others ...*Result) *Result {
|
|||
}
|
||||
r.mergeWithoutRootSchemata(other)
|
||||
r.rootObjectSchemata.Append(other.rootObjectSchemata)
|
||||
if other.wantsRedeemOnMerge {
|
||||
pools.poolOfResults.RedeemResult(other)
|
||||
}
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
|
@ -133,8 +140,8 @@ func (r *Result) RootObjectSchemata() []*spec.Schema {
|
|||
|
||||
// FieldSchemata returns the schemata which apply to fields in objects.
|
||||
func (r *Result) FieldSchemata() map[FieldKey][]*spec.Schema {
|
||||
if r.cachedFieldSchemta != nil {
|
||||
return r.cachedFieldSchemta
|
||||
if r.cachedFieldSchemata != nil {
|
||||
return r.cachedFieldSchemata
|
||||
}
|
||||
|
||||
ret := make(map[FieldKey][]*spec.Schema, len(r.fieldSchemata))
|
||||
|
|
@ -146,7 +153,8 @@ func (r *Result) FieldSchemata() map[FieldKey][]*spec.Schema {
|
|||
ret[key] = append(ret[key], fs.schemata.multiple...)
|
||||
}
|
||||
}
|
||||
r.cachedFieldSchemta = ret
|
||||
r.cachedFieldSchemata = ret
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
|
|
@ -170,7 +178,7 @@ func (r *Result) ItemSchemata() map[ItemKey][]*spec.Schema {
|
|||
}
|
||||
|
||||
func (r *Result) resetCaches() {
|
||||
r.cachedFieldSchemta = nil
|
||||
r.cachedFieldSchemata = nil
|
||||
r.cachedItemSchemata = nil
|
||||
}
|
||||
|
||||
|
|
@ -187,12 +195,16 @@ func (r *Result) mergeForField(obj map[string]interface{}, field string, other *
|
|||
if r.fieldSchemata == nil {
|
||||
r.fieldSchemata = make([]fieldSchemata, len(obj))
|
||||
}
|
||||
// clone other schemata, as other is about to be redeemed to the pool
|
||||
r.fieldSchemata = append(r.fieldSchemata, fieldSchemata{
|
||||
obj: obj,
|
||||
field: field,
|
||||
schemata: other.rootObjectSchemata,
|
||||
schemata: other.rootObjectSchemata.Clone(),
|
||||
})
|
||||
}
|
||||
if other.wantsRedeemOnMerge {
|
||||
pools.poolOfResults.RedeemResult(other)
|
||||
}
|
||||
|
||||
return r
|
||||
}
|
||||
|
|
@ -210,29 +222,38 @@ func (r *Result) mergeForSlice(slice reflect.Value, i int, other *Result) *Resul
|
|||
if r.itemSchemata == nil {
|
||||
r.itemSchemata = make([]itemSchemata, slice.Len())
|
||||
}
|
||||
// clone other schemata, as other is about to be redeemed to the pool
|
||||
r.itemSchemata = append(r.itemSchemata, itemSchemata{
|
||||
slice: slice,
|
||||
index: i,
|
||||
schemata: other.rootObjectSchemata,
|
||||
schemata: other.rootObjectSchemata.Clone(),
|
||||
})
|
||||
}
|
||||
|
||||
if other.wantsRedeemOnMerge {
|
||||
pools.poolOfResults.RedeemResult(other)
|
||||
}
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
// addRootObjectSchemata adds the given schemata for the root object of the result.
|
||||
// The slice schemata might be reused. I.e. do not modify it after being added to a result.
|
||||
//
|
||||
// Since the slice schemata might be reused, it is shallow-cloned before saving it into the result.
|
||||
func (r *Result) addRootObjectSchemata(s *spec.Schema) {
|
||||
r.rootObjectSchemata.Append(schemata{one: s})
|
||||
clone := *s
|
||||
r.rootObjectSchemata.Append(schemata{one: &clone})
|
||||
}
|
||||
|
||||
// addPropertySchemata adds the given schemata for the object and field.
|
||||
// The slice schemata might be reused. I.e. do not modify it after being added to a result.
|
||||
//
|
||||
// Since the slice schemata might be reused, it is shallow-cloned before saving it into the result.
|
||||
func (r *Result) addPropertySchemata(obj map[string]interface{}, fld string, schema *spec.Schema) {
|
||||
if r.fieldSchemata == nil {
|
||||
r.fieldSchemata = make([]fieldSchemata, 0, len(obj))
|
||||
}
|
||||
r.fieldSchemata = append(r.fieldSchemata, fieldSchemata{obj: obj, field: fld, schemata: schemata{one: schema}})
|
||||
clone := *schema
|
||||
r.fieldSchemata = append(r.fieldSchemata, fieldSchemata{obj: obj, field: fld, schemata: schemata{one: &clone}})
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -255,17 +276,21 @@ func (r *Result) mergeWithoutRootSchemata(other *Result) {
|
|||
|
||||
if other.fieldSchemata != nil {
|
||||
if r.fieldSchemata == nil {
|
||||
r.fieldSchemata = other.fieldSchemata
|
||||
} else {
|
||||
r.fieldSchemata = append(r.fieldSchemata, other.fieldSchemata...)
|
||||
r.fieldSchemata = make([]fieldSchemata, 0, len(other.fieldSchemata))
|
||||
}
|
||||
for _, field := range other.fieldSchemata {
|
||||
field.schemata = field.schemata.Clone()
|
||||
r.fieldSchemata = append(r.fieldSchemata, field)
|
||||
}
|
||||
}
|
||||
|
||||
if other.itemSchemata != nil {
|
||||
if r.itemSchemata == nil {
|
||||
r.itemSchemata = other.itemSchemata
|
||||
} else {
|
||||
r.itemSchemata = append(r.itemSchemata, other.itemSchemata...)
|
||||
r.itemSchemata = make([]itemSchemata, 0, len(other.itemSchemata))
|
||||
}
|
||||
for _, field := range other.itemSchemata {
|
||||
field.schemata = field.schemata.Clone()
|
||||
r.itemSchemata = append(r.itemSchemata, field)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -280,6 +305,9 @@ func (r *Result) MergeAsErrors(others ...*Result) *Result {
|
|||
r.AddErrors(other.Errors...)
|
||||
r.AddErrors(other.Warnings...)
|
||||
r.MatchCount += other.MatchCount
|
||||
if other.wantsRedeemOnMerge {
|
||||
pools.poolOfResults.RedeemResult(other)
|
||||
}
|
||||
}
|
||||
}
|
||||
return r
|
||||
|
|
@ -295,6 +323,9 @@ func (r *Result) MergeAsWarnings(others ...*Result) *Result {
|
|||
r.AddWarnings(other.Errors...)
|
||||
r.AddWarnings(other.Warnings...)
|
||||
r.MatchCount += other.MatchCount
|
||||
if other.wantsRedeemOnMerge {
|
||||
pools.poolOfResults.RedeemResult(other)
|
||||
}
|
||||
}
|
||||
}
|
||||
return r
|
||||
|
|
@ -356,16 +387,21 @@ func (r *Result) keepRelevantErrors() *Result {
|
|||
strippedErrors := []error{}
|
||||
for _, e := range r.Errors {
|
||||
if strings.HasPrefix(e.Error(), "IMPORTANT!") {
|
||||
strippedErrors = append(strippedErrors, fmt.Errorf(strings.TrimPrefix(e.Error(), "IMPORTANT!")))
|
||||
strippedErrors = append(strippedErrors, stderrors.New(strings.TrimPrefix(e.Error(), "IMPORTANT!")))
|
||||
}
|
||||
}
|
||||
strippedWarnings := []error{}
|
||||
for _, e := range r.Warnings {
|
||||
if strings.HasPrefix(e.Error(), "IMPORTANT!") {
|
||||
strippedWarnings = append(strippedWarnings, fmt.Errorf(strings.TrimPrefix(e.Error(), "IMPORTANT!")))
|
||||
strippedWarnings = append(strippedWarnings, stderrors.New(strings.TrimPrefix(e.Error(), "IMPORTANT!")))
|
||||
}
|
||||
}
|
||||
strippedResult := new(Result)
|
||||
var strippedResult *Result
|
||||
if r.wantsRedeemOnMerge {
|
||||
strippedResult = pools.poolOfResults.BorrowResult()
|
||||
} else {
|
||||
strippedResult = new(Result)
|
||||
}
|
||||
strippedResult.Errors = strippedErrors
|
||||
strippedResult.Warnings = strippedWarnings
|
||||
return strippedResult
|
||||
|
|
@ -427,6 +463,27 @@ func (r *Result) AsError() error {
|
|||
return errors.CompositeValidationError(r.Errors...)
|
||||
}
|
||||
|
||||
func (r *Result) cleared() *Result {
|
||||
// clear the Result to be reusable. Keep allocated capacity.
|
||||
r.Errors = r.Errors[:0]
|
||||
r.Warnings = r.Warnings[:0]
|
||||
r.MatchCount = 0
|
||||
r.data = nil
|
||||
r.rootObjectSchemata.one = nil
|
||||
r.rootObjectSchemata.multiple = r.rootObjectSchemata.multiple[:0]
|
||||
r.fieldSchemata = r.fieldSchemata[:0]
|
||||
r.itemSchemata = r.itemSchemata[:0]
|
||||
for k := range r.cachedFieldSchemata {
|
||||
delete(r.cachedFieldSchemata, k)
|
||||
}
|
||||
for k := range r.cachedItemSchemata {
|
||||
delete(r.cachedItemSchemata, k)
|
||||
}
|
||||
r.wantsRedeemOnMerge = true // mark this result as eligible for redeem when merged into another
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
// schemata is an arbitrary number of schemata. It does a distinction between zero,
|
||||
// one and many schemata to avoid slice allocations.
|
||||
type schemata struct {
|
||||
|
|
@ -453,7 +510,7 @@ func (s *schemata) Slice() []*spec.Schema {
|
|||
return s.multiple
|
||||
}
|
||||
|
||||
// appendSchemata appends the schemata in other to s. It mutated s in-place.
|
||||
// appendSchemata appends the schemata in other to s. It mutates s in-place.
|
||||
func (s *schemata) Append(other schemata) {
|
||||
if other.one == nil && len(other.multiple) == 0 {
|
||||
return
|
||||
|
|
@ -484,3 +541,23 @@ func (s *schemata) Append(other schemata) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s schemata) Clone() schemata {
|
||||
var clone schemata
|
||||
|
||||
if s.one != nil {
|
||||
clone.one = new(spec.Schema)
|
||||
*clone.one = *s.one
|
||||
}
|
||||
|
||||
if len(s.multiple) > 0 {
|
||||
clone.multiple = make([]*spec.Schema, len(s.multiple))
|
||||
for idx := 0; idx < len(s.multiple); idx++ {
|
||||
sp := new(spec.Schema)
|
||||
*sp = *s.multiple[idx]
|
||||
clone.multiple[idx] = sp
|
||||
}
|
||||
}
|
||||
|
||||
return clone
|
||||
}
|
||||
|
|
|
|||
258
vendor/github.com/go-openapi/validate/schema.go
generated
vendored
258
vendor/github.com/go-openapi/validate/schema.go
generated
vendored
|
|
@ -24,32 +24,32 @@ import (
|
|||
"github.com/go-openapi/swag"
|
||||
)
|
||||
|
||||
var (
|
||||
specSchemaType = reflect.TypeOf(&spec.Schema{})
|
||||
specParameterType = reflect.TypeOf(&spec.Parameter{})
|
||||
specHeaderType = reflect.TypeOf(&spec.Header{})
|
||||
// specItemsType = reflect.TypeOf(&spec.Items{})
|
||||
)
|
||||
|
||||
// SchemaValidator validates data against a JSON schema
|
||||
type SchemaValidator struct {
|
||||
Path string
|
||||
in string
|
||||
Schema *spec.Schema
|
||||
validators []valueValidator
|
||||
validators [8]valueValidator
|
||||
Root interface{}
|
||||
KnownFormats strfmt.Registry
|
||||
Options SchemaValidatorOptions
|
||||
Options *SchemaValidatorOptions
|
||||
}
|
||||
|
||||
// AgainstSchema validates the specified data against the provided schema, using a registry of supported formats.
|
||||
//
|
||||
// When no pre-parsed *spec.Schema structure is provided, it uses a JSON schema as default. See example.
|
||||
func AgainstSchema(schema *spec.Schema, data interface{}, formats strfmt.Registry, options ...Option) error {
|
||||
res := NewSchemaValidator(schema, nil, "", formats, options...).Validate(data)
|
||||
res := NewSchemaValidator(schema, nil, "", formats,
|
||||
append(options, WithRecycleValidators(true), withRecycleResults(true))...,
|
||||
).Validate(data)
|
||||
defer func() {
|
||||
pools.poolOfResults.RedeemResult(res)
|
||||
}()
|
||||
|
||||
if res.HasErrors() {
|
||||
return errors.CompositeValidationError(res.Errors...)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -57,6 +57,15 @@ func AgainstSchema(schema *spec.Schema, data interface{}, formats strfmt.Registr
|
|||
//
|
||||
// Panics if the provided schema is invalid.
|
||||
func NewSchemaValidator(schema *spec.Schema, rootSchema interface{}, root string, formats strfmt.Registry, options ...Option) *SchemaValidator {
|
||||
opts := new(SchemaValidatorOptions)
|
||||
for _, o := range options {
|
||||
o(opts)
|
||||
}
|
||||
|
||||
return newSchemaValidator(schema, rootSchema, root, formats, opts)
|
||||
}
|
||||
|
||||
func newSchemaValidator(schema *spec.Schema, rootSchema interface{}, root string, formats strfmt.Registry, opts *SchemaValidatorOptions) *SchemaValidator {
|
||||
if schema == nil {
|
||||
return nil
|
||||
}
|
||||
|
|
@ -72,17 +81,26 @@ func NewSchemaValidator(schema *spec.Schema, rootSchema interface{}, root string
|
|||
panic(msg)
|
||||
}
|
||||
}
|
||||
s := SchemaValidator{
|
||||
Path: root,
|
||||
in: "body",
|
||||
Schema: schema,
|
||||
Root: rootSchema,
|
||||
KnownFormats: formats,
|
||||
Options: SchemaValidatorOptions{}}
|
||||
for _, o := range options {
|
||||
o(&s.Options)
|
||||
|
||||
if opts == nil {
|
||||
opts = new(SchemaValidatorOptions)
|
||||
}
|
||||
s.validators = []valueValidator{
|
||||
|
||||
var s *SchemaValidator
|
||||
if opts.recycleValidators {
|
||||
s = pools.poolOfSchemaValidators.BorrowValidator()
|
||||
} else {
|
||||
s = new(SchemaValidator)
|
||||
}
|
||||
|
||||
s.Path = root
|
||||
s.in = "body"
|
||||
s.Schema = schema
|
||||
s.Root = rootSchema
|
||||
s.Options = opts
|
||||
s.KnownFormats = formats
|
||||
|
||||
s.validators = [8]valueValidator{
|
||||
s.typeValidator(),
|
||||
s.schemaPropsValidator(),
|
||||
s.stringValidator(),
|
||||
|
|
@ -92,7 +110,8 @@ func NewSchemaValidator(schema *spec.Schema, rootSchema interface{}, root string
|
|||
s.commonValidator(),
|
||||
s.objectValidator(),
|
||||
}
|
||||
return &s
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
// SetPath sets the path for this schema valdiator
|
||||
|
|
@ -108,17 +127,39 @@ func (s *SchemaValidator) Applies(source interface{}, _ reflect.Kind) bool {
|
|||
|
||||
// Validate validates the data against the schema
|
||||
func (s *SchemaValidator) Validate(data interface{}) *Result {
|
||||
result := &Result{data: data}
|
||||
if s == nil {
|
||||
return result
|
||||
return emptyResult
|
||||
}
|
||||
if s.Schema != nil {
|
||||
|
||||
if s.Options.recycleValidators {
|
||||
defer func() {
|
||||
s.redeemChildren()
|
||||
s.redeem() // one-time use validator
|
||||
}()
|
||||
}
|
||||
|
||||
var result *Result
|
||||
if s.Options.recycleResult {
|
||||
result = pools.poolOfResults.BorrowResult()
|
||||
result.data = data
|
||||
} else {
|
||||
result = &Result{data: data}
|
||||
}
|
||||
|
||||
if s.Schema != nil && !s.Options.skipSchemataResult {
|
||||
result.addRootObjectSchemata(s.Schema)
|
||||
}
|
||||
|
||||
if data == nil {
|
||||
// early exit with minimal validation
|
||||
result.Merge(s.validators[0].Validate(data)) // type validator
|
||||
result.Merge(s.validators[6].Validate(data)) // common validator
|
||||
|
||||
if s.Options.recycleValidators {
|
||||
s.validators[0] = nil
|
||||
s.validators[6] = nil
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
|
|
@ -147,6 +188,7 @@ func (s *SchemaValidator) Validate(data interface{}) *Result {
|
|||
if erri != nil {
|
||||
result.AddErrors(invalidTypeConversionMsg(s.Path, erri))
|
||||
result.Inc()
|
||||
|
||||
return result
|
||||
}
|
||||
d = in
|
||||
|
|
@ -155,6 +197,7 @@ func (s *SchemaValidator) Validate(data interface{}) *Result {
|
|||
if errf != nil {
|
||||
result.AddErrors(invalidTypeConversionMsg(s.Path, errf))
|
||||
result.Inc()
|
||||
|
||||
return result
|
||||
}
|
||||
d = nf
|
||||
|
|
@ -164,14 +207,26 @@ func (s *SchemaValidator) Validate(data interface{}) *Result {
|
|||
kind = tpe.Kind()
|
||||
}
|
||||
|
||||
for _, v := range s.validators {
|
||||
for idx, v := range s.validators {
|
||||
if !v.Applies(s.Schema, kind) {
|
||||
debugLog("%T does not apply for %v", v, kind)
|
||||
if s.Options.recycleValidators {
|
||||
// Validate won't be called, so relinquish this validator
|
||||
if redeemableChildren, ok := v.(interface{ redeemChildren() }); ok {
|
||||
redeemableChildren.redeemChildren()
|
||||
}
|
||||
if redeemable, ok := v.(interface{ redeem() }); ok {
|
||||
redeemable.redeem()
|
||||
}
|
||||
s.validators[idx] = nil // prevents further (unsafe) usage
|
||||
}
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
err := v.Validate(d)
|
||||
result.Merge(err)
|
||||
result.Merge(v.Validate(d))
|
||||
if s.Options.recycleValidators {
|
||||
s.validators[idx] = nil // prevents further (unsafe) usage
|
||||
}
|
||||
result.Inc()
|
||||
}
|
||||
result.Inc()
|
||||
|
|
@ -180,81 +235,120 @@ func (s *SchemaValidator) Validate(data interface{}) *Result {
|
|||
}
|
||||
|
||||
func (s *SchemaValidator) typeValidator() valueValidator {
|
||||
return &typeValidator{Type: s.Schema.Type, Nullable: s.Schema.Nullable, Format: s.Schema.Format, In: s.in, Path: s.Path}
|
||||
return newTypeValidator(
|
||||
s.Path,
|
||||
s.in,
|
||||
s.Schema.Type,
|
||||
s.Schema.Nullable,
|
||||
s.Schema.Format,
|
||||
s.Options,
|
||||
)
|
||||
}
|
||||
|
||||
func (s *SchemaValidator) commonValidator() valueValidator {
|
||||
return &basicCommonValidator{
|
||||
Path: s.Path,
|
||||
In: s.in,
|
||||
Enum: s.Schema.Enum,
|
||||
}
|
||||
return newBasicCommonValidator(
|
||||
s.Path,
|
||||
s.in,
|
||||
s.Schema.Default,
|
||||
s.Schema.Enum,
|
||||
s.Options,
|
||||
)
|
||||
}
|
||||
|
||||
func (s *SchemaValidator) sliceValidator() valueValidator {
|
||||
return &schemaSliceValidator{
|
||||
Path: s.Path,
|
||||
In: s.in,
|
||||
MaxItems: s.Schema.MaxItems,
|
||||
MinItems: s.Schema.MinItems,
|
||||
UniqueItems: s.Schema.UniqueItems,
|
||||
AdditionalItems: s.Schema.AdditionalItems,
|
||||
Items: s.Schema.Items,
|
||||
Root: s.Root,
|
||||
KnownFormats: s.KnownFormats,
|
||||
Options: s.Options,
|
||||
}
|
||||
return newSliceValidator(
|
||||
s.Path,
|
||||
s.in,
|
||||
s.Schema.MaxItems,
|
||||
s.Schema.MinItems,
|
||||
s.Schema.UniqueItems,
|
||||
s.Schema.AdditionalItems,
|
||||
s.Schema.Items,
|
||||
s.Root,
|
||||
s.KnownFormats,
|
||||
s.Options,
|
||||
)
|
||||
}
|
||||
|
||||
func (s *SchemaValidator) numberValidator() valueValidator {
|
||||
return &numberValidator{
|
||||
Path: s.Path,
|
||||
In: s.in,
|
||||
Default: s.Schema.Default,
|
||||
MultipleOf: s.Schema.MultipleOf,
|
||||
Maximum: s.Schema.Maximum,
|
||||
ExclusiveMaximum: s.Schema.ExclusiveMaximum,
|
||||
Minimum: s.Schema.Minimum,
|
||||
ExclusiveMinimum: s.Schema.ExclusiveMinimum,
|
||||
}
|
||||
return newNumberValidator(
|
||||
s.Path,
|
||||
s.in,
|
||||
s.Schema.Default,
|
||||
s.Schema.MultipleOf,
|
||||
s.Schema.Maximum,
|
||||
s.Schema.ExclusiveMaximum,
|
||||
s.Schema.Minimum,
|
||||
s.Schema.ExclusiveMinimum,
|
||||
"",
|
||||
"",
|
||||
s.Options,
|
||||
)
|
||||
}
|
||||
|
||||
func (s *SchemaValidator) stringValidator() valueValidator {
|
||||
return &stringValidator{
|
||||
Path: s.Path,
|
||||
In: s.in,
|
||||
MaxLength: s.Schema.MaxLength,
|
||||
MinLength: s.Schema.MinLength,
|
||||
Pattern: s.Schema.Pattern,
|
||||
}
|
||||
return newStringValidator(
|
||||
s.Path,
|
||||
s.in,
|
||||
nil,
|
||||
false,
|
||||
false,
|
||||
s.Schema.MaxLength,
|
||||
s.Schema.MinLength,
|
||||
s.Schema.Pattern,
|
||||
s.Options,
|
||||
)
|
||||
}
|
||||
|
||||
func (s *SchemaValidator) formatValidator() valueValidator {
|
||||
return &formatValidator{
|
||||
Path: s.Path,
|
||||
In: s.in,
|
||||
Format: s.Schema.Format,
|
||||
KnownFormats: s.KnownFormats,
|
||||
}
|
||||
return newFormatValidator(
|
||||
s.Path,
|
||||
s.in,
|
||||
s.Schema.Format,
|
||||
s.KnownFormats,
|
||||
s.Options,
|
||||
)
|
||||
}
|
||||
|
||||
func (s *SchemaValidator) schemaPropsValidator() valueValidator {
|
||||
sch := s.Schema
|
||||
return newSchemaPropsValidator(s.Path, s.in, sch.AllOf, sch.OneOf, sch.AnyOf, sch.Not, sch.Dependencies, s.Root, s.KnownFormats, s.Options.Options()...)
|
||||
return newSchemaPropsValidator(
|
||||
s.Path, s.in, sch.AllOf, sch.OneOf, sch.AnyOf, sch.Not, sch.Dependencies, s.Root, s.KnownFormats,
|
||||
s.Options,
|
||||
)
|
||||
}
|
||||
|
||||
func (s *SchemaValidator) objectValidator() valueValidator {
|
||||
return &objectValidator{
|
||||
Path: s.Path,
|
||||
In: s.in,
|
||||
MaxProperties: s.Schema.MaxProperties,
|
||||
MinProperties: s.Schema.MinProperties,
|
||||
Required: s.Schema.Required,
|
||||
Properties: s.Schema.Properties,
|
||||
AdditionalProperties: s.Schema.AdditionalProperties,
|
||||
PatternProperties: s.Schema.PatternProperties,
|
||||
Root: s.Root,
|
||||
KnownFormats: s.KnownFormats,
|
||||
Options: s.Options,
|
||||
return newObjectValidator(
|
||||
s.Path,
|
||||
s.in,
|
||||
s.Schema.MaxProperties,
|
||||
s.Schema.MinProperties,
|
||||
s.Schema.Required,
|
||||
s.Schema.Properties,
|
||||
s.Schema.AdditionalProperties,
|
||||
s.Schema.PatternProperties,
|
||||
s.Root,
|
||||
s.KnownFormats,
|
||||
s.Options,
|
||||
)
|
||||
}
|
||||
|
||||
func (s *SchemaValidator) redeem() {
|
||||
pools.poolOfSchemaValidators.RedeemValidator(s)
|
||||
}
|
||||
|
||||
func (s *SchemaValidator) redeemChildren() {
|
||||
for i, validator := range s.validators {
|
||||
if validator == nil {
|
||||
continue
|
||||
}
|
||||
if redeemableChildren, ok := validator.(interface{ redeemChildren() }); ok {
|
||||
redeemableChildren.redeemChildren()
|
||||
}
|
||||
if redeemable, ok := validator.(interface{ redeem() }); ok {
|
||||
redeemable.redeem()
|
||||
}
|
||||
s.validators[i] = nil // free up allocated children if not in pool
|
||||
}
|
||||
}
|
||||
|
|
|
|||
31
vendor/github.com/go-openapi/validate/schema_option.go
generated
vendored
31
vendor/github.com/go-openapi/validate/schema_option.go
generated
vendored
|
|
@ -18,6 +18,9 @@ package validate
|
|||
type SchemaValidatorOptions struct {
|
||||
EnableObjectArrayTypeCheck bool
|
||||
EnableArrayMustHaveItemsCheck bool
|
||||
recycleValidators bool
|
||||
recycleResult bool
|
||||
skipSchemataResult bool
|
||||
}
|
||||
|
||||
// Option sets optional rules for schema validation
|
||||
|
|
@ -45,10 +48,36 @@ func SwaggerSchema(enable bool) Option {
|
|||
}
|
||||
}
|
||||
|
||||
// Options returns current options
|
||||
// WithRecycleValidators saves memory allocations and makes validators
|
||||
// available for a single use of Validate() only.
|
||||
//
|
||||
// When a validator is recycled, called MUST not call the Validate() method twice.
|
||||
func WithRecycleValidators(enable bool) Option {
|
||||
return func(svo *SchemaValidatorOptions) {
|
||||
svo.recycleValidators = enable
|
||||
}
|
||||
}
|
||||
|
||||
func withRecycleResults(enable bool) Option {
|
||||
return func(svo *SchemaValidatorOptions) {
|
||||
svo.recycleResult = enable
|
||||
}
|
||||
}
|
||||
|
||||
// WithSkipSchemataResult skips the deep audit payload stored in validation Result
|
||||
func WithSkipSchemataResult(enable bool) Option {
|
||||
return func(svo *SchemaValidatorOptions) {
|
||||
svo.skipSchemataResult = enable
|
||||
}
|
||||
}
|
||||
|
||||
// Options returns the current set of options
|
||||
func (svo SchemaValidatorOptions) Options() []Option {
|
||||
return []Option{
|
||||
EnableObjectArrayTypeCheck(svo.EnableObjectArrayTypeCheck),
|
||||
EnableArrayMustHaveItemsCheck(svo.EnableArrayMustHaveItemsCheck),
|
||||
WithRecycleValidators(svo.recycleValidators),
|
||||
withRecycleResults(svo.recycleResult),
|
||||
WithSkipSchemataResult(svo.skipSchemataResult),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
436
vendor/github.com/go-openapi/validate/schema_props.go
generated
vendored
436
vendor/github.com/go-openapi/validate/schema_props.go
generated
vendored
|
|
@ -30,211 +30,327 @@ type schemaPropsValidator struct {
|
|||
AnyOf []spec.Schema
|
||||
Not *spec.Schema
|
||||
Dependencies spec.Dependencies
|
||||
anyOfValidators []SchemaValidator
|
||||
allOfValidators []SchemaValidator
|
||||
oneOfValidators []SchemaValidator
|
||||
anyOfValidators []*SchemaValidator
|
||||
allOfValidators []*SchemaValidator
|
||||
oneOfValidators []*SchemaValidator
|
||||
notValidator *SchemaValidator
|
||||
Root interface{}
|
||||
KnownFormats strfmt.Registry
|
||||
Options SchemaValidatorOptions
|
||||
Options *SchemaValidatorOptions
|
||||
}
|
||||
|
||||
func (s *schemaPropsValidator) SetPath(path string) {
|
||||
s.Path = path
|
||||
}
|
||||
|
||||
func newSchemaPropsValidator(path string, in string, allOf, oneOf, anyOf []spec.Schema, not *spec.Schema, deps spec.Dependencies, root interface{}, formats strfmt.Registry, options ...Option) *schemaPropsValidator {
|
||||
anyValidators := make([]SchemaValidator, 0, len(anyOf))
|
||||
for _, v := range anyOf {
|
||||
v := v
|
||||
anyValidators = append(anyValidators, *NewSchemaValidator(&v, root, path, formats, options...))
|
||||
func newSchemaPropsValidator(
|
||||
path string, in string, allOf, oneOf, anyOf []spec.Schema, not *spec.Schema, deps spec.Dependencies, root interface{}, formats strfmt.Registry,
|
||||
opts *SchemaValidatorOptions) *schemaPropsValidator {
|
||||
if opts == nil {
|
||||
opts = new(SchemaValidatorOptions)
|
||||
}
|
||||
allValidators := make([]SchemaValidator, 0, len(allOf))
|
||||
for _, v := range allOf {
|
||||
v := v
|
||||
allValidators = append(allValidators, *NewSchemaValidator(&v, root, path, formats, options...))
|
||||
|
||||
anyValidators := make([]*SchemaValidator, 0, len(anyOf))
|
||||
for i := range anyOf {
|
||||
anyValidators = append(anyValidators, newSchemaValidator(&anyOf[i], root, path, formats, opts))
|
||||
}
|
||||
oneValidators := make([]SchemaValidator, 0, len(oneOf))
|
||||
for _, v := range oneOf {
|
||||
v := v
|
||||
oneValidators = append(oneValidators, *NewSchemaValidator(&v, root, path, formats, options...))
|
||||
allValidators := make([]*SchemaValidator, 0, len(allOf))
|
||||
for i := range allOf {
|
||||
allValidators = append(allValidators, newSchemaValidator(&allOf[i], root, path, formats, opts))
|
||||
}
|
||||
oneValidators := make([]*SchemaValidator, 0, len(oneOf))
|
||||
for i := range oneOf {
|
||||
oneValidators = append(oneValidators, newSchemaValidator(&oneOf[i], root, path, formats, opts))
|
||||
}
|
||||
|
||||
var notValidator *SchemaValidator
|
||||
if not != nil {
|
||||
notValidator = NewSchemaValidator(not, root, path, formats, options...)
|
||||
notValidator = newSchemaValidator(not, root, path, formats, opts)
|
||||
}
|
||||
|
||||
schOptions := &SchemaValidatorOptions{}
|
||||
for _, o := range options {
|
||||
o(schOptions)
|
||||
}
|
||||
return &schemaPropsValidator{
|
||||
Path: path,
|
||||
In: in,
|
||||
AllOf: allOf,
|
||||
OneOf: oneOf,
|
||||
AnyOf: anyOf,
|
||||
Not: not,
|
||||
Dependencies: deps,
|
||||
anyOfValidators: anyValidators,
|
||||
allOfValidators: allValidators,
|
||||
oneOfValidators: oneValidators,
|
||||
notValidator: notValidator,
|
||||
Root: root,
|
||||
KnownFormats: formats,
|
||||
Options: *schOptions,
|
||||
var s *schemaPropsValidator
|
||||
if opts.recycleValidators {
|
||||
s = pools.poolOfSchemaPropsValidators.BorrowValidator()
|
||||
} else {
|
||||
s = new(schemaPropsValidator)
|
||||
}
|
||||
|
||||
s.Path = path
|
||||
s.In = in
|
||||
s.AllOf = allOf
|
||||
s.OneOf = oneOf
|
||||
s.AnyOf = anyOf
|
||||
s.Not = not
|
||||
s.Dependencies = deps
|
||||
s.anyOfValidators = anyValidators
|
||||
s.allOfValidators = allValidators
|
||||
s.oneOfValidators = oneValidators
|
||||
s.notValidator = notValidator
|
||||
s.Root = root
|
||||
s.KnownFormats = formats
|
||||
s.Options = opts
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *schemaPropsValidator) Applies(source interface{}, kind reflect.Kind) bool {
|
||||
r := reflect.TypeOf(source) == specSchemaType
|
||||
debugLog("schema props validator for %q applies %t for %T (kind: %v)\n", s.Path, r, source, kind)
|
||||
return r
|
||||
func (s *schemaPropsValidator) Applies(source interface{}, _ reflect.Kind) bool {
|
||||
_, isSchema := source.(*spec.Schema)
|
||||
return isSchema
|
||||
}
|
||||
|
||||
func (s *schemaPropsValidator) Validate(data interface{}) *Result {
|
||||
mainResult := new(Result)
|
||||
var mainResult *Result
|
||||
if s.Options.recycleResult {
|
||||
mainResult = pools.poolOfResults.BorrowResult()
|
||||
} else {
|
||||
mainResult = new(Result)
|
||||
}
|
||||
|
||||
// Intermediary error results
|
||||
|
||||
// IMPORTANT! messages from underlying validators
|
||||
keepResultAnyOf := new(Result)
|
||||
keepResultOneOf := new(Result)
|
||||
keepResultAllOf := new(Result)
|
||||
var keepResultAnyOf, keepResultOneOf, keepResultAllOf *Result
|
||||
|
||||
if s.Options.recycleValidators {
|
||||
defer func() {
|
||||
s.redeemChildren()
|
||||
s.redeem()
|
||||
|
||||
// results are redeemed when merged
|
||||
}()
|
||||
}
|
||||
|
||||
// Validates at least one in anyOf schemas
|
||||
var firstSuccess *Result
|
||||
if len(s.anyOfValidators) > 0 {
|
||||
var bestFailures *Result
|
||||
succeededOnce := false
|
||||
for _, anyOfSchema := range s.anyOfValidators {
|
||||
result := anyOfSchema.Validate(data)
|
||||
// We keep inner IMPORTANT! errors no matter what MatchCount tells us
|
||||
keepResultAnyOf.Merge(result.keepRelevantErrors())
|
||||
if result.IsValid() {
|
||||
bestFailures = nil
|
||||
succeededOnce = true
|
||||
if firstSuccess == nil {
|
||||
firstSuccess = result
|
||||
}
|
||||
keepResultAnyOf = new(Result)
|
||||
break
|
||||
}
|
||||
// MatchCount is used to select errors from the schema with most positive checks
|
||||
if bestFailures == nil || result.MatchCount > bestFailures.MatchCount {
|
||||
bestFailures = result
|
||||
}
|
||||
}
|
||||
|
||||
if !succeededOnce {
|
||||
mainResult.AddErrors(mustValidateAtLeastOneSchemaMsg(s.Path))
|
||||
}
|
||||
if bestFailures != nil {
|
||||
mainResult.Merge(bestFailures)
|
||||
} else if firstSuccess != nil {
|
||||
mainResult.Merge(firstSuccess)
|
||||
}
|
||||
keepResultAnyOf = pools.poolOfResults.BorrowResult()
|
||||
s.validateAnyOf(data, mainResult, keepResultAnyOf)
|
||||
}
|
||||
|
||||
// Validates exactly one in oneOf schemas
|
||||
if len(s.oneOfValidators) > 0 {
|
||||
var bestFailures *Result
|
||||
var firstSuccess *Result
|
||||
validated := 0
|
||||
|
||||
for _, oneOfSchema := range s.oneOfValidators {
|
||||
result := oneOfSchema.Validate(data)
|
||||
// We keep inner IMPORTANT! errors no matter what MatchCount tells us
|
||||
keepResultOneOf.Merge(result.keepRelevantErrors())
|
||||
if result.IsValid() {
|
||||
validated++
|
||||
bestFailures = nil
|
||||
if firstSuccess == nil {
|
||||
firstSuccess = result
|
||||
}
|
||||
keepResultOneOf = new(Result)
|
||||
continue
|
||||
}
|
||||
// MatchCount is used to select errors from the schema with most positive checks
|
||||
if validated == 0 && (bestFailures == nil || result.MatchCount > bestFailures.MatchCount) {
|
||||
bestFailures = result
|
||||
}
|
||||
}
|
||||
|
||||
if validated != 1 {
|
||||
var additionalMsg string
|
||||
if validated == 0 {
|
||||
additionalMsg = "Found none valid"
|
||||
} else {
|
||||
additionalMsg = fmt.Sprintf("Found %d valid alternatives", validated)
|
||||
}
|
||||
|
||||
mainResult.AddErrors(mustValidateOnlyOneSchemaMsg(s.Path, additionalMsg))
|
||||
if bestFailures != nil {
|
||||
mainResult.Merge(bestFailures)
|
||||
}
|
||||
} else if firstSuccess != nil {
|
||||
mainResult.Merge(firstSuccess)
|
||||
}
|
||||
keepResultOneOf = pools.poolOfResults.BorrowResult()
|
||||
s.validateOneOf(data, mainResult, keepResultOneOf)
|
||||
}
|
||||
|
||||
// Validates all of allOf schemas
|
||||
if len(s.allOfValidators) > 0 {
|
||||
validated := 0
|
||||
|
||||
for _, allOfSchema := range s.allOfValidators {
|
||||
result := allOfSchema.Validate(data)
|
||||
// We keep inner IMPORTANT! errors no matter what MatchCount tells us
|
||||
keepResultAllOf.Merge(result.keepRelevantErrors())
|
||||
// keepResultAllOf.Merge(result)
|
||||
if result.IsValid() {
|
||||
validated++
|
||||
}
|
||||
mainResult.Merge(result)
|
||||
}
|
||||
|
||||
if validated != len(s.allOfValidators) {
|
||||
additionalMsg := ""
|
||||
if validated == 0 {
|
||||
additionalMsg = ". None validated"
|
||||
}
|
||||
|
||||
mainResult.AddErrors(mustValidateAllSchemasMsg(s.Path, additionalMsg))
|
||||
}
|
||||
keepResultAllOf = pools.poolOfResults.BorrowResult()
|
||||
s.validateAllOf(data, mainResult, keepResultAllOf)
|
||||
}
|
||||
|
||||
if s.notValidator != nil {
|
||||
result := s.notValidator.Validate(data)
|
||||
// We keep inner IMPORTANT! errors no matter what MatchCount tells us
|
||||
if result.IsValid() {
|
||||
mainResult.AddErrors(mustNotValidatechemaMsg(s.Path))
|
||||
}
|
||||
s.validateNot(data, mainResult)
|
||||
}
|
||||
|
||||
if s.Dependencies != nil && len(s.Dependencies) > 0 && reflect.TypeOf(data).Kind() == reflect.Map {
|
||||
val := data.(map[string]interface{})
|
||||
for key := range val {
|
||||
if dep, ok := s.Dependencies[key]; ok {
|
||||
|
||||
if dep.Schema != nil {
|
||||
mainResult.Merge(NewSchemaValidator(dep.Schema, s.Root, s.Path+"."+key, s.KnownFormats, s.Options.Options()...).Validate(data))
|
||||
continue
|
||||
}
|
||||
|
||||
if len(dep.Property) > 0 {
|
||||
for _, depKey := range dep.Property {
|
||||
if _, ok := val[depKey]; !ok {
|
||||
mainResult.AddErrors(hasADependencyMsg(s.Path, depKey))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
s.validateDependencies(data, mainResult)
|
||||
}
|
||||
|
||||
mainResult.Inc()
|
||||
|
||||
// In the end we retain best failures for schema validation
|
||||
// plus, if any, composite errors which may explain special cases (tagged as IMPORTANT!).
|
||||
return mainResult.Merge(keepResultAllOf, keepResultOneOf, keepResultAnyOf)
|
||||
}
|
||||
|
||||
func (s *schemaPropsValidator) validateAnyOf(data interface{}, mainResult, keepResultAnyOf *Result) {
|
||||
// Validates at least one in anyOf schemas
|
||||
var bestFailures *Result
|
||||
|
||||
for i, anyOfSchema := range s.anyOfValidators {
|
||||
result := anyOfSchema.Validate(data)
|
||||
if s.Options.recycleValidators {
|
||||
s.anyOfValidators[i] = nil
|
||||
}
|
||||
// We keep inner IMPORTANT! errors no matter what MatchCount tells us
|
||||
keepResultAnyOf.Merge(result.keepRelevantErrors()) // merges (and redeems) a new instance of Result
|
||||
|
||||
if result.IsValid() {
|
||||
if bestFailures != nil && bestFailures.wantsRedeemOnMerge {
|
||||
pools.poolOfResults.RedeemResult(bestFailures)
|
||||
}
|
||||
|
||||
_ = keepResultAnyOf.cleared()
|
||||
mainResult.Merge(result)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// MatchCount is used to select errors from the schema with most positive checks
|
||||
if bestFailures == nil || result.MatchCount > bestFailures.MatchCount {
|
||||
if bestFailures != nil && bestFailures.wantsRedeemOnMerge {
|
||||
pools.poolOfResults.RedeemResult(bestFailures)
|
||||
}
|
||||
bestFailures = result
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
if result.wantsRedeemOnMerge {
|
||||
pools.poolOfResults.RedeemResult(result) // this result is ditched
|
||||
}
|
||||
}
|
||||
|
||||
mainResult.AddErrors(mustValidateAtLeastOneSchemaMsg(s.Path))
|
||||
mainResult.Merge(bestFailures)
|
||||
}
|
||||
|
||||
func (s *schemaPropsValidator) validateOneOf(data interface{}, mainResult, keepResultOneOf *Result) {
|
||||
// Validates exactly one in oneOf schemas
|
||||
var (
|
||||
firstSuccess, bestFailures *Result
|
||||
validated int
|
||||
)
|
||||
|
||||
for i, oneOfSchema := range s.oneOfValidators {
|
||||
result := oneOfSchema.Validate(data)
|
||||
if s.Options.recycleValidators {
|
||||
s.oneOfValidators[i] = nil
|
||||
}
|
||||
|
||||
// We keep inner IMPORTANT! errors no matter what MatchCount tells us
|
||||
keepResultOneOf.Merge(result.keepRelevantErrors()) // merges (and redeems) a new instance of Result
|
||||
|
||||
if result.IsValid() {
|
||||
validated++
|
||||
_ = keepResultOneOf.cleared()
|
||||
|
||||
if firstSuccess == nil {
|
||||
firstSuccess = result
|
||||
} else if result.wantsRedeemOnMerge {
|
||||
pools.poolOfResults.RedeemResult(result) // this result is ditched
|
||||
}
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
// MatchCount is used to select errors from the schema with most positive checks
|
||||
if validated == 0 && (bestFailures == nil || result.MatchCount > bestFailures.MatchCount) {
|
||||
if bestFailures != nil && bestFailures.wantsRedeemOnMerge {
|
||||
pools.poolOfResults.RedeemResult(bestFailures)
|
||||
}
|
||||
bestFailures = result
|
||||
} else if result.wantsRedeemOnMerge {
|
||||
pools.poolOfResults.RedeemResult(result) // this result is ditched
|
||||
}
|
||||
}
|
||||
|
||||
switch validated {
|
||||
case 0:
|
||||
mainResult.AddErrors(mustValidateOnlyOneSchemaMsg(s.Path, "Found none valid"))
|
||||
mainResult.Merge(bestFailures)
|
||||
// firstSucess necessarily nil
|
||||
case 1:
|
||||
mainResult.Merge(firstSuccess)
|
||||
if bestFailures != nil && bestFailures.wantsRedeemOnMerge {
|
||||
pools.poolOfResults.RedeemResult(bestFailures)
|
||||
}
|
||||
default:
|
||||
mainResult.AddErrors(mustValidateOnlyOneSchemaMsg(s.Path, fmt.Sprintf("Found %d valid alternatives", validated)))
|
||||
mainResult.Merge(bestFailures)
|
||||
if firstSuccess != nil && firstSuccess.wantsRedeemOnMerge {
|
||||
pools.poolOfResults.RedeemResult(firstSuccess)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *schemaPropsValidator) validateAllOf(data interface{}, mainResult, keepResultAllOf *Result) {
|
||||
// Validates all of allOf schemas
|
||||
var validated int
|
||||
|
||||
for i, allOfSchema := range s.allOfValidators {
|
||||
result := allOfSchema.Validate(data)
|
||||
if s.Options.recycleValidators {
|
||||
s.allOfValidators[i] = nil
|
||||
}
|
||||
// We keep inner IMPORTANT! errors no matter what MatchCount tells us
|
||||
keepResultAllOf.Merge(result.keepRelevantErrors())
|
||||
if result.IsValid() {
|
||||
validated++
|
||||
}
|
||||
mainResult.Merge(result)
|
||||
}
|
||||
|
||||
switch validated {
|
||||
case 0:
|
||||
mainResult.AddErrors(mustValidateAllSchemasMsg(s.Path, ". None validated"))
|
||||
case len(s.allOfValidators):
|
||||
default:
|
||||
mainResult.AddErrors(mustValidateAllSchemasMsg(s.Path, ""))
|
||||
}
|
||||
}
|
||||
|
||||
func (s *schemaPropsValidator) validateNot(data interface{}, mainResult *Result) {
|
||||
result := s.notValidator.Validate(data)
|
||||
if s.Options.recycleValidators {
|
||||
s.notValidator = nil
|
||||
}
|
||||
// We keep inner IMPORTANT! errors no matter what MatchCount tells us
|
||||
if result.IsValid() {
|
||||
mainResult.AddErrors(mustNotValidatechemaMsg(s.Path))
|
||||
}
|
||||
if result.wantsRedeemOnMerge {
|
||||
pools.poolOfResults.RedeemResult(result) // this result is ditched
|
||||
}
|
||||
}
|
||||
|
||||
func (s *schemaPropsValidator) validateDependencies(data interface{}, mainResult *Result) {
|
||||
val := data.(map[string]interface{})
|
||||
for key := range val {
|
||||
dep, ok := s.Dependencies[key]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
if dep.Schema != nil {
|
||||
mainResult.Merge(
|
||||
newSchemaValidator(dep.Schema, s.Root, s.Path+"."+key, s.KnownFormats, s.Options).Validate(data),
|
||||
)
|
||||
continue
|
||||
}
|
||||
|
||||
if len(dep.Property) > 0 {
|
||||
for _, depKey := range dep.Property {
|
||||
if _, ok := val[depKey]; !ok {
|
||||
mainResult.AddErrors(hasADependencyMsg(s.Path, depKey))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *schemaPropsValidator) redeem() {
|
||||
pools.poolOfSchemaPropsValidators.RedeemValidator(s)
|
||||
}
|
||||
|
||||
func (s *schemaPropsValidator) redeemChildren() {
|
||||
for _, v := range s.anyOfValidators {
|
||||
if v == nil {
|
||||
continue
|
||||
}
|
||||
v.redeemChildren()
|
||||
v.redeem()
|
||||
}
|
||||
s.anyOfValidators = nil
|
||||
|
||||
for _, v := range s.allOfValidators {
|
||||
if v == nil {
|
||||
continue
|
||||
}
|
||||
v.redeemChildren()
|
||||
v.redeem()
|
||||
}
|
||||
s.allOfValidators = nil
|
||||
|
||||
for _, v := range s.oneOfValidators {
|
||||
if v == nil {
|
||||
continue
|
||||
}
|
||||
v.redeemChildren()
|
||||
v.redeem()
|
||||
}
|
||||
s.oneOfValidators = nil
|
||||
|
||||
if s.notValidator != nil {
|
||||
s.notValidator.redeemChildren()
|
||||
s.notValidator.redeem()
|
||||
s.notValidator = nil
|
||||
}
|
||||
}
|
||||
|
|
|
|||
57
vendor/github.com/go-openapi/validate/slice_validator.go
generated
vendored
57
vendor/github.com/go-openapi/validate/slice_validator.go
generated
vendored
|
|
@ -32,7 +32,36 @@ type schemaSliceValidator struct {
|
|||
Items *spec.SchemaOrArray
|
||||
Root interface{}
|
||||
KnownFormats strfmt.Registry
|
||||
Options SchemaValidatorOptions
|
||||
Options *SchemaValidatorOptions
|
||||
}
|
||||
|
||||
func newSliceValidator(path, in string,
|
||||
maxItems, minItems *int64, uniqueItems bool,
|
||||
additionalItems *spec.SchemaOrBool, items *spec.SchemaOrArray,
|
||||
root interface{}, formats strfmt.Registry, opts *SchemaValidatorOptions) *schemaSliceValidator {
|
||||
if opts == nil {
|
||||
opts = new(SchemaValidatorOptions)
|
||||
}
|
||||
|
||||
var v *schemaSliceValidator
|
||||
if opts.recycleValidators {
|
||||
v = pools.poolOfSliceValidators.BorrowValidator()
|
||||
} else {
|
||||
v = new(schemaSliceValidator)
|
||||
}
|
||||
|
||||
v.Path = path
|
||||
v.In = in
|
||||
v.MaxItems = maxItems
|
||||
v.MinItems = minItems
|
||||
v.UniqueItems = uniqueItems
|
||||
v.AdditionalItems = additionalItems
|
||||
v.Items = items
|
||||
v.Root = root
|
||||
v.KnownFormats = formats
|
||||
v.Options = opts
|
||||
|
||||
return v
|
||||
}
|
||||
|
||||
func (s *schemaSliceValidator) SetPath(path string) {
|
||||
|
|
@ -46,7 +75,18 @@ func (s *schemaSliceValidator) Applies(source interface{}, kind reflect.Kind) bo
|
|||
}
|
||||
|
||||
func (s *schemaSliceValidator) Validate(data interface{}) *Result {
|
||||
result := new(Result)
|
||||
if s.Options.recycleValidators {
|
||||
defer func() {
|
||||
s.redeem()
|
||||
}()
|
||||
}
|
||||
|
||||
var result *Result
|
||||
if s.Options.recycleResult {
|
||||
result = pools.poolOfResults.BorrowResult()
|
||||
} else {
|
||||
result = new(Result)
|
||||
}
|
||||
if data == nil {
|
||||
return result
|
||||
}
|
||||
|
|
@ -54,8 +94,8 @@ func (s *schemaSliceValidator) Validate(data interface{}) *Result {
|
|||
size := val.Len()
|
||||
|
||||
if s.Items != nil && s.Items.Schema != nil {
|
||||
validator := NewSchemaValidator(s.Items.Schema, s.Root, s.Path, s.KnownFormats, s.Options.Options()...)
|
||||
for i := 0; i < size; i++ {
|
||||
validator := newSchemaValidator(s.Items.Schema, s.Root, s.Path, s.KnownFormats, s.Options)
|
||||
validator.SetPath(fmt.Sprintf("%s.%d", s.Path, i))
|
||||
value := val.Index(i)
|
||||
result.mergeForSlice(val, i, validator.Validate(value.Interface()))
|
||||
|
|
@ -66,10 +106,11 @@ func (s *schemaSliceValidator) Validate(data interface{}) *Result {
|
|||
if s.Items != nil && len(s.Items.Schemas) > 0 {
|
||||
itemsSize = len(s.Items.Schemas)
|
||||
for i := 0; i < itemsSize; i++ {
|
||||
validator := NewSchemaValidator(&s.Items.Schemas[i], s.Root, fmt.Sprintf("%s.%d", s.Path, i), s.KnownFormats, s.Options.Options()...)
|
||||
if val.Len() <= i {
|
||||
if size <= i {
|
||||
break
|
||||
}
|
||||
|
||||
validator := newSchemaValidator(&s.Items.Schemas[i], s.Root, fmt.Sprintf("%s.%d", s.Path, i), s.KnownFormats, s.Options)
|
||||
result.mergeForSlice(val, i, validator.Validate(val.Index(i).Interface()))
|
||||
}
|
||||
}
|
||||
|
|
@ -79,7 +120,7 @@ func (s *schemaSliceValidator) Validate(data interface{}) *Result {
|
|||
}
|
||||
if s.AdditionalItems.Schema != nil {
|
||||
for i := itemsSize; i < size-itemsSize+1; i++ {
|
||||
validator := NewSchemaValidator(s.AdditionalItems.Schema, s.Root, fmt.Sprintf("%s.%d", s.Path, i), s.KnownFormats, s.Options.Options()...)
|
||||
validator := newSchemaValidator(s.AdditionalItems.Schema, s.Root, fmt.Sprintf("%s.%d", s.Path, i), s.KnownFormats, s.Options)
|
||||
result.mergeForSlice(val, i, validator.Validate(val.Index(i).Interface()))
|
||||
}
|
||||
}
|
||||
|
|
@ -103,3 +144,7 @@ func (s *schemaSliceValidator) Validate(data interface{}) *Result {
|
|||
result.Inc()
|
||||
return result
|
||||
}
|
||||
|
||||
func (s *schemaSliceValidator) redeem() {
|
||||
pools.poolOfSliceValidators.RedeemValidator(s)
|
||||
}
|
||||
|
|
|
|||
130
vendor/github.com/go-openapi/validate/spec.go
generated
vendored
130
vendor/github.com/go-openapi/validate/spec.go
generated
vendored
|
|
@ -15,6 +15,8 @@
|
|||
package validate
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/gob"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"sort"
|
||||
|
|
@ -26,6 +28,7 @@ import (
|
|||
"github.com/go-openapi/loads"
|
||||
"github.com/go-openapi/spec"
|
||||
"github.com/go-openapi/strfmt"
|
||||
"github.com/go-openapi/swag"
|
||||
)
|
||||
|
||||
// Spec validates an OpenAPI 2.0 specification document.
|
||||
|
|
@ -52,25 +55,38 @@ func Spec(doc *loads.Document, formats strfmt.Registry) error {
|
|||
|
||||
// SpecValidator validates a swagger 2.0 spec
|
||||
type SpecValidator struct {
|
||||
schema *spec.Schema // swagger 2.0 schema
|
||||
spec *loads.Document
|
||||
analyzer *analysis.Spec
|
||||
expanded *loads.Document
|
||||
KnownFormats strfmt.Registry
|
||||
Options Opts // validation options
|
||||
schema *spec.Schema // swagger 2.0 schema
|
||||
spec *loads.Document
|
||||
analyzer *analysis.Spec
|
||||
expanded *loads.Document
|
||||
KnownFormats strfmt.Registry
|
||||
Options Opts // validation options
|
||||
schemaOptions *SchemaValidatorOptions
|
||||
}
|
||||
|
||||
// NewSpecValidator creates a new swagger spec validator instance
|
||||
func NewSpecValidator(schema *spec.Schema, formats strfmt.Registry) *SpecValidator {
|
||||
// schema options that apply to all called validators
|
||||
schemaOptions := new(SchemaValidatorOptions)
|
||||
for _, o := range []Option{
|
||||
SwaggerSchema(true),
|
||||
WithRecycleValidators(true),
|
||||
// withRecycleResults(true),
|
||||
} {
|
||||
o(schemaOptions)
|
||||
}
|
||||
|
||||
return &SpecValidator{
|
||||
schema: schema,
|
||||
KnownFormats: formats,
|
||||
Options: defaultOpts,
|
||||
schema: schema,
|
||||
KnownFormats: formats,
|
||||
Options: defaultOpts,
|
||||
schemaOptions: schemaOptions,
|
||||
}
|
||||
}
|
||||
|
||||
// Validate validates the swagger spec
|
||||
func (s *SpecValidator) Validate(data interface{}) (*Result, *Result) {
|
||||
s.schemaOptions.skipSchemataResult = s.Options.SkipSchemataResult
|
||||
var sd *loads.Document
|
||||
errs, warnings := new(Result), new(Result)
|
||||
|
||||
|
|
@ -84,11 +100,8 @@ func (s *SpecValidator) Validate(data interface{}) (*Result, *Result) {
|
|||
s.spec = sd
|
||||
s.analyzer = analysis.New(sd.Spec())
|
||||
|
||||
// Swagger schema validator
|
||||
schv := NewSchemaValidator(s.schema, nil, "", s.KnownFormats, SwaggerSchema(true))
|
||||
var obj interface{}
|
||||
|
||||
// Raw spec unmarshalling errors
|
||||
var obj interface{}
|
||||
if err := json.Unmarshal(sd.Raw(), &obj); err != nil {
|
||||
// NOTE: under normal conditions, the *load.Document has been already unmarshalled
|
||||
// So this one is just a paranoid check on the behavior of the spec package
|
||||
|
|
@ -102,6 +115,8 @@ func (s *SpecValidator) Validate(data interface{}) (*Result, *Result) {
|
|||
warnings.AddErrors(errs.Warnings...)
|
||||
}()
|
||||
|
||||
// Swagger schema validator
|
||||
schv := newSchemaValidator(s.schema, nil, "", s.KnownFormats, s.schemaOptions)
|
||||
errs.Merge(schv.Validate(obj)) // error -
|
||||
// There may be a point in continuing to try and determine more accurate errors
|
||||
if !s.Options.ContinueOnErrors && errs.HasErrors() {
|
||||
|
|
@ -129,13 +144,13 @@ func (s *SpecValidator) Validate(data interface{}) (*Result, *Result) {
|
|||
}
|
||||
|
||||
// Values provided as default MUST validate their schema
|
||||
df := &defaultValidator{SpecValidator: s}
|
||||
df := &defaultValidator{SpecValidator: s, schemaOptions: s.schemaOptions}
|
||||
errs.Merge(df.Validate())
|
||||
|
||||
// Values provided as examples MUST validate their schema
|
||||
// Value provided as examples in a response without schema generate a warning
|
||||
// Known limitations: examples in responses for mime type not application/json are ignored (warning)
|
||||
ex := &exampleValidator{SpecValidator: s}
|
||||
ex := &exampleValidator{SpecValidator: s, schemaOptions: s.schemaOptions}
|
||||
errs.Merge(ex.Validate())
|
||||
|
||||
errs.Merge(s.validateNonEmptyPathParamNames())
|
||||
|
|
@ -147,22 +162,27 @@ func (s *SpecValidator) Validate(data interface{}) (*Result, *Result) {
|
|||
}
|
||||
|
||||
func (s *SpecValidator) validateNonEmptyPathParamNames() *Result {
|
||||
res := new(Result)
|
||||
res := pools.poolOfResults.BorrowResult()
|
||||
if s.spec.Spec().Paths == nil {
|
||||
// There is no Paths object: error
|
||||
res.AddErrors(noValidPathMsg())
|
||||
} else {
|
||||
if s.spec.Spec().Paths.Paths == nil {
|
||||
// Paths may be empty: warning
|
||||
res.AddWarnings(noValidPathMsg())
|
||||
} else {
|
||||
for k := range s.spec.Spec().Paths.Paths {
|
||||
if strings.Contains(k, "{}") {
|
||||
res.AddErrors(emptyPathParameterMsg(k))
|
||||
}
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
if s.spec.Spec().Paths.Paths == nil {
|
||||
// Paths may be empty: warning
|
||||
res.AddWarnings(noValidPathMsg())
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
for k := range s.spec.Spec().Paths.Paths {
|
||||
if strings.Contains(k, "{}") {
|
||||
res.AddErrors(emptyPathParameterMsg(k))
|
||||
}
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
|
|
@ -176,7 +196,7 @@ func (s *SpecValidator) validateDuplicateOperationIDs() *Result {
|
|||
// fallback on possible incomplete picture because of previous errors
|
||||
analyzer = s.analyzer
|
||||
}
|
||||
res := new(Result)
|
||||
res := pools.poolOfResults.BorrowResult()
|
||||
known := make(map[string]int)
|
||||
for _, v := range analyzer.OperationIDs() {
|
||||
if v != "" {
|
||||
|
|
@ -198,7 +218,7 @@ type dupProp struct {
|
|||
|
||||
func (s *SpecValidator) validateDuplicatePropertyNames() *Result {
|
||||
// definition can't declare a property that's already defined by one of its ancestors
|
||||
res := new(Result)
|
||||
res := pools.poolOfResults.BorrowResult()
|
||||
for k, sch := range s.spec.Spec().Definitions {
|
||||
if len(sch.AllOf) == 0 {
|
||||
continue
|
||||
|
|
@ -247,7 +267,7 @@ func (s *SpecValidator) validateSchemaPropertyNames(nm string, sch spec.Schema,
|
|||
|
||||
schn := nm
|
||||
schc := &sch
|
||||
res := new(Result)
|
||||
res := pools.poolOfResults.BorrowResult()
|
||||
|
||||
for schc.Ref.String() != "" {
|
||||
// gather property names
|
||||
|
|
@ -284,7 +304,7 @@ func (s *SpecValidator) validateSchemaPropertyNames(nm string, sch spec.Schema,
|
|||
}
|
||||
|
||||
func (s *SpecValidator) validateCircularAncestry(nm string, sch spec.Schema, knowns map[string]struct{}) ([]string, *Result) {
|
||||
res := new(Result)
|
||||
res := pools.poolOfResults.BorrowResult()
|
||||
|
||||
if sch.Ref.String() == "" && len(sch.AllOf) == 0 { // Safeguard. We should not be able to actually get there
|
||||
return nil, res
|
||||
|
|
@ -334,7 +354,7 @@ func (s *SpecValidator) validateCircularAncestry(nm string, sch spec.Schema, kno
|
|||
|
||||
func (s *SpecValidator) validateItems() *Result {
|
||||
// validate parameter, items, schema and response objects for presence of item if type is array
|
||||
res := new(Result)
|
||||
res := pools.poolOfResults.BorrowResult()
|
||||
|
||||
for method, pi := range s.analyzer.Operations() {
|
||||
for path, op := range pi {
|
||||
|
|
@ -393,7 +413,7 @@ func (s *SpecValidator) validateItems() *Result {
|
|||
|
||||
// Verifies constraints on array type
|
||||
func (s *SpecValidator) validateSchemaItems(schema spec.Schema, prefix, opID string) *Result {
|
||||
res := new(Result)
|
||||
res := pools.poolOfResults.BorrowResult()
|
||||
if !schema.Type.Contains(arrayType) {
|
||||
return res
|
||||
}
|
||||
|
|
@ -417,7 +437,7 @@ func (s *SpecValidator) validateSchemaItems(schema spec.Schema, prefix, opID str
|
|||
func (s *SpecValidator) validatePathParamPresence(path string, fromPath, fromOperation []string) *Result {
|
||||
// Each defined operation path parameters must correspond to a named element in the API's path pattern.
|
||||
// (For example, you cannot have a path parameter named id for the following path /pets/{petId} but you must have a path parameter named petId.)
|
||||
res := new(Result)
|
||||
res := pools.poolOfResults.BorrowResult()
|
||||
for _, l := range fromPath {
|
||||
var matched bool
|
||||
for _, r := range fromOperation {
|
||||
|
|
@ -473,7 +493,7 @@ func (s *SpecValidator) validateReferencedParameters() *Result {
|
|||
if len(expected) == 0 {
|
||||
return nil
|
||||
}
|
||||
result := new(Result)
|
||||
result := pools.poolOfResults.BorrowResult()
|
||||
for k := range expected {
|
||||
result.AddWarnings(unusedParamMsg(k))
|
||||
}
|
||||
|
|
@ -498,7 +518,7 @@ func (s *SpecValidator) validateReferencedResponses() *Result {
|
|||
if len(expected) == 0 {
|
||||
return nil
|
||||
}
|
||||
result := new(Result)
|
||||
result := pools.poolOfResults.BorrowResult()
|
||||
for k := range expected {
|
||||
result.AddWarnings(unusedResponseMsg(k))
|
||||
}
|
||||
|
|
@ -533,7 +553,7 @@ func (s *SpecValidator) validateReferencedDefinitions() *Result {
|
|||
|
||||
func (s *SpecValidator) validateRequiredDefinitions() *Result {
|
||||
// Each property listed in the required array must be defined in the properties of the model
|
||||
res := new(Result)
|
||||
res := pools.poolOfResults.BorrowResult()
|
||||
|
||||
DEFINITIONS:
|
||||
for d, schema := range s.spec.Spec().Definitions {
|
||||
|
|
@ -552,7 +572,7 @@ DEFINITIONS:
|
|||
|
||||
func (s *SpecValidator) validateRequiredProperties(path, in string, v *spec.Schema) *Result {
|
||||
// Takes care of recursive property definitions, which may be nested in additionalProperties schemas
|
||||
res := new(Result)
|
||||
res := pools.poolOfResults.BorrowResult()
|
||||
propertyMatch := false
|
||||
patternMatch := false
|
||||
additionalPropertiesMatch := false
|
||||
|
|
@ -618,7 +638,7 @@ func (s *SpecValidator) validateParameters() *Result {
|
|||
// - parameters with pattern property must specify valid patterns
|
||||
// - $ref in parameters must resolve
|
||||
// - path param must be required
|
||||
res := new(Result)
|
||||
res := pools.poolOfResults.BorrowResult()
|
||||
rexGarbledPathSegment := mustCompileRegexp(`.*[{}\s]+.*`)
|
||||
for method, pi := range s.expandedAnalyzer().Operations() {
|
||||
methodPaths := make(map[string]map[string]string)
|
||||
|
|
@ -657,7 +677,23 @@ func (s *SpecValidator) validateParameters() *Result {
|
|||
// TODO: should be done after param expansion
|
||||
res.Merge(s.checkUniqueParams(path, method, op))
|
||||
|
||||
// pick the root schema from the swagger specification which describes a parameter
|
||||
origSchema, ok := s.schema.Definitions["parameter"]
|
||||
if !ok {
|
||||
panic("unexpected swagger schema: missing #/definitions/parameter")
|
||||
}
|
||||
// clone it once to avoid expanding a global schema (e.g. swagger spec)
|
||||
paramSchema, err := deepCloneSchema(origSchema)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("can't clone schema: %v", err))
|
||||
}
|
||||
|
||||
for _, pr := range paramHelp.safeExpandedParamsFor(path, method, op.ID, res, s) {
|
||||
// An expanded parameter must validate the Parameter schema (an unexpanded $ref always passes high-level schema validation)
|
||||
schv := newSchemaValidator(¶mSchema, s.schema, fmt.Sprintf("%s.%s.parameters.%s", path, method, pr.Name), s.KnownFormats, s.schemaOptions)
|
||||
obj := swag.ToDynamicJSON(pr)
|
||||
res.Merge(schv.Validate(obj))
|
||||
|
||||
// Validate pattern regexp for parameters with a Pattern property
|
||||
if _, err := compileRegexp(pr.Pattern); err != nil {
|
||||
res.AddErrors(invalidPatternInParamMsg(op.ID, pr.Name, pr.Pattern))
|
||||
|
|
@ -739,7 +775,7 @@ func (s *SpecValidator) validateParameters() *Result {
|
|||
|
||||
func (s *SpecValidator) validateReferencesValid() *Result {
|
||||
// each reference must point to a valid object
|
||||
res := new(Result)
|
||||
res := pools.poolOfResults.BorrowResult()
|
||||
for _, r := range s.analyzer.AllRefs() {
|
||||
if !r.IsValidURI(s.spec.SpecFilePath()) { // Safeguard - spec should always yield a valid URI
|
||||
res.AddErrors(invalidRefMsg(r.String()))
|
||||
|
|
@ -765,7 +801,7 @@ func (s *SpecValidator) checkUniqueParams(path, method string, op *spec.Operatio
|
|||
// However, there are some issues with such a factorization:
|
||||
// - analysis does not seem to fully expand params
|
||||
// - param keys may be altered by x-go-name
|
||||
res := new(Result)
|
||||
res := pools.poolOfResults.BorrowResult()
|
||||
pnames := make(map[string]struct{})
|
||||
|
||||
if op.Parameters != nil { // Safeguard
|
||||
|
|
@ -800,3 +836,17 @@ func (s *SpecValidator) expandedAnalyzer() *analysis.Spec {
|
|||
}
|
||||
return s.analyzer
|
||||
}
|
||||
|
||||
func deepCloneSchema(src spec.Schema) (spec.Schema, error) {
|
||||
var b bytes.Buffer
|
||||
if err := gob.NewEncoder(&b).Encode(src); err != nil {
|
||||
return spec.Schema{}, err
|
||||
}
|
||||
|
||||
var dst spec.Schema
|
||||
if err := gob.NewDecoder(&b).Decode(&dst); err != nil {
|
||||
return spec.Schema{}, err
|
||||
}
|
||||
|
||||
return dst, nil
|
||||
}
|
||||
|
|
|
|||
5
vendor/github.com/go-openapi/validate/spec_messages.go
generated
vendored
5
vendor/github.com/go-openapi/validate/spec_messages.go
generated
vendored
|
|
@ -187,6 +187,8 @@ const (
|
|||
|
||||
// UnusedResponseWarning ...
|
||||
UnusedResponseWarning = "response %q is not used anywhere"
|
||||
|
||||
InvalidObject = "expected an object in %q.%s"
|
||||
)
|
||||
|
||||
// Additional error codes
|
||||
|
|
@ -347,6 +349,9 @@ func invalidParameterDefinitionAsSchemaMsg(path, method, operationID string) err
|
|||
func parameterValidationTypeMismatchMsg(param, path, typ string) errors.Error {
|
||||
return errors.New(errors.CompositeErrorCode, ParamValidationTypeMismatch, param, path, typ)
|
||||
}
|
||||
func invalidObjectMsg(path, in string) errors.Error {
|
||||
return errors.New(errors.CompositeErrorCode, InvalidObject, path, in)
|
||||
}
|
||||
|
||||
// disabled
|
||||
//
|
||||
|
|
|
|||
70
vendor/github.com/go-openapi/validate/type.go
generated
vendored
70
vendor/github.com/go-openapi/validate/type.go
generated
vendored
|
|
@ -25,11 +25,34 @@ import (
|
|||
)
|
||||
|
||||
type typeValidator struct {
|
||||
Path string
|
||||
In string
|
||||
Type spec.StringOrArray
|
||||
Nullable bool
|
||||
Format string
|
||||
In string
|
||||
Path string
|
||||
Options *SchemaValidatorOptions
|
||||
}
|
||||
|
||||
func newTypeValidator(path, in string, typ spec.StringOrArray, nullable bool, format string, opts *SchemaValidatorOptions) *typeValidator {
|
||||
if opts == nil {
|
||||
opts = new(SchemaValidatorOptions)
|
||||
}
|
||||
|
||||
var t *typeValidator
|
||||
if opts.recycleValidators {
|
||||
t = pools.poolOfTypeValidators.BorrowValidator()
|
||||
} else {
|
||||
t = new(typeValidator)
|
||||
}
|
||||
|
||||
t.Path = path
|
||||
t.In = in
|
||||
t.Type = typ
|
||||
t.Nullable = nullable
|
||||
t.Format = format
|
||||
t.Options = opts
|
||||
|
||||
return t
|
||||
}
|
||||
|
||||
func (t *typeValidator) schemaInfoForType(data interface{}) (string, string) {
|
||||
|
|
@ -125,23 +148,33 @@ func (t *typeValidator) SetPath(path string) {
|
|||
t.Path = path
|
||||
}
|
||||
|
||||
func (t *typeValidator) Applies(source interface{}, kind reflect.Kind) bool {
|
||||
func (t *typeValidator) Applies(source interface{}, _ reflect.Kind) bool {
|
||||
// typeValidator applies to Schema, Parameter and Header objects
|
||||
stpe := reflect.TypeOf(source)
|
||||
r := (len(t.Type) > 0 || t.Format != "") && (stpe == specSchemaType || stpe == specParameterType || stpe == specHeaderType)
|
||||
debugLog("type validator for %q applies %t for %T (kind: %v)\n", t.Path, r, source, kind)
|
||||
return r
|
||||
switch source.(type) {
|
||||
case *spec.Schema:
|
||||
case *spec.Parameter:
|
||||
case *spec.Header:
|
||||
default:
|
||||
return false
|
||||
}
|
||||
|
||||
return (len(t.Type) > 0 || t.Format != "")
|
||||
}
|
||||
|
||||
func (t *typeValidator) Validate(data interface{}) *Result {
|
||||
result := new(Result)
|
||||
result.Inc()
|
||||
if t.Options.recycleValidators {
|
||||
defer func() {
|
||||
t.redeem()
|
||||
}()
|
||||
}
|
||||
|
||||
if data == nil {
|
||||
// nil or zero value for the passed structure require Type: null
|
||||
if len(t.Type) > 0 && !t.Type.Contains(nullType) && !t.Nullable { // TODO: if a property is not required it also passes this
|
||||
return errorHelp.sErr(errors.InvalidType(t.Path, t.In, strings.Join(t.Type, ","), nullType))
|
||||
return errorHelp.sErr(errors.InvalidType(t.Path, t.In, strings.Join(t.Type, ","), nullType), t.Options.recycleResult)
|
||||
}
|
||||
return result
|
||||
|
||||
return emptyResult
|
||||
}
|
||||
|
||||
// check if the type matches, should be used in every validator chain as first item
|
||||
|
|
@ -151,8 +184,6 @@ func (t *typeValidator) Validate(data interface{}) *Result {
|
|||
// infer schema type (JSON) and format from passed data type
|
||||
schType, format := t.schemaInfoForType(data)
|
||||
|
||||
debugLog("path: %s, schType: %s, format: %s, expType: %s, expFmt: %s, kind: %s", t.Path, schType, format, t.Type, t.Format, val.Kind().String())
|
||||
|
||||
// check numerical types
|
||||
// TODO: check unsigned ints
|
||||
// TODO: check json.Number (see schema.go)
|
||||
|
|
@ -163,15 +194,20 @@ func (t *typeValidator) Validate(data interface{}) *Result {
|
|||
|
||||
if kind != reflect.String && kind != reflect.Slice && t.Format != "" && !(t.Type.Contains(schType) || format == t.Format || isFloatInt || isIntFloat || isLowerInt || isLowerFloat) {
|
||||
// TODO: test case
|
||||
return errorHelp.sErr(errors.InvalidType(t.Path, t.In, t.Format, format))
|
||||
return errorHelp.sErr(errors.InvalidType(t.Path, t.In, t.Format, format), t.Options.recycleResult)
|
||||
}
|
||||
|
||||
if !(t.Type.Contains(numberType) || t.Type.Contains(integerType)) && t.Format != "" && (kind == reflect.String || kind == reflect.Slice) {
|
||||
return result
|
||||
return emptyResult
|
||||
}
|
||||
|
||||
if !(t.Type.Contains(schType) || isFloatInt || isIntFloat) {
|
||||
return errorHelp.sErr(errors.InvalidType(t.Path, t.In, strings.Join(t.Type, ","), schType))
|
||||
return errorHelp.sErr(errors.InvalidType(t.Path, t.In, strings.Join(t.Type, ","), schType), t.Options.recycleResult)
|
||||
}
|
||||
return result
|
||||
|
||||
return emptyResult
|
||||
}
|
||||
|
||||
func (t *typeValidator) redeem() {
|
||||
pools.poolOfTypeValidators.RedeemValidator(t)
|
||||
}
|
||||
|
|
|
|||
936
vendor/github.com/go-openapi/validate/validator.go
generated
vendored
936
vendor/github.com/go-openapi/validate/validator.go
generated
vendored
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue