99from openapi_core .schema .schemas .enums import SchemaType , SchemaFormat
1010from openapi_core .schema .schemas .exceptions import (
1111 InvalidSchemaValue , UndefinedSchemaProperty , MissingSchemaProperty ,
12+ OpenAPISchemaError , NoOneOfSchema , MultipleOneOfSchema ,
1213)
1314from openapi_core .schema .schemas .util import forcebool
1415
@@ -27,7 +28,7 @@ class Schema(object):
2728 def __init__ (
2829 self , schema_type = None , model = None , properties = None , items = None ,
2930 schema_format = None , required = None , default = None , nullable = False ,
30- enum = None , deprecated = False , all_of = None ):
31+ enum = None , deprecated = False , all_of = None , one_of = None ):
3132 self .type = schema_type and SchemaType (schema_type )
3233 self .model = model
3334 self .properties = properties and dict (properties ) or {}
@@ -39,6 +40,10 @@ def __init__(
3940 self .enum = enum
4041 self .deprecated = deprecated
4142 self .all_of = all_of and list (all_of ) or []
43+ self .one_of = one_of and list (one_of ) or []
44+
45+ self ._all_required_properties_cache = None
46+ self ._all_optional_properties_cache = None
4247
4348 def __getitem__ (self , name ):
4449 return self .properties [name ]
@@ -52,14 +57,35 @@ def get_all_properties(self):
5257
5358 return properties
5459
60+ def get_all_properties_names (self ):
61+ all_properties = self .get_all_properties ()
62+ return set (all_properties .keys ())
63+
5564 def get_all_required_properties (self ):
65+ if self ._all_required_properties_cache is None :
66+ self ._all_required_properties_cache = \
67+ self ._get_all_required_properties ()
68+
69+ return self ._all_required_properties_cache
70+
71+ def _get_all_required_properties (self ):
72+ all_properties = self .get_all_properties ()
73+ required = self .get_all_required_properties_names ()
74+
75+ return dict (
76+ (prop_name , val )
77+ for prop_name , val in all_properties .items ()
78+ if prop_name in required
79+ )
80+
81+ def get_all_required_properties_names (self ):
5682 required = self .required .copy ()
5783
5884 for subschema in self .all_of :
5985 subschema_req = subschema .get_all_required_properties ()
6086 required += subschema_req
6187
62- return required
88+ return set ( required )
6389
6490 def get_cast_mapping (self ):
6591 mapping = self .DEFAULT_CAST_CALLABLE_GETTER .copy ()
@@ -119,27 +145,58 @@ def _unmarshal_object(self, value):
119145 raise InvalidSchemaValue (
120146 "Value of {0} not an object" .format (value ))
121147
122- all_properties = self .get_all_properties ()
123- all_required_properties = self .get_all_required_properties ()
124- all_properties_keys = all_properties .keys ()
125- value_keys = value .keys ()
148+ if self .one_of :
149+ properties = None
150+ for one_of_schema in self .one_of :
151+ try :
152+ found_props = self ._unmarshal_properties (
153+ value , one_of_schema )
154+ except OpenAPISchemaError :
155+ pass
156+ else :
157+ if properties is not None :
158+ raise MultipleOneOfSchema (
159+ "Exactly one schema should be valid,"
160+ "multiple found" )
161+ properties = found_props
162+
163+ if properties is None :
164+ raise NoOneOfSchema (
165+ "Exactly one valid schema should be valid, None found." )
166+
167+ else :
168+ properties = self ._unmarshal_properties (value )
169+
170+ return ModelFactory ().create (properties , name = self .model )
126171
127- extra_props = set (value_keys ) - set (all_properties_keys )
172+ def _unmarshal_properties (self , value , one_of_schema = None ):
173+ all_props = self .get_all_properties ()
174+ all_props_names = self .get_all_properties_names ()
175+ all_req_props_names = self .get_all_required_properties_names ()
128176
177+ if one_of_schema is not None :
178+ all_props .update (one_of_schema .get_all_properties ())
179+ all_props_names |= one_of_schema .\
180+ get_all_properties_names ()
181+ all_req_props_names |= one_of_schema .\
182+ get_all_required_properties_names ()
183+
184+ value_props_names = value .keys ()
185+ extra_props = set (value_props_names ) - set (all_props_names )
129186 if extra_props :
130187 raise UndefinedSchemaProperty (
131188 "Undefined properties in schema: {0}" .format (extra_props ))
132189
133190 properties = {}
134- for prop_name , prop in iteritems (all_properties ):
191+ for prop_name , prop in iteritems (all_props ):
135192 try :
136193 prop_value = value [prop_name ]
137194 except KeyError :
138- if prop_name in all_required_properties :
195+ if prop_name in all_req_props_names :
139196 raise MissingSchemaProperty (
140197 "Missing schema property {0}" .format (prop_name ))
141198 if not prop .nullable and not prop .default :
142199 continue
143200 prop_value = prop .default
144201 properties [prop_name ] = prop .unmarshal (prop_value )
145- return ModelFactory (). create ( properties , name = self . model )
202+ return properties
0 commit comments