refactor: update links to different endpoints (#338)

This commit is contained in:
pablohashescobar 2023-02-28 02:09:22 +05:30 committed by GitHub
parent 7b4d7f12f5
commit 1b369feb6a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 126 additions and 115 deletions

View File

@ -36,9 +36,15 @@ from .issue import (
IssueSerializer, IssueSerializer,
IssueFlatSerializer, IssueFlatSerializer,
IssueStateSerializer, IssueStateSerializer,
IssueLinkSerializer,
) )
from .module import ModuleWriteSerializer, ModuleSerializer, ModuleIssueSerializer from .module import (
ModuleWriteSerializer,
ModuleSerializer,
ModuleIssueSerializer,
ModuleLinkSerializer,
)
from .api_token import APITokenSerializer from .api_token import APITokenSerializer

View File

@ -28,11 +28,6 @@ from plane.db.models import (
) )
class IssueLinkCreateSerializer(serializers.Serializer):
url = serializers.CharField(required=True)
title = serializers.CharField(required=False)
class IssueFlatSerializer(BaseSerializer): class IssueFlatSerializer(BaseSerializer):
## Contain only flat fields ## Contain only flat fields
@ -82,11 +77,6 @@ class IssueCreateSerializer(BaseSerializer):
write_only=True, write_only=True,
required=False, required=False,
) )
links_list = serializers.ListField(
child=IssueLinkCreateSerializer(),
write_only=True,
required=False,
)
class Meta: class Meta:
model = Issue model = Issue
@ -105,7 +95,6 @@ class IssueCreateSerializer(BaseSerializer):
assignees = validated_data.pop("assignees_list", None) assignees = validated_data.pop("assignees_list", None)
labels = validated_data.pop("labels_list", None) labels = validated_data.pop("labels_list", None)
blocks = validated_data.pop("blocks_list", None) blocks = validated_data.pop("blocks_list", None)
links = validated_data.pop("links_list", None)
project = self.context["project"] project = self.context["project"]
issue = Issue.objects.create(**validated_data, project=project) issue = Issue.objects.create(**validated_data, project=project)
@ -174,24 +163,6 @@ class IssueCreateSerializer(BaseSerializer):
batch_size=10, batch_size=10,
) )
if links is not None:
IssueLink.objects.bulk_create(
[
IssueLink(
issue=issue,
project=project,
workspace=project.workspace,
created_by=issue.created_by,
updated_by=issue.updated_by,
title=link.get("title", None),
url=link.get("url", None),
)
for link in links
],
batch_size=10,
ignore_conflicts=True,
)
return issue return issue
def update(self, instance, validated_data): def update(self, instance, validated_data):
@ -199,7 +170,6 @@ class IssueCreateSerializer(BaseSerializer):
assignees = validated_data.pop("assignees_list", None) assignees = validated_data.pop("assignees_list", None)
labels = validated_data.pop("labels_list", None) labels = validated_data.pop("labels_list", None)
blocks = validated_data.pop("blocks_list", None) blocks = validated_data.pop("blocks_list", None)
links = validated_data.pop("links_list", None)
if blockers is not None: if blockers is not None:
IssueBlocker.objects.filter(block=instance).delete() IssueBlocker.objects.filter(block=instance).delete()
@ -269,25 +239,6 @@ class IssueCreateSerializer(BaseSerializer):
batch_size=10, batch_size=10,
) )
if links is not None:
IssueLink.objects.filter(issue=instance).delete()
IssueLink.objects.bulk_create(
[
IssueLink(
issue=instance,
project=instance.project,
workspace=instance.project.workspace,
created_by=instance.created_by,
updated_by=instance.updated_by,
title=link.get("title", None),
url=link.get("url", None),
)
for link in links
],
batch_size=10,
ignore_conflicts=True,
)
return super().update(instance, validated_data) return super().update(instance, validated_data)
@ -456,6 +407,15 @@ class IssueLinkSerializer(BaseSerializer):
class Meta: class Meta:
model = IssueLink model = IssueLink
fields = "__all__" fields = "__all__"
read_only_fields = [
"workspace",
"project",
"created_by",
"updated_by",
"created_at",
"updated_at",
"issue",
]
# Issue Serializer with state details # Issue Serializer with state details

View File

