diff options
Diffstat (limited to 'vendor/github.com/go-openapi/validate/schema.go')
-rw-r--r-- | vendor/github.com/go-openapi/validate/schema.go | 260 |
1 files changed, 260 insertions, 0 deletions
diff --git a/vendor/github.com/go-openapi/validate/schema.go b/vendor/github.com/go-openapi/validate/schema.go new file mode 100644 index 000000000..b817eb0ef --- /dev/null +++ b/vendor/github.com/go-openapi/validate/schema.go @@ -0,0 +1,260 @@ +// 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" +) + +var ( + specSchemaType = reflect.TypeOf(&spec.Schema{}) + specParameterType = reflect.TypeOf(&spec.Parameter{}) + specHeaderType = reflect.TypeOf(&spec.Header{}) + // specItemsType = reflect.TypeOf(&spec.Items{}) +) + +// SchemaValidator validates data against a JSON schema +type SchemaValidator struct { + Path string + in string + Schema *spec.Schema + validators []valueValidator + 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, options...).Validate(data) + 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 { + 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) + } + } + s := SchemaValidator{ + Path: root, + in: "body", + Schema: schema, + Root: rootSchema, + KnownFormats: formats, + Options: SchemaValidatorOptions{}} + for _, o := range options { + o(&s.Options) + } + s.validators = []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{}, kind reflect.Kind) bool { + _, ok := source.(*spec.Schema) + return ok +} + +// Validate validates the data against the schema +func (s *SchemaValidator) Validate(data interface{}) *Result { + result := &Result{data: data} + if s == nil { + return result + } + if s.Schema != nil { + result.addRootObjectSchemata(s.Schema) + } + + if data == nil { + result.Merge(s.validators[0].Validate(data)) // type validator + result.Merge(s.validators[6].Validate(data)) // common validator + 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 _, v := range s.validators { + if !v.Applies(s.Schema, kind) { + debugLog("%T does not apply for %v", v, kind) + continue + } + + err := v.Validate(d) + result.Merge(err) + result.Inc() + } + result.Inc() + + return result +} + +func (s *SchemaValidator) typeValidator() valueValidator { + return &typeValidator{Type: s.Schema.Type, Nullable: s.Schema.Nullable, Format: s.Schema.Format, In: s.in, Path: s.Path} +} + +func (s *SchemaValidator) commonValidator() valueValidator { + return &basicCommonValidator{ + Path: s.Path, + In: s.in, + Enum: s.Schema.Enum, + } +} + +func (s *SchemaValidator) sliceValidator() valueValidator { + return &schemaSliceValidator{ + Path: s.Path, + In: s.in, + MaxItems: s.Schema.MaxItems, + MinItems: s.Schema.MinItems, + UniqueItems: s.Schema.UniqueItems, + AdditionalItems: s.Schema.AdditionalItems, + Items: s.Schema.Items, + Root: s.Root, + KnownFormats: s.KnownFormats, + Options: s.Options, + } +} + +func (s *SchemaValidator) numberValidator() valueValidator { + return &numberValidator{ + Path: s.Path, + In: s.in, + Default: s.Schema.Default, + MultipleOf: s.Schema.MultipleOf, + Maximum: s.Schema.Maximum, + ExclusiveMaximum: s.Schema.ExclusiveMaximum, + Minimum: s.Schema.Minimum, + ExclusiveMinimum: s.Schema.ExclusiveMinimum, + } +} + +func (s *SchemaValidator) stringValidator() valueValidator { + return &stringValidator{ + Path: s.Path, + In: s.in, + MaxLength: s.Schema.MaxLength, + MinLength: s.Schema.MinLength, + Pattern: s.Schema.Pattern, + } +} + +func (s *SchemaValidator) formatValidator() valueValidator { + return &formatValidator{ + Path: s.Path, + In: s.in, + Format: s.Schema.Format, + KnownFormats: s.KnownFormats, + } +} + +func (s *SchemaValidator) schemaPropsValidator() valueValidator { + sch := s.Schema + return newSchemaPropsValidator(s.Path, s.in, sch.AllOf, sch.OneOf, sch.AnyOf, sch.Not, sch.Dependencies, s.Root, s.KnownFormats, s.Options.Options()...) +} + +func (s *SchemaValidator) objectValidator() valueValidator { + return &objectValidator{ + Path: s.Path, + In: s.in, + MaxProperties: s.Schema.MaxProperties, + MinProperties: s.Schema.MinProperties, + Required: s.Schema.Required, + Properties: s.Schema.Properties, + AdditionalProperties: s.Schema.AdditionalProperties, + PatternProperties: s.Schema.PatternProperties, + Root: s.Root, + KnownFormats: s.KnownFormats, + Options: s.Options, + } +} |