From: Stephen Finucane <[email protected]>

Signed-off-by: Franciszek Stachura <[email protected]>
---
v3: 
- Also prefetch labels in cover queryset
---
 docs/api/schemas/latest/patchwork.yaml        |  5 +++
 docs/api/schemas/patchwork.j2                 |  7 ++++
 docs/api/schemas/v1.4/patchwork.yaml          |  5 +++
 patchwork/api/cover.py                        |  9 +++-
 patchwork/api/patch.py                        | 10 ++++-
 patchwork/tests/unit/api/test_cover.py        | 26 ++++++++++++
 patchwork/tests/unit/api/test_patch.py        | 42 +++++++++++++++++++
 .../notes/labels-6d0096c7d8505627.yaml        |  7 ++++
 8 files changed, 109 insertions(+), 2 deletions(-)

diff --git a/docs/api/schemas/latest/patchwork.yaml 
b/docs/api/schemas/latest/patchwork.yaml
index b2bb220f..1b85e952 100644
--- a/docs/api/schemas/latest/patchwork.yaml
+++ b/docs/api/schemas/latest/patchwork.yaml
@@ -2312,6 +2312,11 @@ components:
           type: array
           items:
             $ref: '#/components/schemas/PatchEmbedded'
+        labels:
+          title: Labels
+          type: array
+          items:
+            type: string
     PatchDetail:
       type: object
       title: Patches
diff --git a/docs/api/schemas/patchwork.j2 b/docs/api/schemas/patchwork.j2
index f37d3213..cea28fc0 100644
--- a/docs/api/schemas/patchwork.j2
+++ b/docs/api/schemas/patchwork.j2
@@ -2397,6 +2397,13 @@ components:
           type: array
           items:
             $ref: '#/components/schemas/PatchEmbedded'
+{% endif %}
+{% if version >= (1, 4) %}
+        labels:
+          title: Labels
+          type: array
+          items:
+            type: string
 {% endif %}
     PatchDetail:
       type: object
diff --git a/docs/api/schemas/v1.4/patchwork.yaml 
b/docs/api/schemas/v1.4/patchwork.yaml
index 036fe15f..359e8224 100644
--- a/docs/api/schemas/v1.4/patchwork.yaml
+++ b/docs/api/schemas/v1.4/patchwork.yaml
@@ -2312,6 +2312,11 @@ components:
           type: array
           items:
             $ref: '#/components/schemas/PatchEmbedded'
+        labels:
+          title: Labels
+          type: array
+          items:
+            type: string
     PatchDetail:
       type: object
       title: Patches
diff --git a/patchwork/api/cover.py b/patchwork/api/cover.py
index ee6b301c..20ed81f5 100644
--- a/patchwork/api/cover.py
+++ b/patchwork/api/cover.py
@@ -9,6 +9,7 @@ from rest_framework.generics import ListAPIView
 from rest_framework.generics import RetrieveAPIView
 from rest_framework.reverse import reverse
 from rest_framework.serializers import SerializerMethodField
+from rest_framework.serializers import StringRelatedField
 
 from patchwork.api.base import BaseHyperlinkedModelSerializer
 from patchwork.api.filters import CoverFilterSet
@@ -25,6 +26,7 @@ class CoverListSerializer(BaseHyperlinkedModelSerializer):
     mbox = SerializerMethodField()
     series = SeriesSerializer(read_only=True)
     comments = SerializerMethodField()
+    labels = StringRelatedField(many=True)
 
     def get_web_url(self, instance):
         request = self.context.get('request')
@@ -62,11 +64,13 @@ class CoverListSerializer(BaseHyperlinkedModelSerializer):
             'mbox',
             'series',
             'comments',
+            'labels',
         )
         read_only_fields = fields
         versioned_fields = {
             '1.1': ('web_url', 'mbox', 'comments'),
             '1.2': ('list_archive_url',),
+            '1.4': ('labels',),
         }
         extra_kwargs = {
             'url': {'view_name': 'api-cover-detail'},
@@ -110,7 +114,10 @@ class CoverList(ListAPIView):
     def get_queryset(self):
         return (
             Cover.objects.all()
-            .prefetch_related('series__project')
+            .prefetch_related(
+                'series__project',
+                'labels',
+            )
             .select_related('project', 'submitter', 'series')
             .defer('content', 'headers')
         )
diff --git a/patchwork/api/patch.py b/patchwork/api/patch.py
index 443c3822..31d1dadf 100644
--- a/patchwork/api/patch.py
+++ b/patchwork/api/patch.py
@@ -18,6 +18,7 @@ from rest_framework.relations import RelatedField
 from rest_framework.reverse import reverse
 from rest_framework.serializers import SerializerMethodField
 from rest_framework import status
+from rest_framework.serializers import StringRelatedField
 
 from patchwork.api.base import BaseHyperlinkedModelSerializer
 from patchwork.api.base import PatchworkPermission
@@ -94,6 +95,7 @@ class PatchListSerializer(BaseHyperlinkedModelSerializer):
         default=[],
         style={'base_template': 'input.html'},
     )
