Платформа ЦРНП "Мирокод" для разработки проектов
https://git.mirocod.ru
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
240 lines
6.9 KiB
240 lines
6.9 KiB
// 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, options ...Option) *schemaPropsValidator { |
|
anyValidators := make([]SchemaValidator, 0, len(anyOf)) |
|
for _, v := range anyOf { |
|
v := v |
|
anyValidators = append(anyValidators, *NewSchemaValidator(&v, root, path, formats, options...)) |
|
} |
|
allValidators := make([]SchemaValidator, 0, len(allOf)) |
|
for _, v := range allOf { |
|
v := v |
|
allValidators = append(allValidators, *NewSchemaValidator(&v, root, path, formats, options...)) |
|
} |
|
oneValidators := make([]SchemaValidator, 0, len(oneOf)) |
|
for _, v := range oneOf { |
|
v := v |
|
oneValidators = append(oneValidators, *NewSchemaValidator(&v, root, path, formats, options...)) |
|
} |
|
|
|
var notValidator *SchemaValidator |
|
if not != nil { |
|
notValidator = NewSchemaValidator(not, root, path, formats, options...) |
|
} |
|
|
|
schOptions := &SchemaValidatorOptions{} |
|
for _, o := range options { |
|
o(schOptions) |
|
} |
|
return &schemaPropsValidator{ |
|
Path: path, |
|
In: in, |
|
AllOf: allOf, |
|
OneOf: oneOf, |
|
AnyOf: anyOf, |
|
Not: not, |
|
Dependencies: deps, |
|
anyOfValidators: anyValidators, |
|
allOfValidators: allValidators, |
|
oneOfValidators: oneValidators, |
|
notValidator: notValidator, |
|
Root: root, |
|
KnownFormats: formats, |
|
Options: *schOptions, |
|
} |
|
} |
|
|
|
func (s *schemaPropsValidator) Applies(source interface{}, kind reflect.Kind) bool { |
|
r := reflect.TypeOf(source) == specSchemaType |
|
debugLog("schema props validator for %q applies %t for %T (kind: %v)\n", s.Path, r, source, kind) |
|
return r |
|
} |
|
|
|
func (s *schemaPropsValidator) Validate(data interface{}) *Result { |
|
mainResult := new(Result) |
|
|
|
// Intermediary error results |
|
|
|
// IMPORTANT! messages from underlying validators |
|
keepResultAnyOf := new(Result) |
|
keepResultOneOf := new(Result) |
|
keepResultAllOf := new(Result) |
|
|
|
// Validates at least one in anyOf schemas |
|
var firstSuccess *Result |
|
if len(s.anyOfValidators) > 0 { |
|
var bestFailures *Result |
|
succeededOnce := false |
|
for _, anyOfSchema := range s.anyOfValidators { |
|
result := anyOfSchema.Validate(data) |
|
// We keep inner IMPORTANT! errors no matter what MatchCount tells us |
|
keepResultAnyOf.Merge(result.keepRelevantErrors()) |
|
if result.IsValid() { |
|
bestFailures = nil |
|
succeededOnce = true |
|
if firstSuccess == nil { |
|
firstSuccess = result |
|
} |
|
keepResultAnyOf = new(Result) |
|
break |
|
} |
|
// MatchCount is used to select errors from the schema with most positive checks |
|
if bestFailures == nil || result.MatchCount > bestFailures.MatchCount { |
|
bestFailures = result |
|
} |
|
} |
|
|
|
if !succeededOnce { |
|
mainResult.AddErrors(mustValidateAtLeastOneSchemaMsg(s.Path)) |
|
} |
|
if bestFailures != nil { |
|
mainResult.Merge(bestFailures) |
|
} else if firstSuccess != nil { |
|
mainResult.Merge(firstSuccess) |
|
} |
|
} |
|
|
|
// Validates exactly one in oneOf schemas |
|
if len(s.oneOfValidators) > 0 { |
|
var bestFailures *Result |
|
var firstSuccess *Result |
|
validated := 0 |
|
|
|
for _, oneOfSchema := range s.oneOfValidators { |
|
result := oneOfSchema.Validate(data) |
|
// We keep inner IMPORTANT! errors no matter what MatchCount tells us |
|
keepResultOneOf.Merge(result.keepRelevantErrors()) |
|
if result.IsValid() { |
|
validated++ |
|
bestFailures = nil |
|
if firstSuccess == nil { |
|
firstSuccess = result |
|
} |
|
keepResultOneOf = new(Result) |
|
continue |
|
} |
|
// MatchCount is used to select errors from the schema with most positive checks |
|
if validated == 0 && (bestFailures == nil || result.MatchCount > bestFailures.MatchCount) { |
|
bestFailures = result |
|
} |
|
} |
|
|
|
if validated != 1 { |
|
additionalMsg := "" |
|
if validated == 0 { |
|
additionalMsg = "Found none valid" |
|
} else { |
|
additionalMsg = fmt.Sprintf("Found %d valid alternatives", validated) |
|
} |
|
|
|
mainResult.AddErrors(mustValidateOnlyOneSchemaMsg(s.Path, additionalMsg)) |
|
if bestFailures != nil { |
|
mainResult.Merge(bestFailures) |
|
} |
|
} else if firstSuccess != nil { |
|
mainResult.Merge(firstSuccess) |
|
} |
|
} |
|
|
|
// Validates all of allOf schemas |
|
if len(s.allOfValidators) > 0 { |
|
validated := 0 |
|
|
|
for _, allOfSchema := range s.allOfValidators { |
|
result := allOfSchema.Validate(data) |
|
// We keep inner IMPORTANT! errors no matter what MatchCount tells us |
|
keepResultAllOf.Merge(result.keepRelevantErrors()) |
|
//keepResultAllOf.Merge(result) |
|
if result.IsValid() { |
|
validated++ |
|
} |
|
mainResult.Merge(result) |
|
} |
|
|
|
if validated != len(s.allOfValidators) { |
|
additionalMsg := "" |
|
if validated == 0 { |
|
additionalMsg = ". None validated" |
|
} |
|
|
|
mainResult.AddErrors(mustValidateAllSchemasMsg(s.Path, additionalMsg)) |
|
} |
|
} |
|
|
|
if s.notValidator != nil { |
|
result := s.notValidator.Validate(data) |
|
// We keep inner IMPORTANT! errors no matter what MatchCount tells us |
|
if result.IsValid() { |
|
mainResult.AddErrors(mustNotValidatechemaMsg(s.Path)) |
|
} |
|
} |
|
|
|
if s.Dependencies != nil && len(s.Dependencies) > 0 && reflect.TypeOf(data).Kind() == reflect.Map { |
|
val := data.(map[string]interface{}) |
|
for key := range val { |
|
if dep, ok := s.Dependencies[key]; ok { |
|
|
|
if dep.Schema != nil { |
|
mainResult.Merge(NewSchemaValidator(dep.Schema, s.Root, s.Path+"."+key, s.KnownFormats, s.Options.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)) |
|
} |
|
} |
|
} |
|
} |
|
} |
|
} |
|
|
|
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) |
|
}
|
|
|