Re: [PATCH v2 1/2] api: Add comments to patch and cover endpoints

2018-04-16 Thread Veronika Kabatova

- Original Message -
> From: "Stephen Finucane" 
> To: vkaba...@redhat.com, patchwork@lists.ozlabs.org
> Sent: Monday, April 16, 2018 1:15:24 AM
> Subject: Re: [PATCH v2 1/2] api: Add comments to patch and cover endpoints
> 
> On Wed, 2018-04-11 at 19:45 +0200, vkaba...@redhat.com wrote:
> > From: Veronika Kabatova 
> > 
> > Signed-off-by: Veronika Kabatova 
> 
> I thought I'd replied to this already but I clearly didn't. This all
> looks pretty good but I think I might have confused you. It seems with
> this version our patch/cover letter responses gain an entire nested
> version of the comments. This seems overly detailed, given that it's
> not something most people need. What I meant instead is to add a
> comments URL like so:
> 

Yes, that's why I double questioned your idea :)

>   /patch/{patchID}/comments
> 
> ...and include this in the patch response:
> 
>   {
> "id": 123,
> ...
> "comments": "https://www.example.com/api/patches/123/comments";
>   }
> 
> Any reason not to go for this option?
> 

I guess not, will move the comments there.

> Also, if you do end up respinning this just squash tests/migrations in
> with this patch. I'd be applying them together anyway.
> 

Will do.

Thanks,
Veronika

