hack/pin-dependency.sh github.com/go-openapi/validate v0.19.5

This commit is contained in:
Dr. Stefan Schimanski
2019-11-15 13:48:59 +01:00
parent 323639cbba
commit ef88c43c02
188 changed files with 17483 additions and 9558 deletions

View File

@@ -4,17 +4,25 @@ linters-settings:
golint:
min-confidence: 0
gocyclo:
min-complexity: 25
min-complexity: 50
maligned:
suggest-new: true
dupl:
threshold: 100
goconst:
min-len: 2
min-occurrences: 2
min-occurrences: 3
linters:
enable-all: true
disable:
- maligned
- lll
- godox
- gocognit
- whitespace
- wsl
- funlen
- gochecknoglobals
- gochecknoinits
- scopelint

View File

@@ -33,14 +33,8 @@ func (d *defaultValidator) resetVisited() {
d.visitedSchemas = map[string]bool{}
}
// beingVisited asserts a schema is being visited
func (d *defaultValidator) beingVisited(path string) {
d.visitedSchemas[path] = true
}
// isVisited tells if a path has already been visited
func (d *defaultValidator) isVisited(path string) bool {
found := d.visitedSchemas[path]
func isVisited(path string, visitedSchemas map[string]bool) bool {
found := visitedSchemas[path]
if !found {
// search for overlapping paths
frags := strings.Split(path, ".")
@@ -70,6 +64,16 @@ func (d *defaultValidator) isVisited(path string) bool {
return found
}
// beingVisited asserts a schema is being visited
func (d *defaultValidator) beingVisited(path string) {
d.visitedSchemas[path] = true
}
// isVisited tells if a path has already been visited
func (d *defaultValidator) isVisited(path string) bool {
return isVisited(path, d.visitedSchemas)
}
// Validate validates the default values declared in the swagger spec
func (d *defaultValidator) Validate() (errs *Result) {
errs = new(Result)
@@ -89,64 +93,60 @@ func (d *defaultValidator) validateDefaultValueValidAgainstSchema() *Result {
s := d.SpecValidator
for method, pathItem := range s.analyzer.Operations() {
if pathItem != nil { // Safeguard
for path, op := range pathItem {
// parameters
for _, param := range paramHelp.safeExpandedParamsFor(path, method, op.ID, res, s) {
if param.Default != nil && param.Required {
res.AddWarnings(requiredHasDefaultMsg(param.Name, param.In))
}
for path, op := range pathItem {
// parameters
for _, param := range paramHelp.safeExpandedParamsFor(path, method, op.ID, res, s) {
if param.Default != nil && param.Required {
res.AddWarnings(requiredHasDefaultMsg(param.Name, param.In))
}
// reset explored schemas to get depth-first recursive-proof exploration
d.resetVisited()
// reset explored schemas to get depth-first recursive-proof exploration
d.resetVisited()
// Check simple parameters first
// 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(&param, s.KnownFormats).Validate(param.Default)
if red.HasErrorsOrWarnings() {
res.AddErrors(defaultValueDoesNotValidateMsg(param.Name, param.In))
res.Merge(red)
}
}
// Recursively follows Items and Schemas
if param.Items != nil {
red := d.validateDefaultValueItemsAgainstSchema(param.Name, param.In, &param, param.Items)
if red.HasErrorsOrWarnings() {
res.AddErrors(defaultValueItemsDoesNotValidateMsg(param.Name, param.In))
res.Merge(red)
}
}
if param.Schema != nil {
// Validate default value against schema
red := d.validateDefaultValueSchemaAgainstSchema(param.Name, param.In, param.Schema)
if red.HasErrorsOrWarnings() {
res.AddErrors(defaultValueDoesNotValidateMsg(param.Name, param.In))
res.Merge(red)
}
// Check simple parameters first
// 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(&param, s.KnownFormats).Validate(param.Default)
if red.HasErrorsOrWarnings() {
res.AddErrors(defaultValueDoesNotValidateMsg(param.Name, param.In))
res.Merge(red)
}
}
if op.Responses != nil {
if op.Responses.Default != nil {
// Same constraint on default Response
res.Merge(d.validateDefaultInResponse(op.Responses.Default, "default", path, 0, op.ID))
}
// Same constraint on regular Responses
if op.Responses.StatusCodeResponses != nil { // Safeguard
for code, r := range op.Responses.StatusCodeResponses {
res.Merge(d.validateDefaultInResponse(&r, "response", path, code, op.ID))
}
}
} else {
// Empty op.ID means there is no meaningful operation: no need to report a specific message
if op.ID != "" {
res.AddErrors(noValidResponseMsg(op.ID))
// Recursively follows Items and Schemas
if param.Items != nil {
red := d.validateDefaultValueItemsAgainstSchema(param.Name, param.In, &param, param.Items)
if red.HasErrorsOrWarnings() {
res.AddErrors(defaultValueItemsDoesNotValidateMsg(param.Name, param.In))
res.Merge(red)
}
}
if param.Schema != nil {
// Validate default value against schema
red := d.validateDefaultValueSchemaAgainstSchema(param.Name, param.In, param.Schema)
if red.HasErrorsOrWarnings() {
res.AddErrors(defaultValueDoesNotValidateMsg(param.Name, param.In))
res.Merge(red)
}
}
}
if op.Responses != nil {
if op.Responses.Default != nil {
// Same constraint on default Response
res.Merge(d.validateDefaultInResponse(op.Responses.Default, jsonDefault, path, 0, op.ID))
}
// Same constraint on regular Responses
if op.Responses.StatusCodeResponses != nil { // Safeguard
for code, r := range op.Responses.StatusCodeResponses {
res.Merge(d.validateDefaultInResponse(&r, "response", path, code, op.ID))
}
}
} else if op.ID != "" {
// Empty op.ID means there is no meaningful operation: no need to report a specific message
res.AddErrors(noValidResponseMsg(op.ID))
}
}
}
@@ -170,6 +170,7 @@ func (d *defaultValidator) validateDefaultInResponse(resp *spec.Response, respon
responseName, responseCodeAsStr := responseHelp.responseMsgVariants(responseType, responseCode)
// nolint: dupl
if response.Headers != nil { // Safeguard
for nm, h := range response.Headers {
// reset explored schemas to get depth-first recursive-proof exploration
@@ -223,7 +224,7 @@ func (d *defaultValidator) validateDefaultValueSchemaAgainstSchema(path, in stri
s := d.SpecValidator
if schema.Default != nil {
res.Merge(NewSchemaValidator(schema, s.spec.Spec(), path+".default", s.KnownFormats).Validate(schema.Default))
res.Merge(NewSchemaValidator(schema, s.spec.Spec(), path+".default", s.KnownFormats, SwaggerSchema(true)).Validate(schema.Default))
}
if schema.Items != nil {
if schema.Items.Schema != nil {
@@ -260,6 +261,8 @@ func (d *defaultValidator) validateDefaultValueSchemaAgainstSchema(path, in stri
return res
}
// TODO: Temporary duplicated code. Need to refactor with examples
// nolint: dupl
func (d *defaultValidator) validateDefaultValueItemsAgainstSchema(path, in string, root interface{}, items *spec.Items) *Result {
res := new(Result)
s := d.SpecValidator

View File

@@ -16,7 +16,6 @@ package validate
import (
"fmt"
"strings"
"github.com/go-openapi/spec"
)
@@ -39,34 +38,7 @@ func (ex *exampleValidator) beingVisited(path string) {
// isVisited tells if a path has already been visited
func (ex *exampleValidator) isVisited(path string) bool {
found := ex.visitedSchemas[path]
if !found {
// search for overlapping paths
frags := strings.Split(path, ".")
if len(frags) < 2 {
// shortcut exit on smaller paths
return found
}
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
}
}
}
return found
return isVisited(path, ex.visitedSchemas)
}
// Validate validates the example values declared in the swagger spec
@@ -97,64 +69,60 @@ func (ex *exampleValidator) validateExampleValueValidAgainstSchema() *Result {
s := ex.SpecValidator
for method, pathItem := range s.analyzer.Operations() {
if pathItem != nil { // Safeguard
for path, op := range pathItem {
// parameters
for _, param := range paramHelp.safeExpandedParamsFor(path, method, op.ID, res, s) {
for path, op := range pathItem {
// parameters
for _, param := range paramHelp.safeExpandedParamsFor(path, method, op.ID, res, s) {
// As of swagger 2.0, Examples are not supported in simple parameters
// However, it looks like it is supported by go-openapi
// As of swagger 2.0, Examples are not supported in simple parameters
// However, it looks like it is supported by go-openapi
// reset explored schemas to get depth-first recursive-proof exploration
ex.resetVisited()
// reset explored schemas to get depth-first recursive-proof exploration
ex.resetVisited()
// Check simple parameters first
// 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(&param, s.KnownFormats).Validate(param.Example)
if red.HasErrorsOrWarnings() {
res.AddWarnings(exampleValueDoesNotValidateMsg(param.Name, param.In))
res.MergeAsWarnings(red)
}
}
// Recursively follows Items and Schemas
if param.Items != nil {
red := ex.validateExampleValueItemsAgainstSchema(param.Name, param.In, &param, param.Items)
if red.HasErrorsOrWarnings() {
res.AddWarnings(exampleValueItemsDoesNotValidateMsg(param.Name, param.In))
res.Merge(red)
}
}
if param.Schema != nil {
// Validate example value against schema
red := ex.validateExampleValueSchemaAgainstSchema(param.Name, param.In, param.Schema)
if red.HasErrorsOrWarnings() {
res.AddWarnings(exampleValueDoesNotValidateMsg(param.Name, param.In))
res.Merge(red)
}
// Check simple parameters first
// 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(&param, s.KnownFormats).Validate(param.Example)
if red.HasErrorsOrWarnings() {
res.AddWarnings(exampleValueDoesNotValidateMsg(param.Name, param.In))
res.MergeAsWarnings(red)
}
}
if op.Responses != nil {
if op.Responses.Default != nil {
// Same constraint on default Response
res.Merge(ex.validateExampleInResponse(op.Responses.Default, "default", path, 0, op.ID))
}
// Same constraint on regular Responses
if op.Responses.StatusCodeResponses != nil { // Safeguard
for code, r := range op.Responses.StatusCodeResponses {
res.Merge(ex.validateExampleInResponse(&r, "response", path, code, op.ID))
}
}
} else {
// Empty op.ID means there is no meaningful operation: no need to report a specific message
if op.ID != "" {
res.AddErrors(noValidResponseMsg(op.ID))
// Recursively follows Items and Schemas
if param.Items != nil {
red := ex.validateExampleValueItemsAgainstSchema(param.Name, param.In, &param, param.Items)
if red.HasErrorsOrWarnings() {
res.AddWarnings(exampleValueItemsDoesNotValidateMsg(param.Name, param.In))
res.Merge(red)
}
}
if param.Schema != nil {
// Validate example value against schema
red := ex.validateExampleValueSchemaAgainstSchema(param.Name, param.In, param.Schema)
if red.HasErrorsOrWarnings() {
res.AddWarnings(exampleValueDoesNotValidateMsg(param.Name, param.In))
res.Merge(red)
}
}
}
if op.Responses != nil {
if op.Responses.Default != nil {
// Same constraint on default Response
res.Merge(ex.validateExampleInResponse(op.Responses.Default, jsonDefault, path, 0, op.ID))
}
// Same constraint on regular Responses
if op.Responses.StatusCodeResponses != nil { // Safeguard
for code, r := range op.Responses.StatusCodeResponses {
res.Merge(ex.validateExampleInResponse(&r, "response", path, code, op.ID))
}
}
} else if op.ID != "" {
// Empty op.ID means there is no meaningful operation: no need to report a specific message
res.AddErrors(noValidResponseMsg(op.ID))
}
}
}
@@ -178,6 +146,7 @@ func (ex *exampleValidator) validateExampleInResponse(resp *spec.Response, respo
responseName, responseCodeAsStr := responseHelp.responseMsgVariants(responseType, responseCode)
// nolint: dupl
if response.Headers != nil { // Safeguard
for nm, h := range response.Headers {
// reset explored schemas to get depth-first recursive-proof exploration
@@ -222,7 +191,7 @@ func (ex *exampleValidator) validateExampleInResponse(resp *spec.Response, respo
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, s.KnownFormats).Validate(example))
res.MergeAsWarnings(NewSchemaValidator(response.Schema, s.spec.Spec(), path+".examples", s.KnownFormats, SwaggerSchema(true)).Validate(example))
} else {
// TODO: validate other media types too
res.AddWarnings(examplesMimeNotSupportedMsg(operationID, responseName))
@@ -244,7 +213,7 @@ func (ex *exampleValidator) validateExampleValueSchemaAgainstSchema(path, in str
res := new(Result)
if schema.Example != nil {
res.MergeAsWarnings(NewSchemaValidator(schema, s.spec.Spec(), path+".example", s.KnownFormats).Validate(schema.Example))
res.MergeAsWarnings(NewSchemaValidator(schema, s.spec.Spec(), path+".example", s.KnownFormats, SwaggerSchema(true)).Validate(schema.Example))
}
if schema.Items != nil {
if schema.Items.Schema != nil {
@@ -281,6 +250,8 @@ func (ex *exampleValidator) validateExampleValueSchemaAgainstSchema(path, in str
return res
}
// TODO: Temporary duplicated code. Need to refactor with examples
// nolint: dupl
func (ex *exampleValidator) validateExampleValueItemsAgainstSchema(path, in string, root interface{}, items *spec.Items) *Result {
res := new(Result)
s := ex.SpecValidator

View File

@@ -37,19 +37,15 @@ func (f *formatValidator) Applies(source interface{}, kind reflect.Kind) bool {
if source == nil {
return false
}
switch source.(type) {
switch source := source.(type) {
case *spec.Items:
it := source.(*spec.Items)
return kind == reflect.String && f.KnownFormats.ContainsName(it.Format)
return kind == reflect.String && f.KnownFormats.ContainsName(source.Format)
case *spec.Parameter:
par := source.(*spec.Parameter)
return kind == reflect.String && f.KnownFormats.ContainsName(par.Format)
return kind == reflect.String && f.KnownFormats.ContainsName(source.Format)
case *spec.Schema:
sch := source.(*spec.Schema)
return kind == reflect.String && f.KnownFormats.ContainsName(sch.Format)
return kind == reflect.String && f.KnownFormats.ContainsName(source.Format)
case *spec.Header:
hdr := source.(*spec.Header)
return kind == reflect.String && f.KnownFormats.ContainsName(hdr.Format)
return kind == reflect.String && f.KnownFormats.ContainsName(source.Format)
}
return false
}

View File

@@ -1,14 +1,20 @@
module github.com/go-openapi/validate
require (
github.com/go-openapi/analysis v0.19.2
github.com/go-openapi/analysis v0.19.5
github.com/go-openapi/errors v0.19.2
github.com/go-openapi/jsonpointer v0.19.2
github.com/go-openapi/loads v0.19.2
github.com/go-openapi/runtime v0.19.0
github.com/go-openapi/spec v0.19.2
github.com/go-openapi/strfmt v0.19.0
github.com/go-openapi/swag v0.19.2
github.com/stretchr/testify v1.3.0
gopkg.in/yaml.v2 v2.2.2
github.com/go-openapi/jsonpointer v0.19.3
github.com/go-openapi/jsonreference v0.19.3 // indirect
github.com/go-openapi/loads v0.19.4
github.com/go-openapi/runtime v0.19.4
github.com/go-openapi/spec v0.19.3
github.com/go-openapi/strfmt v0.19.3
github.com/go-openapi/swag v0.19.5
github.com/mailru/easyjson v0.7.0 // indirect
github.com/stretchr/testify v1.4.0
github.com/vektah/gqlparser v1.1.2
go.mongodb.org/mongo-driver v1.1.2 // indirect
gopkg.in/yaml.v2 v2.2.4
)
go 1.13

View File

@@ -3,6 +3,9 @@ github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tN
github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M=
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
github.com/agnivade/levenshtein v1.0.1 h1:3oJU7J3FGFmyhn8KHjmVaZCN5hxTr7GxgRue+sxIXdQ=
github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM=
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=
github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a h1:idn718Q4B6AGu/h5Sxe66HYVdqdGu2l9Iebqhi/AEoA=
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
@@ -10,6 +13,7 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8 h1:DujepqpGd1hyOd7aW59XpK7Qymp8iy83xq74fLr21is=
github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
@@ -18,6 +22,8 @@ github.com/go-openapi/analysis v0.17.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpR
github.com/go-openapi/analysis v0.18.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik=
github.com/go-openapi/analysis v0.19.2 h1:ophLETFestFZHk3ji7niPEL4d466QjW+0Tdg5VyDq7E=
github.com/go-openapi/analysis v0.19.2/go.mod h1:3P1osvZa9jKjb8ed2TPng3f0i/UY9snX6gxi44djMjk=
github.com/go-openapi/analysis v0.19.5 h1:8b2ZgKfKIUTVQpTb77MoRDIMEIwvDVw40o3aOXdfYzI=
github.com/go-openapi/analysis v0.19.5/go.mod h1:hkEAkxagaIvIP7VTn8ygJNkd4kAYON2rCu0v0ObL0AU=
github.com/go-openapi/errors v0.17.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0=
github.com/go-openapi/errors v0.18.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0=
github.com/go-openapi/errors v0.19.2 h1:a2kIyV3w+OS3S97zxUndRVD46+FhGOUBDFY7nmu4CsY=
@@ -26,32 +32,53 @@ github.com/go-openapi/jsonpointer v0.17.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwds
github.com/go-openapi/jsonpointer v0.18.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M=
github.com/go-openapi/jsonpointer v0.19.2 h1:A9+F4Dc/MCNB5jibxf6rRvOvR/iFgQdyNx9eIhnGqq0=
github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg=
github.com/go-openapi/jsonpointer v0.19.3 h1:gihV7YNZK1iK6Tgwwsxo2rJbD1GTbdm72325Bq8FI3w=
github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
github.com/go-openapi/jsonreference v0.17.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I=
github.com/go-openapi/jsonreference v0.18.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I=
github.com/go-openapi/jsonreference v0.19.2 h1:o20suLFB4Ri0tuzpWtyHlh7E7HnkqTNLq6aR6WVNS1w=
github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc=
github.com/go-openapi/jsonreference v0.19.3 h1:5cxNfTy0UVC3X8JL5ymxzyoUZmo8iZb+jeTWn7tUa8o=
github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8=
github.com/go-openapi/loads v0.17.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU=
github.com/go-openapi/loads v0.18.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU=
github.com/go-openapi/loads v0.19.0 h1:wCOBNscACI8L93tt5tvB2zOMkJ098XCw3fP0BY2ybDA=
github.com/go-openapi/loads v0.19.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU=
github.com/go-openapi/loads v0.19.2 h1:rf5ArTHmIJxyV5Oiks+Su0mUens1+AjpkPoWr5xFRcI=
github.com/go-openapi/loads v0.19.2/go.mod h1:QAskZPMX5V0C2gvfkGZzJlINuP7Hx/4+ix5jWFxsNPs=
github.com/go-openapi/loads v0.19.3 h1:jwIoahqCmaA5OBoc/B+1+Mu2L0Gr8xYQnbeyQEo/7b0=
github.com/go-openapi/loads v0.19.3/go.mod h1:YVfqhUCdahYwR3f3iiwQLhicVRvLlU/WO5WPaZvcvSI=
github.com/go-openapi/loads v0.19.4 h1:5I4CCSqoWzT+82bBkNIvmLc0UOsoKKQ4Fz+3VxOB7SY=
github.com/go-openapi/loads v0.19.4/go.mod h1:zZVHonKd8DXyxyw4yfnVjPzBjIQcLt0CCsn0N0ZrQsk=
github.com/go-openapi/runtime v0.0.0-20180920151709-4f900dc2ade9/go.mod h1:6v9a6LTXWQCdL8k1AO3cvqx5OtZY/Y9wKTgaoP6YRfA=
github.com/go-openapi/runtime v0.19.0 h1:sU6pp4dSV2sGlNKKyHxZzi1m1kG4WnYtWcJ+HYbygjE=
github.com/go-openapi/runtime v0.19.0/go.mod h1:OwNfisksmmaZse4+gpV3Ne9AyMOlP1lt4sK4FXt0O64=
github.com/go-openapi/runtime v0.19.4 h1:csnOgcgAiuGoM/Po7PEpKDoNulCcF3FGbSnbHfxgjMI=
github.com/go-openapi/runtime v0.19.4/go.mod h1:X277bwSUBxVlCYR3r7xgZZGKVvBd/29gLDlFGtJ8NL4=
github.com/go-openapi/spec v0.17.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI=
github.com/go-openapi/spec v0.18.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI=
github.com/go-openapi/spec v0.19.2 h1:SStNd1jRcYtfKCN7R0laGNs80WYYvn5CbBjM2sOmCrE=
github.com/go-openapi/spec v0.19.2/go.mod h1:sCxk3jxKgioEJikev4fgkNmwS+3kuYdJtcsZsD5zxMY=
github.com/go-openapi/spec v0.19.3 h1:0XRyw8kguri6Yw4SxhsQA/atC88yqrk0+G4YhI2wabc=
github.com/go-openapi/spec v0.19.3/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo=
github.com/go-openapi/strfmt v0.17.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU=
github.com/go-openapi/strfmt v0.18.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU=
github.com/go-openapi/strfmt v0.19.0 h1:0Dn9qy1G9+UJfRU7TR8bmdGxb4uifB7HNrJjOnV0yPk=
github.com/go-openapi/strfmt v0.19.0/go.mod h1:+uW+93UVvGGq2qGaZxdDeJqSAqBqBdl+ZPMF/cC8nDY=
github.com/go-openapi/strfmt v0.19.3 h1:eRfyY5SkaNJCAwmmMcADjY31ow9+N7MCLW7oRkbsINA=
github.com/go-openapi/strfmt v0.19.3/go.mod h1:0yX7dbo8mKIvc3XSKp7MNfxw4JytCfCD6+bY1AVL9LU=
github.com/go-openapi/swag v0.17.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg=
github.com/go-openapi/swag v0.18.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg=
github.com/go-openapi/swag v0.19.2 h1:jvO6bCMBEilGwMfHhrd61zIID4oIFdwb76V17SM88dE=
github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
github.com/go-openapi/swag v0.19.5 h1:lTz6Ys4CmqqCQmZPBlbQENR1/GucA2bzYTE12Pw4tFY=
github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
github.com/go-openapi/validate v0.18.0/go.mod h1:Uh4HdOzKt19xGIGm1qHf/ofbX1YQ4Y+MYsct2VUrAJ4=
github.com/go-openapi/validate v0.19.2/go.mod h1:1tRCw7m3jtI8eNWEEliiAqUIcBztB2KDnRCRMUi7GTA=
github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
@@ -65,26 +92,48 @@ github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN
github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63 h1:nTT4s92Dgz2HlrB2NaMgvlfqHH39OgMhA7z3PK7PGD4=
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e h1:hB2xlXdHp/pmPZq0y3QnmWAArdw9PqbmotexnWx/FU8=
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.7.0 h1:aizVhC/NAAcKWb+5QsU1iNOZb4Yws5UO2I+aIprQITM=
github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs=
github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4=
github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
github.com/vektah/gqlparser v1.1.2 h1:ZsyLGn7/7jDNI+y4SEhI4yAxRChlv15pUHMjijT+e68=
github.com/vektah/gqlparser v1.1.2/go.mod h1:1ycwN7Ij5njmMkPPAOaRFY4rET2Enx7IkVv3vaXspKw=
go.mongodb.org/mongo-driver v1.0.3 h1:GKoji1ld3tw2aC+GX1wbr/J2fX13yNacEYoJ8Nhr0yU=
go.mongodb.org/mongo-driver v1.0.3/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM=
go.mongodb.org/mongo-driver v1.1.1 h1:Sq1fR+0c58RME5EoqKdjkiQAmPjmfHlZOoRI6fTUOcs=
go.mongodb.org/mongo-driver v1.1.1/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM=
go.mongodb.org/mongo-driver v1.1.2 h1:jxcFYjlkl8xaERsgLo+RNquI0epW6zuy/ZRQs6jnrFA=
go.mongodb.org/mongo-driver v1.1.2/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190320223903-b7391e95e576/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190617133340-57b3e21c3d56/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190320064053-1272bf9dcd53/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980 h1:dfGZHvZk057jK2MCeWus/TowKpJ8y4AmooUzdBSR9GU=
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297 h1:k7pJ2yAPLPgbskkFdhRCsA77k2fySZ1zf2zCjvQCiIM=
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20191014212845-da9a3fd4c582 h1:p9xBe/w/OzkeYVKm234g55gMdD1nSIooTir5kV11kfA=
golang.org/x/net v0.0.0-20191014212845-da9a3fd4c582/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190321052220-f7bb7a8bee54/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -94,10 +143,14 @@ golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190125232054-d66bd3c5d5a6/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190617190820-da514acc4774/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=

View File

@@ -26,8 +26,67 @@ import (
"github.com/go-openapi/spec"
)
const swaggerBody = "body"
const objectType = "object"
const (
swaggerBody = "body"
swaggerExample = "example"
swaggerExamples = "examples"
)
const (
objectType = "object"
arrayType = "array"
stringType = "string"
integerType = "integer"
numberType = "number"
booleanType = "boolean"
fileType = "file"
nullType = "null"
)
const (
jsonProperties = "properties"
jsonItems = "items"
jsonType = "type"
//jsonSchema = "schema"
jsonDefault = "default"
)
const (
stringFormatDate = "date"
stringFormatDateTime = "date-time"
stringFormatPassword = "password"
stringFormatByte = "byte"
//stringFormatBinary = "binary"
stringFormatCreditCard = "creditcard"
stringFormatDuration = "duration"
stringFormatEmail = "email"
stringFormatHexColor = "hexcolor"
stringFormatHostname = "hostname"
stringFormatIPv4 = "ipv4"
stringFormatIPv6 = "ipv6"
stringFormatISBN = "isbn"
stringFormatISBN10 = "isbn10"
stringFormatISBN13 = "isbn13"
stringFormatMAC = "mac"
stringFormatBSONObjectID = "bsonobjectid"
stringFormatRGBColor = "rgbcolor"
stringFormatSSN = "ssn"
stringFormatURI = "uri"
stringFormatUUID = "uuid"
stringFormatUUID3 = "uuid3"
stringFormatUUID4 = "uuid4"
stringFormatUUID5 = "uuid5"
integerFormatInt32 = "int32"
integerFormatInt64 = "int64"
integerFormatUInt32 = "uint32"
integerFormatUInt64 = "uint64"
numberFormatFloat32 = "float32"
numberFormatFloat64 = "float64"
numberFormatFloat = "float"
numberFormatDouble = "double"
)
// Helpers available at the package level
var (
@@ -205,7 +264,8 @@ func (h *paramHelper) checkExpandedParam(pr *spec.Parameter, path, in, operation
res := new(Result)
simpleZero := spec.SimpleSchema{}
// Try to explain why... best guess
if pr.In == swaggerBody && (pr.SimpleSchema != simpleZero && pr.SimpleSchema.Type != objectType) {
switch {
case pr.In == swaggerBody && (pr.SimpleSchema != simpleZero && pr.SimpleSchema.Type != objectType):
if isRef {
// Most likely, a $ref with a sibling is an unwanted situation: in itself this is a warning...
// but we detect it because of the following error:
@@ -213,13 +273,12 @@ func (h *paramHelper) checkExpandedParam(pr *spec.Parameter, path, in, operation
res.AddWarnings(refShouldNotHaveSiblingsMsg(path, operation))
}
res.AddErrors(invalidParameterDefinitionMsg(path, in, operation))
} else if pr.In != swaggerBody && pr.Schema != nil {
case pr.In != swaggerBody && pr.Schema != nil:
if isRef {
res.AddWarnings(refShouldNotHaveSiblingsMsg(path, operation))
}
res.AddErrors(invalidParameterDefinitionAsSchemaMsg(path, in, operation))
} else if (pr.In == swaggerBody && pr.Schema == nil) ||
(pr.In != swaggerBody && pr.SimpleSchema == simpleZero) { // Safeguard
case (pr.In == swaggerBody && pr.Schema == nil) || (pr.In != swaggerBody && pr.SimpleSchema == simpleZero):
// Other unexpected mishaps
res.AddErrors(invalidParameterDefinitionMsg(path, in, operation))
}
@@ -254,8 +313,8 @@ func (r *responseHelper) responseMsgVariants(
responseType string,
responseCode int) (responseName, responseCodeAsStr string) {
// Path variants for messages
if responseType == "default" {
responseCodeAsStr = "default"
if responseType == jsonDefault {
responseCodeAsStr = jsonDefault
responseName = "default response"
} else {
responseCodeAsStr = strconv.Itoa(responseCode)

View File

@@ -51,40 +51,54 @@ func (o *objectValidator) Applies(source interface{}, kind reflect.Kind) bool {
return r
}
func (o *objectValidator) isPropertyName() bool {
func (o *objectValidator) isProperties() bool {
p := strings.Split(o.Path, ".")
return p[len(p)-1] == "properties" && p[len(p)-2] != "properties"
return len(p) > 1 && p[len(p)-1] == jsonProperties && p[len(p)-2] != jsonProperties
}
func (o *objectValidator) isDefault() bool {
p := strings.Split(o.Path, ".")
return len(p) > 1 && p[len(p)-1] == jsonDefault && p[len(p)-2] != jsonDefault
}
func (o *objectValidator) isExample() bool {
p := strings.Split(o.Path, ".")
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{}) {
if t, typeFound := val["type"]; typeFound {
if tpe, ok := t.(string); ok && tpe == "array" {
if _, itemsKeyFound := val["items"]; !itemsKeyFound {
res.AddErrors(errors.Required("items", o.Path))
// 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 _, itemsKeyFound := val[jsonItems]; !itemsKeyFound {
res.AddErrors(errors.Required(jsonItems, o.Path))
}
}
}
}
func (o *objectValidator) checkItemsMustBeTypeArray(res *Result, val map[string]interface{}) {
if !o.isPropertyName() {
if _, itemsKeyFound := val["items"]; itemsKeyFound {
t, typeFound := val["type"]
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 != "array" {
res.AddErrors(errors.InvalidType(o.Path, o.In, "array", nil))
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("type", o.Path))
res.AddErrors(errors.Required(jsonType, o.Path))
}
}
}
}
func (o *objectValidator) precheck(res *Result, val map[string]interface{}) {
o.checkArrayMustHaveItems(res, val)
if !o.Options.DisableObjectArrayTypeCheck {
if o.Options.EnableArrayMustHaveItemsCheck {
o.checkArrayMustHaveItems(res, val)
}
if o.Options.EnableObjectArrayTypeCheck {
o.checkItemsMustBeTypeArray(res, val)
}
}
@@ -134,21 +148,18 @@ func (o *objectValidator) Validate(data interface{}) *Result {
// NOTE: prefix your messages here by "IMPORTANT!" so there are not filtered
// by higher level callers (the IMPORTANT! tag will be eventually
// removed).
switch k {
// $ref is forbidden in header
case "headers":
if val[k] != nil {
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))
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))
}
}
}

View File

@@ -132,6 +132,7 @@ func (r *Result) RootObjectSchemata() []*spec.Schema {
}
// FieldSchemata returns the schemata which apply to fields in objects.
// nolint: dupl
func (r *Result) FieldSchemata() map[FieldKey][]*spec.Schema {
if r.cachedFieldSchemta != nil {
return r.cachedFieldSchemta
@@ -151,6 +152,7 @@ func (r *Result) FieldSchemata() map[FieldKey][]*spec.Schema {
}
// ItemSchemata returns the schemata which apply to items in slices.
// nolint: dupl
func (r *Result) ItemSchemata() map[ItemKey][]*spec.Schema {
if r.cachedItemSchemata != nil {
return r.cachedItemSchemata
@@ -175,6 +177,7 @@ func (r *Result) resetCaches() {
}
// mergeForField merges other into r, assigning other's root schemata to the given Object and field name.
// nolint: unparam
func (r *Result) mergeForField(obj map[string]interface{}, field string, other *Result) *Result {
if other == nil {
return r
@@ -196,6 +199,7 @@ func (r *Result) mergeForField(obj map[string]interface{}, field string, other *
}
// mergeForSlice merges other into r, assigning other's root schemata to the given slice and index.
// nolint: unparam
func (r *Result) mergeForSlice(slice reflect.Value, i int, other *Result) *Result {
if other == nil {
return r
@@ -231,6 +235,7 @@ func (r *Result) addPropertySchemata(obj map[string]interface{}, fld string, sch
r.fieldSchemata = append(r.fieldSchemata, fieldSchemata{obj: obj, field: fld, schemata: schemata{one: schema}})
}
/*
// addSliceSchemata adds the given schemata for the slice and index.
// The slice schemata might be reused. I.e. do not modify it after being added to a result.
func (r *Result) addSliceSchemata(slice reflect.Value, i int, schema *spec.Schema) {
@@ -239,6 +244,7 @@ func (r *Result) addSliceSchemata(slice reflect.Value, i int, schema *spec.Schem
}
r.itemSchemata = append(r.itemSchemata, itemSchemata{slice: slice, index: i, schemata: schemata{one: schema}})
}
*/
// mergeWithoutRootSchemata merges other into r, ignoring the rootObject schemata.
func (r *Result) mergeWithoutRootSchemata(other *Result) {
@@ -251,9 +257,7 @@ func (r *Result) mergeWithoutRootSchemata(other *Result) {
if r.fieldSchemata == nil {
r.fieldSchemata = other.fieldSchemata
} else {
for _, x := range other.fieldSchemata {
r.fieldSchemata = append(r.fieldSchemata, x)
}
r.fieldSchemata = append(r.fieldSchemata, other.fieldSchemata...)
}
}
@@ -261,9 +265,7 @@ func (r *Result) mergeWithoutRootSchemata(other *Result) {
if r.itemSchemata == nil {
r.itemSchemata = other.itemSchemata
} else {
for _, x := range other.itemSchemata {
r.itemSchemata = append(r.itemSchemata, x)
}
r.itemSchemata = append(r.itemSchemata, other.itemSchemata...)
}
}
}

View File

@@ -27,8 +27,8 @@ import (
var (
specSchemaType = reflect.TypeOf(&spec.Schema{})
specParameterType = reflect.TypeOf(&spec.Parameter{})
specItemsType = reflect.TypeOf(&spec.Items{})
specHeaderType = reflect.TypeOf(&spec.Header{})
//specItemsType = reflect.TypeOf(&spec.Items{})
)
// SchemaValidator validates data against a JSON schema
@@ -39,14 +39,14 @@ type SchemaValidator struct {
validators []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) error {
res := NewSchemaValidator(schema, nil, "", formats).Validate(data)
func AgainstSchema(schema *spec.Schema, data interface{}, formats strfmt.Registry, options ...Option) error {
res := NewSchemaValidator(schema, nil, "", formats, options...).Validate(data)
if res.HasErrors() {
return errors.CompositeValidationError(res.Errors...)
}
@@ -72,9 +72,15 @@ 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{}}
s := SchemaValidator{
Path: root,
in: "body",
Schema: schema,
Root: rootSchema,
KnownFormats: formats,
Options: SchemaValidatorOptions{}}
for _, o := range options {
o(s.Options)
o(&s.Options)
}
s.validators = []valueValidator{
s.typeValidator(),
@@ -134,9 +140,9 @@ func (s *SchemaValidator) Validate(data interface{}) *Result {
// TODO: this part should be handed over to type validator
// Handle special case of json.Number data (number marshalled as string)
isnumber := s.Schema.Type.Contains("number") || s.Schema.Type.Contains("integer")
isnumber := s.Schema.Type.Contains(numberType) || s.Schema.Type.Contains(integerType)
if num, ok := data.(json.Number); ok && isnumber {
if s.Schema.Type.Contains("integer") { // avoid lossy conversion
if s.Schema.Type.Contains(integerType) { // avoid lossy conversion
in, erri := num.Int64()
if erri != nil {
result.AddErrors(invalidTypeConversionMsg(s.Path, erri))
@@ -196,6 +202,7 @@ func (s *SchemaValidator) sliceValidator() valueValidator {
Items: s.Schema.Items,
Root: s.Root,
KnownFormats: s.KnownFormats,
Options: s.Options,
}
}
@@ -248,6 +255,6 @@ func (s *SchemaValidator) objectValidator() valueValidator {
PatternProperties: s.Schema.PatternProperties,
Root: s.Root,
KnownFormats: s.KnownFormats,
Options: *s.Options,
Options: s.Options,
}
}

View File

@@ -14,20 +14,41 @@
package validate
// SchemaValidatorOptions defines optional rules for schema validation
type SchemaValidatorOptions struct {
DisableObjectArrayTypeCheck bool
EnableObjectArrayTypeCheck bool
EnableArrayMustHaveItemsCheck bool
}
// Option sets optional rules for schema validation
type Option func(*SchemaValidatorOptions)
func DisableObjectArrayTypeCheck(disable bool) Option {
// EnableObjectArrayTypeCheck activates the swagger rule: an items must be in type: array
func EnableObjectArrayTypeCheck(enable bool) Option {
return func(svo *SchemaValidatorOptions) {
svo.DisableObjectArrayTypeCheck = disable
svo.EnableObjectArrayTypeCheck = enable
}
}
func (svo SchemaValidatorOptions) Options() []Option {
return []Option{
DisableObjectArrayTypeCheck(svo.DisableObjectArrayTypeCheck),
// EnableArrayMustHaveItemsCheck activates the swagger rule: an array must have items defined
func EnableArrayMustHaveItemsCheck(enable bool) Option {
return func(svo *SchemaValidatorOptions) {
svo.EnableArrayMustHaveItemsCheck = enable
}
}
// SwaggerSchema activates swagger schema validation rules
func SwaggerSchema(enable bool) Option {
return func(svo *SchemaValidatorOptions) {
svo.EnableObjectArrayTypeCheck = enable
svo.EnableArrayMustHaveItemsCheck = enable
}
}
// Options returns current options
func (svo SchemaValidatorOptions) Options() []Option {
return []Option{
EnableObjectArrayTypeCheck(svo.EnableObjectArrayTypeCheck),
EnableArrayMustHaveItemsCheck(svo.EnableArrayMustHaveItemsCheck),
}
}

View File

@@ -32,6 +32,7 @@ type schemaSliceValidator struct {
Items *spec.SchemaOrArray
Root interface{}
KnownFormats strfmt.Registry
Options SchemaValidatorOptions
}
func (s *schemaSliceValidator) SetPath(path string) {
@@ -53,7 +54,7 @@ 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)
validator := NewSchemaValidator(s.Items.Schema, s.Root, s.Path, s.KnownFormats, s.Options.Options()...)
for i := 0; i < size; i++ {
validator.SetPath(fmt.Sprintf("%s.%d", s.Path, i))
value := val.Index(i)
@@ -65,11 +66,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)
validator := NewSchemaValidator(&s.Items.Schemas[i], s.Root, fmt.Sprintf("%s.%d", s.Path, i), s.KnownFormats, s.Options.Options()...)
if val.Len() <= i {
break
}
result.mergeForSlice(val, int(i), validator.Validate(val.Index(i).Interface()))
result.mergeForSlice(val, i, validator.Validate(val.Index(i).Interface()))
}
}
if s.AdditionalItems != nil && itemsSize < size {
@@ -78,8 +79,8 @@ 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)
result.mergeForSlice(val, int(i), validator.Validate(val.Index(int(i)).Interface()))
validator := NewSchemaValidator(s.AdditionalItems.Schema, s.Root, fmt.Sprintf("%s.%d", s.Path, i), s.KnownFormats, s.Options.Options()...)
result.mergeForSlice(val, i, validator.Validate(val.Index(i).Interface()))
}
}
}

View File

@@ -71,25 +71,22 @@ func NewSpecValidator(schema *spec.Schema, formats strfmt.Registry) *SpecValidat
}
// Validate validates the swagger spec
func (s *SpecValidator) Validate(data interface{}) (errs *Result, warnings *Result) {
func (s *SpecValidator) Validate(data interface{}) (*Result, *Result) {
var sd *loads.Document
errs = new(Result)
errs, warnings := new(Result), new(Result)
switch v := data.(type) {
case *loads.Document:
if v, ok := data.(*loads.Document); ok {
sd = v
}
if sd == nil {
errs.AddErrors(invalidDocumentMsg())
return
return errs, warnings // no point in continuing
}
s.spec = sd
s.analyzer = analysis.New(sd.Spec())
warnings = new(Result)
// Swagger schema validator
schv := NewSchemaValidator(s.schema, nil, "", s.KnownFormats)
schv := NewSchemaValidator(s.schema, nil, "", s.KnownFormats, SwaggerSchema(true))
var obj interface{}
// Raw spec unmarshalling errors
@@ -109,13 +106,13 @@ func (s *SpecValidator) Validate(data interface{}) (errs *Result, warnings *Resu
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() {
return // no point in continuing
return errs, warnings // no point in continuing
}
errs.Merge(s.validateReferencesValid()) // error -
// There may be a point in continuing to try and determine more accurate errors
if !s.Options.ContinueOnErrors && errs.HasErrors() {
return // no point in continuing
return errs, warnings // no point in continuing
}
errs.Merge(s.validateDuplicateOperationIDs())
@@ -129,7 +126,7 @@ func (s *SpecValidator) Validate(data interface{}) (errs *Result, warnings *Resu
// There may be a point in continuing to try and determine more accurate errors
if !s.Options.ContinueOnErrors && errs.HasErrors() {
return // no point in continuing
return errs, warnings // no point in continuing
}
// Values provided as default MUST validate their schema
@@ -147,7 +144,7 @@ func (s *SpecValidator) Validate(data interface{}) (errs *Result, warnings *Resu
//errs.Merge(s.validateRefNoSibling()) // warning only
errs.Merge(s.validateReferenced()) // warning only
return
return errs, warnings
}
func (s *SpecValidator) validateNonEmptyPathParamNames() *Result {
@@ -336,14 +333,14 @@ func (s *SpecValidator) validateItems() *Result {
for path, op := range pi {
for _, param := range paramHelp.safeExpandedParamsFor(path, method, op.ID, res, s) {
if param.TypeName() == "array" && param.ItemsTypeName() == "" {
if param.TypeName() == arrayType && param.ItemsTypeName() == "" {
res.AddErrors(arrayInParamRequiresItemsMsg(param.Name, op.ID))
continue
}
if param.In != "body" {
if param.In != swaggerBody {
if param.Items != nil {
items := param.Items
for items.TypeName() == "array" {
for items.TypeName() == arrayType {
if items.ItemsTypeName() == "" {
res.AddErrors(arrayInParamRequiresItemsMsg(param.Name, op.ID))
break
@@ -374,7 +371,7 @@ func (s *SpecValidator) validateItems() *Result {
for _, resp := range responses {
// Response headers with array
for hn, hv := range resp.Headers {
if hv.TypeName() == "array" && hv.ItemsTypeName() == "" {
if hv.TypeName() == arrayType && hv.ItemsTypeName() == "" {
res.AddErrors(arrayInHeaderRequiresItemsMsg(hn, op.ID))
}
}
@@ -390,7 +387,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)
if !schema.Type.Contains("array") {
if !schema.Type.Contains(arrayType) {
return res
}
@@ -451,6 +448,7 @@ func (s *SpecValidator) validateReferenced() *Result {
return &res
}
// nolint: dupl
func (s *SpecValidator) validateReferencedParameters() *Result {
// Each referenceable definition should have references.
params := s.spec.Spec().Parameters
@@ -463,9 +461,7 @@ func (s *SpecValidator) validateReferencedParameters() *Result {
expected["#/parameters/"+jsonpointer.Escape(k)] = struct{}{}
}
for _, k := range s.analyzer.AllParameterReferences() {
if _, ok := expected[k]; ok {
delete(expected, k)
}
delete(expected, k)
}
if len(expected) == 0 {
@@ -478,6 +474,7 @@ func (s *SpecValidator) validateReferencedParameters() *Result {
return result
}
// nolint: dupl
func (s *SpecValidator) validateReferencedResponses() *Result {
// Each referenceable definition should have references.
responses := s.spec.Spec().Responses
@@ -490,9 +487,7 @@ func (s *SpecValidator) validateReferencedResponses() *Result {
expected["#/responses/"+jsonpointer.Escape(k)] = struct{}{}
}
for _, k := range s.analyzer.AllResponseReferences() {
if _, ok := expected[k]; ok {
delete(expected, k)
}
delete(expected, k)
}
if len(expected) == 0 {
@@ -505,6 +500,7 @@ func (s *SpecValidator) validateReferencedResponses() *Result {
return result
}
// nolint: dupl
func (s *SpecValidator) validateReferencedDefinitions() *Result {
// Each referenceable definition must have references.
defs := s.spec.Spec().Definitions
@@ -517,9 +513,7 @@ func (s *SpecValidator) validateReferencedDefinitions() *Result {
expected["#/definitions/"+jsonpointer.Escape(k)] = struct{}{}
}
for _, k := range s.analyzer.AllDefinitionReferences() {
if _, ok := expected[k]; ok {
delete(expected, k)
}
delete(expected, k)
}
if len(expected) == 0 {
@@ -624,98 +618,114 @@ func (s *SpecValidator) validateParameters() *Result {
rexGarbledPathSegment := mustCompileRegexp(`.*[{}\s]+.*`)
for method, pi := range s.analyzer.Operations() {
methodPaths := make(map[string]map[string]string)
if pi != nil { // Safeguard
for path, op := range pi {
pathToAdd := pathHelp.stripParametersInPath(path)
for path, op := range pi {
pathToAdd := pathHelp.stripParametersInPath(path)
// Warn on garbled path afer param stripping
if rexGarbledPathSegment.MatchString(pathToAdd) {
res.AddWarnings(pathStrippedParamGarbledMsg(pathToAdd))
}
// Check uniqueness of stripped paths
if _, found := methodPaths[method][pathToAdd]; found {
// Sort names for stable, testable output
if strings.Compare(path, methodPaths[method][pathToAdd]) < 0 {
res.AddErrors(pathOverlapMsg(path, methodPaths[method][pathToAdd]))
} else {
res.AddErrors(pathOverlapMsg(methodPaths[method][pathToAdd], path))
}
} else {
if _, found := methodPaths[method]; !found {
methodPaths[method] = map[string]string{}
}
methodPaths[method][pathToAdd] = path //Original non stripped path
}
var bodyParams []string
var paramNames []string
var hasForm, hasBody bool
// Check parameters names uniqueness for operation
// TODO: should be done after param expansion
res.Merge(s.checkUniqueParams(path, method, op))
for _, pr := range paramHelp.safeExpandedParamsFor(path, method, op.ID, res, s) {
// 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))
}
// There must be at most one parameter in body: list them all
if pr.In == "body" {
bodyParams = append(bodyParams, fmt.Sprintf("%q", pr.Name))
hasBody = true
}
if pr.In == "path" {
paramNames = append(paramNames, pr.Name)
// Path declared in path must have the required: true property
if !pr.Required {
res.AddErrors(pathParamRequiredMsg(op.ID, pr.Name))
}
}
if pr.In == "formData" {
hasForm = true
}
}
// In:formData and In:body are mutually exclusive
if hasBody && hasForm {
res.AddErrors(bothFormDataAndBodyMsg(op.ID))
}
// There must be at most one body param
// Accurately report situations when more than 1 body param is declared (possibly unnamed)
if len(bodyParams) > 1 {
sort.Strings(bodyParams)
res.AddErrors(multipleBodyParamMsg(op.ID, bodyParams))
}
// Check uniqueness of parameters in path
paramsInPath := pathHelp.extractPathParams(path)
for i, p := range paramsInPath {
for j, q := range paramsInPath {
if p == q && i > j {
res.AddErrors(pathParamNotUniqueMsg(path, p, q))
break
}
}
}
// Warns about possible malformed params in path
rexGarbledParam := mustCompileRegexp(`{.*[{}\s]+.*}`)
for _, p := range paramsInPath {
if rexGarbledParam.MatchString(p) {
res.AddWarnings(pathParamGarbledMsg(path, p))
}
}
// Match params from path vs params from params section
res.Merge(s.validatePathParamPresence(path, paramsInPath, paramNames))
// Warn on garbled path afer param stripping
if rexGarbledPathSegment.MatchString(pathToAdd) {
res.AddWarnings(pathStrippedParamGarbledMsg(pathToAdd))
}
// Check uniqueness of stripped paths
if _, found := methodPaths[method][pathToAdd]; found {
// Sort names for stable, testable output
if strings.Compare(path, methodPaths[method][pathToAdd]) < 0 {
res.AddErrors(pathOverlapMsg(path, methodPaths[method][pathToAdd]))
} else {
res.AddErrors(pathOverlapMsg(methodPaths[method][pathToAdd], path))
}
} else {
if _, found := methodPaths[method]; !found {
methodPaths[method] = map[string]string{}
}
methodPaths[method][pathToAdd] = path //Original non stripped path
}
var bodyParams []string
var paramNames []string
var hasForm, hasBody bool
// Check parameters names uniqueness for operation
// TODO: should be done after param expansion
res.Merge(s.checkUniqueParams(path, method, op))
for _, pr := range paramHelp.safeExpandedParamsFor(path, method, op.ID, res, s) {
// 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))
}
// There must be at most one parameter in body: list them all
if pr.In == swaggerBody {
bodyParams = append(bodyParams, fmt.Sprintf("%q", pr.Name))
hasBody = true
}
if pr.In == "path" {
paramNames = append(paramNames, pr.Name)
// Path declared in path must have the required: true property
if !pr.Required {
res.AddErrors(pathParamRequiredMsg(op.ID, pr.Name))
}
}
if pr.In == "formData" {
hasForm = true
}
if !(pr.Type == numberType || pr.Type == integerType) &&
(pr.Maximum != nil || pr.Minimum != nil || pr.MultipleOf != nil) {
// A non-numeric parameter has validation keywords for numeric instances (number and integer)
res.AddWarnings(parameterValidationTypeMismatchMsg(pr.Name, path, pr.Type))
}
if !(pr.Type == stringType) &&
// A non-string parameter has validation keywords for strings
(pr.MaxLength != nil || pr.MinLength != nil || pr.Pattern != "") {
res.AddWarnings(parameterValidationTypeMismatchMsg(pr.Name, path, pr.Type))
}
if !(pr.Type == arrayType) &&
// A non-array parameter has validation keywords for arrays
(pr.MaxItems != nil || pr.MinItems != nil || pr.UniqueItems) {
res.AddWarnings(parameterValidationTypeMismatchMsg(pr.Name, path, pr.Type))
}
}
// In:formData and In:body are mutually exclusive
if hasBody && hasForm {
res.AddErrors(bothFormDataAndBodyMsg(op.ID))
}
// There must be at most one body param
// Accurately report situations when more than 1 body param is declared (possibly unnamed)
if len(bodyParams) > 1 {
sort.Strings(bodyParams)
res.AddErrors(multipleBodyParamMsg(op.ID, bodyParams))
}
// Check uniqueness of parameters in path
paramsInPath := pathHelp.extractPathParams(path)
for i, p := range paramsInPath {
for j, q := range paramsInPath {
if p == q && i > j {
res.AddErrors(pathParamNotUniqueMsg(path, p, q))
break
}
}
}
// Warns about possible malformed params in path
rexGarbledParam := mustCompileRegexp(`{.*[{}\s]+.*}`)
for _, p := range paramsInPath {
if rexGarbledParam.MatchString(p) {
res.AddWarnings(pathParamGarbledMsg(path, p))
}
}
// Match params from path vs params from params section
res.Merge(s.validatePathParamPresence(path, paramsInPath, paramNames))
}
}
return res

View File

@@ -163,6 +163,9 @@ const (
// PathParamGarbledWarning ...
PathParamGarbledWarning = "in path %q, param %q contains {,} or white space. Albeit not stricly illegal, this is probably no what you want"
// ParamValidationTypeMismatch indicates that parameter has validation which does not match its type
ParamValidationTypeMismatch = "validation keywords of parameter %q in path %q don't match its type %s"
// PathStrippedParamGarbledWarning ...
PathStrippedParamGarbledWarning = "path stripped from path parameters %s contains {,} or white space. This is probably no what you want."
@@ -341,6 +344,9 @@ func invalidParameterDefinitionMsg(path, method, operationID string) errors.Erro
func invalidParameterDefinitionAsSchemaMsg(path, method, operationID string) errors.Error {
return errors.New(errors.CompositeErrorCode, InvalidParameterDefinitionAsSchemaError, path, method, operationID)
}
func parameterValidationTypeMismatchMsg(param, path, typ string) errors.Error {
return errors.New(errors.CompositeErrorCode, ParamValidationTypeMismatch, param, path, typ)
}
// disabled
//func invalidResponseDefinitionAsSchemaMsg(path, method string) errors.Error {

View File

@@ -39,53 +39,53 @@ func (t *typeValidator) schemaInfoForType(data interface{}) (string, string) {
// TODO: this switch really is some sort of reverse lookup for formats. It should be provided by strfmt.
switch data.(type) {
case []byte, strfmt.Base64, *strfmt.Base64:
return "string", "byte"
return stringType, stringFormatByte
case strfmt.CreditCard, *strfmt.CreditCard:
return "string", "creditcard"
return stringType, stringFormatCreditCard
case strfmt.Date, *strfmt.Date:
return "string", "date"
return stringType, stringFormatDate
case strfmt.DateTime, *strfmt.DateTime:
return "string", "date-time"
return stringType, stringFormatDateTime
case strfmt.Duration, *strfmt.Duration:
return "string", "duration"
return stringType, stringFormatDuration
case runtime.File, *runtime.File:
return "file", ""
return fileType, ""
case strfmt.Email, *strfmt.Email:
return "string", "email"
return stringType, stringFormatEmail
case strfmt.HexColor, *strfmt.HexColor:
return "string", "hexcolor"
return stringType, stringFormatHexColor
case strfmt.Hostname, *strfmt.Hostname:
return "string", "hostname"
return stringType, stringFormatHostname
case strfmt.IPv4, *strfmt.IPv4:
return "string", "ipv4"
return stringType, stringFormatIPv4
case strfmt.IPv6, *strfmt.IPv6:
return "string", "ipv6"
return stringType, stringFormatIPv6
case strfmt.ISBN, *strfmt.ISBN:
return "string", "isbn"
return stringType, stringFormatISBN
case strfmt.ISBN10, *strfmt.ISBN10:
return "string", "isbn10"
return stringType, stringFormatISBN10
case strfmt.ISBN13, *strfmt.ISBN13:
return "string", "isbn13"
return stringType, stringFormatISBN13
case strfmt.MAC, *strfmt.MAC:
return "string", "mac"
return stringType, stringFormatMAC
case strfmt.ObjectId, *strfmt.ObjectId:
return "string", "bsonobjectid"
return stringType, stringFormatBSONObjectID
case strfmt.Password, *strfmt.Password:
return "string", "password"
return stringType, stringFormatPassword
case strfmt.RGBColor, *strfmt.RGBColor:
return "string", "rgbcolor"
return stringType, stringFormatRGBColor
case strfmt.SSN, *strfmt.SSN:
return "string", "ssn"
return stringType, stringFormatSSN
case strfmt.URI, *strfmt.URI:
return "string", "uri"
return stringType, stringFormatURI
case strfmt.UUID, *strfmt.UUID:
return "string", "uuid"
return stringType, stringFormatUUID
case strfmt.UUID3, *strfmt.UUID3:
return "string", "uuid3"
return stringType, stringFormatUUID3
case strfmt.UUID4, *strfmt.UUID4:
return "string", "uuid4"
return stringType, stringFormatUUID4
case strfmt.UUID5, *strfmt.UUID5:
return "string", "uuid5"
return stringType, stringFormatUUID5
// TODO: missing binary (io.ReadCloser)
// TODO: missing json.Number
default:
@@ -93,25 +93,25 @@ func (t *typeValidator) schemaInfoForType(data interface{}) (string, string) {
tpe := val.Type()
switch tpe.Kind() {
case reflect.Bool:
return "boolean", ""
return booleanType, ""
case reflect.String:
return "string", ""
return stringType, ""
case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Uint8, reflect.Uint16, reflect.Uint32:
// NOTE: that is the spec. With go-openapi, is that not uint32 for unsigned integers?
return "integer", "int32"
return integerType, integerFormatInt32
case reflect.Int, reflect.Int64, reflect.Uint, reflect.Uint64:
return "integer", "int64"
return integerType, integerFormatInt64
case reflect.Float32:
// NOTE: is that not "float"?
return "number", "float32"
// NOTE: is that not numberFormatFloat?
return numberType, numberFormatFloat32
case reflect.Float64:
// NOTE: is that not "double"?
return "number", "float64"
return numberType, numberFormatFloat64
// NOTE: go arrays (reflect.Array) are not supported (fixed length)
case reflect.Slice:
return "array", ""
return arrayType, ""
case reflect.Map, reflect.Struct:
return "object", ""
return objectType, ""
case reflect.Interface:
// What to do here?
panic("dunno what to do here")
@@ -139,8 +139,8 @@ func (t *typeValidator) Validate(data interface{}) *Result {
result.Inc()
if data == nil || reflect.DeepEqual(reflect.Zero(reflect.TypeOf(data)), reflect.ValueOf(data)) {
// nil or zero value for the passed structure require Type: null
if len(t.Type) > 0 && !t.Type.Contains("null") && !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, ","), "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 result
}
@@ -157,17 +157,17 @@ func (t *typeValidator) Validate(data interface{}) *Result {
// check numerical types
// TODO: check unsigned ints
// TODO: check json.Number (see schema.go)
isLowerInt := t.Format == "int64" && format == "int32"
isLowerFloat := t.Format == "float64" && format == "float32"
isFloatInt := schType == "number" && swag.IsFloat64AJSONInteger(val.Float()) && t.Type.Contains("integer")
isIntFloat := schType == "integer" && t.Type.Contains("number")
isLowerInt := t.Format == integerFormatInt64 && format == integerFormatInt32
isLowerFloat := t.Format == numberFormatFloat64 && format == numberFormatFloat32
isFloatInt := schType == numberType && swag.IsFloat64AJSONInteger(val.Float()) && t.Type.Contains(integerType)
isIntFloat := schType == integerType && t.Type.Contains(numberType)
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))
}
if !(t.Type.Contains("number") || t.Type.Contains("integer")) && t.Format != "" && (kind == reflect.String || kind == reflect.Slice) {
if !(t.Type.Contains(numberType) || t.Type.Contains(integerType)) && t.Format != "" && (kind == reflect.String || kind == reflect.Slice) {
return result
}

View File

@@ -452,6 +452,7 @@ func (s *basicSliceValidator) Validate(data interface{}) *Result {
return nil
}
/* unused
func (s *basicSliceValidator) hasDuplicates(value reflect.Value, size int) bool {
dict := make(map[interface{}]struct{})
for i := 0; i < size; i++ {
@@ -463,6 +464,7 @@ func (s *basicSliceValidator) hasDuplicates(value reflect.Value, size int) bool
}
return false
}
*/
type numberValidator struct {
Path string
@@ -530,6 +532,7 @@ func (n *numberValidator) Validate(val interface{}) *Result {
// Is the provided value within the range of the specified numeric type and format?
res.AddErrors(IsValueValidAgainstRange(val, n.Type, n.Format, "Checked", n.Path))
// nolint: dupl
if n.MultipleOf != nil {
// Is the constraint specifier within the range of the specific numeric type and format?
resMultiple.AddErrors(IsValueValidAgainstRange(*n.MultipleOf, n.Type, n.Format, "MultipleOf", n.Path))
@@ -546,6 +549,7 @@ func (n *numberValidator) Validate(val interface{}) *Result {
}
}
// nolint: dupl
if n.Maximum != nil {
// Is the constraint specifier within the range of the specific numeric type and format?
resMaximum.AddErrors(IsValueValidAgainstRange(*n.Maximum, n.Type, n.Format, "Maximum boundary", n.Path))
@@ -562,6 +566,7 @@ func (n *numberValidator) Validate(val interface{}) *Result {
}
}
// nolint: dupl
if n.Minimum != nil {
// Is the constraint specifier within the range of the specific numeric type and format?
resMinimum.AddErrors(IsValueValidAgainstRange(*n.Minimum, n.Type, n.Format, "Minimum boundary", n.Path))
@@ -611,7 +616,7 @@ func (s *stringValidator) Applies(source interface{}, kind reflect.Kind) bool {
func (s *stringValidator) Validate(val interface{}) *Result {
data, ok := val.(string)
if !ok {
return errorHelp.sErr(errors.InvalidType(s.Path, s.In, "string", val))
return errorHelp.sErr(errors.InvalidType(s.Path, s.In, stringType, val))
}
if s.Required && !s.AllowEmptyValue && (s.Default == nil || s.Default == "") {

View File

@@ -361,26 +361,26 @@ func IsValueValidAgainstRange(val interface{}, typeName, format, prefix, path st
var errVal error
switch typeName {
case "integer":
case integerType:
switch format {
case "int32":
case integerFormatInt32:
_, errVal = swag.ConvertInt32(stringRep)
case "uint32":
case integerFormatUInt32:
_, errVal = swag.ConvertUint32(stringRep)
case "uint64":
case integerFormatUInt64:
_, errVal = swag.ConvertUint64(stringRep)
case "int64":
case integerFormatInt64:
fallthrough
default:
_, errVal = swag.ConvertInt64(stringRep)
}
case "number":
case numberType:
fallthrough
default:
switch format {
case "float", "float32":
case numberFormatFloat, numberFormatFloat32:
_, errVal = swag.ConvertFloat32(stringRep)
case "double", "float64":
case numberFormatDouble, numberFormatFloat64:
fallthrough
default:
// No check can be performed here since