From 1b369feb6abe75c81a2a6ce45076b09e7177c6cb Mon Sep 17 00:00:00 2001 From: pablohashescobar <118773738+pablohashescobar@users.noreply.github.com> Date: Tue, 28 Feb 2023 02:09:22 +0530 Subject: [PATCH] refactor: update links to different endpoints (#338) --- apiserver/plane/api/serializers/__init__.py | 8 ++- apiserver/plane/api/serializers/issue.py | 58 ++++----------------- apiserver/plane/api/serializers/module.py | 57 +------------------- apiserver/plane/api/urls.py | 46 ++++++++++++++++ apiserver/plane/api/views/__init__.py | 3 +- apiserver/plane/api/views/issue.py | 29 ++++++++++- apiserver/plane/api/views/module.py | 27 ++++++++++ apiserver/plane/db/models/issue.py | 1 + apiserver/plane/db/models/module.py | 12 ++--- 9 files changed, 126 insertions(+), 115 deletions(-) diff --git a/apiserver/plane/api/serializers/__init__.py b/apiserver/plane/api/serializers/__init__.py index 183129939..3040930b4 100644 --- a/apiserver/plane/api/serializers/__init__.py +++ b/apiserver/plane/api/serializers/__init__.py @@ -36,9 +36,15 @@ from .issue import ( IssueSerializer, IssueFlatSerializer, IssueStateSerializer, + IssueLinkSerializer, ) -from .module import ModuleWriteSerializer, ModuleSerializer, ModuleIssueSerializer +from .module import ( + ModuleWriteSerializer, + ModuleSerializer, + ModuleIssueSerializer, + ModuleLinkSerializer, +) from .api_token import APITokenSerializer diff --git a/apiserver/plane/api/serializers/issue.py b/apiserver/plane/api/serializers/issue.py index 6a3c06e22..e3afe75cf 100644 --- a/apiserver/plane/api/serializers/issue.py +++ b/apiserver/plane/api/serializers/issue.py @@ -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): ## Contain only flat fields @@ -82,11 +77,6 @@ class IssueCreateSerializer(BaseSerializer): write_only=True, required=False, ) - links_list = serializers.ListField( - child=IssueLinkCreateSerializer(), - write_only=True, - required=False, - ) class Meta: model = Issue @@ -105,7 +95,6 @@ class IssueCreateSerializer(BaseSerializer): assignees = validated_data.pop("assignees_list", None) labels = validated_data.pop("labels_list", None) blocks = validated_data.pop("blocks_list", None) - links = validated_data.pop("links_list", None) project = self.context["project"] issue = Issue.objects.create(**validated_data, project=project) @@ -174,24 +163,6 @@ class IssueCreateSerializer(BaseSerializer): 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 def update(self, instance, validated_data): @@ -199,7 +170,6 @@ class IssueCreateSerializer(BaseSerializer): assignees = validated_data.pop("assignees_list", None) labels = validated_data.pop("labels_list", None) blocks = validated_data.pop("blocks_list", None) - links = validated_data.pop("links_list", None) if blockers is not None: IssueBlocker.objects.filter(block=instance).delete() @@ -269,25 +239,6 @@ class IssueCreateSerializer(BaseSerializer): 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) @@ -456,6 +407,15 @@ class IssueLinkSerializer(BaseSerializer): class Meta: model = IssueLink fields = "__all__" + read_only_fields = [ + "workspace", + "project", + "created_by", + "updated_by", + "created_at", + "updated_at", + "issue", + ] # Issue Serializer with state details diff --git a/apiserver/plane/api/serializers/module.py b/apiserver/plane/api/serializers/module.py index 2aa5ec208..ab52c2ec8 100644 --- a/apiserver/plane/api/serializers/module.py +++ b/apiserver/plane/api/serializers/module.py @@ -10,24 +10,12 @@ from .issue import IssueStateSerializer 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): - members_list = serializers.ListField( child=serializers.PrimaryKeyRelatedField(queryset=User.objects.all()), write_only=True, required=False, ) - links_list = serializers.ListField( - child=LinkCreateSerializer(), - write_only=True, - required=False, - ) class Meta: model = Module @@ -42,9 +30,7 @@ class ModuleWriteSerializer(BaseSerializer): ] def create(self, validated_data): - members = validated_data.pop("members_list", None) - links = validated_data.pop("links_list", None) project = self.context["project"] @@ -67,30 +53,10 @@ class ModuleWriteSerializer(BaseSerializer): 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 def update(self, instance, validated_data): - members = validated_data.pop("members_list", None) - links = validated_data.pop("links_list", None) if members is not None: ModuleMember.objects.filter(module=instance).delete() @@ -110,25 +76,6 @@ class ModuleWriteSerializer(BaseSerializer): 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) @@ -147,7 +94,6 @@ class ModuleFlatSerializer(BaseSerializer): class ModuleIssueSerializer(BaseSerializer): - module_detail = ModuleFlatSerializer(read_only=True, source="module") issue_detail = IssueStateSerializer(read_only=True, source="issue") sub_issues_count = serializers.IntegerField(read_only=True) @@ -167,7 +113,6 @@ class ModuleIssueSerializer(BaseSerializer): class ModuleLinkSerializer(BaseSerializer): - created_by_detail = UserLiteSerializer(read_only=True, source="created_by") class Meta: @@ -180,11 +125,11 @@ class ModuleLinkSerializer(BaseSerializer): "updated_by", "created_at", "updated_at", + "module", ] class ModuleSerializer(BaseSerializer): - project_detail = ProjectSerializer(read_only=True, source="project") lead_detail = UserLiteSerializer(read_only=True, source="lead") members_detail = UserLiteSerializer(read_only=True, many=True, source="members") diff --git a/apiserver/plane/api/urls.py b/apiserver/plane/api/urls.py index abed2de62..b52c989d9 100644 --- a/apiserver/plane/api/urls.py +++ b/apiserver/plane/api/urls.py @@ -65,6 +65,8 @@ from plane.api.views import ( IssuePropertyViewSet, LabelViewSet, SubIssuesEndpoint, + IssueLinkViewSet, + ModuleLinkViewSet, ## End Issues # States StateViewSet, @@ -573,6 +575,28 @@ urlpatterns = [ SubIssuesEndpoint.as_view(), name="sub-issues", ), + path( + "workspaces//projects//issues//issue-links/", + IssueLinkViewSet.as_view( + { + "get": "list", + "post": "create", + } + ), + name="project-issue-links", + ), + path( + "workspaces//projects//issues//issue-links//", + IssueLinkViewSet.as_view( + { + "get": "retrieve", + "put": "update", + "patch": "partial_update", + "delete": "destroy", + } + ), + name="project-issue-links", + ), ## End Issues ## Issue Activity path( @@ -705,6 +729,28 @@ urlpatterns = [ ), name="project-module-issues", ), + path( + "workspaces//projects//modules//module-links/", + ModuleLinkViewSet.as_view( + { + "get": "list", + "post": "create", + } + ), + name="project-issue-module-links", + ), + path( + "workspaces//projects//modules//module-links//", + ModuleLinkViewSet.as_view( + { + "get": "retrieve", + "put": "update", + "patch": "partial_update", + "delete": "destroy", + } + ), + name="project-issue-module-links", + ), ## End Modules # API Tokens path("api-tokens/", ApiTokenEndpoint.as_view(), name="api-tokens"), diff --git a/apiserver/plane/api/views/__init__.py b/apiserver/plane/api/views/__init__.py index 9f9fd87d4..00c652906 100644 --- a/apiserver/plane/api/views/__init__.py +++ b/apiserver/plane/api/views/__init__.py @@ -58,6 +58,7 @@ from .issue import ( BulkDeleteIssuesEndpoint, UserWorkSpaceIssues, SubIssuesEndpoint, + IssueLinkViewSet, ) from .auth_extended import ( @@ -76,7 +77,7 @@ from .authentication import ( MagicSignInGenerateEndpoint, ) -from .module import ModuleViewSet, ModuleIssueViewSet +from .module import ModuleViewSet, ModuleIssueViewSet, ModuleLinkViewSet from .api_token import ApiTokenEndpoint diff --git a/apiserver/plane/api/views/issue.py b/apiserver/plane/api/views/issue.py index 68797c296..ca40606ec 100644 --- a/apiserver/plane/api/views/issue.py +++ b/apiserver/plane/api/views/issue.py @@ -23,6 +23,7 @@ from plane.api.serializers import ( IssueSerializer, LabelSerializer, IssueFlatSerializer, + IssueLinkSerializer, ) from plane.api.permissions import ( ProjectEntityPermission, @@ -185,7 +186,7 @@ class IssueViewSet(BaseViewSet): ) issues = IssueSerializer(issue_queryset, many=True).data - + ## Grouping the results group_by = request.GET.get("group_by", False) if group_by: @@ -690,3 +691,29 @@ class SubIssuesEndpoint(BaseAPIView): {"error": "Something went wrong please try again later"}, 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() + ) diff --git a/apiserver/plane/api/views/module.py b/apiserver/plane/api/views/module.py index 1bd93d1c1..ffeadea26 100644 --- a/apiserver/plane/api/views/module.py +++ b/apiserver/plane/api/views/module.py @@ -17,6 +17,7 @@ from plane.api.serializers import ( ModuleWriteSerializer, ModuleSerializer, ModuleIssueSerializer, + ModuleLinkSerializer, ) from plane.api.permissions import ProjectEntityPermission from plane.db.models import ( @@ -258,3 +259,29 @@ class ModuleIssueViewSet(BaseViewSet): {"error": "Something went wrong please try again later"}, 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() + ) diff --git a/apiserver/plane/db/models/issue.py b/apiserver/plane/db/models/issue.py index aea41677e..fc9971000 100644 --- a/apiserver/plane/db/models/issue.py +++ b/apiserver/plane/db/models/issue.py @@ -174,6 +174,7 @@ class IssueLink(ProjectBaseModel): issue = models.ForeignKey( "db.Issue", on_delete=models.CASCADE, related_name="issue_link" ) + metadata = models.JSONField(default=dict) class Meta: verbose_name = "Issue Link" diff --git a/apiserver/plane/db/models/module.py b/apiserver/plane/db/models/module.py index c5dfef588..3371c961b 100644 --- a/apiserver/plane/db/models/module.py +++ b/apiserver/plane/db/models/module.py @@ -7,7 +7,6 @@ from . import ProjectBaseModel class Module(ProjectBaseModel): - name = models.CharField(max_length=255, verbose_name="Module Name") description = models.TextField(verbose_name="Module Description", blank=True) description_text = models.JSONField( @@ -41,7 +40,6 @@ class Module(ProjectBaseModel): through_fields=("module", "member"), ) - class Meta: unique_together = ["name", "project"] verbose_name = "Module" @@ -54,7 +52,6 @@ class Module(ProjectBaseModel): class ModuleMember(ProjectBaseModel): - module = models.ForeignKey("db.Module", on_delete=models.CASCADE) member = models.ForeignKey("db.User", on_delete=models.CASCADE) @@ -70,7 +67,6 @@ class ModuleMember(ProjectBaseModel): class ModuleIssue(ProjectBaseModel): - module = models.ForeignKey( "db.Module", on_delete=models.CASCADE, related_name="issue_module" ) @@ -89,10 +85,12 @@ class ModuleIssue(ProjectBaseModel): class ModuleLink(ProjectBaseModel): - title = models.CharField(max_length=255, null=True) 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: verbose_name = "Module Link" @@ -101,4 +99,4 @@ class ModuleLink(ProjectBaseModel): ordering = ("-created_at",) def __str__(self): - return f"{self.module.name} {self.url}" \ No newline at end of file + return f"{self.module.name} {self.url}"