Skip to content

Commit 312838f

Browse files
stephenfinfstachura
authored andcommitted
REST: Expose Patch.labels
REST: Fix bug 335 tests One extra query is necessary for patch/cover endpoints to fetch labels information.
1 parent ae7083f commit 312838f

8 files changed

Lines changed: 111 additions & 4 deletions

File tree

‎docs/api/schemas/latest/patchwork.yaml‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2312,6 +2312,11 @@ components:
23122312
type: array
23132313
items:
23142314
$ref: '#/components/schemas/PatchEmbedded'
2315+
labels:
2316+
title: Labels
2317+
type: array
2318+
items:
2319+
type: string
23152320
PatchDetail:
23162321
type: object
23172322
title: Patches

‎docs/api/schemas/patchwork.j2‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2397,6 +2397,13 @@ components:
23972397
type: array
23982398
items:
23992399
$ref: '#/components/schemas/PatchEmbedded'
2400+
{% endif %}
2401+
{% if version >= (1, 4) %}
2402+
labels:
2403+
title: Labels
2404+
type: array
2405+
items:
2406+
type: string
24002407
{% endif %}
24012408
PatchDetail:
24022409
type: object

‎docs/api/schemas/v1.4/patchwork.yaml‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2312,6 +2312,11 @@ components:
23122312
type: array
23132313
items:
23142314
$ref: '#/components/schemas/PatchEmbedded'
2315+
labels:
2316+
title: Labels
2317+
type: array
2318+
items:
2319+
type: string
23152320
PatchDetail:
23162321
type: object
23172322
title: Patches

‎patchwork/api/cover.py‎

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
from rest_framework.generics import RetrieveAPIView
1010
from rest_framework.reverse import reverse
1111
from rest_framework.serializers import SerializerMethodField
12+
from rest_framework.serializers import StringRelatedField
1213

1314
from patchwork.api.base import BaseHyperlinkedModelSerializer
1415
from patchwork.api.filters import CoverFilterSet
@@ -25,6 +26,7 @@ class CoverListSerializer(BaseHyperlinkedModelSerializer):
2526
mbox = SerializerMethodField()
2627
series = SeriesSerializer(read_only=True)
2728
comments = SerializerMethodField()
29+
labels = StringRelatedField(many=True)
2830

2931
def get_web_url(self, instance):
3032
request = self.context.get('request')
@@ -62,11 +64,13 @@ class Meta:
6264
'mbox',
6365
'series',
6466
'comments',
67+
'labels',
6568
)
6669
read_only_fields = fields
6770
versioned_fields = {
6871
'1.1': ('web_url', 'mbox', 'comments'),
6972
'1.2': ('list_archive_url',),
73+
'1.4': ('labels',),
7074
}
7175
extra_kwargs = {
7276
'url': {'view_name': 'api-cover-detail'},
@@ -110,7 +114,10 @@ class CoverList(ListAPIView):
110114
def get_queryset(self):
111115
return (
112116
Cover.objects.all()
113-
.prefetch_related('series__project')
117+
.prefetch_related(
118+
'series__project',
119+
'labels',
120+
)
114121
.select_related('project', 'submitter', 'series')
115122
.defer('content', 'headers')
116123
)

‎patchwork/api/patch.py‎

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
from rest_framework.reverse import reverse
1919
from rest_framework.serializers import SerializerMethodField
2020
from rest_framework import status
21+
from rest_framework.serializers import StringRelatedField
2122

2223
from patchwork.api.base import BaseHyperlinkedModelSerializer
2324
from patchwork.api.base import LinkHeaderPagination
@@ -95,6 +96,7 @@ class PatchListSerializer(BaseHyperlinkedModelSerializer):
9596
default=[],
9697
style={'base_template': 'input.html'},
9798
)
99+
labels = StringRelatedField(many=True)
98100

