diff --git a/apiserver/plane/app/serializers/__init__.py b/apiserver/plane/app/serializers/__init__.py index 3568e2b382..479f08d5a3 100644 --- a/apiserver/plane/app/serializers/__init__.py +++ b/apiserver/plane/app/serializers/__init__.py @@ -72,6 +72,8 @@ from .issue import ( IssueReactionLiteSerializer, IssueAttachmentLiteSerializer, IssueLinkLiteSerializer, + IssueVersionDetailSerializer, + IssueDescriptionVersionDetailSerializer, ) from .module import ( diff --git a/apiserver/plane/app/serializers/issue.py b/apiserver/plane/app/serializers/issue.py index 007f34849a..b4fe260f77 100644 --- a/apiserver/plane/app/serializers/issue.py +++ b/apiserver/plane/app/serializers/issue.py @@ -33,6 +33,8 @@ from plane.db.models import ( IssueVote, IssueRelation, State, + IssueVersion, + IssueDescriptionVersion, ) @@ -667,3 +669,64 @@ class IssueSubscriberSerializer(BaseSerializer): model = IssueSubscriber fields = "__all__" read_only_fields = ["workspace", "project", "issue"] + + +class IssueVersionDetailSerializer(BaseSerializer): + class Meta: + model = IssueVersion + fields = [ + "id", + "workspace", + "project", + "issue", + "parent", + "state", + "estimate_point", + "name", + "priority", + "start_date", + "target_date", + "assignees", + "sequence_id", + "labels", + "sort_order", + "completed_at", + "archived_at", + "is_draft", + "external_source", + "external_id", + "type", + "cycle", + "modules", + "meta", + "name", + "last_saved_at", + "owned_by", + "created_at", + "updated_at", + "created_by", + "updated_by", + ] + read_only_fields = ["workspace", "project", "issue"] + + +class IssueDescriptionVersionDetailSerializer(BaseSerializer): + class Meta: + model = IssueDescriptionVersion + fields = [ + "id", + "workspace", + "project", + "issue", + "description_binary", + "description_html", + "description_stripped", + "description_json", + "last_saved_at", + "owned_by", + "created_at", + "updated_at", + "created_by", + "updated_by", + ] + read_only_fields = ["workspace", "project", "issue"] diff --git a/apiserver/plane/app/urls/issue.py b/apiserver/plane/app/urls/issue.py index 07dfa5112e..f09370d7d1 100644 --- a/apiserver/plane/app/urls/issue.py +++ b/apiserver/plane/app/urls/issue.py @@ -24,6 +24,8 @@ from plane.app.views import ( IssueDetailEndpoint, IssueAttachmentV2Endpoint, IssueBulkUpdateDateEndpoint, + IssueVersionEndpoint, + IssueDescriptionVersionEndpoint, ) urlpatterns = [ @@ -256,4 +258,24 @@ urlpatterns = [ IssueBulkUpdateDateEndpoint.as_view(), name="project-issue-dates", ), + path( + "workspaces//projects//issues//versions/", + IssueVersionEndpoint.as_view(), + name="page-versions", + ), + path( + "workspaces//projects//issues//versions//", + IssueVersionEndpoint.as_view(), + name="page-versions", + ), + path( + "workspaces//projects//issues//description-versions/", + IssueDescriptionVersionEndpoint.as_view(), + name="page-versions", + ), + path( + "workspaces//projects//issues//description-versions//", + IssueDescriptionVersionEndpoint.as_view(), + name="page-versions", + ), ] diff --git a/apiserver/plane/app/views/__init__.py b/apiserver/plane/app/views/__init__.py index 5b3cf0b4a5..6c49ca9a84 100644 --- a/apiserver/plane/app/views/__init__.py +++ b/apiserver/plane/app/views/__init__.py @@ -141,6 +141,8 @@ from .issue.sub_issue import SubIssuesEndpoint from .issue.subscriber import IssueSubscriberViewSet +from .issue.version import IssueVersionEndpoint, IssueDescriptionVersionEndpoint + from .module.base import ( ModuleViewSet, ModuleLinkViewSet, diff --git a/apiserver/plane/app/views/issue/version.py b/apiserver/plane/app/views/issue/version.py new file mode 100644 index 0000000000..ab26ca5a65 --- /dev/null +++ b/apiserver/plane/app/views/issue/version.py @@ -0,0 +1,118 @@ +# Third party imports +from rest_framework import status +from rest_framework.response import Response + +# Module imports +from plane.db.models import IssueVersion, IssueDescriptionVersion +from ..base import BaseAPIView +from plane.app.serializers import ( + IssueVersionDetailSerializer, + IssueDescriptionVersionDetailSerializer, +) +from plane.app.permissions import allow_permission, ROLE +from plane.utils.global_paginator import paginate +from plane.utils.timezone_converter import user_timezone_converter + + +class IssueVersionEndpoint(BaseAPIView): + def process_paginated_result(self, fields, results, timezone): + paginated_data = results.values(*fields) + + datetime_fields = ["created_at", "updated_at"] + paginated_data = user_timezone_converter( + paginated_data, datetime_fields, timezone + ) + + return paginated_data + + @allow_permission(allowed_roles=[ROLE.ADMIN, ROLE.MEMBER, ROLE.GUEST]) + def get(self, request, slug, project_id, issue_id, pk=None): + if pk: + issue_version = IssueVersion.objects.get( + workspace__slug=slug, project_id=project_id, issue_id=issue_id, pk=pk + ) + + serializer = IssueVersionDetailSerializer(issue_version) + return Response(serializer.data, status=status.HTTP_200_OK) + + cursor = request.GET.get("cursor", None) + + required_fields = [ + "id", + "workspace", + "project", + "issue", + "last_saved_at", + "owned_by", + "created_at", + "updated_at", + "created_by", + "updated_by", + ] + + issue_versions_queryset = IssueVersion.objects.filter( + workspace__slug=slug, project_id=project_id, issue_id=issue_id + ) + + paginated_data = paginate( + base_queryset=issue_versions_queryset, + queryset=issue_versions_queryset, + cursor=cursor, + on_result=lambda results: self.process_paginated_result( + required_fields, results, request.user.user_timezone + ), + ) + + return Response(paginated_data, status=status.HTTP_200_OK) + + +class IssueDescriptionVersionEndpoint(BaseAPIView): + def process_paginated_result(self, fields, results, timezone): + paginated_data = results.values(*fields) + + datetime_fields = ["created_at", "updated_at"] + paginated_data = user_timezone_converter( + paginated_data, datetime_fields, timezone + ) + + return paginated_data + + @allow_permission(allowed_roles=[ROLE.ADMIN, ROLE.MEMBER, ROLE.GUEST]) + def get(self, request, slug, project_id, issue_id, pk=None): + if pk: + issue_description_version = IssueDescriptionVersion.objects.get( + workspace__slug=slug, project_id=project_id, issue_id=issue_id, pk=pk + ) + + serializer = IssueDescriptionVersionDetailSerializer( + issue_description_version + ) + return Response(serializer.data, status=status.HTTP_200_OK) + + cursor = request.GET.get("cursor", None) + + required_fields = [ + "id", + "workspace", + "project", + "issue", + "last_saved_at", + "owned_by", + "created_at", + "updated_at", + "created_by", + "updated_by", + ] + + issue_description_versions_queryset = IssueDescriptionVersion.objects.filter( + workspace__slug=slug, project_id=project_id, issue_id=issue_id + ) + paginated_data = paginate( + base_queryset=issue_description_versions_queryset, + queryset=issue_description_versions_queryset, + cursor=cursor, + on_result=lambda results: self.process_paginated_result( + required_fields, results, request.user.user_timezone + ), + ) + return Response(paginated_data, status=status.HTTP_200_OK)