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

Issue:
SparseFieldsetsMixin catches invalid key from URL
Hi,

Once I want to use sparse fields in URL eg.:

`/api/something/?fields[bar]=somefield`

The SparseFieldsetsMixin is checking for the key as substring:

`key for key in request.query_params if sparse_fieldset_query_param in key`

That causes the situation where not only 'fields[bar]' matches the if case but also prefixes:

`/api/something/?someotherchars_fields[bar]=somefield`

or 

`/api/something/?foooooooofields[bar]=somefield`

Such behavior can cause some unexpected situations once for instance other mixin will use 'fields' key in some way to perform other operations on the same endpoint.


Discussion:
codecov[bot]: # [Codecov](https://codecov.io/gh/django-json-api/django-rest-framework-json-api/pull/842?src=pr&el=h1) Report
> Merging [#842](https://codecov.io/gh/django-json-api/django-rest-framework-json-api/pull/842?src=pr&el=desc) into [master](https://codecov.io/gh/django-json-api/django-rest-framework-json-api/commit/ab3fbfc8020b24c5aab54d53aa08cd927992bd48?el=desc) will **increase** coverage by `0.01%`.
> The diff coverage is `100.00%`.

[![Impacted file tree graph](https://codecov.io/gh/django-json-api/django-rest-framework-json-api/pull/842/graphs/tree.svg?width=650&height=150&src=pr&token=kJ4T4hcVh8)](https://codecov.io/gh/django-json-api/django-rest-framework-json-api/pull/842?src=pr&el=tree)

```diff
@@            Coverage Diff             @@
##           master     #842      +/-   ##
==========================================
+ Coverage   97.76%   97.77%   +0.01%     
==========================================
  Files          58       58              
  Lines        3089     3104      +15     
==========================================
+ Hits         3020     3035      +15     
  Misses         69       69              
```


| [Impacted Files](https://codecov.io/gh/django-json-api/django-rest-framework-json-api/pull/842?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [example/tests/integration/test\_sparse\_fieldsets.py](https://codecov.io/gh/django-json-api/django-rest-framework-json-api/pull/842/diff?src=pr&el=tree#diff-ZXhhbXBsZS90ZXN0cy9pbnRlZ3JhdGlvbi90ZXN0X3NwYXJzZV9maWVsZHNldHMucHk=) | `100.00% <100.00%> (ø)` | |

------

[Continue to review full report at Codecov](https://codecov.io/gh/django-json-api/django-rest-framework-json-api/pull/842?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/django-json-api/django-rest-framework-json-api/pull/842?src=pr&el=footer). Last update [ab3fbfc...e1f3929](https://codecov.io/gh/django-json-api/django-rest-framework-json-api/pull/842?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2174900..d008982 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -27,6 +27,9 @@ This release is not backwards compatible. For easy migration best upgrade first
 * Removed obsolete `source` argument of `SerializerMethodResourceRelatedField`
 * Removed obsolete setting `JSON_API_SERIALIZE_NESTED_SERIALIZERS_AS_ATTRIBUTE` to render nested serializers as relationships. Default is as attribute now.
 
+### Fixed
+
+* Stopped `SparseFieldsetsMixin` interpretting invalid fields query parameter (e.g. invalidfields[entries]=blog,headline)
 
 ## [3.2.0] - 2020-08-26
 
diff --git a/rest_framework_json_api/serializers.py b/rest_framework_json_api/serializers.py
index f7d884e..31f2a86 100644
--- a/rest_framework_json_api/serializers.py
+++ b/rest_framework_json_api/serializers.py
@@ -71,7 +71,7 @@ class SparseFieldsetsMixin(object):
             )
             try:
                 param_name = next(
-                    key for key in request.query_params if sparse_fieldset_query_param in key
+                    key for key in request.query_params if sparse_fieldset_query_param == key
                 )
             except StopIteration:
                 pass


Test patch:
diff --git a/example/tests/integration/test_sparse_fieldsets.py b/example/tests/integration/test_sparse_fieldsets.py
index c76f1ef..83b8560 100644
--- a/example/tests/integration/test_sparse_fieldsets.py
+++ b/example/tests/integration/test_sparse_fieldsets.py
@@ -1,12 +1,36 @@
 import pytest
 from django.urls import reverse
+from rest_framework import status
 
 pytestmark = pytest.mark.django_db
 
 
-def test_sparse_fieldset_ordered_dict_error(multiple_entries, client):
+def test_sparse_fieldset_valid_fields(client, entry):
     base_url = reverse('entry-list')
-    querystring = '?fields[entries]=blog,headline'
-    # RuntimeError: OrderedDict mutated during iteration
-    response = client.get(base_url + querystring)
-    assert response.status_code == 200  # succeed if we didn't fail due to the above RuntimeError
+    response = client.get(base_url, data={'fields[entries]': 'blog,headline'})
+    assert response.status_code == status.HTTP_200_OK
+    data = response.json()['data']
+
+    assert len(data) == 1
+    entry = data[0]
+    assert entry['attributes'].keys() == {'headline'}
+    assert entry['relationships'].keys() == {'blog'}
+
+
+@pytest.mark.parametrize("fields_param", ['invalidfields[entries]', 'fieldsinvalid[entries'])
+def test_sparse_fieldset_invalid_fields_parameter(client, entry, fields_param):
+    """
+    Test that invalid fields query parameter is not processed by sparse fieldset.
+
+    rest_framework_json_api.filters.QueryParameterValidationFilter takes care of error
+    handling in such a case.
+    """
+    base_url = reverse('entry-list')
+    response = client.get(base_url, data={'invalidfields[entries]': 'blog,headline'})
+    assert response.status_code == status.HTTP_200_OK
+    data = response.json()['data']
+
+    assert len(data) == 1
+    entry = data[0]
+    assert entry['attributes'].keys() != {'headline'}
+    assert entry['relationships'].keys() != {'blog'}