> Stephen
> 
> > ---
> >  patchwork/api/cover.py | 14 ++--
> >  patchwork/api/embedded.py  | 38
> >  ++
> >  patchwork/api/patch.py | 15 ++---
> >  patchwork/models.py|  3 ++
> >  .../notes/comments-api-b7dff6ee4ce04c9b.yaml   |  8 +
> >  5 files changed, 70 insertions(+), 8 deletions(-)
> >  create mode 100644 releasenotes/notes/comments-api-b7dff6ee4ce04c9b.yaml
> > 
> > diff --git a/patchwork/api/cover.py b/patchwork/api/cover.py
> > index fc7ae97..5a6e377 100644
> > --- a/patchwork/api/cover.py
> > +++ b/patchwork/api/cover.py
> > @@ -25,6 +25,7 @@ from rest_framework.serializers import
> > SerializerMethodField
> >  
> >  from patchwork.api.base import BaseHyperlinkedModelSerializer
> >  from patchwork.api.filters import CoverLetterFilter
> > +from patchwork.api.embedded import CommentSerializer
> >  from patchwork.api.embedded import PersonSerializer
> >  from patchwork.api.embedded import ProjectSerializer
> >  from patchwork.api.embedded import SeriesSerializer
> > @@ -58,5 +59,6 @@ class
> > CoverLetterListSerializer(BaseHyperlinkedModelSerializer):
> >  class CoverLetterDetailSerializer(CoverLetterListSerializer):
> >  
> >  headers = SerializerMethodField()
> > +comments = CommentSerializer(many=True, read_only=True)
> >  
> >  def get_headers(self, instance):
> > @@ -65,9 +67,14 @@ class
> > CoverLetterDetailSerializer(CoverLetterListSerializer):
> >  
> >  class Meta:
> >  model = CoverLetter
> > -fields = CoverLetterListSerializer.Meta.fields + ('headers',
> > 'content')
> > +fields = CoverLetterListSerializer.Meta.fields + ('headers',
> > +  'content',
> > +  'comments')
> >  read_only_fields = fields
> >  extra_kwargs = CoverLetterListSerializer.Meta.extra_kwargs
> > +versioned_fields = {
> > +'1.1': ('mbox', 'comments'),
> > +}
> >  
> >  
> >  class CoverLetterList(ListAPIView):
> > @@ -91,5 +98,6 @@ class CoverLetterDetail(RetrieveAPIView):
> >  serializer_class = CoverLetterDetailSerializer
> >  
> >  def get_queryset(self):
> > -return CoverLetter.objects.all().prefetch_related('series')\
> > -.select_related('project', 'submitter')
> > +return CoverLetter.objects.all().prefetch_related(
> > +'series', 'comments'
> > +).select_related('project', 'submitter')
> > diff --git a/patchwork/api/embedded.py b/patchwork/api/embedded.py
> > index d79724c..afd511a 100644
> > --- a/patchwork/api/embedded.py
> > +++ b/patchwork/api/embedded.py
> > @@ -23,6 +23,8 @@ A collection of serializers. None of the serializers here
> > should reference
> >  nested fields.
> >  """
> >  
> > +import email.parser
> > +
> >  from rest_framewor

Re: [PATCH v2 1/2] api: Add comments to patch and cover endpoints

2018-04-15 Thread Stephen Finucane
On Wed, 2018-04-11 at 19:45 +0200, vkaba...@redhat.com wrote:
> From: Veronika Kabatova 
> 
> Signed-off-by: Veronika Kabatova 

I thought I'd replied to this already but I clearly didn't. This all
looks pretty good but I think I might have confused you. It seems with
this version our patch/cover letter responses gain an entire nested
version of the comments. This seems overly detailed, given that it's
not something most people need. What I meant instead is to add a
comments URL like so:

  /patch/{patchID}/comments

...and include this in the patch response:

  {
"id": 123,
...
"comments": "https://www.example.com/api/patches/123/comments";
  }

Any reason not to go for this option?

Also, if you do end up respinning this just squash tests/migrations in
with this patch. I'd be applying them together anyway.

Stephen

> ---
>  patchwork/api/cover.py | 14 ++--
>  patchwork/api/embedded.py  | 38 
> ++
>  patchwork/api/patch.py | 15 ++---
>  patchwork/models.py|  3 ++
>  .../notes/comments-api-b7dff6ee4ce04c9b.yaml   |  8 +
>  5 files changed, 70 insertions(+), 8 deletions(-)
>  create mode 100644 releasenotes/notes/comments-api-b7dff6ee4ce04c9b.yaml
> 
> diff --git a/patchwork/api/cover.py b/patchwork/api/cover.py
> index fc7ae97..5a6e377 100644
> --- a/patchwork/api/cover.py
> +++ b/patchwork/api/cover.py
> @@ -25,6 +25,7 @@ from rest_framework.serializers import SerializerMethodField
>  
>  from patchwork.api.base import BaseHyperlinkedModelSerializer
>  from patchwork.api.filters import CoverLetterFilter
> +from patchwork.api.embedded import CommentSerializer
>  from patchwork.api.embedded import PersonSerializer
>  from patchwork.api.embedded import ProjectSerializer
>  from patchwork.api.embedded import SeriesSerializer
> @@ -58,5 +59,6 @@ class 
> CoverLetterListSerializer(BaseHyperlinkedModelSerializer):
>  class CoverLetterDetailSerializer(CoverLetterListSerializer):
>  
>  headers = SerializerMethodField()
> +comments = CommentSerializer(many=True, read_only=True)
>  
>  def get_headers(self, instance):
> @@ -65,9 +67,14 @@ class 
> CoverLetterDetailSerializer(CoverLetterListSerializer):
>  
>  class Meta:
>  model = CoverLetter
> -fields = CoverLetterListSerializer.Meta.fields + ('headers', 
> 'content')
> +fields = CoverLetterListSerializer.Meta.fields + ('headers',
> +  'content',
> +  'comments')
>  read_only_fields = fields
>  extra_kwargs = CoverLetterListSerializer.Meta.extra_kwargs
> +versioned_fields = {
> +'1.1': ('mbox', 'comments'),
> +}
>  
>  
>  class CoverLetterList(ListAPIView):
> @@ -91,5 +98,6 @@ class CoverLetterDetail(RetrieveAPIView):
>  serializer_class = CoverLetterDetailSerializer
>  
>  def get_queryset(self):
> -return CoverLetter.objects.all().prefetch_related('series')\
> -.select_related('project', 'submitter')
> +return CoverLetter.objects.all().prefetch_related(
> +'series', 'comments'
> +).select_related('project', 'submitter')
> diff --git a/patchwork/api/embedded.py b/patchwork/api/embedded.py
> index d79724c..afd511a 100644
> --- a/patchwork/api/embedded.py
> +++ b/patchwork/api/embedded.py
> @@ -23,6 +23,8 @@ A collection of serializers. None of the serializers here 
> should reference
>  nested fields.
>  """
>  
> +import email.parser
> +
>  from rest_framework.serializers import CharField
>  from rest_framework.serializers import SerializerMethodField
>  
> @@ -163,3 +165,39 @@ class 
> UserProfileSerializer(BaseHyperlinkedModelSerializer):
>  extra_kwargs = {
>  'url': {'view_name': 'api-user-detail'},
>  }
> +
> +
> +class CommentSerializer(BaseHyperlinkedModelSerializer):
> +
> +tags = SerializerMethodField()
> +subject = SerializerMethodField()
> +headers = SerializerMethodField()
> +submitter = PersonSerializer(read_only=True)
> +
> +def get_tags(self, comment):
> +# TODO implement after we get support for tags on comments
> +return {}
> +
> +def get_subject(self, comment):
> +return email.parser.Parser().parsestr(comment.headers,
> +  True).get('Subject', '')
> +
> +def get_headers(self, comment):
> +headers = {}
> +
> +if comment.headers:
> +parsed = email.parser.Parser().parsestr(comment.headers, True)
> +for key in parsed.keys():
> +headers[key] = parsed.get_all(key)
> +# Let's return a single string instead of a list if only one
> +# header with this key is present
> +if len(headers[key]) == 1:
> +heade

