mirror of
https://github.com/superseriousbusiness/gotosocial.git
synced 2024-11-25 05:06:38 +00:00
fd8a724e77
* bump go swagger version * bump swagger version
1052 lines
24 KiB
Go
1052 lines
24 KiB
Go
// 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/errors"
|
|
"github.com/go-openapi/spec"
|
|
"github.com/go-openapi/strfmt"
|
|
)
|
|
|
|
// An EntityValidator is an interface for things that can validate entities
|
|
type EntityValidator interface {
|
|
Validate(interface{}) *Result
|
|
}
|
|
|
|
type valueValidator interface {
|
|
SetPath(path string)
|
|
Applies(interface{}, reflect.Kind) bool
|
|
Validate(interface{}) *Result
|
|
}
|
|
|
|
type itemsValidator struct {
|
|
items *spec.Items
|
|
root interface{}
|
|
path string
|
|
in string
|
|
validators [6]valueValidator
|
|
KnownFormats strfmt.Registry
|
|
Options *SchemaValidatorOptions
|
|
}
|
|
|
|
func newItemsValidator(path, in string, items *spec.Items, root interface{}, formats strfmt.Registry, opts *SchemaValidatorOptions) *itemsValidator {
|
|
if opts == nil {
|
|
opts = new(SchemaValidatorOptions)
|
|
}
|
|
|
|
var iv *itemsValidator
|
|
if opts.recycleValidators {
|
|
iv = pools.poolOfItemsValidators.BorrowValidator()
|
|
} else {
|
|
iv = new(itemsValidator)
|
|
}
|
|
|
|
iv.path = path
|
|
iv.in = in
|
|
iv.items = items
|
|
iv.root = root
|
|
iv.KnownFormats = formats
|
|
iv.Options = opts
|
|
iv.validators = [6]valueValidator{
|
|
iv.typeValidator(),
|
|
iv.stringValidator(),
|
|
iv.formatValidator(),
|
|
iv.numberValidator(),
|
|
iv.sliceValidator(),
|
|
iv.commonValidator(),
|
|
}
|
|
return iv
|
|
}
|
|
|
|
func (i *itemsValidator) Validate(index int, data interface{}) *Result {
|
|
if i.Options.recycleValidators {
|
|
defer func() {
|
|
i.redeemChildren()
|
|
i.redeem()
|
|
}()
|
|
}
|
|
|
|
tpe := reflect.TypeOf(data)
|
|
kind := tpe.Kind()
|
|
var result *Result
|
|
if i.Options.recycleResult {
|
|
result = pools.poolOfResults.BorrowResult()
|
|
} else {
|
|
result = new(Result)
|
|
}
|
|
|
|
path := fmt.Sprintf("%s.%d", i.path, index)
|
|
|
|
for idx, validator := range i.validators {
|
|
if !validator.Applies(i.root, kind) {
|
|
if i.Options.recycleValidators {
|
|
// Validate won't be called, so relinquish this validator
|
|
if redeemableChildren, ok := validator.(interface{ redeemChildren() }); ok {
|
|
redeemableChildren.redeemChildren()
|
|
}
|
|
if redeemable, ok := validator.(interface{ redeem() }); ok {
|
|
redeemable.redeem()
|
|
}
|
|
i.validators[idx] = nil // prevents further (unsafe) usage
|
|
}
|
|
|
|
continue
|
|
}
|
|
|
|
validator.SetPath(path)
|
|
err := validator.Validate(data)
|
|
if i.Options.recycleValidators {
|
|
i.validators[idx] = nil // prevents further (unsafe) usage
|
|
}
|
|
if err != nil {
|
|
result.Inc()
|
|
if err.HasErrors() {
|
|
result.Merge(err)
|
|
|
|
break
|
|
}
|
|
|
|
result.Merge(err)
|
|
}
|
|
}
|
|
|
|
return result
|
|
}
|
|
|
|
func (i *itemsValidator) typeValidator() valueValidator {
|
|
return newTypeValidator(
|
|
i.path,
|
|
i.in,
|
|
spec.StringOrArray([]string{i.items.Type}),
|
|
i.items.Nullable,
|
|
i.items.Format,
|
|
i.Options,
|
|
)
|
|
}
|
|
|
|
func (i *itemsValidator) commonValidator() valueValidator {
|
|
return newBasicCommonValidator(
|
|
"",
|
|
i.in,
|
|
i.items.Default,
|
|
i.items.Enum,
|
|
i.Options,
|
|
)
|
|
}
|
|
|
|
func (i *itemsValidator) sliceValidator() valueValidator {
|
|
return newBasicSliceValidator(
|
|
"",
|
|
i.in,
|
|
i.items.Default,
|
|
i.items.MaxItems,
|
|
i.items.MinItems,
|
|
i.items.UniqueItems,
|
|
i.items.Items,
|
|
i.root,
|
|
i.KnownFormats,
|
|
i.Options,
|
|
)
|
|
}
|
|
|
|
func (i *itemsValidator) numberValidator() valueValidator {
|
|
return newNumberValidator(
|
|
"",
|
|
i.in,
|
|
i.items.Default,
|
|
i.items.MultipleOf,
|
|
i.items.Maximum,
|
|
i.items.ExclusiveMaximum,
|
|
i.items.Minimum,
|
|
i.items.ExclusiveMinimum,
|
|
i.items.Type,
|
|
i.items.Format,
|
|
i.Options,
|
|
)
|
|
}
|
|
|
|
func (i *itemsValidator) stringValidator() valueValidator {
|
|
return newStringValidator(
|
|
"",
|
|
i.in,
|
|
i.items.Default,
|
|
false, // Required
|
|
false, // AllowEmpty
|
|
i.items.MaxLength,
|
|
i.items.MinLength,
|
|
i.items.Pattern,
|
|
i.Options,
|
|
)
|
|
}
|
|
|
|
func (i *itemsValidator) formatValidator() valueValidator {
|
|
return newFormatValidator(
|
|
"",
|
|
i.in,
|
|
i.items.Format,
|
|
i.KnownFormats,
|
|
i.Options,
|
|
)
|
|
}
|
|
|
|
func (i *itemsValidator) redeem() {
|
|
pools.poolOfItemsValidators.RedeemValidator(i)
|
|
}
|
|
|
|
func (i *itemsValidator) redeemChildren() {
|
|
for idx, validator := range i.validators {
|
|
if validator == nil {
|
|
continue
|
|
}
|
|
if redeemableChildren, ok := validator.(interface{ redeemChildren() }); ok {
|
|
redeemableChildren.redeemChildren()
|
|
}
|
|
if redeemable, ok := validator.(interface{ redeem() }); ok {
|
|
redeemable.redeem()
|
|
}
|
|
i.validators[idx] = nil // free up allocated children if not in pool
|
|
}
|
|
}
|
|
|
|
type basicCommonValidator struct {
|
|
Path string
|
|
In string
|
|
Default interface{}
|
|
Enum []interface{}
|
|
Options *SchemaValidatorOptions
|
|
}
|
|
|
|
func newBasicCommonValidator(path, in string, def interface{}, enum []interface{}, opts *SchemaValidatorOptions) *basicCommonValidator {
|
|
if opts == nil {
|
|
opts = new(SchemaValidatorOptions)
|
|
}
|
|
|
|
var b *basicCommonValidator
|
|
if opts.recycleValidators {
|
|
b = pools.poolOfBasicCommonValidators.BorrowValidator()
|
|
} else {
|
|
b = new(basicCommonValidator)
|
|
}
|
|
|
|
b.Path = path
|
|
b.In = in
|
|
b.Default = def
|
|
b.Enum = enum
|
|
b.Options = opts
|
|
|
|
return b
|
|
}
|
|
|
|
func (b *basicCommonValidator) SetPath(path string) {
|
|
b.Path = path
|
|
}
|
|
|
|
func (b *basicCommonValidator) Applies(source interface{}, _ reflect.Kind) bool {
|
|
switch source.(type) {
|
|
case *spec.Parameter, *spec.Schema, *spec.Header:
|
|
return true
|
|
default:
|
|
return false
|
|
}
|
|
}
|
|
|
|
func (b *basicCommonValidator) Validate(data interface{}) (res *Result) {
|
|
if b.Options.recycleValidators {
|
|
defer func() {
|
|
b.redeem()
|
|
}()
|
|
}
|
|
|
|
if len(b.Enum) == 0 {
|
|
return nil
|
|
}
|
|
|
|
for _, enumValue := range b.Enum {
|
|
actualType := reflect.TypeOf(enumValue)
|
|
if actualType == nil { // Safeguard
|
|
continue
|
|
}
|
|
|
|
expectedValue := reflect.ValueOf(data)
|
|
if expectedValue.IsValid() &&
|
|
expectedValue.Type().ConvertibleTo(actualType) &&
|
|
reflect.DeepEqual(expectedValue.Convert(actualType).Interface(), enumValue) {
|
|
return nil
|
|
}
|
|
}
|
|
|
|
return errorHelp.sErr(errors.EnumFail(b.Path, b.In, data, b.Enum), b.Options.recycleResult)
|
|
}
|
|
|
|
func (b *basicCommonValidator) redeem() {
|
|
pools.poolOfBasicCommonValidators.RedeemValidator(b)
|
|
}
|
|
|
|
// A HeaderValidator has very limited subset of validations to apply
|
|
type HeaderValidator struct {
|
|
name string
|
|
header *spec.Header
|
|
validators [6]valueValidator
|
|
KnownFormats strfmt.Registry
|
|
Options *SchemaValidatorOptions
|
|
}
|
|
|
|
// NewHeaderValidator creates a new header validator object
|
|
func NewHeaderValidator(name string, header *spec.Header, formats strfmt.Registry, options ...Option) *HeaderValidator {
|
|
opts := new(SchemaValidatorOptions)
|
|
for _, o := range options {
|
|
o(opts)
|
|
}
|
|
|
|
return newHeaderValidator(name, header, formats, opts)
|
|
}
|
|
|
|
func newHeaderValidator(name string, header *spec.Header, formats strfmt.Registry, opts *SchemaValidatorOptions) *HeaderValidator {
|
|
if opts == nil {
|
|
opts = new(SchemaValidatorOptions)
|
|
}
|
|
|
|
var p *HeaderValidator
|
|
if opts.recycleValidators {
|
|
p = pools.poolOfHeaderValidators.BorrowValidator()
|
|
} else {
|
|
p = new(HeaderValidator)
|
|
}
|
|
|
|
p.name = name
|
|
p.header = header
|
|
p.KnownFormats = formats
|
|
p.Options = opts
|
|
p.validators = [6]valueValidator{
|
|
newTypeValidator(
|
|
name,
|
|
"header",
|
|
spec.StringOrArray([]string{header.Type}),
|
|
header.Nullable,
|
|
header.Format,
|
|
p.Options,
|
|
),
|
|
p.stringValidator(),
|
|
p.formatValidator(),
|
|
p.numberValidator(),
|
|
p.sliceValidator(),
|
|
p.commonValidator(),
|
|
}
|
|
|
|
return p
|
|
}
|
|
|
|
// Validate the value of the header against its schema
|
|
func (p *HeaderValidator) Validate(data interface{}) *Result {
|
|
if p.Options.recycleValidators {
|
|
defer func() {
|
|
p.redeemChildren()
|
|
p.redeem()
|
|
}()
|
|
}
|
|
|
|
if data == nil {
|
|
return nil
|
|
}
|
|
|
|
var result *Result
|
|
if p.Options.recycleResult {
|
|
result = pools.poolOfResults.BorrowResult()
|
|
} else {
|
|
result = new(Result)
|
|
}
|
|
|
|
tpe := reflect.TypeOf(data)
|
|
kind := tpe.Kind()
|
|
|
|
for idx, validator := range p.validators {
|
|
if !validator.Applies(p.header, kind) {
|
|
if p.Options.recycleValidators {
|
|
// Validate won't be called, so relinquish this validator
|
|
if redeemableChildren, ok := validator.(interface{ redeemChildren() }); ok {
|
|
redeemableChildren.redeemChildren()
|
|
}
|
|
if redeemable, ok := validator.(interface{ redeem() }); ok {
|
|
redeemable.redeem()
|
|
}
|
|
p.validators[idx] = nil // prevents further (unsafe) usage
|
|
}
|
|
|
|
continue
|
|
}
|
|
|
|
err := validator.Validate(data)
|
|
if p.Options.recycleValidators {
|
|
p.validators[idx] = nil // prevents further (unsafe) usage
|
|
}
|
|
if err != nil {
|
|
if err.HasErrors() {
|
|
result.Merge(err)
|
|
break
|
|
}
|
|
result.Merge(err)
|
|
}
|
|
}
|
|
|
|
return result
|
|
}
|
|
|
|
func (p *HeaderValidator) commonValidator() valueValidator {
|
|
return newBasicCommonValidator(
|
|
p.name,
|
|
"response",
|
|
p.header.Default,
|
|
p.header.Enum,
|
|
p.Options,
|
|
)
|
|
}
|
|
|
|
func (p *HeaderValidator) sliceValidator() valueValidator {
|
|
return newBasicSliceValidator(
|
|
p.name,
|
|
"response",
|
|
p.header.Default,
|
|
p.header.MaxItems,
|
|
p.header.MinItems,
|
|
p.header.UniqueItems,
|
|
p.header.Items,
|
|
p.header,
|
|
p.KnownFormats,
|
|
p.Options,
|
|
)
|
|
}
|
|
|
|
func (p *HeaderValidator) numberValidator() valueValidator {
|
|
return newNumberValidator(
|
|
p.name,
|
|
"response",
|
|
p.header.Default,
|
|
p.header.MultipleOf,
|
|
p.header.Maximum,
|
|
p.header.ExclusiveMaximum,
|
|
p.header.Minimum,
|
|
p.header.ExclusiveMinimum,
|
|
p.header.Type,
|
|
p.header.Format,
|
|
p.Options,
|
|
)
|
|
}
|
|
|
|
func (p *HeaderValidator) stringValidator() valueValidator {
|
|
return newStringValidator(
|
|
p.name,
|
|
"response",
|
|
p.header.Default,
|
|
true,
|
|
false,
|
|
p.header.MaxLength,
|
|
p.header.MinLength,
|
|
p.header.Pattern,
|
|
p.Options,
|
|
)
|
|
}
|
|
|
|
func (p *HeaderValidator) formatValidator() valueValidator {
|
|
return newFormatValidator(
|
|
p.name,
|
|
"response",
|
|
p.header.Format,
|
|
p.KnownFormats,
|
|
p.Options,
|
|
)
|
|
}
|
|
|
|
func (p *HeaderValidator) redeem() {
|
|
pools.poolOfHeaderValidators.RedeemValidator(p)
|
|
}
|
|
|
|
func (p *HeaderValidator) redeemChildren() {
|
|
for idx, validator := range p.validators {
|
|
if validator == nil {
|
|
continue
|
|
}
|
|
if redeemableChildren, ok := validator.(interface{ redeemChildren() }); ok {
|
|
redeemableChildren.redeemChildren()
|
|
}
|
|
if redeemable, ok := validator.(interface{ redeem() }); ok {
|
|
redeemable.redeem()
|
|
}
|
|
p.validators[idx] = nil // free up allocated children if not in pool
|
|
}
|
|
}
|
|
|
|
// A ParamValidator has very limited subset of validations to apply
|
|
type ParamValidator struct {
|
|
param *spec.Parameter
|
|
validators [6]valueValidator
|
|
KnownFormats strfmt.Registry
|
|
Options *SchemaValidatorOptions
|
|
}
|
|
|
|
// NewParamValidator creates a new param validator object
|
|
func NewParamValidator(param *spec.Parameter, formats strfmt.Registry, options ...Option) *ParamValidator {
|
|
opts := new(SchemaValidatorOptions)
|
|
for _, o := range options {
|
|
o(opts)
|
|
}
|
|
|
|
return newParamValidator(param, formats, opts)
|
|
}
|
|
|
|
func newParamValidator(param *spec.Parameter, formats strfmt.Registry, opts *SchemaValidatorOptions) *ParamValidator {
|
|
if opts == nil {
|
|
opts = new(SchemaValidatorOptions)
|
|
}
|
|
|
|
var p *ParamValidator
|
|
if opts.recycleValidators {
|
|
p = pools.poolOfParamValidators.BorrowValidator()
|
|
} else {
|
|
p = new(ParamValidator)
|
|
}
|
|
|
|
p.param = param
|
|
p.KnownFormats = formats
|
|
p.Options = opts
|
|
p.validators = [6]valueValidator{
|
|
newTypeValidator(
|
|
param.Name,
|
|
param.In,
|
|
spec.StringOrArray([]string{param.Type}),
|
|
param.Nullable,
|
|
param.Format,
|
|
p.Options,
|
|
),
|
|
p.stringValidator(),
|
|
p.formatValidator(),
|
|
p.numberValidator(),
|
|
p.sliceValidator(),
|
|
p.commonValidator(),
|
|
}
|
|
|
|
return p
|
|
}
|
|
|
|
// Validate the data against the description of the parameter
|
|
func (p *ParamValidator) Validate(data interface{}) *Result {
|
|
if data == nil {
|
|
return nil
|
|
}
|
|
|
|
var result *Result
|
|
if p.Options.recycleResult {
|
|
result = pools.poolOfResults.BorrowResult()
|
|
} else {
|
|
result = new(Result)
|
|
}
|
|
|
|
tpe := reflect.TypeOf(data)
|
|
kind := tpe.Kind()
|
|
|
|
if p.Options.recycleValidators {
|
|
defer func() {
|
|
p.redeemChildren()
|
|
p.redeem()
|
|
}()
|
|
}
|
|
|
|
// TODO: validate type
|
|
for idx, validator := range p.validators {
|
|
if !validator.Applies(p.param, kind) {
|
|
if p.Options.recycleValidators {
|
|
// Validate won't be called, so relinquish this validator
|
|
if redeemableChildren, ok := validator.(interface{ redeemChildren() }); ok {
|
|
redeemableChildren.redeemChildren()
|
|
}
|
|
if redeemable, ok := validator.(interface{ redeem() }); ok {
|
|
redeemable.redeem()
|
|
}
|
|
p.validators[idx] = nil // prevents further (unsafe) usage
|
|
}
|
|
|
|
continue
|
|
}
|
|
|
|
err := validator.Validate(data)
|
|
if p.Options.recycleValidators {
|
|
p.validators[idx] = nil // prevents further (unsafe) usage
|
|
}
|
|
if err != nil {
|
|
if err.HasErrors() {
|
|
result.Merge(err)
|
|
break
|
|
}
|
|
result.Merge(err)
|
|
}
|
|
}
|
|
|
|
return result
|
|
}
|
|
|
|
func (p *ParamValidator) commonValidator() valueValidator {
|
|
return newBasicCommonValidator(
|
|
p.param.Name,
|
|
p.param.In,
|
|
p.param.Default,
|
|
p.param.Enum,
|
|
p.Options,
|
|
)
|
|
}
|
|
|
|
func (p *ParamValidator) sliceValidator() valueValidator {
|
|
return newBasicSliceValidator(
|
|
p.param.Name,
|
|
p.param.In,
|
|
p.param.Default,
|
|
p.param.MaxItems,
|
|
p.param.MinItems,
|
|
p.param.UniqueItems,
|
|
p.param.Items,
|
|
p.param,
|
|
p.KnownFormats,
|
|
p.Options,
|
|
)
|
|
}
|
|
|
|
func (p *ParamValidator) numberValidator() valueValidator {
|
|
return newNumberValidator(
|
|
p.param.Name,
|
|
p.param.In,
|
|
p.param.Default,
|
|
p.param.MultipleOf,
|
|
p.param.Maximum,
|
|
p.param.ExclusiveMaximum,
|
|
p.param.Minimum,
|
|
p.param.ExclusiveMinimum,
|
|
p.param.Type,
|
|
p.param.Format,
|
|
p.Options,
|
|
)
|
|
}
|
|
|
|
func (p *ParamValidator) stringValidator() valueValidator {
|
|
return newStringValidator(
|
|
p.param.Name,
|
|
p.param.In,
|
|
p.param.Default,
|
|
p.param.Required,
|
|
p.param.AllowEmptyValue,
|
|
p.param.MaxLength,
|
|
p.param.MinLength,
|
|
p.param.Pattern,
|
|
p.Options,
|
|
)
|
|
}
|
|
|
|
func (p *ParamValidator) formatValidator() valueValidator {
|
|
return newFormatValidator(
|
|
p.param.Name,
|
|
p.param.In,
|
|
p.param.Format,
|
|
p.KnownFormats,
|
|
p.Options,
|
|
)
|
|
}
|
|
|
|
func (p *ParamValidator) redeem() {
|
|
pools.poolOfParamValidators.RedeemValidator(p)
|
|
}
|
|
|
|
func (p *ParamValidator) redeemChildren() {
|
|
for idx, validator := range p.validators {
|
|
if validator == nil {
|
|
continue
|
|
}
|
|
if redeemableChildren, ok := validator.(interface{ redeemChildren() }); ok {
|
|
redeemableChildren.redeemChildren()
|
|
}
|
|
if redeemable, ok := validator.(interface{ redeem() }); ok {
|
|
redeemable.redeem()
|
|
}
|
|
p.validators[idx] = nil // free up allocated children if not in pool
|
|
}
|
|
}
|
|
|
|
type basicSliceValidator struct {
|
|
Path string
|
|
In string
|
|
Default interface{}
|
|
MaxItems *int64
|
|
MinItems *int64
|
|
UniqueItems bool
|
|
Items *spec.Items
|
|
Source interface{}
|
|
KnownFormats strfmt.Registry
|
|
Options *SchemaValidatorOptions
|
|
}
|
|
|
|
func newBasicSliceValidator(
|
|
path, in string,
|
|
def interface{}, maxItems, minItems *int64, uniqueItems bool, items *spec.Items,
|
|
source interface{}, formats strfmt.Registry,
|
|
opts *SchemaValidatorOptions) *basicSliceValidator {
|
|
if opts == nil {
|
|
opts = new(SchemaValidatorOptions)
|
|
}
|
|
|
|
var s *basicSliceValidator
|
|
if opts.recycleValidators {
|
|
s = pools.poolOfBasicSliceValidators.BorrowValidator()
|
|
} else {
|
|
s = new(basicSliceValidator)
|
|
}
|
|
|
|
s.Path = path
|
|
s.In = in
|
|
s.Default = def
|
|
s.MaxItems = maxItems
|
|
s.MinItems = minItems
|
|
s.UniqueItems = uniqueItems
|
|
s.Items = items
|
|
s.Source = source
|
|
s.KnownFormats = formats
|
|
s.Options = opts
|
|
|
|
return s
|
|
}
|
|
|
|
func (s *basicSliceValidator) SetPath(path string) {
|
|
s.Path = path
|
|
}
|
|
|
|
func (s *basicSliceValidator) Applies(source interface{}, kind reflect.Kind) bool {
|
|
switch source.(type) {
|
|
case *spec.Parameter, *spec.Items, *spec.Header:
|
|
return kind == reflect.Slice
|
|
default:
|
|
return false
|
|
}
|
|
}
|
|
|
|
func (s *basicSliceValidator) Validate(data interface{}) *Result {
|
|
if s.Options.recycleValidators {
|
|
defer func() {
|
|
s.redeem()
|
|
}()
|
|
}
|
|
val := reflect.ValueOf(data)
|
|
|
|
size := int64(val.Len())
|
|
if s.MinItems != nil {
|
|
if err := MinItems(s.Path, s.In, size, *s.MinItems); err != nil {
|
|
return errorHelp.sErr(err, s.Options.recycleResult)
|
|
}
|
|
}
|
|
|
|
if s.MaxItems != nil {
|
|
if err := MaxItems(s.Path, s.In, size, *s.MaxItems); err != nil {
|
|
return errorHelp.sErr(err, s.Options.recycleResult)
|
|
}
|
|
}
|
|
|
|
if s.UniqueItems {
|
|
if err := UniqueItems(s.Path, s.In, data); err != nil {
|
|
return errorHelp.sErr(err, s.Options.recycleResult)
|
|
}
|
|
}
|
|
|
|
if s.Items == nil {
|
|
return nil
|
|
}
|
|
|
|
for i := 0; i < int(size); i++ {
|
|
itemsValidator := newItemsValidator(s.Path, s.In, s.Items, s.Source, s.KnownFormats, s.Options)
|
|
ele := val.Index(i)
|
|
if err := itemsValidator.Validate(i, ele.Interface()); err != nil {
|
|
if err.HasErrors() {
|
|
return err
|
|
}
|
|
if err.wantsRedeemOnMerge {
|
|
pools.poolOfResults.RedeemResult(err)
|
|
}
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (s *basicSliceValidator) redeem() {
|
|
pools.poolOfBasicSliceValidators.RedeemValidator(s)
|
|
}
|
|
|
|
type numberValidator struct {
|
|
Path string
|
|
In string
|
|
Default interface{}
|
|
MultipleOf *float64
|
|
Maximum *float64
|
|
ExclusiveMaximum bool
|
|
Minimum *float64
|
|
ExclusiveMinimum bool
|
|
// Allows for more accurate behavior regarding integers
|
|
Type string
|
|
Format string
|
|
Options *SchemaValidatorOptions
|
|
}
|
|
|
|
func newNumberValidator(
|
|
path, in string, def interface{},
|
|
multipleOf, maximum *float64, exclusiveMaximum bool, minimum *float64, exclusiveMinimum bool,
|
|
typ, format string,
|
|
opts *SchemaValidatorOptions) *numberValidator {
|
|
if opts == nil {
|
|
opts = new(SchemaValidatorOptions)
|
|
}
|
|
|
|
var n *numberValidator
|
|
if opts.recycleValidators {
|
|
n = pools.poolOfNumberValidators.BorrowValidator()
|
|
} else {
|
|
n = new(numberValidator)
|
|
}
|
|
|
|
n.Path = path
|
|
n.In = in
|
|
n.Default = def
|
|
n.MultipleOf = multipleOf
|
|
n.Maximum = maximum
|
|
n.ExclusiveMaximum = exclusiveMaximum
|
|
n.Minimum = minimum
|
|
n.ExclusiveMinimum = exclusiveMinimum
|
|
n.Type = typ
|
|
n.Format = format
|
|
n.Options = opts
|
|
|
|
return n
|
|
}
|
|
|
|
func (n *numberValidator) SetPath(path string) {
|
|
n.Path = path
|
|
}
|
|
|
|
func (n *numberValidator) Applies(source interface{}, kind reflect.Kind) bool {
|
|
switch source.(type) {
|
|
case *spec.Parameter, *spec.Schema, *spec.Items, *spec.Header:
|
|
isInt := kind >= reflect.Int && kind <= reflect.Uint64
|
|
isFloat := kind == reflect.Float32 || kind == reflect.Float64
|
|
return isInt || isFloat
|
|
default:
|
|
return false
|
|
}
|
|
}
|
|
|
|
// Validate provides a validator for generic JSON numbers,
|
|
//
|
|
// By default, numbers are internally represented as float64.
|
|
// Formats float, or float32 may alter this behavior by mapping to float32.
|
|
// A special validation process is followed for integers, with optional "format":
|
|
// this is an attempt to provide a validation with native types.
|
|
//
|
|
// NOTE: since the constraint specified (boundary, multipleOf) is unmarshalled
|
|
// as float64, loss of information remains possible (e.g. on very large integers).
|
|
//
|
|
// Since this value directly comes from the unmarshalling, it is not possible
|
|
// at this stage of processing to check further and guarantee the correctness of such values.
|
|
//
|
|
// Normally, the JSON Number.MAX_SAFE_INTEGER (resp. Number.MIN_SAFE_INTEGER)
|
|
// would check we do not get such a loss.
|
|
//
|
|
// If this is the case, replace AddErrors() by AddWarnings() and IsValid() by !HasWarnings().
|
|
//
|
|
// TODO: consider replacing boundary check errors by simple warnings.
|
|
//
|
|
// TODO: default boundaries with MAX_SAFE_INTEGER are not checked (specific to json.Number?)
|
|
func (n *numberValidator) Validate(val interface{}) *Result {
|
|
if n.Options.recycleValidators {
|
|
defer func() {
|
|
n.redeem()
|
|
}()
|
|
}
|
|
|
|
var res, resMultiple, resMinimum, resMaximum *Result
|
|
if n.Options.recycleResult {
|
|
res = pools.poolOfResults.BorrowResult()
|
|
} else {
|
|
res = new(Result)
|
|
}
|
|
|
|
// Used only to attempt to validate constraint on value,
|
|
// even though value or constraint specified do not match type and format
|
|
data := valueHelp.asFloat64(val)
|
|
|
|
// Is the provided value within the range of the specified numeric type and format?
|
|
res.AddErrors(IsValueValidAgainstRange(val, n.Type, n.Format, "Checked", n.Path))
|
|
|
|
if n.MultipleOf != nil {
|
|
resMultiple = pools.poolOfResults.BorrowResult()
|
|
|
|
// Is the constraint specifier within the range of the specific numeric type and format?
|
|
resMultiple.AddErrors(IsValueValidAgainstRange(*n.MultipleOf, n.Type, n.Format, "MultipleOf", n.Path))
|
|
if resMultiple.IsValid() {
|
|
// Constraint validated with compatible types
|
|
if err := MultipleOfNativeType(n.Path, n.In, val, *n.MultipleOf); err != nil {
|
|
resMultiple.Merge(errorHelp.sErr(err, n.Options.recycleResult))
|
|
}
|
|
} else {
|
|
// Constraint nevertheless validated, converted as general number
|
|
if err := MultipleOf(n.Path, n.In, data, *n.MultipleOf); err != nil {
|
|
resMultiple.Merge(errorHelp.sErr(err, n.Options.recycleResult))
|
|
}
|
|
}
|
|
}
|
|
|
|
if n.Maximum != nil {
|
|
resMaximum = pools.poolOfResults.BorrowResult()
|
|
|
|
// Is the constraint specifier within the range of the specific numeric type and format?
|
|
resMaximum.AddErrors(IsValueValidAgainstRange(*n.Maximum, n.Type, n.Format, "Maximum boundary", n.Path))
|
|
if resMaximum.IsValid() {
|
|
// Constraint validated with compatible types
|
|
if err := MaximumNativeType(n.Path, n.In, val, *n.Maximum, n.ExclusiveMaximum); err != nil {
|
|
resMaximum.Merge(errorHelp.sErr(err, n.Options.recycleResult))
|
|
}
|
|
} else {
|
|
// Constraint nevertheless validated, converted as general number
|
|
if err := Maximum(n.Path, n.In, data, *n.Maximum, n.ExclusiveMaximum); err != nil {
|
|
resMaximum.Merge(errorHelp.sErr(err, n.Options.recycleResult))
|
|
}
|
|
}
|
|
}
|
|
|
|
if n.Minimum != nil {
|
|
resMinimum = pools.poolOfResults.BorrowResult()
|
|
|
|
// Is the constraint specifier within the range of the specific numeric type and format?
|
|
resMinimum.AddErrors(IsValueValidAgainstRange(*n.Minimum, n.Type, n.Format, "Minimum boundary", n.Path))
|
|
if resMinimum.IsValid() {
|
|
// Constraint validated with compatible types
|
|
if err := MinimumNativeType(n.Path, n.In, val, *n.Minimum, n.ExclusiveMinimum); err != nil {
|
|
resMinimum.Merge(errorHelp.sErr(err, n.Options.recycleResult))
|
|
}
|
|
} else {
|
|
// Constraint nevertheless validated, converted as general number
|
|
if err := Minimum(n.Path, n.In, data, *n.Minimum, n.ExclusiveMinimum); err != nil {
|
|
resMinimum.Merge(errorHelp.sErr(err, n.Options.recycleResult))
|
|
}
|
|
}
|
|
}
|
|
res.Merge(resMultiple, resMinimum, resMaximum)
|
|
res.Inc()
|
|
|
|
return res
|
|
}
|
|
|
|
func (n *numberValidator) redeem() {
|
|
pools.poolOfNumberValidators.RedeemValidator(n)
|
|
}
|
|
|
|
type stringValidator struct {
|
|
Path string
|
|
In string
|
|
Default interface{}
|
|
Required bool
|
|
AllowEmptyValue bool
|
|
MaxLength *int64
|
|
MinLength *int64
|
|
Pattern string
|
|
Options *SchemaValidatorOptions
|
|
}
|
|
|
|
func newStringValidator(
|
|
path, in string,
|
|
def interface{}, required, allowEmpty bool, maxLength, minLength *int64, pattern string,
|
|
opts *SchemaValidatorOptions) *stringValidator {
|
|
if opts == nil {
|
|
opts = new(SchemaValidatorOptions)
|
|
}
|
|
|
|
var s *stringValidator
|
|
if opts.recycleValidators {
|
|
s = pools.poolOfStringValidators.BorrowValidator()
|
|
} else {
|
|
s = new(stringValidator)
|
|
}
|
|
|
|
s.Path = path
|
|
s.In = in
|
|
s.Default = def
|
|
s.Required = required
|
|
s.AllowEmptyValue = allowEmpty
|
|
s.MaxLength = maxLength
|
|
s.MinLength = minLength
|
|
s.Pattern = pattern
|
|
s.Options = opts
|
|
|
|
return s
|
|
}
|
|
|
|
func (s *stringValidator) SetPath(path string) {
|
|
s.Path = path
|
|
}
|
|
|
|
func (s *stringValidator) Applies(source interface{}, kind reflect.Kind) bool {
|
|
switch source.(type) {
|
|
case *spec.Parameter, *spec.Schema, *spec.Items, *spec.Header:
|
|
return kind == reflect.String
|
|
default:
|
|
return false
|
|
}
|
|
}
|
|
|
|
func (s *stringValidator) Validate(val interface{}) *Result {
|
|
if s.Options.recycleValidators {
|
|
defer func() {
|
|
s.redeem()
|
|
}()
|
|
}
|
|
|
|
data, ok := val.(string)
|
|
if !ok {
|
|
return errorHelp.sErr(errors.InvalidType(s.Path, s.In, stringType, val), s.Options.recycleResult)
|
|
}
|
|
|
|
if s.Required && !s.AllowEmptyValue && (s.Default == nil || s.Default == "") {
|
|
if err := RequiredString(s.Path, s.In, data); err != nil {
|
|
return errorHelp.sErr(err, s.Options.recycleResult)
|
|
}
|
|
}
|
|
|
|
if s.MaxLength != nil {
|
|
if err := MaxLength(s.Path, s.In, data, *s.MaxLength); err != nil {
|
|
return errorHelp.sErr(err, s.Options.recycleResult)
|
|
}
|
|
}
|
|
|
|
if s.MinLength != nil {
|
|
if err := MinLength(s.Path, s.In, data, *s.MinLength); err != nil {
|
|
return errorHelp.sErr(err, s.Options.recycleResult)
|
|
}
|
|
}
|
|
|
|
if s.Pattern != "" {
|
|
if err := Pattern(s.Path, s.In, data, s.Pattern); err != nil {
|
|
return errorHelp.sErr(err, s.Options.recycleResult)
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (s *stringValidator) redeem() {
|
|
pools.poolOfStringValidators.RedeemValidator(s)
|
|
}
|