From 1573db24c5975264a0946c21b81fed30869e916e Mon Sep 17 00:00:00 2001 From: NarayanBavisetti Date: Mon, 27 May 2024 18:32:11 +0530 Subject: [PATCH] chore: storing and streaming binary response --- apiserver/plane/app/urls/inbox.py | 11 +++++ apiserver/plane/app/urls/issue.py | 11 +++++ apiserver/plane/app/views/__init__.py | 7 +++- apiserver/plane/app/views/inbox/base.py | 47 ++++++++++++++++++++++ apiserver/plane/app/views/issue/base.py | 53 +++++++++++++++++++++++-- 5 files changed, 124 insertions(+), 5 deletions(-) diff --git a/apiserver/plane/app/urls/inbox.py b/apiserver/plane/app/urls/inbox.py index b6848244b..f8b93efd6 100644 --- a/apiserver/plane/app/urls/inbox.py +++ b/apiserver/plane/app/urls/inbox.py @@ -4,6 +4,7 @@ from django.urls import path from plane.app.views import ( InboxViewSet, InboxIssueViewSet, + InboxIssueDescriptionViewSet, ) @@ -50,4 +51,14 @@ urlpatterns = [ ), name="inbox-issue", ), + path( + "workspaces//projects//inbox-issues//description/", + InboxIssueDescriptionViewSet.as_view( + { + "get": "retrieve", + "patch": "partial_update", + } + ), + name="inbox-issue-description", + ), ] diff --git a/apiserver/plane/app/urls/issue.py b/apiserver/plane/app/urls/issue.py index 0d3b9e063..76c63edfb 100644 --- a/apiserver/plane/app/urls/issue.py +++ b/apiserver/plane/app/urls/issue.py @@ -19,6 +19,7 @@ from plane.app.views import ( IssueUserDisplayPropertyEndpoint, IssueViewSet, LabelViewSet, + IssueDescriptionViewSet, ) urlpatterns = [ @@ -49,6 +50,16 @@ urlpatterns = [ ), name="project-issue", ), + path( + "workspaces//projects//issues//description/", + IssueDescriptionViewSet.as_view( + { + "get": "retrieve", + "patch": "partial_update", + } + ), + name="issue-description", + ), path( "workspaces//projects//issue-labels/", LabelViewSet.as_view( diff --git a/apiserver/plane/app/views/__init__.py b/apiserver/plane/app/views/__init__.py index 0c489593d..f1dfc8864 100644 --- a/apiserver/plane/app/views/__init__.py +++ b/apiserver/plane/app/views/__init__.py @@ -106,6 +106,7 @@ from .issue.base import ( IssueListEndpoint, IssueViewSet, IssueUserDisplayPropertyEndpoint, + IssueDescriptionViewSet, BulkDeleteIssuesEndpoint, ) @@ -192,7 +193,11 @@ from .estimate.base import ( BulkEstimatePointEndpoint, ) -from .inbox.base import InboxViewSet, InboxIssueViewSet +from .inbox.base import ( + InboxViewSet, + InboxIssueViewSet, + InboxIssueDescriptionViewSet, +) from .analytic.base import ( AnalyticsEndpoint, diff --git a/apiserver/plane/app/views/inbox/base.py b/apiserver/plane/app/views/inbox/base.py index 7919899fa..a6fa578c5 100644 --- a/apiserver/plane/app/views/inbox/base.py +++ b/apiserver/plane/app/views/inbox/base.py @@ -1,5 +1,6 @@ # Python imports import json +import base64 # Django import from django.utils import timezone @@ -9,6 +10,7 @@ from django.contrib.postgres.aggregates import ArrayAgg from django.contrib.postgres.fields import ArrayField from django.db.models import Value, UUIDField from django.db.models.functions import Coalesce +from django.http import StreamingHttpResponse # Third party imports from rest_framework import status @@ -578,3 +580,48 @@ class InboxIssueViewSet(BaseViewSet): inbox_issue.delete() return Response(status=status.HTTP_204_NO_CONTENT) + + +class InboxIssueDescriptionViewSet(BaseViewSet): + permission_classes = [ + ProjectLitePermission, + ] + + def retrieve(self, request, slug, project_id, pk): + issue = Issue.objects.get( + pk=pk, workspace__slug=slug, project_id=project_id + ) + binary_data = issue.description_binary + + def stream_data(): + if binary_data: + yield binary_data + else: + yield b"" + + response = StreamingHttpResponse( + stream_data(), content_type="application/octet-stream" + ) + response["Content-Disposition"] = ( + 'attachment; filename="issue_description.bin"' + ) + return response + + def partial_update(self, request, slug, project_id, pk): + issue = Issue.objects.get( + pk=pk, workspace__slug=slug, project_id=project_id + ) + + base64_data = request.data.get("description_binary") + + if base64_data: + # Decode the base64 data to bytes + new_binary_data = base64.b64decode(base64_data) + + # Store the updated binary data + issue.description_binary = new_binary_data + issue.description_html = request.data.get("description_html") + issue.save() + return Response({"message": "Updated successfully"}) + else: + return Response({"error": "No binary data provided"}) diff --git a/apiserver/plane/app/views/issue/base.py b/apiserver/plane/app/views/issue/base.py index fad85b79d..8a9e2fe42 100644 --- a/apiserver/plane/app/views/issue/base.py +++ b/apiserver/plane/app/views/issue/base.py @@ -1,6 +1,8 @@ # Python imports import json +import base64 +# Django imports from django.contrib.postgres.aggregates import ArrayAgg from django.contrib.postgres.fields import ArrayField from django.core.serializers.json import DjangoJSONEncoder @@ -19,16 +21,14 @@ from django.db.models import ( When, ) from django.db.models.functions import Coalesce - -# Django imports +from django.http import StreamingHttpResponse from django.utils import timezone from django.utils.decorators import method_decorator from django.views.decorators.gzip import gzip_page -from rest_framework import status # Third Party imports +from rest_framework import status from rest_framework.response import Response - from plane.app.permissions import ( ProjectEntityPermission, ProjectLitePermission, @@ -674,3 +674,48 @@ class BulkDeleteIssuesEndpoint(BaseAPIView): {"message": f"{total_issues} issues were deleted"}, status=status.HTTP_200_OK, ) + + +class IssueDescriptionViewSet(BaseViewSet): + permission_classes = [ + ProjectEntityPermission, + ] + + def retrieve(self, request, slug, project_id, pk): + issue = Issue.objects.get( + pk=pk, workspace__slug=slug, project_id=project_id + ) + binary_data = issue.description_binary + + def stream_data(): + if binary_data: + yield binary_data + else: + yield b"" + + response = StreamingHttpResponse( + stream_data(), content_type="application/octet-stream" + ) + response["Content-Disposition"] = ( + 'attachment; filename="issue_description.bin"' + ) + return response + + def partial_update(self, request, slug, project_id, pk): + issue = Issue.objects.get( + pk=pk, workspace__slug=slug, project_id=project_id + ) + + base64_data = request.data.get("description_binary") + + if base64_data: + # Decode the base64 data to bytes + new_binary_data = base64.b64decode(base64_data) + + # Store the updated binary data + issue.description_binary = new_binary_data + issue.description_html = request.data.get("description_html") + issue.save() + return Response({"message": "Updated successfully"}) + else: + return Response({"error": "No binary data provided"})