@ -10,24 +10,12 @@ from .issue import IssueStateSerializer
from plane.db.models import User, Module, ModuleMember, ModuleIssue, ModuleLink from plane.db.models import User, Module, ModuleMember, ModuleIssue, ModuleLink
class LinkCreateSerializer(serializers.Serializer):
url = serializers.CharField(required=True)
title = serializers.CharField(required=False)
class ModuleWriteSerializer(BaseSerializer): class ModuleWriteSerializer(BaseSerializer):
members_list = serializers.ListField( members_list = serializers.ListField(
child=serializers.PrimaryKeyRelatedField(queryset=User.objects.all()), child=serializers.PrimaryKeyRelatedField(queryset=User.objects.all()),
write_only=True, write_only=True,
required=False, required=False,
) )
links_list = serializers.ListField(
child=LinkCreateSerializer(),
write_only=True,
required=False,
)
class Meta: class Meta:
model = Module model = Module
@ -42,9 +30,7 @@ class ModuleWriteSerializer(BaseSerializer):
] ]
def create(self, validated_data): def create(self, validated_data):
members = validated_data.pop("members_list", None) members = validated_data.pop("members_list", None)
links = validated_data.pop("links_list", None)
project = self.context["project"] project = self.context["project"]
@ -67,30 +53,10 @@ class ModuleWriteSerializer(BaseSerializer):
ignore_conflicts=True, ignore_conflicts=True,
) )
if links is not None:
ModuleLink.objects.bulk_create(
[
ModuleLink(
module=module,
project=project,
workspace=project.workspace,
created_by=module.created_by,
updated_by=module.updated_by,
title=link.get("title", None),
url=link.get("url", None),
)
for link in links
],
batch_size=10,
ignore_conflicts=True,
)
return module return module
def update(self, instance, validated_data): def update(self, instance, validated_data):
members = validated_data.pop("members_list", None) members = validated_data.pop("members_list", None)
links = validated_data.pop("links_list", None)
if members is not None: if members is not None:
ModuleMember.objects.filter(module=instance).delete() ModuleMember.objects.filter(module=instance).delete()
@ -110,25 +76,6 @@ class ModuleWriteSerializer(BaseSerializer):
ignore_conflicts=True, ignore_conflicts=True,
) )
if links is not None:
ModuleLink.objects.filter(module=instance).delete()
ModuleLink.objects.bulk_create(
[
ModuleLink(
module=instance,
project=instance.project,
workspace=instance.project.workspace,
created_by=instance.created_by,
updated_by=instance.updated_by,
title=link.get("title", None),
url=link.get("url", None),
)
for link in links
],
batch_size=10,
ignore_conflicts=True,
)
return super().update(instance, validated_data) return super().update(instance, validated_data)
@ -147,7 +94,6 @@ class ModuleFlatSerializer(BaseSerializer):
class ModuleIssueSerializer(BaseSerializer): class ModuleIssueSerializer(BaseSerializer):
module_detail = ModuleFlatSerializer(read_only=True, source="module") module_detail = ModuleFlatSerializer(read_only=True, source="module")
issue_detail = IssueStateSerializer(read_only=True, source="issue") issue_detail = IssueStateSerializer(read_only=True, source="issue")
sub_issues_count = serializers.IntegerField(read_only=True) sub_issues_count = serializers.IntegerField(read_only=True)
@ -167,7 +113,6 @@ class ModuleIssueSerializer(BaseSerializer):
class ModuleLinkSerializer(BaseSerializer): class ModuleLinkSerializer(BaseSerializer):
created_by_detail = UserLiteSerializer(read_only=True, source="created_by") created_by_detail = UserLiteSerializer(read_only=True, source="created_by")
class Meta: class Meta:
@ -180,11 +125,11 @@ class ModuleLinkSerializer(BaseSerializer):
"updated_by", "updated_by",
"created_at", "created_at",
"updated_at", "updated_at",
"module",
] ]
class ModuleSerializer(BaseSerializer): class ModuleSerializer(BaseSerializer):
project_detail = ProjectSerializer(read_only=True, source="project") project_detail = ProjectSerializer(read_only=True, source="project")
lead_detail = UserLiteSerializer(read_only=True, source="lead") lead_detail = UserLiteSerializer(read_only=True, source="lead")
members_detail = UserLiteSerializer(read_only=True, many=True, source="members") members_detail = UserLiteSerializer(read_only=True, many=True, source="members")

View File

