2
2
import logging
3
3
from collections import defaultdict
4
4
from datetime import date , datetime
5
+ import re
5
6
import warnings
6
7
7
8
from six import iteritems , integer_types , binary_type , text_type
@@ -61,7 +62,11 @@ def __init__(
61
62
self , schema_type = None , model = None , properties = None , items = None ,
62
63
schema_format = None , required = None , default = None , nullable = False ,
63
64
enum = None , deprecated = False , all_of = None , one_of = None ,
64
- additional_properties = None ):
65
+ additional_properties = None , min_items = None , max_items = None ,
66
+ min_length = None , max_length = None , pattern = None , unique_items = False ,
67
+ minimum = None , maximum = None , multiple_of = None ,
68
+ exclusive_minimum = False , exclusive_maximum = False ,
69
+ min_properties = None , max_properties = None ):
65
70
self .type = SchemaType (schema_type )
66
71
self .model = model
67
72
self .properties = properties and dict (properties ) or {}
@@ -75,6 +80,22 @@ def __init__(
75
80
self .all_of = all_of and list (all_of ) or []
76
81
self .one_of = one_of and list (one_of ) or []
77
82
self .additional_properties = additional_properties
83
+ self .min_items = int (min_items ) if min_items is not None else None
84
+ self .max_items = int (max_items ) if max_items is not None else None
85
+ self .min_length = int (min_length ) if min_length is not None else None
86
+ self .max_length = int (max_length ) if max_length is not None else None
87
+ self .pattern = pattern and re .compile (pattern ) or None
88
+ self .unique_items = unique_items
89
+ self .minimum = int (minimum ) if minimum is not None else None
90
+ self .maximum = int (maximum ) if maximum is not None else None
91
+ self .multiple_of = int (multiple_of )\
92
+ if multiple_of is not None else None
93
+ self .exclusive_minimum = exclusive_minimum
94
+ self .exclusive_maximum = exclusive_maximum
95
+ self .min_properties = int (min_properties )\
96
+ if min_properties is not None else None
97
+ self .max_properties = int (max_properties )\
98
+ if max_properties is not None else None
78
99
79
100
self ._all_required_properties_cache = None
80
101
self ._all_optional_properties_cache = None
@@ -288,6 +309,8 @@ def get_validator_mapping(self):
288
309
SchemaType .ARRAY : self ._validate_collection ,
289
310
SchemaType .STRING : self ._validate_string ,
290
311
SchemaType .OBJECT : self ._validate_object ,
312
+ SchemaType .INTEGER : self ._validate_number ,
313
+ SchemaType .NUMBER : self ._validate_number ,
291
314
}
292
315
293
316
return defaultdict (lambda : lambda x : x , mapping )
@@ -318,8 +341,66 @@ def _validate_collection(self, value):
318
341
if self .items is None :
319
342
raise OpenAPISchemaError ("Schema for collection not defined" )
320
343
344
+ if self .min_items is not None :
345
+ if self .min_items < 0 :
346
+ raise OpenAPISchemaError (
347
+ "Schema for collection invalid:"
348
+ " minItems must be non-negative"
349
+ )
350
+ if len (value ) < self .min_items :
351
+ raise InvalidSchemaValue (
352
+ "Value must contain at least {0} item(s),"
353
+ " {1} found" .format (
354
+ self .min_items , len (value ))
355
+ )
356
+ if self .max_items is not None :
357
+ if self .max_items < 0 :
358
+ raise OpenAPISchemaError (
359
+ "Schema for collection invalid:"
360
+ " maxItems must be non-negative"
361
+ )
362
+ if len (value ) > self .max_items :
363
+ raise InvalidSchemaValue (
364
+ "Value must contain at most {0} item(s),"
365
+ " {1} found" .format (
366
+ self .max_items , len (value ))
367
+ )
368
+ if self .unique_items and len (set (value )) != len (value ):
369
+ raise InvalidSchemaValue ("Value may not contain duplicate items" )
370
+
321
371
return list (map (self .items .validate , value ))
322
372
373
+ def _validate_number (self , value ):
374
+ if self .minimum is not None :
375
+ if self .exclusive_minimum and value <= self .minimum :
376
+ raise InvalidSchemaValue (
377
+ "Value {0} is not less than or equal to {1}" .format (
378
+ value , self .minimum )
379
+ )
380
+ elif value < self .minimum :
381
+ raise InvalidSchemaValue (
382
+ "Value {0} is not less than {1}" .format (
383
+ value , self .minimum )
384
+ )
385
+
386
+ if self .maximum is not None :
387
+ if self .exclusive_maximum and value >= self .maximum :
388
+ raise InvalidSchemaValue (
389
+ "Value {0} is not greater than or equal to {1}" .format (
390
+ value , self .maximum )
391
+ )
392
+ elif value > self .maximum :
393
+ raise InvalidSchemaValue (
394
+ "Value {0} is not greater than {1}" .format (
395
+ value , self .maximum )
396
+ )
397
+
398
+ if self .multiple_of is not None and value % self .multiple_of :
399
+ raise InvalidSchemaValue (
400
+ "Value {0} is not a multiple of {1}" .format (
401
+ value , self .multiple_of )
402
+ )
403
+
323
404
def _validate_string (self , value ):
324
405
try :
325
406
schema_format = SchemaFormat (self .format )
@@ -338,6 +419,34 @@ def _validate_string(self, value):
338
419
value , self .format )
339
420
)
340
421
422
+ if self .min_length is not None :
423
+ if self .min_length < 0 :
424
+ raise OpenAPISchemaError (
425
+ "Schema for string invalid:"
426
+ " minLength must be non-negative"
427
+ )
428
+ if len (value ) < self .min_length :
429
+ raise InvalidSchemaValue (
430
+ "Value is shorter than the minimum length of {0}" .format (
431
+ self .min_length )
432
+ )
433
+ if self .max_length is not None :
434
+ if self .max_length < 0 :
435
+ raise OpenAPISchemaError (
436
+ "Schema for string invalid:"
437
+ " maxLength must be non-negative"
438
+ )
439
+ if len (value ) > self .max_length :
440
+ raise InvalidSchemaValue (
441
+ "Value is longer than the maximum length of {0}" .format (
442
+ self .max_length )
443
+ )
444
+ if self .pattern is not None and not self .pattern .search (value ):
445
+ raise InvalidSchemaValue (
446
+ "Value {0} does not match the pattern {1}" .format (
447
+ value , self .pattern .pattern )
448
+ )
449
+
341
450
return True
342
451
343
452
def _validate_object (self , value ):
@@ -364,6 +473,33 @@ def _validate_object(self, value):
364
473
else :
365
474
self ._validate_properties (properties )
366
475
476
+ if self .min_properties is not None :
477
+ if self .min_properties < 0 :
478
+ raise OpenAPISchemaError (
479
+ "Schema for object invalid:"
480
+ " minProperties must be non-negative"
481
+ )
482
+
483
+ if len (properties ) < self .min_properties :
484
+ raise InvalidSchemaValue (
485
+ "Value must contain at least {0} properties,"
486
+ " {1} found" .format (
487
+ self .min_properties , len (properties ))
488
+ )
489
+
490
+ if self .max_properties is not None :
491
+ if self .max_properties < 0 :
492
+ raise OpenAPISchemaError (
493
+ "Schema for object invalid:"
494
+ " maxProperties must be non-negative"
495
+ )
496
+ if len (properties ) > self .max_properties :
497
+ raise InvalidSchemaValue (
498
+ "Value must contain at most {0} properties,"
499
+ " {1} found" .format (
500
+ self .max_properties , len (properties ))
501
+ )
502
+
367
503
return True
368
504
369
505
def _validate_properties (self , value , one_of_schema = None ):
0 commit comments