From 2de3c38c494e5aea768e16cf807a608f9228cc9d Mon Sep 17 00:00:00 2001 From: NarayanBavisetti Date: Wed, 26 Jun 2024 13:17:00 +0530 Subject: [PATCH 1/2] chore: pages custom error codes --- apiserver/plane/app/views/issue/archive.py | 9 +++++--- .../plane/app/views/issue/bulk_operations.py | 23 ++++++++++++++----- apiserver/plane/app/views/page/base.py | 16 +++++++++---- apiserver/plane/utils/error_codes.py | 10 ++++++++ 4 files changed, 44 insertions(+), 14 deletions(-) create mode 100644 apiserver/plane/utils/error_codes.py diff --git a/apiserver/plane/app/views/issue/archive.py b/apiserver/plane/app/views/issue/archive.py index 584edd8f938..dba06028b0f 100644 --- a/apiserver/plane/app/views/issue/archive.py +++ b/apiserver/plane/app/views/issue/archive.py @@ -25,7 +25,7 @@ from plane.app.serializers import ( IssueFlatSerializer, IssueSerializer, - IssueDetailSerializer + IssueDetailSerializer, ) from plane.bgtasks.issue_activites_task import issue_activity from plane.db.models import ( @@ -46,6 +46,7 @@ GroupedOffsetPaginator, SubGroupedOffsetPaginator, ) +from plane.utils.error_codes import ERROR_CODES # Module imports from .. import BaseViewSet, BaseAPIView @@ -341,8 +342,10 @@ def post(self, request, slug, project_id): if issue.state.group not in ["completed", "cancelled"]: return Response( { - "error_code": 4091, - "error_message": "INVALID_ARCHIVE_STATE_GROUP" + "error_code": ERROR_CODES[ + "INVALID_ARCHIVE_STATE_GROUP" + ], + "error_message": "INVALID_ARCHIVE_STATE_GROUP", }, status=status.HTTP_400_BAD_REQUEST, ) diff --git a/apiserver/plane/app/views/issue/bulk_operations.py b/apiserver/plane/app/views/issue/bulk_operations.py index ea663782607..06ec0b4c8c5 100644 --- a/apiserver/plane/app/views/issue/bulk_operations.py +++ b/apiserver/plane/app/views/issue/bulk_operations.py @@ -21,6 +21,7 @@ IssueAssignee, ) from plane.bgtasks.issue_activites_task import issue_activity +from plane.utils.error_codes import ERROR_CODES class BulkIssueOperationsEndpoint(BaseAPIView): @@ -59,14 +60,20 @@ def post(self, request, slug, project_id): properties = request.data.get("properties", {}) - if properties.get("start_date", False) and properties.get("target_date", False): + if properties.get("start_date", False) and properties.get( + "target_date", False + ): if ( - datetime.strptime(properties.get("start_date"), "%Y-%m-%d").date() - > datetime.strptime(properties.get("target_date"), "%Y-%m-%d").date() + datetime.strptime( + properties.get("start_date"), "%Y-%m-%d" + ).date() + > datetime.strptime( + properties.get("target_date"), "%Y-%m-%d" + ).date() ): return Response( { - "error_code": 4100, + "error_code": ERROR_CODES["INVALID_ISSUE_DATES"], "error_message": "INVALID_ISSUE_DATES", }, status=status.HTTP_400_BAD_REQUEST, @@ -124,7 +131,9 @@ def post(self, request, slug, project_id): ): return Response( { - "error_code": 4101, + "error_code": ERROR_CODES[ + "INVALID_ISSUE_START_DATE" + ], "error_message": "INVALID_ISSUE_START_DATE", }, status=status.HTTP_400_BAD_REQUEST, @@ -158,7 +167,9 @@ def post(self, request, slug, project_id): ): return Response( { - "error_code": 4102, + "error_code": ERROR_CODES[ + "INVALID_ISSUE_TARGET_DATE" + ], "error_message": "INVALID_ISSUE_TARGET_DATE", }, status=status.HTTP_400_BAD_REQUEST, diff --git a/apiserver/plane/app/views/page/base.py b/apiserver/plane/app/views/page/base.py index 60fb81eebc8..6228b109e77 100644 --- a/apiserver/plane/app/views/page/base.py +++ b/apiserver/plane/app/views/page/base.py @@ -33,7 +33,7 @@ ProjectMember, ProjectPage, ) - +from plane.utils.error_codes import ERROR_CODES # Module imports from ..base import BaseAPIView, BaseViewSet @@ -465,14 +465,20 @@ def partial_update(self, request, slug, project_id, pk): if page.is_locked: return Response( - {"error": "Page is locked"}, - status=471, + { + "error_code": ERROR_CODES["PAGE_LOCKED"], + "error_message": "PAGE_LOCKED", + }, + status=status.HTTP_400_BAD_REQUEST, ) if page.archived_at: return Response( - {"error": "Page is archived"}, - status=472, + { + "error_code": ERROR_CODES["PAGE_ARCHIVED"], + "error_message": "PAGE_ARCHIVED", + }, + status=status.HTTP_400_BAD_REQUEST, ) base64_data = request.data.get("description_binary") diff --git a/apiserver/plane/utils/error_codes.py b/apiserver/plane/utils/error_codes.py new file mode 100644 index 00000000000..15d38f6bf96 --- /dev/null +++ b/apiserver/plane/utils/error_codes.py @@ -0,0 +1,10 @@ +ERROR_CODES = { + # issues + "INVALID_ARCHIVE_STATE_GROUP": 4091, + "INVALID_ISSUE_DATES": 4100, + "INVALID_ISSUE_START_DATE": 4101, + "INVALID_ISSUE_TARGET_DATE": 4102, + # pages + "PAGE_LOCKED": 4701, + "PAGE_ARCHIVED": 4702, +} From c9b55207f72dfc3ea9bbc6f262ec169af428bb0e Mon Sep 17 00:00:00 2001 From: NarayanBavisetti Date: Tue, 20 Aug 2024 14:18:24 +0530 Subject: [PATCH 2/2] fix: view role permission --- apiserver/plane/app/urls/issue.py | 8 +- apiserver/plane/app/views/__init__.py | 3 - .../plane/app/views/issue/bulk_operations.py | 298 ------------------ apiserver/plane/app/views/view/base.py | 15 +- 4 files changed, 2 insertions(+), 322 deletions(-) delete mode 100644 apiserver/plane/app/views/issue/bulk_operations.py diff --git a/apiserver/plane/app/urls/issue.py b/apiserver/plane/app/urls/issue.py index aa6a8e2f06c..4ad7f611821 100644 --- a/apiserver/plane/app/urls/issue.py +++ b/apiserver/plane/app/urls/issue.py @@ -19,7 +19,6 @@ IssueUserDisplayPropertyEndpoint, IssueViewSet, LabelViewSet, - BulkIssueOperationsEndpoint, BulkArchiveIssuesEndpoint, ) @@ -304,10 +303,5 @@ } ), name="project-issue-draft", - ), - path( - "workspaces//projects//bulk-operation-issues/", - BulkIssueOperationsEndpoint.as_view(), - name="bulk-operations-issues", - ), + ) ] diff --git a/apiserver/plane/app/views/__init__.py b/apiserver/plane/app/views/__init__.py index 9d8929fda51..5568542f70a 100644 --- a/apiserver/plane/app/views/__init__.py +++ b/apiserver/plane/app/views/__init__.py @@ -156,9 +156,6 @@ IssueSubscriberViewSet, ) - -from .issue.bulk_operations import BulkIssueOperationsEndpoint - from .module.base import ( ModuleViewSet, ModuleLinkViewSet, diff --git a/apiserver/plane/app/views/issue/bulk_operations.py b/apiserver/plane/app/views/issue/bulk_operations.py deleted file mode 100644 index ffef0e3edc3..00000000000 --- a/apiserver/plane/app/views/issue/bulk_operations.py +++ /dev/null @@ -1,298 +0,0 @@ -# Python imports -import json -from datetime import datetime - -# Django imports -from django.utils import timezone - -# Third Party imports -from rest_framework.response import Response -from rest_framework import status - -# Module imports -from .. import BaseAPIView -from plane.app.permissions import ( - ProjectEntityPermission, -) -from plane.db.models import ( - Project, - Issue, - IssueLabel, - IssueAssignee, -) -from plane.bgtasks.issue_activities_task import issue_activity -from plane.utils.error_codes import ERROR_CODES - - -class BulkIssueOperationsEndpoint(BaseAPIView): - permission_classes = [ - ProjectEntityPermission, - ] - - def post(self, request, slug, project_id): - issue_ids = request.data.get("issue_ids", []) - if not len(issue_ids): - return Response( - {"error": "Issue IDs are required"}, - status=status.HTTP_400_BAD_REQUEST, - ) - - # Get all the issues - issues = ( - Issue.objects.filter( - workspace__slug=slug, project_id=project_id, pk__in=issue_ids - ) - .select_related("state") - .prefetch_related("labels", "assignees") - ) - # Current epoch - epoch = int(timezone.now().timestamp()) - - # Project details - project = Project.objects.get(workspace__slug=slug, pk=project_id) - workspace_id = project.workspace_id - - # Initialize arrays - bulk_update_issues = [] - bulk_issue_activities = [] - bulk_update_issue_labels = [] - bulk_update_issue_assignees = [] - - properties = request.data.get("properties", {}) - - if properties.get("start_date", False) and properties.get( - "target_date", False - ): - if ( - datetime.strptime( - properties.get("start_date"), "%Y-%m-%d" - ).date() - > datetime.strptime( - properties.get("target_date"), "%Y-%m-%d" - ).date() - ): - return Response( - { - "error_code": ERROR_CODES["INVALID_ISSUE_DATES"], - "error_message": "INVALID_ISSUE_DATES", - }, - status=status.HTTP_400_BAD_REQUEST, - ) - - for issue in issues: - # Priority - if properties.get("priority", False): - bulk_issue_activities.append( - { - "type": "issue.activity.updated", - "requested_data": json.dumps( - {"priority": properties.get("priority")} - ), - "current_instance": json.dumps( - {"priority": (issue.priority)} - ), - "issue_id": str(issue.id), - "actor_id": str(request.user.id), - "project_id": str(project_id), - "epoch": epoch, - } - ) - issue.priority = properties.get("priority") - - # State - if properties.get("state_id", False): - bulk_issue_activities.append( - { - "type": "issue.activity.updated", - "requested_data": json.dumps( - {"state": properties.get("state")} - ), - "current_instance": json.dumps( - {"state": str(issue.state_id)} - ), - "issue_id": str(issue.id), - "actor_id": str(request.user.id), - "project_id": str(project_id), - "epoch": epoch, - } - ) - issue.state_id = properties.get("state_id") - - # Start date - if properties.get("start_date", False): - if ( - issue.target_date - and not properties.get("target_date", False) - and issue.target_date - <= datetime.strptime( - properties.get("start_date"), "%Y-%m-%d" - ).date() - ): - return Response( - { - "error_code": ERROR_CODES[ - "INVALID_ISSUE_START_DATE" - ], - "error_message": "INVALID_ISSUE_START_DATE", - }, - status=status.HTTP_400_BAD_REQUEST, - ) - bulk_issue_activities.append( - { - "type": "issue.activity.updated", - "requested_data": json.dumps( - {"start_date": properties.get("start_date")} - ), - "current_instance": json.dumps( - {"start_date": str(issue.start_date)} - ), - "issue_id": str(issue.id), - "actor_id": str(request.user.id), - "project_id": str(project_id), - "epoch": epoch, - } - ) - issue.start_date = properties.get("start_date") - - # Target date - if properties.get("target_date", False): - if ( - issue.start_date - and not properties.get("start_date", False) - and issue.start_date - >= datetime.strptime( - properties.get("target_date"), "%Y-%m-%d" - ).date() - ): - return Response( - { - "error_code": ERROR_CODES[ - "INVALID_ISSUE_TARGET_DATE" - ], - "error_message": "INVALID_ISSUE_TARGET_DATE", - }, - status=status.HTTP_400_BAD_REQUEST, - ) - bulk_issue_activities.append( - { - "type": "issue.activity.updated", - "requested_data": json.dumps( - {"target_date": properties.get("target_date")} - ), - "current_instance": json.dumps( - {"target_date": str(issue.target_date)} - ), - "issue_id": str(issue.id), - "actor_id": str(request.user.id), - "project_id": str(project_id), - "epoch": epoch, - } - ) - issue.target_date = properties.get("target_date") - - bulk_update_issues.append(issue) - - # Labels - if properties.get("label_ids", []): - for label_id in properties.get("label_ids", []): - bulk_update_issue_labels.append( - IssueLabel( - issue=issue, - label_id=label_id, - created_by=request.user, - project_id=project_id, - workspace_id=workspace_id, - ) - ) - bulk_issue_activities.append( - { - "type": "issue.activity.updated", - "requested_data": json.dumps( - {"label_ids": properties.get("label_ids", [])} - ), - "current_instance": json.dumps( - { - "label_ids": [ - str(label.id) - for label in issue.labels.all() - ] - } - ), - "issue_id": str(issue.id), - "actor_id": str(request.user.id), - "project_id": str(project_id), - "epoch": epoch, - } - ) - - # Assignees - if properties.get("assignee_ids", []): - for assignee_id in properties.get( - "assignee_ids", issue.assignees - ): - bulk_update_issue_assignees.append( - IssueAssignee( - issue=issue, - assignee_id=assignee_id, - created_by=request.user, - project_id=project_id, - workspace_id=workspace_id, - ) - ) - bulk_issue_activities.append( - { - "type": "issue.activity.updated", - "requested_data": json.dumps( - { - "assignee_ids": properties.get( - "assignee_ids", [] - ) - } - ), - "current_instance": json.dumps( - { - "assignee_ids": [ - str(assignee.id) - for assignee in issue.assignees.all() - ] - } - ), - "issue_id": str(issue.id), - "actor_id": str(request.user.id), - "project_id": str(project_id), - "epoch": epoch, - } - ) - - # Bulk update all the objects - Issue.objects.bulk_update( - bulk_update_issues, - [ - "priority", - "start_date", - "target_date", - "state", - ], - batch_size=100, - ) - - # Create new labels - IssueLabel.objects.bulk_create( - bulk_update_issue_labels, - ignore_conflicts=True, - batch_size=100, - ) - - # Create new assignees - IssueAssignee.objects.bulk_create( - bulk_update_issue_assignees, - ignore_conflicts=True, - batch_size=100, - ) - # update the issue activity - [ - issue_activity.delay(**activity) - for activity in bulk_issue_activities - ] - - return Response(status=status.HTTP_204_NO_CONTENT) diff --git a/apiserver/plane/app/views/view/base.py b/apiserver/plane/app/views/view/base.py index c16f7ef5d8f..4a571ef2576 100644 --- a/apiserver/plane/app/views/view/base.py +++ b/apiserver/plane/app/views/view/base.py @@ -149,7 +149,7 @@ def retrieve(self, request, slug, pk): ) @allow_permission( - allowed_roles=[ROLE.ADMIN], + allowed_roles=[], level="WORKSPACE", creator=True, model=IssueView, @@ -159,19 +159,6 @@ def destroy(self, request, slug, pk): pk=pk, workspace__slug=slug, ) - if not ( - WorkspaceMember.objects.filter( - workspace__slug=slug, - member=request.user, - role=20, - is_active=True, - ).exists() - and workspace_view.owned_by_id != request.user.id - ): - return Response( - {"error": "You do not have permission to delete this view"}, - status=status.HTTP_403_FORBIDDEN, - ) workspace_member = WorkspaceMember.objects.filter( workspace__slug=slug,