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