Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion apps/api/plane/api/apps.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,4 @@ def ready(self):
try:
import plane.utils.openapi.auth # noqa
except ImportError:
pass
pass
8 changes: 2 additions & 6 deletions apps/api/plane/api/serializers/asset.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,7 @@ class AssetUpdateSerializer(serializers.Serializer):
and upload confirmation for S3-based file storage workflows.
"""

attributes = serializers.JSONField(
required=False, help_text="Additional attributes to update for the asset"
)
attributes = serializers.JSONField(required=False, help_text="Additional attributes to update for the asset")


class GenericAssetUploadSerializer(serializers.Serializer):
Expand Down Expand Up @@ -85,9 +83,7 @@ class GenericAssetUpdateSerializer(serializers.Serializer):
upload completion marking and metadata finalization.
"""

is_uploaded = serializers.BooleanField(
default=True, help_text="Whether the asset has been successfully uploaded"
)
is_uploaded = serializers.BooleanField(default=True, help_text="Whether the asset has been successfully uploaded")


class FileAssetSerializer(BaseSerializer):
Expand Down
8 changes: 2 additions & 6 deletions apps/api/plane/api/serializers/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,13 +103,9 @@ def to_representation(self, instance):
# Check if field in expansion then expand the field
if expand in expansion:
if isinstance(response.get(expand), list):
exp_serializer = expansion[expand](
getattr(instance, expand), many=True
)
exp_serializer = expansion[expand](getattr(instance, expand), many=True)
else:
exp_serializer = expansion[expand](
getattr(instance, expand)
)
exp_serializer = expansion[expand](getattr(instance, expand))
response[expand] = exp_serializer.data
else:
# You might need to handle this case differently
Expand Down
17 changes: 4 additions & 13 deletions apps/api/plane/api/serializers/cycle.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,14 +55,9 @@ def validate(self, data):
):
raise serializers.ValidationError("Start date cannot exceed end date")

if (
data.get("start_date", None) is not None
and data.get("end_date", None) is not None
):
if data.get("start_date", None) is not None and data.get("end_date", None) is not None:
project_id = self.initial_data.get("project_id") or (
self.instance.project_id
if self.instance and hasattr(self.instance, "project_id")
else None
self.instance.project_id if self.instance and hasattr(self.instance, "project_id") else None
)

if not project_id:
Expand Down Expand Up @@ -166,9 +161,7 @@ class CycleIssueRequestSerializer(serializers.Serializer):
cycle assignment and sprint planning workflows.
"""

issues = serializers.ListField(
child=serializers.UUIDField(), help_text="List of issue IDs to add to the cycle"
)
issues = serializers.ListField(child=serializers.UUIDField(), help_text="List of issue IDs to add to the cycle")


class TransferCycleIssueRequestSerializer(serializers.Serializer):
Expand All @@ -179,6 +172,4 @@ class TransferCycleIssueRequestSerializer(serializers.Serializer):
and relationship updates for sprint reallocation workflows.
"""

new_cycle_id = serializers.UUIDField(
help_text="ID of the target cycle to transfer issues to"
)
new_cycle_id = serializers.UUIDField(help_text="ID of the target cycle to transfer issues to")
12 changes: 3 additions & 9 deletions apps/api/plane/api/serializers/intake.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,9 +98,7 @@ class IntakeIssueUpdateSerializer(BaseSerializer):
and embedded issue updates for issue queue processing workflows.
"""

issue = IssueForIntakeSerializer(
required=False, help_text="Issue data to update in the intake issue"
)
issue = IssueForIntakeSerializer(required=False, help_text="Issue data to update in the intake issue")

class Meta:
model = IntakeIssue
Expand Down Expand Up @@ -132,9 +130,5 @@ class IssueDataSerializer(serializers.Serializer):
"""

