1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package validate
16
17 import (
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
27
28 type SchemaValidator struct {
29 Path string
30 in string
31 Schema *spec.Schema
32 validators [8]valueValidator
33 Root interface{}
34 KnownFormats strfmt.Registry
35 Options *SchemaValidatorOptions
36 }
37
38
39
40
41 func AgainstSchema(schema *spec.Schema, data interface{}, formats strfmt.Registry, options ...Option) error {
42 res := NewSchemaValidator(schema, nil, "", formats,
43 append(options, WithRecycleValidators(true), withRecycleResults(true))...,
44 ).Validate(data)
45 defer func() {
46 pools.poolOfResults.RedeemResult(res)
47 }()
48
49 if res.HasErrors() {
50 return errors.CompositeValidationError(res.Errors...)
51 }
52
53 return nil
54 }
55
56
57
58
59 func NewSchemaValidator(schema *spec.Schema, rootSchema interface{}, root string, formats strfmt.Registry, options ...Option) *SchemaValidator {
60 opts := new(SchemaValidatorOptions)
61 for _, o := range options {
62 o(opts)
63 }
64
65 return newSchemaValidator(schema, rootSchema, root, formats, opts)
66 }
67
68 func newSchemaValidator(schema *spec.Schema, rootSchema interface{}, root string, formats strfmt.Registry, opts *SchemaValidatorOptions) *SchemaValidator {
69 if schema == nil {
70 return nil
71 }
72
73 if rootSchema == nil {
74 rootSchema = schema
75 }
76
77 if schema.ID != "" || schema.Ref.String() != "" || schema.Ref.IsRoot() {
78 err := spec.ExpandSchema(schema, rootSchema, nil)
79 if err != nil {
80 msg := invalidSchemaProvidedMsg(err).Error()
81 panic(msg)
82 }
83 }
84
85 if opts == nil {
86 opts = new(SchemaValidatorOptions)
87 }
88
89 var s *SchemaValidator
90 if opts.recycleValidators {
91 s = pools.poolOfSchemaValidators.BorrowValidator()
92 } else {
93 s = new(SchemaValidator)
94 }
95
96 s.Path = root
97 s.in = "body"
98 s.Schema = schema
99 s.Root = rootSchema
100 s.Options = opts
101 s.KnownFormats = formats
102
103 s.validators = [8]valueValidator{
104 s.typeValidator(),
105 s.schemaPropsValidator(),
106 s.stringValidator(),
107 s.formatValidator(),
108 s.numberValidator(),
109 s.sliceValidator(),
110 s.commonValidator(),
111 s.objectValidator(),
112 }
113
114 return s
115 }
116
117
118 func (s *SchemaValidator) SetPath(path string) {
119 s.Path = path
120 }
121
122
123 func (s *SchemaValidator) Applies(source interface{}, _ reflect.Kind) bool {
124 _, ok := source.(*spec.Schema)
125 return ok
126 }
127
128
129 func (s *SchemaValidator) Validate(data interface{}) *Result {
130 if s == nil {
131 return emptyResult
132 }
133
134 if s.Options.recycleValidators {
135 defer func() {
136 s.redeemChildren()
137 s.redeem()
138 }()
139 }
140
141 var result *Result
142 if s.Options.recycleResult {
143 result = pools.poolOfResults.BorrowResult()
144 result.data = data
145 } else {
146 result = &Result{data: data}
147 }
148
149 if s.Schema != nil && !s.Options.skipSchemataResult {
150 result.addRootObjectSchemata(s.Schema)
151 }
152
153 if data == nil {
154
155 result.Merge(s.validators[0].Validate(data))
156 result.Merge(s.validators[6].Validate(data))
157
158 if s.Options.recycleValidators {
159 s.validators[0] = nil
160 s.validators[6] = nil
161 }
162
163 return result
164 }
165
166 tpe := reflect.TypeOf(data)
167 kind := tpe.Kind()
168 for kind == reflect.Ptr {
169 tpe = tpe.Elem()
170 kind = tpe.Kind()
171 }
172 d := data
173
174 if kind == reflect.Struct {
175
176
177
178
179 d = swag.ToDynamicJSON(data)
180 }
181
182
183
184 isnumber := s.Schema.Type.Contains(numberType) || s.Schema.Type.Contains(integerType)
185 if num, ok := data.(json.Number); ok && isnumber {
186 if s.Schema.Type.Contains(integerType) {
187 in, erri := num.Int64()
188 if erri != nil {
189 result.AddErrors(invalidTypeConversionMsg(s.Path, erri))
190 result.Inc()
191
192 return result
193 }
194 d = in
195 } else {
196 nf, errf := num.Float64()
197 if errf != nil {
198 result.AddErrors(invalidTypeConversionMsg(s.Path, errf))
199 result.Inc()
200
201 return result
202 }
203 d = nf
204 }
205
206 tpe = reflect.TypeOf(d)
207 kind = tpe.Kind()
208 }
209
210 for idx, v := range s.validators {
211 if !v.Applies(s.Schema, kind) {
212 if s.Options.recycleValidators {
213
214 if redeemableChildren, ok := v.(interface{ redeemChildren() }); ok {
215 redeemableChildren.redeemChildren()
216 }
217 if redeemable, ok := v.(interface{ redeem() }); ok {
218 redeemable.redeem()
219 }
220 s.validators[idx] = nil
221 }
222
223 continue
224 }
225
226 result.Merge(v.Validate(d))
227 if s.Options.recycleValidators {
228 s.validators[idx] = nil
229 }
230 result.Inc()
231 }
232 result.Inc()
233
234 return result
235 }
236
237 func (s *SchemaValidator) typeValidator() valueValidator {
238 return newTypeValidator(
239 s.Path,
240 s.in,
241 s.Schema.Type,
242 s.Schema.Nullable,
243 s.Schema.Format,
244 s.Options,
245 )
246 }
247
248 func (s *SchemaValidator) commonValidator() valueValidator {
249 return newBasicCommonValidator(
250 s.Path,
251 s.in,
252 s.Schema.Default,
253 s.Schema.Enum,
254 s.Options,
255 )
256 }
257
258 func (s *SchemaValidator) sliceValidator() valueValidator {
259 return newSliceValidator(
260 s.Path,
261 s.in,
262 s.Schema.MaxItems,
263 s.Schema.MinItems,
264 s.Schema.UniqueItems,
265 s.Schema.AdditionalItems,
266 s.Schema.Items,
267 s.Root,
268 s.KnownFormats,
269 s.Options,
270 )
271 }
272
273 func (s *SchemaValidator) numberValidator() valueValidator {
274 return newNumberValidator(
275 s.Path,
276 s.in,
277 s.Schema.Default,
278 s.Schema.MultipleOf,
279 s.Schema.Maximum,
280 s.Schema.ExclusiveMaximum,
281 s.Schema.Minimum,
282 s.Schema.ExclusiveMinimum,
283 "",
284 "",
285 s.Options,
286 )
287 }
288
289 func (s *SchemaValidator) stringValidator() valueValidator {
290 return newStringValidator(
291 s.Path,
292 s.in,
293 nil,
294 false,
295 false,
296 s.Schema.MaxLength,
297 s.Schema.MinLength,
298 s.Schema.Pattern,
299 s.Options,
300 )
301 }
302
303 func (s *SchemaValidator) formatValidator() valueValidator {
304 return newFormatValidator(
305 s.Path,
306 s.in,
307 s.Schema.Format,
308 s.KnownFormats,
309 s.Options,
310 )
311 }
312
313 func (s *SchemaValidator) schemaPropsValidator() valueValidator {
314 sch := s.Schema
315 return newSchemaPropsValidator(
316 s.Path, s.in, sch.AllOf, sch.OneOf, sch.AnyOf, sch.Not, sch.Dependencies, s.Root, s.KnownFormats,
317 s.Options,
318 )
319 }
320
321 func (s *SchemaValidator) objectValidator() valueValidator {
322 return newObjectValidator(
323 s.Path,
324 s.in,
325 s.Schema.MaxProperties,
326 s.Schema.MinProperties,
327 s.Schema.Required,
328 s.Schema.Properties,
329 s.Schema.AdditionalProperties,
330 s.Schema.PatternProperties,
331 s.Root,
332 s.KnownFormats,
333 s.Options,
334 )
335 }
336
337 func (s *SchemaValidator) redeem() {
338 pools.poolOfSchemaValidators.RedeemValidator(s)
339 }
340
341 func (s *SchemaValidator) redeemChildren() {
342 for i, validator := range s.validators {
343 if validator == nil {
344 continue
345 }
346 if redeemableChildren, ok := validator.(interface{ redeemChildren() }); ok {
347 redeemableChildren.redeemChildren()
348 }
349 if redeemable, ok := validator.(interface{ redeem() }); ok {
350 redeemable.redeem()
351 }
352 s.validators[i] = nil
353 }
354 }
355
View as plain text