Skip to content

Commit 2cb3ec9

Browse files
[WEB-4100] Unique custom properties UI validation (#3240)
* chore: raise error for adding custom properties with specific keywords * chore: remove print statement * fix: duplicate custom properties * fix: duplicate issue types * fix: error message * chore: added ui validation for restricted keywords in issue types * fix: updated the variable names and refactor --------- Co-authored-by: sangeethailango <[email protected]>
1 parent 4014546 commit 2cb3ec9

File tree

17 files changed

+71
-26
lines changed

17 files changed

+71
-26
lines changed

apiserver/plane/ee/serializers/app/issue_property.py

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@
66
from plane.db.models import IssueType
77
from plane.ee.models import IssueProperty, IssuePropertyOption, IssuePropertyActivity
88
from plane.app.serializers import UserLiteSerializer
9+
from plane.utils.constants import (
10+
RESTRICTED_ISSUE_PROPERTY_DISPLAY_NAMES,
11+
RESTRICTED_ISSUE_TYPES,
12+
)
913

1014

1115
class IssueTypeSerializer(BaseSerializer):
@@ -17,13 +21,23 @@ class Meta:
1721
fields = "__all__"
1822
read_only_fields = ["workspace", "project", "is_default", "deleted_at"]
1923

24+
def validate_name(self, value):
25+
if value in RESTRICTED_ISSUE_TYPES:
26+
raise serializers.ValidationError(f"Issue type cannot be the {value}")
27+
return value
28+
2029

2130
class IssuePropertySerializer(BaseSerializer):
2231
class Meta:
2332
model = IssueProperty
2433
fields = "__all__"
2534
read_only_fields = ["name", "issue_type", "workspace", "deleted_at"]
2635

36+
def validate_display_name(self, value):
37+
if value in RESTRICTED_ISSUE_PROPERTY_DISPLAY_NAMES:
38+
raise serializers.ValidationError(f"Display name cannot be the {value}")
39+
return value
40+
2741

2842
class IssuePropertyOptionSerializer(BaseSerializer):
2943
class Meta:
@@ -33,8 +47,8 @@ class Meta:
3347

3448

3549
class IssuePropertyActivitySerializer(BaseSerializer):
36-
actor_detail = UserLiteSerializer(read_only=True, source='actor')
37-
50+
actor_detail = UserLiteSerializer(read_only=True, source="actor")
51+
3852
class Meta:
3953
model = IssuePropertyActivity
4054
fields = "__all__"

apiserver/plane/ee/views/app/issue_property/base.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,19 @@ def post(self, request, slug, project_id, issue_type_id):
179179
# Get the options
180180
options = request.data.pop("options", [])
181181

182+
issue_properties = IssueProperty.objects.filter(
183+
workspace__slug=slug,
184+
project_id=project_id,
185+
issue_type_id=issue_type_id,
186+
issue_type__is_epic=False,
187+
).values_list("display_name", flat=True)
188+
189+
if request.data.get("display_name") in issue_properties:
190+
return Response(
191+
{"error": "Custom property with this name already exists"},
192+
status=status.HTTP_400_BAD_REQUEST,
193+
)
194+
182195
# Check is_active
183196
if not request.data.get("is_active"):
184197
request.data["is_active"] = False

apiserver/plane/ee/views/app/issue_property/type.py

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,18 @@ def get(self, request, slug, project_id, pk=None):
102102

103103
@check_feature_flag(FeatureFlag.ISSUE_TYPES)
104104
def post(self, request, slug, project_id):
105+
issue_types = IssueType.objects.filter(
106+
workspace__slug=slug,
107+
project_issue_types__project_id=project_id,
108+
is_epic=False,
109+
).values_list("name", flat=True)
110+
111+
if request.data.get("name") in issue_types:
112+
return Response(
113+
{"error": "Issue type with this name already exists"},
114+
status=status.HTTP_400_BAD_REQUEST,
115+
)
116+
105117
# Fetch the project
106118
project = Project.objects.get(pk=project_id)
107119
# Create a new issue type
@@ -261,7 +273,9 @@ def post(self, request, slug, project_id):
261273
)
262274
.annotate(
263275
issue_exists=Exists(
264-
Issue.objects.filter(project_id=project_id, type_id=OuterRef("pk"))
276+
Issue.objects.filter(
277+
project_id=project_id, type_id=OuterRef("pk")
278+
)
265279
)
266280
)
267281
.annotate(
@@ -289,9 +303,7 @@ def post(self, request, slug, project_id):
289303
"is_epic": work_item_type.is_epic,
290304
}
291305
WorkitemTemplate.objects.filter(
292-
project_id=project_id,
293-
workspace__slug=slug,
294-
type__exact={},
306+
project_id=project_id, workspace__slug=slug, type__exact={}
295307
).update(type=work_item_type_template_schema)
296308

