Skip to content

Commit 83f364a

Browse files
metpdaxtens
authored andcommitted
REST: Add patch relations
View relations and add/update/delete them as a maintainer. Maintainers can only create relations of patches which are part of a project they maintain. Because this is a writable many-many nested relationship, it behaves a little unusually. In short: - All operations use PATCH to the 'related' field of a patch - To relate a patch to another patch, say 7 to 19, either: PATCH /api/patch/7 related := [19] PATCH /api/patch/19 related := [7] - To delete a patch from a relation, say 1, 21 and 42 are related but we only want it to be 1 and 42: PATCH /api/patch/21 related := [] * You _cannot_ remove a patch from a relation by patching another patch in the relation: I'm trying to avoid read-modify-write loops. * Relations that would be left with just 1 patch are deleted. This is only ensured in the API - the admin interface will let you do this. - Break-before-make: if you have [1, 12, 24] and [7, 15, 42] and you want to end up with [1, 12, 15, 42], you have to remove 15 from the second relation first: PATCH /api/patch/1 related := [15] will fail with 409 Conflict. Instead do: PATCH /api/patch/15 related := [] PATCH /api/patch/1 related := [15] -> 200 OK, [1, 12, 15, 42] and [7, 42] are the resulting relations Signed-off-by: Mete Polat <[email protected]> Signed-off-by: Stephen Finucane <[email protected]> Signed-off-by: Daniel Axtens <[email protected]>
1 parent 27c2acf commit 83f364a

File tree

11 files changed

+656
-6
lines changed

11 files changed

+656
-6
lines changed

docs/api/schemas/latest/patchwork.yaml

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -352,6 +352,7 @@ paths:
352352
- patch-created
353353
- patch-completed
354354
- patch-state-changed
355+
- patch-relation-changed
355356
- patch-delegated
356357
- check-created
357358
- series-created
@@ -390,6 +391,7 @@ paths:
390391
- $ref: '#/components/schemas/EventPatchCreated'
391392
- $ref: '#/components/schemas/EventPatchCompleted'
392393
- $ref: '#/components/schemas/EventPatchStateChanged'
394+
- $ref: '#/components/schemas/EventPatchRelationChanged'
393395
- $ref: '#/components/schemas/EventPatchDelegated'
394396
- $ref: '#/components/schemas/EventCheckCreated'
395397
- $ref: '#/components/schemas/EventSeriesCreated'
@@ -403,6 +405,8 @@ paths:
403405
'#/components/schemas/EventPatchCompleted'
404406
patch-state-changed: >
405407
'#/components/schemas/EventPatchStateChanged'
408+
patch-relation-changed: >
409+
'#/components/schemas/EventPatchRelationChanged'
406410
patch-delegated: >
407411
'#/components/schemas/EventPatchDelegated'
408412
check-created: '#/components/schemas/EventCheckCreated'
@@ -552,6 +556,12 @@ paths:
552556
application/json:
553557
schema:
554558
$ref: '#/components/schemas/Error'
559+
'409':
560+
description: Conflict
561+
content:
562+
application/json:
563+
schema:
564+
$ref: '#/components/schemas/Error'
555565
tags:
556566
- patches
557567
put:
@@ -595,6 +605,12 @@ paths:
595605
application/json:
596606
schema:
597607
$ref: '#/components/schemas/Error'
608+
'409':
609+
description: Conflict
610+
content:
611+
application/json:
612+
schema:
613+
$ref: '#/components/schemas/Error'
598614
tags:
599615
- patches
600616
/api/patches/{id}/comments/:
@@ -1721,6 +1737,24 @@ components:
17211737
current_state:
17221738
title: Current state
17231739
type: string
1740+
EventPatchRelationChanged:
1741+
allOf:
1742+
- $ref: '#/components/schemas/EventBase'
1743+
- type: object
1744+
properties:
1745+
category:
1746+
enum:
1747+
- patch-relation-changed
1748+
payload:
1749+
properties:
1750+
patch:
1751+
$ref: '#/components/schemas/PatchEmbedded'
1752+
previous_relation:
1753+
title: Previous relation
1754+
type: string
1755+
current_relation:
1756+
title: Current relation
1757+
type: string
17241758
EventPatchDelegated:
17251759
allOf:
17261760
- $ref: '#/components/schemas/EventBase'
@@ -1893,6 +1927,11 @@ components:
18931927
items:
18941928
type: string
18951929
readOnly: true
1930+
related:
1931+
title: Relations
1932+
type: array
1933+
items:
1934+
type: string
18961935
PatchDetail:
18971936
allOf:
18981937
- $ref: '#/components/schemas/PatchList'
@@ -1943,6 +1982,11 @@ components:
19431982
title: Delegate
19441983
type: integer
19451984
nullable: true
1985+
related:
1986+
title: Relations
1987+
type: array
1988+
items:
1989+
type: string
19461990
Person:
19471991
type: object
19481992
properties:

