Skip to content
This repository was archived by the owner on Jun 6, 2024. It is now read-only.

Commit b0407aa

Browse files
authored
Merge pull request #90 from rgant/format-id-error
Special case for id field error formatting.
2 parents a6f8a94 + 899b65f commit b0407aa

2 files changed

Lines changed: 53 additions & 11 deletions

File tree

marshmallow_jsonapi/schema.py

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -277,26 +277,28 @@ def format_error(self, field_name, message, index=None):
277277
278278
See: http://jsonapi.org/format/#error-objects
279279
"""
280+
pointer = ['/data']
281+
282+
if index is not None:
283+
pointer.append(str(index))
284+
280285
relationship = isinstance(
281286
self.declared_fields.get(field_name), BaseRelationship)
282287
if relationship:
283-
container = 'relationships'
284-
else:
285-
container = 'attributes'
288+
pointer.append('relationships')
289+
elif field_name != 'id':
290+
# JSONAPI identifier is a special field that exists above the attribute object.
291+
pointer.append('attributes')
286292

287-
inflected_name = self.inflect(field_name)
288-
if index is not None:
289-
pointer = '/data/{}/{}/{}'.format(index, container, inflected_name)
290-
else:
291-
pointer = '/data/{}/{}'.format(container, inflected_name)
293+
pointer.append(self.inflect(field_name))
292294

293295
if relationship:
294-
pointer = '{}/data'.format(pointer)
296+
pointer.append('data')
295297

296298
return {
297299
'detail': message,
298300
'source': {
299-
'pointer': pointer
301+
'pointer': '/'.join(pointer)
300302
}
301303
}
302304

tests/test_schema.py

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -375,7 +375,8 @@ class Meta:
375375

376376
def get_error_by_field(errors, field):
377377
for err in errors['errors']:
378-
if err['source']['pointer'].split('/data/attributes/')[1] == field:
378+
# Relationship error pointers won't match with this.
379+
if err['source']['pointer'].endswith('/' + field):
379380
return err
380381
return None
381382

@@ -489,6 +490,24 @@ def test_validate_type(self):
489490
]
490491
}
491492

493+
def test_validate_id(self):
494+
""" the pointer for id should be at the data object, not attributes """
495+
author = {'data': {'type': 'people', 'id': 'invalid',
496+
'attributes': {'first_name': 'Rob', 'password': 'correcthorses'}}}
497+
errors = AuthorSchema().validate(author)
498+
assert 'errors' in errors
499+
assert len(errors['errors']) == 2
500+
501+
lname_err = get_error_by_field(errors, 'last_name')
502+
assert lname_err
503+
assert lname_err['source']['pointer'] == '/data/attributes/last_name'
504+
assert lname_err['detail'] == 'Missing data for required field.'
505+
506+
id_err = get_error_by_field(errors, 'id')
507+
assert id_err
508+
assert id_err['source']['pointer'] == '/data/id'
509+
assert id_err['detail'] == 'Not a valid integer.'
510+
492511
def test_load(self):
493512
_, errors = AuthorSchema().load(make_author({'first_name': 'Dan', 'password': 'short'}))
494513
assert 'errors' in errors
@@ -520,6 +539,27 @@ def test_errors_many(self):
520539
assert 'source' in err
521540
assert err['source']['pointer'] == '/data/0/attributes/password'
522541

542+
def test_many_id_errors(self):
543+
""" the pointer for id should be at the data object, not attributes """
544+
author = {'data': [{'type': 'people', 'id': 'invalid',
545+
'attributes': {'first_name': 'Rob', 'password': 'correcthorses'}},
546+
{'type': 'people', 'id': '37',
547+
'attributes': {'first_name': 'Dan', 'last_name': 'Gebhardt',
548+
'password': 'supersecret'}}]}
549+
errors = AuthorSchema(many=True).validate(author)
550+
assert 'errors' in errors
551+
assert len(errors['errors']) == 2
552+
553+
lname_err = get_error_by_field(errors, 'last_name')
554+
assert lname_err
555+
assert lname_err['source']['pointer'] == '/data/0/attributes/last_name'
556+
assert lname_err['detail'] == 'Missing data for required field.'
557+
558+
id_err = get_error_by_field(errors, 'id')
559+
assert id_err
560+
assert id_err['source']['pointer'] == '/data/0/id'
561+
assert id_err['detail'] == 'Not a valid integer.'
562+
523563
def dasherize(text):
524564
return text.replace('_', '-')
525565

0 commit comments

Comments
 (0)