Skip to content

Commit 63d025c

Browse files
[WEB-4544] chore: added field validations in serializer (#7460)
* chore: added field validations in serializer * chore: added enum for roles
1 parent 27f7420 commit 63d025c

File tree

3 files changed

+157
-16
lines changed

3 files changed

+157
-16
lines changed

apps/api/plane/api/serializers/issue.py

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
ProjectMember,
2121
State,
2222
User,
23+
EstimatePoint,
2324
)
2425

2526
from .base import BaseSerializer
@@ -112,13 +113,27 @@ def validate(self, data):
112113
if (
113114
data.get("parent")
114115
and not Issue.objects.filter(
115-
workspace_id=self.context.get("workspace_id"), pk=data.get("parent").id
116+
workspace_id=self.context.get("workspace_id"),
117+
project_id=self.context.get("project_id"),
118+
pk=data.get("parent").id,
116119
).exists()
117120
):
118121
raise serializers.ValidationError(
119122
"Parent is not valid issue_id please pass a valid issue_id"
120123
)
121124

125+
if (
126+
data.get("estimate_point")
127+
and not EstimatePoint.objects.filter(
128+
workspace_id=self.context.get("workspace_id"),
129+
project_id=self.context.get("project_id"),
130+
pk=data.get("estimate_point").id,
131+
).exists()
132+
):
133+
raise serializers.ValidationError(
134+
"Estimate point is not valid please pass a valid estimate_point_id"
135+
)
136+
122137
return data
123138

124139
def create(self, validated_data):

apps/api/plane/app/serializers/draft.py

Lines changed: 79 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
from lxml import html
2+
13
# Django imports
24
from django.utils import timezone
35

@@ -16,7 +18,10 @@
1618
DraftIssueLabel,
1719
DraftIssueCycle,
1820
DraftIssueModule,
21+
ProjectMember,
22+
EstimatePoint,
1923
)
24+
from plane.app.permissions import ROLE
2025

2126

2227
class DraftIssueCreateSerializer(BaseSerializer):
@@ -57,14 +62,77 @@ def to_representation(self, instance):
5762
data["label_ids"] = label_ids if label_ids else []
5863
return data
5964

60-
def validate(self, data):
65+
def validate(self, attrs):
6166
if (
62-
data.get("start_date", None) is not None
63-
and data.get("target_date", None) is not None
64-
and data.get("start_date", None) > data.get("target_date", None)
67+
attrs.get("start_date", None) is not None
68+
and attrs.get("target_date", None) is not None
69+
and attrs.get("start_date", None) > attrs.get("target_date", None)
6570
):
6671
raise serializers.ValidationError("Start date cannot exceed target date")
67-
return data
72+
73+
try:
74+
if attrs.get("description_html", None) is not None:
75+
parsed = html.fromstring(attrs["description_html"])
76+
parsed_str = html.tostring(parsed, encoding="unicode")
77+
attrs["description_html"] = parsed_str
78+
79+
except Exception:
80+
raise serializers.ValidationError("Invalid HTML passed")
81+
82+
# Validate assignees are from project
83+
if attrs.get("assignee_ids", []):
84+
attrs["assignee_ids"] = ProjectMember.objects.filter(
85+
project_id=self.context["project_id"],
86+
role__gte=ROLE.MEMBER.value,
87+
is_active=True,
88+
member_id__in=attrs["assignee_ids"],
89+
).values_list("member_id", flat=True)
90+
91+
# Validate labels are from project
92+
if attrs.get("label_ids"):
93+
label_ids = [label.id for label in attrs["label_ids"]]
94+
attrs["label_ids"] = list(
95+
Label.objects.filter(
96+
project_id=self.context.get("project_id"), id__in=label_ids
97+
).values_list("id", flat=True)
98+
)
99+
100+
# # Check state is from the project only else raise validation error
101+
if (
102+
attrs.get("state")
103+
and not State.objects.filter(
104+
project_id=self.context.get("project_id"),
105+
pk=attrs.get("state").id,
106+
).exists()
107+
):
108+
raise serializers.ValidationError(
109+
"State is not valid please pass a valid state_id"
110+
)
111+
112+
# # Check parent issue is from workspace as it can be cross workspace
113+
if (
114+
attrs.get("parent")
115+
and not Issue.objects.filter(
116+
project_id=self.context.get("project_id"),
117+
pk=attrs.get("parent").id,
118+
).exists()
119+
):
120+
raise serializers.ValidationError(
121+
"Parent is not valid issue_id please pass a valid issue_id"
122+
)
123+
124+
if (
125+
attrs.get("estimate_point")
126+
and not EstimatePoint.objects.filter(
127+
project_id=self.context.get("project_id"),
128+
pk=attrs.get("estimate_point").id,
129+
).exists()
130+
):
131+
raise serializers.ValidationError(
132+
"Estimate point is not valid please pass a valid estimate_point_id"
133+
)
134+
135+
return attrs
68136