+    labels = StringRelatedField(many=True)
 
     def get_web_url(self, instance):
         request = self.context.get('request')
@@ -177,6 +179,7 @@ class PatchListSerializer(BaseHyperlinkedModelSerializer):
             'checks',
             'tags',
             'related',
+            'labels',
         )
         read_only_fields = (
             'url',
@@ -194,6 +197,7 @@ class PatchListSerializer(BaseHyperlinkedModelSerializer):
             'check',
             'checks',
             'tags',
+            'labels',
         )
         versioned_fields = {
             '1.1': ('comments', 'web_url'),
@@ -201,6 +205,7 @@ class PatchListSerializer(BaseHyperlinkedModelSerializer):
                 'list_archive_url',
                 'related',
             ),
+            '1.4': ('labels',),
         }
         extra_kwargs = {
             'url': {'view_name': 'api-patch-detail'},
@@ -367,6 +372,7 @@ class PatchList(ListAPIView):
                 'project',
                 'series__project',
                 'related__patches__project',
+                'labels',
             )
             .select_related('state', 'submitter', 'series')
             .defer('content', 'diff', 'headers')
@@ -391,7 +397,9 @@ class PatchDetail(RetrieveUpdateAPIView):
     def get_queryset(self):
         return (
             Patch.objects.all()
-            .prefetch_related('check_set', 'related__patches__project')
+            .prefetch_related(
+                'check_set', 'related__patches__project', 'labels'
+            )
             .select_related(
                 'project', 'state', 'submitter', 'delegate', 'series'
             )
diff --git a/patchwork/tests/unit/api/test_cover.py 
b/patchwork/tests/unit/api/test_cover.py
index 643806d9..21f24026 100644
--- a/patchwork/tests/unit/api/test_cover.py
+++ b/patchwork/tests/unit/api/test_cover.py
@@ -40,6 +40,11 @@ class TestCoverAPI(utils.APITestCase):
         self.assertIn(cover_obj.get_absolute_url(), cover_json['web_url'])
         self.assertIn('comments', cover_json)
 
+        # list fields
+
+        for label in cover_obj.labels.all():
+            self.assertIn(label.name, cover_json['labels'])
+
         # nested fields
 
         self.assertEqual(cover_obj.submitter.id, cover_json['submitter']['id'])
@@ -130,6 +135,18 @@ class TestCoverAPI(utils.APITestCase):
         self.assertIn('url', resp.data[0])
         self.assertNotIn('mbox', resp.data[0])
         self.assertNotIn('web_url', resp.data[0])
+        self.assertNotIn('labels', resp.data[0])
+
+    def test_list_version_1_1(self):
+        create_cover()
+
+        resp = self.client.get(self.api_url(version='1.1'))
+        self.assertEqual(status.HTTP_200_OK, resp.status_code)
+        self.assertEqual(1, len(resp.data))
+        self.assertIn('url', resp.data[0])
+        self.assertIn('mbox', resp.data[0])
+        self.assertIn('web_url', resp.data[0])
+        self.assertNotIn('labels', resp.data[0])
 
     def test_list_bug_335(self):
         """Ensure we retrieve the embedded series project once."""
@@ -177,6 +194,15 @@ class TestCoverAPI(utils.APITestCase):
         with self.assertRaises(NoReverseMatch):
             self.client.get(self.api_url('foo'))
 
+    def test_detail_version_1_1(self):
+        cover = create_cover()
+
+        resp = self.client.get(self.api_url(cover.id, version='1.1'))
+        self.assertIn('url', resp.data)
+        self.assertIn('web_url', resp.data)
+        self.assertIn('comments', resp.data)
+        self.assertNotIn('labels', resp.data)
+
     def test_create_update_delete(self):
         user = create_maintainer()
         user.is_superuser = True
diff --git a/patchwork/tests/unit/api/test_patch.py 
b/patchwork/tests/unit/api/test_patch.py
index b2890c22..9df684b1 100644
--- a/patchwork/tests/unit/api/test_patch.py
+++ b/patchwork/tests/unit/api/test_patch.py
@@ -13,6 +13,7 @@ from rest_framework import status
 
 from patchwork.models import Patch
 from patchwork.tests.unit.api import utils
+from patchwork.tests.utils import create_label
 from patchwork.tests.utils import create_maintainer
 from patchwork.tests.utils import create_patch
 from patchwork.tests.utils import create_patches
@@ -54,6 +55,11 @@ class TestPatchAPI(utils.APITestCase):
         self.assertIn(patch_obj.get_absolute_url(), patch_json['web_url'])
         self.assertIn('comments', patch_json)
 
+        # list fields
+
+        for label in patch_obj.labels.all():
+            self.assertIn(label.name, patch_json['labels'])
+
         # nested fields
 
         self.assertEqual(patch_obj.submitter.id, patch_json['submitter']['id'])
@@ -232,6 +238,16 @@ class TestPatchAPI(utils.APITestCase):
         self.assertEqual(1, len(resp.data))
         self.assertIn('url', resp.data[0])
         self.assertNotIn('web_url', resp.data[0])
+        self.assertNotIn('labels', resp.data[0])
+
+    def test_list_version_1_1(self):
+        create_patch()
+
+        resp = self.client.get(self.api_url(version='1.1'))
+        self.assertEqual(status.HTTP_200_OK, resp.status_code)
+        self.assertEqual(1, len(resp.data))
+        self.assertIn('web_url', resp.data[0])
+        self.assertNotIn('labels', resp.data[0])
 
     def test_list_bug_335(self):
         """Ensure we retrieve the embedded series project in O(1)."""
@@ -271,6 +287,23 @@ class TestPatchAPI(utils.APITestCase):
         self.assertIn('url', resp.data)
         self.assertNotIn('web_url', resp.data)
         self.assertNotIn('comments', resp.data)
+        self.assertNotIn('labels', resp.data)
+
+    def test_detail_version_1_1(self):
+        patch = create_patch()
+
+        resp = self.client.get(self.api_url(item=patch.id, version='1.1'))
+        self.assertIn('url', resp.data)
+        self.assertIn('web_url', resp.data)
+        self.assertIn('comments', resp.data)
+        self.assertNotIn('labels', resp.data)
+
+    def test_detail_version_1_4(self):
+        label = create_label()
+        patch = create_patch(labels=[label])
+
+        resp = self.client.get(self.api_url(item=patch.id, version='1.4'))
+        self.assertEqual(resp.data['labels'], [label.name])
 
     def test_detail_non_existent(self):
         """Ensure we get a 404 for a non-existent patch."""
@@ -282,6 +315,15 @@ class TestPatchAPI(utils.APITestCase):
         with self.assertRaises(NoReverseMatch):
             self.client.get(self.api_url('foo'))
 
+    def test_detail_labels(self):
+        patch = create_patch()
+
+        resp = self.client.get(self.api_url(item=patch.id, version='1.4'))
+        self.assertIn('url', resp.data)
+        self.assertIn('web_url', resp.data)
+        self.assertIn('comments', resp.data)
+        self.assertIn('labels', resp.data)
+
     def test_create(self):
         """Ensure creations are rejected."""
         project = create_project()
diff --git a/releasenotes/notes/labels-6d0096c7d8505627.yaml 
b/releasenotes/notes/labels-6d0096c7d8505627.yaml
index fdebd6b7..cb8a9213 100644
--- a/releasenotes/notes/labels-6d0096c7d8505627.yaml
+++ b/releasenotes/notes/labels-6d0096c7d8505627.yaml
@@ -9,3 +9,10 @@ features:
     Labels can have an optional description attached, which will provide a
     little insight into the purpose of the label. Labels are completely
     customizable and the labels available will vary by instance.
+api:
+  - |
+    The ``/patches`` endpoint now exposes a ``labels`` attribute for each
+    patch.
+  - |
+    The ``/covers`` endpoint now exposes a ``labels`` attribute for each cover
+    letter.
-- 
2.55.0

_______________________________________________
Patchwork mailing list
[email protected]
https://lists.ozlabs.org/listinfo/patchwork

Reply via email to