99101
def get_web_url(self, instance):
100102
request = self.context.get('request')
@@ -178,6 +180,7 @@ class Meta:
178180
'checks',
179181
'tags',
180182
'related',
183+
'labels',
181184
)
182185
read_only_fields = (
183186
'url',
@@ -195,13 +198,15 @@ class Meta:
195198
'check',
196199
'checks',
197200
'tags',
201+
'labels',
198202
)
199203
versioned_fields = {
200204
'1.1': ('comments', 'web_url'),
201205
'1.2': (
202206
'list_archive_url',
203207
'related',
204208
),
209+
'1.4': ('labels',),
205210
}
206211
extra_kwargs = {
207212
'url': {'view_name': 'api-patch-detail'},
@@ -384,6 +389,7 @@ def get_queryset(self):
384389
'project',
385390
'series__project',
386391
'related__patches__project',
392+
'labels',
387393
)
388394
.select_related('state', 'submitter', 'series')
389395
.defer('content', 'diff', 'headers')
@@ -408,7 +414,9 @@ class PatchDetail(RetrieveUpdateAPIView):
408414
def get_queryset(self):
409415
return (
410416
Patch.objects.all()
411-
.prefetch_related('check_set', 'related__patches__project')
417+
.prefetch_related(
418+
'check_set', 'related__patches__project', 'labels'
419+
)
412420
.select_related(
413421
'project', 'state', 'submitter', 'delegate', 'series'
414422
)

‎patchwork/tests/unit/api/test_cover.py‎

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,11 @@ def assertSerialized(self, cover_obj, cover_json):
4040
self.assertIn(cover_obj.get_absolute_url(), cover_json['web_url'])
4141
self.assertIn('comments', cover_json)
4242

43+
# list fields
44+
45+
for label in cover_obj.labels.all():
46+
self.assertIn(label.name, cover_json['labels'])
47+
4348
# nested fields
4449

4550
self.assertEqual(cover_obj.submitter.id, cover_json['submitter']['id'])
@@ -130,13 +135,25 @@ def test_list_version_1_0(self):
130135
self.assertIn('url', resp.data[0])
131136
self.assertNotIn('mbox', resp.data[0])
132137
self.assertNotIn('web_url', resp.data[0])
138+
self.assertNotIn('labels', resp.data[0])
139+
140+
def test_list_version_1_1(self):
141+
create_cover()
142+
143+
resp = self.client.get(self.api_url(version='1.1'))
144+
self.assertEqual(status.HTTP_200_OK, resp.status_code)
145+
self.assertEqual(1, len(resp.data))
146+
self.assertIn('url', resp.data[0])
147+
self.assertIn('mbox', resp.data[0])
148+
self.assertIn('web_url', resp.data[0])
149+
self.assertNotIn('labels', resp.data[0])
133150

134151
def test_list_bug_335(self):
135152
"""Ensure we retrieve the embedded series project once."""
136153
series = create_series()
137154
create_covers(5, series=series)
138155

139-
with self.assertNumQueries(3):
156+
with self.assertNumQueries(4):
140157
self.client.get(self.api_url())
141158

142159
@utils.store_samples('cover-detail')
@@ -177,6 +194,15 @@ def test_detail_invalid(self):
177194
with self.assertRaises(NoReverseMatch):
178195
self.client.get(self.api_url('foo'))
179196

197+
def test_detail_version_1_1(self):
198+
cover = create_cover()
199+
200+
resp = self.client.get(self.api_url(cover.id, version='1.1'))
201+
self.assertIn('url', resp.data)
202+
self.assertIn('web_url', resp.data)
203+
self.assertIn('comments', resp.data)
204+
self.assertNotIn('labels', resp.data)
205+
180206
def test_create_update_delete(self):
181207
user = create_maintainer()
182208
user.is_superuser = True

‎patchwork/tests/unit/api/test_patch.py‎

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
from patchwork.models import Patch
1515
from patchwork.tests.unit.api import utils
16+
from patchwork.tests.utils import create_label
1617
from patchwork.tests.utils import create_maintainer
1718
from patchwork.tests.utils import create_patch
1819
from patchwork.tests.utils import create_patches
@@ -54,6 +55,11 @@ def assertSerialized(self, patch_obj, patch_json):
5455
self.assertIn(patch_obj.get_absolute_url(), patch_json['web_url'])
5556
self.assertIn('comments', patch_json)
5657

58+
# list fields
59+
60+
for label in patch_obj.labels.all():
61+
self.assertIn(label.name, patch_json['labels'])
62+
5763
# nested fields
5864

5965
self.assertEqual(patch_obj.submitter.id, patch_json['submitter']['id'])
@@ -257,14 +263,24 @@ def test_list_version_1_0(self):
257263
self.assertEqual(1, len(resp.data))
258264
self.assertIn('url', resp.data[0])
259265
self.assertNotIn('web_url', resp.data[0])
266+
self.assertNotIn('labels', resp.data[0])
267+
268+
def test_list_version_1_1(self):
269+
create_patch()
270+
271+
resp = self.client.get(self.api_url(version='1.1'))
272+
self.assertEqual(status.HTTP_200_OK, resp.status_code)
273+
self.assertEqual(1, len(resp.data))
274+
self.assertIn('web_url', resp.data[0])
275+
self.assertNotIn('labels', resp.data[0])
260276

261277
def test_list_bug_335(self):
262278
"""Ensure we retrieve the embedded series project in O(1)."""
263279
series = create_series()
264280
create_patches(5, series=series)
265281

266282
# Count, page IDs, patch rows and the constant-size prefetches.
267-
with self.assertNumQueries(6):
283+
with self.assertNumQueries(7):
268284
self.client.get(self.api_url())
269285

270286
@utils.store_samples('patch-detail')
@@ -297,6 +313,23 @@ def test_detail_version_1_0(self):
297313
self.assertIn('url', resp.data)
298314
self.assertNotIn('web_url', resp.data)
299315
self.assertNotIn('comments', resp.data)
316+
self.assertNotIn('labels', resp.data)
317+
318+
def test_detail_version_1_1(self):
319+
patch = create_patch()
320+
321+
resp = self.client.get(self.api_url(item=patch.id, version='1.1'))
322+
self.assertIn('url', resp.data)
323+
self.assertIn('web_url', resp.data)
324+
self.assertIn('comments', resp.data)
325+
self.assertNotIn('labels', resp.data)
326+
327+
def test_detail_version_1_4(self):
328+
label = create_label()
329+
patch = create_patch(labels=[label])
330+
331+
resp = self.client.get(self.api_url(item=patch.id, version='1.4'))
332+
self.assertEqual(resp.data['labels'], [label.name])
300333

301334
def test_detail_non_existent(self):
302335
"""Ensure we get a 404 for a non-existent patch."""
@@ -308,6 +341,15 @@ def test_detail_invalid(self):
308341
with self.assertRaises(NoReverseMatch):
309342
self.client.get(self.api_url('foo'))
310343

344+
def test_detail_labels(self):
345+
patch = create_patch()
346+
347+
resp = self.client.get(self.api_url(item=patch.id, version='1.4'))
348+
self.assertIn('url', resp.data)
349+
self.assertIn('web_url', resp.data)
350+
self.assertIn('comments', resp.data)
351+
self.assertIn('labels', resp.data)
352+
311353
def test_create(self):
312354
"""Ensure creations are rejected."""
313355
project = create_project()

‎releasenotes/notes/labels-6d0096c7d8505627.yaml‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,10 @@ features:
99
Labels can have an optional description attached, which will provide a
1010
little insight into the purpose of the label. Labels are completely
1111
customizable and the labels available will vary by instance.
12+
api:
13+
- |
14+
The ``/patches`` endpoint now exposes a ``labels`` attribute for each
15+
patch.
16+
- |
17+
The ``/covers`` endpoint now exposes a ``labels`` attribute for each cover
18+
letter.

0 commit comments

Comments
 (0)