Skip to content

Commit b54f23d

Browse files
koriafsliverc
authored andcommitted
Prevented overwriting of pointer in custom error object
1 parent 46551a9 commit b54f23d

File tree

4 files changed

+64
-6
lines changed

4 files changed

+64
-6
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ any parts of the framework not mentioned in the documentation should generally b
1414

1515
* Fixed invalid relationship pointer in error objects when field naming formatting is used.
1616
* Properly resolved related resource type when nested source field is defined.
17+
* Prevented overwriting of pointer in custom error object
1718

1819
### Added
1920

docs/usage.md

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -237,14 +237,29 @@ class MyViewset(ModelViewSet):
237237

238238
```
239239

240-
### Exception handling
240+
### Error objects / Exception handling
241241

242242
For the `exception_handler` class, if the optional `JSON_API_UNIFORM_EXCEPTIONS` is set to True,
243243
all exceptions will respond with the JSON:API [error format](https://jsonapi.org/format/#error-objects).
244244

245245
When `JSON_API_UNIFORM_EXCEPTIONS` is False (the default), non-JSON:API views will respond
246246
with the normal DRF error format.
247247

248+
In case you need a custom error object you can simply raise an `rest_framework.serializers.ValidationError` like the following:
249+
250+
```python
251+
raise serializers.ValidationError(
252+
{
253+
"id": "your-id",
254+
"detail": "your detail message",
255+
"source": {
256+
"pointer": "/data/attributes/your-pointer",
257+
}
258+
259+
}
260+
)
261+
```
262+
248263
### Performance Testing
249264

250265
If you are trying to see if your viewsets are configured properly to optimize performance,

rest_framework_json_api/utils.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -417,21 +417,20 @@ def format_error_object(message, pointer, response):
417417
if isinstance(message, dict):
418418

419419
# as there is no required field in error object we check that all fields are string
420-
# except links and source which might be a dict
420+
# except links, source or meta which might be a dict
421421
is_custom_error = all(
422422
[
423423
isinstance(value, str)
424424
for key, value in message.items()
425-
if key not in ["links", "source"]
425+
if key not in ["links", "source", "meta"]
426426
]
427427
)
428428

429429
if is_custom_error:
430430
if "source" not in message:
431431
message["source"] = {}
432-
message["source"] = {
433-
"pointer": pointer,
434-
}
432+
if "pointer" not in message["source"]:
433+
message["source"]["pointer"] = pointer
435434
errors.append(message)
436435
else:
437436
for k, v in message.items():

tests/test_utils.py

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
from rest_framework_json_api import serializers
99
from rest_framework_json_api.utils import (
10+
format_error_object,
1011
format_field_name,
1112
format_field_names,
1213
format_link_segment,
@@ -389,3 +390,45 @@ class SerializerWithoutResourceName(serializers.Serializer):
389390
"can not detect 'resource_name' on serializer "
390391
"'SerializerWithoutResourceName' in module 'tests.test_utils'"
391392
)
393+
394+
395+
@pytest.mark.parametrize(
396+
"message,pointer,response,result",
397+
[
398+
# Test that pointer does not get overridden in custom error
399+
(
400+
{
401+
"status": "400",
402+
"source": {
403+
"pointer": "/data/custom-pointer",
404+
},
405+
"meta": {"key": "value"},
406+
},
407+
"/data/default-pointer",
408+
Response(status.HTTP_400_BAD_REQUEST),
409+
[
410+
{
411+
"status": "400",
412+
"source": {"pointer": "/data/custom-pointer"},
413+
"meta": {"key": "value"},
414+
}
415+
],
416+
),
417+
# Test that pointer gets added to custom error
418+
(
419+
{
420+
"detail": "custom message",
421+
},
422+
"/data/default-pointer",
423+
Response(status.HTTP_400_BAD_REQUEST),
424+
[
425+
{
426+
"detail": "custom message",
427+
"source": {"pointer": "/data/default-pointer"},
428+
}
429+
],
430+
),
431+
],
432+
)
433+
def test_format_error_object(message, pointer, response, result):
434+
assert result == format_error_object(message, pointer, response)

0 commit comments

Comments
 (0)