blob: 24606da8d5afdaa4edb79369f91d05c1bcfb26b8 [file] [log] [blame]
Serge Bazanskicc25bdf2018-10-25 14:02:58 +02001// 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 "fmt"
19 "reflect"
20 "unicode/utf8"
21
22 "github.com/go-openapi/errors"
23 "github.com/go-openapi/strfmt"
24 "github.com/go-openapi/swag"
25)
26
27// Enum validates if the data is a member of the enum
28func Enum(path, in string, data interface{}, enum interface{}) *errors.Validation {
29 val := reflect.ValueOf(enum)
30 if val.Kind() != reflect.Slice {
31 return nil
32 }
33
34 var values []interface{}
35 for i := 0; i < val.Len(); i++ {
36 ele := val.Index(i)
37 enumValue := ele.Interface()
38 if data != nil {
39 if reflect.DeepEqual(data, enumValue) {
40 return nil
41 }
42 actualType := reflect.TypeOf(enumValue)
43 if actualType == nil { // Safeguard. Frankly, I don't know how we may get a nil
44 continue
45 }
46 expectedValue := reflect.ValueOf(data)
47 if expectedValue.IsValid() && expectedValue.Type().ConvertibleTo(actualType) {
48 // Attempt comparison after type conversion
49 if reflect.DeepEqual(expectedValue.Convert(actualType).Interface(), enumValue) {
50 return nil
51 }
52 }
53 }
54 values = append(values, enumValue)
55 }
56 return errors.EnumFail(path, in, data, values)
57}
58
59// MinItems validates that there are at least n items in a slice
60func MinItems(path, in string, size, min int64) *errors.Validation {
61 if size < min {
62 return errors.TooFewItems(path, in, min)
63 }
64 return nil
65}
66
67// MaxItems validates that there are at most n items in a slice
68func MaxItems(path, in string, size, max int64) *errors.Validation {
69 if size > max {
70 return errors.TooManyItems(path, in, max)
71 }
72 return nil
73}
74
75// UniqueItems validates that the provided slice has unique elements
76func UniqueItems(path, in string, data interface{}) *errors.Validation {
77 val := reflect.ValueOf(data)
78 if val.Kind() != reflect.Slice {
79 return nil
80 }
81 var unique []interface{}
82 for i := 0; i < val.Len(); i++ {
83 v := val.Index(i).Interface()
84 for _, u := range unique {
85 if reflect.DeepEqual(v, u) {
86 return errors.DuplicateItems(path, in)
87 }
88 }
89 unique = append(unique, v)
90 }
91 return nil
92}
93
94// MinLength validates a string for minimum length
95func MinLength(path, in, data string, minLength int64) *errors.Validation {
96 strLen := int64(utf8.RuneCount([]byte(data)))
97 if strLen < minLength {
98 return errors.TooShort(path, in, minLength)
99 }
100 return nil
101}
102
103// MaxLength validates a string for maximum length
104func MaxLength(path, in, data string, maxLength int64) *errors.Validation {
105 strLen := int64(utf8.RuneCount([]byte(data)))
106 if strLen > maxLength {
107 return errors.TooLong(path, in, maxLength)
108 }
109 return nil
110}
111
112// Required validates an interface for requiredness
113func Required(path, in string, data interface{}) *errors.Validation {
114 val := reflect.ValueOf(data)
115 if val.IsValid() {
116 if reflect.DeepEqual(reflect.Zero(val.Type()).Interface(), val.Interface()) {
117 return errors.Required(path, in)
118 }
119 return nil
120 }
121 return errors.Required(path, in)
122}
123
124// RequiredString validates a string for requiredness
125func RequiredString(path, in, data string) *errors.Validation {
126 if data == "" {
127 return errors.Required(path, in)
128 }
129 return nil
130}
131
132// RequiredNumber validates a number for requiredness
133func RequiredNumber(path, in string, data float64) *errors.Validation {
134 if data == 0 {
135 return errors.Required(path, in)
136 }
137 return nil
138}
139
140// Pattern validates a string against a regular expression
141func Pattern(path, in, data, pattern string) *errors.Validation {
142 re, err := compileRegexp(pattern)
143 if err != nil {
144 return errors.FailedPattern(path, in, fmt.Sprintf("%s, but pattern is invalid: %s", pattern, err.Error()))
145 }
146 if !re.MatchString(data) {
147 return errors.FailedPattern(path, in, pattern)
148 }
149 return nil
150}
151
152// MaximumInt validates if a number is smaller than a given maximum
153func MaximumInt(path, in string, data, max int64, exclusive bool) *errors.Validation {
154 if (!exclusive && data > max) || (exclusive && data >= max) {
155 return errors.ExceedsMaximumInt(path, in, max, exclusive)
156 }
157 return nil
158}
159
160// MaximumUint validates if a number is smaller than a given maximum
161func MaximumUint(path, in string, data, max uint64, exclusive bool) *errors.Validation {
162 if (!exclusive && data > max) || (exclusive && data >= max) {
163 return errors.ExceedsMaximumUint(path, in, max, exclusive)
164 }
165 return nil
166}
167
168// Maximum validates if a number is smaller than a given maximum
169func Maximum(path, in string, data, max float64, exclusive bool) *errors.Validation {
170 if (!exclusive && data > max) || (exclusive && data >= max) {
171 return errors.ExceedsMaximum(path, in, max, exclusive)
172 }
173 return nil
174}
175
176// Minimum validates if a number is smaller than a given minimum
177func Minimum(path, in string, data, min float64, exclusive bool) *errors.Validation {
178 if (!exclusive && data < min) || (exclusive && data <= min) {
179 return errors.ExceedsMinimum(path, in, min, exclusive)
180 }
181 return nil
182}
183
184// MinimumInt validates if a number is smaller than a given minimum
185func MinimumInt(path, in string, data, min int64, exclusive bool) *errors.Validation {
186 if (!exclusive && data < min) || (exclusive && data <= min) {
187 return errors.ExceedsMinimumInt(path, in, min, exclusive)
188 }
189 return nil
190}
191
192// MinimumUint validates if a number is smaller than a given minimum
193func MinimumUint(path, in string, data, min uint64, exclusive bool) *errors.Validation {
194 if (!exclusive && data < min) || (exclusive && data <= min) {
195 return errors.ExceedsMinimumUint(path, in, min, exclusive)
196 }
197 return nil
198}
199
200// MultipleOf validates if the provided number is a multiple of the factor
201func MultipleOf(path, in string, data, factor float64) *errors.Validation {
202 // multipleOf factor must be positive
203 if factor < 0 {
204 return errors.MultipleOfMustBePositive(path, in, factor)
205 }
206 var mult float64
207 if factor < 1 {
208 mult = 1 / factor * data
209 } else {
210 mult = data / factor
211 }
212 if !swag.IsFloat64AJSONInteger(mult) {
213 return errors.NotMultipleOf(path, in, factor)
214 }
215 return nil
216}
217
218// MultipleOfInt validates if the provided integer is a multiple of the factor
219func MultipleOfInt(path, in string, data int64, factor int64) *errors.Validation {
220 // multipleOf factor must be positive
221 if factor < 0 {
222 return errors.MultipleOfMustBePositive(path, in, factor)
223 }
224 mult := data / factor
225 if mult*factor != data {
226 return errors.NotMultipleOf(path, in, factor)
227 }
228 return nil
229}
230
231// MultipleOfUint validates if the provided unsigned integer is a multiple of the factor
232func MultipleOfUint(path, in string, data, factor uint64) *errors.Validation {
233 mult := data / factor
234 if mult*factor != data {
235 return errors.NotMultipleOf(path, in, factor)
236 }
237 return nil
238}
239
240// FormatOf validates if a string matches a format in the format registry
241func FormatOf(path, in, format, data string, registry strfmt.Registry) *errors.Validation {
242 if registry == nil {
243 registry = strfmt.Default
244 }
245 if ok := registry.ContainsName(format); !ok {
246 return errors.InvalidTypeName(format)
247 }
248 if ok := registry.Validates(format, data); !ok {
249 return errors.InvalidType(path, in, format, data)
250 }
251 return nil
252}
253
254// MaximumNativeType provides native type constraint validation as a facade
255// to various numeric types versions of Maximum constraint check.
256//
257// Assumes that any possible loss conversion during conversion has been
258// checked beforehand.
259//
260// NOTE: currently, the max value is marshalled as a float64, no matter what,
261// which means there may be a loss during conversions (e.g. for very large integers)
262//
263// TODO: Normally, a JSON MAX_SAFE_INTEGER check would ensure conversion remains loss-free
264func MaximumNativeType(path, in string, val interface{}, max float64, exclusive bool) *errors.Validation {
265 kind := reflect.ValueOf(val).Type().Kind()
266 switch kind {
267 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
268 value := valueHelp.asInt64(val)
269 return MaximumInt(path, in, value, int64(max), exclusive)
270 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
271 value := valueHelp.asUint64(val)
272 if max < 0 {
273 return errors.ExceedsMaximum(path, in, max, exclusive)
274 }
275 return MaximumUint(path, in, value, uint64(max), exclusive)
276 case reflect.Float32, reflect.Float64:
277 fallthrough
278 default:
279 value := valueHelp.asFloat64(val)
280 return Maximum(path, in, value, max, exclusive)
281 }
282}
283
284// MinimumNativeType provides native type constraint validation as a facade
285// to various numeric types versions of Minimum constraint check.
286//
287// Assumes that any possible loss conversion during conversion has been
288// checked beforehand.
289//
290// NOTE: currently, the min value is marshalled as a float64, no matter what,
291// which means there may be a loss during conversions (e.g. for very large integers)
292//
293// TODO: Normally, a JSON MAX_SAFE_INTEGER check would ensure conversion remains loss-free
294func MinimumNativeType(path, in string, val interface{}, min float64, exclusive bool) *errors.Validation {
295 kind := reflect.ValueOf(val).Type().Kind()
296 switch kind {
297 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
298 value := valueHelp.asInt64(val)
299 return MinimumInt(path, in, value, int64(min), exclusive)
300 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
301 value := valueHelp.asUint64(val)
302 if min < 0 {
303 return nil
304 }
305 return MinimumUint(path, in, value, uint64(min), exclusive)
306 case reflect.Float32, reflect.Float64:
307 fallthrough
308 default:
309 value := valueHelp.asFloat64(val)
310 return Minimum(path, in, value, min, exclusive)
311 }
312}
313
314// MultipleOfNativeType provides native type constraint validation as a facade
315// to various numeric types version of MultipleOf constraint check.
316//
317// Assumes that any possible loss conversion during conversion has been
318// checked beforehand.
319//
320// NOTE: currently, the multipleOf factor is marshalled as a float64, no matter what,
321// which means there may be a loss during conversions (e.g. for very large integers)
322//
323// TODO: Normally, a JSON MAX_SAFE_INTEGER check would ensure conversion remains loss-free
324func MultipleOfNativeType(path, in string, val interface{}, multipleOf float64) *errors.Validation {
325 kind := reflect.ValueOf(val).Type().Kind()
326 switch kind {
327 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
328 value := valueHelp.asInt64(val)
329 return MultipleOfInt(path, in, value, int64(multipleOf))
330 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
331 value := valueHelp.asUint64(val)
332 return MultipleOfUint(path, in, value, uint64(multipleOf))
333 case reflect.Float32, reflect.Float64:
334 fallthrough
335 default:
336 value := valueHelp.asFloat64(val)
337 return MultipleOf(path, in, value, multipleOf)
338 }
339}
340
341// IsValueValidAgainstRange checks that a numeric value is compatible with
342// the range defined by Type and Format, that is, may be converted without loss.
343//
344// NOTE: this check is about type capacity and not formal verification such as: 1.0 != 1L
345func IsValueValidAgainstRange(val interface{}, typeName, format, prefix, path string) error {
346 kind := reflect.ValueOf(val).Type().Kind()
347
348 // What is the string representation of val
349 stringRep := ""
350 switch kind {
351 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
352 stringRep = swag.FormatUint64(valueHelp.asUint64(val))
353 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
354 stringRep = swag.FormatInt64(valueHelp.asInt64(val))
355 case reflect.Float32, reflect.Float64:
356 stringRep = swag.FormatFloat64(valueHelp.asFloat64(val))
357 default:
358 return fmt.Errorf("%s value number range checking called with invalid (non numeric) val type in %s", prefix, path)
359 }
360
361 var errVal error
362
363 switch typeName {
364 case "integer":
365 switch format {
366 case "int32":
367 _, errVal = swag.ConvertInt32(stringRep)
368 case "uint32":
369 _, errVal = swag.ConvertUint32(stringRep)
370 case "uint64":
371 _, errVal = swag.ConvertUint64(stringRep)
372 case "int64":
373 fallthrough
374 default:
375 _, errVal = swag.ConvertInt64(stringRep)
376 }
377 case "number":
378 fallthrough
379 default:
380 switch format {
381 case "float", "float32":
382 _, errVal = swag.ConvertFloat32(stringRep)
383 case "double", "float64":
384 fallthrough
385 default:
386 // No check can be performed here since
387 // no number beyond float64 is supported
388 }
389 }
390 if errVal != nil { // We don't report the actual errVal from strconv
391 if format != "" {
392 errVal = fmt.Errorf("%s value must be of type %s with format %s in %s", prefix, typeName, format, path)
393 } else {
394 errVal = fmt.Errorf("%s value must be of type %s (default format) in %s", prefix, typeName, path)
395 }
396 }
397 return errVal
398}