Skip to content

Commit

Permalink
catch validation errors during patch serialization
Browse files Browse the repository at this point in the history
- 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
  • Loading branch information
Kiptoo Magutt committed Feb 6, 2018
1 parent 8e32477 commit b3788d2
Show file tree
Hide file tree
Showing 4 changed files with 57 additions and 0 deletions.
2 changes: 2 additions & 0 deletions flask_restless/views/resources.py
Original file line number Diff line number Diff line change
Expand Up @@ -721,6 +721,8 @@ def patch(self, resource_id):
result = self.serializer.serialize(instance, only=only)
except SerializationException as exception:
return errors_from_serialization_exceptions([exception])
except self.validation_exceptions as exception:
return self._handle_validation_exception(exception)
status = 200
else:
result = dict()
Expand Down
1 change: 1 addition & 0 deletions requirements/test.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
-r install.txt
unittest2
marshmallow
24 changes: 24 additions & 0 deletions tests/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
from sqlalchemy.orm.session import Session as SessionBase
from sqlalchemy.types import CHAR
from sqlalchemy.types import TypeDecorator
from marshmallow import ValidationError

from flask_restless import APIManager
from flask_restless import collection_name
Expand Down Expand Up @@ -119,6 +120,29 @@ def deserialize_many(self, *args, **kw):
raise DeserializationException


class raise_s_validation_error(DefaultSerializer):
"""A serializer that unconditionally raises a validation error when
either :meth:`.serialize` or :meth:`.serialize_many` is called.
This class is useful for tests of validation errors.
"""

def serialize(self, instance, *args, **kw):
"""Immediately raises a :exc:`ValidationError` with
access to the provided `instance` of a SQLAlchemy model.
"""
raise ValidationError(instance)

def serialize_many(self, instances, *args, **kw):
"""Immediately raises a :exc:`ValidationError`.
This function requires `instances` to be non-empty.
"""
raise ValidationError(instances[0])

def isclass(obj):
"""Returns ``True`` if and only if the specified object is a type (or a
class).
Expand Down
30 changes: 30 additions & 0 deletions tests/test_updating.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
from sqlalchemy.ext.hybrid import hybrid_property
from sqlalchemy.orm import backref
from sqlalchemy.orm import relationship
from marshmallow import ValidationError

from flask_restless import APIManager
from flask_restless import JSONAPI_MIMETYPE
Expand All @@ -55,6 +56,7 @@
from .helpers import MSIE9_UA
from .helpers import ManagerTestBase
from .helpers import raise_s_exception as raise_exception
from .helpers import raise_s_validation_error as raise_validation_error


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

def test_serialization_validation_error(self):
"""Tests that serialization validation errors are caught when
responding with content.
A representation of the modified resource is returned to the
client when an update causes additional changes in the resource
in ways other than those specified by the client.
"""
tag = self.Tag(id=1)
self.session.add(tag)
self.session.commit()
self.manager.create_api(self.Tag, methods=['PATCH'],
validation_exceptions=[ValidationError],
serializer_class=raise_validation_error)
data = {
'data': {
'type': 'tag',
'id': '1',
'attributes': {
'name': u'foo'
}
}
}
response = self.app.patch('/api/tag/1', data=dumps(data))
assert response.status_code == 400
assert response.status == '400 BAD REQUEST'

def test_dont_assign_to_method(self):
"""Tests that if a certain method is to be included in a
resource, that method is not assigned to when updating the
Expand Down

0 comments on commit b3788d2

Please sign in to comment.