@ -65,6 +65,8 @@ from plane.api.views import (
IssuePropertyViewSet, IssuePropertyViewSet,
LabelViewSet, LabelViewSet,
SubIssuesEndpoint, SubIssuesEndpoint,
IssueLinkViewSet,
ModuleLinkViewSet,
## End Issues ## End Issues
# States # States
StateViewSet, StateViewSet,
@ -573,6 +575,28 @@ urlpatterns = [
SubIssuesEndpoint.as_view(), SubIssuesEndpoint.as_view(),
name="sub-issues", name="sub-issues",
), ),
path(
"workspaces/<str:slug>/projects/<uuid:project_id>/issues/<uuid:issue_id>/issue-links/",
IssueLinkViewSet.as_view(
{
"get": "list",
"post": "create",
}
),
name="project-issue-links",
),
path(
"workspaces/<str:slug>/projects/<uuid:project_id>/issues/<uuid:issue_id>/issue-links/<uuid:pk>/",
IssueLinkViewSet.as_view(
{
"get": "retrieve",
"put": "update",
"patch": "partial_update",
"delete": "destroy",
}
),
name="project-issue-links",
),
## End Issues ## End Issues
## Issue Activity ## Issue Activity
path( path(
@ -705,6 +729,28 @@ urlpatterns = [
), ),
name="project-module-issues", name="project-module-issues",
), ),
path(
"workspaces/<str:slug>/projects/<uuid:project_id>/modules/<uuid:module_id>/module-links/",
ModuleLinkViewSet.as_view(
{
"get": "list",
"post": "create",
}
),
name="project-issue-module-links",
),
path(
"workspaces/<str:slug>/projects/<uuid:project_id>/modules/<uuid:module_id>/module-links/<uuid:pk>/",
ModuleLinkViewSet.as_view(
{
"get": "retrieve",
"put": "update",
"patch": "partial_update",
"delete": "destroy",
}
),
name="project-issue-module-links",
),
## End Modules ## End Modules
# API Tokens # API Tokens
path("api-tokens/", ApiTokenEndpoint.as_view(), name="api-tokens"), path("api-tokens/", ApiTokenEndpoint.as_view(), name="api-tokens"),

View File

@ -58,6 +58,7 @@ from .issue import (
BulkDeleteIssuesEndpoint, BulkDeleteIssuesEndpoint,
UserWorkSpaceIssues, UserWorkSpaceIssues,
SubIssuesEndpoint, SubIssuesEndpoint,
IssueLinkViewSet,
) )
from .auth_extended import ( from .auth_extended import (
@ -76,7 +77,7 @@ from .authentication import (
MagicSignInGenerateEndpoint, MagicSignInGenerateEndpoint,
) )
from .module import ModuleViewSet, ModuleIssueViewSet from .module import ModuleViewSet, ModuleIssueViewSet, ModuleLinkViewSet
from .api_token import ApiTokenEndpoint from .api_token import ApiTokenEndpoint

View File

@ -23,6 +23,7 @@ from plane.api.serializers import (
IssueSerializer, IssueSerializer,
LabelSerializer, LabelSerializer,
IssueFlatSerializer, IssueFlatSerializer,
IssueLinkSerializer,
) )
from plane.api.permissions import ( from plane.api.permissions import (
ProjectEntityPermission, ProjectEntityPermission,
@ -185,7 +186,7 @@ class IssueViewSet(BaseViewSet):
) )
issues = IssueSerializer(issue_queryset, many=True).data issues = IssueSerializer(issue_queryset, many=True).data
## Grouping the results ## Grouping the results
group_by = request.GET.get("group_by", False) group_by = request.GET.get("group_by", False)
if group_by: if group_by:
@ -690,3 +691,29 @@ class SubIssuesEndpoint(BaseAPIView):
{"error": "Something went wrong please try again later"}, {"error": "Something went wrong please try again later"},
status=status.HTTP_400_BAD_REQUEST, status=status.HTTP_400_BAD_REQUEST,
) )
class IssueLinkViewSet(BaseViewSet):
permission_classes = [
ProjectEntityPermission,
]
model = IssueLink
serializer_class = IssueLinkSerializer
def perform_create(self, serializer):
serializer.save(
project_id=self.kwargs.get("project_id"),
issue_id=self.kwargs.get("issue_id"),
)
def get_queryset(self):
return (
super()
.get_queryset()
.filter(workspace__slug=self.kwargs.get("slug"))
.filter(project_id=self.kwargs.get("project_id"))
.filter(issue_id=self.kwargs.get("issue_id"))
.filter(project__project_projectmember__member=self.request.user)
.distinct()
)

