...

Source file src/github.com/go-openapi/validate/validator.go

Documentation: github.com/go-openapi/validate

     1  // Copyright 2015 go-swagger maintainers
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //    http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package validate
    16  
    17  import (
    18  	"fmt"
    19  	"reflect"
    20  
    21  	"github.com/go-openapi/errors"
    22  	"github.com/go-openapi/spec"
    23  	"github.com/go-openapi/strfmt"
    24  )
    25  
    26  // An EntityValidator is an interface for things that can validate entities
    27  type EntityValidator interface {
    28  	Validate(interface{}) *Result
    29  }
    30  
    31  type valueValidator interface {
    32  	SetPath(path string)
    33  	Applies(interface{}, reflect.Kind) bool
    34  	Validate(interface{}) *Result
    35  }
    36  
    37  type itemsValidator struct {
    38  	items        *spec.Items
    39  	root         interface{}
    40  	path         string
    41  	in           string
    42  	validators   [6]valueValidator
    43  	KnownFormats strfmt.Registry
    44  	Options      *SchemaValidatorOptions
    45  }
    46  
    47  func newItemsValidator(path, in string, items *spec.Items, root interface{}, formats strfmt.Registry, opts *SchemaValidatorOptions) *itemsValidator {
    48  	if opts == nil {
    49  		opts = new(SchemaValidatorOptions)
    50  	}
    51  
    52  	var iv *itemsValidator
    53  	if opts.recycleValidators {
    54  		iv = pools.poolOfItemsValidators.BorrowValidator()
    55  	} else {
    56  		iv = new(itemsValidator)
    57  	}
    58  
    59  	iv.path = path
    60  	iv.in = in
    61  	iv.items = items
    62  	iv.root = root
    63  	iv.KnownFormats = formats
    64  	iv.Options = opts
    65  	iv.validators = [6]valueValidator{
    66  		iv.typeValidator(),
    67  		iv.stringValidator(),
    68  		iv.formatValidator(),
    69  		iv.numberValidator(),
    70  		iv.sliceValidator(),
    71  		iv.commonValidator(),
    72  	}
    73  	return iv
    74  }
    75  
    76  func (i *itemsValidator) Validate(index int, data interface{}) *Result {
    77  	if i.Options.recycleValidators {
    78  		defer func() {
    79  			i.redeemChildren()
    80  			i.redeem()
    81  		}()
    82  	}
    83  
    84  	tpe := reflect.TypeOf(data)
    85  	kind := tpe.Kind()
    86  	var result *Result
    87  	if i.Options.recycleResult {
    88  		result = pools.poolOfResults.BorrowResult()
    89  	} else {
    90  		result = new(Result)
    91  	}
    92  
    93  	path := fmt.Sprintf("%s.%d", i.path, index)
    94  
    95  	for idx, validator := range i.validators {
    96  		if !validator.Applies(i.root, kind) {
    97  			if i.Options.recycleValidators {
    98  				// Validate won't be called, so relinquish this validator
    99  				if redeemableChildren, ok := validator.(interface{ redeemChildren() }); ok {
   100  					redeemableChildren.redeemChildren()
   101  				}
   102  				if redeemable, ok := validator.(interface{ redeem() }); ok {
   103  					redeemable.redeem()
   104  				}
   105  				i.validators[idx] = nil // prevents further (unsafe) usage
   106  			}
   107  
   108  			continue
   109  		}
   110  
   111  		validator.SetPath(path)
   112  		err := validator.Validate(data)
   113  		if i.Options.recycleValidators {
   114  			i.validators[idx] = nil // prevents further (unsafe) usage
   115  		}
   116  		if err != nil {
   117  			result.Inc()
   118  			if err.HasErrors() {
   119  				result.Merge(err)
   120  
   121  				break
   122  			}
   123  
   124  			result.Merge(err)
   125  		}
   126  	}
   127  
   128  	return result
   129  }
   130  
   131  func (i *itemsValidator) typeValidator() valueValidator {
   132  	return newTypeValidator(
   133  		i.path,
   134  		i.in,
   135  		spec.StringOrArray([]string{i.items.Type}),
   136  		i.items.Nullable,
   137  		i.items.Format,
   138  		i.Options,
   139  	)
   140  }
   141  
   142  func (i *itemsValidator) commonValidator() valueValidator {
   143  	return newBasicCommonValidator(
   144  		"",
   145  		i.in,
   146  		i.items.Default,
   147  		i.items.Enum,
   148  		i.Options,
   149  	)
   150  }
   151  
   152  func (i *itemsValidator) sliceValidator() valueValidator {
   153  	return newBasicSliceValidator(
   154  		"",
   155  		i.in,
   156  		i.items.Default,
   157  		i.items.MaxItems,
   158  		i.items.MinItems,
   159  		i.items.UniqueItems,
   160  		i.items.Items,
   161  		i.root,
   162  		i.KnownFormats,
   163  		i.Options,
   164  	)
   165  }
   166  
   167  func (i *itemsValidator) numberValidator() valueValidator {
   168  	return newNumberValidator(
   169  		"",
   170  		i.in,
   171  		i.items.Default,
   172  		i.items.MultipleOf,
   173  		i.items.Maximum,
   174  		i.items.ExclusiveMaximum,
   175  		i.items.Minimum,
   176  		i.items.ExclusiveMinimum,
   177  		i.items.Type,
   178  		i.items.Format,
   179  		i.Options,
   180  	)
   181  }
   182  
   183  func (i *itemsValidator) stringValidator() valueValidator {
   184  	return newStringValidator(
   185  		"",
   186  		i.in,
   187  		i.items.Default,
   188  		false, // Required
   189  		false, // AllowEmpty
   190  		i.items.MaxLength,
   191  		i.items.MinLength,
   192  		i.items.Pattern,
   193  		i.Options,
   194  	)
   195  }
   196  
   197  func (i *itemsValidator) formatValidator() valueValidator {
   198  	return newFormatValidator(
   199  		"",
   200  		i.in,
   201  		i.items.Format,
   202  		i.KnownFormats,
   203  		i.Options,
   204  	)
   205  }
   206  
   207  func (i *itemsValidator) redeem() {
   208  	pools.poolOfItemsValidators.RedeemValidator(i)
   209  }
   210  
   211  func (i *itemsValidator) redeemChildren() {
   212  	for idx, validator := range i.validators {
   213  		if validator == nil {
   214  			continue
   215  		}
   216  		if redeemableChildren, ok := validator.(interface{ redeemChildren() }); ok {
   217  			redeemableChildren.redeemChildren()
   218  		}
   219  		if redeemable, ok := validator.(interface{ redeem() }); ok {
   220  			redeemable.redeem()
   221  		}
   222  		i.validators[idx] = nil // free up allocated children if not in pool
   223  	}
   224  }
   225  
   226  type basicCommonValidator struct {
   227  	Path    string
   228  	In      string
   229  	Default interface{}
   230  	Enum    []interface{}
   231  	Options *SchemaValidatorOptions
   232  }
   233  
   234  func newBasicCommonValidator(path, in string, def interface{}, enum []interface{}, opts *SchemaValidatorOptions) *basicCommonValidator {
   235  	if opts == nil {
   236  		opts = new(SchemaValidatorOptions)
   237  	}
   238  
   239  	var b *basicCommonValidator
   240  	if opts.recycleValidators {
   241  		b = pools.poolOfBasicCommonValidators.BorrowValidator()
   242  	} else {
   243  		b = new(basicCommonValidator)
   244  	}
   245  
   246  	b.Path = path
   247  	b.In = in
   248  	b.Default = def
   249  	b.Enum = enum
   250  	b.Options = opts
   251  
   252  	return b
   253  }
   254  
   255  func (b *basicCommonValidator) SetPath(path string) {
   256  	b.Path = path
   257  }
   258  
   259  func (b *basicCommonValidator) Applies(source interface{}, _ reflect.Kind) bool {
   260  	switch source.(type) {
   261  	case *spec.Parameter, *spec.Schema, *spec.Header:
   262  		return true
   263  	default:
   264  		return false
   265  	}
   266  }
   267  
   268  func (b *basicCommonValidator) Validate(data interface{}) (res *Result) {
   269  	if b.Options.recycleValidators {
   270  		defer func() {
   271  			b.redeem()
   272  		}()
   273  	}
   274  
   275  	if len(b.Enum) == 0 {
   276  		return nil
   277  	}
   278  
   279  	for _, enumValue := range b.Enum {
   280  		actualType := reflect.TypeOf(enumValue)
   281  		if actualType == nil { // Safeguard
   282  			continue
   283  		}
   284  
   285  		expectedValue := reflect.ValueOf(data)
   286  		if expectedValue.IsValid() &&
   287  			expectedValue.Type().ConvertibleTo(actualType) &&
   288  			reflect.DeepEqual(expectedValue.Convert(actualType).Interface(), enumValue) {
   289  			return nil
   290  		}
   291  	}
   292  
   293  	return errorHelp.sErr(errors.EnumFail(b.Path, b.In, data, b.Enum), b.Options.recycleResult)
   294  }
   295  
   296  func (b *basicCommonValidator) redeem() {
   297  	pools.poolOfBasicCommonValidators.RedeemValidator(b)
   298  }
   299  
   300  // A HeaderValidator has very limited subset of validations to apply
   301  type HeaderValidator struct {
   302  	name         string
   303  	header       *spec.Header
   304  	validators   [6]valueValidator
   305  	KnownFormats strfmt.Registry
   306  	Options      *SchemaValidatorOptions
   307  }
   308  
   309  // NewHeaderValidator creates a new header validator object
   310  func NewHeaderValidator(name string, header *spec.Header, formats strfmt.Registry, options ...Option) *HeaderValidator {
   311  	opts := new(SchemaValidatorOptions)
   312  	for _, o := range options {
   313  		o(opts)
   314  	}
   315  
   316  	return newHeaderValidator(name, header, formats, opts)
   317  }
   318  
   319  func newHeaderValidator(name string, header *spec.Header, formats strfmt.Registry, opts *SchemaValidatorOptions) *HeaderValidator {
   320  	if opts == nil {
   321  		opts = new(SchemaValidatorOptions)
   322  	}
   323  
   324  	var p *HeaderValidator
   325  	if opts.recycleValidators {
   326  		p = pools.poolOfHeaderValidators.BorrowValidator()
   327  	} else {
   328  		p = new(HeaderValidator)
   329  	}
   330  
   331  	p.name = name
   332  	p.header = header
   333  	p.KnownFormats = formats
   334  	p.Options = opts
   335  	p.validators = [6]valueValidator{
   336  		newTypeValidator(
   337  			name,
   338  			"header",
   339  			spec.StringOrArray([]string{header.Type}),
   340  			header.Nullable,
   341  			header.Format,
   342  			p.Options,
   343  		),
   344  		p.stringValidator(),
   345  		p.formatValidator(),
   346  		p.numberValidator(),
   347  		p.sliceValidator(),
   348  		p.commonValidator(),
   349  	}
   350  
   351  	return p
   352  }
   353  
   354  // Validate the value of the header against its schema
   355  func (p *HeaderValidator) Validate(data interface{}) *Result {
   356  	if p.Options.recycleValidators {
   357  		defer func() {
   358  			p.redeemChildren()
   359  			p.redeem()
   360  		}()
   361  	}
   362  
   363  	if data == nil {
   364  		return nil
   365  	}
   366  
   367  	var result *Result
   368  	if p.Options.recycleResult {
   369  		result = pools.poolOfResults.BorrowResult()
   370  	} else {
   371  		result = new(Result)
   372  	}
   373  
   374  	tpe := reflect.TypeOf(data)
   375  	kind := tpe.Kind()
   376  
   377  	for idx, validator := range p.validators {
   378  		if !validator.Applies(p.header, kind) {
   379  			if p.Options.recycleValidators {
   380  				// Validate won't be called, so relinquish this validator
   381  				if redeemableChildren, ok := validator.(interface{ redeemChildren() }); ok {
   382  					redeemableChildren.redeemChildren()
   383  				}
   384  				if redeemable, ok := validator.(interface{ redeem() }); ok {
   385  					redeemable.redeem()
   386  				}
   387  				p.validators[idx] = nil // prevents further (unsafe) usage
   388  			}
   389  
   390  			continue
   391  		}
   392  
   393  		err := validator.Validate(data)
   394  		if p.Options.recycleValidators {
   395  			p.validators[idx] = nil // prevents further (unsafe) usage
   396  		}
   397  		if err != nil {
   398  			if err.HasErrors() {
   399  				result.Merge(err)
   400  				break
   401  			}
   402  			result.Merge(err)
   403  		}
   404  	}
   405  
   406  	return result
   407  }
   408  
   409  func (p *HeaderValidator) commonValidator() valueValidator {
   410  	return newBasicCommonValidator(
   411  		p.name,
   412  		"response",
   413  		p.header.Default,
   414  		p.header.Enum,
   415  		p.Options,
   416  	)
   417  }
   418  
   419  func (p *HeaderValidator) sliceValidator() valueValidator {
   420  	return newBasicSliceValidator(
   421  		p.name,
   422  		"response",
   423  		p.header.Default,
   424  		p.header.MaxItems,
   425  		p.header.MinItems,
   426  		p.header.UniqueItems,
   427  		p.header.Items,
   428  		p.header,
   429  		p.KnownFormats,
   430  		p.Options,
   431  	)
   432  }
   433  
   434  func (p *HeaderValidator) numberValidator() valueValidator {
   435  	return newNumberValidator(
   436  		p.name,
   437  		"response",
   438  		p.header.Default,
   439  		p.header.MultipleOf,
   440  		p.header.Maximum,
   441  		p.header.ExclusiveMaximum,
   442  		p.header.Minimum,
   443  		p.header.ExclusiveMinimum,
   444  		p.header.Type,
   445  		p.header.Format,
   446  		p.Options,
   447  	)
   448  }
   449  
   450  func (p *HeaderValidator) stringValidator() valueValidator {
   451  	return newStringValidator(
   452  		p.name,
   453  		"response",
   454  		p.header.Default,
   455  		true,
   456  		false,
   457  		p.header.MaxLength,
   458  		p.header.MinLength,
   459  		p.header.Pattern,
   460  		p.Options,
   461  	)
   462  }
   463  
   464  func (p *HeaderValidator) formatValidator() valueValidator {
   465  	return newFormatValidator(
   466  		p.name,
   467  		"response",
   468  		p.header.Format,
   469  		p.KnownFormats,
   470  		p.Options,
   471  	)
   472  }
   473  
   474  func (p *HeaderValidator) redeem() {
   475  	pools.poolOfHeaderValidators.RedeemValidator(p)
   476  }
   477  
   478  func (p *HeaderValidator) redeemChildren() {
   479  	for idx, validator := range p.validators {
   480  		if validator == nil {
   481  			continue
   482  		}
   483  		if redeemableChildren, ok := validator.(interface{ redeemChildren() }); ok {
   484  			redeemableChildren.redeemChildren()
   485  		}
   486  		if redeemable, ok := validator.(interface{ redeem() }); ok {
   487  			redeemable.redeem()
   488  		}
   489  		p.validators[idx] = nil // free up allocated children if not in pool
   490  	}
   491  }
   492  
   493  // A ParamValidator has very limited subset of validations to apply
   494  type ParamValidator struct {
   495  	param        *spec.Parameter
   496  	validators   [6]valueValidator
   497  	KnownFormats strfmt.Registry
   498  	Options      *SchemaValidatorOptions
   499  }
   500  
   501  // NewParamValidator creates a new param validator object
   502  func NewParamValidator(param *spec.Parameter, formats strfmt.Registry, options ...Option) *ParamValidator {
   503  	opts := new(SchemaValidatorOptions)
   504  	for _, o := range options {
   505  		o(opts)
   506  	}
   507  
   508  	return newParamValidator(param, formats, opts)
   509  }
   510  
   511  func newParamValidator(param *spec.Parameter, formats strfmt.Registry, opts *SchemaValidatorOptions) *ParamValidator {
   512  	if opts == nil {
   513  		opts = new(SchemaValidatorOptions)
   514  	}
   515  
   516  	var p *ParamValidator
   517  	if opts.recycleValidators {
   518  		p = pools.poolOfParamValidators.BorrowValidator()
   519  	} else {
   520  		p = new(ParamValidator)
   521  	}
   522  
   523  	p.param = param
   524  	p.KnownFormats = formats
   525  	p.Options = opts
   526  	p.validators = [6]valueValidator{
   527  		newTypeValidator(
   528  			param.Name,
   529  			param.In,
   530  			spec.StringOrArray([]string{param.Type}),
   531  			param.Nullable,
   532  			param.Format,
   533  			p.Options,
   534  		),
   535  		p.stringValidator(),
   536  		p.formatValidator(),
   537  		p.numberValidator(),
   538  		p.sliceValidator(),
   539  		p.commonValidator(),
   540  	}
   541  
   542  	return p
   543  }
   544  
   545  // Validate the data against the description of the parameter
   546  func (p *ParamValidator) Validate(data interface{}) *Result {
   547  	if data == nil {
   548  		return nil
   549  	}
   550  
   551  	var result *Result
   552  	if p.Options.recycleResult {
   553  		result = pools.poolOfResults.BorrowResult()
   554  	} else {
   555  		result = new(Result)
   556  	}
   557  
   558  	tpe := reflect.TypeOf(data)
   559  	kind := tpe.Kind()
   560  
   561  	if p.Options.recycleValidators {
   562  		defer func() {
   563  			p.redeemChildren()
   564  			p.redeem()
   565  		}()
   566  	}
   567  
   568  	// TODO: validate type
   569  	for idx, validator := range p.validators {
   570  		if !validator.Applies(p.param, kind) {
   571  			if p.Options.recycleValidators {
   572  				// Validate won't be called, so relinquish this validator
   573  				if redeemableChildren, ok := validator.(interface{ redeemChildren() }); ok {
   574  					redeemableChildren.redeemChildren()
   575  				}
   576  				if redeemable, ok := validator.(interface{ redeem() }); ok {
   577  					redeemable.redeem()
   578  				}
   579  				p.validators[idx] = nil // prevents further (unsafe) usage
   580  			}
   581  
   582  			continue
   583  		}
   584  
   585  		err := validator.Validate(data)
   586  		if p.Options.recycleValidators {
   587  			p.validators[idx] = nil // prevents further (unsafe) usage
   588  		}
   589  		if err != nil {
   590  			if err.HasErrors() {
   591  				result.Merge(err)
   592  				break
   593  			}
   594  			result.Merge(err)
   595  		}
   596  	}
   597  
   598  	return result
   599  }
   600  
   601  func (p *ParamValidator) commonValidator() valueValidator {
   602  	return newBasicCommonValidator(
   603  		p.param.Name,
   604  		p.param.In,
   605  		p.param.Default,
   606  		p.param.Enum,
   607  		p.Options,
   608  	)
   609  }
   610  
   611  func (p *ParamValidator) sliceValidator() valueValidator {
   612  	return newBasicSliceValidator(
   613  		p.param.Name,
   614  		p.param.In,
   615  		p.param.Default,
   616  		p.param.MaxItems,
   617  		p.param.MinItems,
   618  		p.param.UniqueItems,
   619  		p.param.Items,
   620  		p.param,
   621  		p.KnownFormats,
   622  		p.Options,
   623  	)
   624  }
   625  
   626  func (p *ParamValidator) numberValidator() valueValidator {
   627  	return newNumberValidator(
   628  		p.param.Name,
   629  		p.param.In,
   630  		p.param.Default,
   631  		p.param.MultipleOf,
   632  		p.param.Maximum,
   633  		p.param.ExclusiveMaximum,
   634  		p.param.Minimum,
   635  		p.param.ExclusiveMinimum,
   636  		p.param.Type,
   637  		p.param.Format,
   638  		p.Options,
   639  	)
   640  }
   641  
   642  func (p *ParamValidator) stringValidator() valueValidator {
   643  	return newStringValidator(
   644  		p.param.Name,
   645  		p.param.In,
   646  		p.param.Default,
   647  		p.param.Required,
   648  		p.param.AllowEmptyValue,
   649  		p.param.MaxLength,
   650  		p.param.MinLength,
   651  		p.param.Pattern,
   652  		p.Options,
   653  	)
   654  }
   655  
   656  func (p *ParamValidator) formatValidator() valueValidator {
   657  	return newFormatValidator(
   658  		p.param.Name,
   659  		p.param.In,
   660  		p.param.Format,
   661  		p.KnownFormats,
   662  		p.Options,
   663  	)
   664  }
   665  
   666  func (p *ParamValidator) redeem() {
   667  	pools.poolOfParamValidators.RedeemValidator(p)
   668  }
   669  
   670  func (p *ParamValidator) redeemChildren() {
   671  	for idx, validator := range p.validators {
   672  		if validator == nil {
   673  			continue
   674  		}
   675  		if redeemableChildren, ok := validator.(interface{ redeemChildren() }); ok {
   676  			redeemableChildren.redeemChildren()
   677  		}
   678  		if redeemable, ok := validator.(interface{ redeem() }); ok {
   679  			redeemable.redeem()
   680  		}
   681  		p.validators[idx] = nil // free up allocated children if not in pool
   682  	}
   683  }
   684  
   685  type basicSliceValidator struct {
   686  	Path         string
   687  	In           string
   688  	Default      interface{}
   689  	MaxItems     *int64
   690  	MinItems     *int64
   691  	UniqueItems  bool
   692  	Items        *spec.Items
   693  	Source       interface{}
   694  	KnownFormats strfmt.Registry
   695  	Options      *SchemaValidatorOptions
   696  }
   697  
   698  func newBasicSliceValidator(
   699  	path, in string,
   700  	def interface{}, maxItems, minItems *int64, uniqueItems bool, items *spec.Items,
   701  	source interface{}, formats strfmt.Registry,
   702  	opts *SchemaValidatorOptions) *basicSliceValidator {
   703  	if opts == nil {
   704  		opts = new(SchemaValidatorOptions)
   705  	}
   706  
   707  	var s *basicSliceValidator
   708  	if opts.recycleValidators {
   709  		s = pools.poolOfBasicSliceValidators.BorrowValidator()
   710  	} else {
   711  		s = new(basicSliceValidator)
   712  	}
   713  
   714  	s.Path = path
   715  	s.In = in
   716  	s.Default = def
   717  	s.MaxItems = maxItems
   718  	s.MinItems = minItems
   719  	s.UniqueItems = uniqueItems
   720  	s.Items = items
   721  	s.Source = source
   722  	s.KnownFormats = formats
   723  	s.Options = opts
   724  
   725  	return s
   726  }
   727  
   728  func (s *basicSliceValidator) SetPath(path string) {
   729  	s.Path = path
   730  }
   731  
   732  func (s *basicSliceValidator) Applies(source interface{}, kind reflect.Kind) bool {
   733  	switch source.(type) {
   734  	case *spec.Parameter, *spec.Items, *spec.Header:
   735  		return kind == reflect.Slice
   736  	default:
   737  		return false
   738  	}
   739  }
   740  
   741  func (s *basicSliceValidator) Validate(data interface{}) *Result {
   742  	if s.Options.recycleValidators {
   743  		defer func() {
   744  			s.redeem()
   745  		}()
   746  	}
   747  	val := reflect.ValueOf(data)
   748  
   749  	size := int64(val.Len())
   750  	if s.MinItems != nil {
   751  		if err := MinItems(s.Path, s.In, size, *s.MinItems); err != nil {
   752  			return errorHelp.sErr(err, s.Options.recycleResult)
   753  		}
   754  	}
   755  
   756  	if s.MaxItems != nil {
   757  		if err := MaxItems(s.Path, s.In, size, *s.MaxItems); err != nil {
   758  			return errorHelp.sErr(err, s.Options.recycleResult)
   759  		}
   760  	}
   761  
   762  	if s.UniqueItems {
   763  		if err := UniqueItems(s.Path, s.In, data); err != nil {
   764  			return errorHelp.sErr(err, s.Options.recycleResult)
   765  		}
   766  	}
   767  
   768  	if s.Items == nil {
   769  		return nil
   770  	}
   771  
   772  	for i := 0; i < int(size); i++ {
   773  		itemsValidator := newItemsValidator(s.Path, s.In, s.Items, s.Source, s.KnownFormats, s.Options)
   774  		ele := val.Index(i)
   775  		if err := itemsValidator.Validate(i, ele.Interface()); err != nil {
   776  			if err.HasErrors() {
   777  				return err
   778  			}
   779  			if err.wantsRedeemOnMerge {
   780  				pools.poolOfResults.RedeemResult(err)
   781  			}
   782  		}
   783  	}
   784  
   785  	return nil
   786  }
   787  
   788  func (s *basicSliceValidator) redeem() {
   789  	pools.poolOfBasicSliceValidators.RedeemValidator(s)
   790  }
   791  
   792  type numberValidator struct {
   793  	Path             string
   794  	In               string
   795  	Default          interface{}
   796  	MultipleOf       *float64
   797  	Maximum          *float64
   798  	ExclusiveMaximum bool
   799  	Minimum          *float64
   800  	ExclusiveMinimum bool
   801  	// Allows for more accurate behavior regarding integers
   802  	Type    string
   803  	Format  string
   804  	Options *SchemaValidatorOptions
   805  }
   806  
   807  func newNumberValidator(
   808  	path, in string, def interface{},
   809  	multipleOf, maximum *float64, exclusiveMaximum bool, minimum *float64, exclusiveMinimum bool,
   810  	typ, format string,
   811  	opts *SchemaValidatorOptions) *numberValidator {
   812  	if opts == nil {
   813  		opts = new(SchemaValidatorOptions)
   814  	}
   815  
   816  	var n *numberValidator
   817  	if opts.recycleValidators {
   818  		n = pools.poolOfNumberValidators.BorrowValidator()
   819  	} else {
   820  		n = new(numberValidator)
   821  	}
   822  
   823  	n.Path = path
   824  	n.In = in
   825  	n.Default = def
   826  	n.MultipleOf = multipleOf
   827  	n.Maximum = maximum
   828  	n.ExclusiveMaximum = exclusiveMaximum
   829  	n.Minimum = minimum
   830  	n.ExclusiveMinimum = exclusiveMinimum
   831  	n.Type = typ
   832  	n.Format = format
   833  	n.Options = opts
   834  
   835  	return n
   836  }
   837  
   838  func (n *numberValidator) SetPath(path string) {
   839  	n.Path = path
   840  }
   841  
   842  func (n *numberValidator) Applies(source interface{}, kind reflect.Kind) bool {
   843  	switch source.(type) {
   844  	case *spec.Parameter, *spec.Schema, *spec.Items, *spec.Header:
   845  		isInt := kind >= reflect.Int && kind <= reflect.Uint64
   846  		isFloat := kind == reflect.Float32 || kind == reflect.Float64
   847  		return isInt || isFloat
   848  	default:
   849  		return false
   850  	}
   851  }
   852  
   853  // Validate provides a validator for generic JSON numbers,
   854  //
   855  // By default, numbers are internally represented as float64.
   856  // Formats float, or float32 may alter this behavior by mapping to float32.
   857  // A special validation process is followed for integers, with optional "format":
   858  // this is an attempt to provide a validation with native types.
   859  //
   860  // NOTE: since the constraint specified (boundary, multipleOf) is unmarshalled
   861  // as float64, loss of information remains possible (e.g. on very large integers).
   862  //
   863  // Since this value directly comes from the unmarshalling, it is not possible
   864  // at this stage of processing to check further and guarantee the correctness of such values.
   865  //
   866  // Normally, the JSON Number.MAX_SAFE_INTEGER (resp. Number.MIN_SAFE_INTEGER)
   867  // would check we do not get such a loss.
   868  //
   869  // If this is the case, replace AddErrors() by AddWarnings() and IsValid() by !HasWarnings().
   870  //
   871  // TODO: consider replacing boundary check errors by simple warnings.
   872  //
   873  // TODO: default boundaries with MAX_SAFE_INTEGER are not checked (specific to json.Number?)
   874  func (n *numberValidator) Validate(val interface{}) *Result {
   875  	if n.Options.recycleValidators {
   876  		defer func() {
   877  			n.redeem()
   878  		}()
   879  	}
   880  
   881  	var res, resMultiple, resMinimum, resMaximum *Result
   882  	if n.Options.recycleResult {
   883  		res = pools.poolOfResults.BorrowResult()
   884  	} else {
   885  		res = new(Result)
   886  	}
   887  
   888  	// Used only to attempt to validate constraint on value,
   889  	// even though value or constraint specified do not match type and format
   890  	data := valueHelp.asFloat64(val)
   891  
   892  	// Is the provided value within the range of the specified numeric type and format?
   893  	res.AddErrors(IsValueValidAgainstRange(val, n.Type, n.Format, "Checked", n.Path))
   894  
   895  	if n.MultipleOf != nil {
   896  		resMultiple = pools.poolOfResults.BorrowResult()
   897  
   898  		// Is the constraint specifier within the range of the specific numeric type and format?
   899  		resMultiple.AddErrors(IsValueValidAgainstRange(*n.MultipleOf, n.Type, n.Format, "MultipleOf", n.Path))
   900  		if resMultiple.IsValid() {
   901  			// Constraint validated with compatible types
   902  			if err := MultipleOfNativeType(n.Path, n.In, val, *n.MultipleOf); err != nil {
   903  				resMultiple.Merge(errorHelp.sErr(err, n.Options.recycleResult))
   904  			}
   905  		} else {
   906  			// Constraint nevertheless validated, converted as general number
   907  			if err := MultipleOf(n.Path, n.In, data, *n.MultipleOf); err != nil {
   908  				resMultiple.Merge(errorHelp.sErr(err, n.Options.recycleResult))
   909  			}
   910  		}
   911  	}
   912  
   913  	if n.Maximum != nil {
   914  		resMaximum = pools.poolOfResults.BorrowResult()
   915  
   916  		// Is the constraint specifier within the range of the specific numeric type and format?
   917  		resMaximum.AddErrors(IsValueValidAgainstRange(*n.Maximum, n.Type, n.Format, "Maximum boundary", n.Path))
   918  		if resMaximum.IsValid() {
   919  			// Constraint validated with compatible types
   920  			if err := MaximumNativeType(n.Path, n.In, val, *n.Maximum, n.ExclusiveMaximum); err != nil {
   921  				resMaximum.Merge(errorHelp.sErr(err, n.Options.recycleResult))
   922  			}
   923  		} else {
   924  			// Constraint nevertheless validated, converted as general number
   925  			if err := Maximum(n.Path, n.In, data, *n.Maximum, n.ExclusiveMaximum); err != nil {
   926  				resMaximum.Merge(errorHelp.sErr(err, n.Options.recycleResult))
   927  			}
   928  		}
   929  	}
   930  
   931  	if n.Minimum != nil {
   932  		resMinimum = pools.poolOfResults.BorrowResult()
   933  
   934  		// Is the constraint specifier within the range of the specific numeric type and format?
   935  		resMinimum.AddErrors(IsValueValidAgainstRange(*n.Minimum, n.Type, n.Format, "Minimum boundary", n.Path))
   936  		if resMinimum.IsValid() {
   937  			// Constraint validated with compatible types
   938  			if err := MinimumNativeType(n.Path, n.In, val, *n.Minimum, n.ExclusiveMinimum); err != nil {
   939  				resMinimum.Merge(errorHelp.sErr(err, n.Options.recycleResult))
   940  			}
   941  		} else {
   942  			// Constraint nevertheless validated, converted as general number
   943  			if err := Minimum(n.Path, n.In, data, *n.Minimum, n.ExclusiveMinimum); err != nil {
   944  				resMinimum.Merge(errorHelp.sErr(err, n.Options.recycleResult))
   945  			}
   946  		}
   947  	}
   948  	res.Merge(resMultiple, resMinimum, resMaximum)
   949  	res.Inc()
   950  
   951  	return res
   952  }
   953  
   954  func (n *numberValidator) redeem() {
   955  	pools.poolOfNumberValidators.RedeemValidator(n)
   956  }
   957  
   958  type stringValidator struct {
   959  	Path            string
   960  	In              string
   961  	Default         interface{}
   962  	Required        bool
   963  	AllowEmptyValue bool
   964  	MaxLength       *int64
   965  	MinLength       *int64
   966  	Pattern         string
   967  	Options         *SchemaValidatorOptions
   968  }
   969  
   970  func newStringValidator(
   971  	path, in string,
   972  	def interface{}, required, allowEmpty bool, maxLength, minLength *int64, pattern string,
   973  	opts *SchemaValidatorOptions) *stringValidator {
   974  	if opts == nil {
   975  		opts = new(SchemaValidatorOptions)
   976  	}
   977  
   978  	var s *stringValidator
   979  	if opts.recycleValidators {
   980  		s = pools.poolOfStringValidators.BorrowValidator()
   981  	} else {
   982  		s = new(stringValidator)
   983  	}
   984  
   985  	s.Path = path
   986  	s.In = in
   987  	s.Default = def
   988  	s.Required = required
   989  	s.AllowEmptyValue = allowEmpty
   990  	s.MaxLength = maxLength
   991  	s.MinLength = minLength
   992  	s.Pattern = pattern
   993  	s.Options = opts
   994  
   995  	return s
   996  }
   997  
   998  func (s *stringValidator) SetPath(path string) {
   999  	s.Path = path
  1000  }
  1001  
  1002  func (s *stringValidator) Applies(source interface{}, kind reflect.Kind) bool {
  1003  	switch source.(type) {
  1004  	case *spec.Parameter, *spec.Schema, *spec.Items, *spec.Header:
  1005  		return kind == reflect.String
  1006  	default:
  1007  		return false
  1008  	}
  1009  }
  1010  
  1011  func (s *stringValidator) Validate(val interface{}) *Result {
  1012  	if s.Options.recycleValidators {
  1013  		defer func() {
  1014  			s.redeem()
  1015  		}()
  1016  	}
  1017  
  1018  	data, ok := val.(string)
  1019  	if !ok {
  1020  		return errorHelp.sErr(errors.InvalidType(s.Path, s.In, stringType, val), s.Options.recycleResult)
  1021  	}
  1022  
  1023  	if s.Required && !s.AllowEmptyValue && (s.Default == nil || s.Default == "") {
  1024  		if err := RequiredString(s.Path, s.In, data); err != nil {
  1025  			return errorHelp.sErr(err, s.Options.recycleResult)
  1026  		}
  1027  	}
  1028  
  1029  	if s.MaxLength != nil {
  1030  		if err := MaxLength(s.Path, s.In, data, *s.MaxLength); err != nil {
  1031  			return errorHelp.sErr(err, s.Options.recycleResult)
  1032  		}
  1033  	}
  1034  
  1035  	if s.MinLength != nil {
  1036  		if err := MinLength(s.Path, s.In, data, *s.MinLength); err != nil {
  1037  			return errorHelp.sErr(err, s.Options.recycleResult)
  1038  		}
  1039  	}
  1040  
  1041  	if s.Pattern != "" {
  1042  		if err := Pattern(s.Path, s.In, data, s.Pattern); err != nil {
  1043  			return errorHelp.sErr(err, s.Options.recycleResult)
  1044  		}
  1045  	}
  1046  	return nil
  1047  }
  1048  
  1049  func (s *stringValidator) redeem() {
  1050  	pools.poolOfStringValidators.RedeemValidator(s)
  1051  }
  1052  

View as plain text