docs/api/schemas/patchwork.j2

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -359,6 +359,9 @@ paths:
359359
- patch-created
360360
- patch-completed
361361
- patch-state-changed
362+
{% if version >= (1, 2) %}
363+
- patch-relation-changed
364+
{% endif %}
362365
- patch-delegated
363366
- check-created
364367
- series-created
@@ -397,6 +400,9 @@ paths:
397400
- $ref: '#/components/schemas/EventPatchCreated'
398401
- $ref: '#/components/schemas/EventPatchCompleted'
399402
- $ref: '#/components/schemas/EventPatchStateChanged'
403+
{% if version >= (1, 2) %}
404+
- $ref: '#/components/schemas/EventPatchRelationChanged'
405+
{% endif %}
400406
- $ref: '#/components/schemas/EventPatchDelegated'
401407
- $ref: '#/components/schemas/EventCheckCreated'
402408
- $ref: '#/components/schemas/EventSeriesCreated'
@@ -410,6 +416,10 @@ paths:
410416
'#/components/schemas/EventPatchCompleted'
411417
patch-state-changed: >
412418
'#/components/schemas/EventPatchStateChanged'
419+
{% if version >= (1, 2) %}
420+
patch-relation-changed: >
421+
'#/components/schemas/EventPatchRelationChanged'
422+
{% endif %}
413423
patch-delegated: >
414424
'#/components/schemas/EventPatchDelegated'
415425
check-created: '#/components/schemas/EventCheckCreated'
@@ -561,6 +571,14 @@ paths:
561571
application/json:
562572
schema:
563573
$ref: '#/components/schemas/Error'
574+
{% if version >= (1, 2) %}
575+
'409':
576+
description: Conflict
577+
content:
578+
application/json:
579+
schema:
580+
$ref: '#/components/schemas/Error'
581+
{% endif %}
564582
tags:
565583
- patches
566584
put:
@@ -604,6 +622,14 @@ paths:
604622
application/json:
605623
schema:
606624
$ref: '#/components/schemas/Error'
625+
{% if version >= (1, 2) %}
626+
'409':
627+
description: Conflict
628+
content:
629+
application/json:
630+
schema:
631+
$ref: '#/components/schemas/Error'
632+
{% endif %}
607633
tags:
608634
- patches
609635
/api/{{ version_url }}patches/{id}/comments/:
@@ -1777,6 +1803,26 @@ components:
17771803
current_state:
17781804
title: Current state
17791805
type: string
1806+
{% if version >= (1, 1) %}
1807+
EventPatchRelationChanged:
1808+
allOf:
1809+
- $ref: '#/components/schemas/EventBase'
1810+
- type: object
1811+
properties:
1812+
category:
1813+
enum:
1814+
- patch-relation-changed
1815+
payload:
1816+
properties:
1817+
patch:
1818+
$ref: '#/components/schemas/PatchEmbedded'
1819+
previous_relation:
1820+
title: Previous relation
1821+
type: string
1822+
current_relation:
1823+
title: Current relation
1824+
type: string
1825+
{% endif %}
17801826
EventPatchDelegated:
17811827
allOf:
17821828
- $ref: '#/components/schemas/EventBase'
@@ -1955,6 +2001,13 @@ components:
19552001
items:
19562002
type: string
19572003
readOnly: true
2004+
{% if version >= (1, 2) %}
2005+
related:
2006+
title: Relations
2007+
type: array
2008+
items:
2009+
type: string
2010+
{% endif %}
19582011
PatchDetail:
19592012
allOf:
19602013
- $ref: '#/components/schemas/PatchList'
@@ -2005,6 +2058,13 @@ components:
20052058
title: Delegate
20062059
type: integer
20072060
nullable: true
2061+
{% if version >= (1, 2) %}
2062+
related:
2063+
title: Relations
2064+
type: array
2065+
items:
2066+
type: string
2067+
{% endif %}
20082068
Person:
20092069
type: object
20102070
properties:

docs/api/schemas/v1.1/patchwork.yaml

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1551,6 +1551,24 @@ components:
15511551
current_state:
15521552
title: Current state
15531553
type: string
1554+
EventPatchRelationChanged:
1555+
allOf:
1556+
- $ref: '#/components/schemas/EventBase'
1557+
- type: object
1558+
properties:
1559+
category:
1560+
enum:
1561+
- patch-relation-changed
1562+
payload:
1563+
properties:
1564+
patch:
1565+
$ref: '#/components/schemas/PatchEmbedded'
1566+
previous_relation:
1567+
title: Previous relation
1568+
type: string
1569+
current_relation:
1570+
title: Current relation
1571+
type: string
15541572
EventPatchDelegated:
15551573
allOf:
15561574
- $ref: '#/components/schemas/EventBase'