View File

@ -17,6 +17,7 @@ from plane.api.serializers import (
ModuleWriteSerializer, ModuleWriteSerializer,
ModuleSerializer, ModuleSerializer,
ModuleIssueSerializer, ModuleIssueSerializer,
ModuleLinkSerializer,
) )
from plane.api.permissions import ProjectEntityPermission from plane.api.permissions import ProjectEntityPermission
from plane.db.models import ( from plane.db.models import (
@ -258,3 +259,29 @@ class ModuleIssueViewSet(BaseViewSet):
{"error": "Something went wrong please try again later"}, {"error": "Something went wrong please try again later"},
status=status.HTTP_400_BAD_REQUEST, status=status.HTTP_400_BAD_REQUEST,
) )
class ModuleLinkViewSet(BaseViewSet):
permission_classes = [
ProjectEntityPermission,
]
model = ModuleLink
serializer_class = ModuleLinkSerializer
def perform_create(self, serializer):
serializer.save(
project_id=self.kwargs.get("project_id"),
module_id=self.kwargs.get("module_id"),
)
def get_queryset(self):
return (
super()
.get_queryset()
.filter(workspace__slug=self.kwargs.get("slug"))
.filter(project_id=self.kwargs.get("project_id"))
.filter(module_id=self.kwargs.get("module_id"))
.filter(project__project_projectmember__member=self.request.user)
.distinct()
)

View File

@ -174,6 +174,7 @@ class IssueLink(ProjectBaseModel):
issue = models.ForeignKey( issue = models.ForeignKey(
"db.Issue", on_delete=models.CASCADE, related_name="issue_link" "db.Issue", on_delete=models.CASCADE, related_name="issue_link"
) )
metadata = models.JSONField(default=dict)
class Meta: class Meta:
verbose_name = "Issue Link" verbose_name = "Issue Link"

View File

@ -7,7 +7,6 @@ from . import ProjectBaseModel
class Module(ProjectBaseModel): class Module(ProjectBaseModel):
name = models.CharField(max_length=255, verbose_name="Module Name") name = models.CharField(max_length=255, verbose_name="Module Name")
description = models.TextField(verbose_name="Module Description", blank=True) description = models.TextField(verbose_name="Module Description", blank=True)
description_text = models.JSONField( description_text = models.JSONField(
@ -41,7 +40,6 @@ class Module(ProjectBaseModel):
through_fields=("module", "member"), through_fields=("module", "member"),
) )
class Meta: class Meta:
unique_together = ["name", "project"] unique_together = ["name", "project"]
verbose_name = "Module" verbose_name = "Module"
@ -54,7 +52,6 @@ class Module(ProjectBaseModel):
class ModuleMember(ProjectBaseModel): class ModuleMember(ProjectBaseModel):
module = models.ForeignKey("db.Module", on_delete=models.CASCADE) module = models.ForeignKey("db.Module", on_delete=models.CASCADE)
member = models.ForeignKey("db.User", on_delete=models.CASCADE) member = models.ForeignKey("db.User", on_delete=models.CASCADE)
@ -70,7 +67,6 @@ class ModuleMember(ProjectBaseModel):
class ModuleIssue(ProjectBaseModel): class ModuleIssue(ProjectBaseModel):
module = models.ForeignKey( module = models.ForeignKey(
"db.Module", on_delete=models.CASCADE, related_name="issue_module" "db.Module", on_delete=models.CASCADE, related_name="issue_module"
) )
@ -89,10 +85,12 @@ class ModuleIssue(ProjectBaseModel):
class ModuleLink(ProjectBaseModel): class ModuleLink(ProjectBaseModel):
title = models.CharField(max_length=255, null=True) title = models.CharField(max_length=255, null=True)
url = models.URLField() url = models.URLField()
module = models.ForeignKey(Module, on_delete=models.CASCADE, related_name="link_module") module = models.ForeignKey(
Module, on_delete=models.CASCADE, related_name="link_module"
)
metadata = models.JSONField(default=dict)
class Meta: class Meta:
verbose_name = "Module Link" verbose_name = "Module Link"
@ -101,4 +99,4 @@ class ModuleLink(ProjectBaseModel):
ordering = ("-created_at",) ordering = ("-created_at",)
def __str__(self): def __str__(self):
return f"{self.module.name} {self.url}" return f"{self.module.name} {self.url}"