From nobody Tue May 13 13:04:09 2025 Delivered-To: importer2@patchew.org Received-SPF: pass (zoho.com: domain of redhat.com designates 209.132.183.28 as permitted sender) client-ip=209.132.183.28; envelope-from=patchew-devel-bounces@redhat.com; helo=mx1.redhat.com; Authentication-Results: mx.zohomail.com; spf=pass (zoho.com: domain of redhat.com designates 209.132.183.28 as permitted sender) smtp.mailfrom=patchew-devel-bounces@redhat.com; dmarc=pass(p=none dis=none) header.from=redhat.com ARC-Seal: i=1; a=rsa-sha256; t=1557925103; cv=none; d=zoho.com; s=zohoarc; b=MV3Mw8GrzW0FRWHd62ud+59JzHJJzJ7w4FNIf1eQIrfLpVCINvTLD9xZzISkVLJvc1kgXFjj61XvQGaYoE1VdqbAcE1IkA7yXoXmA0gD2omV9ZkBWwN0E4uAlS2TwMa6XHOcuD4o5bbk6NehQagn9uNQ3/lwLpYtdG2xYcqQzLw= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zoho.com; s=zohoarc; t=1557925103; h=Content-Type:Content-Transfer-Encoding:Date:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:To:ARC-Authentication-Results; bh=EMOkIeNHtayq0yWmMg+ToBx45Z9Pa5vVHb5XhqOM8QM=; b=DSqqWIFTXczp+/tImZR6ZqCO7ChaZ0RXa9yU+88B82jaFvxnnJCz7Jzdffti5f6ofhgD6nYRtOrY654/E2jrC5tQsfT8A8bfujt2G0NJL8FejGRUwCvpE+DKq4qvamaJbAyRsNtKP31o9Pf0PL7vUP4zcd7t3CQPWKJZ353Mxg0= ARC-Authentication-Results: i=1; mx.zoho.com; spf=pass (zoho.com: domain of redhat.com designates 209.132.183.28 as permitted sender) smtp.mailfrom=patchew-devel-bounces@redhat.com; dmarc=pass header.from= (p=none dis=none) header.from= Return-Path: Received: from mx1.redhat.com (mx1.redhat.com [209.132.183.28]) by mx.zohomail.com with SMTPS id 1557925103351559.4929059727525; Wed, 15 May 2019 05:58:23 -0700 (PDT) Received: from smtp.corp.redhat.com (int-mx07.intmail.prod.int.phx2.redhat.com [10.5.11.22]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 030083001C62; Wed, 15 May 2019 12:58:22 +0000 (UTC) Received: from colo-mx.corp.redhat.com (colo-mx01.intmail.prod.int.phx2.redhat.com [10.5.11.20]) by smtp.corp.redhat.com (Postfix) with ESMTPS id EAF271001E61; Wed, 15 May 2019 12:58:21 +0000 (UTC) Received: from lists01.pubmisc.prod.ext.phx2.redhat.com (lists01.pubmisc.prod.ext.phx2.redhat.com [10.5.19.33]) by colo-mx.corp.redhat.com (Postfix) with ESMTP id E1C5418089CB; Wed, 15 May 2019 12:58:21 +0000 (UTC) Received: from smtp.corp.redhat.com (int-mx02.intmail.prod.int.phx2.redhat.com [10.5.11.12]) by lists01.pubmisc.prod.ext.phx2.redhat.com (8.13.8/8.13.8) with ESMTP id x4FCwL65012629 for ; Wed, 15 May 2019 08:58:21 -0400 Received: by smtp.corp.redhat.com (Postfix) id 150C460CDA; Wed, 15 May 2019 12:58:21 +0000 (UTC) Received: from donizetti.redhat.com (ovpn-112-34.ams2.redhat.com [10.36.112.34]) by smtp.corp.redhat.com (Postfix) with ESMTP id ABB1C60CC0 for ; Wed, 15 May 2019 12:58:16 +0000 (UTC) From: Paolo Bonzini To: patchew-devel@redhat.com Date: Wed, 15 May 2019 14:57:58 +0200 Message-Id: <20190515125808.27716-2-pbonzini@redhat.com> In-Reply-To: <20190515125808.27716-1-pbonzini@redhat.com> References: <20190515125808.27716-1-pbonzini@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.79 on 10.5.11.12 X-loop: patchew-devel@redhat.com Subject: [Patchew-devel] [PATCH 01/11] rest: add PATCH support for series and messages X-BeenThere: patchew-devel@redhat.com X-Mailman-Version: 2.1.12 Precedence: junk List-Id: Patchew development and discussion list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Content-Transfer-Encoding: quoted-printable Sender: patchew-devel-bounces@redhat.com Errors-To: patchew-devel-bounces@redhat.com X-Scanned-By: MIMEDefang 2.84 on 10.5.11.22 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.43]); Wed, 15 May 2019 12:58:22 +0000 (UTC) Content-Type: text/plain; charset="utf-8" Allow modifying those fields that are not directly derived from the mbox. The complication is that PATCH and POST now support a different set of fields (POST allows writing arbitrary data to the message) so we need to use get_serializer_class() instead of the simpler serializer_class. Signed-off-by: Paolo Bonzini --- api/rest.py | 52 +++++++++++++++++++++++++++++++++------------- tests/test_rest.py | 32 ++++++++++++++++++++++++++++ 2 files changed, 70 insertions(+), 14 deletions(-) diff --git a/api/rest.py b/api/rest.py index 1bd637d..3b6ab2f 100644 --- a/api/rest.py +++ b/api/rest.py @@ -22,9 +22,10 @@ from rest_framework.decorators import action from rest_framework.fields import SerializerMethodField, CharField, JSONFi= eld, EmailField, ListField from rest_framework.relations import HyperlinkedIdentityField from rest_framework.response import Response +from rest_framework.views import APIView import rest_framework from mbox import addr_db_to_rest, MboxMessage -from rest_framework.parsers import JSONParser, BaseParser +from rest_framework.parsers import BaseParser =20 SEARCH_PARAM =3D 'q' =20 @@ -274,7 +275,8 @@ class AddressSerializer(serializers.Serializer): class BaseMessageSerializer(serializers.ModelSerializer): class Meta: model =3D Message - fields =3D ('resource_uri', 'message_id', 'subject', 'date', 'send= er', 'recipients', 'tags') + read_only_fields =3D ('resource_uri', 'message_id', 'subject', 'da= te', 'sender', 'recipients') + fields =3D read_only_fields + ('tags', ) =20 resource_uri =3D HyperlinkedMessageField(view_name=3D'messages-detail') recipients =3D AddressSerializer(many=3DTrue) @@ -300,7 +302,7 @@ class BaseMessageViewSet(mixins.ListModelMixin, viewset= s.GenericViewSet): =20 =20 # a (project, message_id) tuple is unique, so we can always retrieve an ob= ject -class ProjectMessagesViewSetMixin(mixins.RetrieveModelMixin): +class ProjectMessagesViewSetMixin(mixins.RetrieveModelMixin, mixins.Update= ModelMixin): def get_queryset(self): return self.queryset.filter(project=3Dself.kwargs['projects_pk']) =20 @@ -342,10 +344,12 @@ class PatchSerializer(BaseMessageSerializer): class SeriesSerializer(BaseMessageSerializer): class Meta: model =3D Message - fields =3D ('resource_uri',) + BaseMessageSerializer.Meta.fields += ( - 'message', 'stripped_subject', 'last_comment_date', 'last_repl= y_date', - 'is_complete', 'is_merged', 'num_patches', 'total_patches', 'r= esults', + subclass_read_only_fields =3D ('message', 'stripped_subject', 'num= _patches', + 'total_patches', 'results') + fields =3D BaseMessageSerializer.Meta.fields + subclass_read_only_= fields + ( + 'last_comment_date', 'last_reply_date', 'is_complete', 'is_mer= ged', 'is_obsolete', 'is_tested', 'is_reviewed', 'maintainers') + read_only_fields =3D BaseMessageSerializer.Meta.read_only_fields += subclass_read_only_fields =20 resource_uri =3D HyperlinkedMessageField(view_name=3D'series-detail') message =3D HyperlinkedMessageField(view_name=3D'messages-detail') @@ -416,7 +420,8 @@ class SeriesViewSet(BaseMessageViewSet): =20 =20 class ProjectSeriesViewSet(ProjectMessagesViewSetMixin, - SeriesViewSet, mixins.DestroyModelMixin): + SeriesViewSet, mixins.UpdateModelMixin, + mixins.DestroyModelMixin): def collect_patches(self, series): if series.is_patch: patches =3D [series] @@ -456,11 +461,12 @@ class ProjectSeriesViewSet(ProjectMessagesViewSetMixi= n, =20 # Messages =20 -# TODO: add POST endpoint connected to email plugin? class MessageSerializer(BaseMessageSerializer): class Meta: model =3D Message fields =3D BaseMessageSerializer.Meta.fields + ('mbox', ) + read_only_fields =3D BaseMessageSerializer.Meta.read_only_fields += ('mbox', ) + mbox =3D CharField() =20 def get_fields(self): @@ -476,6 +482,14 @@ class MessageSerializer(BaseMessageSerializer): return fields =20 =20 +class MessageCreationSerializer(BaseMessageSerializer): + class Meta: + model =3D Message + fields =3D BaseMessageSerializer.Meta.fields + ('mbox', ) + read_only_fields =3D [] + + mbox =3D CharField() + class StaticTextRenderer(renderers.BaseRenderer): media_type =3D 'text/plain' format =3D 'mbox' @@ -497,10 +511,15 @@ class MessagePlainTextParser(BaseParser): return MboxMessage(data).get_json() =20 =20 -class ProjectMessagesViewSet(ProjectMessagesViewSetMixin, - BaseMessageViewSet, mixins.CreateModelMixin): - serializer_class =3D MessageSerializer - parser_classes =3D (JSONParser, MessagePlainTextParser, ) +class ProjectMessagesViewSet(ProjectMessagesViewSetMixin, BaseMessageViewS= et, + mixins.CreateModelMixin, mixins.UpdateModelMi= xin): + parser_classes =3D APIView.parser_classes + [MessagePlainTextParser] + + def get_serializer_class(self, *args, **kwargs): + if self.request.method =3D=3D 'POST': + return MessageCreationSerializer + else: + return MessageSerializer =20 @action(detail=3DTrue, renderer_classes=3D[StaticTextRenderer]) def mbox(self, request, *args, **kwargs): @@ -519,9 +538,14 @@ class ProjectMessagesViewSet(ProjectMessagesViewSetMix= in, =20 =20 class MessagesViewSet(BaseMessageViewSet): - serializer_class =3D MessageSerializer permission_classes =3D (permissions.IsAuthenticatedOrReadOnly,) - parser_classes =3D (JSONParser, MessagePlainTextParser, ) + parser_classes =3D APIView.parser_classes + [MessagePlainTextParser] + + def get_serializer_class(self, *args, **kwargs): + if self.request.method =3D=3D 'POST': + return MessageCreationSerializer + else: + return MessageSerializer =20 def create(self, request, *args, **kwargs): m =3D MboxMessage(request.data['mbox']) diff --git a/tests/test_rest.py b/tests/test_rest.py index dd38035..d6cbf62 100755 --- a/tests/test_rest.py +++ b/tests/test_rest.py @@ -353,6 +353,24 @@ class RestTest(PatchewTestCase): self.assertEqual(resp.data['subject'], "[Qemu-devel] [PATCH v2 10/= 27] imx_fec: Reserve full 4K " "page for the register file") =20 + def test_patch_message(self): + the_tags =3D ['Reviewed-by: Paolo Bonzini