[PATCH v2 1/2] api: Add comments to patch and cover endpoints

2018-04-11 Thread vkabatov
From: Veronika Kabatova 

Signed-off-by: Veronika Kabatova 
---
 patchwork/api/cover.py | 14 ++--
 patchwork/api/embedded.py  | 38 ++
 patchwork/api/patch.py | 15 ++---
 patchwork/models.py|  3 ++
 .../notes/comments-api-b7dff6ee4ce04c9b.yaml   |  8 +
 5 files changed, 70 insertions(+), 8 deletions(-)
 create mode 100644 releasenotes/notes/comments-api-b7dff6ee4ce04c9b.yaml

diff --git a/patchwork/api/cover.py b/patchwork/api/cover.py
index fc7ae97..5a6e377 100644
--- a/patchwork/api/cover.py
+++ b/patchwork/api/cover.py
@@ -25,6 +25,7 @@ from rest_framework.serializers import SerializerMethodField
 
 from patchwork.api.base import BaseHyperlinkedModelSerializer
 from patchwork.api.filters import CoverLetterFilter
+from patchwork.api.embedded import CommentSerializer
 from patchwork.api.embedded import PersonSerializer
 from patchwork.api.embedded import ProjectSerializer
 from patchwork.api.embedded import SeriesSerializer
@@ -58,5 +59,6 @@ class 
CoverLetterListSerializer(BaseHyperlinkedModelSerializer):
 class CoverLetterDetailSerializer(CoverLetterListSerializer):
 
 headers = SerializerMethodField()
+comments = CommentSerializer(many=True, read_only=True)
 
 def get_headers(self, instance):
@@ -65,9 +67,14 @@ class CoverLetterDetailSerializer(CoverLetterListSerializer):
 
 class Meta:
 model = CoverLetter
-fields = CoverLetterListSerializer.Meta.fields + ('headers', 'content')
+fields = CoverLetterListSerializer.Meta.fields + ('headers',
+  'content',
+  'comments')
 read_only_fields = fields
 extra_kwargs = CoverLetterListSerializer.Meta.extra_kwargs
+versioned_fields = {
+'1.1': ('mbox', 'comments'),
+}
 
 
 class CoverLetterList(ListAPIView):
@@ -91,5 +98,6 @@ class CoverLetterDetail(RetrieveAPIView):
 serializer_class = CoverLetterDetailSerializer
 
 def get_queryset(self):
-return CoverLetter.objects.all().prefetch_related('series')\
-.select_related('project', 'submitter')
+return CoverLetter.objects.all().prefetch_related(
+'series', 'comments'
+).select_related('project', 'submitter')
diff --git a/patchwork/api/embedded.py b/patchwork/api/embedded.py
index d79724c..afd511a 100644
--- a/patchwork/api/embedded.py
+++ b/patchwork/api/embedded.py
@@ -23,6 +23,8 @@ A collection of serializers. None of the serializers here 
should reference
 nested fields.
 """
 
+import email.parser
+
 from rest_framework.serializers import CharField
 from rest_framework.serializers import SerializerMethodField
 
@@ -163,3 +165,39 @@ class 
UserProfileSerializer(BaseHyperlinkedModelSerializer):
 extra_kwargs = {
 'url': {'view_name': 'api-user-detail'},
 }
+
+
+class CommentSerializer(BaseHyperlinkedModelSerializer):
+
+tags = SerializerMethodField()
+subject = SerializerMethodField()
+headers = SerializerMethodField()
+submitter = PersonSerializer(read_only=True)
+
+def get_tags(self, comment):
+# TODO implement after we get support for tags on comments
+return {}
+
+def get_subject(self, comment):
+return email.parser.Parser().parsestr(comment.headers,
+  True).get('Subject', '')
+
+def get_headers(self, comment):
+headers = {}
+
+if comment.headers:
+parsed = email.parser.Parser().parsestr(comment.headers, True)
+for key in parsed.keys():
+headers[key] = parsed.get_all(key)
+# Let's return a single string instead of a list if only one
+# header with this key is present
+if len(headers[key]) == 1:
+headers[key] = headers[key][0]
+
+return headers
+
+class Meta:
+model = models.Comment
+fields = ('id', 'msgid', 'date', 'subject', 'submitter', 'tags',
+  'content', 'headers')
+read_only_fields = fields
diff --git a/patchwork/api/patch.py b/patchwork/api/patch.py
index 115feff..5affa87 100644
--- a/patchwork/api/patch.py
+++ b/patchwork/api/patch.py
@@ -24,11 +24,12 @@ from rest_framework.generics import ListAPIView
 from rest_framework.generics import RetrieveUpdateAPIView
 from rest_framework.relations import RelatedField
 from rest_framework.reverse import reverse
-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 PatchFilter
+from patchwork.api.embedded import CommentSerializer
 from patchwork.api.em