[RFC/PATCH 1/3] api: add addressed field and detail endpoint for patch comments
Raxel Gutierrez
raxel at google.com
Fri Jul 23 07:35:41 AEST 2021
Add addressed boolean field to PatchComment to be able to distinguish
between them in the patch-detail page. Change PatchComment to have same
is_editable from the related patch.
Add endpoint for patch comments at api/.../comments/<comment_id>.
The endpoint will make it possible to use the REST API to update the new
addressed field for patch comments with JavaScript on the client side.
In the process of these changes, clean up use of CurrentPatchDefault so
that it exists in base.py and can be used throughout the API.
Signed-off-by: Raxel Gutierrez <raxel at google.com>
---
patchwork/api/base.py | 24 +++++-
patchwork/api/check.py | 21 +----
patchwork/api/comment.py | 76 +++++++++++++++----
.../migrations/0045_patchcomment_addressed.py | 18 +++++
patchwork/models.py | 3 +-
patchwork/urls.py | 7 +-
6 files changed, 111 insertions(+), 38 deletions(-)
create mode 100644 patchwork/migrations/0045_patchcomment_addressed.py
diff --git a/patchwork/api/base.py b/patchwork/api/base.py
index 89a4311..856fbd3 100644
--- a/patchwork/api/base.py
+++ b/patchwork/api/base.py
@@ -3,6 +3,7 @@
#
# SPDX-License-Identifier: GPL-2.0-or-later
+import rest_framework
from django.conf import settings
from django.shortcuts import get_object_or_404
@@ -15,6 +16,24 @@ from rest_framework.serializers import HyperlinkedModelSerializer
from patchwork.api import utils
+DRF_VERSION = tuple(int(x) for x in rest_framework.__version__.split('.'))
+
+
+if DRF_VERSION > (3, 11):
+ class CurrentPatchDefault(object):
+ requires_context = True
+
+ def __call__(self, serializer_field):
+ return serializer_field.context['request'].patch
+else:
+ class CurrentPatchDefault(object):
+ def set_context(self, serializer_field):
+ self.patch = serializer_field.context['request'].patch
+
+ def __call__(self):
+ return self.patch
+
+
class LinkHeaderPagination(PageNumberPagination):
"""Provide pagination based on rfc5988.
@@ -44,7 +63,10 @@ class LinkHeaderPagination(PageNumberPagination):
class PatchworkPermission(permissions.BasePermission):
- """This permission works for Project and Patch model objects"""
+ """
+ This permission works for Project, Patch, and PatchComment
+ model objects
+ """
def has_object_permission(self, request, view, obj):
# read only for everyone
if request.method in permissions.SAFE_METHODS:
diff --git a/patchwork/api/check.py b/patchwork/api/check.py
index a6bf5f8..fde6b61 100644
--- a/patchwork/api/check.py
+++ b/patchwork/api/check.py
@@ -6,7 +6,7 @@
from django.http import Http404
from django.http.request import QueryDict
from django.shortcuts import get_object_or_404
-import rest_framework
+
from rest_framework.exceptions import PermissionDenied
from rest_framework.generics import ListCreateAPIView
from rest_framework.generics import RetrieveAPIView
@@ -17,30 +17,13 @@ from rest_framework.serializers import ValidationError
from patchwork.api.base import CheckHyperlinkedIdentityField
from patchwork.api.base import MultipleFieldLookupMixin
+from patchwork.api.base import CurrentPatchDefault
from patchwork.api.embedded import UserSerializer
from patchwork.api.filters import CheckFilterSet
from patchwork.models import Check
from patchwork.models import Patch
-DRF_VERSION = tuple(int(x) for x in rest_framework.__version__.split('.'))
-
-
-if DRF_VERSION > (3, 11):
- class CurrentPatchDefault(object):
- requires_context = True
-
- def __call__(self, serializer_field):
- return serializer_field.context['request'].patch
-else:
- class CurrentPatchDefault(object):
- def set_context(self, serializer_field):
- self.patch = serializer_field.context['request'].patch
-
- def __call__(self):
- return self.patch
-
-
class CheckSerializer(HyperlinkedModelSerializer):
url = CheckHyperlinkedIdentityField('api-check-detail')
diff --git a/patchwork/api/comment.py b/patchwork/api/comment.py
index 43b26c6..7f1e401 100644
--- a/patchwork/api/comment.py
+++ b/patchwork/api/comment.py
@@ -5,12 +5,17 @@
import email.parser
+from django.shortcuts import get_object_or_404
from django.http import Http404
from rest_framework.generics import ListAPIView
+from rest_framework.generics import RetrieveUpdateDestroyAPIView
+from rest_framework.serializers import HiddenField
from rest_framework.serializers import SerializerMethodField
from patchwork.api.base import BaseHyperlinkedModelSerializer
+from patchwork.api.base import MultipleFieldLookupMixin
from patchwork.api.base import PatchworkPermission
+from patchwork.api.base import CurrentPatchDefault
from patchwork.api.embedded import PersonSerializer
from patchwork.models import Cover
from patchwork.models import CoverComment
@@ -55,6 +60,9 @@ class BaseCommentListSerializer(BaseHyperlinkedModelSerializer):
'1.1': ('web_url', ),
'1.2': ('list_archive_url',),
}
+ extra_kwargs = {
+ 'url': {'view_name': 'api-patch-comment-detail'},
+ }
class CoverCommentListSerializer(BaseCommentListSerializer):
@@ -66,15 +74,47 @@ class CoverCommentListSerializer(BaseCommentListSerializer):
versioned_fields = BaseCommentListSerializer.Meta.versioned_fields
-class PatchCommentListSerializer(BaseCommentListSerializer):
+class PatchCommentSerializer(BaseCommentListSerializer):
+
+ patch = HiddenField(default=CurrentPatchDefault())
class Meta:
model = PatchComment
- fields = BaseCommentListSerializer.Meta.fields
- read_only_fields = fields
+ fields = BaseCommentListSerializer.Meta.fields + (
+ 'patch', 'addressed')
+ read_only_fields = fields[:-1] # able to write to addressed field
+ extra_kwargs = {
+ 'url': {'view_name': 'api-patch-comment-detail'}
+ }
versioned_fields = BaseCommentListSerializer.Meta.versioned_fields
+class PatchCommentMixin(object):
+
+ permission_classes = (PatchworkPermission,)
+ serializer_class = PatchCommentSerializer
+
+ def get_object(self):
+ queryset = self.filter_queryset(self.get_queryset())
+ comment_id = self.kwargs['comment_id']
+ try:
+ obj = queryset.get(id=int(comment_id))
+ except (ValueError, PatchComment.DoesNotExist):
+ obj = get_object_or_404(queryset, linkname=comment_id)
+ self.kwargs['comment_id'] = obj.id
+ self.check_object_permissions(self.request, obj)
+ return obj
+
+ def get_queryset(self):
+ patch_id = self.kwargs['patch_id']
+ if not Patch.objects.filter(id=patch_id).exists():
+ raise Http404
+
+ return PatchComment.objects.filter(
+ patch=patch_id
+ ).select_related('submitter')
+
+
class CoverCommentList(ListAPIView):
"""List cover comments"""
@@ -94,20 +134,24 @@ class CoverCommentList(ListAPIView):
).select_related('submitter')
-class PatchCommentList(ListAPIView):
- """List comments"""
+class PatchCommentList(PatchCommentMixin, ListAPIView):
+ """List patch comments"""
- permission_classes = (PatchworkPermission,)
- serializer_class = PatchCommentListSerializer
search_fields = ('subject',)
ordering_fields = ('id', 'subject', 'date', 'submitter')
ordering = 'id'
- lookup_url_kwarg = 'pk'
-
- def get_queryset(self):
- if not Patch.objects.filter(pk=self.kwargs['pk']).exists():
- raise Http404
-
- return PatchComment.objects.filter(
- patch=self.kwargs['pk']
- ).select_related('submitter')
+ lookup_url_kwarg = 'patch_id'
+
+
+class PatchCommentDetail(PatchCommentMixin, MultipleFieldLookupMixin,
+ RetrieveUpdateDestroyAPIView):
+ """
+ get:
+ Show a patch comment.
+ patch:
+ Update a patch comment.
+ put:
+ Update a patch comment.
+ """
+ lookup_url_kwargs = ('patch_id', 'comment_id')
+ lookup_fields = ('patch_id', 'id')
diff --git a/patchwork/migrations/0045_patchcomment_addressed.py b/patchwork/migrations/0045_patchcomment_addressed.py
new file mode 100644
index 0000000..92e6c4e
--- /dev/null
+++ b/patchwork/migrations/0045_patchcomment_addressed.py
@@ -0,0 +1,18 @@
+# Generated by Django 3.1.12 on 2021-07-16 04:12
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('patchwork', '0044_add_project_linkname_validation'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='patchcomment',
+ name='addressed',
+ field=models.BooleanField(default=False),
+ ),
+ ]
diff --git a/patchwork/models.py b/patchwork/models.py
index 00273da..0a77b23 100644
--- a/patchwork/models.py
+++ b/patchwork/models.py
@@ -693,6 +693,7 @@ class PatchComment(EmailMixin, models.Model):
related_query_name='comment',
on_delete=models.CASCADE,
)
+ addressed = models.BooleanField(default=False)
@property
def list_archive_url(self):
@@ -718,7 +719,7 @@ class PatchComment(EmailMixin, models.Model):
self.patch.refresh_tag_counts()
def is_editable(self, user):
- return False
+ return self.patch.is_editable(user)
class Meta:
ordering = ['date']
diff --git a/patchwork/urls.py b/patchwork/urls.py
index 6ac9b81..81db982 100644
--- a/patchwork/urls.py
+++ b/patchwork/urls.py
@@ -332,10 +332,15 @@ if settings.ENABLE_REST_API:
api_1_1_patterns = [
path(
- 'patches/<pk>/comments/',
+ 'patches/<patch_id>/comments/',
api_comment_views.PatchCommentList.as_view(),
name='api-patch-comment-list',
),
+ path(
+ 'patches/<patch_id>/comments/<comment_id>/',
+ api_comment_views.PatchCommentDetail.as_view(),
+ name='api-patch-comment-detail',
+ ),
path(
'covers/<pk>/comments/',
api_comment_views.CoverCommentList.as_view(),
--
2.32.0.432.gabb21c7263-goog
More information about the Patchwork
mailing list