[PATCH 3/4] REST: Allow filtering of submitters by email

Stephen Finucane stephen at that.guru
Mon Dec 11 04:30:34 AEDT 2017


This means we don't need to make a request to '/people' to filter things
like patches or series.

Signed-off-by: Stephen Finucane <stephen at that.guru>
---
 patchwork/api/filters.py                           | 39 ++++++++++++++++++---
 patchwork/tests/test_rest_api.py                   | 40 ++++++++++++++++++++--
 .../improved-rest-filtering-bf68399270a9b245.yaml  |  9 +++++
 3 files changed, 80 insertions(+), 8 deletions(-)
 create mode 100644 releasenotes/notes/improved-rest-filtering-bf68399270a9b245.yaml

diff --git a/patchwork/api/filters.py b/patchwork/api/filters.py
index 198d64f4..c34f5496 100644
--- a/patchwork/api/filters.py
+++ b/patchwork/api/filters.py
@@ -29,6 +29,7 @@ from patchwork.models import Check
 from patchwork.models import CoverLetter
 from patchwork.models import Event
 from patchwork.models import Patch
+from patchwork.models import Person
 from patchwork.models import Project
 from patchwork.models import Series
 from patchwork.models import State
@@ -41,16 +42,16 @@ class TimestampMixin(FilterSet):
     since = IsoDateTimeFilter(name='date', **{LOOKUP_FIELD: 'gte'})
 
 
-class ProjectChoiceField(ModelChoiceField):
+class ModelMultiChoiceField(ModelChoiceField):
+
+    def _get_filters(self, value):
+        raise NotImplementedError
 
     def to_python(self, value):
         if value in self.empty_values:
             return None
 
-        try:
-            filters = {'pk': int(value)}
-        except ValueError:
-            filters = {'linkname__iexact': value}
+        filters = self._get_filters(value)
 
         try:
             value = self.queryset.get(**filters)
@@ -60,6 +61,15 @@ class ProjectChoiceField(ModelChoiceField):
         return value
 
 
+class ProjectChoiceField(ModelMultiChoiceField):
+
+    def _get_filters(self, value):
+        try:
+            return {'pk': int(value)}
+        except ValueError:
+            return {'linkname__iexact': value}
+
+
 class ProjectFilter(ModelChoiceFilter):
 
     field_class = ProjectChoiceField
@@ -71,8 +81,24 @@ class ProjectMixin(FilterSet):
                             queryset=Project.objects.all())
 
 
+class PersonChoiceField(ModelMultiChoiceField):
+
+    def _get_filters(self, value):
+        try:
+            return {'pk': int(value)}
+        except ValueError:
+            return {'email__iexact': value}
+
+
+class PersonFilter(ModelChoiceFilter):
+
+    field_class = PersonChoiceField
+
+
 class SeriesFilter(ProjectMixin, TimestampMixin, FilterSet):
 
+    submitter = PersonFilter(queryset=Person.objects.all())
+
     class Meta:
         model = Series
         fields = ('submitter', 'project')
@@ -80,6 +106,8 @@ class SeriesFilter(ProjectMixin, TimestampMixin, FilterSet):
 
 class CoverLetterFilter(ProjectMixin, TimestampMixin, FilterSet):
 
+    submitter = PersonFilter(queryset=Person.objects.all())
+
     class Meta:
         model = CoverLetter
         fields = ('project', 'series', 'submitter')
@@ -113,6 +141,7 @@ class StateFilter(ModelChoiceFilter):
 class PatchFilter(ProjectMixin, TimestampMixin, FilterSet):
 
     state = StateFilter(queryset=State.objects.all())
+    submitter = PersonFilter(queryset=Person.objects.all())
 
     class Meta:
         model = Patch
diff --git a/patchwork/tests/test_rest_api.py b/patchwork/tests/test_rest_api.py
index 87112d9f..7d10f909 100644
--- a/patchwork/tests/test_rest_api.py
+++ b/patchwork/tests/test_rest_api.py
@@ -341,9 +341,11 @@ class TestPatchAPI(APITestCase):
         self.assertEqual(status.HTTP_200_OK, resp.status_code)
         self.assertEqual(0, len(resp.data))
 
+        person_obj = create_person(email='test at example.com')
         state_obj = create_state(name='Under Review')
         project_obj = create_project(linkname='myproject')