docs/api/schemas/v1.2/patchwork.yaml

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -352,6 +352,7 @@ paths:
352352
- patch-created
353353
- patch-completed
354354
- patch-state-changed
355+
- patch-relation-changed
355356
- patch-delegated
356357
- check-created
357358
- series-created
@@ -390,6 +391,7 @@ paths:
390391
- $ref: '#/components/schemas/EventPatchCreated'
391392
- $ref: '#/components/schemas/EventPatchCompleted'
392393
- $ref: '#/components/schemas/EventPatchStateChanged'
394+
- $ref: '#/components/schemas/EventPatchRelationChanged'
393395
- $ref: '#/components/schemas/EventPatchDelegated'
394396
- $ref: '#/components/schemas/EventCheckCreated'
395397
- $ref: '#/components/schemas/EventSeriesCreated'
@@ -403,6 +405,8 @@ paths:
403405
'#/components/schemas/EventPatchCompleted'
404406
patch-state-changed: >
405407
'#/components/schemas/EventPatchStateChanged'
408+
patch-relation-changed: >
409+
'#/components/schemas/EventPatchRelationChanged'
406410
patch-delegated: >
407411
'#/components/schemas/EventPatchDelegated'
408412
check-created: '#/components/schemas/EventCheckCreated'
@@ -552,6 +556,12 @@ paths:
552556
application/json:
553557
schema:
554558
$ref: '#/components/schemas/Error'
559+
'409':
560+
description: Conflict
561+
content:
562+
application/json:
563+
schema:
564+
$ref: '#/components/schemas/Error'
555565
tags:
556566
- patches
557567
put:
@@ -595,6 +605,12 @@ paths:
595605
application/json:
596606
schema:
597607
$ref: '#/components/schemas/Error'
608+
'409':
609+
description: Conflict
610+
content:
611+
application/json:
612+
schema:
613+
$ref: '#/components/schemas/Error'
598614
tags:
599615
- patches
600616
/api/1.2/patches/{id}/comments/:
@@ -1721,6 +1737,24 @@ components:
17211737
current_state:
17221738
title: Current state
17231739
type: string
1740+
EventPatchRelationChanged:
1741+
allOf:
1742+
- $ref: '#/components/schemas/EventBase'
1743+
- type: object
1744+
properties:
1745+
category:
1746+
enum:
1747+
- patch-relation-changed
1748+
payload:
1749+
properties:
1750+
patch:
1751+
$ref: '#/components/schemas/PatchEmbedded'
1752+
previous_relation:
1753+
title: Previous relation
1754+
type: string
1755+
current_relation:
1756+
title: Current relation
1757+
type: string
17241758
EventPatchDelegated:
17251759
allOf:
17261760
- $ref: '#/components/schemas/EventBase'
@@ -1893,6 +1927,11 @@ components:
18931927
items:
18941928
type: string
18951929
readOnly: true
1930+
related:
1931+
title: Relations
1932+
type: array
1933+
items:
1934+
type: string
18961935
PatchDetail:
18971936
allOf:
18981937
- $ref: '#/components/schemas/PatchList'
@@ -1943,6 +1982,11 @@ components:
19431982
title: Delegate
19441983
type: integer
19451984
nullable: true
1985+
related:
1986+
title: Relations
1987+
type: array
1988+
items:
1989+
type: string
19461990
Person:
19471991
type: object
19481992
properties:

patchwork/api/embedded.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
from rest_framework.serializers import CharField
1515
from rest_framework.serializers import SerializerMethodField
1616
from rest_framework.serializers import PrimaryKeyRelatedField
17+
from rest_framework.serializers import ValidationError
1718

1819
from patchwork.api.base import BaseHyperlinkedModelSerializer
1920
from patchwork.api.base import CheckHyperlinkedIdentityField
@@ -138,6 +139,30 @@ class Meta:
138139
}
139140

140141

142+
class PatchRelationSerializer(BaseHyperlinkedModelSerializer):
143+
"""Hide the PatchRelation model, just show the list"""
144+
patches = PatchSerializer(many=True)
145+
146+
def to_internal_value(self, data):
147+
if not isinstance(data, type([])):
148+
raise ValidationError(
149+
"Patch relations must be specified as a list of patch IDs"
150+
)
151+
result = super(PatchRelationSerializer, self).to_internal_value(
152+
{'patches': data}
153+
)
154+
return result
155+
156+
def to_representation(self, instance):
157+
data = super(PatchRelationSerializer, self).to_representation(instance)
158+
data = data['patches']
159+
return data
160+
161+
class Meta:
162+
model = models.PatchRelation
163+
fields = ('patches',)
164+
165+
141166
class PersonSerializer(SerializedRelatedField):
142167

143168
class _Serializer(BaseHyperlinkedModelSerializer):

patchwork/api/event.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
from patchwork.api.embedded import CheckSerializer
1414
from patchwork.api.embedded import CoverLetterSerializer
1515
from patchwork.api.embedded import PatchSerializer
16+
from patchwork.api.embedded import PatchRelationSerializer
1617
from patchwork.api.embedded import ProjectSerializer
1718
from patchwork.api.embedded import SeriesSerializer
1819
from patchwork.api.embedded import UserSerializer
@@ -33,6 +34,8 @@ class EventSerializer(ModelSerializer):
3334
current_delegate = UserSerializer()
3435
created_check = SerializerMethodField()
3536
created_check = CheckSerializer()
37+
previous_relation = PatchRelationSerializer(read_only=True)
38+
current_relation = PatchRelationSerializer(read_only=True)
3639

3740
_category_map = {
3841
Event.CATEGORY_COVER_CREATED: ['cover'],

0 commit comments

Comments
 (0)