ColtonProvias / sqlalchemy-jsonapi

JSONAPI implementation for use with SQLAlchemy
MIT License
70 stars 27 forks source link

Post_collection is not raising BadRequestError when payload relationships is not a dictionary #60

Open kaitj91 opened 7 years ago

kaitj91 commented 7 years ago

It is very important that the library is able to return some sort of response that let's the user know they submitted an invalid request by raising a BadRequestError and returning a 400 and some sort of description explaining why.

Through testing post_collection, I found that we are not raising a BadRequestError when we send a payload where the relationships is an array rather than a dictionary

For example:

    def test_add_resource_when_payload_relationships_is_array(self):
        """Create a resource when payload relationships is an array returns 400.

        The relationships must be of type dictionary.
        A BadRequestError is raised.
        """
        user = models.User(
            first='Sally', last='Smith',
            password='password', username='SallySmith1')
        self.session.add(user)
        self.session.commit()

        payload = {
            'data': {
                'type': 'posts',
                'attributes': {
                    'title': 'Some Title',
                    'content': 'Some Content Inside'
                },
                'relationships': [{
                    'author': {
                        'data': {
                            'type': 'users',
                            'id': 1,
                        }
                    }
                }]
            }
        }

        with self.assertRaises(errors.BadRequestError) as error:
            models.serializer.post_collection(
                self.session, payload, 'posts')

        self.assertEqual(
            error.exception.detail, 'relationships must be an object')
        self.assertEqual(error.exception.status_code, 400)

This test fails because there is no check for this in place in post_collection. Thus when I try to run this test a AttributeError occurs. Specifically the AttributeError is occuring because we are failing to check

if not isinstance(data['data']['relationships'], dict):
    raise BadRequestError('relationships must be an object')

before executing this piece of code in post_collection.

                    data_keys = set(map((
                        lambda x: resource.__jsonapi_map_to_py__.get(x, None)),
                        data['data'].get('relationships', {}).keys()))

Because we do not have this check, a AttributeError occurs because data['data']['relationships'] does not have any keys when trying to execute data['data'].get('relationships', {}).keys() because it is a list. This is an easy fix that would provide an informative error message to users who send badly formatted payloads.