Commit ade37eea authored by Arkadiusz Hiler's avatar Arkadiusz Hiler

API: Make msg-id lookup endpoint consistent with others

 * Use django-rest-framework views for nice browser previews
 * Add pretended pagination for consistency with other list endpoints
 * Extra: remove accidental print
Signed-off-by: Arkadiusz Hiler's avatarArkadiusz Hiler <arkadiusz.hiler@intel.com>
parent 9b042b14
Pipeline #13952 passed with stage
in 10 minutes and 31 seconds
...@@ -780,9 +780,62 @@ Patches ...@@ -780,9 +780,62 @@ Patches
$ curl -s http://patchwork.example.com/api/1.0/patches/42/mbox/ | git am -3 $ curl -s http://patchwork.example.com/api/1.0/patches/42/mbox/ | git am -3
Message-Id Lookup
~~~~~~~~~~~~~~~~~
.. http:get:: /api/1.0/msgids/(string: msgid)/
List series containing patches/cover letter with given Message-Id. If the
Message-Id resolves to a patch (instead of a cover letter) the ``patch_id``
of that patch is also provided. Additionaly ``project_id`` of the series and
list of revisions containing the patch/cover letter are always provided.
``msgid`` can be supplied either with or without less-than and greater-than
sings, i.e. both ``<msg@id>`` and ``msg@id`` are accepted.
This endpoint is never paginated.
.. sourcecode:: http
GET /api/1.0/msgids/<msg@id>/ HTTP/1.1
Accept: application/json
.. sourcecode:: http
HTTP/1.1 200 OK
Content-Type: application/json
Vary: Accept
Allow: GET, HEAD, OPTIONS
{
"count": 2,
"next": null,
"previous": null,
"results": [
{
"patch_id": 112227,
"project_id": 9,
"series_id": 12938,
"revision_ids": [ 1 ]
},
{
"patch_id": 112229,
"project_id": 18,
"series_id": 12937,
"revision_ids": [ 1 ]
}
]
}
API Revisions API Revisions
------------- -------------
**Revision 4**
- Add Msg-Id Lookup entry point:
- /api/1.0/msgids/${msgid}/
**Revision 3** **Revision 3**
- Add test results entry points: - Add test results entry points:
......
...@@ -466,12 +466,13 @@ class MsgidsLookupTest(APITestBase): ...@@ -466,12 +466,13 @@ class MsgidsLookupTest(APITestBase):
def testSingleSeriesSingleRevisionLookup(self): def testSingleSeriesSingleRevisionLookup(self):
response = self.get_json(self.msgid_url.format(self.patch.msgid)) response = self.get_json(self.msgid_url.format(self.patch.msgid))
results = response['results']
self.assertEquals(len(response), 1) # just one matching patch self.assertEquals(response['count'], 1) # just one matching patch
self.assertEquals(response[0]['patch_id'], self.patch.id) self.assertEquals(results[0]['patch_id'], self.patch.id)
self.assertEquals(response[0]['project_id'], self.patch.project_id) self.assertEquals(results[0]['project_id'], self.patch.project_id)
self.assertEquals(response[0]['series_id'], self.patch.series().id) self.assertEquals(results[0]['series_id'], self.patch.series().id)
self.assertEquals(response[0]['revision_ids'], [self.revision.version]) self.assertEquals(results[0]['revision_ids'], [self.revision.version])
def testUnbracketedLookup(self): def testUnbracketedLookup(self):
msgid = self.patch.msgid msgid = self.patch.msgid
...@@ -482,16 +483,17 @@ class MsgidsLookupTest(APITestBase): ...@@ -482,16 +483,17 @@ class MsgidsLookupTest(APITestBase):
response1 = self.get_json(self.msgid_url.format(msgid)) response1 = self.get_json(self.msgid_url.format(msgid))
response2 = self.get_json(self.msgid_url.format(stripped_msgid)) response2 = self.get_json(self.msgid_url.format(stripped_msgid))
self.assertTrue(len(response1) != 0) self.assertTrue(response1['count'] != 0)
self.assertEquals(response1, response2) self.assertEquals(response1, response2)
def testSingleSeriesMultipleRevisionsLookup(self): def testSingleSeriesMultipleRevisionsLookup(self):
rev2 = self.revision.duplicate() rev2 = self.revision.duplicate()
response = self.get_json(self.msgid_url.format(self.patch.msgid)) response = self.get_json(self.msgid_url.format(self.patch.msgid))
results = response['results']
self.assertEquals(len(response), 1) # just one matching patch self.assertEquals(response['count'], 1) # just one matching patch
self.assertEquals(response[0]['revision_ids'], # multiple revs self.assertEquals(results[0]['revision_ids'], # multiple revs
[self.revision.version, rev2.version]) [self.revision.version, rev2.version])
def testMulitpleSeriesSingleRevisionsLookup(self): def testMulitpleSeriesSingleRevisionsLookup(self):
...@@ -508,16 +510,17 @@ class MsgidsLookupTest(APITestBase): ...@@ -508,16 +510,17 @@ class MsgidsLookupTest(APITestBase):
response = self.get_json(self.msgid_url.format(self.patch.msgid)) response = self.get_json(self.msgid_url.format(self.patch.msgid))
self.assertEquals(len(response), 2) self.assertEquals(response['count'], 2)
def testCoverLetterLookup(self): def testCoverLetterLookup(self):
response = self.get_json(self.msgid_url.format(self.root_msgid)) response = self.get_json(self.msgid_url.format(self.root_msgid))
results = response['results']
self.assertEquals(len(response), 1) self.assertEquals(response['count'], 1)
self.assertTrue('patch_id' not in response[0].keys()) self.assertTrue('patch_id' not in results[0].keys())
self.assertEquals(response[0]['project_id'], self.series.project_id) self.assertEquals(results[0]['project_id'], self.series.project_id)
self.assertEquals(response[0]['series_id'], self.series.id) self.assertEquals(results[0]['series_id'], self.series.id)
self.assertEquals(response[0]['revision_ids'], [self.revision.version]) self.assertEquals(results[0]['revision_ids'], [self.revision.version])
class TestResultTest(APITestBase): class TestResultTest(APITestBase):
......
...@@ -835,7 +835,6 @@ class SeriesRevisionTest(GeneratedSeriesTest): ...@@ -835,7 +835,6 @@ class SeriesRevisionTest(GeneratedSeriesTest):
dup = org.duplicate_meta() dup = org.duplicate_meta()
print(dup.pk)
self.assertTrue(dup.test_state is None) self.assertTrue(dup.test_state is None)
self.assertNotEqual(dup.pk, org.pk) self.assertNotEqual(dup.pk, org.pk)
......
...@@ -89,8 +89,8 @@ urlpatterns = [ ...@@ -89,8 +89,8 @@ urlpatterns = [
url(r'^api/1.0/', include(patches_router.urls)), url(r'^api/1.0/', include(patches_router.urls)),
url(r'^api/1.0/', include(patch_results_router.urls)), url(r'^api/1.0/', include(patch_results_router.urls)),
url(r'^api/1.0/', include(event_router.urls)), url(r'^api/1.0/', include(event_router.urls)),
url(r'^api/1.0/msgids/(?P<msgid>[^/]+)/$', api.msgid, url(r'^api/1.0/msgids/(?P<msgid>[^/]+)/$', api.MsgidResultsView.as_view(),
name='msgid_to_patches'), name='msgid_to_series'),
# project views: # project views:
url(r'^$', patchwork.views.base.projects, url(r'^$', patchwork.views.base.projects,
......
...@@ -23,8 +23,7 @@ from django.core.exceptions import FieldDoesNotExist, PermissionDenied ...@@ -23,8 +23,7 @@ from django.core.exceptions import FieldDoesNotExist, PermissionDenied
from django.conf import settings from django.conf import settings
from django.core import mail from django.core import mail
from django.db.models import Q from django.db.models import Q
from django.http import HttpResponse, JsonResponse from django.http import HttpResponse
from itertools import groupby
from patchwork.tasks import send_reviewer_notification from patchwork.tasks import send_reviewer_notification
from patchwork.models import (Project, Series, SeriesRevision, Patch, EventLog, from patchwork.models import (Project, Series, SeriesRevision, Patch, EventLog,
State, Test, TestResult, TestState, Person, State, Test, TestResult, TestState, Person,
...@@ -43,9 +42,10 @@ from patchwork.views import patch_to_mbox, revision_cover_letter_to_mbox ...@@ -43,9 +42,10 @@ from patchwork.views import patch_to_mbox, revision_cover_letter_to_mbox
from patchwork.views.patch import mbox as patch_mbox from patchwork.views.patch import mbox as patch_mbox
from patchwork.permissions import Can from patchwork.permissions import Can
import django_filters import django_filters
import itertools
API_REVISION = 2 API_REVISION = 4
class RelatedOrderingFilter(filters.OrderingFilter): class RelatedOrderingFilter(filters.OrderingFilter):
...@@ -549,39 +549,44 @@ class PatchViewSet(mixins.ListModelMixin, ...@@ -549,39 +549,44 @@ class PatchViewSet(mixins.ListModelMixin,
return patch_mbox(request, pk) return patch_mbox(request, pk)
def __grouping(iterable, key): def group(iterable, key):
"""Sort and group in one go"""
sorted_iterable = sorted(iterable, key=key) sorted_iterable = sorted(iterable, key=key)
return groupby(sorted_iterable, key) return itertools.groupby(sorted_iterable, key)
def msgid(request, msgid): class MsgidResultsView(views.APIView):
output = [] def get(self, request, msgid):
output = []
if not (msgid.startswith('<') and msgid.endswith('>')): if not (msgid.startswith('<') and msgid.endswith('>')):
msgid = '<{}>'.format(msgid) msgid = '<{}>'.format(msgid)
patches = Patch.objects.filter(msgid=msgid) patches = Patch.objects.filter(msgid=msgid)
for patch in patches: for patch in patches:
series = patch.series() series = patch.series()
revisions = series.revisions().filter(patches=patch) revisions = series.revisions().filter(patches=patch)
desc = {'patch_id': patch.id, desc = {'patch_id': patch.id,
'project_id': patch.project_id, 'project_id': patch.project_id,
'series_id': series.id, 'series_id': series.id,
'revision_ids': [rev.version for rev in revisions]} 'revision_ids': [rev.version for rev in revisions]}
output += [desc] output += [desc]
revisions = SeriesRevision.objects.filter(root_msgid=msgid, revisions = SeriesRevision.objects.filter(root_msgid=msgid,
cover_letter__isnull=False) cover_letter__isnull=False)
for (series_id, rev_grp) in __grouping(revisions, lambda x: x.series_id): for (series_id, rev_grp) in group(revisions, lambda x: x.series_id):
rev_grp = list(rev_grp) rev_grp = list(rev_grp)
desc = {'project_id': rev_grp[0].series.project_id, desc = {'project_id': rev_grp[0].series.project_id,
'series_id': series_id, 'series_id': series_id,
'revision_ids': [rev.version for rev in rev_grp]} 'revision_ids': [rev.version for rev in rev_grp]}
output += [desc] output += [desc]
return JsonResponse(output, safe=False) # let's mimic rest-framework's wrapping for consistency
return Response({'count': len(output),
'next': None, 'previous': None,
'results': output})
class PatchResultViewSet(viewsets.ViewSet, ResultMixin): class PatchResultViewSet(viewsets.ViewSet, ResultMixin):
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment