22import logging
33from collections import defaultdict
44from datetime import date , datetime
5+ import re
56import warnings
67
78from six import iteritems , integer_types , binary_type , text_type
@@ -61,7 +62,11 @@ def __init__(
6162 self , schema_type = None , model = None , properties = None , items = None ,
6263 schema_format = None , required = None , default = None , nullable = False ,
6364 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 ):
6570 self .type = SchemaType (schema_type )
6671 self .model = model
6772 self .properties = properties and dict (properties ) or {}
@@ -75,6 +80,22 @@ def __init__(
7580 self .all_of = all_of and list (all_of ) or []
7681 self .one_of = one_of and list (one_of ) or []
7782 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
7899
79100 self ._all_required_properties_cache = None
80101 self ._all_optional_properties_cache = None
@@ -288,6 +309,8 @@ def get_validator_mapping(self):
288309 SchemaType .ARRAY : self ._validate_collection ,
289310 SchemaType .STRING : self ._validate_string ,
290311 SchemaType .OBJECT : self ._validate_object ,
312+ SchemaType .INTEGER : self ._validate_number ,
313+ SchemaType .NUMBER : self ._validate_number ,
291314 }
292315
293316 return defaultdict (lambda : lambda x : x , mapping )
@@ -318,8 +341,66 @@ def _validate_collection(self, value):
318341 if self .items is None :
319342 raise OpenAPISchemaError ("Schema for collection not defined" )
320343
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+
321371 return list (map (self .items .validate , value ))
322372
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+
323404 def _validate_string (self , value ):
324405 try :
325406 schema_format = SchemaFormat (self .format )
@@ -338,6 +419,34 @@ def _validate_string(self, value):
338419 value , self .format )
339420 )
340421
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+
341450 return True
342451
343452 def _validate_object (self , value ):
@@ -364,6 +473,33 @@ def _validate_object(self, value):
364473 else :
365474 self ._validate_properties (properties )
366475
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+
367503 return True
368504
369505 def _validate_properties (self , value , one_of_schema = None ):
0 commit comments