297309
# Serialize the data
@@ -371,9 +383,7 @@ def post(self, request, slug, project_id):
371383
"is_epic": work_item_type.is_epic,
372384
}
373385
WorkitemTemplate.objects.filter(
374-
project_id=project_id,
375-
workspace__slug=slug,
376-
type__exact={},
386+
project_id=project_id, workspace__slug=slug, type__exact={}
377387
).update(type=work_item_type_template_schema)
378388

379389
# Serialize the data

apiserver/plane/utils/constants.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,3 +67,6 @@
6767
"oauth",
6868
"applications",
6969
]
70+
71+
RESTRICTED_ISSUE_PROPERTY_DISPLAY_NAMES = ["state", "due date", "cycle", "modules"]
72+
RESTRICTED_ISSUE_TYPES = ["Task"]

packages/constants/src/work-item-properties.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -172,8 +172,7 @@ export const DROPDOWN_ATTRIBUTES: Partial<{
172172
],
173173
};
174174

175-
export const ISSUE_PROPERTY_SETTINGS_CONFIGURATIONS: Partial<TIssuePropertySettingsConfigurationsDetails> =
176-
{
175+
export const ISSUE_PROPERTY_SETTINGS_CONFIGURATIONS: Partial<TIssuePropertySettingsConfigurationsDetails> = {
177176
TEXT: [
178177
{
179178
keyToUpdate: ["display_format"],
@@ -227,3 +226,5 @@ export const ISSUE_PROPERTY_SETTINGS_CONFIGURATIONS: Partial<TIssuePropertySetti
227226
},
228227
],
229228
};
229+
230+
export const RESTRICTED_WORK_ITEM_PROPERTY_DISPLAY_NAMES = ["state", "due date", "cycle", "modules"];

packages/constants/src/work-item-types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,5 @@ export const DEFAULT_BACKGROUND_COLORS = [
1414
"#5D407A",
1515
"#999AA0",
1616
];
17+
18+
export const RESTRICTED_WORK_ITEM_TYPES = ["task"];

packages/i18n/src/locales/cs/translations.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -502,7 +502,6 @@
502502
"export": "Exportovat",
503503
"member": "{count, plural, one{# člen} few{# členové} other{# členů}}",
504504
"new_password_must_be_different_from_old_password": "Nové heslo musí být odlišné od starého hesla",
505-
506505
"project_view": {
507506
"sort_by": {
508507
"created_at": "Vytvořeno dne",
@@ -2463,4 +2462,4 @@
24632462
"previously_edited_by": "Dříve upraveno uživatelem",
24642463
"edited_by": "Upraveno uživatelem"
24652464
}
2466-
}
2465+
}

packages/i18n/src/locales/de/translations.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -504,7 +504,6 @@
504504
"new_password_must_be_different_from_old_password": "Das neue Passwort muss von dem alten Passwort abweichen",
505505
"edited": "Bearbeitet",
506506
"bot": "Bot",
507-
508507
"project_view": {
509508
"sort_by": {
510509
"created_at": "Erstellt am",

packages/i18n/src/locales/en/translations.json

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -336,8 +336,6 @@
336336
"new_password_must_be_different_from_old_password": "New password must be different from old password",
337337
"edited": "edited",
338338
"bot": "Bot",
339-
340-
341339
"project_view": {
342340
"sort_by": {
343341
"created_at": "Created at",
@@ -2309,7 +2307,6 @@
23092307
"previously_edited_by": "Previously edited by",
23102308
"edited_by": "Edited by"
23112309
},
2312-
23132310
"intake_forms": {
23142311
"create": {
23152312
"title": "Create a work item",

packages/i18n/src/locales/id/translations.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -502,7 +502,6 @@
502502
"export": "Ekspor",
503503
"member": "{count, plural, one{# anggota} other{# anggota}}",
504504
"new_password_must_be_different_from_old_password": "Kata sandi baru harus berbeda dari kata sandi lama",
505-
506505
"project_view": {
507506
"sort_by": {
508507
"created_at": "Dibuat pada",
@@ -2457,4 +2456,4 @@
24572456
"previously_edited_by": "Sebelumnya disunting oleh",
24582457
"edited_by": "Disunting oleh"
24592458
}
2460-
}
2459+
}

0 commit comments

Comments
 (0)