69137
def create(self, validated_data):
70138
assignees = validated_data.pop("assignee_ids", None)
@@ -89,14 +157,14 @@ def create(self, validated_data):
89157
DraftIssueAssignee.objects.bulk_create(
90158
[
91159
DraftIssueAssignee(
92-
assignee=user,
160+
assignee_id=assignee_id,
93161
draft_issue=issue,
94162
workspace_id=workspace_id,
95163
project_id=project_id,
96164
created_by_id=created_by_id,
97165
updated_by_id=updated_by_id,
98166
)
99-
for user in assignees
167+
for assignee_id in assignees
100168
],
101169
batch_size=10,
102170
)
@@ -105,14 +173,14 @@ def create(self, validated_data):
105173
DraftIssueLabel.objects.bulk_create(
106174
[
107175
DraftIssueLabel(
108-
label=label,
176+
label_id=label_id,
109177
draft_issue=issue,
110178
project_id=project_id,
111179
workspace_id=workspace_id,
112180
created_by_id=created_by_id,
113181
updated_by_id=updated_by_id,
114182
)
115-
for label in labels
183+
for label_id in labels
116184
],
117185
batch_size=10,
118186
)
@@ -163,14 +231,14 @@ def update(self, instance, validated_data):
163231
DraftIssueAssignee.objects.bulk_create(
164232
[
165233
DraftIssueAssignee(
166-
assignee=user,
234+
assignee_id=assignee_id,
167235
draft_issue=instance,
168236
workspace_id=workspace_id,
169237
project_id=project_id,
170238
created_by_id=created_by_id,
171239
updated_by_id=updated_by_id,
172240
)
173-
for user in assignees
241+
for assignee_id in assignees
174242
],
175243
batch_size=10,
176244
)

apps/api/plane/app/serializers/issue.py

Lines changed: 62 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
from lxml import html
2+
13
# Django imports
24
from django.utils import timezone
35
from django.core.validators import URLValidator
@@ -37,6 +39,7 @@
3739
IssueVersion,
3840
IssueDescriptionVersion,
3941
ProjectMember,
42+
EstimatePoint,
4043
)
4144

4245

@@ -119,6 +122,16 @@ def validate(self, attrs):
119122
):
120123
raise serializers.ValidationError("Start date cannot exceed target date")
121124

125+
try:
126+
if attrs.get("description_html", None) is not None:
127+
parsed = html.fromstring(attrs["description_html"])
128+
parsed_str = html.tostring(parsed, encoding="unicode")
129+
attrs["description_html"] = parsed_str
130+
131+
except Exception:
132+
raise serializers.ValidationError("Invalid HTML passed")
133+
134+
# Validate assignees are from project
122135
if attrs.get("assignee_ids", []):
123136
attrs["assignee_ids"] = ProjectMember.objects.filter(
124137
project_id=self.context["project_id"],
@@ -127,6 +140,51 @@ def validate(self, attrs):
127140
member_id__in=attrs["assignee_ids"],
128141
).values_list("member_id", flat=True)
129142

143+
# Validate labels are from project
144+
if attrs.get("label_ids"):
145+
label_ids = [label.id for label in attrs["label_ids"]]
146+
attrs["label_ids"] = list(
147+
Label.objects.filter(
148+
project_id=self.context.get("project_id"),
149+
id__in=label_ids,
150+
).values_list("id", flat=True)
151+
)
152+
153+
# Check state is from the project only else raise validation error
154+
if (
155+
attrs.get("state")
156+
and not State.objects.filter(
157+
project_id=self.context.get("project_id"),
158+
pk=attrs.get("state").id,
159+
).exists()
160+
):
161+
raise serializers.ValidationError(
162+
"State is not valid please pass a valid state_id"
163+
)
164+
165+
# Check parent issue is from workspace as it can be cross workspace
166+
if (
167+
attrs.get("parent")
168+
and not Issue.objects.filter(
169+
project_id=self.context.get("project_id"),
170+
pk=attrs.get("parent").id,
171+
).exists()
172+
):
173+
raise serializers.ValidationError(
174+
"Parent is not valid issue_id please pass a valid issue_id"
175+
)
176+
177+
if (
178+
attrs.get("estimate_point")
179+
and not EstimatePoint.objects.filter(
180+
project_id=self.context.get("project_id"),
181+
pk=attrs.get("estimate_point").id,
182+
).exists()
183+
):
184+
raise serializers.ValidationError(
185+
"Estimate point is not valid please pass a valid estimate_point_id"
186+
)
187+
130188
return attrs
131189

132190
def create(self, validated_data):
@@ -190,14 +248,14 @@ def create(self, validated_data):
190248
IssueLabel.objects.bulk_create(
191249
[
192250
IssueLabel(
193-
label=label,
251+
label_id=label_id,
194252
issue=issue,
195253
project_id=project_id,
196254
workspace_id=workspace_id,
197255
created_by_id=created_by_id,
198256
updated_by_id=updated_by_id,
199257
)
200-
for label in labels
258+
for label_id in labels
201259
],
202260
batch_size=10,
203261
)
@@ -243,14 +301,14 @@ def update(self, instance, validated_data):
243301
IssueLabel.objects.bulk_create(
244302
[
245303
IssueLabel(
246-
label=label,
304+
label_id=label_id,
247305
issue=instance,
248306
project_id=project_id,
249307
workspace_id=workspace_id,
250308
created_by_id=created_by_id,
251309
updated_by_id=updated_by_id,
252310
)
253-
for label in labels
311+
for label_id in labels
254312
],
255313
batch_size=10,
256314
ignore_conflicts=True,

0 commit comments

Comments
 (0)