-        patch_obj = create_patch(state=state_obj, project=project_obj)
+        patch_obj = create_patch(state=state_obj, project=project_obj,
+                                 submitter=person_obj)
 
         # anonymous user
         resp = self.client.get(self.api_url())
@@ -376,6 +378,16 @@ class TestPatchAPI(APITestCase):
         resp = self.client.get(self.api_url(), {'project': 'invalidproject'})
         self.assertEqual(0, len(resp.data))
 
+        # test filtering by submitter, both ID and email
+        resp = self.client.get(self.api_url(), {'submitter': person_obj.id})
+        self.assertEqual([patch_obj.id], [x['id'] for x in resp.data])
+        resp = self.client.get(self.api_url(), {
+            'submitter': 'test at example.com'})
+        self.assertEqual([patch_obj.id], [x['id'] for x in resp.data])
+        resp = self.client.get(self.api_url(), {
+            'submitter': 'test at example.org'})
+        self.assertEqual(0, len(resp.data))
+
     def test_detail(self):
         """Validate we can get a specific patch."""
         patch = create_patch(
@@ -493,8 +505,9 @@ class TestCoverLetterAPI(APITestCase):
         self.assertEqual(status.HTTP_200_OK, resp.status_code)
         self.assertEqual(0, len(resp.data))
 
+        person_obj = create_person(email='test at example.com')
         project_obj = create_project(linkname='myproject')
-        cover_obj = create_cover(project=project_obj)
+        cover_obj = create_cover(project=project_obj, submitter=person_obj)
 
         # anonymous user
         resp = self.client.get(self.api_url())
@@ -516,6 +529,16 @@ class TestCoverLetterAPI(APITestCase):
         resp = self.client.get(self.api_url(), {'project': 'invalidproject'})
         self.assertEqual(0, len(resp.data))
 
+        # test filtering by submitter, both ID and email
+        resp = self.client.get(self.api_url(), {'submitter': person_obj.id})
+        self.assertEqual([cover_obj.id], [x['id'] for x in resp.data])
+        resp = self.client.get(self.api_url(), {
+            'submitter': 'test at example.com'})
+        self.assertEqual([cover_obj.id], [x['id'] for x in resp.data])
+        resp = self.client.get(self.api_url(), {
+            'submitter': 'test at example.org'})
+        self.assertEqual(0, len(resp.data))
+
     def test_detail(self):
         """Validate we can get a specific cover letter."""
         cover_obj = create_cover()
@@ -574,8 +597,9 @@ class TestSeriesAPI(APITestCase):
         self.assertEqual(status.HTTP_200_OK, resp.status_code)
         self.assertEqual(0, len(resp.data))
 
+        person_obj = create_person(email='test at example.com')
         project_obj = create_project(linkname='myproject')
-        series_obj = create_series(project=project_obj)
+        series_obj = create_series(project=project_obj, submitter=person_obj)
 
         # anonymous user
         resp = self.client.get(self.api_url())
@@ -597,6 +621,16 @@ class TestSeriesAPI(APITestCase):
         resp = self.client.get(self.api_url(), {'project': 'invalidproject'})
         self.assertEqual(0, len(resp.data))
 
+        # test filtering by submitter, both ID and email
+        resp = self.client.get(self.api_url(), {'submitter': person_obj.id})
+        self.assertEqual([series_obj.id], [x['id'] for x in resp.data])
+        resp = self.client.get(self.api_url(), {
+            'submitter': 'test at example.com'})
+        self.assertEqual([series_obj.id], [x['id'] for x in resp.data])
+        resp = self.client.get(self.api_url(), {
+            'submitter': 'test at example.org'})
+        self.assertEqual(0, len(resp.data))
+
     def test_detail(self):
         """Validate we can get a specific series."""
         series = create_series()
diff --git a/releasenotes/notes/improved-rest-filtering-bf68399270a9b245.yaml b/releasenotes/notes/improved-rest-filtering-bf68399270a9b245.yaml
new file mode 100644
index 00000000..fda68790
--- /dev/null
+++ b/releasenotes/notes/improved-rest-filtering-bf68399270a9b245.yaml
@@ -0,0 +1,9 @@
+---
+api:
+  - |
+    Series, patches and cover letters can be filtered by submitter using email
+    addresses. For example:
+
+    .. code-block:: shell
+
+       $ curl /covers/?submitter=stephen at that.guru
-- 
2.14.3



More information about the Patchwork mailing list