Repository: django-json-api/django-rest-framework-json-api

Issue:
Avoid overwriting of pointer when using custom errors
The code that produces JSON:API error objects assumes that all fields being validated are under `/data/attributes`:

https://github.com/django-json-api/django-rest-framework-json-api/blob/b0257c065ac2439036ac0543139e7f5913755177/rest_framework_json_api/utils.py#L339

and there doesn't appear to be a way to override the `pointer` value when specifying the field on a serializer.

Use case: I would like to be able to specify a serializer for the top-level `"meta"` field and have any validation errors on that data generate responses properly populated with `"source": {"pointer": "/meta/foo"}`.


Discussion:
More generally: I would like to be able to override the `pointer` that DRFJA creates with a custom value per field. I would guess (but have not tested) that the code linked above also doesn't handle specifying indices for individual errors when `data` is a list.
It is currently not possible to have custom error format. It might be handy though also to add other information allowed by [json api](http://jsonapi.org/format/#errors). Maybe needs to be solved with a custom json api `ValidationException` but would need to investigate how this can be done properly.

What you could always do is to return your own [Response](http://www.django-rest-framework.org/api-guide/responses/#response) with the error in the format you need it.


While discussing #776 it occurred to me that it actually is already possible to define custom error objects - see [test code snippet](https://github.com/django-json-api/django-rest-framework-json-api/blob/master/example/api/serializers/identity.py#L18).

Closing this issue therefore. Please comment in case this doesn't solve this issue.
**serializers.ValidationError** does not support custom `source: { pointer: ... }`. E.g.
```                    
raise serializers.ValidationError([{
        "detail": "This field is required.",
        "source": {
        "pointer": "/data/something"
     }
}])
```
results in 
```
{
    "errors": [
        {
            "detail": "This field is required.",
            "source": {
                "pointer": "/data"
            }
        }
    ]
}
```
This issue needs to be reopen. 
Thanks @Elawphant for pointing this out. I have done some research. Indeed, there seems to be an issue but not with custom errors in general but just that the pointer gets overwritten. I think the error is [here](https://github.com/django-json-api/django-rest-framework-json-api/blob/main/rest_framework_json_api/utils.py#L432). Assigning the pointer should only happen if there is no previous pointer. A PR is welcome.

Resolving patch:
diff --git a/rest_framework_json_api/utils.py b/rest_framework_json_api/utils.py
--- a/rest_framework_json_api/utils.py
+++ b/rest_framework_json_api/utils.py
@@ -417,21 +417,20 @@ def format_error_object(message, pointer, response):
     if isinstance(message, dict):
 
         # as there is no required field in error object we check that all fields are string
-        # except links and source which might be a dict
+        # except links, source or meta which might be a dict
         is_custom_error = all(
             [
                 isinstance(value, str)
                 for key, value in message.items()
-                if key not in ["links", "source"]
+                if key not in ["links", "source", "meta"]
             ]
         )
 
         if is_custom_error:
             if "source" not in message:
                 message["source"] = {}
-            message["source"] = {
-                "pointer": pointer,
-            }
+            if "pointer" not in message["source"]:
+                message["source"]["pointer"] = pointer
             errors.append(message)
         else:
             for k, v in message.items():


Test patch:
diff --git a/tests/test_utils.py b/tests/test_utils.py
--- a/tests/test_utils.py
+++ b/tests/test_utils.py
@@ -7,6 +7,7 @@
 
 from rest_framework_json_api import serializers
 from rest_framework_json_api.utils import (
+    format_error_object,
     format_field_name,
     format_field_names,
     format_link_segment,
@@ -389,3 +390,45 @@ class SerializerWithoutResourceName(serializers.Serializer):
         "can not detect 'resource_name' on serializer "
         "'SerializerWithoutResourceName' in module 'tests.test_utils'"
     )
+
+
+@pytest.mark.parametrize(
+    "message,pointer,response,result",
+    [
+        # Test that pointer does not get overridden in custom error
+        (
+            {
+                "status": "400",
+                "source": {
+                    "pointer": "/data/custom-pointer",
+                },
+                "meta": {"key": "value"},
+            },
+            "/data/default-pointer",
+            Response(status.HTTP_400_BAD_REQUEST),
+            [
+                {
+                    "status": "400",
+                    "source": {"pointer": "/data/custom-pointer"},
+                    "meta": {"key": "value"},
+                }
+            ],
+        ),
+        # Test that pointer gets added to custom error
+        (
+            {
+                "detail": "custom message",
+            },
+            "/data/default-pointer",
+            Response(status.HTTP_400_BAD_REQUEST),
+            [
+                {
+                    "detail": "custom message",
+                    "source": {"pointer": "/data/default-pointer"},
+                }
+            ],
+        ),
+    ],
+)
+def test_format_error_object(message, pointer, response, result):
+    assert result == format_error_object(message, pointer, response)