diff --git a/wsme/rest/json.py b/wsme/rest/json.py index 42a9559..9adfd6b 100644 --- a/wsme/rest/json.py +++ b/wsme/rest/json.py @@ -135,8 +135,11 @@ def fromjson(datatype, value): obj = datatype() for attrdef in wsme.types.list_attributes(datatype): if attrdef.name in value: - setattr(obj, attrdef.key, - fromjson(attrdef.datatype, value[attrdef.name])) + val_fromjson = fromjson(attrdef.datatype, value[attrdef.name]) + if getattr(attrdef, 'readonly', False): + raise InvalidInput(attrdef.name, val_fromjson, + "Cannot set read only field.") + setattr(obj, attrdef.key, val_fromjson) elif attrdef.mandatory: raise InvalidInput(attrdef.name, None, "Mandatory field missing.") diff --git a/wsme/rest/xml.py b/wsme/rest/xml.py index 9a836c3..286afa7 100644 --- a/wsme/rest/xml.py +++ b/wsme/rest/xml.py @@ -101,7 +101,11 @@ def fromxml(datatype, element): for attrdef in wsme.types.list_attributes(datatype): sub = element.find(attrdef.name) if sub is not None: - setattr(obj, attrdef.key, fromxml(attrdef.datatype, sub)) + val_fromxml = fromxml(attrdef.datatype, sub) + if getattr(attrdef, 'readonly', False): + raise InvalidInput(attrdef.name, val_fromxml, + "Cannot set read only field.") + setattr(obj, attrdef.key, val_fromxml) elif attrdef.mandatory: raise InvalidInput(attrdef.name, None, "Mandatory field missing.") diff --git a/wsme/tests/test_api.py b/wsme/tests/test_api.py index db85c51..9149cf9 100644 --- a/wsme/tests/test_api.py +++ b/wsme/tests/test_api.py @@ -287,6 +287,25 @@ Value should be one of:")) headers={'Accept': 'application/json'}) self.assertEqual(res.status_int, 400) + def test_wsattr_readonly(self): + class ComplexType(object): + attr = wsme.types.wsattr(int, readonly=True) + + class MyRoot(WSRoot): + @expose(int, body=ComplexType) + @validate(ComplexType) + def clx(self, a): + return a.attr + + r = MyRoot(['restjson']) + app = webtest.TestApp(r.wsgiapp()) + res = app.post_json('/clx', params={'attr': 1005}, expect_errors=True, + headers={'Accept': 'application/json'}) + self.assertIn('Cannot set read only field.', + res.json_body['faultstring']) + self.assertIn('1005', res.json_body['faultstring']) + self.assertEqual(res.status_int, 400) + def test_wsattr_default(self): class ComplexType(object): attr = wsme.types.wsattr(wsme.types.Enum(str, 'or', 'and'), diff --git a/wsme/types.py b/wsme/types.py index 7adef83..9701a61 100644 --- a/wsme/types.py +++ b/wsme/types.py @@ -447,7 +447,8 @@ class wsattr(object): mandatoryvalue = wsattr(int, mandatory=True) """ - def __init__(self, datatype, mandatory=False, name=None, default=Unset): + def __init__(self, datatype, mandatory=False, name=None, default=Unset, + readonly=False): #: The attribute name in the parent python class. #: Set by :func:`inspect_class` self.key = None # will be set by class inspection @@ -460,6 +461,8 @@ class wsattr(object): #: Default value. The attribute will return this instead #: of :data:`Unset` if no value has been set. self.default = default + #: If True value cannot be set from json/xml input data + self.readonly = readonly self.complextype = None