Skip to content

Commit b3788d2

Browse files
author
Kiptoo Magutt
committed
catch validation errors during patch serialization
- we want to force triggering of validation exceptions during serialiation - add unit test to verify that validation exception is raised on patch - a validation exception should be raised when a validation error occurs during serialization - create a serializer class that automatically throws the validation exception - uses marshmallow's ValidationError exception, but in theory any exception could be used to force the check in order to pass them through to the flask-restless validations exceptions handler and thus have it return meaningful errors to client fix spacing issue caught by houndci
1 parent 8e32477 commit b3788d2

File tree

4 files changed

+57
-0
lines changed

4 files changed

+57
-0
lines changed

flask_restless/views/resources.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -721,6 +721,8 @@ def patch(self, resource_id):
721721
result = self.serializer.serialize(instance, only=only)
722722
except SerializationException as exception:
723723
return errors_from_serialization_exceptions([exception])
724+
except self.validation_exceptions as exception:
725+
return self._handle_validation_exception(exception)
724726
status = 200
725727
else:
726728
result = dict()

requirements/test.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
-r install.txt
22
unittest2
3+
marshmallow

tests/helpers.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
from sqlalchemy.orm.session import Session as SessionBase
4040
from sqlalchemy.types import CHAR
4141
from sqlalchemy.types import TypeDecorator
42+
from marshmallow import ValidationError
4243

4344
from flask_restless import APIManager
4445
from flask_restless import collection_name
@@ -119,6 +120,29 @@ def deserialize_many(self, *args, **kw):
119120
raise DeserializationException
120121

121122

123+
class raise_s_validation_error(DefaultSerializer):
124+
"""A serializer that unconditionally raises a validation error when
125+
either :meth:`.serialize` or :meth:`.serialize_many` is called.
126+
127+
This class is useful for tests of validation errors.
128+
129+
"""
130+
131+
def serialize(self, instance, *args, **kw):
132+
"""Immediately raises a :exc:`ValidationError` with
133+
access to the provided `instance` of a SQLAlchemy model.
134+
135+
"""
136+
raise ValidationError(instance)
137+
138+
def serialize_many(self, instances, *args, **kw):
139+
"""Immediately raises a :exc:`ValidationError`.
140+
141+
This function requires `instances` to be non-empty.
142+
143+
"""
144+
raise ValidationError(instances[0])
145+
122146
def isclass(obj):
123147
"""Returns ``True`` if and only if the specified object is a type (or a
124148
class).

tests/test_updating.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
from sqlalchemy.ext.hybrid import hybrid_property
4242
from sqlalchemy.orm import backref
4343
from sqlalchemy.orm import relationship
44+
from marshmallow import ValidationError
4445

4546
from flask_restless import APIManager
4647
from flask_restless import JSONAPI_MIMETYPE
@@ -55,6 +56,7 @@
5556
from .helpers import MSIE9_UA
5657
from .helpers import ManagerTestBase
5758
from .helpers import raise_s_exception as raise_exception
59+
from .helpers import raise_s_validation_error as raise_validation_error
5860

5961

6062
class TestUpdating(ManagerTestBase):
@@ -893,6 +895,34 @@ def test_serialization_exception(self):
893895
check_sole_error(response, 500, ['Failed to serialize', 'type', 'tag',
894896
'ID', '1'])
895897

898+
def test_serialization_validation_error(self):
899+
"""Tests that serialization validation errors are caught when
900+
responding with content.
901+
902+
A representation of the modified resource is returned to the
903+
client when an update causes additional changes in the resource
904+
in ways other than those specified by the client.
905+
906+
"""
907+
tag = self.Tag(id=1)
908+
self.session.add(tag)
909+
self.session.commit()
910+
self.manager.create_api(self.Tag, methods=['PATCH'],
911+
validation_exceptions=[ValidationError],
912+
serializer_class=raise_validation_error)
913+
data = {
914+
'data': {
915+
'type': 'tag',
916+
'id': '1',
917+
'attributes': {
918+
'name': u'foo'
919+
}
920+
}
921+
}
922+
response = self.app.patch('/api/tag/1', data=dumps(data))
923+
assert response.status_code == 400
924+
assert response.status == '400 BAD REQUEST'
925+
896926
def test_dont_assign_to_method(self):
897927
"""Tests that if a certain method is to be included in a
898928
resource, that method is not assigned to when updating the

0 commit comments

Comments
 (0)