from django.db.models import Q
from rest_framework.generics import ListAPIView
from rest_framework.generics import RetrieveAPIView
-from rest_framework.serializers import HyperlinkedModelSerializer
from rest_framework.serializers import SerializerMethodField
+from patchwork.api.base import BaseHyperlinkedModelSerializer
from patchwork.api.base import PatchworkPermission
from patchwork.api.filters import BundleFilterSet
from patchwork.api.embedded import PatchSerializer
from patchwork.models import Bundle
-class BundleSerializer(HyperlinkedModelSerializer):
+class BundleSerializer(BaseHyperlinkedModelSerializer):
+ web_url = SerializerMethodField()
project = ProjectSerializer(read_only=True)
mbox = SerializerMethodField()
owner = UserSerializer(read_only=True)
patches = PatchSerializer(many=True, read_only=True)
+ def get_web_url(self, instance):
+ request = self.context.get('request')
+ return request.build_absolute_uri(instance.get_absolute_url())
+
def get_mbox(self, instance):
request = self.context.get('request')
return request.build_absolute_uri(instance.get_mbox_url())
class Meta:
model = Bundle
- fields = ('id', 'url', 'project', 'name', 'owner', 'patches',
- 'public', 'mbox')
+ fields = ('id', 'url', 'web_url', 'project', 'name', 'owner',
+ 'patches', 'public', 'mbox')
read_only_fields = ('owner', 'patches', 'mbox')
+ versioned_fields = {
+ '1.1': ('web_url', ),
+ }
extra_kwargs = {
'url': {'view_name': 'api-bundle-detail'},
}
class CommentListSerializer(BaseHyperlinkedModelSerializer):
+ web_url = SerializerMethodField()
subject = SerializerMethodField()
headers = SerializerMethodField()
submitter = PersonSerializer(read_only=True)
+ def get_web_url(self, instance):
+ request = self.context.get('request')
+ return request.build_absolute_uri(instance.get_absolute_url())
+
def get_subject(self, comment):
return email.parser.Parser().parsestr(comment.headers,
True).get('Subject', '')
class Meta:
model = Comment
- fields = ('id', 'msgid', 'date', 'subject', 'submitter', 'content',
- 'headers')
+ fields = ('id', 'web_url', 'msgid', 'date', 'subject', 'submitter',
+ 'content', 'headers')
read_only_fields = fields
+ versioned_fields = {
+ '1.1': ('web_url', ),
+ }
class CommentList(ListAPIView):
class CoverLetterListSerializer(BaseHyperlinkedModelSerializer):
+ web_url = SerializerMethodField()
project = ProjectSerializer(read_only=True)
submitter = PersonSerializer(read_only=True)
mbox = SerializerMethodField()
series = SeriesSerializer(many=True, read_only=True)
comments = SerializerMethodField()
+ def get_web_url(self, instance):
+ request = self.context.get('request')
+ return request.build_absolute_uri(instance.get_absolute_url())
+
def get_mbox(self, instance):
request = self.context.get('request')
return request.build_absolute_uri(instance.get_mbox_url())
class Meta:
model = CoverLetter
- fields = ('id', 'url', 'project', 'msgid', 'date', 'name', 'submitter',
- 'mbox', 'series', 'comments')
+ fields = ('id', 'url', 'web_url', 'project', 'msgid', 'date', 'name',
+ 'submitter', 'mbox', 'series', 'comments')
read_only_fields = fields
versioned_fields = {
- '1.1': ('mbox', 'comments'),
+ '1.1': ('web_url', 'mbox', 'comments'),
}
extra_kwargs = {
'url': {'view_name': 'api-cover-detail'},
class MboxMixin(BaseHyperlinkedModelSerializer):
- """Embed an link to the mbox URL.
+ """Embed a link to the mbox URL.
This field is just way too useful to leave out of even the embedded
serialization.
return request.build_absolute_uri(instance.get_mbox_url())
-class BundleSerializer(MboxMixin, BaseHyperlinkedModelSerializer):
+class WebURLMixin(BaseHyperlinkedModelSerializer):
+ """Embed a link to the web URL."""
+
+ web_url = SerializerMethodField()
+
+ def get_web_url(self, instance):
+ request = self.context.get('request')
+ return request.build_absolute_uri(instance.get_absolute_url())
+
+
+class BundleSerializer(MboxMixin, WebURLMixin, BaseHyperlinkedModelSerializer):
class Meta:
model = models.Bundle
- fields = ('id', 'url', 'name', 'mbox')
+ fields = ('id', 'url', 'web_url', 'name', 'mbox')
read_only_fields = fields
+ versioned_field = {
+ '1.1': ('web_url', ),
+ }
extra_kwargs = {
'url': {'view_name': 'api-bundle-detail'},
}
}
-class CoverLetterSerializer(MboxMixin, BaseHyperlinkedModelSerializer):
+class CoverLetterSerializer(MboxMixin, WebURLMixin,
+ BaseHyperlinkedModelSerializer):
class Meta:
model = models.CoverLetter
- fields = ('id', 'url', 'msgid', 'date', 'name', 'mbox')
+ fields = ('id', 'url', 'web_url', 'msgid', 'date', 'name', 'mbox')
read_only_fields = fields
versioned_field = {
- '1.1': ('mbox', ),
+ '1.1': ('web_url', 'mbox', ),
}
extra_kwargs = {
'url': {'view_name': 'api-cover-detail'},
}
-class PatchSerializer(MboxMixin, BaseHyperlinkedModelSerializer):
+class PatchSerializer(MboxMixin, WebURLMixin, BaseHyperlinkedModelSerializer):
class Meta:
model = models.Patch
- fields = ('id', 'url', 'msgid', 'date', 'name', 'mbox')
+ fields = ('id', 'url', 'web_url', 'msgid', 'date', 'name', 'mbox')
read_only_fields = fields
+ versioned_field = {
+ '1.1': ('web_url', ),
+ }
extra_kwargs = {
'url': {'view_name': 'api-patch-detail'},
}
}
-class SeriesSerializer(MboxMixin, BaseHyperlinkedModelSerializer):
+class SeriesSerializer(MboxMixin, WebURLMixin,
+ BaseHyperlinkedModelSerializer):
class Meta:
model = models.Series
fields = ('id', 'url', 'date', 'name', 'version', 'mbox')
read_only_fields = fields
+ versioned_field = {
+ '1.1': ('web_url', ),
+ }
extra_kwargs = {
'url': {'view_name': 'api-series-detail'},
}
class PatchListSerializer(BaseHyperlinkedModelSerializer):
+ web_url = SerializerMethodField()
project = ProjectSerializer(read_only=True)
state = StateField()
submitter = PersonSerializer(read_only=True)
checks = SerializerMethodField()
tags = SerializerMethodField()
+ def get_web_url(self, instance):
+ request = self.context.get('request')
+ return request.build_absolute_uri(instance.get_absolute_url())
+
def get_mbox(self, instance):
request = self.context.get('request')
return request.build_absolute_uri(instance.get_mbox_url())
class Meta:
model = Patch
- fields = ('id', 'url', 'project', 'msgid', 'date', 'name',
+ fields = ('id', 'url', 'web_url', 'project', 'msgid', 'date', 'name',
'commit_ref', 'pull_url', 'state', 'archived', 'hash',
'submitter', 'delegate', 'mbox', 'series', 'comments',
'check', 'checks', 'tags')
- read_only_fields = ('project', 'msgid', 'date', 'name', 'hash',
- 'submitter', 'mbox', 'mbox', 'series', 'comments',
+ read_only_fields = ('web_url', 'project', 'msgid', 'date', 'name',
+ 'hash', 'submitter', 'mbox', 'series', 'comments',
'check', 'checks', 'tags')
versioned_fields = {
- '1.1': ('comments', ),
+ '1.1': ('comments', 'web_url'),
}
extra_kwargs = {
'url': {'view_name': 'api-patch-detail'},
from rest_framework.generics import ListAPIView
from rest_framework.generics import RetrieveAPIView
-from rest_framework.serializers import HyperlinkedModelSerializer
from rest_framework.serializers import SerializerMethodField
+from patchwork.api.base import BaseHyperlinkedModelSerializer
from patchwork.api.base import PatchworkPermission
from patchwork.api.filters import SeriesFilterSet
from patchwork.api.embedded import CoverLetterSerializer
from patchwork.models import Series
-class SeriesSerializer(HyperlinkedModelSerializer):
+class SeriesSerializer(BaseHyperlinkedModelSerializer):
+ web_url = SerializerMethodField()
project = ProjectSerializer(read_only=True)
submitter = PersonSerializer(read_only=True)
mbox = SerializerMethodField()
cover_letter = CoverLetterSerializer(read_only=True)
patches = PatchSerializer(read_only=True, many=True)
+ def get_web_url(self, instance):
+ request = self.context.get('request')
+ return request.build_absolute_uri(instance.get_absolute_url())
+
def get_mbox(self, instance):
request = self.context.get('request')
return request.build_absolute_uri(instance.get_mbox_url())
class Meta:
model = Series
- fields = ('id', 'url', 'project', 'name', 'date', 'submitter',
- 'version', 'total', 'received_total', 'received_all',
- 'mbox', 'cover_letter', 'patches')
+ fields = ('id', 'url', 'web_url', 'project', 'name', 'date',
+ 'submitter', 'version', 'total', 'received_total',
+ 'received_all', 'mbox', 'cover_letter', 'patches')
read_only_fields = ('date', 'submitter', 'total', 'received_total',
'received_all', 'mbox', 'cover_letter', 'patches')
+ versioned_fields = {
+ '1.1': ('web_url', ),
+ }
extra_kwargs = {
'url': {'view_name': 'api-series-detail'},
}
class CoverLetter(SeriesMixin, Submission):
+ def get_absolute_url(self):
+ return reverse('cover-detail', kwargs={'cover_id': self.id})
+
def get_mbox_url(self):
return reverse('cover-mbox', kwargs={'cover_id': self.id})
related_query_name='comment',
on_delete=models.CASCADE)
+ def get_absolute_url(self):
+ return reverse('comment-redirect', kwargs={'comment_id': self.id})
+
def save(self, *args, **kwargs):
super(Comment, self).save(*args, **kwargs)
if hasattr(self.submission, 'patch'):
patch=patch,
number=number)
+ def get_absolute_url(self):
+ # TODO(stephenfin): We really need a proper series view
+ return reverse('patch-list',
+ kwargs={'project_id': self.project.linkname}) + (
+ '?series=%d' % self.id)
+
def get_mbox_url(self):
return reverse('series-mbox', kwargs={'series_id': self.id})
fixtures = ['default_tags']
@staticmethod
- def api_url(item=None):
+ def api_url(item=None, version=None):
+ kwargs = {}
+ if version:
+ kwargs['version'] = version
+
if item is None:
- return reverse('api-bundle-list')
- return reverse('api-bundle-detail', args=[item])
+ return reverse('api-bundle-list', kwargs=kwargs)
+ kwargs['pk'] = item
+ return reverse('api-bundle-detail', kwargs=kwargs)
def assertSerialized(self, bundle_obj, bundle_json):
self.assertEqual(bundle_obj.id, bundle_json['id'])
self.assertEqual(bundle_obj.name, bundle_json['name'])
self.assertEqual(bundle_obj.public, bundle_json['public'])
self.assertIn(bundle_obj.get_mbox_url(), bundle_json['mbox'])
+ self.assertIn(bundle_obj.get_absolute_url(), bundle_json['web_url'])
# nested fields
resp = self.client.get(self.api_url(), {'owner': 'otheruser'})
self.assertEqual(0, len(resp.data))
+ def test_list_version_1_0(self):
+ """Validate that newer fields are dropped for older API versions."""
+ create_bundle(public=True)
+
+ resp = self.client.get(self.api_url(version='1.0'))
+ self.assertEqual(status.HTTP_200_OK, resp.status_code)
+ self.assertEqual(1, len(resp.data))
+ self.assertIn('url', resp.data[0])
+ self.assertNotIn('web_url', resp.data[0])
+
def test_detail(self):
"""Validate we can get a specific bundle."""
bundle = create_bundle(public=True)
self.assertEqual(status.HTTP_200_OK, resp.status_code)
self.assertSerialized(bundle, resp.data)
+ def test_detail_version_1_0(self):
+ bundle = create_bundle(public=True)
+
+ resp = self.client.get(self.api_url(bundle.id, version='1.0'))
+ self.assertIn('url', resp.data)
+ self.assertNotIn('web_url', resp.data)
+
def test_create_update_delete(self):
"""Ensure creates, updates and deletes aren't allowed"""
user = create_maintainer()
self.assertEqual(cover_obj.id, cover_json['id'])
self.assertEqual(cover_obj.name, cover_json['name'])
self.assertIn(cover_obj.get_mbox_url(), cover_json['mbox'])
+ self.assertIn(cover_obj.get_absolute_url(), cover_json['web_url'])
self.assertIn('comments', cover_json)
# nested fields
'submitter': 'test@example.org'})
self.assertEqual(0, len(resp.data))
- # test old version of API
+ def test_list_version_1_0(self):
+ create_cover()
+
resp = self.client.get(self.api_url(version='1.0'))
self.assertEqual(status.HTTP_200_OK, resp.status_code)
self.assertEqual(1, len(resp.data))
+ self.assertIn('url', resp.data[0])
self.assertNotIn('mbox', resp.data[0])
+ self.assertNotIn('web_url', resp.data[0])
def test_detail(self):
"""Validate we can get a specific cover letter."""
for key, value in parsed_headers.items():
self.assertIn(value, resp.data['headers'][key])
- # test old version of API
- resp = self.client.get(self.api_url(cover_obj.id, version='1.0'))
+ def test_detail_version_1_0(self):
+ cover = create_cover()
+
+ resp = self.client.get(self.api_url(cover.id, version='1.0'))
+ self.assertIn('url', resp.data)
+ self.assertNotIn('web_url', resp.data)
self.assertNotIn('comments', resp.data)
def test_create_update_delete(self):
self.assertEqual(patch_obj.msgid, patch_json['msgid'])
self.assertEqual(patch_obj.state.slug, patch_json['state'])
self.assertIn(patch_obj.get_mbox_url(), patch_json['mbox'])
+ self.assertIn(patch_obj.get_absolute_url(), patch_json['web_url'])
self.assertIn('comments', patch_json)
# nested fields
('state', 'new')])
self.assertEqual(2, len(resp.data))
+ def test_list_version_1_0(self):
+ create_patch()
+
+ resp = self.client.get(self.api_url(version='1.0'))
+ self.assertEqual(status.HTTP_200_OK, resp.status_code)
+ self.assertEqual(1, len(resp.data))
+ self.assertIn('url', resp.data[0])
+ self.assertNotIn('web_url', resp.data[0])
+
def test_detail(self):
"""Validate we can get a specific patch."""
patch = create_patch(
self.assertEqual(patch.diff, resp.data['diff'])
self.assertEqual(0, len(resp.data['tags']))
- # test old version of API
+ def test_detail_version_1_0(self):
+ patch = create_patch()
+
resp = self.client.get(self.api_url(item=patch.id, version='1.0'))
+ self.assertIn('url', resp.data)
+ self.assertNotIn('web_url', resp.data)
self.assertNotIn('comments', resp.data)
def test_create(self):
self.assertEqual(series_obj.received_total,
series_json['received_total'])
self.assertIn(series_obj.get_mbox_url(), series_json['mbox'])
+ self.assertIn(series_obj.get_absolute_url(), series_json['web_url'])
# nested fields
'submitter': 'test@example.org'})
self.assertEqual(0, len(resp.data))
+ def test_list_old_version(self):
+ """Validate that newer fields are dropped for older API versions."""
+ create_series()
+
+ resp = self.client.get(self.api_url(version='1.0'))
+ self.assertEqual(status.HTTP_200_OK, resp.status_code)
+ self.assertEqual(1, len(resp.data))
+ self.assertIn('url', resp.data[0])
+ self.assertNotIn('web_url', resp.data[0])
+
def test_detail(self):
"""Validate we can get a specific series."""
cover = create_cover()
self.assertEqual(status.HTTP_200_OK, resp.status_code)
self.assertSerialized(series, resp.data)
+ def test_detail_version_1_0(self):
+ series = create_series()
+
+ resp = self.client.get(self.api_url(series.id, version='1.0'))
+ self.assertIn('url', resp.data)
+ self.assertNotIn('web_url', resp.data)
+
def test_create_update_delete(self):
"""Ensure creates, updates and deletes aren't allowed"""
user = create_maintainer()