Skip to content

Commit f97307d

Browse files
committed
Implementing readOnly and writeOnly on all methods that unmarshal or validate properties
1 parent 8dc2d7e commit f97307d

File tree

2 files changed

+40
-8
lines changed

2 files changed

+40
-8
lines changed

openapi_core/schema/media_types/models.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
from openapi_core.schema.media_types.exceptions import InvalidMediaTypeValue
77
from openapi_core.schema.schemas.exceptions import OpenAPISchemaError
8-
8+
from openapi_core.schema.schemas.util import requires_context
99

1010
MEDIA_TYPE_DESERIALIZERS = {
1111
'application/json': loads,

openapi_core/schema/schemas/models.py

Lines changed: 39 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -325,8 +325,11 @@ def _unmarshal_object(self, value, model_factory=None,
325325

326326
return model_factory.create(properties, name=self.model)
327327

328+
@requires_context
328329
def _unmarshal_properties(self, value, one_of_schema=None,
329-
custom_formatters=None, strict=True):
330+
custom_formatters=None, strict=True,
331+
context=None):
332+
330333
all_props = self.get_all_properties()
331334
all_props_names = self.get_all_properties_names()
332335
all_req_props_names = self.get_all_required_properties_names()
@@ -338,6 +341,21 @@ def _unmarshal_properties(self, value, one_of_schema=None,
338341
all_req_props_names |= one_of_schema.\
339342
get_all_required_properties_names()
340343

344+
if context is UnmarshalContext.REQUEST:
345+
forbidden_prop_names = {prop for prop in all_props_names
346+
if all_props[prop].read_only}
347+
else: # Response
348+
forbidden_prop_names = {prop for prop in all_props_names
349+
if all_props[prop].write_only}
350+
all_req_props_names = all_req_props_names - forbidden_prop_names
351+
for forbidden_prop in forbidden_prop_names:
352+
if forbidden_prop in value:
353+
raise InvalidSchemaProperty(
354+
forbidden_prop,
355+
"Field {} is not allowed on a {} context".format(
356+
forbidden_prop, context.value)
357+
)
358+
341359
value_props_names = value.keys()
342360
extra_props = set(value_props_names) - set(all_props_names)
343361
extra_props_allowed = self.are_additional_properties_allowed(
@@ -350,7 +368,8 @@ def _unmarshal_properties(self, value, one_of_schema=None,
350368
for prop_name in extra_props:
351369
prop_value = value[prop_name]
352370
properties[prop_name] = self.additional_properties.unmarshal(
353-
prop_value, custom_formatters=custom_formatters)
371+
prop_value, custom_formatters=custom_formatters,
372+
context=context)
354373

355374
for prop_name, prop in iteritems(all_props):
356375
try:
@@ -363,12 +382,14 @@ def _unmarshal_properties(self, value, one_of_schema=None,
363382
prop_value = prop.default
364383
try:
365384
properties[prop_name] = prop.unmarshal(
366-
prop_value, custom_formatters=custom_formatters)
385+
prop_value, custom_formatters=custom_formatters,
386+
context=context)
367387
except OpenAPISchemaError as exc:
368388
raise InvalidSchemaProperty(prop_name, exc)
369389

370390
self._validate_properties(properties, one_of_schema=one_of_schema,
371-
custom_formatters=custom_formatters)
391+
custom_formatters=custom_formatters,
392+
context=context)
372393

373394
return properties
374395

@@ -559,8 +580,9 @@ def _validate_object(self, value, custom_formatters=None):
559580

560581
return True
561582

583+
@requires_context
562584
def _validate_properties(self, value, one_of_schema=None,
563-
custom_formatters=None):
585+
custom_formatters=None, context=None):
564586
all_props = self.get_all_properties()
565587
all_props_names = self.get_all_properties_names()
566588
all_req_props_names = self.get_all_required_properties_names()
@@ -572,6 +594,14 @@ def _validate_properties(self, value, one_of_schema=None,
572594
all_req_props_names |= one_of_schema.\
573595
get_all_required_properties_names()
574596

597+
if context is UnmarshalContext.REQUEST:
598+
forbidden_prop_names = {prop for prop in all_props_names
599+
if all_props[prop].read_only}
600+
else: # Response
601+
forbidden_prop_names = {prop for prop in all_props_names
602+
if all_props[prop].write_only}
603+
all_req_props_names = all_req_props_names - forbidden_prop_names
604+
575605
value_props_names = value.keys()
576606
extra_props = set(value_props_names) - set(all_props_names)
577607
extra_props_allowed = self.are_additional_properties_allowed(
@@ -583,7 +613,8 @@ def _validate_properties(self, value, one_of_schema=None,
583613
for prop_name in extra_props:
584614
prop_value = value[prop_name]
585615
self.additional_properties.validate(
586-
prop_value, custom_formatters=custom_formatters)
616+
prop_value, custom_formatters=custom_formatters,
617+
context=context)
587618

588619
for prop_name, prop in iteritems(all_props):
589620
try:
@@ -595,7 +626,8 @@ def _validate_properties(self, value, one_of_schema=None,
595626
continue
596627
prop_value = prop.default
597628
try:
598-
prop.validate(prop_value, custom_formatters=custom_formatters)
629+
prop.validate(prop_value, custom_formatters=custom_formatters,
630+
context=context)
599631
except OpenAPISchemaError as exc:
600632
raise InvalidSchemaProperty(prop_name, original_exception=exc)
601633

0 commit comments

Comments
 (0)