name = serializers.CharField(max_length=255, help_text="Issue name")
description_html = serializers.CharField(
required=False, allow_null=True, help_text="Issue description HTML"
)
priority = serializers.ChoiceField(
choices=Issue.PRIORITY_CHOICES, default="none", help_text="Issue priority"
)
description_html = serializers.CharField(required=False, allow_null=True, help_text="Issue description HTML")
priority = serializers.ChoiceField(choices=Issue.PRIORITY_CHOICES, default="none", help_text="Issue priority")
89 changes: 22 additions & 67 deletions apps/api/plane/api/serializers/issue.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,17 +48,13 @@ class IssueSerializer(BaseSerializer):
"""

assignees = serializers.ListField(
child=serializers.PrimaryKeyRelatedField(
queryset=User.objects.values_list("id", flat=True)
),
child=serializers.PrimaryKeyRelatedField(queryset=User.objects.values_list("id", flat=True)),
write_only=True,
required=False,
)

labels = serializers.ListField(
child=serializers.PrimaryKeyRelatedField(
queryset=Label.objects.values_list("id", flat=True)
),
child=serializers.PrimaryKeyRelatedField(queryset=Label.objects.values_list("id", flat=True)),
write_only=True,
required=False,
)
Expand Down Expand Up @@ -90,23 +86,17 @@ def validate(self, data):

# Validate description content for security
if data.get("description_html"):
is_valid, error_msg, sanitized_html = validate_html_content(
data["description_html"]
)
is_valid, error_msg, sanitized_html = validate_html_content(data["description_html"])
if not is_valid:
raise serializers.ValidationError(
{"error": "html content is not valid"}
)
raise serializers.ValidationError({"error": "html content is not valid"})
# Update the data with sanitized HTML if available
if sanitized_html is not None:
data["description_html"] = sanitized_html

if data.get("description_binary"):
is_valid, error_msg = validate_binary_data(data["description_binary"])
if not is_valid:
raise serializers.ValidationError(
{"description_binary": "Invalid binary data"}
)
raise serializers.ValidationError({"description_binary": "Invalid binary data"})

# Validate assignees are from project
if data.get("assignees", []):
Expand All @@ -126,13 +116,9 @@ def validate(self, data):
# Check state is from the project only else raise validation error
if (
data.get("state")
and not State.objects.filter(
project_id=self.context.get("project_id"), pk=data.get("state").id
).exists()
and not State.objects.filter(project_id=self.context.get("project_id"), pk=data.get("state").id).exists()
):
raise serializers.ValidationError(
"State is not valid please pass a valid state_id"
)
raise serializers.ValidationError("State is not valid please pass a valid state_id")

# Check parent issue is from workspace as it can be cross workspace
if (
Expand All @@ -143,9 +129,7 @@ def validate(self, data):
pk=data.get("parent").id,
).exists()
):
raise serializers.ValidationError(
"Parent is not valid issue_id please pass a valid issue_id"
)
raise serializers.ValidationError("Parent is not valid issue_id please pass a valid issue_id")

if (
data.get("estimate_point")
Expand All @@ -155,9 +139,7 @@ def validate(self, data):
pk=data.get("estimate_point").id,
).exists()
):
raise serializers.ValidationError(
"Estimate point is not valid please pass a valid estimate_point_id"
)
raise serializers.ValidationError("Estimate point is not valid please pass a valid estimate_point_id")

return data

Expand All @@ -173,14 +155,10 @@ def create(self, validated_data):

if not issue_type:
# Get default issue type
issue_type = IssueType.objects.filter(
project_issue_types__project_id=project_id, is_default=True
).first()
issue_type = IssueType.objects.filter(project_issue_types__project_id=project_id, is_default=True).first()
issue_type = issue_type

issue = Issue.objects.create(
**validated_data, project_id=project_id, type=issue_type
)
issue = Issue.objects.create(**validated_data, project_id=project_id, type=issue_type)

# Issue Audit Users
created_by_id = issue.created_by_id
Expand Down Expand Up @@ -312,35 +290,26 @@ def to_representation(self, instance):

data["assignees"] = UserLiteSerializer(
User.objects.filter(
pk__in=IssueAssignee.objects.filter(issue=instance).values_list(
"assignee_id", flat=True
)
pk__in=IssueAssignee.objects.filter(issue=instance).values_list("assignee_id", flat=True)
),
many=True,
).data
else:
data["assignees"] = [
str(assignee)
for assignee in IssueAssignee.objects.filter(
issue=instance
).values_list("assignee_id", flat=True)
for assignee in IssueAssignee.objects.filter(issue=instance).values_list("assignee_id", flat=True)
]
if "labels" in self.fields:
if "labels" in self.expand:
data["labels"] = LabelSerializer(
Label.objects.filter(
pk__in=IssueLabel.objects.filter(issue=instance).values_list(
"label_id", flat=True
)
pk__in=IssueLabel.objects.filter(issue=instance).values_list("label_id", flat=True)
),
many=True,
).data
else:
data["labels"] = [
str(label)
for label in IssueLabel.objects.filter(issue=instance).values_list(
"label_id", flat=True
)
str(label) for label in IssueLabel.objects.filter(issue=instance).values_list("label_id", flat=True)
]

return data
Expand Down Expand Up @@ -452,12 +421,8 @@ def validate_url(self, value):

# Validation if url already exists
def create(self, validated_data):
if IssueLink.objects.filter(
url=validated_data.get("url"), issue_id=validated_data.get("issue_id")
).exists():
raise serializers.ValidationError(
{"error": "URL already exists for this Issue"}
)
if IssueLink.objects.filter(url=validated_data.get("url"), issue_id=validated_data.get("issue_id")).exists():
raise serializers.ValidationError({"error": "URL already exists for this Issue"})
return IssueLink.objects.create(**validated_data)


Expand All @@ -478,15 +443,11 @@ class Meta(IssueLinkCreateSerializer.Meta):

def update(self, instance, validated_data):
if (
IssueLink.objects.filter(
url=validated_data.get("url"), issue_id=instance.issue_id
)
IssueLink.objects.filter(url=validated_data.get("url"), issue_id=instance.issue_id)
.exclude(pk=instance.id)
.exists()
):
raise serializers.ValidationError(
{"error": "URL already exists for this Issue"}
)
raise serializers.ValidationError({"error": "URL already exists for this Issue"})

return super().update(instance, validated_data)

Expand Down Expand Up @@ -677,17 +638,13 @@ def get_labels(self, obj):
expand = self.context.get("expand", [])
if "labels" in expand:
# Use prefetched data
return LabelLiteSerializer(
[il.label for il in obj.label_issue.all()], many=True
).data
return LabelLiteSerializer([il.label for il in obj.label_issue.all()], many=True).data
return [il.label_id for il in obj.label_issue.all()]

def get_assignees(self, obj):
expand = self.context.get("expand", [])
if "assignees" in expand:
return UserLiteSerializer(
[ia.assignee for ia in obj.issue_assignee.all()], many=True
).data
return UserLiteSerializer([ia.assignee for ia in obj.issue_assignee.all()], many=True).data
return [ia.assignee_id for ia in obj.issue_assignee.all()]

class Meta:
Expand Down Expand Up @@ -735,8 +692,6 @@ class IssueSearchSerializer(serializers.Serializer):
id = serializers.CharField(required=True, help_text="Issue ID")
name = serializers.CharField(required=True, help_text="Issue name")
sequence_id = serializers.CharField(required=True, help_text="Issue sequence ID")
project__identifier = serializers.CharField(
required=True, help_text="Project identifier"
)
project__identifier = serializers.CharField(required=True, help_text="Project identifier")
project_id = serializers.CharField(required=True, help_text="Project ID")
workspace__slug = serializers.CharField(required=True, help_text="Workspace slug")
28 changes: 12 additions & 16 deletions apps/api/plane/api/serializers/module.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,9 +76,15 @@ def create(self, validated_data):
module_name = validated_data.get("name")
if module_name:
# Lookup for the module name in the module table for that project
if Module.objects.filter(name=module_name, project_id=project_id).exists():
module = Module.objects.filter(name=module_name, project_id=project_id).first()
if module:
raise serializers.ValidationError(
{"error": "Module with this name already exists"}
{
"id": str(module.id),
"code": "MODULE_NAME_ALREADY_EXISTS",
"error": "Module with this name already exists",
"message": "Module with this name already exists",
}
)

module = Module.objects.create(**validated_data, project_id=project_id)
Expand Down Expand Up @@ -123,14 +129,8 @@ def update(self, instance, validated_data):
module_name = validated_data.get("name")
if module_name:
# Lookup for the module name in the module table for that project
if (
Module.objects.filter(name=module_name, project=instance.project)
.exclude(id=instance.id)
.exists()
):
raise serializers.ValidationError(
{"error": "Module with this name already exists"}
)
if Module.objects.filter(name=module_name, project=instance.project).exclude(id=instance.id).exists():
raise serializers.ValidationError({"error": "Module with this name already exists"})

if members is not None:
ModuleMember.objects.filter(module=instance).delete()
Expand Down Expand Up @@ -240,12 +240,8 @@ class Meta:

# Validation if url already exists
def create(self, validated_data):
if ModuleLink.objects.filter(
url=validated_data.get("url"), module_id=validated_data.get("module_id")
).exists():
raise serializers.ValidationError(
{"error": "URL already exists for this Issue"}
)
if ModuleLink.objects.filter(url=validated_data.get("url"), module_id=validated_data.get("module_id")).exists():
raise serializers.ValidationError({"error": "URL already exists for this Issue"})
return ModuleLink.objects.create(**validated_data)


Expand Down
Loading
Loading