Commit dd89ff2e authored by Arkadiusz Hiler's avatar Arkadiusz Hiler

API: Add endpoint for finding patches via msg-ids

the endpoint is: /api/1.0/msgids/$MSG_ID/
msg-id works both with and without <>

[{'patch_id': 112223,
  'project_id': 9,
  'series_id': 12938,
  'revisions': [1,2]},
 {'patch_id': 112224,
  'project_id': 18,
  'series_id': 12937,
  'revisions': [1]}]

The implementation is not using django-rest-framework because the
generated routes disallow dots in the identifiers and requires adding
much more boilerplate to have it working.
Signed-off-by: Arkadiusz Hiler's avatarArkadiusz Hiler <>
parent 8d49649c
Pipeline #11464 passed with stage
in 11 minutes and 24 seconds
......@@ -24,6 +24,7 @@ import dateutil.parser
import hashlib
import json
import re
import os
import time
import dateutil.parser as dateparse
......@@ -33,11 +34,14 @@ from django.utils import six
import patchwork.tests.test_series as test_series
from patchwork.tests.test_user import TestUser
from patchwork.tests.utils import TestSeries
from patchwork.tests.utils import TestSeries, read_mail
from patchwork.models import (
Series, Patch, SeriesRevision, Test, TestResult, TestState, State, Person
Series, Patch, SeriesRevision, Test, TestResult, TestState, State,
Project, Person
from patchwork.serializers import SeriesSerializer
from patchwork.bin.parsemail import parse_mail
entry_points = {
'/': {
......@@ -445,6 +449,60 @@ class EventTest(APITestBase):
self.assertEqual(events['count'], 1)
class MsgidsLookupTest(APITestBase):
msgid_url = '/msgids/{}/'
def setUp(self):
super(MsgidsLookupTest, self).setUp()
self.revision = SeriesRevision.objects.get(series=self.series)
def testSingleSeriesSingleRevisionLookup(self):
response = self.get_json(self.msgid_url.format(self.patch.msgid))
self.assertEquals(len(response), 1) # just one matching patch
self.assertEquals(response[0]['project_id'], self.patch.project_id)
self.assertEquals(response[0]['series_id'], self.patch.series().id)
self.assertEquals(response[0]['revision_ids'], [self.revision.version])
def testUnbracketedLookup(self):
msgid = self.patch.msgid
stripped_msgid = msgid.strip('<>')
self.assertTrue('<' in msgid)
self.assertTrue('<' not in stripped_msgid)
response1 = self.get_json(self.msgid_url.format(msgid))
response2 = self.get_json(self.msgid_url.format(stripped_msgid))
self.assertTrue(len(response1) != 0)
self.assertEquals(response1, response2)
def testSingleSeriesMultipleRevisionsLookup(self):
rev2 = self.revision.duplicate()
response = self.get_json(self.msgid_url.format(self.patch.msgid))
self.assertEquals(len(response), 1) # just one matching patch
self.assertEquals(response[0]['revision_ids'], # multiple revs
[self.revision.version, rev2.version])
def testMulitpleSeriesSingleRevisionsLookup(self):
project2 = Project(linkname='project-two',
name='Project Two',
# read the same emails but post them in the new project
for filename in self.mails:
mail = read_mail(os.path.join('series', filename), project2)
response = self.get_json(self.msgid_url.format(self.patch.msgid))
self.assertEquals(len(response), 2)
class TestResultTest(APITestBase):
rev_url = '/series/%(series_id)s/revisions/%(version)s/test-results/'
patch_url = '/patches/%(patch_id)s/test-results/'
......@@ -89,6 +89,8 @@ urlpatterns = [
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(event_router.urls)),
url(r'^api/1.0/msgids/(?P<msgid>[^/]+)/$', api.msgid,
# project views:
url(r'^$', patchwork.views.base.projects,
......@@ -23,7 +23,7 @@ from django.core.exceptions import FieldDoesNotExist, PermissionDenied
from django.conf import settings
from django.core import mail
from django.db.models import Q
from django.http import HttpResponse
from django.http import HttpResponse, JsonResponse
from patchwork.tasks import send_reviewer_notification
from patchwork.models import (Project, Series, SeriesRevision, Patch, EventLog,
State, Test, TestResult, TestState, Person,
......@@ -540,6 +540,27 @@ class PatchViewSet(mixins.ListModelMixin,
return patch_mbox(request, pk)
def msgid(request, msgid):
output = []
if not (msgid.startswith('<') and msgid.endswith('>')):
msgid = '<{}>'.format(msgid)
patches = Patch.objects.filter(msgid=msgid)
for patch in patches:
series = patch.series()
revisions = series.revisions().filter(patches=patch)
desc = {'patch_id':,
'project_id': patch.project_id,
'revision_ids': [rev.version for rev in revisions]}
output += [desc]
return JsonResponse(output, safe=False)
class PatchResultViewSet(viewsets.ViewSet, ResultMixin):
permission_classes = (MaintainerPermission, )
authentication_classes = (BasicAuthentication, )
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