fix: error handling and vendor
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
This commit is contained in:
26
vendor/github.com/go-openapi/validate/.editorconfig
generated
vendored
Normal file
26
vendor/github.com/go-openapi/validate/.editorconfig
generated
vendored
Normal file
@ -0,0 +1,26 @@
|
||||
# top-most EditorConfig file
|
||||
root = true
|
||||
|
||||
# Unix-style newlines with a newline ending every file
|
||||
[*]
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
# Set default charset
|
||||
[*.{js,py,go,scala,rb,java,html,css,less,sass,md}]
|
||||
charset = utf-8
|
||||
|
||||
# Tab indentation (no size specified)
|
||||
[*.go]
|
||||
indent_style = tab
|
||||
|
||||
[*.md]
|
||||
trim_trailing_whitespace = false
|
||||
|
||||
# Matches the exact files either package.json or .travis.yml
|
||||
[{package.json,.travis.yml}]
|
||||
indent_style = space
|
||||
indent_size = 2
|
2
vendor/github.com/go-openapi/validate/.gitattributes
generated
vendored
Normal file
2
vendor/github.com/go-openapi/validate/.gitattributes
generated
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
# gofmt always uses LF, whereas Git uses CRLF on Windows.
|
||||
*.go text eol=lf
|
5
vendor/github.com/go-openapi/validate/.gitignore
generated
vendored
Normal file
5
vendor/github.com/go-openapi/validate/.gitignore
generated
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
secrets.yml
|
||||
coverage.out
|
||||
*.cov
|
||||
*.out
|
||||
playground
|
61
vendor/github.com/go-openapi/validate/.golangci.yml
generated
vendored
Normal file
61
vendor/github.com/go-openapi/validate/.golangci.yml
generated
vendored
Normal file
@ -0,0 +1,61 @@
|
||||
linters-settings:
|
||||
govet:
|
||||
check-shadowing: true
|
||||
golint:
|
||||
min-confidence: 0
|
||||
gocyclo:
|
||||
min-complexity: 45
|
||||
maligned:
|
||||
suggest-new: true
|
||||
dupl:
|
||||
threshold: 200
|
||||
goconst:
|
||||
min-len: 2
|
||||
min-occurrences: 3
|
||||
|
||||
linters:
|
||||
enable-all: true
|
||||
disable:
|
||||
- maligned
|
||||
- unparam
|
||||
- lll
|
||||
- gochecknoinits
|
||||
- gochecknoglobals
|
||||
- funlen
|
||||
- godox
|
||||
- gocognit
|
||||
- whitespace
|
||||
- wsl
|
||||
- wrapcheck
|
||||
- testpackage
|
||||
- nlreturn
|
||||
- gomnd
|
||||
- exhaustivestruct
|
||||
- goerr113
|
||||
- errorlint
|
||||
- nestif
|
||||
- godot
|
||||
- gofumpt
|
||||
- paralleltest
|
||||
- tparallel
|
||||
- thelper
|
||||
- ifshort
|
||||
- exhaustruct
|
||||
- varnamelen
|
||||
- gci
|
||||
- depguard
|
||||
- errchkjson
|
||||
- inamedparam
|
||||
- nonamedreturns
|
||||
- musttag
|
||||
- ireturn
|
||||
- forcetypeassert
|
||||
- cyclop
|
||||
# deprecated linters
|
||||
- deadcode
|
||||
- interfacer
|
||||
- scopelint
|
||||
- varcheck
|
||||
- structcheck
|
||||
- golint
|
||||
- nosnakecase
|
31
vendor/github.com/go-openapi/validate/BENCHMARK.md
generated
vendored
Normal file
31
vendor/github.com/go-openapi/validate/BENCHMARK.md
generated
vendored
Normal file
@ -0,0 +1,31 @@
|
||||
# Benchmark
|
||||
|
||||
Validating the Kubernetes Swagger API
|
||||
|
||||
## v0.22.6: 60,000,000 allocs
|
||||
```
|
||||
goos: linux
|
||||
goarch: amd64
|
||||
pkg: github.com/go-openapi/validate
|
||||
cpu: AMD Ryzen 7 5800X 8-Core Processor
|
||||
Benchmark_KubernetesSpec/validating_kubernetes_API-16 1 8549863982 ns/op 7067424936 B/op 59583275 allocs/op
|
||||
```
|
||||
|
||||
## After refact PR: minor but noticable improvements: 25,000,000 allocs
|
||||
```
|
||||
go test -bench Spec
|
||||
goos: linux
|
||||
goarch: amd64
|
||||
pkg: github.com/go-openapi/validate
|
||||
cpu: AMD Ryzen 7 5800X 8-Core Processor
|
||||
Benchmark_KubernetesSpec/validating_kubernetes_API-16 1 4064535557 ns/op 3379715592 B/op 25320330 allocs/op
|
||||
```
|
||||
|
||||
## After reduce GC pressure PR: 17,000,000 allocs
|
||||
```
|
||||
goos: linux
|
||||
goarch: amd64
|
||||
pkg: github.com/go-openapi/validate
|
||||
cpu: AMD Ryzen 7 5800X 8-Core Processor
|
||||
Benchmark_KubernetesSpec/validating_kubernetes_API-16 1 3758414145 ns/op 2593881496 B/op 17111373 allocs/op
|
||||
```
|
74
vendor/github.com/go-openapi/validate/CODE_OF_CONDUCT.md
generated
vendored
Normal file
74
vendor/github.com/go-openapi/validate/CODE_OF_CONDUCT.md
generated
vendored
Normal file
@ -0,0 +1,74 @@
|
||||
# Contributor Covenant Code of Conduct
|
||||
|
||||
## Our Pledge
|
||||
|
||||
In the interest of fostering an open and welcoming environment, we as
|
||||
contributors and maintainers pledge to making participation in our project and
|
||||
our community a harassment-free experience for everyone, regardless of age, body
|
||||
size, disability, ethnicity, gender identity and expression, level of experience,
|
||||
nationality, personal appearance, race, religion, or sexual identity and
|
||||
orientation.
|
||||
|
||||
## Our Standards
|
||||
|
||||
Examples of behavior that contributes to creating a positive environment
|
||||
include:
|
||||
|
||||
* Using welcoming and inclusive language
|
||||
* Being respectful of differing viewpoints and experiences
|
||||
* Gracefully accepting constructive criticism
|
||||
* Focusing on what is best for the community
|
||||
* Showing empathy towards other community members
|
||||
|
||||
Examples of unacceptable behavior by participants include:
|
||||
|
||||
* The use of sexualized language or imagery and unwelcome sexual attention or
|
||||
advances
|
||||
* Trolling, insulting/derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
* Publishing others' private information, such as a physical or electronic
|
||||
address, without explicit permission
|
||||
* Other conduct which could reasonably be considered inappropriate in a
|
||||
professional setting
|
||||
|
||||
## Our Responsibilities
|
||||
|
||||
Project maintainers are responsible for clarifying the standards of acceptable
|
||||
behavior and are expected to take appropriate and fair corrective action in
|
||||
response to any instances of unacceptable behavior.
|
||||
|
||||
Project maintainers have the right and responsibility to remove, edit, or
|
||||
reject comments, commits, code, wiki edits, issues, and other contributions
|
||||
that are not aligned to this Code of Conduct, or to ban temporarily or
|
||||
permanently any contributor for other behaviors that they deem inappropriate,
|
||||
threatening, offensive, or harmful.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies both within project spaces and in public spaces
|
||||
when an individual is representing the project or its community. Examples of
|
||||
representing a project or community include using an official project e-mail
|
||||
address, posting via an official social media account, or acting as an appointed
|
||||
representative at an online or offline event. Representation of a project may be
|
||||
further defined and clarified by project maintainers.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||
reported by contacting the project team at ivan+abuse@flanders.co.nz. All
|
||||
complaints will be reviewed and investigated and will result in a response that
|
||||
is deemed necessary and appropriate to the circumstances. The project team is
|
||||
obligated to maintain confidentiality with regard to the reporter of an incident.
|
||||
Further details of specific enforcement policies may be posted separately.
|
||||
|
||||
Project maintainers who do not follow or enforce the Code of Conduct in good
|
||||
faith may face temporary or permanent repercussions as determined by other
|
||||
members of the project's leadership.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
|
||||
available at [http://contributor-covenant.org/version/1/4][version]
|
||||
|
||||
[homepage]: http://contributor-covenant.org
|
||||
[version]: http://contributor-covenant.org/version/1/4/
|
202
vendor/github.com/go-openapi/validate/LICENSE
generated
vendored
Normal file
202
vendor/github.com/go-openapi/validate/LICENSE
generated
vendored
Normal file
@ -0,0 +1,202 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
36
vendor/github.com/go-openapi/validate/README.md
generated
vendored
Normal file
36
vendor/github.com/go-openapi/validate/README.md
generated
vendored
Normal file
@ -0,0 +1,36 @@
|
||||
# Validation helpers [](https://github.com/go-openapi/validate/actions?query=workflow%3A"go+test") [](https://codecov.io/gh/go-openapi/validate)
|
||||
|
||||
[](https://slackin.goswagger.io)
|
||||
[](https://raw.githubusercontent.com/go-openapi/validate/master/LICENSE)
|
||||
[](https://pkg.go.dev/github.com/go-openapi/validate)
|
||||
[](https://goreportcard.com/report/github.com/go-openapi/validate)
|
||||
|
||||
This package provides helpers to validate 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.
|
||||
|
||||
## What's inside?
|
||||
|
||||
* A validator for Swagger specifications
|
||||
* A validator for JSON schemas draft4
|
||||
* Helper functions to validate individual values (used by code generated by [go-swagger](https://github.com/go-swagger/go-swagger)).
|
||||
* Required, RequiredNumber, RequiredString
|
||||
* ReadOnly
|
||||
* UniqueItems, MaxItems, MinItems
|
||||
* Enum, EnumCase
|
||||
* Pattern, MinLength, MaxLength
|
||||
* Minimum, Maximum, MultipleOf
|
||||
* FormatOf
|
||||
|
||||
[Documentation](https://pkg.go.dev/github.com/go-openapi/validate)
|
||||
|
||||
## FAQ
|
||||
|
||||
* Does this library support OpenAPI 3?
|
||||
|
||||
> No.
|
||||
> This package currently only supports OpenAPI 2.0 (aka Swagger 2.0).
|
||||
> There is no plan to make it evolve toward supporting OpenAPI 3.x.
|
||||
> This [discussion thread](https://github.com/go-openapi/spec/issues/21) relates the full story.
|
||||
>
|
||||
> An early attempt to support Swagger 3 may be found at: https://github.com/go-openapi/spec3
|
56
vendor/github.com/go-openapi/validate/context.go
generated
vendored
Normal file
56
vendor/github.com/go-openapi/validate/context.go
generated
vendored
Normal file
@ -0,0 +1,56 @@
|
||||
package validate
|
||||
|
||||
import (
|
||||
"context"
|
||||
)
|
||||
|
||||
// validateCtxKey is the key type of context key in this pkg
|
||||
type validateCtxKey string
|
||||
|
||||
const (
|
||||
operationTypeKey validateCtxKey = "operationTypeKey"
|
||||
)
|
||||
|
||||
type operationType string
|
||||
|
||||
const (
|
||||
request operationType = "request"
|
||||
response operationType = "response"
|
||||
none operationType = "none" // not specified in ctx
|
||||
)
|
||||
|
||||
var operationTypeEnum = []operationType{request, response, none}
|
||||
|
||||
// WithOperationRequest returns a new context with operationType request
|
||||
// in context value
|
||||
func WithOperationRequest(ctx context.Context) context.Context {
|
||||
return withOperation(ctx, request)
|
||||
}
|
||||
|
||||
// WithOperationRequest returns a new context with operationType response
|
||||
// in context value
|
||||
func WithOperationResponse(ctx context.Context) context.Context {
|
||||
return withOperation(ctx, response)
|
||||
}
|
||||
|
||||
func withOperation(ctx context.Context, operation operationType) context.Context {
|
||||
return context.WithValue(ctx, operationTypeKey, operation)
|
||||
}
|
||||
|
||||
// extractOperationType extracts the operation type from ctx
|
||||
// if not specified or of unknown value, return none operation type
|
||||
func extractOperationType(ctx context.Context) operationType {
|
||||
v := ctx.Value(operationTypeKey)
|
||||
if v == nil {
|
||||
return none
|
||||
}
|
||||
res, ok := v.(operationType)
|
||||
if !ok {
|
||||
return none
|
||||
}
|
||||
// validate the value is in operation enum
|
||||
if err := Enum("", "", res, operationTypeEnum); err != nil {
|
||||
return none
|
||||
}
|
||||
return res
|
||||
}
|
47
vendor/github.com/go-openapi/validate/debug.go
generated
vendored
Normal file
47
vendor/github.com/go-openapi/validate/debug.go
generated
vendored
Normal file
@ -0,0 +1,47 @@
|
||||
// Copyright 2015 go-swagger maintainers
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package validate
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
)
|
||||
|
||||
var (
|
||||
// Debug is true when the SWAGGER_DEBUG env var is not empty.
|
||||
// It enables a more verbose logging of validators.
|
||||
Debug = os.Getenv("SWAGGER_DEBUG") != ""
|
||||
// validateLogger is a debug logger for this package
|
||||
validateLogger *log.Logger
|
||||
)
|
||||
|
||||
func init() {
|
||||
debugOptions()
|
||||
}
|
||||
|
||||
func debugOptions() {
|
||||
validateLogger = log.New(os.Stdout, "validate:", log.LstdFlags)
|
||||
}
|
||||
|
||||
func debugLog(msg string, args ...interface{}) {
|
||||
// A private, trivial trace logger, based on go-openapi/spec/expander.go:debugLog()
|
||||
if Debug {
|
||||
_, file1, pos1, _ := runtime.Caller(1)
|
||||
validateLogger.Printf("%s:%d: %s", filepath.Base(file1), pos1, fmt.Sprintf(msg, args...))
|
||||
}
|
||||
}
|
304
vendor/github.com/go-openapi/validate/default_validator.go
generated
vendored
Normal file
304
vendor/github.com/go-openapi/validate/default_validator.go
generated
vendored
Normal file
@ -0,0 +1,304 @@
|
||||
// Copyright 2015 go-swagger maintainers
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package validate
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/go-openapi/spec"
|
||||
)
|
||||
|
||||
// defaultValidator validates default values in a spec.
|
||||
// According to Swagger spec, default values MUST validate their schema.
|
||||
type defaultValidator struct {
|
||||
SpecValidator *SpecValidator
|
||||
visitedSchemas map[string]struct{}
|
||||
schemaOptions *SchemaValidatorOptions
|
||||
}
|
||||
|
||||
// resetVisited resets the internal state of visited schemas
|
||||
func (d *defaultValidator) resetVisited() {
|
||||
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]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
|
||||
}
|
||||
|
||||
parent = path[0:i]
|
||||
suffix = path[i+1:]
|
||||
|
||||
if strings.HasSuffix(parent, suffix) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// beingVisited asserts a schema is being visited
|
||||
func (d *defaultValidator) beingVisited(path string) {
|
||||
d.visitedSchemas[path] = struct{}{}
|
||||
}
|
||||
|
||||
// isVisited tells if a path has already been visited
|
||||
func (d *defaultValidator) isVisited(path string) bool {
|
||||
return isVisited(path, d.visitedSchemas)
|
||||
}
|
||||
|
||||
// Validate validates the default values declared in the swagger spec
|
||||
func (d *defaultValidator) Validate() *Result {
|
||||
errs := pools.poolOfResults.BorrowResult() // will redeem when merged
|
||||
|
||||
if d == nil || d.SpecValidator == nil {
|
||||
return errs
|
||||
}
|
||||
d.resetVisited()
|
||||
errs.Merge(d.validateDefaultValueValidAgainstSchema()) // error -
|
||||
return errs
|
||||
}
|
||||
|
||||
func (d *defaultValidator) validateDefaultValueValidAgainstSchema() *Result {
|
||||
// every default value that is specified must validate against the schema for that property
|
||||
// headers, items, parameters, schema
|
||||
|
||||
res := pools.poolOfResults.BorrowResult() // will redeem when merged
|
||||
s := d.SpecValidator
|
||||
|
||||
for method, pathItem := range s.expandedAnalyzer().Operations() {
|
||||
for path, op := range pathItem {
|
||||
// parameters
|
||||
for _, param := range paramHelp.safeExpandedParamsFor(path, method, op.ID, res, s) {
|
||||
if param.Default != nil && param.Required {
|
||||
res.AddWarnings(requiredHasDefaultMsg(param.Name, param.In))
|
||||
}
|
||||
|
||||
// reset explored schemas to get depth-first recursive-proof exploration
|
||||
d.resetVisited()
|
||||
|
||||
// Check simple parameters first
|
||||
// default values provided must validate against their inline definition (no explicit schema)
|
||||
if param.Default != nil && param.Schema == nil {
|
||||
// check param default value is valid
|
||||
red := newParamValidator(¶m, s.KnownFormats, d.schemaOptions).Validate(param.Default) //#nosec
|
||||
if red.HasErrorsOrWarnings() {
|
||||
res.AddErrors(defaultValueDoesNotValidateMsg(param.Name, param.In))
|
||||
res.Merge(red)
|
||||
} else if red.wantsRedeemOnMerge {
|
||||
pools.poolOfResults.RedeemResult(red)
|
||||
}
|
||||
}
|
||||
|
||||
// Recursively follows Items and Schemas
|
||||
if param.Items != nil {
|
||||
red := d.validateDefaultValueItemsAgainstSchema(param.Name, param.In, ¶m, param.Items) //#nosec
|
||||
if red.HasErrorsOrWarnings() {
|
||||
res.AddErrors(defaultValueItemsDoesNotValidateMsg(param.Name, param.In))
|
||||
res.Merge(red)
|
||||
} else if red.wantsRedeemOnMerge {
|
||||
pools.poolOfResults.RedeemResult(red)
|
||||
}
|
||||
}
|
||||
|
||||
if param.Schema != nil {
|
||||
// Validate default value against schema
|
||||
red := d.validateDefaultValueSchemaAgainstSchema(param.Name, param.In, param.Schema)
|
||||
if red.HasErrorsOrWarnings() {
|
||||
res.AddErrors(defaultValueDoesNotValidateMsg(param.Name, param.In))
|
||||
res.Merge(red)
|
||||
} else if red.wantsRedeemOnMerge {
|
||||
pools.poolOfResults.RedeemResult(red)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if op.Responses != nil {
|
||||
if op.Responses.Default != nil {
|
||||
// Same constraint on default Response
|
||||
res.Merge(d.validateDefaultInResponse(op.Responses.Default, jsonDefault, path, 0, op.ID))
|
||||
}
|
||||
// Same constraint on regular Responses
|
||||
if op.Responses.StatusCodeResponses != nil { // Safeguard
|
||||
for code, r := range op.Responses.StatusCodeResponses {
|
||||
res.Merge(d.validateDefaultInResponse(&r, "response", path, code, op.ID)) //#nosec
|
||||
}
|
||||
}
|
||||
} else if op.ID != "" {
|
||||
// Empty op.ID means there is no meaningful operation: no need to report a specific message
|
||||
res.AddErrors(noValidResponseMsg(op.ID))
|
||||
}
|
||||
}
|
||||
}
|
||||
if s.spec.Spec().Definitions != nil { // Safeguard
|
||||
// reset explored schemas to get depth-first recursive-proof exploration
|
||||
d.resetVisited()
|
||||
for nm, sch := range s.spec.Spec().Definitions {
|
||||
res.Merge(d.validateDefaultValueSchemaAgainstSchema("definitions."+nm, "body", &sch)) //#nosec
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
func (d *defaultValidator) validateDefaultInResponse(resp *spec.Response, responseType, path string, responseCode int, operationID string) *Result {
|
||||
s := d.SpecValidator
|
||||
|
||||
response, res := responseHelp.expandResponseRef(resp, path, s)
|
||||
if !res.IsValid() {
|
||||
return res
|
||||
}
|
||||
|
||||
responseName, responseCodeAsStr := responseHelp.responseMsgVariants(responseType, responseCode)
|
||||
|
||||
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, 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)
|
||||
}
|
||||
}
|
||||
|
||||
// Headers have inline definition, like params
|
||||
if h.Items != nil {
|
||||
red := d.validateDefaultValueItemsAgainstSchema(nm, "header", &h, h.Items) //#nosec
|
||||
if red.HasErrorsOrWarnings() {
|
||||
res.AddErrors(defaultValueHeaderItemsDoesNotValidateMsg(operationID, nm, responseName))
|
||||
res.Merge(red)
|
||||
} else if red.wantsRedeemOnMerge {
|
||||
pools.poolOfResults.RedeemResult(red)
|
||||
}
|
||||
}
|
||||
|
||||
if _, err := compileRegexp(h.Pattern); err != nil {
|
||||
res.AddErrors(invalidPatternInHeaderMsg(operationID, nm, responseName, h.Pattern, err))
|
||||
}
|
||||
|
||||
// Headers don't have schema
|
||||
}
|
||||
}
|
||||
if response.Schema != nil {
|
||||
// reset explored schemas to get depth-first recursive-proof exploration
|
||||
d.resetVisited()
|
||||
|
||||
red := d.validateDefaultValueSchemaAgainstSchema(responseCodeAsStr, "response", response.Schema)
|
||||
if red.HasErrorsOrWarnings() {
|
||||
// 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
|
||||
}
|
||||
|
||||
func (d *defaultValidator) validateDefaultValueSchemaAgainstSchema(path, in string, schema *spec.Schema) *Result {
|
||||
if schema == nil || d.isVisited(path) {
|
||||
// Avoids recursing if we are already done with that check
|
||||
return nil
|
||||
}
|
||||
d.beingVisited(path)
|
||||
res := pools.poolOfResults.BorrowResult()
|
||||
s := d.SpecValidator
|
||||
|
||||
if schema.Default != nil {
|
||||
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 {
|
||||
res.Merge(d.validateDefaultValueSchemaAgainstSchema(path+".items.default", in, schema.Items.Schema))
|
||||
}
|
||||
// Multiple schemas in items
|
||||
if schema.Items.Schemas != nil { // Safeguard
|
||||
for i, sch := range schema.Items.Schemas {
|
||||
res.Merge(d.validateDefaultValueSchemaAgainstSchema(fmt.Sprintf("%s.items[%d].default", path, i), in, &sch)) //#nosec
|
||||
}
|
||||
}
|
||||
}
|
||||
if _, err := compileRegexp(schema.Pattern); err != nil {
|
||||
res.AddErrors(invalidPatternInMsg(path, in, schema.Pattern))
|
||||
}
|
||||
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(path+".additionalItems", in, schema.AdditionalItems.Schema))
|
||||
}
|
||||
for propName, prop := range schema.Properties {
|
||||
res.Merge(d.validateDefaultValueSchemaAgainstSchema(path+"."+propName, in, &prop)) //#nosec
|
||||
}
|
||||
for propName, prop := range schema.PatternProperties {
|
||||
res.Merge(d.validateDefaultValueSchemaAgainstSchema(path+"."+propName, in, &prop)) //#nosec
|
||||
}
|
||||
if schema.AdditionalProperties != nil && schema.AdditionalProperties.Schema != nil {
|
||||
res.Merge(d.validateDefaultValueSchemaAgainstSchema(path+".additionalProperties", in, schema.AdditionalProperties.Schema))
|
||||
}
|
||||
if schema.AllOf != nil {
|
||||
for i, aoSch := range schema.AllOf {
|
||||
res.Merge(d.validateDefaultValueSchemaAgainstSchema(fmt.Sprintf("%s.allOf[%d]", path, i), in, &aoSch)) //#nosec
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
// TODO: Temporary duplicated code. Need to refactor with examples
|
||||
|
||||
func (d *defaultValidator) validateDefaultValueItemsAgainstSchema(path, in string, root interface{}, items *spec.Items) *Result {
|
||||
res := pools.poolOfResults.BorrowResult()
|
||||
s := d.SpecValidator
|
||||
if items != nil {
|
||||
if items.Default != nil {
|
||||
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))
|
||||
}
|
||||
if _, err := compileRegexp(items.Pattern); err != nil {
|
||||
res.AddErrors(invalidPatternInMsg(path, in, items.Pattern))
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
87
vendor/github.com/go-openapi/validate/doc.go
generated
vendored
Normal file
87
vendor/github.com/go-openapi/validate/doc.go
generated
vendored
Normal file
@ -0,0 +1,87 @@
|
||||
// Copyright 2015 go-swagger maintainers
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
/*
|
||||
Package validate provides methods to validate a swagger specification,
|
||||
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
|
||||
|
||||
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.
|
||||
|
||||
Entry points:
|
||||
- Spec()
|
||||
- NewSpecValidator()
|
||||
- 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. 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
|
||||
|
||||
The schema validation toolkit validates data against JSON-schema-draft 04 schema.
|
||||
|
||||
It is tested against the full json-schema-testing-suite (https://github.com/json-schema-org/JSON-Schema-Test-Suite),
|
||||
except for the optional part (bignum, ECMA regexp, ...).
|
||||
|
||||
It supports the complete JSON-schema vocabulary, including keywords not supported by Swagger (e.g. additionalItems, ...)
|
||||
|
||||
Entry points:
|
||||
- AgainstSchema()
|
||||
- ...
|
||||
|
||||
# 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
|
||||
*/
|
||||
package validate
|
299
vendor/github.com/go-openapi/validate/example_validator.go
generated
vendored
Normal file
299
vendor/github.com/go-openapi/validate/example_validator.go
generated
vendored
Normal file
@ -0,0 +1,299 @@
|
||||
// Copyright 2015 go-swagger maintainers
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package validate
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/go-openapi/spec"
|
||||
)
|
||||
|
||||
// ExampleValidator validates example values defined in a spec
|
||||
type exampleValidator struct {
|
||||
SpecValidator *SpecValidator
|
||||
visitedSchemas map[string]struct{}
|
||||
schemaOptions *SchemaValidatorOptions
|
||||
}
|
||||
|
||||
// resetVisited resets the internal state of visited schemas
|
||||
func (ex *exampleValidator) resetVisited() {
|
||||
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] = struct{}{}
|
||||
}
|
||||
|
||||
// isVisited tells if a path has already been visited
|
||||
func (ex *exampleValidator) isVisited(path string) bool {
|
||||
return isVisited(path, ex.visitedSchemas)
|
||||
}
|
||||
|
||||
// Validate validates the example values declared in the swagger spec
|
||||
// Example values MUST conform to their schema.
|
||||
//
|
||||
// With Swagger 2.0, examples are supported in:
|
||||
// - schemas
|
||||
// - individual property
|
||||
// - responses
|
||||
func (ex *exampleValidator) Validate() *Result {
|
||||
errs := pools.poolOfResults.BorrowResult()
|
||||
|
||||
if ex == nil || ex.SpecValidator == nil {
|
||||
return errs
|
||||
}
|
||||
ex.resetVisited()
|
||||
errs.Merge(ex.validateExampleValueValidAgainstSchema()) // error -
|
||||
|
||||
return errs
|
||||
}
|
||||
|
||||
func (ex *exampleValidator) validateExampleValueValidAgainstSchema() *Result {
|
||||
// every example value that is specified must validate against the schema for that property
|
||||
// in: schemas, properties, object, items
|
||||
// not in: headers, parameters without schema
|
||||
|
||||
res := pools.poolOfResults.BorrowResult()
|
||||
s := ex.SpecValidator
|
||||
|
||||
for method, pathItem := range s.expandedAnalyzer().Operations() {
|
||||
for path, op := range pathItem {
|
||||
// parameters
|
||||
for _, param := range paramHelp.safeExpandedParamsFor(path, method, op.ID, res, s) {
|
||||
|
||||
// As of swagger 2.0, Examples are not supported in simple parameters
|
||||
// However, it looks like it is supported by go-openapi
|
||||
|
||||
// reset explored schemas to get depth-first recursive-proof exploration
|
||||
ex.resetVisited()
|
||||
|
||||
// Check simple parameters first
|
||||
// default values provided must validate against their inline definition (no explicit schema)
|
||||
if param.Example != nil && param.Schema == nil {
|
||||
// check param default value is valid
|
||||
red := newParamValidator(¶m, s.KnownFormats, ex.schemaOptions).Validate(param.Example) //#nosec
|
||||
if red.HasErrorsOrWarnings() {
|
||||
res.AddWarnings(exampleValueDoesNotValidateMsg(param.Name, param.In))
|
||||
res.MergeAsWarnings(red)
|
||||
} else if red.wantsRedeemOnMerge {
|
||||
pools.poolOfResults.RedeemResult(red)
|
||||
}
|
||||
}
|
||||
|
||||
// Recursively follows Items and Schemas
|
||||
if param.Items != nil {
|
||||
red := ex.validateExampleValueItemsAgainstSchema(param.Name, param.In, ¶m, param.Items) //#nosec
|
||||
if red.HasErrorsOrWarnings() {
|
||||
res.AddWarnings(exampleValueItemsDoesNotValidateMsg(param.Name, param.In))
|
||||
res.Merge(red)
|
||||
} else if red.wantsRedeemOnMerge {
|
||||
pools.poolOfResults.RedeemResult(red)
|
||||
}
|
||||
}
|
||||
|
||||
if param.Schema != nil {
|
||||
// Validate example value against schema
|
||||
red := ex.validateExampleValueSchemaAgainstSchema(param.Name, param.In, param.Schema)
|
||||
if red.HasErrorsOrWarnings() {
|
||||
res.AddWarnings(exampleValueDoesNotValidateMsg(param.Name, param.In))
|
||||
res.Merge(red)
|
||||
} else if red.wantsRedeemOnMerge {
|
||||
pools.poolOfResults.RedeemResult(red)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if op.Responses != nil {
|
||||
if op.Responses.Default != nil {
|
||||
// Same constraint on default Response
|
||||
res.Merge(ex.validateExampleInResponse(op.Responses.Default, jsonDefault, path, 0, op.ID))
|
||||
}
|
||||
// Same constraint on regular Responses
|
||||
if op.Responses.StatusCodeResponses != nil { // Safeguard
|
||||
for code, r := range op.Responses.StatusCodeResponses {
|
||||
res.Merge(ex.validateExampleInResponse(&r, "response", path, code, op.ID)) //#nosec
|
||||
}
|
||||
}
|
||||
} else if op.ID != "" {
|
||||
// Empty op.ID means there is no meaningful operation: no need to report a specific message
|
||||
res.AddErrors(noValidResponseMsg(op.ID))
|
||||
}
|
||||
}
|
||||
}
|
||||
if s.spec.Spec().Definitions != nil { // Safeguard
|
||||
// reset explored schemas to get depth-first recursive-proof exploration
|
||||
ex.resetVisited()
|
||||
for nm, sch := range s.spec.Spec().Definitions {
|
||||
res.Merge(ex.validateExampleValueSchemaAgainstSchema("definitions."+nm, "body", &sch)) //#nosec
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
func (ex *exampleValidator) validateExampleInResponse(resp *spec.Response, responseType, path string, responseCode int, operationID string) *Result {
|
||||
s := ex.SpecValidator
|
||||
|
||||
response, res := responseHelp.expandResponseRef(resp, path, s)
|
||||
if !res.IsValid() { // Safeguard
|
||||
return res
|
||||
}
|
||||
|
||||
responseName, responseCodeAsStr := responseHelp.responseMsgVariants(responseType, responseCode)
|
||||
|
||||
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, 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)
|
||||
}
|
||||
}
|
||||
|
||||
// Headers have inline definition, like params
|
||||
if h.Items != nil {
|
||||
red := ex.validateExampleValueItemsAgainstSchema(nm, "header", &h, h.Items) //#nosec
|
||||
if red.HasErrorsOrWarnings() {
|
||||
res.AddWarnings(exampleValueHeaderItemsDoesNotValidateMsg(operationID, nm, responseName))
|
||||
res.MergeAsWarnings(red)
|
||||
} else if red.wantsRedeemOnMerge {
|
||||
pools.poolOfResults.RedeemResult(red)
|
||||
}
|
||||
}
|
||||
|
||||
if _, err := compileRegexp(h.Pattern); err != nil {
|
||||
res.AddErrors(invalidPatternInHeaderMsg(operationID, nm, responseName, h.Pattern, err))
|
||||
}
|
||||
|
||||
// Headers don't have schema
|
||||
}
|
||||
}
|
||||
if response.Schema != nil {
|
||||
// reset explored schemas to get depth-first recursive-proof exploration
|
||||
ex.resetVisited()
|
||||
|
||||
red := ex.validateExampleValueSchemaAgainstSchema(responseCodeAsStr, "response", response.Schema)
|
||||
if red.HasErrorsOrWarnings() {
|
||||
// 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, s.schemaOptions).Validate(example),
|
||||
)
|
||||
} else {
|
||||
// TODO: validate other media types too
|
||||
res.AddWarnings(examplesMimeNotSupportedMsg(operationID, responseName))
|
||||
}
|
||||
} else {
|
||||
res.AddWarnings(examplesWithoutSchemaMsg(operationID, responseName))
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
func (ex *exampleValidator) validateExampleValueSchemaAgainstSchema(path, in string, schema *spec.Schema) *Result {
|
||||
if schema == nil || ex.isVisited(path) {
|
||||
// Avoids recursing if we are already done with that check
|
||||
return nil
|
||||
}
|
||||
ex.beingVisited(path)
|
||||
s := ex.SpecValidator
|
||||
res := pools.poolOfResults.BorrowResult()
|
||||
|
||||
if schema.Example != nil {
|
||||
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 {
|
||||
res.Merge(ex.validateExampleValueSchemaAgainstSchema(path+".items.example", in, schema.Items.Schema))
|
||||
}
|
||||
// Multiple schemas in items
|
||||
if schema.Items.Schemas != nil { // Safeguard
|
||||
for i, sch := range schema.Items.Schemas {
|
||||
res.Merge(ex.validateExampleValueSchemaAgainstSchema(fmt.Sprintf("%s.items[%d].example", path, i), in, &sch)) //#nosec
|
||||
}
|
||||
}
|
||||
}
|
||||
if _, err := compileRegexp(schema.Pattern); err != nil {
|
||||
res.AddErrors(invalidPatternInMsg(path, in, schema.Pattern))
|
||||
}
|
||||
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(path+".additionalItems", in, schema.AdditionalItems.Schema))
|
||||
}
|
||||
for propName, prop := range schema.Properties {
|
||||
res.Merge(ex.validateExampleValueSchemaAgainstSchema(path+"."+propName, in, &prop)) //#nosec
|
||||
}
|
||||
for propName, prop := range schema.PatternProperties {
|
||||
res.Merge(ex.validateExampleValueSchemaAgainstSchema(path+"."+propName, in, &prop)) //#nosec
|
||||
}
|
||||
if schema.AdditionalProperties != nil && schema.AdditionalProperties.Schema != nil {
|
||||
res.Merge(ex.validateExampleValueSchemaAgainstSchema(path+".additionalProperties", in, schema.AdditionalProperties.Schema))
|
||||
}
|
||||
if schema.AllOf != nil {
|
||||
for i, aoSch := range schema.AllOf {
|
||||
res.Merge(ex.validateExampleValueSchemaAgainstSchema(fmt.Sprintf("%s.allOf[%d]", path, i), in, &aoSch)) //#nosec
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
// TODO: Temporary duplicated code. Need to refactor with examples
|
||||
//
|
||||
|
||||
func (ex *exampleValidator) validateExampleValueItemsAgainstSchema(path, in string, root interface{}, items *spec.Items) *Result {
|
||||
res := pools.poolOfResults.BorrowResult()
|
||||
s := ex.SpecValidator
|
||||
if items != nil {
|
||||
if items.Example != nil {
|
||||
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))
|
||||
}
|
||||
if _, err := compileRegexp(items.Pattern); err != nil {
|
||||
res.AddErrors(invalidPatternInMsg(path, in, items.Pattern))
|
||||
}
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
99
vendor/github.com/go-openapi/validate/formats.go
generated
vendored
Normal file
99
vendor/github.com/go-openapi/validate/formats.go
generated
vendored
Normal file
@ -0,0 +1,99 @@
|
||||
// Copyright 2015 go-swagger maintainers
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package validate
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
|
||||
"github.com/go-openapi/spec"
|
||||
"github.com/go-openapi/strfmt"
|
||||
)
|
||||
|
||||
type formatValidator struct {
|
||||
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) {
|
||||
f.Path = path
|
||||
}
|
||||
|
||||
func (f *formatValidator) Applies(source interface{}, kind reflect.Kind) bool {
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
func (f *formatValidator) Validate(val interface{}) *Result {
|
||||
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)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func (f *formatValidator) redeem() {
|
||||
pools.poolOfFormatValidators.RedeemValidator(f)
|
||||
}
|
333
vendor/github.com/go-openapi/validate/helpers.go
generated
vendored
Normal file
333
vendor/github.com/go-openapi/validate/helpers.go
generated
vendored
Normal file
@ -0,0 +1,333 @@
|
||||
// Copyright 2015 go-swagger maintainers
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package validate
|
||||
|
||||
// TODO: define this as package validate/internal
|
||||
// This must be done while keeping CI intact with all tests and test coverage
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/go-openapi/errors"
|
||||
"github.com/go-openapi/spec"
|
||||
)
|
||||
|
||||
const (
|
||||
swaggerBody = "body"
|
||||
swaggerExample = "example"
|
||||
swaggerExamples = "examples"
|
||||
)
|
||||
|
||||
const (
|
||||
objectType = "object"
|
||||
arrayType = "array"
|
||||
stringType = "string"
|
||||
integerType = "integer"
|
||||
numberType = "number"
|
||||
booleanType = "boolean"
|
||||
fileType = "file"
|
||||
nullType = "null"
|
||||
)
|
||||
|
||||
const (
|
||||
jsonProperties = "properties"
|
||||
jsonItems = "items"
|
||||
jsonType = "type"
|
||||
// jsonSchema = "schema"
|
||||
jsonDefault = "default"
|
||||
)
|
||||
|
||||
const (
|
||||
stringFormatDate = "date"
|
||||
stringFormatDateTime = "date-time"
|
||||
stringFormatPassword = "password"
|
||||
stringFormatByte = "byte"
|
||||
// stringFormatBinary = "binary"
|
||||
stringFormatCreditCard = "creditcard"
|
||||
stringFormatDuration = "duration"
|
||||
stringFormatEmail = "email"
|
||||
stringFormatHexColor = "hexcolor"
|
||||
stringFormatHostname = "hostname"
|
||||
stringFormatIPv4 = "ipv4"
|
||||
stringFormatIPv6 = "ipv6"
|
||||
stringFormatISBN = "isbn"
|
||||
stringFormatISBN10 = "isbn10"
|
||||
stringFormatISBN13 = "isbn13"
|
||||
stringFormatMAC = "mac"
|
||||
stringFormatBSONObjectID = "bsonobjectid"
|
||||
stringFormatRGBColor = "rgbcolor"
|
||||
stringFormatSSN = "ssn"
|
||||
stringFormatURI = "uri"
|
||||
stringFormatUUID = "uuid"
|
||||
stringFormatUUID3 = "uuid3"
|
||||
stringFormatUUID4 = "uuid4"
|
||||
stringFormatUUID5 = "uuid5"
|
||||
|
||||
integerFormatInt32 = "int32"
|
||||
integerFormatInt64 = "int64"
|
||||
integerFormatUInt32 = "uint32"
|
||||
integerFormatUInt64 = "uint64"
|
||||
|
||||
numberFormatFloat32 = "float32"
|
||||
numberFormatFloat64 = "float64"
|
||||
numberFormatFloat = "float"
|
||||
numberFormatDouble = "double"
|
||||
)
|
||||
|
||||
// Helpers available at the package level
|
||||
var (
|
||||
pathHelp *pathHelper
|
||||
valueHelp *valueHelper
|
||||
errorHelp *errorHelper
|
||||
paramHelp *paramHelper
|
||||
responseHelp *responseHelper
|
||||
)
|
||||
|
||||
type errorHelper struct {
|
||||
// A collection of unexported helpers for error construction
|
||||
}
|
||||
|
||||
func (h *errorHelper) sErr(err errors.Error, recycle bool) *Result {
|
||||
// Builds a Result from standard errors.Error
|
||||
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 {
|
||||
// Provides more context on error messages
|
||||
// reported by the jsoinpointer package by altering the passed Result
|
||||
if err != nil {
|
||||
res.AddErrors(cannotResolveRefMsg(fromPath, ref, err))
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
type pathHelper struct {
|
||||
// A collection of unexported helpers for path validation
|
||||
}
|
||||
|
||||
func (h *pathHelper) stripParametersInPath(path string) string {
|
||||
// Returns a path stripped from all path parameters, with multiple or trailing slashes removed.
|
||||
//
|
||||
// Stripping is performed on a slash-separated basis, e.g '/a{/b}' remains a{/b} and not /a.
|
||||
// - Trailing "/" make a difference, e.g. /a/ !~ /a (ex: canary/bitbucket.org/swagger.json)
|
||||
// - presence or absence of a parameter makes a difference, e.g. /a/{log} !~ /a/ (ex: canary/kubernetes/swagger.json)
|
||||
|
||||
// Regexp to extract parameters from path, with surrounding {}.
|
||||
// NOTE: important non-greedy modifier
|
||||
rexParsePathParam := mustCompileRegexp(`{[^{}]+?}`)
|
||||
strippedSegments := []string{}
|
||||
|
||||
for _, segment := range strings.Split(path, "/") {
|
||||
strippedSegments = append(strippedSegments, rexParsePathParam.ReplaceAllString(segment, "X"))
|
||||
}
|
||||
return strings.Join(strippedSegments, "/")
|
||||
}
|
||||
|
||||
func (h *pathHelper) extractPathParams(path string) (params []string) {
|
||||
// Extracts all params from a path, with surrounding "{}"
|
||||
rexParsePathParam := mustCompileRegexp(`{[^{}]+?}`)
|
||||
|
||||
for _, segment := range strings.Split(path, "/") {
|
||||
for _, v := range rexParsePathParam.FindAllStringSubmatch(segment, -1) {
|
||||
params = append(params, v...)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
type valueHelper struct {
|
||||
// A collection of unexported helpers for value validation
|
||||
}
|
||||
|
||||
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() { //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:
|
||||
return int64(v.Uint())
|
||||
case reflect.Float32, reflect.Float64:
|
||||
return int64(v.Float())
|
||||
default:
|
||||
// panic("Non numeric value in asInt64()")
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
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() { //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:
|
||||
return v.Uint()
|
||||
case reflect.Float32, reflect.Float64:
|
||||
return uint64(v.Float())
|
||||
default:
|
||||
// panic("Non numeric value in asUint64()")
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
// Same for unsigned floats
|
||||
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() { //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:
|
||||
return float64(v.Uint())
|
||||
case reflect.Float32, reflect.Float64:
|
||||
return v.Float()
|
||||
default:
|
||||
// panic("Non numeric value in asFloat64()")
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
type paramHelper struct {
|
||||
// A collection of unexported helpers for parameters resolution
|
||||
}
|
||||
|
||||
func (h *paramHelper) safeExpandedParamsFor(path, method, operationID string, res *Result, s *SpecValidator) (params []spec.Parameter) {
|
||||
operation, ok := s.expandedAnalyzer().OperationFor(method, path)
|
||||
if ok {
|
||||
// expand parameters first if necessary
|
||||
resolvedParams := []spec.Parameter{}
|
||||
for _, ppr := range operation.Parameters {
|
||||
resolvedParam, red := h.resolveParam(path, method, operationID, &ppr, s) //#nosec
|
||||
res.Merge(red)
|
||||
if resolvedParam != nil {
|
||||
resolvedParams = append(resolvedParams, *resolvedParam)
|
||||
}
|
||||
}
|
||||
// remove params with invalid expansion from Slice
|
||||
operation.Parameters = resolvedParams
|
||||
|
||||
for _, ppr := range s.expandedAnalyzer().SafeParamsFor(method, path,
|
||||
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
|
||||
res.AddErrors(err)
|
||||
return true
|
||||
}) {
|
||||
params = append(params, ppr)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (h *paramHelper) resolveParam(path, method, operationID string, param *spec.Parameter, s *SpecValidator) (*spec.Parameter, *Result) {
|
||||
// Ensure parameter is expanded
|
||||
var err error
|
||||
res := new(Result)
|
||||
isRef := param.Ref.String() != ""
|
||||
if s.spec.SpecFilePath() == "" {
|
||||
err = spec.ExpandParameterWithRoot(param, s.spec.Spec(), nil)
|
||||
} else {
|
||||
err = spec.ExpandParameter(param, s.spec.SpecFilePath())
|
||||
|
||||
}
|
||||
if err != nil { // Safeguard
|
||||
// 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
|
||||
}
|
||||
res.Merge(h.checkExpandedParam(param, param.Name, param.In, operationID, isRef))
|
||||
return param, res
|
||||
}
|
||||
|
||||
func (h *paramHelper) checkExpandedParam(pr *spec.Parameter, path, in, operation string, isRef bool) *Result {
|
||||
// Secure parameter structure after $ref resolution
|
||||
res := new(Result)
|
||||
simpleZero := spec.SimpleSchema{}
|
||||
// Try to explain why... best guess
|
||||
switch {
|
||||
case pr.In == swaggerBody && (pr.SimpleSchema != simpleZero && pr.SimpleSchema.Type != objectType):
|
||||
if isRef {
|
||||
// Most likely, a $ref with a sibling is an unwanted situation: in itself this is a warning...
|
||||
// but we detect it because of the following error:
|
||||
// schema took over Parameter for an unexplained reason
|
||||
res.AddWarnings(refShouldNotHaveSiblingsMsg(path, operation))
|
||||
}
|
||||
res.AddErrors(invalidParameterDefinitionMsg(path, in, operation))
|
||||
case pr.In != swaggerBody && pr.Schema != nil:
|
||||
if isRef {
|
||||
res.AddWarnings(refShouldNotHaveSiblingsMsg(path, operation))
|
||||
}
|
||||
res.AddErrors(invalidParameterDefinitionAsSchemaMsg(path, in, operation))
|
||||
case (pr.In == swaggerBody && pr.Schema == nil) || (pr.In != swaggerBody && pr.SimpleSchema == simpleZero):
|
||||
// Other unexpected mishaps
|
||||
res.AddErrors(invalidParameterDefinitionMsg(path, in, operation))
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
type responseHelper struct {
|
||||
// A collection of unexported helpers for response resolution
|
||||
}
|
||||
|
||||
func (r *responseHelper) expandResponseRef(
|
||||
response *spec.Response,
|
||||
path string, s *SpecValidator) (*spec.Response, *Result) {
|
||||
// Ensure response is expanded
|
||||
var err error
|
||||
res := new(Result)
|
||||
if s.spec.SpecFilePath() == "" {
|
||||
// there is no physical document to resolve $ref in response
|
||||
err = spec.ExpandResponseWithRoot(response, s.spec.Spec(), nil)
|
||||
} else {
|
||||
err = spec.ExpandResponse(response, s.spec.SpecFilePath())
|
||||
}
|
||||
if err != nil { // Safeguard
|
||||
// NOTE: we may enter here when the whole response is an unresolved $ref.
|
||||
errorHelp.addPointerError(res, err, response.Ref.String(), path)
|
||||
return nil, res
|
||||
}
|
||||
|
||||
return response, res
|
||||
}
|
||||
|
||||
func (r *responseHelper) responseMsgVariants(
|
||||
responseType string,
|
||||
responseCode int) (responseName, responseCodeAsStr string) {
|
||||
// Path variants for messages
|
||||
if responseType == jsonDefault {
|
||||
responseCodeAsStr = jsonDefault
|
||||
responseName = "default response"
|
||||
} else {
|
||||
responseCodeAsStr = strconv.Itoa(responseCode)
|
||||
responseName = "response " + responseCodeAsStr
|
||||
}
|
||||
return
|
||||
}
|
431
vendor/github.com/go-openapi/validate/object_validator.go
generated
vendored
Normal file
431
vendor/github.com/go-openapi/validate/object_validator.go
generated
vendored
Normal file
@ -0,0 +1,431 @@
|
||||
// Copyright 2015 go-swagger maintainers
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package validate
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
"github.com/go-openapi/errors"
|
||||
"github.com/go-openapi/spec"
|
||||
"github.com/go-openapi/strfmt"
|
||||
)
|
||||
|
||||
type objectValidator struct {
|
||||
Path string
|
||||
In string
|
||||
MaxProperties *int64
|
||||
MinProperties *int64
|
||||
Required []string
|
||||
Properties map[string]spec.Schema
|
||||
AdditionalProperties *spec.SchemaOrBool
|
||||
PatternProperties map[string]spec.Schema
|
||||
Root interface{}
|
||||
KnownFormats strfmt.Registry
|
||||
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
|
||||
_, isSchema := source.(*spec.Schema)
|
||||
return isSchema && (kind == reflect.Map || kind == reflect.Struct)
|
||||
}
|
||||
|
||||
func (o *objectValidator) isProperties() bool {
|
||||
p := o.splitPath
|
||||
return len(p) > 1 && p[len(p)-1] == jsonProperties && p[len(p)-2] != jsonProperties
|
||||
}
|
||||
|
||||
func (o *objectValidator) isDefault() bool {
|
||||
p := o.splitPath
|
||||
return len(p) > 1 && p[len(p)-1] == jsonDefault && p[len(p)-2] != jsonDefault
|
||||
}
|
||||
|
||||
func (o *objectValidator) isExample() bool {
|
||||
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 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 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))
|
||||
}
|
||||
}
|
||||
|
||||
func (o *objectValidator) precheck(res *Result, val map[string]interface{}) {
|
||||
if o.Options.EnableArrayMustHaveItemsCheck {
|
||||
o.checkArrayMustHaveItems(res, val)
|
||||
}
|
||||
if o.Options.EnableObjectArrayTypeCheck {
|
||||
o.checkItemsMustBeTypeArray(res, val)
|
||||
}
|
||||
}
|
||||
|
||||
func (o *objectValidator) Validate(data interface{}) *Result {
|
||||
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), o.Options.recycleResult)
|
||||
}
|
||||
if o.MaxProperties != nil && numKeys > *o.MaxProperties {
|
||||
return errorHelp.sErr(errors.TooManyProperties(o.Path, o.In, *o.MaxProperties), o.Options.recycleResult)
|
||||
}
|
||||
|
||||
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
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
func (o *objectValidator) validateNoAdditionalProperties(val map[string]interface{}, res *Result) {
|
||||
for k := range val {
|
||||
if k == "$schema" || k == "id" {
|
||||
// special properties "$schema" and "id" are ignored
|
||||
continue
|
||||
}
|
||||
|
||||
_, regularProperty := o.Properties[k]
|
||||
if regularProperty {
|
||||
continue
|
||||
}
|
||||
|
||||
matched := false
|
||||
for pk := range o.PatternProperties {
|
||||
re, err := compileRegexp(pk)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
if matches := re.MatchString(k); matches {
|
||||
matched = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if matched {
|
||||
continue
|
||||
}
|
||||
|
||||
res.AddErrors(errors.PropertyNotAllowed(o.Path, o.In, k))
|
||||
|
||||
// BUG(fredbi): This section should move to a part dedicated to spec validation as
|
||||
// it will conflict with regular schemas where a property "headers" is defined.
|
||||
|
||||
//
|
||||
// Croaks a more explicit message on top of the standard one
|
||||
// on some recognized cases.
|
||||
//
|
||||
// NOTE: edge cases with invalid type assertion are simply ignored here.
|
||||
// NOTE: prefix your messages here by "IMPORTANT!" so there are not filtered
|
||||
// by higher level callers (the IMPORTANT! tag will be eventually
|
||||
// removed).
|
||||
if k != "headers" || val[k] == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
// $ref is forbidden in header
|
||||
headers, mapOk := val[k].(map[string]interface{})
|
||||
if !mapOk {
|
||||
continue
|
||||
}
|
||||
|
||||
for headerKey, headerBody := range headers {
|
||||
if headerBody == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
headerSchema, mapOfMapOk := headerBody.(map[string]interface{})
|
||||
if !mapOfMapOk {
|
||||
continue
|
||||
}
|
||||
|
||||
_, found := headerSchema["$ref"]
|
||||
if !found {
|
||||
continue
|
||||
}
|
||||
|
||||
refString, stringOk := headerSchema["$ref"].(string)
|
||||
if !stringOk {
|
||||
continue
|
||||
}
|
||||
|
||||
msg := strings.Join([]string{", one may not use $ref=\":", refString, "\""}, "")
|
||||
res.AddErrors(refNotAllowedInHeaderMsg(o.Path, headerKey, msg))
|
||||
/*
|
||||
case "$ref":
|
||||
if val[k] != nil {
|
||||
// TODO: check context of that ref: warn about siblings, check against invalid context
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (o *objectValidator) validateAdditionalProperties(val map[string]interface{}, res *Result) {
|
||||
for key, value := range val {
|
||||
_, regularProperty := o.Properties[key]
|
||||
if regularProperty {
|
||||
continue
|
||||
}
|
||||
|
||||
// Validates property against "patternProperties" if applicable
|
||||
// BUG(fredbi): succeededOnce is always false
|
||||
|
||||
// NOTE: how about regular properties which do not match patternProperties?
|
||||
matched, succeededOnce, _ := o.validatePatternProperty(key, value, res)
|
||||
if matched || succeededOnce {
|
||||
continue
|
||||
}
|
||||
|
||||
if o.AdditionalProperties == nil || o.AdditionalProperties.Schema == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
// Cases: properties which are not regular properties and have not been matched by the PatternProperties validator
|
||||
// AdditionalProperties as Schema
|
||||
r := newSchemaValidator(o.AdditionalProperties.Schema, o.Root, o.Path+"."+key, o.KnownFormats, o.Options).Validate(value)
|
||||
res.mergeForField(val, key, r)
|
||||
}
|
||||
// Valid cases: additionalProperties: true or undefined
|
||||
}
|
||||
|
||||
func (o *objectValidator) validatePropertiesSchema(val map[string]interface{}, res *Result) {
|
||||
createdFromDefaults := map[string]struct{}{}
|
||||
|
||||
// Property types:
|
||||
// - regular Property
|
||||
pSchema := pools.poolOfSchemas.BorrowSchema() // recycle a spec.Schema object which lifespan extends only to the validation of properties
|
||||
defer func() {
|
||||
pools.poolOfSchemas.RedeemSchema(pSchema)
|
||||
}()
|
||||
|
||||
for pName := range o.Properties {
|
||||
*pSchema = o.Properties[pName]
|
||||
var rName string
|
||||
if o.Path == "" {
|
||||
rName = pName
|
||||
} else {
|
||||
rName = o.Path + "." + pName
|
||||
}
|
||||
|
||||
// Recursively validates each property against its schema
|
||||
v, ok := val[pName]
|
||||
if ok {
|
||||
r := newSchemaValidator(pSchema, o.Root, rName, o.KnownFormats, o.Options).Validate(v)
|
||||
res.mergeForField(val, pName, r)
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
if pSchema.Default != nil {
|
||||
// if a default value is defined, creates the property from defaults
|
||||
// NOTE: JSON schema does not enforce default values to be valid against schema. Swagger does.
|
||||
createdFromDefaults[pName] = struct{}{}
|
||||
if !o.Options.skipSchemataResult {
|
||||
res.addPropertySchemata(val, pName, pSchema) // this shallow-clones the content of the pSchema pointer
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(o.Required) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
// Check required properties
|
||||
for _, k := range o.Required {
|
||||
v, ok := val[k]
|
||||
if ok {
|
||||
continue
|
||||
}
|
||||
_, isCreatedFromDefaults := createdFromDefaults[k]
|
||||
if isCreatedFromDefaults {
|
||||
continue
|
||||
}
|
||||
|
||||
res.AddErrors(errors.Required(fmt.Sprintf("%s.%s", o.Path, k), o.In, v))
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: succeededOnce is not used anywhere
|
||||
func (o *objectValidator) validatePatternProperty(key string, value interface{}, result *Result) (bool, bool, []string) {
|
||||
if len(o.PatternProperties) == 0 {
|
||||
return false, false, nil
|
||||
}
|
||||
|
||||
matched := false
|
||||
succeededOnce := false
|
||||
patterns := make([]string, 0, len(o.PatternProperties))
|
||||
|
||||
schema := pools.poolOfSchemas.BorrowSchema()
|
||||
defer func() {
|
||||
pools.poolOfSchemas.RedeemSchema(schema)
|
||||
}()
|
||||
|
||||
for k := range o.PatternProperties {
|
||||
re, err := compileRegexp(k)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
match := re.MatchString(key)
|
||||
if !match {
|
||||
continue
|
||||
}
|
||||
|
||||
*schema = o.PatternProperties[k]
|
||||
patterns = append(patterns, k)
|
||||
matched = true
|
||||
validator := newSchemaValidator(schema, o.Root, fmt.Sprintf("%s.%s", o.Path, key), o.KnownFormats, o.Options)
|
||||
|
||||
res := validator.Validate(value)
|
||||
result.Merge(res)
|
||||
}
|
||||
|
||||
return matched, succeededOnce, patterns
|
||||
}
|
||||
|
||||
func (o *objectValidator) redeem() {
|
||||
pools.poolOfObjectValidators.RedeemValidator(o)
|
||||
}
|
62
vendor/github.com/go-openapi/validate/options.go
generated
vendored
Normal file
62
vendor/github.com/go-openapi/validate/options.go
generated
vendored
Normal file
@ -0,0 +1,62 @@
|
||||
// Copyright 2015 go-swagger maintainers
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package validate
|
||||
|
||||
import "sync"
|
||||
|
||||
// Opts specifies validation options for a SpecValidator.
|
||||
//
|
||||
// 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{
|
||||
// default is to stop validation on errors
|
||||
ContinueOnErrors: false,
|
||||
|
||||
// StrictPathParamUniqueness is defaulted to true. This maintains existing
|
||||
// behavior.
|
||||
StrictPathParamUniqueness: true,
|
||||
}
|
||||
|
||||
defaultOptsMutex = &sync.Mutex{}
|
||||
)
|
||||
|
||||
// SetContinueOnErrors sets global default behavior regarding spec validation errors reporting.
|
||||
//
|
||||
// For extended error reporting, you most likely want to set it to true.
|
||||
// For faster validation, it's better to give up early when a spec is detected as invalid: set it to false (this is the default).
|
||||
//
|
||||
// Setting this mode does NOT affect the validation status.
|
||||
//
|
||||
// NOTE: this method affects global defaults. It is not suitable for a concurrent usage.
|
||||
func SetContinueOnErrors(c bool) {
|
||||
defer defaultOptsMutex.Unlock()
|
||||
defaultOptsMutex.Lock()
|
||||
defaultOpts.ContinueOnErrors = c
|
||||
}
|
366
vendor/github.com/go-openapi/validate/pools.go
generated
vendored
Normal file
366
vendor/github.com/go-openapi/validate/pools.go
generated
vendored
Normal file
@ -0,0 +1,366 @@
|
||||
//go:build !validatedebug
|
||||
|
||||
package validate
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/go-openapi/spec"
|
||||
)
|
||||
|
||||
var pools allPools
|
||||
|
||||
func init() {
|
||||
resetPools()
|
||||
}
|
||||
|
||||
func resetPools() {
|
||||
// NOTE: for testing purpose, we might want to reset pools after calling Validate twice.
|
||||
// The pool is corrupted in that case: calling Put twice inserts a duplicate in the pool
|
||||
// and further calls to Get are mishandled.
|
||||
|
||||
pools = allPools{
|
||||
poolOfSchemaValidators: schemaValidatorsPool{
|
||||
Pool: &sync.Pool{
|
||||
New: func() any {
|
||||
s := &SchemaValidator{}
|
||||
|
||||
return s
|
||||
},
|
||||
},
|
||||
},
|
||||
poolOfObjectValidators: objectValidatorsPool{
|
||||
Pool: &sync.Pool{
|
||||
New: func() any {
|
||||
s := &objectValidator{}
|
||||
|
||||
return s
|
||||
},
|
||||
},
|
||||
},
|
||||
poolOfSliceValidators: sliceValidatorsPool{
|
||||
Pool: &sync.Pool{
|
||||
New: func() any {
|
||||
s := &schemaSliceValidator{}
|
||||
|
||||
return s
|
||||
},
|
||||
},
|
||||
},
|
||||
poolOfItemsValidators: itemsValidatorsPool{
|
||||
Pool: &sync.Pool{
|
||||
New: func() any {
|
||||
s := &itemsValidator{}
|
||||
|
||||
return s
|
||||
},
|
||||
},
|
||||
},
|
||||
poolOfBasicCommonValidators: basicCommonValidatorsPool{
|
||||
Pool: &sync.Pool{
|
||||
New: func() any {
|
||||
s := &basicCommonValidator{}
|
||||
|
||||
return s
|
||||
},
|
||||
},
|
||||
},
|
||||
poolOfHeaderValidators: headerValidatorsPool{
|
||||
Pool: &sync.Pool{
|
||||
New: func() any {
|
||||
s := &HeaderValidator{}
|
||||
|
||||
return s
|
||||
},
|
||||
},
|
||||
},
|
||||
poolOfParamValidators: paramValidatorsPool{
|
||||
Pool: &sync.Pool{
|
||||
New: func() any {
|
||||
s := &ParamValidator{}
|
||||
|
||||
return s
|
||||
},
|
||||
},
|
||||
},
|
||||
poolOfBasicSliceValidators: basicSliceValidatorsPool{
|
||||
Pool: &sync.Pool{
|
||||
New: func() any {
|
||||
s := &basicSliceValidator{}
|
||||
|
||||
return s
|
||||
},
|
||||
},
|
||||
},
|
||||
poolOfNumberValidators: numberValidatorsPool{
|
||||
Pool: &sync.Pool{
|
||||
New: func() any {
|
||||
s := &numberValidator{}
|
||||
|
||||
return s
|
||||
},
|
||||
},
|
||||
},
|
||||
poolOfStringValidators: stringValidatorsPool{
|
||||
Pool: &sync.Pool{
|
||||
New: func() any {
|
||||
s := &stringValidator{}
|
||||
|
||||
return s
|
||||
},
|
||||
},
|
||||
},
|
||||
poolOfSchemaPropsValidators: schemaPropsValidatorsPool{
|
||||
Pool: &sync.Pool{
|
||||
New: func() any {
|
||||
s := &schemaPropsValidator{}
|
||||
|
||||
return s
|
||||
},
|
||||
},
|
||||
},
|
||||
poolOfFormatValidators: formatValidatorsPool{
|
||||
Pool: &sync.Pool{
|
||||
New: func() any {
|
||||
s := &formatValidator{}
|
||||
|
||||
return s
|
||||
},
|
||||
},
|
||||
},
|
||||
poolOfTypeValidators: typeValidatorsPool{
|
||||
Pool: &sync.Pool{
|
||||
New: func() any {
|
||||
s := &typeValidator{}
|
||||
|
||||
return s
|
||||
},
|
||||
},
|
||||
},
|
||||
poolOfSchemas: schemasPool{
|
||||
Pool: &sync.Pool{
|
||||
New: func() any {
|
||||
s := &spec.Schema{}
|
||||
|
||||
return s
|
||||
},
|
||||
},
|
||||
},
|
||||
poolOfResults: resultsPool{
|
||||
Pool: &sync.Pool{
|
||||
New: func() any {
|
||||
s := &Result{}
|
||||
|
||||
return s
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
type (
|
||||
allPools struct {
|
||||
// memory pools for all validator objects.
|
||||
//
|
||||
// Each pool can be borrowed from and redeemed to.
|
||||
poolOfSchemaValidators schemaValidatorsPool
|
||||
poolOfObjectValidators objectValidatorsPool
|
||||
poolOfSliceValidators sliceValidatorsPool
|
||||
poolOfItemsValidators itemsValidatorsPool
|
||||
poolOfBasicCommonValidators basicCommonValidatorsPool
|
||||
poolOfHeaderValidators headerValidatorsPool
|
||||
poolOfParamValidators paramValidatorsPool
|
||||
poolOfBasicSliceValidators basicSliceValidatorsPool
|
||||
poolOfNumberValidators numberValidatorsPool
|
||||
poolOfStringValidators stringValidatorsPool
|
||||
poolOfSchemaPropsValidators schemaPropsValidatorsPool
|
||||
poolOfFormatValidators formatValidatorsPool
|
||||
poolOfTypeValidators typeValidatorsPool
|
||||
poolOfSchemas schemasPool
|
||||
poolOfResults resultsPool
|
||||
}
|
||||
|
||||
schemaValidatorsPool struct {
|
||||
*sync.Pool
|
||||
}
|
||||
|
||||
objectValidatorsPool struct {
|
||||
*sync.Pool
|
||||
}
|
||||
|
||||
sliceValidatorsPool struct {
|
||||
*sync.Pool
|
||||
}
|
||||
|
||||
itemsValidatorsPool struct {
|
||||
*sync.Pool
|
||||
}
|
||||
|
||||
basicCommonValidatorsPool struct {
|
||||
*sync.Pool
|
||||
}
|
||||
|
||||
headerValidatorsPool struct {
|
||||
*sync.Pool
|
||||
}
|
||||
|
||||
paramValidatorsPool struct {
|
||||
*sync.Pool
|
||||
}
|
||||
|
||||
basicSliceValidatorsPool struct {
|
||||
*sync.Pool
|
||||
}
|
||||
|
||||
numberValidatorsPool struct {
|
||||
*sync.Pool
|
||||
}
|
||||
|
||||
stringValidatorsPool struct {
|
||||
*sync.Pool
|
||||
}
|
||||
|
||||
schemaPropsValidatorsPool struct {
|
||||
*sync.Pool
|
||||
}
|
||||
|
||||
formatValidatorsPool struct {
|
||||
*sync.Pool
|
||||
}
|
||||
|
||||
typeValidatorsPool struct {
|
||||
*sync.Pool
|
||||
}
|
||||
|
||||
schemasPool struct {
|
||||
*sync.Pool
|
||||
}
|
||||
|
||||
resultsPool struct {
|
||||
*sync.Pool
|
||||
}
|
||||
)
|
||||
|
||||
func (p schemaValidatorsPool) BorrowValidator() *SchemaValidator {
|
||||
return p.Get().(*SchemaValidator)
|
||||
}
|
||||
|
||||
func (p schemaValidatorsPool) RedeemValidator(s *SchemaValidator) {
|
||||
// NOTE: s might be nil. In that case, Put is a noop.
|
||||
p.Put(s)
|
||||
}
|
||||
|
||||
func (p objectValidatorsPool) BorrowValidator() *objectValidator {
|
||||
return p.Get().(*objectValidator)
|
||||
}
|
||||
|
||||
func (p objectValidatorsPool) RedeemValidator(s *objectValidator) {
|
||||
p.Put(s)
|
||||
}
|
||||
|
||||
func (p sliceValidatorsPool) BorrowValidator() *schemaSliceValidator {
|
||||
return p.Get().(*schemaSliceValidator)
|
||||
}
|
||||
|
||||
func (p sliceValidatorsPool) RedeemValidator(s *schemaSliceValidator) {
|
||||
p.Put(s)
|
||||
}
|
||||
|
||||
func (p itemsValidatorsPool) BorrowValidator() *itemsValidator {
|
||||
return p.Get().(*itemsValidator)
|
||||
}
|
||||
|
||||
func (p itemsValidatorsPool) RedeemValidator(s *itemsValidator) {
|
||||
p.Put(s)
|
||||
}
|
||||
|
||||
func (p basicCommonValidatorsPool) BorrowValidator() *basicCommonValidator {
|
||||
return p.Get().(*basicCommonValidator)
|
||||
}
|
||||
|
||||
func (p basicCommonValidatorsPool) RedeemValidator(s *basicCommonValidator) {
|
||||
p.Put(s)
|
||||
}
|
||||
|
||||
func (p headerValidatorsPool) BorrowValidator() *HeaderValidator {
|
||||
return p.Get().(*HeaderValidator)
|
||||
}
|
||||
|
||||
func (p headerValidatorsPool) RedeemValidator(s *HeaderValidator) {
|
||||
p.Put(s)
|
||||
}
|
||||
|
||||
func (p paramValidatorsPool) BorrowValidator() *ParamValidator {
|
||||
return p.Get().(*ParamValidator)
|
||||
}
|
||||
|
||||
func (p paramValidatorsPool) RedeemValidator(s *ParamValidator) {
|
||||
p.Put(s)
|
||||
}
|
||||
|
||||
func (p basicSliceValidatorsPool) BorrowValidator() *basicSliceValidator {
|
||||
return p.Get().(*basicSliceValidator)
|
||||
}
|
||||
|
||||
func (p basicSliceValidatorsPool) RedeemValidator(s *basicSliceValidator) {
|
||||
p.Put(s)
|
||||
}
|
||||
|
||||
func (p numberValidatorsPool) BorrowValidator() *numberValidator {
|
||||
return p.Get().(*numberValidator)
|
||||
}
|
||||
|
||||
func (p numberValidatorsPool) RedeemValidator(s *numberValidator) {
|
||||
p.Put(s)
|
||||
}
|
||||
|
||||
func (p stringValidatorsPool) BorrowValidator() *stringValidator {
|
||||
return p.Get().(*stringValidator)
|
||||
}
|
||||
|
||||
func (p stringValidatorsPool) RedeemValidator(s *stringValidator) {
|
||||
p.Put(s)
|
||||
}
|
||||
|
||||
func (p schemaPropsValidatorsPool) BorrowValidator() *schemaPropsValidator {
|
||||
return p.Get().(*schemaPropsValidator)
|
||||
}
|
||||
|
||||
func (p schemaPropsValidatorsPool) RedeemValidator(s *schemaPropsValidator) {
|
||||
p.Put(s)
|
||||
}
|
||||
|
||||
func (p formatValidatorsPool) BorrowValidator() *formatValidator {
|
||||
return p.Get().(*formatValidator)
|
||||
}
|
||||
|
||||
func (p formatValidatorsPool) RedeemValidator(s *formatValidator) {
|
||||
p.Put(s)
|
||||
}
|
||||
|
||||
func (p typeValidatorsPool) BorrowValidator() *typeValidator {
|
||||
return p.Get().(*typeValidator)
|
||||
}
|
||||
|
||||
func (p typeValidatorsPool) RedeemValidator(s *typeValidator) {
|
||||
p.Put(s)
|
||||
}
|
||||
|
||||
func (p schemasPool) BorrowSchema() *spec.Schema {
|
||||
return p.Get().(*spec.Schema)
|
||||
}
|
||||
|
||||
func (p schemasPool) RedeemSchema(s *spec.Schema) {
|
||||
p.Put(s)
|
||||
}
|
||||
|
||||
func (p resultsPool) BorrowResult() *Result {
|
||||
return p.Get().(*Result).cleared()
|
||||
}
|
||||
|
||||
func (p resultsPool) RedeemResult(s *Result) {
|
||||
if s == emptyResult {
|
||||
return
|
||||
}
|
||||
p.Put(s)
|
||||
}
|
1012
vendor/github.com/go-openapi/validate/pools_debug.go
generated
vendored
Normal file
1012
vendor/github.com/go-openapi/validate/pools_debug.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
563
vendor/github.com/go-openapi/validate/result.go
generated
vendored
Normal file
563
vendor/github.com/go-openapi/validate/result.go
generated
vendored
Normal file
@ -0,0 +1,563 @@
|
||||
// Copyright 2015 go-swagger maintainers
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package validate
|
||||
|
||||
import (
|
||||
stderrors "errors"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
"github.com/go-openapi/errors"
|
||||
"github.com/go-openapi/spec"
|
||||
)
|
||||
|
||||
var emptyResult = &Result{MatchCount: 1}
|
||||
|
||||
// Result represents a validation result set, composed of
|
||||
// errors and warnings.
|
||||
//
|
||||
// It is used to keep track of all detected errors and warnings during
|
||||
// the validation of a specification.
|
||||
//
|
||||
// Matchcount is used to determine
|
||||
// which errors are relevant in the case of AnyOf, OneOf
|
||||
// schema validation. Results from the validation branch
|
||||
// with most matches get eventually selected.
|
||||
//
|
||||
// TODO: keep path of key originating the error
|
||||
type Result struct {
|
||||
Errors []error
|
||||
Warnings []error
|
||||
MatchCount int
|
||||
|
||||
// the object data
|
||||
data interface{}
|
||||
|
||||
// Schemata for the root object
|
||||
rootObjectSchemata schemata
|
||||
// Schemata for object fields
|
||||
fieldSchemata []fieldSchemata
|
||||
// Schemata for slice items
|
||||
itemSchemata []itemSchemata
|
||||
|
||||
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.
|
||||
type FieldKey struct {
|
||||
object reflect.Value // actually a map[string]interface{}, but the latter cannot be a key
|
||||
field string
|
||||
}
|
||||
|
||||
// ItemKey is a pair of a slice and an index, usable as a key for a map.
|
||||
type ItemKey struct {
|
||||
slice reflect.Value // actually a []interface{}, but the latter cannot be a key
|
||||
index int
|
||||
}
|
||||
|
||||
// NewFieldKey returns a pair of an object and field usable as a key of a map.
|
||||
func NewFieldKey(obj map[string]interface{}, field string) FieldKey {
|
||||
return FieldKey{object: reflect.ValueOf(obj), field: field}
|
||||
}
|
||||
|
||||
// Object returns the underlying object of this key.
|
||||
func (fk *FieldKey) Object() map[string]interface{} {
|
||||
return fk.object.Interface().(map[string]interface{})
|
||||
}
|
||||
|
||||
// Field returns the underlying field of this key.
|
||||
func (fk *FieldKey) Field() string {
|
||||
return fk.field
|
||||
}
|
||||
|
||||
// NewItemKey returns a pair of a slice and index usable as a key of a map.
|
||||
func NewItemKey(slice interface{}, i int) ItemKey {
|
||||
return ItemKey{slice: reflect.ValueOf(slice), index: i}
|
||||
}
|
||||
|
||||
// Slice returns the underlying slice of this key.
|
||||
func (ik *ItemKey) Slice() []interface{} {
|
||||
return ik.slice.Interface().([]interface{})
|
||||
}
|
||||
|
||||
// Index returns the underlying index of this key.
|
||||
func (ik *ItemKey) Index() int {
|
||||
return ik.index
|
||||
}
|
||||
|
||||
type fieldSchemata struct {
|
||||
obj map[string]interface{}
|
||||
field string
|
||||
schemata schemata
|
||||
}
|
||||
|
||||
type itemSchemata struct {
|
||||
slice reflect.Value
|
||||
index int
|
||||
schemata schemata
|
||||
}
|
||||
|
||||
// Merge merges this result with the other one(s), preserving match counts etc.
|
||||
func (r *Result) Merge(others ...*Result) *Result {
|
||||
for _, other := range others {
|
||||
if other == nil {
|
||||
continue
|
||||
}
|
||||
r.mergeWithoutRootSchemata(other)
|
||||
r.rootObjectSchemata.Append(other.rootObjectSchemata)
|
||||
if other.wantsRedeemOnMerge {
|
||||
pools.poolOfResults.RedeemResult(other)
|
||||
}
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
// Data returns the original data object used for validation. Mutating this renders
|
||||
// the result invalid.
|
||||
func (r *Result) Data() interface{} {
|
||||
return r.data
|
||||
}
|
||||
|
||||
// RootObjectSchemata returns the schemata which apply to the root object.
|
||||
func (r *Result) RootObjectSchemata() []*spec.Schema {
|
||||
return r.rootObjectSchemata.Slice()
|
||||
}
|
||||
|
||||
// FieldSchemata returns the schemata which apply to fields in objects.
|
||||
func (r *Result) FieldSchemata() map[FieldKey][]*spec.Schema {
|
||||
if r.cachedFieldSchemata != nil {
|
||||
return r.cachedFieldSchemata
|
||||
}
|
||||
|
||||
ret := make(map[FieldKey][]*spec.Schema, len(r.fieldSchemata))
|
||||
for _, fs := range r.fieldSchemata {
|
||||
key := NewFieldKey(fs.obj, fs.field)
|
||||
if fs.schemata.one != nil {
|
||||
ret[key] = append(ret[key], fs.schemata.one)
|
||||
} else if len(fs.schemata.multiple) > 0 {
|
||||
ret[key] = append(ret[key], fs.schemata.multiple...)
|
||||
}
|
||||
}
|
||||
r.cachedFieldSchemata = ret
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
// ItemSchemata returns the schemata which apply to items in slices.
|
||||
func (r *Result) ItemSchemata() map[ItemKey][]*spec.Schema {
|
||||
if r.cachedItemSchemata != nil {
|
||||
return r.cachedItemSchemata
|
||||
}
|
||||
|
||||
ret := make(map[ItemKey][]*spec.Schema, len(r.itemSchemata))
|
||||
for _, ss := range r.itemSchemata {
|
||||
key := NewItemKey(ss.slice, ss.index)
|
||||
if ss.schemata.one != nil {
|
||||
ret[key] = append(ret[key], ss.schemata.one)
|
||||
} else if len(ss.schemata.multiple) > 0 {
|
||||
ret[key] = append(ret[key], ss.schemata.multiple...)
|
||||
}
|
||||
}
|
||||
r.cachedItemSchemata = ret
|
||||
return ret
|
||||
}
|
||||
|
||||
func (r *Result) resetCaches() {
|
||||
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
|
||||
func (r *Result) mergeForField(obj map[string]interface{}, field string, other *Result) *Result {
|
||||
if other == nil {
|
||||
return r
|
||||
}
|
||||
r.mergeWithoutRootSchemata(other)
|
||||
|
||||
if other.rootObjectSchemata.Len() > 0 {
|
||||
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.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
|
||||
func (r *Result) mergeForSlice(slice reflect.Value, i int, other *Result) *Result {
|
||||
if other == nil {
|
||||
return r
|
||||
}
|
||||
r.mergeWithoutRootSchemata(other)
|
||||
|
||||
if other.rootObjectSchemata.Len() > 0 {
|
||||
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.Clone(),
|
||||
})
|
||||
}
|
||||
|
||||
if other.wantsRedeemOnMerge {
|
||||
pools.poolOfResults.RedeemResult(other)
|
||||
}
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
// addRootObjectSchemata adds the given schemata for the root object of the 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) {
|
||||
clone := *s
|
||||
r.rootObjectSchemata.Append(schemata{one: &clone})
|
||||
}
|
||||
|
||||
// addPropertySchemata adds the given schemata for the object and field.
|
||||
//
|
||||
// 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))
|
||||
}
|
||||
clone := *schema
|
||||
r.fieldSchemata = append(r.fieldSchemata, fieldSchemata{obj: obj, field: fld, schemata: schemata{one: &clone}})
|
||||
}
|
||||
|
||||
/*
|
||||
// addSliceSchemata adds the given schemata for the slice and index.
|
||||
// The slice schemata might be reused. I.e. do not modify it after being added to a result.
|
||||
func (r *Result) addSliceSchemata(slice reflect.Value, i int, schema *spec.Schema) {
|
||||
if r.itemSchemata == nil {
|
||||
r.itemSchemata = make([]itemSchemata, 0, slice.Len())
|
||||
}
|
||||
r.itemSchemata = append(r.itemSchemata, itemSchemata{slice: slice, index: i, schemata: schemata{one: schema}})
|
||||
}
|
||||
*/
|
||||
|
||||
// mergeWithoutRootSchemata merges other into r, ignoring the rootObject schemata.
|
||||
func (r *Result) mergeWithoutRootSchemata(other *Result) {
|
||||
r.resetCaches()
|
||||
r.AddErrors(other.Errors...)
|
||||
r.AddWarnings(other.Warnings...)
|
||||
r.MatchCount += other.MatchCount
|
||||
|
||||
if other.fieldSchemata != nil {
|
||||
if r.fieldSchemata == nil {
|
||||
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 = make([]itemSchemata, 0, len(other.itemSchemata))
|
||||
}
|
||||
for _, field := range other.itemSchemata {
|
||||
field.schemata = field.schemata.Clone()
|
||||
r.itemSchemata = append(r.itemSchemata, field)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MergeAsErrors merges this result with the other one(s), preserving match counts etc.
|
||||
//
|
||||
// Warnings from input are merged as Errors in the returned merged Result.
|
||||
func (r *Result) MergeAsErrors(others ...*Result) *Result {
|
||||
for _, other := range others {
|
||||
if other != nil {
|
||||
r.resetCaches()
|
||||
r.AddErrors(other.Errors...)
|
||||
r.AddErrors(other.Warnings...)
|
||||
r.MatchCount += other.MatchCount
|
||||
if other.wantsRedeemOnMerge {
|
||||
pools.poolOfResults.RedeemResult(other)
|
||||
}
|
||||
}
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
// MergeAsWarnings merges this result with the other one(s), preserving match counts etc.
|
||||
//
|
||||
// Errors from input are merged as Warnings in the returned merged Result.
|
||||
func (r *Result) MergeAsWarnings(others ...*Result) *Result {
|
||||
for _, other := range others {
|
||||
if other != nil {
|
||||
r.resetCaches()
|
||||
r.AddWarnings(other.Errors...)
|
||||
r.AddWarnings(other.Warnings...)
|
||||
r.MatchCount += other.MatchCount
|
||||
if other.wantsRedeemOnMerge {
|
||||
pools.poolOfResults.RedeemResult(other)
|
||||
}
|
||||
}
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
// AddErrors adds errors to this validation result (if not already reported).
|
||||
//
|
||||
// Since the same check may be passed several times while exploring the
|
||||
// spec structure (via $ref, ...) reported messages are kept
|
||||
// unique.
|
||||
func (r *Result) AddErrors(errors ...error) {
|
||||
for _, e := range errors {
|
||||
found := false
|
||||
if e != nil {
|
||||
for _, isReported := range r.Errors {
|
||||
if e.Error() == isReported.Error() {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
r.Errors = append(r.Errors, e)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// AddWarnings adds warnings to this validation result (if not already reported).
|
||||
func (r *Result) AddWarnings(warnings ...error) {
|
||||
for _, e := range warnings {
|
||||
found := false
|
||||
if e != nil {
|
||||
for _, isReported := range r.Warnings {
|
||||
if e.Error() == isReported.Error() {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
r.Warnings = append(r.Warnings, e)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Result) keepRelevantErrors() *Result {
|
||||
// TODO: this one is going to disapear...
|
||||
// keepRelevantErrors strips a result from standard errors and keeps
|
||||
// the ones which are supposedly more accurate.
|
||||
//
|
||||
// The original result remains unaffected (creates a new instance of Result).
|
||||
// This method is used to work around the "matchCount" filter which would otherwise
|
||||
// strip our result from some accurate error reporting from lower level validators.
|
||||
//
|
||||
// NOTE: this implementation with a placeholder (IMPORTANT!) is neither clean nor
|
||||
// very efficient. On the other hand, relying on go-openapi/errors to manipulate
|
||||
// codes would require to change a lot here. So, for the moment, let's go with
|
||||
// placeholders.
|
||||
strippedErrors := []error{}
|
||||
for _, e := range r.Errors {
|
||||
if strings.HasPrefix(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, stderrors.New(strings.TrimPrefix(e.Error(), "IMPORTANT!")))
|
||||
}
|
||||
}
|
||||
var strippedResult *Result
|
||||
if r.wantsRedeemOnMerge {
|
||||
strippedResult = pools.poolOfResults.BorrowResult()
|
||||
} else {
|
||||
strippedResult = new(Result)
|
||||
}
|
||||
strippedResult.Errors = strippedErrors
|
||||
strippedResult.Warnings = strippedWarnings
|
||||
return strippedResult
|
||||
}
|
||||
|
||||
// IsValid returns true when this result is valid.
|
||||
//
|
||||
// Returns true on a nil *Result.
|
||||
func (r *Result) IsValid() bool {
|
||||
if r == nil {
|
||||
return true
|
||||
}
|
||||
return len(r.Errors) == 0
|
||||
}
|
||||
|
||||
// HasErrors returns true when this result is invalid.
|
||||
//
|
||||
// Returns false on a nil *Result.
|
||||
func (r *Result) HasErrors() bool {
|
||||
if r == nil {
|
||||
return false
|
||||
}
|
||||
return !r.IsValid()
|
||||
}
|
||||
|
||||
// HasWarnings returns true when this result contains warnings.
|
||||
//
|
||||
// Returns false on a nil *Result.
|
||||
func (r *Result) HasWarnings() bool {
|
||||
if r == nil {
|
||||
return false
|
||||
}
|
||||
return len(r.Warnings) > 0
|
||||
}
|
||||
|
||||
// HasErrorsOrWarnings returns true when this result contains
|
||||
// either errors or warnings.
|
||||
//
|
||||
// Returns false on a nil *Result.
|
||||
func (r *Result) HasErrorsOrWarnings() bool {
|
||||
if r == nil {
|
||||
return false
|
||||
}
|
||||
return len(r.Errors) > 0 || len(r.Warnings) > 0
|
||||
}
|
||||
|
||||
// Inc increments the match count
|
||||
func (r *Result) Inc() {
|
||||
r.MatchCount++
|
||||
}
|
||||
|
||||
// AsError renders this result as an error interface
|
||||
//
|
||||
// TODO: reporting / pretty print with path ordered and indented
|
||||
func (r *Result) AsError() error {
|
||||
if r.IsValid() {
|
||||
return nil
|
||||
}
|
||||
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 {
|
||||
// one is set if there is exactly one schema. In that case multiple must be nil.
|
||||
one *spec.Schema
|
||||
// multiple is an arbitrary number of schemas. If it is set, one must be nil.
|
||||
multiple []*spec.Schema
|
||||
}
|
||||
|
||||
func (s *schemata) Len() int {
|
||||
if s.one != nil {
|
||||
return 1
|
||||
}
|
||||
return len(s.multiple)
|
||||
}
|
||||
|
||||
func (s *schemata) Slice() []*spec.Schema {
|
||||
if s == nil {
|
||||
return nil
|
||||
}
|
||||
if s.one != nil {
|
||||
return []*spec.Schema{s.one}
|
||||
}
|
||||
return s.multiple
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
if s.one == nil && len(s.multiple) == 0 {
|
||||
*s = other
|
||||
return
|
||||
}
|
||||
|
||||
if s.one != nil {
|
||||
if other.one != nil {
|
||||
s.multiple = []*spec.Schema{s.one, other.one}
|
||||
} else {
|
||||
t := make([]*spec.Schema, 0, 1+len(other.multiple))
|
||||
s.multiple = append(append(t, s.one), other.multiple...)
|
||||
}
|
||||
s.one = nil
|
||||
} else {
|
||||
if other.one != nil {
|
||||
s.multiple = append(s.multiple, other.one)
|
||||
} else {
|
||||
if cap(s.multiple) >= len(s.multiple)+len(other.multiple) {
|
||||
s.multiple = append(s.multiple, other.multiple...)
|
||||
} else {
|
||||
t := make([]*spec.Schema, 0, len(s.multiple)+len(other.multiple))
|
||||
s.multiple = append(append(t, s.multiple...), other.multiple...)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
71
vendor/github.com/go-openapi/validate/rexp.go
generated
vendored
Normal file
71
vendor/github.com/go-openapi/validate/rexp.go
generated
vendored
Normal file
@ -0,0 +1,71 @@
|
||||
// Copyright 2015 go-swagger maintainers
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package validate
|
||||
|
||||
import (
|
||||
re "regexp"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
// Cache for compiled regular expressions
|
||||
var (
|
||||
cacheMutex = &sync.Mutex{}
|
||||
reDict = atomic.Value{} // map[string]*re.Regexp
|
||||
)
|
||||
|
||||
func compileRegexp(pattern string) (*re.Regexp, error) {
|
||||
if cache, ok := reDict.Load().(map[string]*re.Regexp); ok {
|
||||
if r := cache[pattern]; r != nil {
|
||||
return r, nil
|
||||
}
|
||||
}
|
||||
|
||||
r, err := re.Compile(pattern)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cacheRegexp(r)
|
||||
return r, nil
|
||||
}
|
||||
|
||||
func mustCompileRegexp(pattern string) *re.Regexp {
|
||||
if cache, ok := reDict.Load().(map[string]*re.Regexp); ok {
|
||||
if r := cache[pattern]; r != nil {
|
||||
return r
|
||||
}
|
||||
}
|
||||
|
||||
r := re.MustCompile(pattern)
|
||||
cacheRegexp(r)
|
||||
return r
|
||||
}
|
||||
|
||||
func cacheRegexp(r *re.Regexp) {
|
||||
cacheMutex.Lock()
|
||||
defer cacheMutex.Unlock()
|
||||
|
||||
if cache, ok := reDict.Load().(map[string]*re.Regexp); !ok || cache[r.String()] == nil {
|
||||
newCache := map[string]*re.Regexp{
|
||||
r.String(): r,
|
||||
}
|
||||
|
||||
for k, v := range cache {
|
||||
newCache[k] = v
|
||||
}
|
||||
|
||||
reDict.Store(newCache)
|
||||
}
|
||||
}
|
354
vendor/github.com/go-openapi/validate/schema.go
generated
vendored
Normal file
354
vendor/github.com/go-openapi/validate/schema.go
generated
vendored
Normal file
@ -0,0 +1,354 @@
|
||||
// Copyright 2015 go-swagger maintainers
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package validate
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"reflect"
|
||||
|
||||
"github.com/go-openapi/errors"
|
||||
"github.com/go-openapi/spec"
|
||||
"github.com/go-openapi/strfmt"
|
||||
"github.com/go-openapi/swag"
|
||||
)
|
||||
|
||||
// SchemaValidator validates data against a JSON schema
|
||||
type SchemaValidator struct {
|
||||
Path string
|
||||
in string
|
||||
Schema *spec.Schema
|
||||
validators [8]valueValidator
|
||||
Root interface{}
|
||||
KnownFormats strfmt.Registry
|
||||
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,
|
||||
append(options, WithRecycleValidators(true), withRecycleResults(true))...,
|
||||
).Validate(data)
|
||||
defer func() {
|
||||
pools.poolOfResults.RedeemResult(res)
|
||||
}()
|
||||
|
||||
if res.HasErrors() {
|
||||
return errors.CompositeValidationError(res.Errors...)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewSchemaValidator creates a new schema validator.
|
||||
//
|
||||
// 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
|
||||
}
|
||||
|
||||
if rootSchema == nil {
|
||||
rootSchema = schema
|
||||
}
|
||||
|
||||
if schema.ID != "" || schema.Ref.String() != "" || schema.Ref.IsRoot() {
|
||||
err := spec.ExpandSchema(schema, rootSchema, nil)
|
||||
if err != nil {
|
||||
msg := invalidSchemaProvidedMsg(err).Error()
|
||||
panic(msg)
|
||||
}
|
||||
}
|
||||
|
||||
if opts == nil {
|
||||
opts = new(SchemaValidatorOptions)
|
||||
}
|
||||
|
||||
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(),
|
||||
s.formatValidator(),
|
||||
s.numberValidator(),
|
||||
s.sliceValidator(),
|
||||
s.commonValidator(),
|
||||
s.objectValidator(),
|
||||
}
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
// SetPath sets the path for this schema valdiator
|
||||
func (s *SchemaValidator) SetPath(path string) {
|
||||
s.Path = path
|
||||
}
|
||||
|
||||
// Applies returns true when this schema validator applies
|
||||
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 {
|
||||
if s == nil {
|
||||
return emptyResult
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
tpe := reflect.TypeOf(data)
|
||||
kind := tpe.Kind()
|
||||
for kind == reflect.Ptr {
|
||||
tpe = tpe.Elem()
|
||||
kind = tpe.Kind()
|
||||
}
|
||||
d := data
|
||||
|
||||
if kind == reflect.Struct {
|
||||
// NOTE: since reflect retrieves the true nature of types
|
||||
// this means that all strfmt types passed here (e.g. strfmt.Datetime, etc..)
|
||||
// are converted here to strings, and structs are systematically converted
|
||||
// to map[string]interface{}.
|
||||
d = swag.ToDynamicJSON(data)
|
||||
}
|
||||
|
||||
// TODO: this part should be handed over to type validator
|
||||
// Handle special case of json.Number data (number marshalled as string)
|
||||
isnumber := s.Schema.Type.Contains(numberType) || s.Schema.Type.Contains(integerType)
|
||||
if num, ok := data.(json.Number); ok && isnumber {
|
||||
if s.Schema.Type.Contains(integerType) { // avoid lossy conversion
|
||||
in, erri := num.Int64()
|
||||
if erri != nil {
|
||||
result.AddErrors(invalidTypeConversionMsg(s.Path, erri))
|
||||
result.Inc()
|
||||
|
||||
return result
|
||||
}
|
||||
d = in
|
||||
} else {
|
||||
nf, errf := num.Float64()
|
||||
if errf != nil {
|
||||
result.AddErrors(invalidTypeConversionMsg(s.Path, errf))
|
||||
result.Inc()
|
||||
|
||||
return result
|
||||
}
|
||||
d = nf
|
||||
}
|
||||
|
||||
tpe = reflect.TypeOf(d)
|
||||
kind = tpe.Kind()
|
||||
}
|
||||
|
||||
for idx, v := range s.validators {
|
||||
if !v.Applies(s.Schema, 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
|
||||
}
|
||||
|
||||
result.Merge(v.Validate(d))
|
||||
if s.Options.recycleValidators {
|
||||
s.validators[idx] = nil // prevents further (unsafe) usage
|
||||
}
|
||||
result.Inc()
|
||||
}
|
||||
result.Inc()
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func (s *SchemaValidator) typeValidator() valueValidator {
|
||||
return newTypeValidator(
|
||||
s.Path,
|
||||
s.in,
|
||||
s.Schema.Type,
|
||||
s.Schema.Nullable,
|
||||
s.Schema.Format,
|
||||
s.Options,
|
||||
)
|
||||
}
|
||||
|
||||
func (s *SchemaValidator) commonValidator() valueValidator {
|
||||
return newBasicCommonValidator(
|
||||
s.Path,
|
||||
s.in,
|
||||
s.Schema.Default,
|
||||
s.Schema.Enum,
|
||||
s.Options,
|
||||
)
|
||||
}
|
||||
|
||||
func (s *SchemaValidator) sliceValidator() valueValidator {
|
||||
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 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 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 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,
|
||||
)
|
||||
}
|
||||
|
||||
func (s *SchemaValidator) objectValidator() valueValidator {
|
||||
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
|
||||
}
|
||||
}
|
78
vendor/github.com/go-openapi/validate/schema_messages.go
generated
vendored
Normal file
78
vendor/github.com/go-openapi/validate/schema_messages.go
generated
vendored
Normal file
@ -0,0 +1,78 @@
|
||||
// Copyright 2015 go-swagger maintainers
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package validate
|
||||
|
||||
import (
|
||||
"github.com/go-openapi/errors"
|
||||
)
|
||||
|
||||
// Error messages related to schema validation and returned as results.
|
||||
const (
|
||||
// ArrayDoesNotAllowAdditionalItemsError when an additionalItems construct is not verified by the array values provided.
|
||||
//
|
||||
// TODO: should move to package go-openapi/errors
|
||||
ArrayDoesNotAllowAdditionalItemsError = "array doesn't allow for additional items"
|
||||
|
||||
// HasDependencyError indicates that a dependencies construct was not verified
|
||||
HasDependencyError = "%q has a dependency on %s"
|
||||
|
||||
// InvalidSchemaProvidedError indicates that the schema provided to validate a value cannot be properly compiled
|
||||
InvalidSchemaProvidedError = "Invalid schema provided to SchemaValidator: %v"
|
||||
|
||||
// InvalidTypeConversionError indicates that a numerical conversion for the given type could not be carried on
|
||||
InvalidTypeConversionError = "invalid type conversion in %s: %v "
|
||||
|
||||
// MustValidateAtLeastOneSchemaError indicates that in a AnyOf construct, none of the schema constraints specified were verified
|
||||
MustValidateAtLeastOneSchemaError = "%q must validate at least one schema (anyOf)"
|
||||
|
||||
// MustValidateOnlyOneSchemaError indicates that in a OneOf construct, either none of the schema constraints specified were verified, or several were
|
||||
MustValidateOnlyOneSchemaError = "%q must validate one and only one schema (oneOf). %s"
|
||||
|
||||
// MustValidateAllSchemasError indicates that in a AllOf construct, at least one of the schema constraints specified were not verified
|
||||
//
|
||||
// TODO: punctuation in message
|
||||
MustValidateAllSchemasError = "%q must validate all the schemas (allOf)%s"
|
||||
|
||||
// MustNotValidateSchemaError indicates that in a Not construct, the schema constraint specified was verified
|
||||
MustNotValidateSchemaError = "%q must not validate the schema (not)"
|
||||
)
|
||||
|
||||
// Warning messages related to schema validation and returned as results
|
||||
const ()
|
||||
|
||||
func invalidSchemaProvidedMsg(err error) errors.Error {
|
||||
return errors.New(InternalErrorCode, InvalidSchemaProvidedError, err)
|
||||
}
|
||||
func invalidTypeConversionMsg(path string, err error) errors.Error {
|
||||
return errors.New(errors.CompositeErrorCode, InvalidTypeConversionError, path, err)
|
||||
}
|
||||
func mustValidateOnlyOneSchemaMsg(path, additionalMsg string) errors.Error {
|
||||
return errors.New(errors.CompositeErrorCode, MustValidateOnlyOneSchemaError, path, additionalMsg)
|
||||
}
|
||||
func mustValidateAtLeastOneSchemaMsg(path string) errors.Error {
|
||||
return errors.New(errors.CompositeErrorCode, MustValidateAtLeastOneSchemaError, path)
|
||||
}
|
||||
func mustValidateAllSchemasMsg(path, additionalMsg string) errors.Error {
|
||||
return errors.New(errors.CompositeErrorCode, MustValidateAllSchemasError, path, additionalMsg)
|
||||
}
|
||||
func mustNotValidatechemaMsg(path string) errors.Error {
|
||||
return errors.New(errors.CompositeErrorCode, MustNotValidateSchemaError, path)
|
||||
}
|
||||
func hasADependencyMsg(path, depkey string) errors.Error {
|
||||
return errors.New(errors.CompositeErrorCode, HasDependencyError, path, depkey)
|
||||
}
|
||||
func arrayDoesNotAllowAdditionalItemsMsg() errors.Error {
|
||||
return errors.New(errors.CompositeErrorCode, ArrayDoesNotAllowAdditionalItemsError)
|
||||
}
|
83
vendor/github.com/go-openapi/validate/schema_option.go
generated
vendored
Normal file
83
vendor/github.com/go-openapi/validate/schema_option.go
generated
vendored
Normal file
@ -0,0 +1,83 @@
|
||||
// Copyright 2015 go-swagger maintainers
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package validate
|
||||
|
||||
// SchemaValidatorOptions defines optional rules for schema validation
|
||||
type SchemaValidatorOptions struct {
|
||||
EnableObjectArrayTypeCheck bool
|
||||
EnableArrayMustHaveItemsCheck bool
|
||||
recycleValidators bool
|
||||
recycleResult bool
|
||||
skipSchemataResult bool
|
||||
}
|
||||
|
||||
// Option sets optional rules for schema validation
|
||||
type Option func(*SchemaValidatorOptions)
|
||||
|
||||
// EnableObjectArrayTypeCheck activates the swagger rule: an items must be in type: array
|
||||
func EnableObjectArrayTypeCheck(enable bool) Option {
|
||||
return func(svo *SchemaValidatorOptions) {
|
||||
svo.EnableObjectArrayTypeCheck = enable
|
||||
}
|
||||
}
|
||||
|
||||
// EnableArrayMustHaveItemsCheck activates the swagger rule: an array must have items defined
|
||||
func EnableArrayMustHaveItemsCheck(enable bool) Option {
|
||||
return func(svo *SchemaValidatorOptions) {
|
||||
svo.EnableArrayMustHaveItemsCheck = enable
|
||||
}
|
||||
}
|
||||
|
||||
// SwaggerSchema activates swagger schema validation rules
|
||||
func SwaggerSchema(enable bool) Option {
|
||||
return func(svo *SchemaValidatorOptions) {
|
||||
svo.EnableObjectArrayTypeCheck = enable
|
||||
svo.EnableArrayMustHaveItemsCheck = enable
|
||||
}
|
||||
}
|
||||
|
||||
// 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),
|
||||
}
|
||||
}
|
356
vendor/github.com/go-openapi/validate/schema_props.go
generated
vendored
Normal file
356
vendor/github.com/go-openapi/validate/schema_props.go
generated
vendored
Normal file
@ -0,0 +1,356 @@
|
||||
// Copyright 2015 go-swagger maintainers
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package validate
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
|
||||
"github.com/go-openapi/spec"
|
||||
"github.com/go-openapi/strfmt"
|
||||
)
|
||||
|
||||
type schemaPropsValidator struct {
|
||||
Path string
|
||||
In string
|
||||
AllOf []spec.Schema
|
||||
OneOf []spec.Schema
|
||||
AnyOf []spec.Schema
|
||||
Not *spec.Schema
|
||||
Dependencies spec.Dependencies
|
||||
anyOfValidators []*SchemaValidator
|
||||
allOfValidators []*SchemaValidator
|
||||
oneOfValidators []*SchemaValidator
|
||||
notValidator *SchemaValidator
|
||||
Root interface{}
|
||||
KnownFormats strfmt.Registry
|
||||
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,
|
||||
opts *SchemaValidatorOptions) *schemaPropsValidator {
|
||||
if opts == nil {
|
||||
opts = new(SchemaValidatorOptions)
|
||||
}
|
||||
|
||||
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 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, 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{}, _ reflect.Kind) bool {
|
||||
_, isSchema := source.(*spec.Schema)
|
||||
return isSchema
|
||||
}
|
||||
|
||||
func (s *schemaPropsValidator) Validate(data interface{}) *Result {
|
||||
var mainResult *Result
|
||||
if s.Options.recycleResult {
|
||||
mainResult = pools.poolOfResults.BorrowResult()
|
||||
} else {
|
||||
mainResult = new(Result)
|
||||
}
|
||||
|
||||
// Intermediary error results
|
||||
|
||||
// IMPORTANT! messages from underlying validators
|
||||
var keepResultAnyOf, keepResultOneOf, keepResultAllOf *Result
|
||||
|
||||
if s.Options.recycleValidators {
|
||||
defer func() {
|
||||
s.redeemChildren()
|
||||
s.redeem()
|
||||
|
||||
// results are redeemed when merged
|
||||
}()
|
||||
}
|
||||
|
||||
if len(s.anyOfValidators) > 0 {
|
||||
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
|
||||
}
|
||||
|
||||
// MatchCount is used to select errors from the schema with most positive checks
|
||||
if bestFailures == nil || result.MatchCount > bestFailures.MatchCount {
|
||||
if bestFailures != nil && bestFailures.wantsRedeemOnMerge {
|
||||
pools.poolOfResults.RedeemResult(bestFailures)
|
||||
}
|
||||
bestFailures = result
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
if result.wantsRedeemOnMerge {
|
||||
pools.poolOfResults.RedeemResult(result) // this result is ditched
|
||||
}
|
||||
}
|
||||
|
||||
mainResult.AddErrors(mustValidateAtLeastOneSchemaMsg(s.Path))
|
||||
mainResult.Merge(bestFailures)
|
||||
}
|
||||
|
||||
func (s *schemaPropsValidator) validateOneOf(data interface{}, mainResult, keepResultOneOf *Result) {
|
||||
// Validates exactly one in oneOf schemas
|
||||
var (
|
||||
firstSuccess, bestFailures *Result
|
||||
validated int
|
||||
)
|
||||
|
||||
for i, oneOfSchema := range s.oneOfValidators {
|
||||
result := oneOfSchema.Validate(data)
|
||||
if s.Options.recycleValidators {
|
||||
s.oneOfValidators[i] = nil
|
||||
}
|
||||
|
||||
// We keep inner IMPORTANT! errors no matter what MatchCount tells us
|
||||
keepResultOneOf.Merge(result.keepRelevantErrors()) // merges (and redeems) a new instance of Result
|
||||
|
||||
if result.IsValid() {
|
||||
validated++
|
||||
_ = keepResultOneOf.cleared()
|
||||
|
||||
if firstSuccess == nil {
|
||||
firstSuccess = result
|
||||
} else if result.wantsRedeemOnMerge {
|
||||
pools.poolOfResults.RedeemResult(result) // this result is ditched
|
||||
}
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
// MatchCount is used to select errors from the schema with most positive checks
|
||||
if validated == 0 && (bestFailures == nil || result.MatchCount > bestFailures.MatchCount) {
|
||||
if bestFailures != nil && bestFailures.wantsRedeemOnMerge {
|
||||
pools.poolOfResults.RedeemResult(bestFailures)
|
||||
}
|
||||
bestFailures = result
|
||||
} else if result.wantsRedeemOnMerge {
|
||||
pools.poolOfResults.RedeemResult(result) // this result is ditched
|
||||
}
|
||||
}
|
||||
|
||||
switch validated {
|
||||
case 0:
|
||||
mainResult.AddErrors(mustValidateOnlyOneSchemaMsg(s.Path, "Found none valid"))
|
||||
mainResult.Merge(bestFailures)
|
||||
// firstSucess necessarily nil
|
||||
case 1:
|
||||
mainResult.Merge(firstSuccess)
|
||||
if bestFailures != nil && bestFailures.wantsRedeemOnMerge {
|
||||
pools.poolOfResults.RedeemResult(bestFailures)
|
||||
}
|
||||
default:
|
||||
mainResult.AddErrors(mustValidateOnlyOneSchemaMsg(s.Path, fmt.Sprintf("Found %d valid alternatives", validated)))
|
||||
mainResult.Merge(bestFailures)
|
||||
if firstSuccess != nil && firstSuccess.wantsRedeemOnMerge {
|
||||
pools.poolOfResults.RedeemResult(firstSuccess)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *schemaPropsValidator) validateAllOf(data interface{}, mainResult, keepResultAllOf *Result) {
|
||||
// Validates all of allOf schemas
|
||||
var validated int
|
||||
|
||||
for i, allOfSchema := range s.allOfValidators {
|
||||
result := allOfSchema.Validate(data)
|
||||
if s.Options.recycleValidators {
|
||||
s.allOfValidators[i] = nil
|
||||
}
|
||||
// We keep inner IMPORTANT! errors no matter what MatchCount tells us
|
||||
keepResultAllOf.Merge(result.keepRelevantErrors())
|
||||
if result.IsValid() {
|
||||
validated++
|
||||
}
|
||||
mainResult.Merge(result)
|
||||
}
|
||||
|
||||
switch validated {
|
||||
case 0:
|
||||
mainResult.AddErrors(mustValidateAllSchemasMsg(s.Path, ". None validated"))
|
||||
case len(s.allOfValidators):
|
||||
default:
|
||||
mainResult.AddErrors(mustValidateAllSchemasMsg(s.Path, ""))
|
||||
}
|
||||
}
|
||||
|
||||
func (s *schemaPropsValidator) validateNot(data interface{}, mainResult *Result) {
|
||||
result := s.notValidator.Validate(data)
|
||||
if s.Options.recycleValidators {
|
||||
s.notValidator = nil
|
||||
}
|
||||
// We keep inner IMPORTANT! errors no matter what MatchCount tells us
|
||||
if result.IsValid() {
|
||||
mainResult.AddErrors(mustNotValidatechemaMsg(s.Path))
|
||||
}
|
||||
if result.wantsRedeemOnMerge {
|
||||
pools.poolOfResults.RedeemResult(result) // this result is ditched
|
||||
}
|
||||
}
|
||||
|
||||
func (s *schemaPropsValidator) validateDependencies(data interface{}, mainResult *Result) {
|
||||
val := data.(map[string]interface{})
|
||||
for key := range val {
|
||||
dep, ok := s.Dependencies[key]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
if dep.Schema != nil {
|
||||
mainResult.Merge(
|
||||
newSchemaValidator(dep.Schema, s.Root, s.Path+"."+key, s.KnownFormats, s.Options).Validate(data),
|
||||
)
|
||||
continue
|
||||
}
|
||||
|
||||
if len(dep.Property) > 0 {
|
||||
for _, depKey := range dep.Property {
|
||||
if _, ok := val[depKey]; !ok {
|
||||
mainResult.AddErrors(hasADependencyMsg(s.Path, depKey))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *schemaPropsValidator) redeem() {
|
||||
pools.poolOfSchemaPropsValidators.RedeemValidator(s)
|
||||
}
|
||||
|
||||
func (s *schemaPropsValidator) redeemChildren() {
|
||||
for _, v := range s.anyOfValidators {
|
||||
if v == nil {
|
||||
continue
|
||||
}
|
||||
v.redeemChildren()
|
||||
v.redeem()
|
||||
}
|
||||
s.anyOfValidators = nil
|
||||
|
||||
for _, v := range s.allOfValidators {
|
||||
if v == nil {
|
||||
continue
|
||||
}
|
||||
v.redeemChildren()
|
||||
v.redeem()
|
||||
}
|
||||
s.allOfValidators = nil
|
||||
|
||||
for _, v := range s.oneOfValidators {
|
||||
if v == nil {
|
||||
continue
|
||||
}
|
||||
v.redeemChildren()
|
||||
v.redeem()
|
||||
}
|
||||
s.oneOfValidators = nil
|
||||
|
||||
if s.notValidator != nil {
|
||||
s.notValidator.redeemChildren()
|
||||
s.notValidator.redeem()
|
||||
s.notValidator = nil
|
||||
}
|
||||
}
|
150
vendor/github.com/go-openapi/validate/slice_validator.go
generated
vendored
Normal file
150
vendor/github.com/go-openapi/validate/slice_validator.go
generated
vendored
Normal file
@ -0,0 +1,150 @@
|
||||
// Copyright 2015 go-swagger maintainers
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package validate
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
|
||||
"github.com/go-openapi/spec"
|
||||
"github.com/go-openapi/strfmt"
|
||||
)
|
||||
|
||||
type schemaSliceValidator struct {
|
||||
Path string
|
||||
In string
|
||||
MaxItems *int64
|
||||
MinItems *int64
|
||||
UniqueItems bool
|
||||
AdditionalItems *spec.SchemaOrBool
|
||||
Items *spec.SchemaOrArray
|
||||
Root interface{}
|
||||
KnownFormats strfmt.Registry
|
||||
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) {
|
||||
s.Path = path
|
||||
}
|
||||
|
||||
func (s *schemaSliceValidator) Applies(source interface{}, kind reflect.Kind) bool {
|
||||
_, ok := source.(*spec.Schema)
|
||||
r := ok && kind == reflect.Slice
|
||||
return r
|
||||
}
|
||||
|
||||
func (s *schemaSliceValidator) Validate(data interface{}) *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
|
||||
}
|
||||
val := reflect.ValueOf(data)
|
||||
size := val.Len()
|
||||
|
||||
if s.Items != nil && s.Items.Schema != nil {
|
||||
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()))
|
||||
}
|
||||
}
|
||||
|
||||
itemsSize := 0
|
||||
if s.Items != nil && len(s.Items.Schemas) > 0 {
|
||||
itemsSize = len(s.Items.Schemas)
|
||||
for i := 0; i < itemsSize; 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()))
|
||||
}
|
||||
}
|
||||
if s.AdditionalItems != nil && itemsSize < size {
|
||||
if s.Items != nil && len(s.Items.Schemas) > 0 && !s.AdditionalItems.Allows {
|
||||
result.AddErrors(arrayDoesNotAllowAdditionalItemsMsg())
|
||||
}
|
||||
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)
|
||||
result.mergeForSlice(val, i, validator.Validate(val.Index(i).Interface()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if s.MinItems != nil {
|
||||
if err := MinItems(s.Path, s.In, int64(size), *s.MinItems); err != nil {
|
||||
result.AddErrors(err)
|
||||
}
|
||||
}
|
||||
if s.MaxItems != nil {
|
||||
if err := MaxItems(s.Path, s.In, int64(size), *s.MaxItems); err != nil {
|
||||
result.AddErrors(err)
|
||||
}
|
||||
}
|
||||
if s.UniqueItems {
|
||||
if err := UniqueItems(s.Path, s.In, val.Interface()); err != nil {
|
||||
result.AddErrors(err)
|
||||
}
|
||||
}
|
||||
result.Inc()
|
||||
return result
|
||||
}
|
||||
|
||||
func (s *schemaSliceValidator) redeem() {
|
||||
pools.poolOfSliceValidators.RedeemValidator(s)
|
||||
}
|
852
vendor/github.com/go-openapi/validate/spec.go
generated
vendored
Normal file
852
vendor/github.com/go-openapi/validate/spec.go
generated
vendored
Normal file
@ -0,0 +1,852 @@
|
||||
// Copyright 2015 go-swagger maintainers
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package validate
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/gob"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/go-openapi/analysis"
|
||||
"github.com/go-openapi/errors"
|
||||
"github.com/go-openapi/jsonpointer"
|
||||
"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
|
||||
//
|
||||
// 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() {
|
||||
return errors.CompositeValidationError(errs.Errors...)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// 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
|
||||
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,
|
||||
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)
|
||||
|
||||
if v, ok := data.(*loads.Document); ok {
|
||||
sd = v
|
||||
}
|
||||
if sd == nil {
|
||||
errs.AddErrors(invalidDocumentMsg())
|
||||
return errs, warnings // no point in continuing
|
||||
}
|
||||
s.spec = sd
|
||||
s.analyzer = analysis.New(sd.Spec())
|
||||
|
||||
// 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
|
||||
panic(InvalidDocumentError)
|
||||
}
|
||||
|
||||
defer func() {
|
||||
// errs holds all errors and warnings,
|
||||
// warnings only warnings
|
||||
errs.MergeAsWarnings(warnings)
|
||||
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() {
|
||||
return errs, warnings // no point in continuing
|
||||
}
|
||||
|
||||
errs.Merge(s.validateReferencesValid()) // error -
|
||||
// There may be a point in continuing to try and determine more accurate errors
|
||||
if !s.Options.ContinueOnErrors && errs.HasErrors() {
|
||||
return errs, warnings // no point in continuing
|
||||
}
|
||||
|
||||
errs.Merge(s.validateDuplicateOperationIDs())
|
||||
errs.Merge(s.validateDuplicatePropertyNames()) // error -
|
||||
errs.Merge(s.validateParameters()) // error -
|
||||
errs.Merge(s.validateItems()) // error -
|
||||
|
||||
// Properties in required definition MUST validate their schema
|
||||
// Properties SHOULD NOT be declared as both required and readOnly (warning)
|
||||
errs.Merge(s.validateRequiredDefinitions()) // error and warning
|
||||
|
||||
// There may be a point in continuing to try and determine more accurate errors
|
||||
if !s.Options.ContinueOnErrors && errs.HasErrors() {
|
||||
return errs, warnings // no point in continuing
|
||||
}
|
||||
|
||||
// Values provided as default MUST validate their schema
|
||||
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, schemaOptions: s.schemaOptions}
|
||||
errs.Merge(ex.Validate())
|
||||
|
||||
errs.Merge(s.validateNonEmptyPathParamNames())
|
||||
|
||||
// errs.Merge(s.validateRefNoSibling()) // warning only
|
||||
errs.Merge(s.validateReferenced()) // warning only
|
||||
|
||||
return errs, warnings
|
||||
}
|
||||
|
||||
func (s *SpecValidator) validateNonEmptyPathParamNames() *Result {
|
||||
res := pools.poolOfResults.BorrowResult()
|
||||
if s.spec.Spec().Paths == nil {
|
||||
// There is no Paths object: error
|
||||
res.AddErrors(noValidPathMsg())
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
func (s *SpecValidator) validateDuplicateOperationIDs() *Result {
|
||||
// OperationID, if specified, must be unique across the board
|
||||
var analyzer *analysis.Spec
|
||||
if s.expanded != nil {
|
||||
// $ref are valid: we can analyze operations on an expanded spec
|
||||
analyzer = analysis.New(s.expanded.Spec())
|
||||
} else {
|
||||
// fallback on possible incomplete picture because of previous errors
|
||||
analyzer = s.analyzer
|
||||
}
|
||||
res := pools.poolOfResults.BorrowResult()
|
||||
known := make(map[string]int)
|
||||
for _, v := range analyzer.OperationIDs() {
|
||||
if v != "" {
|
||||
known[v]++
|
||||
}
|
||||
}
|
||||
for k, v := range known {
|
||||
if v > 1 {
|
||||
res.AddErrors(nonUniqueOperationIDMsg(k, v))
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
type dupProp struct {
|
||||
Name string
|
||||
Definition string
|
||||
}
|
||||
|
||||
func (s *SpecValidator) validateDuplicatePropertyNames() *Result {
|
||||
// definition can't declare a property that's already defined by one of its ancestors
|
||||
res := pools.poolOfResults.BorrowResult()
|
||||
for k, sch := range s.spec.Spec().Definitions {
|
||||
if len(sch.AllOf) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
knownanc := map[string]struct{}{
|
||||
"#/definitions/" + k: {},
|
||||
}
|
||||
|
||||
ancs, rec := s.validateCircularAncestry(k, sch, knownanc)
|
||||
if rec != nil && (rec.HasErrors() || !rec.HasWarnings()) {
|
||||
res.Merge(rec)
|
||||
}
|
||||
if len(ancs) > 0 {
|
||||
res.AddErrors(circularAncestryDefinitionMsg(k, ancs))
|
||||
return res
|
||||
}
|
||||
|
||||
knowns := make(map[string]struct{})
|
||||
dups, rep := s.validateSchemaPropertyNames(k, sch, knowns)
|
||||
if rep != nil && (rep.HasErrors() || rep.HasWarnings()) {
|
||||
res.Merge(rep)
|
||||
}
|
||||
if len(dups) > 0 {
|
||||
var pns []string
|
||||
for _, v := range dups {
|
||||
pns = append(pns, v.Definition+"."+v.Name)
|
||||
}
|
||||
res.AddErrors(duplicatePropertiesMsg(k, pns))
|
||||
}
|
||||
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
func (s *SpecValidator) resolveRef(ref *spec.Ref) (*spec.Schema, error) {
|
||||
if s.spec.SpecFilePath() != "" {
|
||||
return spec.ResolveRefWithBase(s.spec.Spec(), ref, &spec.ExpandOptions{RelativeBase: s.spec.SpecFilePath()})
|
||||
}
|
||||
// NOTE: it looks like with the new spec resolver, this code is now unrecheable
|
||||
return spec.ResolveRef(s.spec.Spec(), ref)
|
||||
}
|
||||
|
||||
func (s *SpecValidator) validateSchemaPropertyNames(nm string, sch spec.Schema, knowns map[string]struct{}) ([]dupProp, *Result) {
|
||||
var dups []dupProp
|
||||
|
||||
schn := nm
|
||||
schc := &sch
|
||||
res := pools.poolOfResults.BorrowResult()
|
||||
|
||||
for schc.Ref.String() != "" {
|
||||
// gather property names
|
||||
reso, err := s.resolveRef(&schc.Ref)
|
||||
if err != nil {
|
||||
errorHelp.addPointerError(res, err, schc.Ref.String(), nm)
|
||||
return dups, res
|
||||
}
|
||||
schc = reso
|
||||
schn = sch.Ref.String()
|
||||
}
|
||||
|
||||
if len(schc.AllOf) > 0 {
|
||||
for _, chld := range schc.AllOf {
|
||||
dup, rep := s.validateSchemaPropertyNames(schn, chld, knowns)
|
||||
if rep != nil && (rep.HasErrors() || rep.HasWarnings()) {
|
||||
res.Merge(rep)
|
||||
}
|
||||
dups = append(dups, dup...)
|
||||
}
|
||||
return dups, res
|
||||
}
|
||||
|
||||
for k := range schc.Properties {
|
||||
_, ok := knowns[k]
|
||||
if ok {
|
||||
dups = append(dups, dupProp{Name: k, Definition: schn})
|
||||
} else {
|
||||
knowns[k] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
return dups, res
|
||||
}
|
||||
|
||||
func (s *SpecValidator) validateCircularAncestry(nm string, sch spec.Schema, knowns map[string]struct{}) ([]string, *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
|
||||
}
|
||||
var ancs []string
|
||||
|
||||
schn := nm
|
||||
schc := &sch
|
||||
|
||||
for schc.Ref.String() != "" {
|
||||
reso, err := s.resolveRef(&schc.Ref)
|
||||
if err != nil {
|
||||
errorHelp.addPointerError(res, err, schc.Ref.String(), nm)
|
||||
return ancs, res
|
||||
}
|
||||
schc = reso
|
||||
schn = sch.Ref.String()
|
||||
}
|
||||
|
||||
if schn != nm && schn != "" {
|
||||
if _, ok := knowns[schn]; ok {
|
||||
ancs = append(ancs, schn)
|
||||
}
|
||||
knowns[schn] = struct{}{}
|
||||
|
||||
if len(ancs) > 0 {
|
||||
return ancs, res
|
||||
}
|
||||
}
|
||||
|
||||
if len(schc.AllOf) > 0 {
|
||||
for _, chld := range schc.AllOf {
|
||||
if chld.Ref.String() != "" || len(chld.AllOf) > 0 {
|
||||
anc, rec := s.validateCircularAncestry(schn, chld, knowns)
|
||||
if rec != nil && (rec.HasErrors() || !rec.HasWarnings()) {
|
||||
res.Merge(rec)
|
||||
}
|
||||
ancs = append(ancs, anc...)
|
||||
if len(ancs) > 0 {
|
||||
return ancs, res
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return ancs, res
|
||||
}
|
||||
|
||||
func (s *SpecValidator) validateItems() *Result {
|
||||
// validate parameter, items, schema and response objects for presence of item if type is array
|
||||
res := pools.poolOfResults.BorrowResult()
|
||||
|
||||
for method, pi := range s.analyzer.Operations() {
|
||||
for path, op := range pi {
|
||||
for _, param := range paramHelp.safeExpandedParamsFor(path, method, op.ID, res, s) {
|
||||
|
||||
if param.TypeName() == arrayType && param.ItemsTypeName() == "" {
|
||||
res.AddErrors(arrayInParamRequiresItemsMsg(param.Name, op.ID))
|
||||
continue
|
||||
}
|
||||
if param.In != swaggerBody {
|
||||
if param.Items != nil {
|
||||
items := param.Items
|
||||
for items.TypeName() == arrayType {
|
||||
if items.ItemsTypeName() == "" {
|
||||
res.AddErrors(arrayInParamRequiresItemsMsg(param.Name, op.ID))
|
||||
break
|
||||
}
|
||||
items = items.Items
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// In: body
|
||||
if param.Schema != nil {
|
||||
res.Merge(s.validateSchemaItems(*param.Schema, fmt.Sprintf("body param %q", param.Name), op.ID))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var responses []spec.Response
|
||||
if op.Responses != nil {
|
||||
if op.Responses.Default != nil {
|
||||
responses = append(responses, *op.Responses.Default)
|
||||
}
|
||||
if op.Responses.StatusCodeResponses != nil {
|
||||
for _, v := range op.Responses.StatusCodeResponses {
|
||||
responses = append(responses, v)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, resp := range responses {
|
||||
// Response headers with array
|
||||
for hn, hv := range resp.Headers {
|
||||
if hv.TypeName() == arrayType && hv.ItemsTypeName() == "" {
|
||||
res.AddErrors(arrayInHeaderRequiresItemsMsg(hn, op.ID))
|
||||
}
|
||||
}
|
||||
if resp.Schema != nil {
|
||||
res.Merge(s.validateSchemaItems(*resp.Schema, "response body", op.ID))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
// Verifies constraints on array type
|
||||
func (s *SpecValidator) validateSchemaItems(schema spec.Schema, prefix, opID string) *Result {
|
||||
res := pools.poolOfResults.BorrowResult()
|
||||
if !schema.Type.Contains(arrayType) {
|
||||
return res
|
||||
}
|
||||
|
||||
if schema.Items == nil || schema.Items.Len() == 0 {
|
||||
res.AddErrors(arrayRequiresItemsMsg(prefix, opID))
|
||||
return res
|
||||
}
|
||||
|
||||
if schema.Items.Schema != nil {
|
||||
schema = *schema.Items.Schema
|
||||
if _, err := compileRegexp(schema.Pattern); err != nil {
|
||||
res.AddErrors(invalidItemsPatternMsg(prefix, opID, schema.Pattern))
|
||||
}
|
||||
|
||||
res.Merge(s.validateSchemaItems(schema, prefix, opID))
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
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 := pools.poolOfResults.BorrowResult()
|
||||
for _, l := range fromPath {
|
||||
var matched bool
|
||||
for _, r := range fromOperation {
|
||||
if l == "{"+r+"}" {
|
||||
matched = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !matched {
|
||||
res.AddErrors(noParameterInPathMsg(l))
|
||||
}
|
||||
}
|
||||
|
||||
for _, p := range fromOperation {
|
||||
var matched bool
|
||||
for _, r := range fromPath {
|
||||
if "{"+p+"}" == r {
|
||||
matched = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !matched {
|
||||
res.AddErrors(pathParamNotInPathMsg(path, p))
|
||||
}
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
func (s *SpecValidator) validateReferenced() *Result {
|
||||
var res Result
|
||||
res.MergeAsWarnings(s.validateReferencedParameters())
|
||||
res.MergeAsWarnings(s.validateReferencedResponses())
|
||||
res.MergeAsWarnings(s.validateReferencedDefinitions())
|
||||
return &res
|
||||
}
|
||||
|
||||
func (s *SpecValidator) validateReferencedParameters() *Result {
|
||||
// Each referenceable definition should have references.
|
||||
params := s.spec.Spec().Parameters
|
||||
if len(params) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
expected := make(map[string]struct{})
|
||||
for k := range params {
|
||||
expected["#/parameters/"+jsonpointer.Escape(k)] = struct{}{}
|
||||
}
|
||||
for _, k := range s.analyzer.AllParameterReferences() {
|
||||
delete(expected, k)
|
||||
}
|
||||
|
||||
if len(expected) == 0 {
|
||||
return nil
|
||||
}
|
||||
result := pools.poolOfResults.BorrowResult()
|
||||
for k := range expected {
|
||||
result.AddWarnings(unusedParamMsg(k))
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func (s *SpecValidator) validateReferencedResponses() *Result {
|
||||
// Each referenceable definition should have references.
|
||||
responses := s.spec.Spec().Responses
|
||||
if len(responses) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
expected := make(map[string]struct{})
|
||||
for k := range responses {
|
||||
expected["#/responses/"+jsonpointer.Escape(k)] = struct{}{}
|
||||
}
|
||||
for _, k := range s.analyzer.AllResponseReferences() {
|
||||
delete(expected, k)
|
||||
}
|
||||
|
||||
if len(expected) == 0 {
|
||||
return nil
|
||||
}
|
||||
result := pools.poolOfResults.BorrowResult()
|
||||
for k := range expected {
|
||||
result.AddWarnings(unusedResponseMsg(k))
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func (s *SpecValidator) validateReferencedDefinitions() *Result {
|
||||
// Each referenceable definition must have references.
|
||||
defs := s.spec.Spec().Definitions
|
||||
if len(defs) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
expected := make(map[string]struct{})
|
||||
for k := range defs {
|
||||
expected["#/definitions/"+jsonpointer.Escape(k)] = struct{}{}
|
||||
}
|
||||
for _, k := range s.analyzer.AllDefinitionReferences() {
|
||||
delete(expected, k)
|
||||
}
|
||||
|
||||
if len(expected) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
result := new(Result)
|
||||
for k := range expected {
|
||||
result.AddWarnings(unusedDefinitionMsg(k))
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func (s *SpecValidator) validateRequiredDefinitions() *Result {
|
||||
// Each property listed in the required array must be defined in the properties of the model
|
||||
res := pools.poolOfResults.BorrowResult()
|
||||
|
||||
DEFINITIONS:
|
||||
for d, schema := range s.spec.Spec().Definitions {
|
||||
if schema.Required != nil { // Safeguard
|
||||
for _, pn := range schema.Required {
|
||||
red := s.validateRequiredProperties(pn, d, &schema) //#nosec
|
||||
res.Merge(red)
|
||||
if !red.IsValid() && !s.Options.ContinueOnErrors {
|
||||
break DEFINITIONS // there is an error, let's stop that bleeding
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
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 := pools.poolOfResults.BorrowResult()
|
||||
propertyMatch := false
|
||||
patternMatch := false
|
||||
additionalPropertiesMatch := false
|
||||
isReadOnly := false
|
||||
|
||||
// Regular properties
|
||||
if _, ok := v.Properties[path]; ok {
|
||||
propertyMatch = true
|
||||
isReadOnly = v.Properties[path].ReadOnly
|
||||
}
|
||||
|
||||
// NOTE: patternProperties are not supported in swagger. Even though, we continue validation here
|
||||
// We check all defined patterns: if one regexp is invalid, croaks an error
|
||||
for pp, pv := range v.PatternProperties {
|
||||
re, err := compileRegexp(pp)
|
||||
if err != nil {
|
||||
res.AddErrors(invalidPatternMsg(pp, in))
|
||||
} else if re.MatchString(path) {
|
||||
patternMatch = true
|
||||
if !propertyMatch {
|
||||
isReadOnly = pv.ReadOnly
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !(propertyMatch || patternMatch) {
|
||||
if v.AdditionalProperties != nil {
|
||||
if v.AdditionalProperties.Allows && v.AdditionalProperties.Schema == nil {
|
||||
additionalPropertiesMatch = true
|
||||
} else if v.AdditionalProperties.Schema != nil {
|
||||
// additionalProperties as schema are upported in swagger
|
||||
// recursively validates additionalProperties schema
|
||||
// TODO : anyOf, allOf, oneOf like in schemaPropsValidator
|
||||
red := s.validateRequiredProperties(path, in, v.AdditionalProperties.Schema)
|
||||
if red.IsValid() {
|
||||
additionalPropertiesMatch = true
|
||||
if !propertyMatch && !patternMatch {
|
||||
isReadOnly = v.AdditionalProperties.Schema.ReadOnly
|
||||
}
|
||||
}
|
||||
res.Merge(red)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !(propertyMatch || patternMatch || additionalPropertiesMatch) {
|
||||
res.AddErrors(requiredButNotDefinedMsg(path, in))
|
||||
}
|
||||
|
||||
if isReadOnly {
|
||||
res.AddWarnings(readOnlyAndRequiredMsg(in, path))
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
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, 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 := 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 {
|
||||
if s.Options.StrictPathParamUniqueness {
|
||||
pathToAdd := pathHelp.stripParametersInPath(path)
|
||||
|
||||
// Warn on garbled path afer param stripping
|
||||
if rexGarbledPathSegment.MatchString(pathToAdd) {
|
||||
res.AddWarnings(pathStrippedParamGarbledMsg(pathToAdd))
|
||||
}
|
||||
|
||||
// Check uniqueness of stripped paths
|
||||
if _, found := methodPaths[method][pathToAdd]; found {
|
||||
|
||||
// Sort names for stable, testable output
|
||||
if strings.Compare(path, methodPaths[method][pathToAdd]) < 0 {
|
||||
res.AddErrors(pathOverlapMsg(path, methodPaths[method][pathToAdd]))
|
||||
} else {
|
||||
res.AddErrors(pathOverlapMsg(methodPaths[method][pathToAdd], path))
|
||||
}
|
||||
} else {
|
||||
if _, found := methodPaths[method]; !found {
|
||||
methodPaths[method] = map[string]string{}
|
||||
}
|
||||
methodPaths[method][pathToAdd] = path // Original non stripped path
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
var bodyParams []string
|
||||
var paramNames []string
|
||||
var hasForm, hasBody bool
|
||||
|
||||
// Check parameters names uniqueness for operation
|
||||
// TODO: should be done after param expansion
|
||||
res.Merge(s.checkUniqueParams(path, method, op))
|
||||
|
||||
// pick the root schema from the swagger specification which describes a parameter
|
||||
origSchema, ok := s.schema.Definitions["parameter"]
|
||||
if !ok {
|
||||
panic("unexpected swagger schema: missing #/definitions/parameter")
|
||||
}
|
||||
// clone it once to avoid expanding a global schema (e.g. swagger spec)
|
||||
paramSchema, err := deepCloneSchema(origSchema)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("can't clone schema: %v", err))
|
||||
}
|
||||
|
||||
for _, pr := range paramHelp.safeExpandedParamsFor(path, method, op.ID, res, s) {
|
||||
// An expanded parameter must validate the Parameter schema (an unexpanded $ref always passes high-level schema validation)
|
||||
schv := newSchemaValidator(¶mSchema, s.schema, fmt.Sprintf("%s.%s.parameters.%s", path, method, pr.Name), s.KnownFormats, s.schemaOptions)
|
||||
obj := swag.ToDynamicJSON(pr)
|
||||
res.Merge(schv.Validate(obj))
|
||||
|
||||
// Validate pattern regexp for parameters with a Pattern property
|
||||
if _, err := compileRegexp(pr.Pattern); err != nil {
|
||||
res.AddErrors(invalidPatternInParamMsg(op.ID, pr.Name, pr.Pattern))
|
||||
}
|
||||
|
||||
// There must be at most one parameter in body: list them all
|
||||
if pr.In == swaggerBody {
|
||||
bodyParams = append(bodyParams, fmt.Sprintf("%q", pr.Name))
|
||||
hasBody = true
|
||||
}
|
||||
|
||||
if pr.In == "path" {
|
||||
paramNames = append(paramNames, pr.Name)
|
||||
// Path declared in path must have the required: true property
|
||||
if !pr.Required {
|
||||
res.AddErrors(pathParamRequiredMsg(op.ID, pr.Name))
|
||||
}
|
||||
}
|
||||
|
||||
if pr.In == "formData" {
|
||||
hasForm = true
|
||||
}
|
||||
|
||||
if !(pr.Type == numberType || pr.Type == integerType) &&
|
||||
(pr.Maximum != nil || pr.Minimum != nil || pr.MultipleOf != nil) {
|
||||
// A non-numeric parameter has validation keywords for numeric instances (number and integer)
|
||||
res.AddWarnings(parameterValidationTypeMismatchMsg(pr.Name, path, pr.Type))
|
||||
}
|
||||
|
||||
if !(pr.Type == stringType) &&
|
||||
// A non-string parameter has validation keywords for strings
|
||||
(pr.MaxLength != nil || pr.MinLength != nil || pr.Pattern != "") {
|
||||
res.AddWarnings(parameterValidationTypeMismatchMsg(pr.Name, path, pr.Type))
|
||||
}
|
||||
|
||||
if !(pr.Type == arrayType) &&
|
||||
// A non-array parameter has validation keywords for arrays
|
||||
(pr.MaxItems != nil || pr.MinItems != nil || pr.UniqueItems) {
|
||||
res.AddWarnings(parameterValidationTypeMismatchMsg(pr.Name, path, pr.Type))
|
||||
}
|
||||
}
|
||||
|
||||
// In:formData and In:body are mutually exclusive
|
||||
if hasBody && hasForm {
|
||||
res.AddErrors(bothFormDataAndBodyMsg(op.ID))
|
||||
}
|
||||
// There must be at most one body param
|
||||
// Accurately report situations when more than 1 body param is declared (possibly unnamed)
|
||||
if len(bodyParams) > 1 {
|
||||
sort.Strings(bodyParams)
|
||||
res.AddErrors(multipleBodyParamMsg(op.ID, bodyParams))
|
||||
}
|
||||
|
||||
// Check uniqueness of parameters in path
|
||||
paramsInPath := pathHelp.extractPathParams(path)
|
||||
for i, p := range paramsInPath {
|
||||
for j, q := range paramsInPath {
|
||||
if p == q && i > j {
|
||||
res.AddErrors(pathParamNotUniqueMsg(path, p, q))
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Warns about possible malformed params in path
|
||||
rexGarbledParam := mustCompileRegexp(`{.*[{}\s]+.*}`)
|
||||
for _, p := range paramsInPath {
|
||||
if rexGarbledParam.MatchString(p) {
|
||||
res.AddWarnings(pathParamGarbledMsg(path, p))
|
||||
}
|
||||
}
|
||||
|
||||
// Match params from path vs params from params section
|
||||
res.Merge(s.validatePathParamPresence(path, paramsInPath, paramNames))
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
func (s *SpecValidator) validateReferencesValid() *Result {
|
||||
// each reference must point to a valid object
|
||||
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()))
|
||||
}
|
||||
}
|
||||
if !res.HasErrors() {
|
||||
// NOTE: with default settings, loads.Document.Expanded()
|
||||
// stops on first error. Anyhow, the expand option to continue
|
||||
// on errors fails to report errors at all.
|
||||
exp, err := s.spec.Expanded()
|
||||
if err != nil {
|
||||
res.AddErrors(unresolvedReferencesMsg(err))
|
||||
}
|
||||
s.expanded = exp
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
func (s *SpecValidator) checkUniqueParams(path, method string, op *spec.Operation) *Result {
|
||||
// Check for duplicate parameters declaration in param section.
|
||||
// Each parameter should have a unique `name` and `type` combination
|
||||
// NOTE: this could be factorized in analysis (when constructing the params map)
|
||||
// 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 := pools.poolOfResults.BorrowResult()
|
||||
pnames := make(map[string]struct{})
|
||||
|
||||
if op.Parameters != nil { // Safeguard
|
||||
for _, ppr := range op.Parameters {
|
||||
var ok bool
|
||||
pr, red := paramHelp.resolveParam(path, method, op.ID, &ppr, s) //#nosec
|
||||
res.Merge(red)
|
||||
|
||||
if pr != nil && pr.Name != "" { // params with empty name does no participate the check
|
||||
key := fmt.Sprintf("%s#%s", pr.In, pr.Name)
|
||||
|
||||
if _, ok = pnames[key]; ok {
|
||||
res.AddErrors(duplicateParamNameMsg(pr.In, pr.Name, op.ID))
|
||||
}
|
||||
pnames[key] = struct{}{}
|
||||
}
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
// SetContinueOnErrors sets the ContinueOnErrors option for this validator.
|
||||
func (s *SpecValidator) SetContinueOnErrors(c bool) {
|
||||
s.Options.ContinueOnErrors = c
|
||||
}
|
||||
|
||||
// expandedAnalyzer returns expanded.Analyzer when it is available.
|
||||
// otherwise just analyzer.
|
||||
func (s *SpecValidator) expandedAnalyzer() *analysis.Spec {
|
||||
if s.expanded != nil && s.expanded.Analyzer != nil {
|
||||
return s.expanded.Analyzer
|
||||
}
|
||||
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
|
||||
}
|
366
vendor/github.com/go-openapi/validate/spec_messages.go
generated
vendored
Normal file
366
vendor/github.com/go-openapi/validate/spec_messages.go
generated
vendored
Normal file
@ -0,0 +1,366 @@
|
||||
// Copyright 2015 go-swagger maintainers
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package validate
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/go-openapi/errors"
|
||||
)
|
||||
|
||||
// Error messages related to spec validation and returned as results.
|
||||
const (
|
||||
// ArrayRequiresItemsError ...
|
||||
ArrayRequiresItemsError = "%s for %q is a collection without an element type (array requires items definition)"
|
||||
|
||||
// ArrayInParamRequiresItemsError ...
|
||||
ArrayInParamRequiresItemsError = "param %q for %q is a collection without an element type (array requires item definition)"
|
||||
|
||||
// ArrayInHeaderRequiresItemsError ...
|
||||
ArrayInHeaderRequiresItemsError = "header %q for %q is a collection without an element type (array requires items definition)"
|
||||
|
||||
// BothFormDataAndBodyError indicates that an operation specifies both a body and a formData parameter, which is forbidden
|
||||
BothFormDataAndBodyError = "operation %q has both formData and body parameters. Only one such In: type may be used for a given operation"
|
||||
|
||||
// CannotResolveRefError when a $ref could not be resolved
|
||||
CannotResolveReferenceError = "could not resolve reference in %s to $ref %s: %v"
|
||||
|
||||
// CircularAncestryDefinitionError ...
|
||||
CircularAncestryDefinitionError = "definition %q has circular ancestry: %v"
|
||||
|
||||
// DefaultValueDoesNotValidateError results from an invalid default value provided
|
||||
DefaultValueDoesNotValidateError = "default value for %s in %s does not validate its schema"
|
||||
|
||||
// DefaultValueItemsDoesNotValidateError results from an invalid default value provided for Items
|
||||
DefaultValueItemsDoesNotValidateError = "default value for %s.items in %s does not validate its schema"
|
||||
|
||||
// DefaultValueHeaderDoesNotValidateError results from an invalid default value provided in header
|
||||
DefaultValueHeaderDoesNotValidateError = "in operation %q, default value in header %s for %s does not validate its schema"
|
||||
|
||||
// DefaultValueHeaderItemsDoesNotValidateError results from an invalid default value provided in header.items
|
||||
DefaultValueHeaderItemsDoesNotValidateError = "in operation %q, default value in header.items %s for %s does not validate its schema"
|
||||
|
||||
// DefaultValueInDoesNotValidateError ...
|
||||
DefaultValueInDoesNotValidateError = "in operation %q, default value in %s does not validate its schema"
|
||||
|
||||
// DuplicateParamNameError ...
|
||||
DuplicateParamNameError = "duplicate parameter name %q for %q in operation %q"
|
||||
|
||||
// DuplicatePropertiesError ...
|
||||
DuplicatePropertiesError = "definition %q contains duplicate properties: %v"
|
||||
|
||||
// ExampleValueDoesNotValidateError results from an invalid example value provided
|
||||
ExampleValueDoesNotValidateError = "example value for %s in %s does not validate its schema"
|
||||
|
||||
// ExampleValueItemsDoesNotValidateError results from an invalid example value provided for Items
|
||||
ExampleValueItemsDoesNotValidateError = "example value for %s.items in %s does not validate its schema"
|
||||
|
||||
// ExampleValueHeaderDoesNotValidateError results from an invalid example value provided in header
|
||||
ExampleValueHeaderDoesNotValidateError = "in operation %q, example value in header %s for %s does not validate its schema"
|
||||
|
||||
// ExampleValueHeaderItemsDoesNotValidateError results from an invalid example value provided in header.items
|
||||
ExampleValueHeaderItemsDoesNotValidateError = "in operation %q, example value in header.items %s for %s does not validate its schema"
|
||||
|
||||
// ExampleValueInDoesNotValidateError ...
|
||||
ExampleValueInDoesNotValidateError = "in operation %q, example value in %s does not validate its schema"
|
||||
|
||||
// EmptyPathParameterError means that a path parameter was found empty (e.g. "{}")
|
||||
EmptyPathParameterError = "%q contains an empty path parameter"
|
||||
|
||||
// InvalidDocumentError states that spec validation only processes spec.Document objects
|
||||
InvalidDocumentError = "spec validator can only validate spec.Document objects"
|
||||
|
||||
// InvalidItemsPatternError indicates an Items definition with invalid pattern
|
||||
InvalidItemsPatternError = "%s for %q has invalid items pattern: %q"
|
||||
|
||||
// InvalidParameterDefinitionError indicates an error detected on a parameter definition
|
||||
InvalidParameterDefinitionError = "invalid definition for parameter %s in %s in operation %q"
|
||||
|
||||
// InvalidParameterDefinitionAsSchemaError indicates an error detected on a parameter definition, which was mistaken with a schema definition.
|
||||
// Most likely, this situation is encountered whenever a $ref has been added as a sibling of the parameter definition.
|
||||
InvalidParameterDefinitionAsSchemaError = "invalid definition as Schema for parameter %s in %s in operation %q"
|
||||
|
||||
// InvalidPatternError ...
|
||||
InvalidPatternError = "pattern %q is invalid in %s"
|
||||
|
||||
// InvalidPatternInError indicates an invalid pattern in a schema or items definition
|
||||
InvalidPatternInError = "%s in %s has invalid pattern: %q"
|
||||
|
||||
// InvalidPatternInHeaderError indicates a header definition with an invalid pattern
|
||||
InvalidPatternInHeaderError = "in operation %q, header %s for %s has invalid pattern %q: %v"
|
||||
|
||||
// InvalidPatternInParamError ...
|
||||
InvalidPatternInParamError = "operation %q has invalid pattern in param %q: %q"
|
||||
|
||||
// InvalidReferenceError indicates that a $ref property could not be resolved
|
||||
InvalidReferenceError = "invalid ref %q"
|
||||
|
||||
// InvalidResponseDefinitionAsSchemaError indicates an error detected on a response definition, which was mistaken with a schema definition.
|
||||
// Most likely, this situation is encountered whenever a $ref has been added as a sibling of the response definition.
|
||||
InvalidResponseDefinitionAsSchemaError = "invalid definition as Schema for response %s in %s"
|
||||
|
||||
// MultipleBodyParamError indicates that an operation specifies multiple parameter with in: body
|
||||
MultipleBodyParamError = "operation %q has more than 1 body param: %v"
|
||||
|
||||
// NonUniqueOperationIDError indicates that the same operationId has been specified several times
|
||||
NonUniqueOperationIDError = "%q is defined %d times"
|
||||
|
||||
// NoParameterInPathError indicates that a path was found without any parameter
|
||||
NoParameterInPathError = "path param %q has no parameter definition"
|
||||
|
||||
// NoValidPathErrorOrWarning indicates that no single path could be validated. If Paths is empty, this message is only a warning.
|
||||
NoValidPathErrorOrWarning = "spec has no valid path defined"
|
||||
|
||||
// NoValidResponseError indicates that no valid response description could be found for an operation
|
||||
NoValidResponseError = "operation %q has no valid response"
|
||||
|
||||
// PathOverlapError ...
|
||||
PathOverlapError = "path %s overlaps with %s"
|
||||
|
||||
// PathParamNotInPathError indicates that a parameter specified with in: path was not found in the path specification
|
||||
PathParamNotInPathError = "path param %q is not present in path %q"
|
||||
|
||||
// PathParamNotUniqueError ...
|
||||
PathParamNotUniqueError = "params in path %q must be unique: %q conflicts with %q"
|
||||
|
||||
// PathParamNotRequiredError ...
|
||||
PathParamRequiredError = "in operation %q,path param %q must be declared as required"
|
||||
|
||||
// RefNotAllowedInHeaderError indicates a $ref was found in a header definition, which is not allowed by Swagger
|
||||
RefNotAllowedInHeaderError = "IMPORTANT!in %q: $ref are not allowed in headers. In context for header %q%s"
|
||||
|
||||
// RequiredButNotDefinedError ...
|
||||
RequiredButNotDefinedError = "%q is present in required but not defined as property in definition %q"
|
||||
|
||||
// SomeParametersBrokenError indicates that some parameters could not be resolved, which might result in partial checks to be carried on
|
||||
SomeParametersBrokenError = "some parameters definitions are broken in %q.%s. Cannot carry on full checks on parameters for operation %s"
|
||||
|
||||
// UnresolvedReferencesError indicates that at least one $ref could not be resolved
|
||||
UnresolvedReferencesError = "some references could not be resolved in spec. First found: %v"
|
||||
)
|
||||
|
||||
// Warning messages related to spec validation and returned as results
|
||||
const (
|
||||
// ExamplesWithoutSchemaWarning indicates that examples are provided for a response,but not schema to validate the example against
|
||||
ExamplesWithoutSchemaWarning = "Examples provided without schema in operation %q, %s"
|
||||
|
||||
// ExamplesMimeNotSupportedWarning indicates that examples are provided with a mime type different than application/json, which
|
||||
// the validator dos not support yetl
|
||||
ExamplesMimeNotSupportedWarning = "No validation attempt for examples for media types other than application/json, in operation %q, %s"
|
||||
|
||||
// PathParamGarbledWarning ...
|
||||
PathParamGarbledWarning = "in path %q, param %q contains {,} or white space. Albeit not stricly illegal, this is probably no what you want"
|
||||
|
||||
// ParamValidationTypeMismatch indicates that parameter has validation which does not match its type
|
||||
ParamValidationTypeMismatch = "validation keywords of parameter %q in path %q don't match its type %s"
|
||||
|
||||
// PathStrippedParamGarbledWarning ...
|
||||
PathStrippedParamGarbledWarning = "path stripped from path parameters %s contains {,} or white space. This is probably no what you want."
|
||||
|
||||
// ReadOnlyAndRequiredWarning ...
|
||||
ReadOnlyAndRequiredWarning = "Required property %s in %q should not be marked as both required and readOnly"
|
||||
|
||||
// RefShouldNotHaveSiblingsWarning indicates that a $ref was found with a sibling definition. This results in the $ref taking over its siblings,
|
||||
// which is most likely not wanted.
|
||||
RefShouldNotHaveSiblingsWarning = "$ref property should have no sibling in %q.%s"
|
||||
|
||||
// RequiredHasDefaultWarning indicates that a required parameter property should not have a default
|
||||
RequiredHasDefaultWarning = "%s in %s has a default value and is required as parameter"
|
||||
|
||||
// UnusedDefinitionWarning ...
|
||||
UnusedDefinitionWarning = "definition %q is not used anywhere"
|
||||
|
||||
// UnusedParamWarning ...
|
||||
UnusedParamWarning = "parameter %q is not used anywhere"
|
||||
|
||||
// UnusedResponseWarning ...
|
||||
UnusedResponseWarning = "response %q is not used anywhere"
|
||||
|
||||
InvalidObject = "expected an object in %q.%s"
|
||||
)
|
||||
|
||||
// Additional error codes
|
||||
const (
|
||||
// InternalErrorCode reports an internal technical error
|
||||
InternalErrorCode = http.StatusInternalServerError
|
||||
// NotFoundErrorCode indicates that a resource (e.g. a $ref) could not be found
|
||||
NotFoundErrorCode = http.StatusNotFound
|
||||
)
|
||||
|
||||
func invalidDocumentMsg() errors.Error {
|
||||
return errors.New(InternalErrorCode, InvalidDocumentError)
|
||||
}
|
||||
func invalidRefMsg(path string) errors.Error {
|
||||
return errors.New(NotFoundErrorCode, InvalidReferenceError, path)
|
||||
}
|
||||
func unresolvedReferencesMsg(err error) errors.Error {
|
||||
return errors.New(errors.CompositeErrorCode, UnresolvedReferencesError, err)
|
||||
}
|
||||
func noValidPathMsg() errors.Error {
|
||||
return errors.New(errors.CompositeErrorCode, NoValidPathErrorOrWarning)
|
||||
}
|
||||
func emptyPathParameterMsg(path string) errors.Error {
|
||||
return errors.New(errors.CompositeErrorCode, EmptyPathParameterError, path)
|
||||
}
|
||||
func nonUniqueOperationIDMsg(path string, i int) errors.Error {
|
||||
return errors.New(errors.CompositeErrorCode, NonUniqueOperationIDError, path, i)
|
||||
}
|
||||
func circularAncestryDefinitionMsg(path string, args interface{}) errors.Error {
|
||||
return errors.New(errors.CompositeErrorCode, CircularAncestryDefinitionError, path, args)
|
||||
}
|
||||
func duplicatePropertiesMsg(path string, args interface{}) errors.Error {
|
||||
return errors.New(errors.CompositeErrorCode, DuplicatePropertiesError, path, args)
|
||||
}
|
||||
func pathParamNotInPathMsg(path, param string) errors.Error {
|
||||
return errors.New(errors.CompositeErrorCode, PathParamNotInPathError, param, path)
|
||||
}
|
||||
func arrayRequiresItemsMsg(path, operation string) errors.Error {
|
||||
return errors.New(errors.CompositeErrorCode, ArrayRequiresItemsError, path, operation)
|
||||
}
|
||||
func arrayInParamRequiresItemsMsg(path, operation string) errors.Error {
|
||||
return errors.New(errors.CompositeErrorCode, ArrayInParamRequiresItemsError, path, operation)
|
||||
}
|
||||
func arrayInHeaderRequiresItemsMsg(path, operation string) errors.Error {
|
||||
return errors.New(errors.CompositeErrorCode, ArrayInHeaderRequiresItemsError, path, operation)
|
||||
}
|
||||
func invalidItemsPatternMsg(path, operation, pattern string) errors.Error {
|
||||
return errors.New(errors.CompositeErrorCode, InvalidItemsPatternError, path, operation, pattern)
|
||||
}
|
||||
func invalidPatternMsg(pattern, path string) errors.Error {
|
||||
return errors.New(errors.CompositeErrorCode, InvalidPatternError, pattern, path)
|
||||
}
|
||||
func requiredButNotDefinedMsg(path, definition string) errors.Error {
|
||||
return errors.New(errors.CompositeErrorCode, RequiredButNotDefinedError, path, definition)
|
||||
}
|
||||
func pathParamGarbledMsg(path, param string) errors.Error {
|
||||
return errors.New(errors.CompositeErrorCode, PathParamGarbledWarning, path, param)
|
||||
}
|
||||
func pathStrippedParamGarbledMsg(path string) errors.Error {
|
||||
return errors.New(errors.CompositeErrorCode, PathStrippedParamGarbledWarning, path)
|
||||
}
|
||||
func pathOverlapMsg(path, arg string) errors.Error {
|
||||
return errors.New(errors.CompositeErrorCode, PathOverlapError, path, arg)
|
||||
}
|
||||
func invalidPatternInParamMsg(operation, param, pattern string) errors.Error {
|
||||
return errors.New(errors.CompositeErrorCode, InvalidPatternInParamError, operation, param, pattern)
|
||||
}
|
||||
func pathParamRequiredMsg(operation, param string) errors.Error {
|
||||
return errors.New(errors.CompositeErrorCode, PathParamRequiredError, operation, param)
|
||||
}
|
||||
func bothFormDataAndBodyMsg(operation string) errors.Error {
|
||||
return errors.New(errors.CompositeErrorCode, BothFormDataAndBodyError, operation)
|
||||
}
|
||||
func multipleBodyParamMsg(operation string, args interface{}) errors.Error {
|
||||
return errors.New(errors.CompositeErrorCode, MultipleBodyParamError, operation, args)
|
||||
}
|
||||
func pathParamNotUniqueMsg(path, param, arg string) errors.Error {
|
||||
return errors.New(errors.CompositeErrorCode, PathParamNotUniqueError, path, param, arg)
|
||||
}
|
||||
func duplicateParamNameMsg(path, param, operation string) errors.Error {
|
||||
return errors.New(errors.CompositeErrorCode, DuplicateParamNameError, param, path, operation)
|
||||
}
|
||||
func unusedParamMsg(arg string) errors.Error {
|
||||
return errors.New(errors.CompositeErrorCode, UnusedParamWarning, arg)
|
||||
}
|
||||
func unusedDefinitionMsg(arg string) errors.Error {
|
||||
return errors.New(errors.CompositeErrorCode, UnusedDefinitionWarning, arg)
|
||||
}
|
||||
func unusedResponseMsg(arg string) errors.Error {
|
||||
return errors.New(errors.CompositeErrorCode, UnusedResponseWarning, arg)
|
||||
}
|
||||
func readOnlyAndRequiredMsg(path, param string) errors.Error {
|
||||
return errors.New(errors.CompositeErrorCode, ReadOnlyAndRequiredWarning, param, path)
|
||||
}
|
||||
func noParameterInPathMsg(param string) errors.Error {
|
||||
return errors.New(errors.CompositeErrorCode, NoParameterInPathError, param)
|
||||
}
|
||||
func requiredHasDefaultMsg(param, path string) errors.Error {
|
||||
return errors.New(errors.CompositeErrorCode, RequiredHasDefaultWarning, param, path)
|
||||
}
|
||||
func defaultValueDoesNotValidateMsg(param, path string) errors.Error {
|
||||
return errors.New(errors.CompositeErrorCode, DefaultValueDoesNotValidateError, param, path)
|
||||
}
|
||||
func defaultValueItemsDoesNotValidateMsg(param, path string) errors.Error {
|
||||
return errors.New(errors.CompositeErrorCode, DefaultValueItemsDoesNotValidateError, param, path)
|
||||
}
|
||||
func noValidResponseMsg(operation string) errors.Error {
|
||||
return errors.New(errors.CompositeErrorCode, NoValidResponseError, operation)
|
||||
}
|
||||
func defaultValueHeaderDoesNotValidateMsg(operation, header, path string) errors.Error {
|
||||
return errors.New(errors.CompositeErrorCode, DefaultValueHeaderDoesNotValidateError, operation, header, path)
|
||||
}
|
||||
func defaultValueHeaderItemsDoesNotValidateMsg(operation, header, path string) errors.Error {
|
||||
return errors.New(errors.CompositeErrorCode, DefaultValueHeaderItemsDoesNotValidateError, operation, header, path)
|
||||
}
|
||||
func invalidPatternInHeaderMsg(operation, header, path, pattern string, args interface{}) errors.Error {
|
||||
return errors.New(errors.CompositeErrorCode, InvalidPatternInHeaderError, operation, header, path, pattern, args)
|
||||
}
|
||||
func invalidPatternInMsg(path, in, pattern string) errors.Error {
|
||||
return errors.New(errors.CompositeErrorCode, InvalidPatternInError, path, in, pattern)
|
||||
}
|
||||
func defaultValueInDoesNotValidateMsg(operation, path string) errors.Error {
|
||||
return errors.New(errors.CompositeErrorCode, DefaultValueInDoesNotValidateError, operation, path)
|
||||
}
|
||||
func exampleValueDoesNotValidateMsg(param, path string) errors.Error {
|
||||
return errors.New(errors.CompositeErrorCode, ExampleValueDoesNotValidateError, param, path)
|
||||
}
|
||||
func exampleValueItemsDoesNotValidateMsg(param, path string) errors.Error {
|
||||
return errors.New(errors.CompositeErrorCode, ExampleValueItemsDoesNotValidateError, param, path)
|
||||
}
|
||||
func exampleValueHeaderDoesNotValidateMsg(operation, header, path string) errors.Error {
|
||||
return errors.New(errors.CompositeErrorCode, ExampleValueHeaderDoesNotValidateError, operation, header, path)
|
||||
}
|
||||
func exampleValueHeaderItemsDoesNotValidateMsg(operation, header, path string) errors.Error {
|
||||
return errors.New(errors.CompositeErrorCode, ExampleValueHeaderItemsDoesNotValidateError, operation, header, path)
|
||||
}
|
||||
func exampleValueInDoesNotValidateMsg(operation, path string) errors.Error {
|
||||
return errors.New(errors.CompositeErrorCode, ExampleValueInDoesNotValidateError, operation, path)
|
||||
}
|
||||
func examplesWithoutSchemaMsg(operation, response string) errors.Error {
|
||||
return errors.New(errors.CompositeErrorCode, ExamplesWithoutSchemaWarning, operation, response)
|
||||
}
|
||||
func examplesMimeNotSupportedMsg(operation, response string) errors.Error {
|
||||
return errors.New(errors.CompositeErrorCode, ExamplesMimeNotSupportedWarning, operation, response)
|
||||
}
|
||||
func refNotAllowedInHeaderMsg(path, header, ref string) errors.Error {
|
||||
return errors.New(errors.CompositeErrorCode, RefNotAllowedInHeaderError, path, header, ref)
|
||||
}
|
||||
func cannotResolveRefMsg(path, ref string, err error) errors.Error {
|
||||
return errors.New(errors.CompositeErrorCode, CannotResolveReferenceError, path, ref, err)
|
||||
}
|
||||
func invalidParameterDefinitionMsg(path, method, operationID string) errors.Error {
|
||||
return errors.New(errors.CompositeErrorCode, InvalidParameterDefinitionError, path, method, operationID)
|
||||
}
|
||||
func invalidParameterDefinitionAsSchemaMsg(path, method, operationID string) errors.Error {
|
||||
return errors.New(errors.CompositeErrorCode, InvalidParameterDefinitionAsSchemaError, path, method, operationID)
|
||||
}
|
||||
func parameterValidationTypeMismatchMsg(param, path, typ string) errors.Error {
|
||||
return errors.New(errors.CompositeErrorCode, ParamValidationTypeMismatch, param, path, typ)
|
||||
}
|
||||
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 someParametersBrokenMsg(path, method, operationID string) errors.Error {
|
||||
return errors.New(errors.CompositeErrorCode, SomeParametersBrokenError, path, method, operationID)
|
||||
}
|
||||
func refShouldNotHaveSiblingsMsg(path, operationID string) errors.Error {
|
||||
return errors.New(errors.CompositeErrorCode, RefShouldNotHaveSiblingsWarning, operationID, path)
|
||||
}
|
213
vendor/github.com/go-openapi/validate/type.go
generated
vendored
Normal file
213
vendor/github.com/go-openapi/validate/type.go
generated
vendored
Normal file
@ -0,0 +1,213 @@
|
||||
// Copyright 2015 go-swagger maintainers
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package validate
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
"github.com/go-openapi/errors"
|
||||
"github.com/go-openapi/spec"
|
||||
"github.com/go-openapi/strfmt"
|
||||
"github.com/go-openapi/swag"
|
||||
)
|
||||
|
||||
type typeValidator struct {
|
||||
Path string
|
||||
In string
|
||||
Type spec.StringOrArray
|
||||
Nullable bool
|
||||
Format 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) {
|
||||
// internal type to JSON type with swagger 2.0 format (with go-openapi/strfmt extensions),
|
||||
// see https://github.com/go-openapi/strfmt/blob/master/README.md
|
||||
// TODO: this switch really is some sort of reverse lookup for formats. It should be provided by strfmt.
|
||||
switch data.(type) {
|
||||
case []byte, strfmt.Base64, *strfmt.Base64:
|
||||
return stringType, stringFormatByte
|
||||
case strfmt.CreditCard, *strfmt.CreditCard:
|
||||
return stringType, stringFormatCreditCard
|
||||
case strfmt.Date, *strfmt.Date:
|
||||
return stringType, stringFormatDate
|
||||
case strfmt.DateTime, *strfmt.DateTime:
|
||||
return stringType, stringFormatDateTime
|
||||
case strfmt.Duration, *strfmt.Duration:
|
||||
return stringType, stringFormatDuration
|
||||
case swag.File, *swag.File:
|
||||
return fileType, ""
|
||||
case strfmt.Email, *strfmt.Email:
|
||||
return stringType, stringFormatEmail
|
||||
case strfmt.HexColor, *strfmt.HexColor:
|
||||
return stringType, stringFormatHexColor
|
||||
case strfmt.Hostname, *strfmt.Hostname:
|
||||
return stringType, stringFormatHostname
|
||||
case strfmt.IPv4, *strfmt.IPv4:
|
||||
return stringType, stringFormatIPv4
|
||||
case strfmt.IPv6, *strfmt.IPv6:
|
||||
return stringType, stringFormatIPv6
|
||||
case strfmt.ISBN, *strfmt.ISBN:
|
||||
return stringType, stringFormatISBN
|
||||
case strfmt.ISBN10, *strfmt.ISBN10:
|
||||
return stringType, stringFormatISBN10
|
||||
case strfmt.ISBN13, *strfmt.ISBN13:
|
||||
return stringType, stringFormatISBN13
|
||||
case strfmt.MAC, *strfmt.MAC:
|
||||
return stringType, stringFormatMAC
|
||||
case strfmt.ObjectId, *strfmt.ObjectId:
|
||||
return stringType, stringFormatBSONObjectID
|
||||
case strfmt.Password, *strfmt.Password:
|
||||
return stringType, stringFormatPassword
|
||||
case strfmt.RGBColor, *strfmt.RGBColor:
|
||||
return stringType, stringFormatRGBColor
|
||||
case strfmt.SSN, *strfmt.SSN:
|
||||
return stringType, stringFormatSSN
|
||||
case strfmt.URI, *strfmt.URI:
|
||||
return stringType, stringFormatURI
|
||||
case strfmt.UUID, *strfmt.UUID:
|
||||
return stringType, stringFormatUUID
|
||||
case strfmt.UUID3, *strfmt.UUID3:
|
||||
return stringType, stringFormatUUID3
|
||||
case strfmt.UUID4, *strfmt.UUID4:
|
||||
return stringType, stringFormatUUID4
|
||||
case strfmt.UUID5, *strfmt.UUID5:
|
||||
return stringType, stringFormatUUID5
|
||||
// TODO: missing binary (io.ReadCloser)
|
||||
// TODO: missing json.Number
|
||||
default:
|
||||
val := reflect.ValueOf(data)
|
||||
tpe := val.Type()
|
||||
switch tpe.Kind() { //nolint:exhaustive
|
||||
case reflect.Bool:
|
||||
return booleanType, ""
|
||||
case reflect.String:
|
||||
return stringType, ""
|
||||
case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Uint8, reflect.Uint16, reflect.Uint32:
|
||||
// NOTE: that is the spec. With go-openapi, is that not uint32 for unsigned integers?
|
||||
return integerType, integerFormatInt32
|
||||
case reflect.Int, reflect.Int64, reflect.Uint, reflect.Uint64:
|
||||
return integerType, integerFormatInt64
|
||||
case reflect.Float32:
|
||||
// NOTE: is that not numberFormatFloat?
|
||||
return numberType, numberFormatFloat32
|
||||
case reflect.Float64:
|
||||
// NOTE: is that not "double"?
|
||||
return numberType, numberFormatFloat64
|
||||
// NOTE: go arrays (reflect.Array) are not supported (fixed length)
|
||||
case reflect.Slice:
|
||||
return arrayType, ""
|
||||
case reflect.Map, reflect.Struct:
|
||||
return objectType, ""
|
||||
case reflect.Interface:
|
||||
// What to do here?
|
||||
panic("dunno what to do here")
|
||||
case reflect.Ptr:
|
||||
return t.schemaInfoForType(reflect.Indirect(val).Interface())
|
||||
}
|
||||
}
|
||||
return "", ""
|
||||
}
|
||||
|
||||
func (t *typeValidator) SetPath(path string) {
|
||||
t.Path = path
|
||||
}
|
||||
|
||||
func (t *typeValidator) Applies(source interface{}, _ reflect.Kind) bool {
|
||||
// typeValidator applies to Schema, Parameter and Header objects
|
||||
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 {
|
||||
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), t.Options.recycleResult)
|
||||
}
|
||||
|
||||
return emptyResult
|
||||
}
|
||||
|
||||
// check if the type matches, should be used in every validator chain as first item
|
||||
val := reflect.Indirect(reflect.ValueOf(data))
|
||||
kind := val.Kind()
|
||||
|
||||
// infer schema type (JSON) and format from passed data type
|
||||
schType, format := t.schemaInfoForType(data)
|
||||
|
||||
// check numerical types
|
||||
// TODO: check unsigned ints
|
||||
// TODO: check json.Number (see schema.go)
|
||||
isLowerInt := t.Format == integerFormatInt64 && format == integerFormatInt32
|
||||
isLowerFloat := t.Format == numberFormatFloat64 && format == numberFormatFloat32
|
||||
isFloatInt := schType == numberType && swag.IsFloat64AJSONInteger(val.Float()) && t.Type.Contains(integerType)
|
||||
isIntFloat := schType == integerType && t.Type.Contains(numberType)
|
||||
|
||||
if kind != reflect.String && kind != reflect.Slice && t.Format != "" && !(t.Type.Contains(schType) || format == t.Format || isFloatInt || isIntFloat || isLowerInt || isLowerFloat) {
|
||||
// TODO: test case
|
||||
return errorHelp.sErr(errors.InvalidType(t.Path, t.In, t.Format, format), t.Options.recycleResult)
|
||||
}
|
||||
|
||||
if !(t.Type.Contains(numberType) || t.Type.Contains(integerType)) && t.Format != "" && (kind == reflect.String || kind == reflect.Slice) {
|
||||
return emptyResult
|
||||
}
|
||||
|
||||
if !(t.Type.Contains(schType) || isFloatInt || isIntFloat) {
|
||||
return errorHelp.sErr(errors.InvalidType(t.Path, t.In, strings.Join(t.Type, ","), schType), t.Options.recycleResult)
|
||||
}
|
||||
|
||||
return emptyResult
|
||||
}
|
||||
|
||||
func (t *typeValidator) redeem() {
|
||||
pools.poolOfTypeValidators.RedeemValidator(t)
|
||||
}
|
15
vendor/github.com/go-openapi/validate/update-fixtures.sh
generated
vendored
Normal file
15
vendor/github.com/go-openapi/validate/update-fixtures.sh
generated
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -eu -o pipefail
|
||||
dir=$(git rev-parse --show-toplevel)
|
||||
scratch=$(mktemp -d -t tmp.XXXXXXXXXX)
|
||||
|
||||
function finish {
|
||||
rm -rf "$scratch"
|
||||
}
|
||||
trap finish EXIT SIGHUP SIGINT SIGTERM
|
||||
|
||||
cd "$scratch"
|
||||
git clone https://github.com/json-schema-org/JSON-Schema-Test-Suite Suite
|
||||
cp -r Suite/tests/draft4/* "$dir/fixtures/jsonschema_suite"
|
||||
cp -a Suite/remotes "$dir/fixtures/jsonschema_suite"
|
1051
vendor/github.com/go-openapi/validate/validator.go
generated
vendored
Normal file
1051
vendor/github.com/go-openapi/validate/validator.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
450
vendor/github.com/go-openapi/validate/values.go
generated
vendored
Normal file
450
vendor/github.com/go-openapi/validate/values.go
generated
vendored
Normal file
@ -0,0 +1,450 @@
|
||||
// Copyright 2015 go-swagger maintainers
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package validate
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/go-openapi/errors"
|
||||
"github.com/go-openapi/strfmt"
|
||||
"github.com/go-openapi/swag"
|
||||
)
|
||||
|
||||
// Enum validates if the data is a member of the enum
|
||||
func Enum(path, in string, data interface{}, enum interface{}) *errors.Validation {
|
||||
return EnumCase(path, in, data, enum, true)
|
||||
}
|
||||
|
||||
// EnumCase validates if the data is a member of the enum and may respect case-sensitivity for strings
|
||||
func EnumCase(path, in string, data interface{}, enum interface{}, caseSensitive bool) *errors.Validation {
|
||||
val := reflect.ValueOf(enum)
|
||||
if val.Kind() != reflect.Slice {
|
||||
return nil
|
||||
}
|
||||
|
||||
dataString := convertEnumCaseStringKind(data, caseSensitive)
|
||||
var values []interface{}
|
||||
for i := 0; i < val.Len(); i++ {
|
||||
ele := val.Index(i)
|
||||
enumValue := ele.Interface()
|
||||
if data != nil {
|
||||
if reflect.DeepEqual(data, enumValue) {
|
||||
return nil
|
||||
}
|
||||
enumString := convertEnumCaseStringKind(enumValue, caseSensitive)
|
||||
if dataString != nil && enumString != nil && strings.EqualFold(*dataString, *enumString) {
|
||||
return nil
|
||||
}
|
||||
actualType := reflect.TypeOf(enumValue)
|
||||
if actualType == nil { // Safeguard. Frankly, I don't know how we may get a nil
|
||||
continue
|
||||
}
|
||||
expectedValue := reflect.ValueOf(data)
|
||||
if expectedValue.IsValid() && expectedValue.Type().ConvertibleTo(actualType) {
|
||||
// Attempt comparison after type conversion
|
||||
if reflect.DeepEqual(expectedValue.Convert(actualType).Interface(), enumValue) {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
values = append(values, enumValue)
|
||||
}
|
||||
return errors.EnumFail(path, in, data, values)
|
||||
}
|
||||
|
||||
// convertEnumCaseStringKind converts interface if it is kind of string and case insensitivity is set
|
||||
func convertEnumCaseStringKind(value interface{}, caseSensitive bool) *string {
|
||||
if caseSensitive {
|
||||
return nil
|
||||
}
|
||||
|
||||
val := reflect.ValueOf(value)
|
||||
if val.Kind() != reflect.String {
|
||||
return nil
|
||||
}
|
||||
|
||||
str := fmt.Sprintf("%v", value)
|
||||
return &str
|
||||
}
|
||||
|
||||
// MinItems validates that there are at least n items in a slice
|
||||
func MinItems(path, in string, size, min int64) *errors.Validation {
|
||||
if size < min {
|
||||
return errors.TooFewItems(path, in, min, size)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// MaxItems validates that there are at most n items in a slice
|
||||
func MaxItems(path, in string, size, max int64) *errors.Validation {
|
||||
if size > max {
|
||||
return errors.TooManyItems(path, in, max, size)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// UniqueItems validates that the provided slice has unique elements
|
||||
func UniqueItems(path, in string, data interface{}) *errors.Validation {
|
||||
val := reflect.ValueOf(data)
|
||||
if val.Kind() != reflect.Slice {
|
||||
return nil
|
||||
}
|
||||
var unique []interface{}
|
||||
for i := 0; i < val.Len(); i++ {
|
||||
v := val.Index(i).Interface()
|
||||
for _, u := range unique {
|
||||
if reflect.DeepEqual(v, u) {
|
||||
return errors.DuplicateItems(path, in)
|
||||
}
|
||||
}
|
||||
unique = append(unique, v)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// MinLength validates a string for minimum length
|
||||
func MinLength(path, in, data string, minLength int64) *errors.Validation {
|
||||
strLen := int64(utf8.RuneCountInString(data))
|
||||
if strLen < minLength {
|
||||
return errors.TooShort(path, in, minLength, data)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// MaxLength validates a string for maximum length
|
||||
func MaxLength(path, in, data string, maxLength int64) *errors.Validation {
|
||||
strLen := int64(utf8.RuneCountInString(data))
|
||||
if strLen > maxLength {
|
||||
return errors.TooLong(path, in, maxLength, data)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ReadOnly validates an interface for readonly
|
||||
func ReadOnly(ctx context.Context, path, in string, data interface{}) *errors.Validation {
|
||||
|
||||
// read only is only validated when operationType is request
|
||||
if op := extractOperationType(ctx); op != request {
|
||||
return nil
|
||||
}
|
||||
|
||||
// data must be of zero value of its type
|
||||
val := reflect.ValueOf(data)
|
||||
if val.IsValid() {
|
||||
if reflect.DeepEqual(reflect.Zero(val.Type()).Interface(), val.Interface()) {
|
||||
return nil
|
||||
}
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
|
||||
return errors.ReadOnly(path, in, data)
|
||||
}
|
||||
|
||||
// Required validates an interface for requiredness
|
||||
func Required(path, in string, data interface{}) *errors.Validation {
|
||||
val := reflect.ValueOf(data)
|
||||
if val.IsValid() {
|
||||
if reflect.DeepEqual(reflect.Zero(val.Type()).Interface(), val.Interface()) {
|
||||
return errors.Required(path, in, data)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
return errors.Required(path, in, data)
|
||||
}
|
||||
|
||||
// RequiredString validates a string for requiredness
|
||||
func RequiredString(path, in, data string) *errors.Validation {
|
||||
if data == "" {
|
||||
return errors.Required(path, in, data)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// RequiredNumber validates a number for requiredness
|
||||
func RequiredNumber(path, in string, data float64) *errors.Validation {
|
||||
if data == 0 {
|
||||
return errors.Required(path, in, data)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Pattern validates a string against a regular expression
|
||||
func Pattern(path, in, data, pattern string) *errors.Validation {
|
||||
re, err := compileRegexp(pattern)
|
||||
if err != nil {
|
||||
return errors.FailedPattern(path, in, fmt.Sprintf("%s, but pattern is invalid: %s", pattern, err.Error()), data)
|
||||
}
|
||||
if !re.MatchString(data) {
|
||||
return errors.FailedPattern(path, in, pattern, data)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// MaximumInt validates if a number is smaller than a given maximum
|
||||
func MaximumInt(path, in string, data, max int64, exclusive bool) *errors.Validation {
|
||||
if (!exclusive && data > max) || (exclusive && data >= max) {
|
||||
return errors.ExceedsMaximumInt(path, in, max, exclusive, data)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// MaximumUint validates if a number is smaller than a given maximum
|
||||
func MaximumUint(path, in string, data, max uint64, exclusive bool) *errors.Validation {
|
||||
if (!exclusive && data > max) || (exclusive && data >= max) {
|
||||
return errors.ExceedsMaximumUint(path, in, max, exclusive, data)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Maximum validates if a number is smaller than a given maximum
|
||||
func Maximum(path, in string, data, max float64, exclusive bool) *errors.Validation {
|
||||
if (!exclusive && data > max) || (exclusive && data >= max) {
|
||||
return errors.ExceedsMaximum(path, in, max, exclusive, data)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Minimum validates if a number is smaller than a given minimum
|
||||
func Minimum(path, in string, data, min float64, exclusive bool) *errors.Validation {
|
||||
if (!exclusive && data < min) || (exclusive && data <= min) {
|
||||
return errors.ExceedsMinimum(path, in, min, exclusive, data)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// MinimumInt validates if a number is smaller than a given minimum
|
||||
func MinimumInt(path, in string, data, min int64, exclusive bool) *errors.Validation {
|
||||
if (!exclusive && data < min) || (exclusive && data <= min) {
|
||||
return errors.ExceedsMinimumInt(path, in, min, exclusive, data)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// MinimumUint validates if a number is smaller than a given minimum
|
||||
func MinimumUint(path, in string, data, min uint64, exclusive bool) *errors.Validation {
|
||||
if (!exclusive && data < min) || (exclusive && data <= min) {
|
||||
return errors.ExceedsMinimumUint(path, in, min, exclusive, data)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// MultipleOf validates if the provided number is a multiple of the factor
|
||||
func MultipleOf(path, in string, data, factor float64) *errors.Validation {
|
||||
// multipleOf factor must be positive
|
||||
if factor <= 0 {
|
||||
return errors.MultipleOfMustBePositive(path, in, factor)
|
||||
}
|
||||
var mult float64
|
||||
if factor < 1 {
|
||||
mult = 1 / factor * data
|
||||
} else {
|
||||
mult = data / factor
|
||||
}
|
||||
if !swag.IsFloat64AJSONInteger(mult) {
|
||||
return errors.NotMultipleOf(path, in, factor, data)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// MultipleOfInt validates if the provided integer is a multiple of the factor
|
||||
func MultipleOfInt(path, in string, data int64, factor int64) *errors.Validation {
|
||||
// multipleOf factor must be positive
|
||||
if factor <= 0 {
|
||||
return errors.MultipleOfMustBePositive(path, in, factor)
|
||||
}
|
||||
mult := data / factor
|
||||
if mult*factor != data {
|
||||
return errors.NotMultipleOf(path, in, factor, data)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// MultipleOfUint validates if the provided unsigned integer is a multiple of the factor
|
||||
func MultipleOfUint(path, in string, data, factor uint64) *errors.Validation {
|
||||
// multipleOf factor must be positive
|
||||
if factor == 0 {
|
||||
return errors.MultipleOfMustBePositive(path, in, factor)
|
||||
}
|
||||
mult := data / factor
|
||||
if mult*factor != data {
|
||||
return errors.NotMultipleOf(path, in, factor, data)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// FormatOf validates if a string matches a format in the format registry
|
||||
func FormatOf(path, in, format, data string, registry strfmt.Registry) *errors.Validation {
|
||||
if registry == nil {
|
||||
registry = strfmt.Default
|
||||
}
|
||||
if ok := registry.ContainsName(format); !ok {
|
||||
return errors.InvalidTypeName(format)
|
||||
}
|
||||
if ok := registry.Validates(format, data); !ok {
|
||||
return errors.InvalidType(path, in, format, data)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// MaximumNativeType provides native type constraint validation as a facade
|
||||
// to various numeric types versions of Maximum constraint check.
|
||||
//
|
||||
// Assumes that any possible loss conversion during conversion has been
|
||||
// checked beforehand.
|
||||
//
|
||||
// NOTE: currently, the max value is marshalled as a float64, no matter what,
|
||||
// which means there may be a loss during conversions (e.g. for very large integers)
|
||||
//
|
||||
// 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 { //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)
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||
value := valueHelp.asUint64(val)
|
||||
if max < 0 {
|
||||
return errors.ExceedsMaximum(path, in, max, exclusive, val)
|
||||
}
|
||||
return MaximumUint(path, in, value, uint64(max), exclusive)
|
||||
case reflect.Float32, reflect.Float64:
|
||||
fallthrough
|
||||
default:
|
||||
value := valueHelp.asFloat64(val)
|
||||
return Maximum(path, in, value, max, exclusive)
|
||||
}
|
||||
}
|
||||
|
||||
// MinimumNativeType provides native type constraint validation as a facade
|
||||
// to various numeric types versions of Minimum constraint check.
|
||||
//
|
||||
// Assumes that any possible loss conversion during conversion has been
|
||||
// checked beforehand.
|
||||
//
|
||||
// NOTE: currently, the min value is marshalled as a float64, no matter what,
|
||||
// which means there may be a loss during conversions (e.g. for very large integers)
|
||||
//
|
||||
// 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 { //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)
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||
value := valueHelp.asUint64(val)
|
||||
if min < 0 {
|
||||
return nil
|
||||
}
|
||||
return MinimumUint(path, in, value, uint64(min), exclusive)
|
||||
case reflect.Float32, reflect.Float64:
|
||||
fallthrough
|
||||
default:
|
||||
value := valueHelp.asFloat64(val)
|
||||
return Minimum(path, in, value, min, exclusive)
|
||||
}
|
||||
}
|
||||
|
||||
// MultipleOfNativeType provides native type constraint validation as a facade
|
||||
// to various numeric types version of MultipleOf constraint check.
|
||||
//
|
||||
// Assumes that any possible loss conversion during conversion has been
|
||||
// checked beforehand.
|
||||
//
|
||||
// NOTE: currently, the multipleOf factor is marshalled as a float64, no matter what,
|
||||
// which means there may be a loss during conversions (e.g. for very large integers)
|
||||
//
|
||||
// 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 { //nolint:exhaustive
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
value := valueHelp.asInt64(val)
|
||||
return MultipleOfInt(path, in, value, int64(multipleOf))
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||
value := valueHelp.asUint64(val)
|
||||
return MultipleOfUint(path, in, value, uint64(multipleOf))
|
||||
case reflect.Float32, reflect.Float64:
|
||||
fallthrough
|
||||
default:
|
||||
value := valueHelp.asFloat64(val)
|
||||
return MultipleOf(path, in, value, multipleOf)
|
||||
}
|
||||
}
|
||||
|
||||
// IsValueValidAgainstRange checks that a numeric value is compatible with
|
||||
// the range defined by Type and Format, that is, may be converted without loss.
|
||||
//
|
||||
// NOTE: this check is about type capacity and not formal verification such as: 1.0 != 1L
|
||||
func IsValueValidAgainstRange(val interface{}, typeName, format, prefix, path string) error {
|
||||
kind := reflect.ValueOf(val).Type().Kind()
|
||||
|
||||
// What is the string representation of val
|
||||
var stringRep string
|
||||
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:
|
||||
stringRep = swag.FormatInt64(valueHelp.asInt64(val))
|
||||
case reflect.Float32, reflect.Float64:
|
||||
stringRep = swag.FormatFloat64(valueHelp.asFloat64(val))
|
||||
default:
|
||||
return fmt.Errorf("%s value number range checking called with invalid (non numeric) val type in %s", prefix, path)
|
||||
}
|
||||
|
||||
var errVal error
|
||||
|
||||
switch typeName {
|
||||
case integerType:
|
||||
switch format {
|
||||
case integerFormatInt32:
|
||||
_, errVal = swag.ConvertInt32(stringRep)
|
||||
case integerFormatUInt32:
|
||||
_, errVal = swag.ConvertUint32(stringRep)
|
||||
case integerFormatUInt64:
|
||||
_, errVal = swag.ConvertUint64(stringRep)
|
||||
case integerFormatInt64:
|
||||
fallthrough
|
||||
default:
|
||||
_, errVal = swag.ConvertInt64(stringRep)
|
||||
}
|
||||
case numberType:
|
||||
fallthrough
|
||||
default:
|
||||
switch format {
|
||||
case numberFormatFloat, numberFormatFloat32:
|
||||
_, errVal = swag.ConvertFloat32(stringRep)
|
||||
case numberFormatDouble, numberFormatFloat64:
|
||||
fallthrough
|
||||
default:
|
||||
// No check can be performed here since
|
||||
// no number beyond float64 is supported
|
||||
}
|
||||
}
|
||||
if errVal != nil { // We don't report the actual errVal from strconv
|
||||
if format != "" {
|
||||
errVal = fmt.Errorf("%s value must be of type %s with format %s in %s", prefix, typeName, format, path)
|
||||
} else {
|
||||
errVal = fmt.Errorf("%s value must be of type %s (default format) in %s", prefix, typeName, path)
|
||||
}
|
||||
}
|
||||
return errVal
|
||||
}
|
Reference in New Issue
Block a user