summaryrefslogtreecommitdiff
path: root/vendor/github.com/go-openapi/validate
diff options
context:
space:
mode:
authorLibravatar tobi <31960611+tsmethurst@users.noreply.github.com>2024-04-26 11:31:10 +0200
committerLibravatar GitHub <noreply@github.com>2024-04-26 11:31:10 +0200
commitfd8a724e77123d5035d4070581dab777b4533ebb (patch)
tree0d3d20ad5c7c2177d970ead12f5108d1d3666623 /vendor/github.com/go-openapi/validate
parent[chore] Upgrade our Go version to 1.22 (#2862) (diff)
downloadgotosocial-fd8a724e77123d5035d4070581dab777b4533ebb.tar.xz
[chore] Bump go swagger (#2871)
* bump go swagger version * bump swagger version
Diffstat (limited to 'vendor/github.com/go-openapi/validate')
-rw-r--r--vendor/github.com/go-openapi/validate/.golangci.yml53
-rw-r--r--vendor/github.com/go-openapi/validate/BENCHMARK.md31
-rw-r--r--vendor/github.com/go-openapi/validate/README.md8
-rw-r--r--vendor/github.com/go-openapi/validate/appveyor.yml32
-rw-r--r--vendor/github.com/go-openapi/validate/default_validator.go109
-rw-r--r--vendor/github.com/go-openapi/validate/doc.go70
-rw-r--r--vendor/github.com/go-openapi/validate/example_validator.go67
-rw-r--r--vendor/github.com/go-openapi/validate/formats.go78
-rw-r--r--vendor/github.com/go-openapi/validate/helpers.go23
-rw-r--r--vendor/github.com/go-openapi/validate/object_validator.go448
-rw-r--r--vendor/github.com/go-openapi/validate/options.go21
-rw-r--r--vendor/github.com/go-openapi/validate/pools.go366
-rw-r--r--vendor/github.com/go-openapi/validate/pools_debug.go1012
-rw-r--r--vendor/github.com/go-openapi/validate/result.go131
-rw-r--r--vendor/github.com/go-openapi/validate/schema.go260
-rw-r--r--vendor/github.com/go-openapi/validate/schema_option.go31
-rw-r--r--vendor/github.com/go-openapi/validate/schema_props.go412
-rw-r--r--vendor/github.com/go-openapi/validate/slice_validator.go57
-rw-r--r--vendor/github.com/go-openapi/validate/spec.go188
-rw-r--r--vendor/github.com/go-openapi/validate/spec_messages.go12
-rw-r--r--vendor/github.com/go-openapi/validate/type.go72
-rw-r--r--vendor/github.com/go-openapi/validate/validator.go936
-rw-r--r--vendor/github.com/go-openapi/validate/values.go12
23 files changed, 3468 insertions, 961 deletions
diff --git a/vendor/github.com/go-openapi/validate/.golangci.yml b/vendor/github.com/go-openapi/validate/.golangci.yml
index 81818ca67..22f8d21cc 100644
--- a/vendor/github.com/go-openapi/validate/.golangci.yml
+++ b/vendor/github.com/go-openapi/validate/.golangci.yml
@@ -1,12 +1,14 @@
linters-settings:
govet:
check-shadowing: true
+ golint:
+ min-confidence: 0
gocyclo:
- min-complexity: 50
+ min-complexity: 45
maligned:
suggest-new: true
dupl:
- threshold: 100
+ threshold: 200
goconst:
min-len: 2
min-occurrences: 3
@@ -15,36 +17,45 @@ linters:
enable-all: true
disable:
- maligned
+ - unparam
- lll
+ - gochecknoinits
+ - gochecknoglobals
+ - funlen
- godox
- gocognit
- whitespace
- wsl
- - funlen
- - gochecknoglobals
- - gochecknoinits
- - scopelint
- wrapcheck
- - exhaustivestruct
- - exhaustive
- - nlreturn
- testpackage
- - gci
- - gofumpt
- - goerr113
+ - nlreturn
- gomnd
- - tparallel
+ - exhaustivestruct
+ - goerr113
+ - errorlint
- nestif
- godot
- - tparallel
+ - gofumpt
- paralleltest
- - cyclop # because we have gocyclo already
- # TODO: review the linters below. We disabled them to make the CI pass first.
- - ireturn
+ - tparallel
+ - thelper
+ - ifshort
+ - exhaustruct
- varnamelen
+ - gci
+ - depguard
+ - errchkjson
+ - inamedparam
+ - nonamedreturns
+ - musttag
+ - ireturn
- forcetypeassert
- - thelper
- # Disable deprecated linters.
- # They will be removed from golangci-lint in future.
+ - cyclop
+ # deprecated linters
+ - deadcode
- interfacer
- - golint \ No newline at end of file
+ - scopelint
+ - varcheck
+ - structcheck
+ - golint
+ - nosnakecase
diff --git a/vendor/github.com/go-openapi/validate/BENCHMARK.md b/vendor/github.com/go-openapi/validate/BENCHMARK.md
new file mode 100644
index 000000000..79cf6a077
--- /dev/null
+++ b/vendor/github.com/go-openapi/validate/BENCHMARK.md
@@ -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
+```
diff --git a/vendor/github.com/go-openapi/validate/README.md b/vendor/github.com/go-openapi/validate/README.md
index ea2d68cb6..e8e1bb218 100644
--- a/vendor/github.com/go-openapi/validate/README.md
+++ b/vendor/github.com/go-openapi/validate/README.md
@@ -1,7 +1,5 @@
-# Validation helpers
-[![Build Status](https://travis-ci.org/go-openapi/validate.svg?branch=master)](https://travis-ci.org/go-openapi/validate)
-[![Build status](https://ci.appveyor.com/api/projects/status/d6epy6vipueyh5fs/branch/master?svg=true)](https://ci.appveyor.com/project/fredbi/validate/branch/master)
-[![codecov](https://codecov.io/gh/go-openapi/validate/branch/master/graph/badge.svg)](https://codecov.io/gh/go-openapi/validate)
+# Validation helpers [![Build Status](https://github.com/go-openapi/validate/actions/workflows/go-test.yml/badge.svg)](https://github.com/go-openapi/validate/actions?query=workflow%3A"go+test") [![codecov](https://codecov.io/gh/go-openapi/validate/branch/master/graph/badge.svg)](https://codecov.io/gh/go-openapi/validate)
+
[![Slack Status](https://slackin.goswagger.io/badge.svg)](https://slackin.goswagger.io)
[![license](http://img.shields.io/badge/license-Apache%20v2-orange.svg)](https://raw.githubusercontent.com/go-openapi/validate/master/LICENSE)
[![Go Reference](https://pkg.go.dev/badge/github.com/go-openapi/validate.svg)](https://pkg.go.dev/github.com/go-openapi/validate)
@@ -24,7 +22,7 @@ Reference can be found here: https://github.com/OAI/OpenAPI-Specification/blob/m
* Minimum, Maximum, MultipleOf
* FormatOf
-[Documentation](https://godoc.org/github.com/go-openapi/validate)
+[Documentation](https://pkg.go.dev/github.com/go-openapi/validate)
## FAQ
diff --git a/vendor/github.com/go-openapi/validate/appveyor.yml b/vendor/github.com/go-openapi/validate/appveyor.yml
deleted file mode 100644
index 89e5bccb3..000000000
--- a/vendor/github.com/go-openapi/validate/appveyor.yml
+++ /dev/null
@@ -1,32 +0,0 @@
-version: "0.1.{build}"
-
-clone_folder: C:\go-openapi\validate
-shallow_clone: true # for startup speed
-pull_requests:
- do_not_increment_build_number: true
-
-#skip_tags: true
-#skip_branch_with_pr: true
-
-# appveyor.yml
-build: off
-
-environment:
- GOPATH: c:\gopath
-
-stack: go 1.15
-
-test_script:
- - go test -v -timeout 20m -args -enable-long ./...
-
-deploy: off
-
-notifications:
- - provider: Slack
- incoming_webhook: https://hooks.slack.com/services/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
- auth_token:
- secure: Sf7kZf7ZGbnwWUMpffHwMu5A0cHkLK2MYY32LNTPj4+/3qC3Ghl7+9v4TSLOqOlCwdRNjOGblAq7s+GDJed6/xgRQl1JtCi1klzZNrYX4q01pgTPvvGcwbBkIYgeMaPeIRcK9OZnud7sRXdttozgTOpytps2U6Js32ip7uj5mHSg2ub0FwoSJwlS6dbezZ8+eDhoha0F/guY99BEwx8Bd+zROrT2TFGsSGOFGN6wFc7moCqTHO/YkWib13a2QNXqOxCCVBy/lt76Wp+JkeFppjHlzs/2lP3EAk13RIUAaesdEUHvIHrzCyNJEd3/+KO2DzsWOYfpktd+KBCvgaYOsoo7ubdT3IROeAegZdCgo/6xgCEsmFc9ZcqCfN5yNx2A+BZ2Vwmpws+bQ1E1+B5HDzzaiLcYfG4X2O210QVGVDLWsv1jqD+uPYeHY2WRfh5ZsIUFvaqgUEnwHwrK44/8REAhQavt1QAj5uJpsRd7CkRVPWRNK+yIky+wgbVUFEchRNmS55E7QWf+W4+4QZkQi7vUTMc9nbTUu2Es9NfvfudOpM2wZbn98fjpb/qq/nRv6Bk+ca+7XD5/IgNLMbWp2ouDdzbiHLCOfDUiHiDJhLfFZx9Bwo7ZwfzeOlbrQX66bx7xRKYmOe4DLrXhNcpbsMa8qbfxlZRCmYbubB/Y8h4=
- channel: bots
- on_build_success: false
- on_build_failure: true
- on_build_status_changed: true
diff --git a/vendor/github.com/go-openapi/validate/default_validator.go b/vendor/github.com/go-openapi/validate/default_validator.go
index bd14c2a26..e0dd93839 100644
--- a/vendor/github.com/go-openapi/validate/default_validator.go
+++ b/vendor/github.com/go-openapi/validate/default_validator.go
@@ -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(&param, s.KnownFormats).Validate(param.Default) //#nosec
+ red := newParamValidator(&param, 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
@@ -170,17 +184,18 @@ 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
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)
}
}
@@ -190,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)
}
}
@@ -209,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
@@ -220,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 {
@@ -242,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
@@ -251,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 {
@@ -262,13 +283,15 @@ func (d *defaultValidator) validateDefaultValueSchemaAgainstSchema(path, in stri
}
// 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)
+ 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))
diff --git a/vendor/github.com/go-openapi/validate/doc.go b/vendor/github.com/go-openapi/validate/doc.go
index f5ca9a5d5..d2b901eab 100644
--- a/vendor/github.com/go-openapi/validate/doc.go
+++ b/vendor/github.com/go-openapi/validate/doc.go
@@ -19,7 +19,7 @@ as well as tools to validate data against their schema.
This package follows Swagger 2.0. specification (aka OpenAPI 2.0). Reference
can be found here: https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md.
-Validating a specification
+# Validating a specification
Validates a spec document (from JSON or YAML) against the JSON schema for swagger,
then checks a number of extra rules that can't be expressed in JSON schema.
@@ -30,34 +30,36 @@ Entry points:
- SpecValidator.Validate()
Reported as errors:
- [x] definition can't declare a property that's already defined by one of its ancestors
- [x] definition's ancestor can't be a descendant of the same model
- [x] path uniqueness: each api path should be non-verbatim (account for path param names) unique per method
- [x] each security reference should contain only unique scopes
- [x] each security scope in a security definition should be unique
- [x] parameters in path must be unique
- [x] each path parameter must correspond to a parameter placeholder and vice versa
- [x] each referenceable definition must have references
- [x] each definition property listed in the required array must be defined in the properties of the model
- [x] each parameter should have a unique `name` and `type` combination
- [x] each operation should have only 1 parameter of type body
- [x] each reference must point to a valid object
- [x] every default value that is specified must validate against the schema for that property
- [x] items property is required for all schemas/definitions of type `array`
- [x] path parameters must be declared a required
- [x] headers must not contain $ref
- [x] schema and property examples provided must validate against their respective object's schema
- [x] examples provided must validate their schema
+
+ [x] definition can't declare a property that's already defined by one of its ancestors
+ [x] definition's ancestor can't be a descendant of the same model
+ [x] path uniqueness: each api path should be non-verbatim (account for path param names) unique per method. Validation can be laxed by disabling StrictPathParamUniqueness.
+ [x] each security reference should contain only unique scopes
+ [x] each security scope in a security definition should be unique
+ [x] parameters in path must be unique
+ [x] each path parameter must correspond to a parameter placeholder and vice versa
+ [x] each referenceable definition must have references
+ [x] each definition property listed in the required array must be defined in the properties of the model
+ [x] each parameter should have a unique `name` and `type` combination
+ [x] each operation should have only 1 parameter of type body
+ [x] each reference must point to a valid object
+ [x] every default value that is specified must validate against the schema for that property
+ [x] items property is required for all schemas/definitions of type `array`
+ [x] path parameters must be declared a required
+ [x] headers must not contain $ref
+ [x] schema and property examples provided must validate against their respective object's schema
+ [x] examples provided must validate their schema
Reported as warnings:
- [x] path parameters should not contain any of [{,},\w]
- [x] empty path
- [x] unused definitions
- [x] unsupported validation of examples on non-JSON media types
- [x] examples in response without schema
- [x] readOnly properties should not be required
-Validating a schema
+ [x] path parameters should not contain any of [{,},\w]
+ [x] empty path
+ [x] unused definitions
+ [x] unsupported validation of examples on non-JSON media types
+ [x] examples in response without schema
+ [x] readOnly properties should not be required
+
+# Validating a schema
The schema validation toolkit validates data against JSON-schema-draft 04 schema.
@@ -70,16 +72,16 @@ Entry points:
- AgainstSchema()
- ...
-Known limitations
+# Known limitations
With the current version of this package, the following aspects of swagger are not yet supported:
- [ ] errors and warnings are not reported with key/line number in spec
- [ ] default values and examples on responses only support application/json producer type
- [ ] invalid numeric constraints (such as Minimum, etc..) are not checked except for default and example values
- [ ] rules for collectionFormat are not implemented
- [ ] no validation rule for polymorphism support (discriminator) [not done here]
- [ ] valid js ECMA regexp not supported by Go regexp engine are considered invalid
- [ ] arbitrary large numbers are not supported: max is math.MaxFloat64
+ [ ] errors and warnings are not reported with key/line number in spec
+ [ ] default values and examples on responses only support application/json producer type
+ [ ] invalid numeric constraints (such as Minimum, etc..) are not checked except for default and example values
+ [ ] rules for collectionFormat are not implemented
+ [ ] no validation rule for polymorphism support (discriminator) [not done here]
+ [ ] valid js ECMA regexp not supported by Go regexp engine are considered invalid
+ [ ] arbitrary large numbers are not supported: max is math.MaxFloat64
*/
package validate
diff --git a/vendor/github.com/go-openapi/validate/example_validator.go b/vendor/github.com/go-openapi/validate/example_validator.go
index c8bffd78e..d08956973 100644
--- a/vendor/github.com/go-openapi/validate/example_validator.go
+++ b/vendor/github.com/go-openapi/validate/example_validator.go
@@ -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,9 +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
}
@@ -65,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() {
@@ -83,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(&param, s.KnownFormats).Validate(param.Example) //#nosec
+ red := newParamValidator(&param, 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)
}
}
@@ -96,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)
}
}
@@ -105,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)
}
}
}
@@ -130,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
@@ -146,17 +162,18 @@ 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
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)
}
}
@@ -166,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)
}
}
@@ -185,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))
@@ -210,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 {
@@ -231,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
@@ -240,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 {
@@ -251,13 +276,16 @@ func (ex *exampleValidator) validateExampleValueSchemaAgainstSchema(path, in str
}
// 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)
+ 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))
@@ -266,5 +294,6 @@ func (ex *exampleValidator) validateExampleValueItemsAgainstSchema(path, in stri
res.AddErrors(invalidPatternInMsg(path, in, items.Pattern))
}
}
+
return res
}
diff --git a/vendor/github.com/go-openapi/validate/formats.go b/vendor/github.com/go-openapi/validate/formats.go
index 0ad996cbb..f4e355213 100644
--- a/vendor/github.com/go-openapi/validate/formats.go
+++ b/vendor/github.com/go-openapi/validate/formats.go
@@ -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)
}
diff --git a/vendor/github.com/go-openapi/validate/helpers.go b/vendor/github.com/go-openapi/validate/helpers.go
index 48ebfab58..757e403d9 100644
--- a/vendor/github.com/go-openapi/validate/helpers.go
+++ b/vendor/github.com/go-openapi/validate/helpers.go
@@ -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 {
@@ -157,7 +165,7 @@ func (h *valueHelper) asInt64(val interface{}) int64 {
// Number conversion function for int64, without error checking
// (implements an implicit type upgrade).
v := reflect.ValueOf(val)
- switch v.Kind() {
+ switch v.Kind() { //nolint:exhaustive
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return v.Int()
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
@@ -174,7 +182,7 @@ func (h *valueHelper) asUint64(val interface{}) uint64 {
// Number conversion function for uint64, without error checking
// (implements an implicit type upgrade).
v := reflect.ValueOf(val)
- switch v.Kind() {
+ switch v.Kind() { //nolint:exhaustive
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return uint64(v.Int())
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
@@ -192,7 +200,7 @@ func (h *valueHelper) asFloat64(val interface{}) float64 {
// Number conversion function for float64, without error checking
// (implements an implicit type upgrade).
v := reflect.ValueOf(val)
- switch v.Kind() {
+ switch v.Kind() { //nolint:exhaustive
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return float64(v.Int())
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
@@ -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
@@ -250,7 +258,7 @@ func (h *paramHelper) resolveParam(path, method, operationID string, param *spec
}
if err != nil { // Safeguard
- // NOTE: we may enter enter here when the whole parameter is an unresolved $ref
+ // NOTE: we may enter here when the whole parameter is an unresolved $ref
refPath := strings.Join([]string{"\"" + path + "\"", method}, ".")
errorHelp.addPointerError(res, err, param.Ref.String(), refPath)
return nil, res
@@ -306,6 +314,7 @@ func (r *responseHelper) expandResponseRef(
errorHelp.addPointerError(res, err, response.Ref.String(), path)
return nil, res
}
+
return response, res
}
diff --git a/vendor/github.com/go-openapi/validate/object_validator.go b/vendor/github.com/go-openapi/validate/object_validator.go
index 7bb12615d..dff73fa98 100644
--- a/vendor/github.com/go-openapi/validate/object_validator.go
+++ b/vendor/github.com/go-openapi/validate/object_validator.go
@@ -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
- }
+ o.validateNoAdditionalProperties(val, res)
+ } else {
+ // Cases: empty additionalProperties (implying: true), or additionalProperties: true, or additionalProperties: { <<schema>> }
+ o.validateAdditionalProperties(val, res)
+ }
+
+ 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, _, 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)
}
+ }
+ }
- 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
- }
- */
- }
+ 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
}
}
- } 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))
- }
+ 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
}
- // Valid cases: additionalProperties: true or undefined
+
+ // 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
+}
- createdFromDefaults := map[string]bool{}
+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] // one instance per iteration
- rName := pName
- if o.Path != "" {
+ *pSchema = o.Properties[pName]
+ var rName string
+ if o.Path == "" {
+ rName = pName
+ } else {
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)
+ v, ok := val[pName]
+ if ok {
+ r := newSchemaValidator(pSchema, o.Root, rName, o.KnownFormats, o.Options).Validate(v)
+ res.mergeForField(val, pName, r)
+
+ continue
}
- }
- // 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
+ 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
}
}
}
- // 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)
- }
- }
+ 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))
}
- return res
}
// TODO: succeededOnce is not used anywhere
func (o *objectValidator) validatePatternProperty(key string, value interface{}, result *Result) (bool, bool, []string) {
+ if len(o.PatternProperties) == 0 {
+ return false, false, nil
+ }
+
matched := false
succeededOnce := false
- var patterns []string
+ patterns := make([]string, 0, len(o.PatternProperties))
- 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()...)
+ schema := pools.poolOfSchemas.BorrowSchema()
+ defer func() {
+ pools.poolOfSchemas.RedeemSchema(schema)
+ }()
- res := validator.Validate(value)
- result.Merge(res)
+ for k := range o.PatternProperties {
+ re, err := compileRegexp(k)
+ if err != nil {
+ continue
}
- }
- // BUG(fredbi): can't get to here. Should remove dead code (commented out).
+ match := re.MatchString(key)
+ if !match {
+ continue
+ }
- // if succeededOnce {
- // result.Inc()
- // }
+ *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)
+}
diff --git a/vendor/github.com/go-openapi/validate/options.go b/vendor/github.com/go-openapi/validate/options.go
index deeec2f2e..cfe9b0660 100644
--- a/vendor/github.com/go-openapi/validate/options.go
+++ b/vendor/github.com/go-openapi/validate/options.go
@@ -21,10 +21,29 @@ import "sync"
// NOTE: other options might be needed, for example a go-swagger specific mode.
type Opts struct {
ContinueOnErrors bool // true: continue reporting errors, even if spec is invalid
+
+ // StrictPathParamUniqueness enables a strict validation of paths that include
+ // path parameters. When true, it will enforce that for each method, the path
+ // is unique, regardless of path parameters such that GET:/petstore/{id} and
+ // GET:/petstore/{pet} anre considered duplicate paths.
+ //
+ // Consider disabling if path parameters can include slashes such as
+ // GET:/v1/{shelve} and GET:/v1/{book}, where the IDs are "shelve/*" and
+ // /"shelve/*/book/*" respectively.
+ StrictPathParamUniqueness bool
+ SkipSchemataResult bool
}
var (
- defaultOpts = Opts{ContinueOnErrors: false} // default is to stop validation on errors
+ defaultOpts = Opts{
+ // default is to stop validation on errors
+ ContinueOnErrors: false,
+
+ // StrictPathParamUniqueness is defaulted to true. This maintains existing
+ // behavior.
+ StrictPathParamUniqueness: true,
+ }
+
defaultOptsMutex = &sync.Mutex{}
)
diff --git a/vendor/github.com/go-openapi/validate/pools.go b/vendor/github.com/go-openapi/validate/pools.go
new file mode 100644
index 000000000..3ddce4dcc
--- /dev/null
+++ b/vendor/github.com/go-openapi/validate/pools.go
@@ -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)
+}
diff --git a/vendor/github.com/go-openapi/validate/pools_debug.go b/vendor/github.com/go-openapi/validate/pools_debug.go
new file mode 100644
index 000000000..12949f02a
--- /dev/null
+++ b/vendor/github.com/go-openapi/validate/pools_debug.go
@@ -0,0 +1,1012 @@
+//go:build validatedebug
+
+package validate
+
+import (
+ "fmt"
+ "runtime"
+ "sync"
+ "testing"
+
+ "github.com/go-openapi/spec"
+)
+
+// This version of the pools is to be used for debugging and testing, with build tag "validatedebug".
+//
+// In this mode, the pools are tracked for allocation and redemption of borrowed objects, so we can
+// verify a few behaviors of the validators. The debug pools panic when an invalid usage pattern is detected.
+
+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
+ },
+ },
+ debugMap: make(map[*SchemaValidator]status),
+ allocMap: make(map[*SchemaValidator]string),
+ redeemMap: make(map[*SchemaValidator]string),
+ },
+ poolOfObjectValidators: objectValidatorsPool{
+ Pool: &sync.Pool{
+ New: func() any {
+ s := &objectValidator{}
+
+ return s
+ },
+ },
+ debugMap: make(map[*objectValidator]status),
+ allocMap: make(map[*objectValidator]string),
+ redeemMap: make(map[*objectValidator]string),
+ },
+ poolOfSliceValidators: sliceValidatorsPool{
+ Pool: &sync.Pool{
+ New: func() any {
+ s := &schemaSliceValidator{}
+
+ return s
+ },
+ },
+ debugMap: make(map[*schemaSliceValidator]status),
+ allocMap: make(map[*schemaSliceValidator]string),
+ redeemMap: make(map[*schemaSliceValidator]string),
+ },
+ poolOfItemsValidators: itemsValidatorsPool{
+ Pool: &sync.Pool{
+ New: func() any {
+ s := &itemsValidator{}
+
+ return s
+ },
+ },
+ debugMap: make(map[*itemsValidator]status),
+ allocMap: make(map[*itemsValidator]string),
+ redeemMap: make(map[*itemsValidator]string),
+ },
+ poolOfBasicCommonValidators: basicCommonValidatorsPool{
+ Pool: &sync.Pool{
+ New: func() any {
+ s := &basicCommonValidator{}
+
+ return s
+ },
+ },
+ debugMap: make(map[*basicCommonValidator]status),
+ allocMap: make(map[*basicCommonValidator]string),
+ redeemMap: make(map[*basicCommonValidator]string),
+ },
+ poolOfHeaderValidators: headerValidatorsPool{
+ Pool: &sync.Pool{
+ New: func() any {
+ s := &HeaderValidator{}
+
+ return s
+ },
+ },
+ debugMap: make(map[*HeaderValidator]status),
+ allocMap: make(map[*HeaderValidator]string),
+ redeemMap: make(map[*HeaderValidator]string),
+ },
+ poolOfParamValidators: paramValidatorsPool{
+ Pool: &sync.Pool{
+ New: func() any {
+ s := &ParamValidator{}
+
+ return s
+ },
+ },
+ debugMap: make(map[*ParamValidator]status),
+ allocMap: make(map[*ParamValidator]string),
+ redeemMap: make(map[*ParamValidator]string),
+ },
+ poolOfBasicSliceValidators: basicSliceValidatorsPool{
+ Pool: &sync.Pool{
+ New: func() any {
+ s := &basicSliceValidator{}
+
+ return s
+ },
+ },
+ debugMap: make(map[*basicSliceValidator]status),
+ allocMap: make(map[*basicSliceValidator]string),
+ redeemMap: make(map[*basicSliceValidator]string),
+ },
+ poolOfNumberValidators: numberValidatorsPool{
+ Pool: &sync.Pool{
+ New: func() any {
+ s := &numberValidator{}
+
+ return s
+ },
+ },
+ debugMap: make(map[*numberValidator]status),
+ allocMap: make(map[*numberValidator]string),
+ redeemMap: make(map[*numberValidator]string),
+ },
+ poolOfStringValidators: stringValidatorsPool{
+ Pool: &sync.Pool{
+ New: func() any {
+ s := &stringValidator{}
+
+ return s
+ },
+ },
+ debugMap: make(map[*stringValidator]status),
+ allocMap: make(map[*stringValidator]string),
+ redeemMap: make(map[*stringValidator]string),
+ },
+ poolOfSchemaPropsValidators: schemaPropsValidatorsPool{
+ Pool: &sync.Pool{
+ New: func() any {
+ s := &schemaPropsValidator{}
+
+ return s
+ },
+ },
+ debugMap: make(map[*schemaPropsValidator]status),
+ allocMap: make(map[*schemaPropsValidator]string),
+ redeemMap: make(map[*schemaPropsValidator]string),
+ },
+ poolOfFormatValidators: formatValidatorsPool{
+ Pool: &sync.Pool{
+ New: func() any {
+ s := &formatValidator{}
+
+ return s
+ },
+ },
+ debugMap: make(map[*formatValidator]status),
+ allocMap: make(map[*formatValidator]string),
+ redeemMap: make(map[*formatValidator]string),
+ },
+ poolOfTypeValidators: typeValidatorsPool{
+ Pool: &sync.Pool{
+ New: func() any {
+ s := &typeValidator{}
+
+ return s
+ },
+ },
+ debugMap: make(map[*typeValidator]status),
+ allocMap: make(map[*typeValidator]string),
+ redeemMap: make(map[*typeValidator]string),
+ },
+ poolOfSchemas: schemasPool{
+ Pool: &sync.Pool{
+ New: func() any {
+ s := &spec.Schema{}
+
+ return s
+ },
+ },
+ debugMap: make(map[*spec.Schema]status),
+ allocMap: make(map[*spec.Schema]string),
+ redeemMap: make(map[*spec.Schema]string),
+ },
+ poolOfResults: resultsPool{
+ Pool: &sync.Pool{
+ New: func() any {
+ s := &Result{}
+
+ return s
+ },
+ },
+ debugMap: make(map[*Result]status),
+ allocMap: make(map[*Result]string),
+ redeemMap: make(map[*Result]string),
+ },
+ }
+}
+
+const (
+ statusFresh status = iota + 1
+ statusRecycled
+ statusRedeemed
+)
+
+func (s status) String() string {
+ switch s {
+ case statusFresh:
+ return "fresh"
+ case statusRecycled:
+ return "recycled"
+ case statusRedeemed:
+ return "redeemed"
+ default:
+ panic(fmt.Errorf("invalid status: %d", s))
+ }
+}
+
+type (
+ // Debug
+ status uint8
+
+ 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
+ debugMap map[*SchemaValidator]status
+ allocMap map[*SchemaValidator]string
+ redeemMap map[*SchemaValidator]string
+ mx sync.Mutex
+ }
+
+ objectValidatorsPool struct {
+ *sync.Pool
+ debugMap map[*objectValidator]status
+ allocMap map[*objectValidator]string
+ redeemMap map[*objectValidator]string
+ mx sync.Mutex
+ }
+
+ sliceValidatorsPool struct {
+ *sync.Pool
+ debugMap map[*schemaSliceValidator]status
+ allocMap map[*schemaSliceValidator]string
+ redeemMap map[*schemaSliceValidator]string
+ mx sync.Mutex
+ }
+
+ itemsValidatorsPool struct {
+ *sync.Pool
+ debugMap map[*itemsValidator]status
+ allocMap map[*itemsValidator]string
+ redeemMap map[*itemsValidator]string
+ mx sync.Mutex
+ }
+
+ basicCommonValidatorsPool struct {
+ *sync.Pool
+ debugMap map[*basicCommonValidator]status
+ allocMap map[*basicCommonValidator]string
+ redeemMap map[*basicCommonValidator]string
+ mx sync.Mutex
+ }
+
+ headerValidatorsPool struct {
+ *sync.Pool
+ debugMap map[*HeaderValidator]status
+ allocMap map[*HeaderValidator]string
+ redeemMap map[*HeaderValidator]string
+ mx sync.Mutex
+ }
+
+ paramValidatorsPool struct {
+ *sync.Pool
+ debugMap map[*ParamValidator]status
+ allocMap map[*ParamValidator]string
+ redeemMap map[*ParamValidator]string
+ mx sync.Mutex
+ }
+
+ basicSliceValidatorsPool struct {
+ *sync.Pool
+ debugMap map[*basicSliceValidator]status
+ allocMap map[*basicSliceValidator]string
+ redeemMap map[*basicSliceValidator]string
+ mx sync.Mutex
+ }
+
+ numberValidatorsPool struct {
+ *sync.Pool
+ debugMap map[*numberValidator]status
+ allocMap map[*numberValidator]string
+ redeemMap map[*numberValidator]string
+ mx sync.Mutex
+ }
+
+ stringValidatorsPool struct {
+ *sync.Pool
+ debugMap map[*stringValidator]status
+ allocMap map[*stringValidator]string
+ redeemMap map[*stringValidator]string
+ mx sync.Mutex
+ }
+
+ schemaPropsValidatorsPool struct {
+ *sync.Pool
+ debugMap map[*schemaPropsValidator]status
+ allocMap map[*schemaPropsValidator]string
+ redeemMap map[*schemaPropsValidator]string
+ mx sync.Mutex
+ }
+
+ formatValidatorsPool struct {
+ *sync.Pool
+ debugMap map[*formatValidator]status
+ allocMap map[*formatValidator]string
+ redeemMap map[*formatValidator]string
+ mx sync.Mutex
+ }
+
+ typeValidatorsPool struct {
+ *sync.Pool
+ debugMap map[*typeValidator]status
+ allocMap map[*typeValidator]string
+ redeemMap map[*typeValidator]string
+ mx sync.Mutex
+ }
+
+ schemasPool struct {
+ *sync.Pool
+ debugMap map[*spec.Schema]status
+ allocMap map[*spec.Schema]string
+ redeemMap map[*spec.Schema]string
+ mx sync.Mutex
+ }
+
+ resultsPool struct {
+ *sync.Pool
+ debugMap map[*Result]status
+ allocMap map[*Result]string
+ redeemMap map[*Result]string
+ mx sync.Mutex
+ }
+)
+
+func (p *schemaValidatorsPool) BorrowValidator() *SchemaValidator {
+ s := p.Get().(*SchemaValidator)
+
+ p.mx.Lock()
+ defer p.mx.Unlock()
+ x, ok := p.debugMap[s]
+ if !ok {
+ p.debugMap[s] = statusFresh
+ } else {
+ if x != statusRedeemed {
+ panic("recycled schema should have been redeemed")
+ }
+ p.debugMap[s] = statusRecycled
+ }
+ p.allocMap[s] = caller()
+
+ return s
+}
+
+func (p *schemaValidatorsPool) RedeemValidator(s *SchemaValidator) {
+ // NOTE: s might be nil. In that case, Put is a noop.
+ p.mx.Lock()
+ defer p.mx.Unlock()
+ x, ok := p.debugMap[s]
+ if !ok {
+ panic("redeemed schema should have been allocated")
+ }
+ if x != statusRecycled && x != statusFresh {
+ panic("redeemed schema should have been allocated from a fresh or recycled pointer")
+ }
+ p.debugMap[s] = statusRedeemed
+ p.redeemMap[s] = caller()
+ p.Put(s)
+}
+
+func (p *objectValidatorsPool) BorrowValidator() *objectValidator {
+ s := p.Get().(*objectValidator)
+
+ p.mx.Lock()
+ defer p.mx.Unlock()
+ x, ok := p.debugMap[s]
+ if !ok {
+ p.debugMap[s] = statusFresh
+ } else {
+ if x != statusRedeemed {
+ panic("recycled object should have been redeemed")
+ }
+ p.debugMap[s] = statusRecycled
+ }
+ p.allocMap[s] = caller()
+
+ return s
+}
+
+func (p *objectValidatorsPool) RedeemValidator(s *objectValidator) {
+ p.mx.Lock()
+ defer p.mx.Unlock()
+ x, ok := p.debugMap[s]
+ if !ok {
+ panic("redeemed object should have been allocated")
+ }
+ if x != statusRecycled && x != statusFresh {
+ panic("redeemed object should have been allocated from a fresh or recycled pointer")
+ }
+ p.debugMap[s] = statusRedeemed
+ p.redeemMap[s] = caller()
+ p.Put(s)
+}
+
+func (p *sliceValidatorsPool) BorrowValidator() *schemaSliceValidator {
+ s := p.Get().(*schemaSliceValidator)
+
+ p.mx.Lock()
+ defer p.mx.Unlock()
+ x, ok := p.debugMap[s]
+ if !ok {
+ p.debugMap[s] = statusFresh
+ } else {
+ if x != statusRedeemed {
+ panic("recycled schemaSliceValidator should have been redeemed")
+ }
+ p.debugMap[s] = statusRecycled
+ }
+ p.allocMap[s] = caller()
+
+ return s
+}
+
+func (p *sliceValidatorsPool) RedeemValidator(s *schemaSliceValidator) {
+ p.mx.Lock()
+ defer p.mx.Unlock()
+ x, ok := p.debugMap[s]
+ if !ok {
+ panic("redeemed schemaSliceValidator should have been allocated")
+ }
+ if x != statusRecycled && x != statusFresh {
+ panic("redeemed schemaSliceValidator should have been allocated from a fresh or recycled pointer")
+ }
+ p.debugMap[s] = statusRedeemed
+ p.redeemMap[s] = caller()
+ p.Put(s)
+}
+
+func (p *itemsValidatorsPool) BorrowValidator() *itemsValidator {
+ s := p.Get().(*itemsValidator)
+
+ p.mx.Lock()
+ defer p.mx.Unlock()
+ x, ok := p.debugMap[s]
+ if !ok {
+ p.debugMap[s] = statusFresh
+ } else {
+ if x != statusRedeemed {
+ panic("recycled itemsValidator should have been redeemed")
+ }
+ p.debugMap[s] = statusRecycled
+ }
+ p.allocMap[s] = caller()
+
+ return s
+}
+
+func (p *itemsValidatorsPool) RedeemValidator(s *itemsValidator) {
+ p.mx.Lock()
+ defer p.mx.Unlock()
+ x, ok := p.debugMap[s]
+ if !ok {
+ panic("redeemed itemsValidator should have been allocated")
+ }
+ if x != statusRecycled && x != statusFresh {
+ panic("redeemed itemsValidator should have been allocated from a fresh or recycled pointer")
+ }
+ p.debugMap[s] = statusRedeemed
+ p.redeemMap[s] = caller()
+ p.Put(s)
+}
+
+func (p *basicCommonValidatorsPool) BorrowValidator() *basicCommonValidator {
+ s := p.Get().(*basicCommonValidator)
+
+ p.mx.Lock()
+ defer p.mx.Unlock()
+ x, ok := p.debugMap[s]
+ if !ok {
+ p.debugMap[s] = statusFresh
+ } else {
+ if x != statusRedeemed {
+ panic("recycled basicCommonValidator should have been redeemed")
+ }
+ p.debugMap[s] = statusRecycled
+ }
+ p.allocMap[s] = caller()
+
+ return s
+}
+
+func (p *basicCommonValidatorsPool) RedeemValidator(s *basicCommonValidator) {
+ p.mx.Lock()
+ defer p.mx.Unlock()
+ x, ok := p.debugMap[s]
+ if !ok {
+ panic("redeemed basicCommonValidator should have been allocated")
+ }
+ if x != statusRecycled && x != statusFresh {
+ panic("redeemed basicCommonValidator should have been allocated from a fresh or recycled pointer")
+ }
+ p.debugMap[s] = statusRedeemed
+ p.redeemMap[s] = caller()
+ p.Put(s)
+}
+
+func (p *headerValidatorsPool) BorrowValidator() *HeaderValidator {
+ s := p.Get().(*HeaderValidator)
+
+ p.mx.Lock()
+ defer p.mx.Unlock()
+ x, ok := p.debugMap[s]
+ if !ok {
+ p.debugMap[s] = statusFresh
+ } else {
+ if x != statusRedeemed {
+ panic("recycled HeaderValidator should have been redeemed")
+ }
+ p.debugMap[s] = statusRecycled
+ }
+ p.allocMap[s] = caller()
+
+ return s
+}
+
+func (p *headerValidatorsPool) RedeemValidator(s *HeaderValidator) {
+ p.mx.Lock()
+ defer p.mx.Unlock()
+ x, ok := p.debugMap[s]
+ if !ok {
+ panic("redeemed header should have been allocated")
+ }
+ if x != statusRecycled && x != statusFresh {
+ panic("redeemed header should have been allocated from a fresh or recycled pointer")
+ }
+ p.debugMap[s] = statusRedeemed
+ p.redeemMap[s] = caller()
+ p.Put(s)
+}
+
+func (p *paramValidatorsPool) BorrowValidator() *ParamValidator {
+ s := p.Get().(*ParamValidator)
+
+ p.mx.Lock()
+ defer p.mx.Unlock()
+ x, ok := p.debugMap[s]
+ if !ok {
+ p.debugMap[s] = statusFresh
+ } else {
+ if x != statusRedeemed {
+ panic("recycled param should have been redeemed")
+ }
+ p.debugMap[s] = statusRecycled
+ }
+ p.allocMap[s] = caller()
+
+ return s
+}
+
+func (p *paramValidatorsPool) RedeemValidator(s *ParamValidator) {
+ p.mx.Lock()
+ defer p.mx.Unlock()
+ x, ok := p.debugMap[s]
+ if !ok {
+ panic("redeemed param should have been allocated")
+ }
+ if x != statusRecycled && x != statusFresh {
+ panic("redeemed param should have been allocated from a fresh or recycled pointer")
+ }
+ p.debugMap[s] = statusRedeemed
+ p.redeemMap[s] = caller()
+ p.Put(s)
+}
+
+func (p *basicSliceValidatorsPool) BorrowValidator() *basicSliceValidator {
+ s := p.Get().(*basicSliceValidator)
+
+ p.mx.Lock()
+ defer p.mx.Unlock()
+ x, ok := p.debugMap[s]
+ if !ok {
+ p.debugMap[s] = statusFresh
+ } else {
+ if x != statusRedeemed {
+ panic("recycled basicSliceValidator should have been redeemed")
+ }
+ p.debugMap[s] = statusRecycled
+ }
+ p.allocMap[s] = caller()
+
+ return s
+}
+
+func (p *basicSliceValidatorsPool) RedeemValidator(s *basicSliceValidator) {
+ p.mx.Lock()
+ defer p.mx.Unlock()
+ x, ok := p.debugMap[s]
+ if !ok {
+ panic("redeemed basicSliceValidator should have been allocated")
+ }
+ if x != statusRecycled && x != statusFresh {
+ panic("redeemed basicSliceValidator should have been allocated from a fresh or recycled pointer")
+ }
+ p.debugMap[s] = statusRedeemed
+ p.redeemMap[s] = caller()
+ p.Put(s)
+}
+
+func (p *numberValidatorsPool) BorrowValidator() *numberValidator {
+ s := p.Get().(*numberValidator)
+
+ p.mx.Lock()
+ defer p.mx.Unlock()
+ x, ok := p.debugMap[s]
+ if !ok {
+ p.debugMap[s] = statusFresh
+ } else {
+ if x != statusRedeemed {
+ panic("recycled number should have been redeemed")
+ }
+ p.debugMap[s] = statusRecycled
+ }
+ p.allocMap[s] = caller()
+
+ return s
+}
+
+func (p *numberValidatorsPool) RedeemValidator(s *numberValidator) {
+ p.mx.Lock()
+ defer p.mx.Unlock()
+ x, ok := p.debugMap[s]
+ if !ok {
+ panic("redeemed number should have been allocated")
+ }
+ if x != statusRecycled && x != statusFresh {
+ panic("redeemed number should have been allocated from a fresh or recycled pointer")
+ }
+ p.debugMap[s] = statusRedeemed
+ p.redeemMap[s] = caller()
+ p.Put(s)
+}
+
+func (p *stringValidatorsPool) BorrowValidator() *stringValidator {
+ s := p.Get().(*stringValidator)
+
+ p.mx.Lock()
+ defer p.mx.Unlock()
+ x, ok := p.debugMap[s]
+ if !ok {
+ p.debugMap[s] = statusFresh
+ } else {
+ if x != statusRedeemed {
+ panic("recycled string should have been redeemed")
+ }
+ p.debugMap[s] = statusRecycled
+ }
+ p.allocMap[s] = caller()
+
+ return s
+}
+
+func (p *stringValidatorsPool) RedeemValidator(s *stringValidator) {
+ p.mx.Lock()
+ defer p.mx.Unlock()
+ x, ok := p.debugMap[s]
+ if !ok {
+ panic("redeemed string should have been allocated")
+ }
+ if x != statusRecycled && x != statusFresh {
+ panic("redeemed string should have been allocated from a fresh or recycled pointer")
+ }
+ p.debugMap[s] = statusRedeemed
+ p.redeemMap[s] = caller()
+ p.Put(s)
+}
+
+func (p *schemaPropsValidatorsPool) BorrowValidator() *schemaPropsValidator {
+ s := p.Get().(*schemaPropsValidator)
+
+ p.mx.Lock()
+ defer p.mx.Unlock()
+ x, ok := p.debugMap[s]
+ if !ok {
+ p.debugMap[s] = statusFresh
+ } else {
+ if x != statusRedeemed {
+ panic("recycled param should have been redeemed")
+ }
+ p.debugMap[s] = statusRecycled
+ }
+ p.allocMap[s] = caller()
+
+ return s
+}
+
+func (p *schemaPropsValidatorsPool) RedeemValidator(s *schemaPropsValidator) {
+ p.mx.Lock()
+ defer p.mx.Unlock()
+ x, ok := p.debugMap[s]
+ if !ok {
+ panic("redeemed schemaProps should have been allocated")
+ }
+ if x != statusRecycled && x != statusFresh {
+ panic("redeemed schemaProps should have been allocated from a fresh or recycled pointer")
+ }
+ p.debugMap[s] = statusRedeemed
+ p.redeemMap[s] = caller()
+ p.Put(s)
+}
+
+func (p *formatValidatorsPool) BorrowValidator() *formatValidator {
+ s := p.Get().(*formatValidator)
+
+ p.mx.Lock()
+ defer p.mx.Unlock()
+ x, ok := p.debugMap[s]
+ if !ok {
+ p.debugMap[s] = statusFresh
+ } else {
+ if x != statusRedeemed {
+ panic("recycled format should have been redeemed")
+ }
+ p.debugMap[s] = statusRecycled
+ }
+ p.allocMap[s] = caller()
+
+ return s
+}
+
+func (p *formatValidatorsPool) RedeemValidator(s *formatValidator) {
+ p.mx.Lock()
+ defer p.mx.Unlock()
+ x, ok := p.debugMap[s]
+ if !ok {
+ panic("redeemed format should have been allocated")
+ }
+ if x != statusRecycled && x != statusFresh {
+ panic("redeemed format should have been allocated from a fresh or recycled pointer")
+ }
+ p.debugMap[s] = statusRedeemed
+ p.redeemMap[s] = caller()
+ p.Put(s)
+}
+
+func (p *typeValidatorsPool) BorrowValidator() *typeValidator {
+ s := p.Get().(*typeValidator)
+
+ p.mx.Lock()
+ defer p.mx.Unlock()
+ x, ok := p.debugMap[s]
+ if !ok {
+ p.debugMap[s] = statusFresh
+ } else {
+ if x != statusRedeemed {
+ panic("recycled type should have been redeemed")
+ }
+ p.debugMap[s] = statusRecycled
+ }
+ p.allocMap[s] = caller()
+
+ return s
+}
+
+func (p *typeValidatorsPool) RedeemValidator(s *typeValidator) {
+ p.mx.Lock()
+ defer p.mx.Unlock()
+ x, ok := p.debugMap[s]
+ if !ok {
+ panic("redeemed type should have been allocated")
+ }
+ if x != statusRecycled && x != statusFresh {
+ panic(fmt.Errorf("redeemed type should have been allocated from a fresh or recycled pointer. Got status %s, already redeamed at: %s", x, p.redeemMap[s]))
+ }
+ p.debugMap[s] = statusRedeemed
+ p.redeemMap[s] = caller()
+ p.Put(s)
+}
+
+func (p *schemasPool) BorrowSchema() *spec.Schema {
+ s := p.Get().(*spec.Schema)
+
+ p.mx.Lock()
+ defer p.mx.Unlock()
+ x, ok := p.debugMap[s]
+ if !ok {
+ p.debugMap[s] = statusFresh
+ } else {
+ if x != statusRedeemed {
+ panic("recycled spec.Schema should have been redeemed")
+ }
+ p.debugMap[s] = statusRecycled
+ }
+ p.allocMap[s] = caller()
+
+ return s
+}
+
+func (p *schemasPool) RedeemSchema(s *spec.Schema) {
+ p.mx.Lock()
+ defer p.mx.Unlock()
+ x, ok := p.debugMap[s]
+ if !ok {
+ panic("redeemed spec.Schema should have been allocated")
+ }
+ if x != statusRecycled && x != statusFresh {
+ panic("redeemed spec.Schema should have been allocated from a fresh or recycled pointer")
+ }
+ p.debugMap[s] = statusRedeemed
+ p.redeemMap[s] = caller()
+ p.Put(s)
+}
+
+func (p *resultsPool) BorrowResult() *Result {
+ s := p.Get().(*Result).cleared()
+
+ p.mx.Lock()
+ defer p.mx.Unlock()
+ x, ok := p.debugMap[s]
+ if !ok {
+ p.debugMap[s] = statusFresh
+ } else {
+ if x != statusRedeemed {
+ panic("recycled result should have been redeemed")
+ }
+ p.debugMap[s] = statusRecycled
+ }
+ p.allocMap[s] = caller()
+
+ return s
+}
+
+func (p *resultsPool) RedeemResult(s *Result) {
+ if s == emptyResult {
+ if len(s.Errors) > 0 || len(s.Warnings) > 0 {
+ panic("empty result should not mutate")
+ }
+ return
+ }
+ p.mx.Lock()
+ defer p.mx.Unlock()
+ x, ok := p.debugMap[s]
+ if !ok {
+ panic("redeemed Result should have been allocated")
+ }
+ if x != statusRecycled && x != statusFresh {
+ panic("redeemed Result should have been allocated from a fresh or recycled pointer")
+ }
+ p.debugMap[s] = statusRedeemed
+ p.redeemMap[s] = caller()
+ p.Put(s)
+}
+
+func (p *allPools) allIsRedeemed(t testing.TB) bool {
+ outcome := true
+ for k, v := range p.poolOfSchemaValidators.debugMap {
+ if v == statusRedeemed {
+ continue
+ }
+ t.Logf("schemaValidator should be redeemed. Allocated by: %s", p.poolOfSchemaValidators.allocMap[k])
+ outcome = false
+ }
+ for k, v := range p.poolOfObjectValidators.debugMap {
+ if v == statusRedeemed {
+ continue
+ }
+ t.Logf("objectValidator should be redeemed. Allocated by: %s", p.poolOfObjectValidators.allocMap[k])
+ outcome = false
+ }
+ for k, v := range p.poolOfSliceValidators.debugMap {
+ if v == statusRedeemed {
+ continue
+ }
+ t.Logf("sliceValidator should be redeemed. Allocated by: %s", p.poolOfSliceValidators.allocMap[k])
+ outcome = false
+ }
+ for k, v := range p.poolOfItemsValidators.debugMap {
+ if v == statusRedeemed {
+ continue
+ }
+ t.Logf("itemsValidator should be redeemed. Allocated by: %s", p.poolOfItemsValidators.allocMap[k])
+ outcome = false
+ }
+ for k, v := range p.poolOfBasicCommonValidators.debugMap {
+ if v == statusRedeemed {
+ continue
+ }
+ t.Logf("basicCommonValidator should be redeemed. Allocated by: %s", p.poolOfBasicCommonValidators.allocMap[k])
+ outcome = false
+ }
+ for k, v := range p.poolOfHeaderValidators.debugMap {
+ if v == statusRedeemed {
+ continue
+ }
+ t.Logf("headerValidator should be redeemed. Allocated by: %s", p.poolOfHeaderValidators.allocMap[k])
+ outcome = false
+ }
+ for k, v := range p.poolOfParamValidators.debugMap {
+ if v == statusRedeemed {
+ continue
+ }
+ t.Logf("paramValidator should be redeemed. Allocated by: %s", p.poolOfParamValidators.allocMap[k])
+ outcome = false
+ }
+ for k, v := range p.poolOfBasicSliceValidators.debugMap {
+ if v == statusRedeemed {
+ continue
+ }
+ t.Logf("basicSliceValidator should be redeemed. Allocated by: %s", p.poolOfBasicSliceValidators.allocMap[k])
+ outcome = false
+ }
+ for k, v := range p.poolOfNumberValidators.debugMap {
+ if v == statusRedeemed {
+ continue
+ }
+ t.Logf("numberValidator should be redeemed. Allocated by: %s", p.poolOfNumberValidators.allocMap[k])
+ outcome = false
+ }
+ for k, v := range p.poolOfStringValidators.debugMap {
+ if v == statusRedeemed {
+ continue
+ }
+ t.Logf("stringValidator should be redeemed. Allocated by: %s", p.poolOfStringValidators.allocMap[k])
+ outcome = false
+ }
+ for k, v := range p.poolOfSchemaPropsValidators.debugMap {
+ if v == statusRedeemed {
+ continue
+ }
+ t.Logf("schemaPropsValidator should be redeemed. Allocated by: %s", p.poolOfSchemaPropsValidators.allocMap[k])
+ outcome = false
+ }
+ for k, v := range p.poolOfFormatValidators.debugMap {
+ if v == statusRedeemed {
+ continue
+ }
+ t.Logf("formatValidator should be redeemed. Allocated by: %s", p.poolOfFormatValidators.allocMap[k])
+ outcome = false
+ }
+ for k, v := range p.poolOfTypeValidators.debugMap {
+ if v == statusRedeemed {
+ continue
+ }
+ t.Logf("typeValidator should be redeemed. Allocated by: %s", p.poolOfTypeValidators.allocMap[k])
+ outcome = false
+ }
+ for k, v := range p.poolOfSchemas.debugMap {
+ if v == statusRedeemed {
+ continue
+ }
+ t.Logf("schemas should be redeemed. Allocated by: %s", p.poolOfSchemas.allocMap[k])
+ outcome = false
+ }
+ for k, v := range p.poolOfResults.debugMap {
+ if v == statusRedeemed {
+ continue
+ }
+ t.Logf("result should be redeemed. Allocated by: %s", p.poolOfResults.allocMap[k])
+ outcome = false
+ }
+
+ return outcome
+}
+
+func caller() string {
+ pc, _, _, _ := runtime.Caller(3) //nolint:dogsled
+ from, line := runtime.FuncForPC(pc).FileLine(pc)
+
+ return fmt.Sprintf("%s:%d", from, line)
+}
diff --git a/vendor/github.com/go-openapi/validate/result.go b/vendor/github.com/go-openapi/validate/result.go
index 8f5f935e5..c80804a93 100644
--- a/vendor/github.com/go-openapi/validate/result.go
+++ b/vendor/github.com/go-openapi/validate/result.go
@@ -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
}
@@ -132,10 +139,9 @@ 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
+ if r.cachedFieldSchemata != nil {
+ return r.cachedFieldSchemata
}
ret := make(map[FieldKey][]*spec.Schema, len(r.fieldSchemata))
@@ -147,12 +153,12 @@ func (r *Result) FieldSchemata() map[FieldKey][]*spec.Schema {
ret[key] = append(ret[key], fs.schemata.multiple...)
}
}
- r.cachedFieldSchemta = ret
+ r.cachedFieldSchemata = ret
+
return ret
}
// 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
@@ -172,12 +178,13 @@ func (r *Result) ItemSchemata() map[ItemKey][]*spec.Schema {
}
func (r *Result) resetCaches() {
- r.cachedFieldSchemta = nil
+ r.cachedFieldSchemata = nil
r.cachedItemSchemata = nil
}
// mergeForField merges other into r, assigning other's root schemata to the given Object and field name.
-// nolint: unparam
+//
+//nolint:unparam
func (r *Result) mergeForField(obj map[string]interface{}, field string, other *Result) *Result {
if other == nil {
return r
@@ -188,18 +195,23 @@ 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
}
// mergeForSlice merges other into r, assigning other's root schemata to the given slice and index.
-// nolint: unparam
+//
+//nolint:unparam
func (r *Result) mergeForSlice(slice reflect.Value, i int, other *Result) *Result {
if other == nil {
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
+}
diff --git a/vendor/github.com/go-openapi/validate/schema.go b/vendor/github.com/go-openapi/validate/schema.go
index b817eb0ef..db65264fd 100644
--- a/vendor/github.com/go-openapi/validate/schema.go
+++ b/vendor/github.com/go-openapi/validate/schema.go
@@ -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
@@ -101,24 +120,46 @@ func (s *SchemaValidator) SetPath(path string) {
}
// Applies returns true when this schema validator applies
-func (s *SchemaValidator) Applies(source interface{}, kind reflect.Kind) bool {
+func (s *SchemaValidator) Applies(source interface{}, _ reflect.Kind) bool {
_, ok := source.(*spec.Schema)
return ok
}
// 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
}
}
diff --git a/vendor/github.com/go-openapi/validate/schema_option.go b/vendor/github.com/go-openapi/validate/schema_option.go
index 4b4879de8..65eeebeaa 100644
--- a/vendor/github.com/go-openapi/validate/schema_option.go
+++ b/vendor/github.com/go-openapi/validate/schema_option.go
@@ -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),
}
}
diff --git a/vendor/github.com/go-openapi/validate/schema_props.go b/vendor/github.com/go-openapi/validate/schema_props.go
index 9bac3d29f..1ca379244 100644
--- a/vendor/github.com/go-openapi/validate/schema_props.go
+++ b/vendor/github.com/go-openapi/validate/schema_props.go
@@ -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))
+ }
+ 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 _, v := range oneOf {
- v := v
- oneValidators = append(oneValidators, *NewSchemaValidator(&v, root, path, formats, options...))
+ 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...)
- }
-
- 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,
+ notValidator = newSchemaValidator(not, root, path, formats, opts)
+ }
+
+ 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
+ keepResultAnyOf = pools.poolOfResults.BorrowResult()
+ s.validateAnyOf(data, mainResult, keepResultAnyOf)
+ }
+
+ if len(s.oneOfValidators) > 0 {
+ keepResultOneOf = pools.poolOfResults.BorrowResult()
+ s.validateOneOf(data, mainResult, keepResultOneOf)
+ }
+
+ if len(s.allOfValidators) > 0 {
+ keepResultAllOf = pools.poolOfResults.BorrowResult()
+ s.validateAllOf(data, mainResult, keepResultAllOf)
+ }
+
+ if s.notValidator != nil {
+ s.validateNot(data, mainResult)
+ }
+
+ if s.Dependencies != nil && len(s.Dependencies) > 0 && reflect.TypeOf(data).Kind() == reflect.Map {
+ 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
}
- if !succeededOnce {
- mainResult.AddErrors(mustValidateAtLeastOneSchemaMsg(s.Path))
+ // 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 bestFailures != nil {
- mainResult.Merge(bestFailures)
- } else if firstSuccess != nil {
- mainResult.Merge(firstSuccess)
+
+ 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
- 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
- }
+ var (
+ firstSuccess, bestFailures *Result
+ validated int
+ )
+
+ for i, oneOfSchema := range s.oneOfValidators {
+ result := oneOfSchema.Validate(data)
+ if s.Options.recycleValidators {
+ s.oneOfValidators[i] = nil
}
- if validated != 1 {
- var additionalMsg string
- if validated == 0 {
- additionalMsg = "Found none valid"
- } else {
- additionalMsg = fmt.Sprintf("Found %d valid alternatives", validated)
- }
+ // We keep inner IMPORTANT! errors no matter what MatchCount tells us
+ keepResultOneOf.Merge(result.keepRelevantErrors()) // merges (and redeems) a new instance of Result
- mainResult.AddErrors(mustValidateOnlyOneSchemaMsg(s.Path, additionalMsg))
- if bestFailures != nil {
- mainResult.Merge(bestFailures)
- }
- } else if firstSuccess != nil {
- mainResult.Merge(firstSuccess)
- }
- }
+ if result.IsValid() {
+ validated++
+ _ = keepResultOneOf.cleared()
- // 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++
+ if firstSuccess == nil {
+ firstSuccess = result
+ } else if result.wantsRedeemOnMerge {
+ pools.poolOfResults.RedeemResult(result) // this result is ditched
}
- mainResult.Merge(result)
+
+ continue
}
- if validated != len(s.allOfValidators) {
- additionalMsg := ""
- if validated == 0 {
- additionalMsg = ". None validated"
+ // 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
+ }
+ }
- mainResult.AddErrors(mustValidateAllSchemasMsg(s.Path, additionalMsg))
+ 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)
}
}
+}
- if s.notValidator != nil {
- result := s.notValidator.Validate(data)
+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() {
- mainResult.AddErrors(mustNotValidatechemaMsg(s.Path))
+ validated++
}
+ mainResult.Merge(result)
}
- 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 {
+ switch validated {
+ case 0:
+ mainResult.AddErrors(mustValidateAllSchemasMsg(s.Path, ". None validated"))
+ case len(s.allOfValidators):
+ default:
+ mainResult.AddErrors(mustValidateAllSchemasMsg(s.Path, ""))
+ }
+}
- if dep.Schema != nil {
- mainResult.Merge(NewSchemaValidator(dep.Schema, s.Root, s.Path+"."+key, s.KnownFormats, s.Options.Options()...).Validate(data))
- continue
- }
+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))
- }
- }
+ if len(dep.Property) > 0 {
+ for _, depKey := range dep.Property {
+ if _, ok := val[depKey]; !ok {
+ mainResult.AddErrors(hasADependencyMsg(s.Path, depKey))
}
}
}
}
+}
- 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) 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
+ }
}
diff --git a/vendor/github.com/go-openapi/validate/slice_validator.go b/vendor/github.com/go-openapi/validate/slice_validator.go
index aa429f518..13bb02087 100644
--- a/vendor/github.com/go-openapi/validate/slice_validator.go
+++ b/vendor/github.com/go-openapi/validate/slice_validator.go
@@ -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)
+}
diff --git a/vendor/github.com/go-openapi/validate/spec.go b/vendor/github.com/go-openapi/validate/spec.go
index dff01f00b..965452566 100644
--- a/vendor/github.com/go-openapi/validate/spec.go
+++ b/vendor/github.com/go-openapi/validate/spec.go
@@ -15,6 +15,8 @@
package validate
import (
+ "bytes"
+ "encoding/gob"
"encoding/json"
"fmt"
"sort"
@@ -26,23 +28,23 @@ 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.
//
// Returns an error flattening in a single standard error, all validation messages.
//
-// - TODO: $ref should not have siblings
-// - TODO: make sure documentation reflects all checks and warnings
-// - TODO: check on discriminators
-// - TODO: explicit message on unsupported keywords (better than "forbidden property"...)
-// - TODO: full list of unresolved refs
-// - TODO: validate numeric constraints (issue#581): this should be handled like defaults and examples
-// - TODO: option to determine if we validate for go-swagger or in a more general context
-// - TODO: check on required properties to support anyOf, allOf, oneOf
+// - TODO: $ref should not have siblings
+// - TODO: make sure documentation reflects all checks and warnings
+// - TODO: check on discriminators
+// - TODO: explicit message on unsupported keywords (better than "forbidden property"...)
+// - TODO: full list of unresolved refs
+// - TODO: validate numeric constraints (issue#581): this should be handled like defaults and examples
+// - TODO: option to determine if we validate for go-swagger or in a more general context
+// - TODO: check on required properties to support anyOf, allOf, oneOf
//
// NOTE: SecurityScopes are maps: no need to check uniqueness
-//
func Spec(doc *loads.Document, formats strfmt.Registry) error {
errs, _ /*warns*/ := NewSpecValidator(doc.Schema(), formats).Validate(doc)
if errs.HasErrors() {
@@ -53,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)
@@ -85,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
@@ -103,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() {
@@ -130,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())
@@ -148,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
}
@@ -177,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 != "" {
@@ -199,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
@@ -248,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
@@ -285,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
@@ -335,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 {
@@ -394,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
}
@@ -418,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 {
@@ -456,7 +475,6 @@ 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
@@ -475,14 +493,13 @@ 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))
}
return result
}
-// nolint: dupl
func (s *SpecValidator) validateReferencedResponses() *Result {
// Each referenceable definition should have references.
responses := s.spec.Spec().Responses
@@ -501,14 +518,13 @@ 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))
}
return result
}
-// nolint: dupl
func (s *SpecValidator) validateReferencedDefinitions() *Result {
// Each referenceable definition must have references.
defs := s.spec.Spec().Definitions
@@ -537,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 {
@@ -556,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
@@ -615,40 +631,42 @@ func (s *SpecValidator) validateRequiredProperties(path, in string, v *spec.Sche
func (s *SpecValidator) validateParameters() *Result {
// - for each method, path is unique, regardless of path parameters
// e.g. GET:/petstore/{id}, GET:/petstore/{pet}, GET:/petstore are
- // considered duplicate paths
+ // considered duplicate paths, if StrictPathParamUniqueness is enabled.
// - each parameter should have a unique `name` and `type` combination
// - each operation should have only 1 parameter of type body
// - there must be at most 1 parameter in body
// - 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)
for path, op := range pi {
- pathToAdd := pathHelp.stripParametersInPath(path)
+ if s.Options.StrictPathParamUniqueness {
+ pathToAdd := pathHelp.stripParametersInPath(path)
- // Warn on garbled path afer param stripping
- if rexGarbledPathSegment.MatchString(pathToAdd) {
- res.AddWarnings(pathStrippedParamGarbledMsg(pathToAdd))
- }
+ // 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 {
+ // 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]))
+ // 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 {
- 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
+ if _, found := methodPaths[method]; !found {
+ methodPaths[method] = map[string]string{}
+ }
+ methodPaths[method][pathToAdd] = path // Original non stripped path
+ }
}
var bodyParams []string
@@ -659,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(&paramSchema, 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))
@@ -741,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()))
@@ -767,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
@@ -802,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
+}
diff --git a/vendor/github.com/go-openapi/validate/spec_messages.go b/vendor/github.com/go-openapi/validate/spec_messages.go
index b3757addd..6d1f0f819 100644
--- a/vendor/github.com/go-openapi/validate/spec_messages.go
+++ b/vendor/github.com/go-openapi/validate/spec_messages.go
@@ -187,6 +187,8 @@ const (
// UnusedResponseWarning ...
UnusedResponseWarning = "response %q is not used anywhere"
+
+ InvalidObject = "expected an object in %q.%s"
)
// Additional error codes
@@ -347,11 +349,15 @@ 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
-// func invalidResponseDefinitionAsSchemaMsg(path, method string) errors.Error {
-// return errors.New(errors.CompositeErrorCode, InvalidResponseDefinitionAsSchemaError, path, method)
-// }
+//
+// func invalidResponseDefinitionAsSchemaMsg(path, method string) errors.Error {
+// return errors.New(errors.CompositeErrorCode, InvalidResponseDefinitionAsSchemaError, path, method)
+// }
func someParametersBrokenMsg(path, method, operationID string) errors.Error {
return errors.New(errors.CompositeErrorCode, SomeParametersBrokenError, path, method, operationID)
}
diff --git a/vendor/github.com/go-openapi/validate/type.go b/vendor/github.com/go-openapi/validate/type.go
index 876467588..f87abb3d5 100644
--- a/vendor/github.com/go-openapi/validate/type.go
+++ b/vendor/github.com/go-openapi/validate/type.go
@@ -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) {
@@ -90,7 +113,7 @@ func (t *typeValidator) schemaInfoForType(data interface{}) (string, string) {
default:
val := reflect.ValueOf(data)
tpe := val.Type()
- switch tpe.Kind() {
+ switch tpe.Kind() { //nolint:exhaustive
case reflect.Bool:
return booleanType, ""
case reflect.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)
}
diff --git a/vendor/github.com/go-openapi/validate/validator.go b/vendor/github.com/go-openapi/validate/validator.go
index 38cdb9bb6..c083aecc9 100644
--- a/vendor/github.com/go-openapi/validate/validator.go
+++ b/vendor/github.com/go-openapi/validate/validator.go
@@ -39,20 +39,31 @@ type itemsValidator struct {
root interface{}
path string
in string
- validators []valueValidator
+ validators [6]valueValidator
KnownFormats strfmt.Registry
+ Options *SchemaValidatorOptions
}
-func newItemsValidator(path, in string, items *spec.Items, root interface{}, formats strfmt.Registry) *itemsValidator {
- iv := &itemsValidator{path: path, in: in, items: items, root: root, KnownFormats: formats}
- iv.validators = []valueValidator{
- &typeValidator{
- Type: spec.StringOrArray([]string{items.Type}),
- Nullable: items.Nullable,
- Format: items.Format,
- In: in,
- Path: path,
- },
+func newItemsValidator(path, in string, items *spec.Items, root interface{}, formats strfmt.Registry, opts *SchemaValidatorOptions) *itemsValidator {
+ if opts == nil {
+ opts = new(SchemaValidatorOptions)
+ }
+
+ var iv *itemsValidator
+ if opts.recycleValidators {
+ iv = pools.poolOfItemsValidators.BorrowValidator()
+ } else {
+ iv = new(itemsValidator)
+ }
+
+ iv.path = path
+ iv.in = in
+ iv.items = items
+ iv.root = root
+ iv.KnownFormats = formats
+ iv.Options = opts
+ iv.validators = [6]valueValidator{
+ iv.typeValidator(),
iv.stringValidator(),
iv.formatValidator(),
iv.numberValidator(),
@@ -63,77 +74,152 @@ func newItemsValidator(path, in string, items *spec.Items, root interface{}, for
}
func (i *itemsValidator) Validate(index int, data interface{}) *Result {
+ if i.Options.recycleValidators {
+ defer func() {
+ i.redeemChildren()
+ i.redeem()
+ }()
+ }
+
tpe := reflect.TypeOf(data)
kind := tpe.Kind()
- mainResult := new(Result)
+ var result *Result
+ if i.Options.recycleResult {
+ result = pools.poolOfResults.BorrowResult()
+ } else {
+ result = new(Result)
+ }
+
path := fmt.Sprintf("%s.%d", i.path, index)
- for _, validator := range i.validators {
+ for idx, validator := range i.validators {
+ if !validator.Applies(i.root, kind) {
+ if i.Options.recycleValidators {
+ // Validate won't be called, so relinquish this validator
+ if redeemableChildren, ok := validator.(interface{ redeemChildren() }); ok {
+ redeemableChildren.redeemChildren()
+ }
+ if redeemable, ok := validator.(interface{ redeem() }); ok {
+ redeemable.redeem()
+ }
+ i.validators[idx] = nil // prevents further (unsafe) usage
+ }
+
+ continue
+ }
+
validator.SetPath(path)
- if validator.Applies(i.root, kind) {
- result := validator.Validate(data)
- mainResult.Merge(result)
- mainResult.Inc()
- if result != nil && result.HasErrors() {
- return mainResult
+ err := validator.Validate(data)
+ if i.Options.recycleValidators {
+ i.validators[idx] = nil // prevents further (unsafe) usage
+ }
+ if err != nil {
+ result.Inc()
+ if err.HasErrors() {
+ result.Merge(err)
+
+ break
}
+
+ result.Merge(err)
}
}
- return mainResult
+
+ return result
+}
+
+func (i *itemsValidator) typeValidator() valueValidator {
+ return newTypeValidator(
+ i.path,
+ i.in,
+ spec.StringOrArray([]string{i.items.Type}),
+ i.items.Nullable,
+ i.items.Format,
+ i.Options,
+ )
}
func (i *itemsValidator) commonValidator() valueValidator {
- return &basicCommonValidator{
- In: i.in,
- Default: i.items.Default,
- Enum: i.items.Enum,
- }
+ return newBasicCommonValidator(
+ "",
+ i.in,
+ i.items.Default,
+ i.items.Enum,
+ i.Options,
+ )
}
func (i *itemsValidator) sliceValidator() valueValidator {
- return &basicSliceValidator{
- In: i.in,
- Default: i.items.Default,
- MaxItems: i.items.MaxItems,
- MinItems: i.items.MinItems,
- UniqueItems: i.items.UniqueItems,
- Source: i.root,
- Items: i.items.Items,
- KnownFormats: i.KnownFormats,
- }
+ return newBasicSliceValidator(
+ "",
+ i.in,
+ i.items.Default,
+ i.items.MaxItems,
+ i.items.MinItems,
+ i.items.UniqueItems,
+ i.items.Items,
+ i.root,
+ i.KnownFormats,
+ i.Options,
+ )
}
func (i *itemsValidator) numberValidator() valueValidator {
- return &numberValidator{
- In: i.in,
- Default: i.items.Default,
- MultipleOf: i.items.MultipleOf,
- Maximum: i.items.Maximum,
- ExclusiveMaximum: i.items.ExclusiveMaximum,
- Minimum: i.items.Minimum,
- ExclusiveMinimum: i.items.ExclusiveMinimum,
- Type: i.items.Type,
- Format: i.items.Format,
- }
+ return newNumberValidator(
+ "",
+ i.in,
+ i.items.Default,
+ i.items.MultipleOf,
+ i.items.Maximum,
+ i.items.ExclusiveMaximum,
+ i.items.Minimum,
+ i.items.ExclusiveMinimum,
+ i.items.Type,
+ i.items.Format,
+ i.Options,
+ )
}
func (i *itemsValidator) stringValidator() valueValidator {
- return &stringValidator{
- In: i.in,
- Default: i.items.Default,
- MaxLength: i.items.MaxLength,
- MinLength: i.items.MinLength,
- Pattern: i.items.Pattern,
- AllowEmptyValue: false,
- }
+ return newStringValidator(
+ "",
+ i.in,
+ i.items.Default,
+ false, // Required
+ false, // AllowEmpty
+ i.items.MaxLength,
+ i.items.MinLength,
+ i.items.Pattern,
+ i.Options,
+ )
}
func (i *itemsValidator) formatValidator() valueValidator {
- return &formatValidator{
- In: i.in,
- //Default: i.items.Default,
- Format: i.items.Format,
- KnownFormats: i.KnownFormats,
+ return newFormatValidator(
+ "",
+ i.in,
+ i.items.Format,
+ i.KnownFormats,
+ i.Options,
+ )
+}
+
+func (i *itemsValidator) redeem() {
+ pools.poolOfItemsValidators.RedeemValidator(i)
+}
+
+func (i *itemsValidator) redeemChildren() {
+ for idx, validator := range i.validators {
+ if validator == nil {
+ continue
+ }
+ if redeemableChildren, ok := validator.(interface{ redeemChildren() }); ok {
+ redeemableChildren.redeemChildren()
+ }
+ if redeemable, ok := validator.(interface{ redeem() }); ok {
+ redeemable.redeem()
+ }
+ i.validators[idx] = nil // free up allocated children if not in pool
}
}
@@ -142,265 +228,501 @@ type basicCommonValidator struct {
In string
Default interface{}
Enum []interface{}
+ Options *SchemaValidatorOptions
+}
+
+func newBasicCommonValidator(path, in string, def interface{}, enum []interface{}, opts *SchemaValidatorOptions) *basicCommonValidator {
+ if opts == nil {
+ opts = new(SchemaValidatorOptions)
+ }
+
+ var b *basicCommonValidator
+ if opts.recycleValidators {
+ b = pools.poolOfBasicCommonValidators.BorrowValidator()
+ } else {
+ b = new(basicCommonValidator)
+ }
+
+ b.Path = path
+ b.In = in
+ b.Default = def
+ b.Enum = enum
+ b.Options = opts
+
+ return b
}
func (b *basicCommonValidator) SetPath(path string) {
b.Path = path
}
-func (b *basicCommonValidator) Applies(source interface{}, kind reflect.Kind) bool {
+func (b *basicCommonValidator) Applies(source interface{}, _ reflect.Kind) bool {
switch source.(type) {
case *spec.Parameter, *spec.Schema, *spec.Header:
return true
+ default:
+ return false
}
- return false
}
func (b *basicCommonValidator) Validate(data interface{}) (res *Result) {
- if len(b.Enum) > 0 {
- for _, enumValue := range b.Enum {
- actualType := reflect.TypeOf(enumValue)
- if actualType != nil { // Safeguard
- expectedValue := reflect.ValueOf(data)
- if expectedValue.IsValid() && expectedValue.Type().ConvertibleTo(actualType) {
- if reflect.DeepEqual(expectedValue.Convert(actualType).Interface(), enumValue) {
- return nil
- }
- }
- }
+ if b.Options.recycleValidators {
+ defer func() {
+ b.redeem()
+ }()
+ }
+
+ if len(b.Enum) == 0 {
+ return nil
+ }
+
+ for _, enumValue := range b.Enum {
+ actualType := reflect.TypeOf(enumValue)
+ if actualType == nil { // Safeguard
+ continue
+ }
+
+ expectedValue := reflect.ValueOf(data)
+ if expectedValue.IsValid() &&
+ expectedValue.Type().ConvertibleTo(actualType) &&
+ reflect.DeepEqual(expectedValue.Convert(actualType).Interface(), enumValue) {
+ return nil
}
- return errorHelp.sErr(errors.EnumFail(b.Path, b.In, data, b.Enum))
}
- return nil
+
+ return errorHelp.sErr(errors.EnumFail(b.Path, b.In, data, b.Enum), b.Options.recycleResult)
+}
+
+func (b *basicCommonValidator) redeem() {
+ pools.poolOfBasicCommonValidators.RedeemValidator(b)
}
// A HeaderValidator has very limited subset of validations to apply
type HeaderValidator struct {
name string
header *spec.Header
- validators []valueValidator
+ validators [6]valueValidator
KnownFormats strfmt.Registry
+ Options *SchemaValidatorOptions
}
// NewHeaderValidator creates a new header validator object
-func NewHeaderValidator(name string, header *spec.Header, formats strfmt.Registry) *HeaderValidator {
- p := &HeaderValidator{name: name, header: header, KnownFormats: formats}
- p.validators = []valueValidator{
- &typeValidator{
- Type: spec.StringOrArray([]string{header.Type}),
- Nullable: header.Nullable,
- Format: header.Format,
- In: "header",
- Path: name,
- },
+func NewHeaderValidator(name string, header *spec.Header, formats strfmt.Registry, options ...Option) *HeaderValidator {
+ opts := new(SchemaValidatorOptions)
+ for _, o := range options {
+ o(opts)
+ }
+
+ return newHeaderValidator(name, header, formats, opts)
+}
+
+func newHeaderValidator(name string, header *spec.Header, formats strfmt.Registry, opts *SchemaValidatorOptions) *HeaderValidator {
+ if opts == nil {
+ opts = new(SchemaValidatorOptions)
+ }
+
+ var p *HeaderValidator
+ if opts.recycleValidators {
+ p = pools.poolOfHeaderValidators.BorrowValidator()
+ } else {
+ p = new(HeaderValidator)
+ }
+
+ p.name = name
+ p.header = header
+ p.KnownFormats = formats
+ p.Options = opts
+ p.validators = [6]valueValidator{
+ newTypeValidator(
+ name,
+ "header",
+ spec.StringOrArray([]string{header.Type}),
+ header.Nullable,
+ header.Format,
+ p.Options,
+ ),
p.stringValidator(),
p.formatValidator(),
p.numberValidator(),
p.sliceValidator(),
p.commonValidator(),
}
+
return p
}
// Validate the value of the header against its schema
func (p *HeaderValidator) Validate(data interface{}) *Result {
- result := new(Result)
+ if p.Options.recycleValidators {
+ defer func() {
+ p.redeemChildren()
+ p.redeem()
+ }()
+ }
+
+ if data == nil {
+ return nil
+ }
+
+ var result *Result
+ if p.Options.recycleResult {
+ result = pools.poolOfResults.BorrowResult()
+ } else {
+ result = new(Result)
+ }
+
tpe := reflect.TypeOf(data)
kind := tpe.Kind()
- for _, validator := range p.validators {
- if validator.Applies(p.header, kind) {
- if err := validator.Validate(data); err != nil {
- result.Merge(err)
- if err.HasErrors() {
- return result
+ for idx, validator := range p.validators {
+ if !validator.Applies(p.header, kind) {
+ if p.Options.recycleValidators {
+ // Validate won't be called, so relinquish this validator
+ if redeemableChildren, ok := validator.(interface{ redeemChildren() }); ok {
+ redeemableChildren.redeemChildren()
+ }
+ if redeemable, ok := validator.(interface{ redeem() }); ok {
+ redeemable.redeem()
}
+ p.validators[idx] = nil // prevents further (unsafe) usage
}
+
+ continue
+ }
+
+ err := validator.Validate(data)
+ if p.Options.recycleValidators {
+ p.validators[idx] = nil // prevents further (unsafe) usage
+ }
+ if err != nil {
+ if err.HasErrors() {
+ result.Merge(err)
+ break
+ }
+ result.Merge(err)
}
}
- return nil
+
+ return result
}
func (p *HeaderValidator) commonValidator() valueValidator {
- return &basicCommonValidator{
- Path: p.name,
- In: "response",
- Default: p.header.Default,
- Enum: p.header.Enum,
- }
+ return newBasicCommonValidator(
+ p.name,
+ "response",
+ p.header.Default,
+ p.header.Enum,
+ p.Options,
+ )
}
func (p *HeaderValidator) sliceValidator() valueValidator {
- return &basicSliceValidator{
- Path: p.name,
- In: "response",
- Default: p.header.Default,
- MaxItems: p.header.MaxItems,
- MinItems: p.header.MinItems,
- UniqueItems: p.header.UniqueItems,
- Items: p.header.Items,
- Source: p.header,
- KnownFormats: p.KnownFormats,
- }
+ return newBasicSliceValidator(
+ p.name,
+ "response",
+ p.header.Default,
+ p.header.MaxItems,
+ p.header.MinItems,
+ p.header.UniqueItems,
+ p.header.Items,
+ p.header,
+ p.KnownFormats,
+ p.Options,
+ )
}
func (p *HeaderValidator) numberValidator() valueValidator {
- return &numberValidator{
- Path: p.name,
- In: "response",
- Default: p.header.Default,
- MultipleOf: p.header.MultipleOf,
- Maximum: p.header.Maximum,
- ExclusiveMaximum: p.header.ExclusiveMaximum,
- Minimum: p.header.Minimum,
- ExclusiveMinimum: p.header.ExclusiveMinimum,
- Type: p.header.Type,
- Format: p.header.Format,
- }
+ return newNumberValidator(
+ p.name,
+ "response",
+ p.header.Default,
+ p.header.MultipleOf,
+ p.header.Maximum,
+ p.header.ExclusiveMaximum,
+ p.header.Minimum,
+ p.header.ExclusiveMinimum,
+ p.header.Type,
+ p.header.Format,
+ p.Options,
+ )
}
func (p *HeaderValidator) stringValidator() valueValidator {
- return &stringValidator{
- Path: p.name,
- In: "response",
- Default: p.header.Default,
- Required: true,
- MaxLength: p.header.MaxLength,
- MinLength: p.header.MinLength,
- Pattern: p.header.Pattern,
- AllowEmptyValue: false,
- }
+ return newStringValidator(
+ p.name,
+ "response",
+ p.header.Default,
+ true,
+ false,
+ p.header.MaxLength,
+ p.header.MinLength,
+ p.header.Pattern,
+ p.Options,
+ )
}
func (p *HeaderValidator) formatValidator() valueValidator {
- return &formatValidator{
- Path: p.name,
- In: "response",
- //Default: p.header.Default,
- Format: p.header.Format,
- KnownFormats: p.KnownFormats,
+ return newFormatValidator(
+ p.name,
+ "response",
+ p.header.Format,
+ p.KnownFormats,
+ p.Options,
+ )
+}
+
+func (p *HeaderValidator) redeem() {
+ pools.poolOfHeaderValidators.RedeemValidator(p)
+}
+
+func (p *HeaderValidator) redeemChildren() {
+ for idx, validator := range p.validators {
+ if validator == nil {
+ continue
+ }
+ if redeemableChildren, ok := validator.(interface{ redeemChildren() }); ok {
+ redeemableChildren.redeemChildren()
+ }
+ if redeemable, ok := validator.(interface{ redeem() }); ok {
+ redeemable.redeem()
+ }
+ p.validators[idx] = nil // free up allocated children if not in pool
}
}
// A ParamValidator has very limited subset of validations to apply
type ParamValidator struct {
param *spec.Parameter
- validators []valueValidator
+ validators [6]valueValidator
KnownFormats strfmt.Registry
+ Options *SchemaValidatorOptions
}
// NewParamValidator creates a new param validator object
-func NewParamValidator(param *spec.Parameter, formats strfmt.Registry) *ParamValidator {
- p := &ParamValidator{param: param, KnownFormats: formats}
- p.validators = []valueValidator{
- &typeValidator{
- Type: spec.StringOrArray([]string{param.Type}),
- Nullable: param.Nullable,
- Format: param.Format,
- In: param.In,
- Path: param.Name,
- },
+func NewParamValidator(param *spec.Parameter, formats strfmt.Registry, options ...Option) *ParamValidator {
+ opts := new(SchemaValidatorOptions)
+ for _, o := range options {
+ o(opts)
+ }
+
+ return newParamValidator(param, formats, opts)
+}
+
+func newParamValidator(param *spec.Parameter, formats strfmt.Registry, opts *SchemaValidatorOptions) *ParamValidator {
+ if opts == nil {
+ opts = new(SchemaValidatorOptions)
+ }
+
+ var p *ParamValidator
+ if opts.recycleValidators {
+ p = pools.poolOfParamValidators.BorrowValidator()
+ } else {
+ p = new(ParamValidator)
+ }
+
+ p.param = param
+ p.KnownFormats = formats
+ p.Options = opts
+ p.validators = [6]valueValidator{
+ newTypeValidator(
+ param.Name,
+ param.In,
+ spec.StringOrArray([]string{param.Type}),
+ param.Nullable,
+ param.Format,
+ p.Options,
+ ),
p.stringValidator(),
p.formatValidator(),
p.numberValidator(),
p.sliceValidator(),
p.commonValidator(),
}
+
return p
}
// Validate the data against the description of the parameter
func (p *ParamValidator) Validate(data interface{}) *Result {
- result := new(Result)
+ if data == nil {
+ return nil
+ }
+
+ var result *Result
+ if p.Options.recycleResult {
+ result = pools.poolOfResults.BorrowResult()
+ } else {
+ result = new(Result)
+ }
+
tpe := reflect.TypeOf(data)
kind := tpe.Kind()
+ if p.Options.recycleValidators {
+ defer func() {
+ p.redeemChildren()
+ p.redeem()
+ }()
+ }
+
// TODO: validate type
- for _, validator := range p.validators {
- if validator.Applies(p.param, kind) {
- if err := validator.Validate(data); err != nil {
- result.Merge(err)
- if err.HasErrors() {
- return result
+ for idx, validator := range p.validators {
+ if !validator.Applies(p.param, kind) {
+ if p.Options.recycleValidators {
+ // Validate won't be called, so relinquish this validator
+ if redeemableChildren, ok := validator.(interface{ redeemChildren() }); ok {
+ redeemableChildren.redeemChildren()
+ }
+ if redeemable, ok := validator.(interface{ redeem() }); ok {
+ redeemable.redeem()
}
+ p.validators[idx] = nil // prevents further (unsafe) usage
}
+
+ continue
+ }
+
+ err := validator.Validate(data)
+ if p.Options.recycleValidators {
+ p.validators[idx] = nil // prevents further (unsafe) usage
+ }
+ if err != nil {
+ if err.HasErrors() {
+ result.Merge(err)
+ break
+ }
+ result.Merge(err)
}
}
- return nil
+
+ return result
}
func (p *ParamValidator) commonValidator() valueValidator {
- return &basicCommonValidator{
- Path: p.param.Name,
- In: p.param.In,
- Default: p.param.Default,
- Enum: p.param.Enum,
- }
+ return newBasicCommonValidator(
+ p.param.Name,
+ p.param.In,
+ p.param.Default,
+ p.param.Enum,
+ p.Options,
+ )
}
func (p *ParamValidator) sliceValidator() valueValidator {
- return &basicSliceValidator{
- Path: p.param.Name,
- In: p.param.In,
- Default: p.param.Default,
- MaxItems: p.param.MaxItems,
- MinItems: p.param.MinItems,
- UniqueItems: p.param.UniqueItems,
- Items: p.param.Items,
- Source: p.param,
- KnownFormats: p.KnownFormats,
- }
+ return newBasicSliceValidator(
+ p.param.Name,
+ p.param.In,
+ p.param.Default,
+ p.param.MaxItems,
+ p.param.MinItems,
+ p.param.UniqueItems,
+ p.param.Items,
+ p.param,
+ p.KnownFormats,
+ p.Options,
+ )
}
func (p *ParamValidator) numberValidator() valueValidator {
- return &numberValidator{
- Path: p.param.Name,
- In: p.param.In,
- Default: p.param.Default,
- MultipleOf: p.param.MultipleOf,
- Maximum: p.param.Maximum,
- ExclusiveMaximum: p.param.ExclusiveMaximum,
- Minimum: p.param.Minimum,
- ExclusiveMinimum: p.param.ExclusiveMinimum,
- Type: p.param.Type,
- Format: p.param.Format,
- }
+ return newNumberValidator(
+ p.param.Name,
+ p.param.In,
+ p.param.Default,
+ p.param.MultipleOf,
+ p.param.Maximum,
+ p.param.ExclusiveMaximum,
+ p.param.Minimum,
+ p.param.ExclusiveMinimum,
+ p.param.Type,
+ p.param.Format,
+ p.Options,
+ )
}
func (p *ParamValidator) stringValidator() valueValidator {
- return &stringValidator{
- Path: p.param.Name,
- In: p.param.In,
- Default: p.param.Default,
- AllowEmptyValue: p.param.AllowEmptyValue,
- Required: p.param.Required,
- MaxLength: p.param.MaxLength,
- MinLength: p.param.MinLength,
- Pattern: p.param.Pattern,
- }
+ return newStringValidator(
+ p.param.Name,
+ p.param.In,
+ p.param.Default,
+ p.param.Required,
+ p.param.AllowEmptyValue,
+ p.param.MaxLength,
+ p.param.MinLength,
+ p.param.Pattern,
+ p.Options,
+ )
}
func (p *ParamValidator) formatValidator() valueValidator {
- return &formatValidator{
- Path: p.param.Name,
- In: p.param.In,
- //Default: p.param.Default,
- Format: p.param.Format,
- KnownFormats: p.KnownFormats,
+ return newFormatValidator(
+ p.param.Name,
+ p.param.In,
+ p.param.Format,
+ p.KnownFormats,
+ p.Options,
+ )
+}
+
+func (p *ParamValidator) redeem() {
+ pools.poolOfParamValidators.RedeemValidator(p)
+}
+
+func (p *ParamValidator) redeemChildren() {
+ for idx, validator := range p.validators {
+ if validator == nil {
+ continue
+ }
+ if redeemableChildren, ok := validator.(interface{ redeemChildren() }); ok {
+ redeemableChildren.redeemChildren()
+ }
+ if redeemable, ok := validator.(interface{ redeem() }); ok {
+ redeemable.redeem()
+ }
+ p.validators[idx] = nil // free up allocated children if not in pool
}
}
type basicSliceValidator struct {
- Path string
- In string
- Default interface{}
- MaxItems *int64
- MinItems *int64
- UniqueItems bool
- Items *spec.Items
- Source interface{}
- itemsValidator *itemsValidator
- KnownFormats strfmt.Registry
+ Path string
+ In string
+ Default interface{}
+ MaxItems *int64
+ MinItems *int64
+ UniqueItems bool
+ Items *spec.Items
+ Source interface{}
+ KnownFormats strfmt.Registry
+ Options *SchemaValidatorOptions
+}
+
+func newBasicSliceValidator(
+ path, in string,
+ def interface{}, maxItems, minItems *int64, uniqueItems bool, items *spec.Items,
+ source interface{}, formats strfmt.Registry,
+ opts *SchemaValidatorOptions) *basicSliceValidator {
+ if opts == nil {
+ opts = new(SchemaValidatorOptions)
+ }
+
+ var s *basicSliceValidator
+ if opts.recycleValidators {
+ s = pools.poolOfBasicSliceValidators.BorrowValidator()
+ } else {
+ s = new(basicSliceValidator)
+ }
+
+ s.Path = path
+ s.In = in
+ s.Default = def
+ s.MaxItems = maxItems
+ s.MinItems = minItems
+ s.UniqueItems = uniqueItems
+ s.Items = items
+ s.Source = source
+ s.KnownFormats = formats
+ s.Options = opts
+
+ return s
}
func (s *basicSliceValidator) SetPath(path string) {
@@ -411,60 +733,61 @@ func (s *basicSliceValidator) Applies(source interface{}, kind reflect.Kind) boo
switch source.(type) {
case *spec.Parameter, *spec.Items, *spec.Header:
return kind == reflect.Slice
+ default:
+ return false
}
- return false
}
func (s *basicSliceValidator) Validate(data interface{}) *Result {
+ if s.Options.recycleValidators {
+ defer func() {
+ s.redeem()
+ }()
+ }
val := reflect.ValueOf(data)
size := int64(val.Len())
if s.MinItems != nil {
if err := MinItems(s.Path, s.In, size, *s.MinItems); err != nil {
- return errorHelp.sErr(err)
+ return errorHelp.sErr(err, s.Options.recycleResult)
}
}
if s.MaxItems != nil {
if err := MaxItems(s.Path, s.In, size, *s.MaxItems); err != nil {
- return errorHelp.sErr(err)
+ return errorHelp.sErr(err, s.Options.recycleResult)
}
}
if s.UniqueItems {
if err := UniqueItems(s.Path, s.In, data); err != nil {
- return errorHelp.sErr(err)
+ return errorHelp.sErr(err, s.Options.recycleResult)
}
}
- if s.itemsValidator == nil && s.Items != nil {
- s.itemsValidator = newItemsValidator(s.Path, s.In, s.Items, s.Source, s.KnownFormats)
+ if s.Items == nil {
+ return nil
}
- if s.itemsValidator != nil {
- for i := 0; i < int(size); i++ {
- ele := val.Index(i)
- if err := s.itemsValidator.Validate(i, ele.Interface()); err != nil && err.HasErrors() {
+ for i := 0; i < int(size); i++ {
+ itemsValidator := newItemsValidator(s.Path, s.In, s.Items, s.Source, s.KnownFormats, s.Options)
+ ele := val.Index(i)
+ if err := itemsValidator.Validate(i, ele.Interface()); err != nil {
+ if err.HasErrors() {
return err
}
+ if err.wantsRedeemOnMerge {
+ pools.poolOfResults.RedeemResult(err)
+ }
}
}
+
return nil
}
-/* unused
-func (s *basicSliceValidator) hasDuplicates(value reflect.Value, size int) bool {
- dict := make(map[interface{}]struct{})
- for i := 0; i < size; i++ {
- ele := value.Index(i)
- if _, ok := dict[ele.Interface()]; ok {
- return true
- }
- dict[ele.Interface()] = struct{}{}
- }
- return false
+func (s *basicSliceValidator) redeem() {
+ pools.poolOfBasicSliceValidators.RedeemValidator(s)
}
-*/
type numberValidator struct {
Path string
@@ -476,8 +799,40 @@ type numberValidator struct {
Minimum *float64
ExclusiveMinimum bool
// Allows for more accurate behavior regarding integers
- Type string
- Format string
+ Type string
+ Format string
+ Options *SchemaValidatorOptions
+}
+
+func newNumberValidator(
+ path, in string, def interface{},
+ multipleOf, maximum *float64, exclusiveMaximum bool, minimum *float64, exclusiveMinimum bool,
+ typ, format string,
+ opts *SchemaValidatorOptions) *numberValidator {
+ if opts == nil {
+ opts = new(SchemaValidatorOptions)
+ }
+
+ var n *numberValidator
+ if opts.recycleValidators {
+ n = pools.poolOfNumberValidators.BorrowValidator()
+ } else {
+ n = new(numberValidator)
+ }
+
+ n.Path = path
+ n.In = in
+ n.Default = def
+ n.MultipleOf = multipleOf
+ n.Maximum = maximum
+ n.ExclusiveMaximum = exclusiveMaximum
+ n.Minimum = minimum
+ n.ExclusiveMinimum = exclusiveMinimum
+ n.Type = typ
+ n.Format = format
+ n.Options = opts
+
+ return n
}
func (n *numberValidator) SetPath(path string) {
@@ -489,12 +844,10 @@ func (n *numberValidator) Applies(source interface{}, kind reflect.Kind) bool {
case *spec.Parameter, *spec.Schema, *spec.Items, *spec.Header:
isInt := kind >= reflect.Int && kind <= reflect.Uint64
isFloat := kind == reflect.Float32 || kind == reflect.Float64
- r := isInt || isFloat
- debugLog("schema props validator for %q applies %t for %T (kind: %v) isInt=%t, isFloat=%t\n", n.Path, r, source, kind, isInt, isFloat)
- return r
+ return isInt || isFloat
+ default:
+ return false
}
- debugLog("schema props validator for %q applies %t for %T (kind: %v)\n", n.Path, false, source, kind)
- return false
}
// Validate provides a validator for generic JSON numbers,
@@ -519,11 +872,18 @@ func (n *numberValidator) Applies(source interface{}, kind reflect.Kind) bool {
//
// TODO: default boundaries with MAX_SAFE_INTEGER are not checked (specific to json.Number?)
func (n *numberValidator) Validate(val interface{}) *Result {
- res := new(Result)
+ if n.Options.recycleValidators {
+ defer func() {
+ n.redeem()
+ }()
+ }
- resMultiple := new(Result)
- resMinimum := new(Result)
- resMaximum := new(Result)
+ var res, resMultiple, resMinimum, resMaximum *Result
+ if n.Options.recycleResult {
+ res = pools.poolOfResults.BorrowResult()
+ } else {
+ res = new(Result)
+ }
// Used only to attempt to validate constraint on value,
// even though value or constraint specified do not match type and format
@@ -533,68 +893,106 @@ func (n *numberValidator) Validate(val interface{}) *Result {
res.AddErrors(IsValueValidAgainstRange(val, n.Type, n.Format, "Checked", n.Path))
if n.MultipleOf != nil {
+ resMultiple = pools.poolOfResults.BorrowResult()
+
// 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))
if resMultiple.IsValid() {
// Constraint validated with compatible types
if err := MultipleOfNativeType(n.Path, n.In, val, *n.MultipleOf); err != nil {
- resMultiple.Merge(errorHelp.sErr(err))
+ resMultiple.Merge(errorHelp.sErr(err, n.Options.recycleResult))
}
} else {
// Constraint nevertheless validated, converted as general number
if err := MultipleOf(n.Path, n.In, data, *n.MultipleOf); err != nil {
- resMultiple.Merge(errorHelp.sErr(err))
+ resMultiple.Merge(errorHelp.sErr(err, n.Options.recycleResult))
}
}
}
- // nolint: dupl
if n.Maximum != nil {
+ resMaximum = pools.poolOfResults.BorrowResult()
+
// 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))
if resMaximum.IsValid() {
// Constraint validated with compatible types
if err := MaximumNativeType(n.Path, n.In, val, *n.Maximum, n.ExclusiveMaximum); err != nil {
- resMaximum.Merge(errorHelp.sErr(err))
+ resMaximum.Merge(errorHelp.sErr(err, n.Options.recycleResult))
}
} else {
// Constraint nevertheless validated, converted as general number
if err := Maximum(n.Path, n.In, data, *n.Maximum, n.ExclusiveMaximum); err != nil {
- resMaximum.Merge(errorHelp.sErr(err))
+ resMaximum.Merge(errorHelp.sErr(err, n.Options.recycleResult))
}
}
}
- // nolint: dupl
if n.Minimum != nil {
+ resMinimum = pools.poolOfResults.BorrowResult()
+
// 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))
if resMinimum.IsValid() {
// Constraint validated with compatible types
if err := MinimumNativeType(n.Path, n.In, val, *n.Minimum, n.ExclusiveMinimum); err != nil {
- resMinimum.Merge(errorHelp.sErr(err))
+ resMinimum.Merge(errorHelp.sErr(err, n.Options.recycleResult))
}
} else {
// Constraint nevertheless validated, converted as general number
if err := Minimum(n.Path, n.In, data, *n.Minimum, n.ExclusiveMinimum); err != nil {
- resMinimum.Merge(errorHelp.sErr(err))
+ resMinimum.Merge(errorHelp.sErr(err, n.Options.recycleResult))
}
}
}
res.Merge(resMultiple, resMinimum, resMaximum)
res.Inc()
+
return res
}
+func (n *numberValidator) redeem() {
+ pools.poolOfNumberValidators.RedeemValidator(n)
+}
+
type stringValidator struct {
+ Path string
+ In string
Default interface{}
Required bool
AllowEmptyValue bool
MaxLength *int64
MinLength *int64
Pattern string
- Path string
- In string
+ Options *SchemaValidatorOptions
+}
+
+func newStringValidator(
+ path, in string,
+ def interface{}, required, allowEmpty bool, maxLength, minLength *int64, pattern string,
+ opts *SchemaValidatorOptions) *stringValidator {
+ if opts == nil {
+ opts = new(SchemaValidatorOptions)
+ }
+
+ var s *stringValidator
+ if opts.recycleValidators {
+ s = pools.poolOfStringValidators.BorrowValidator()
+ } else {
+ s = new(stringValidator)
+ }
+
+ s.Path = path
+ s.In = in
+ s.Default = def
+ s.Required = required
+ s.AllowEmptyValue = allowEmpty
+ s.MaxLength = maxLength
+ s.MinLength = minLength
+ s.Pattern = pattern
+ s.Options = opts
+
+ return s
}
func (s *stringValidator) SetPath(path string) {
@@ -604,42 +1002,50 @@ func (s *stringValidator) SetPath(path string) {
func (s *stringValidator) Applies(source interface{}, kind reflect.Kind) bool {
switch source.(type) {
case *spec.Parameter, *spec.Schema, *spec.Items, *spec.Header:
- r := kind == reflect.String
- debugLog("string validator for %q applies %t for %T (kind: %v)\n", s.Path, r, source, kind)
- return r
+ return kind == reflect.String
+ default:
+ return false
}
- debugLog("string validator for %q applies %t for %T (kind: %v)\n", s.Path, false, source, kind)
- return false
}
func (s *stringValidator) Validate(val interface{}) *Result {
+ if s.Options.recycleValidators {
+ defer func() {
+ s.redeem()
+ }()
+ }
+
data, ok := val.(string)
if !ok {
- return errorHelp.sErr(errors.InvalidType(s.Path, s.In, stringType, val))
+ return errorHelp.sErr(errors.InvalidType(s.Path, s.In, stringType, val), s.Options.recycleResult)
}
if s.Required && !s.AllowEmptyValue && (s.Default == nil || s.Default == "") {
if err := RequiredString(s.Path, s.In, data); err != nil {
- return errorHelp.sErr(err)
+ return errorHelp.sErr(err, s.Options.recycleResult)
}
}
if s.MaxLength != nil {
if err := MaxLength(s.Path, s.In, data, *s.MaxLength); err != nil {
- return errorHelp.sErr(err)
+ return errorHelp.sErr(err, s.Options.recycleResult)
}
}
if s.MinLength != nil {
if err := MinLength(s.Path, s.In, data, *s.MinLength); err != nil {
- return errorHelp.sErr(err)
+ return errorHelp.sErr(err, s.Options.recycleResult)
}
}
if s.Pattern != "" {
if err := Pattern(s.Path, s.In, data, s.Pattern); err != nil {
- return errorHelp.sErr(err)
+ return errorHelp.sErr(err, s.Options.recycleResult)
}
}
return nil
}
+
+func (s *stringValidator) redeem() {
+ pools.poolOfStringValidators.RedeemValidator(s)
+}
diff --git a/vendor/github.com/go-openapi/validate/values.go b/vendor/github.com/go-openapi/validate/values.go
index e7ad8c103..5f6f5ee61 100644
--- a/vendor/github.com/go-openapi/validate/values.go
+++ b/vendor/github.com/go-openapi/validate/values.go
@@ -120,7 +120,7 @@ func UniqueItems(path, in string, data interface{}) *errors.Validation {
// MinLength validates a string for minimum length
func MinLength(path, in, data string, minLength int64) *errors.Validation {
- strLen := int64(utf8.RuneCount([]byte(data)))
+ strLen := int64(utf8.RuneCountInString(data))
if strLen < minLength {
return errors.TooShort(path, in, minLength, data)
}
@@ -129,7 +129,7 @@ func MinLength(path, in, data string, minLength int64) *errors.Validation {
// MaxLength validates a string for maximum length
func MaxLength(path, in, data string, maxLength int64) *errors.Validation {
- strLen := int64(utf8.RuneCount([]byte(data)))
+ strLen := int64(utf8.RuneCountInString(data))
if strLen > maxLength {
return errors.TooLong(path, in, maxLength, data)
}
@@ -315,7 +315,7 @@ func FormatOf(path, in, format, data string, registry strfmt.Registry) *errors.V
// TODO: Normally, a JSON MAX_SAFE_INTEGER check would ensure conversion remains loss-free
func MaximumNativeType(path, in string, val interface{}, max float64, exclusive bool) *errors.Validation {
kind := reflect.ValueOf(val).Type().Kind()
- switch kind {
+ switch kind { //nolint:exhaustive
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
value := valueHelp.asInt64(val)
return MaximumInt(path, in, value, int64(max), exclusive)
@@ -345,7 +345,7 @@ func MaximumNativeType(path, in string, val interface{}, max float64, exclusive
// TODO: Normally, a JSON MAX_SAFE_INTEGER check would ensure conversion remains loss-free
func MinimumNativeType(path, in string, val interface{}, min float64, exclusive bool) *errors.Validation {
kind := reflect.ValueOf(val).Type().Kind()
- switch kind {
+ switch kind { //nolint:exhaustive
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
value := valueHelp.asInt64(val)
return MinimumInt(path, in, value, int64(min), exclusive)
@@ -375,7 +375,7 @@ func MinimumNativeType(path, in string, val interface{}, min float64, exclusive
// TODO: Normally, a JSON MAX_SAFE_INTEGER check would ensure conversion remains loss-free
func MultipleOfNativeType(path, in string, val interface{}, multipleOf float64) *errors.Validation {
kind := reflect.ValueOf(val).Type().Kind()
- switch kind {
+ switch kind { //nolint:exhaustive
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
value := valueHelp.asInt64(val)
return MultipleOfInt(path, in, value, int64(multipleOf))
@@ -399,7 +399,7 @@ func IsValueValidAgainstRange(val interface{}, typeName, format, prefix, path st
// What is the string representation of val
var stringRep string
- switch kind {
+ switch kind { //nolint:exhaustive
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
stringRep = swag.FormatUint64(valueHelp.asUint64(val))
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: