podman
260 строк · 7.2 Кб
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
15package validate
16
17import (
18"encoding/json"
19"reflect"
20
21"github.com/go-openapi/errors"
22"github.com/go-openapi/spec"
23"github.com/go-openapi/strfmt"
24"github.com/go-openapi/swag"
25)
26
27var (
28specSchemaType = reflect.TypeOf(&spec.Schema{})
29specParameterType = reflect.TypeOf(&spec.Parameter{})
30specHeaderType = reflect.TypeOf(&spec.Header{})
31// specItemsType = reflect.TypeOf(&spec.Items{})
32)
33
34// SchemaValidator validates data against a JSON schema
35type SchemaValidator struct {
36Path string
37in string
38Schema *spec.Schema
39validators []valueValidator
40Root interface{}
41KnownFormats strfmt.Registry
42Options SchemaValidatorOptions
43}
44
45// AgainstSchema validates the specified data against the provided schema, using a registry of supported formats.
46//
47// When no pre-parsed *spec.Schema structure is provided, it uses a JSON schema as default. See example.
48func AgainstSchema(schema *spec.Schema, data interface{}, formats strfmt.Registry, options ...Option) error {
49res := NewSchemaValidator(schema, nil, "", formats, options...).Validate(data)
50if res.HasErrors() {
51return errors.CompositeValidationError(res.Errors...)
52}
53return nil
54}
55
56// NewSchemaValidator creates a new schema validator.
57//
58// Panics if the provided schema is invalid.
59func NewSchemaValidator(schema *spec.Schema, rootSchema interface{}, root string, formats strfmt.Registry, options ...Option) *SchemaValidator {
60if schema == nil {
61return nil
62}
63
64if rootSchema == nil {
65rootSchema = schema
66}
67
68if schema.ID != "" || schema.Ref.String() != "" || schema.Ref.IsRoot() {
69err := spec.ExpandSchema(schema, rootSchema, nil)
70if err != nil {
71msg := invalidSchemaProvidedMsg(err).Error()
72panic(msg)
73}
74}
75s := SchemaValidator{
76Path: root,
77in: "body",
78Schema: schema,
79Root: rootSchema,
80KnownFormats: formats,
81Options: SchemaValidatorOptions{}}
82for _, o := range options {
83o(&s.Options)
84}
85s.validators = []valueValidator{
86s.typeValidator(),
87s.schemaPropsValidator(),
88s.stringValidator(),
89s.formatValidator(),
90s.numberValidator(),
91s.sliceValidator(),
92s.commonValidator(),
93s.objectValidator(),
94}
95return &s
96}
97
98// SetPath sets the path for this schema valdiator
99func (s *SchemaValidator) SetPath(path string) {
100s.Path = path
101}
102
103// Applies returns true when this schema validator applies
104func (s *SchemaValidator) Applies(source interface{}, kind reflect.Kind) bool {
105_, ok := source.(*spec.Schema)
106return ok
107}
108
109// Validate validates the data against the schema
110func (s *SchemaValidator) Validate(data interface{}) *Result {
111result := &Result{data: data}
112if s == nil {
113return result
114}
115if s.Schema != nil {
116result.addRootObjectSchemata(s.Schema)
117}
118
119if data == nil {
120result.Merge(s.validators[0].Validate(data)) // type validator
121result.Merge(s.validators[6].Validate(data)) // common validator
122return result
123}
124
125tpe := reflect.TypeOf(data)
126kind := tpe.Kind()
127for kind == reflect.Ptr {
128tpe = tpe.Elem()
129kind = tpe.Kind()
130}
131d := data
132
133if kind == reflect.Struct {
134// NOTE: since reflect retrieves the true nature of types
135// this means that all strfmt types passed here (e.g. strfmt.Datetime, etc..)
136// are converted here to strings, and structs are systematically converted
137// to map[string]interface{}.
138d = swag.ToDynamicJSON(data)
139}
140
141// TODO: this part should be handed over to type validator
142// Handle special case of json.Number data (number marshalled as string)
143isnumber := s.Schema.Type.Contains(numberType) || s.Schema.Type.Contains(integerType)
144if num, ok := data.(json.Number); ok && isnumber {
145if s.Schema.Type.Contains(integerType) { // avoid lossy conversion
146in, erri := num.Int64()
147if erri != nil {
148result.AddErrors(invalidTypeConversionMsg(s.Path, erri))
149result.Inc()
150return result
151}
152d = in
153} else {
154nf, errf := num.Float64()
155if errf != nil {
156result.AddErrors(invalidTypeConversionMsg(s.Path, errf))
157result.Inc()
158return result
159}
160d = nf
161}
162
163tpe = reflect.TypeOf(d)
164kind = tpe.Kind()
165}
166
167for _, v := range s.validators {
168if !v.Applies(s.Schema, kind) {
169debugLog("%T does not apply for %v", v, kind)
170continue
171}
172
173err := v.Validate(d)
174result.Merge(err)
175result.Inc()
176}
177result.Inc()
178
179return result
180}
181
182func (s *SchemaValidator) typeValidator() valueValidator {
183return &typeValidator{Type: s.Schema.Type, Nullable: s.Schema.Nullable, Format: s.Schema.Format, In: s.in, Path: s.Path}
184}
185
186func (s *SchemaValidator) commonValidator() valueValidator {
187return &basicCommonValidator{
188Path: s.Path,
189In: s.in,
190Enum: s.Schema.Enum,
191}
192}
193
194func (s *SchemaValidator) sliceValidator() valueValidator {
195return &schemaSliceValidator{
196Path: s.Path,
197In: s.in,
198MaxItems: s.Schema.MaxItems,
199MinItems: s.Schema.MinItems,
200UniqueItems: s.Schema.UniqueItems,
201AdditionalItems: s.Schema.AdditionalItems,
202Items: s.Schema.Items,
203Root: s.Root,
204KnownFormats: s.KnownFormats,
205Options: s.Options,
206}
207}
208
209func (s *SchemaValidator) numberValidator() valueValidator {
210return &numberValidator{
211Path: s.Path,
212In: s.in,
213Default: s.Schema.Default,
214MultipleOf: s.Schema.MultipleOf,
215Maximum: s.Schema.Maximum,
216ExclusiveMaximum: s.Schema.ExclusiveMaximum,
217Minimum: s.Schema.Minimum,
218ExclusiveMinimum: s.Schema.ExclusiveMinimum,
219}
220}
221
222func (s *SchemaValidator) stringValidator() valueValidator {
223return &stringValidator{
224Path: s.Path,
225In: s.in,
226MaxLength: s.Schema.MaxLength,
227MinLength: s.Schema.MinLength,
228Pattern: s.Schema.Pattern,
229}
230}
231
232func (s *SchemaValidator) formatValidator() valueValidator {
233return &formatValidator{
234Path: s.Path,
235In: s.in,
236Format: s.Schema.Format,
237KnownFormats: s.KnownFormats,
238}
239}
240
241func (s *SchemaValidator) schemaPropsValidator() valueValidator {
242sch := s.Schema
243return newSchemaPropsValidator(s.Path, s.in, sch.AllOf, sch.OneOf, sch.AnyOf, sch.Not, sch.Dependencies, s.Root, s.KnownFormats, s.Options.Options()...)
244}
245
246func (s *SchemaValidator) objectValidator() valueValidator {
247return &objectValidator{
248Path: s.Path,
249In: s.in,
250MaxProperties: s.Schema.MaxProperties,
251MinProperties: s.Schema.MinProperties,
252Required: s.Schema.Required,
253Properties: s.Schema.Properties,
254AdditionalProperties: s.Schema.AdditionalProperties,
255PatternProperties: s.Schema.PatternProperties,
256Root: s.Root,
257KnownFormats: s.KnownFormats,
258Options: s.Options,
259}
260}
261