From 19ec8053c95902725ca19589f8530f4fc2a555c5 Mon Sep 17 00:00:00 2001 From: pablohashescobar Date: Wed, 8 Nov 2023 14:35:19 +0530 Subject: [PATCH 1/3] feat: state list endpoint --- apiserver/plane/api/serializers/state.py | 2 -- apiserver/plane/api/urls/state.py | 7 ++++++- apiserver/plane/api/views/__init__.py | 2 +- apiserver/plane/api/views/state.py | 25 +++++++++++++++++------- 4 files changed, 25 insertions(+), 11 deletions(-) diff --git a/apiserver/plane/api/serializers/state.py b/apiserver/plane/api/serializers/state.py index 097bc4c931f..ad416c340dc 100644 --- a/apiserver/plane/api/serializers/state.py +++ b/apiserver/plane/api/serializers/state.py @@ -7,8 +7,6 @@ class StateSerializer(BaseSerializer): - workspace_detail = WorkspaceLiteSerializer(read_only=True, source="workspace") - project_detail = ProjectLiteSerializer(read_only=True, source="project") class Meta: model = State diff --git a/apiserver/plane/api/urls/state.py b/apiserver/plane/api/urls/state.py index bcfd80cd7cd..f0fb7faa4ae 100644 --- a/apiserver/plane/api/urls/state.py +++ b/apiserver/plane/api/urls/state.py @@ -1,7 +1,7 @@ from django.urls import path -from plane.api.views import StateViewSet +from plane.api.views import StateViewSet, StateListEndpoint urlpatterns = [ @@ -15,6 +15,11 @@ ), name="project-states", ), + path( + "v2/workspaces//projects//states/", + StateListEndpoint.as_view(), + name="project-states", + ), path( "workspaces//projects//states//", StateViewSet.as_view( diff --git a/apiserver/plane/api/views/__init__.py b/apiserver/plane/api/views/__init__.py index 8f4b2fb9d98..fe2897c4798 100644 --- a/apiserver/plane/api/views/__init__.py +++ b/apiserver/plane/api/views/__init__.py @@ -53,7 +53,7 @@ WorkspaceLabelsEndpoint, LeaveWorkspaceEndpoint, ) -from .state import StateViewSet +from .state import StateViewSet, StateListEndpoint from .view import GlobalViewViewSet, GlobalViewIssuesViewSet, IssueViewViewSet, IssueViewFavoriteViewSet from .cycle import ( CycleViewSet, diff --git a/apiserver/plane/api/views/state.py b/apiserver/plane/api/views/state.py index 063abf0e312..741aa2cbe66 100644 --- a/apiserver/plane/api/views/state.py +++ b/apiserver/plane/api/views/state.py @@ -61,24 +61,35 @@ def list(self, request, slug, project_id): def destroy(self, request, slug, project_id, pk): state = State.objects.get( ~Q(name="Triage"), - pk=pk, project_id=project_id, workspace__slug=slug, + pk=pk, + project_id=project_id, + workspace__slug=slug, ) if state.default: - return Response( - {"error": "Default state cannot be deleted"}, status=False - ) + return Response({"error": "Default state cannot be deleted"}, status=False) # Check for any issues in the state issue_exist = Issue.issue_objects.filter(state=pk).exists() if issue_exist: return Response( - { - "error": "The state is not empty, only empty states can be deleted" - }, + {"error": "The state is not empty, only empty states can be deleted"}, status=status.HTTP_400_BAD_REQUEST, ) state.delete() return Response(status=status.HTTP_204_NO_CONTENT) + + +class StateListEndpoint(BaseAPIView): + permission_classes = [ + ProjectEntityPermission, + ] + + def get(self, request, slug, project_id): + states = State.objects.filter( + workspace__slug=slug, project_id=project_id + ) + serializer = StateSerializer(states, many=True) + return Response(serializer.data, status=status.HTTP_200_OK) From 38c378bbb42733541e6868f7d2efca2c99c6f5a2 Mon Sep 17 00:00:00 2001 From: pablohashescobar Date: Wed, 8 Nov 2023 17:39:37 +0530 Subject: [PATCH 2/3] dev: update states endpoint --- apiserver/plane/api/urls/state.py | 7 +----- apiserver/plane/api/views/__init__.py | 2 +- apiserver/plane/api/views/state.py | 32 +++++++++------------------ 3 files changed, 12 insertions(+), 29 deletions(-) diff --git a/apiserver/plane/api/urls/state.py b/apiserver/plane/api/urls/state.py index f0fb7faa4ae..bcfd80cd7cd 100644 --- a/apiserver/plane/api/urls/state.py +++ b/apiserver/plane/api/urls/state.py @@ -1,7 +1,7 @@ from django.urls import path -from plane.api.views import StateViewSet, StateListEndpoint +from plane.api.views import StateViewSet urlpatterns = [ @@ -15,11 +15,6 @@ ), name="project-states", ), - path( - "v2/workspaces//projects//states/", - StateListEndpoint.as_view(), - name="project-states", - ), path( "workspaces//projects//states//", StateViewSet.as_view( diff --git a/apiserver/plane/api/views/__init__.py b/apiserver/plane/api/views/__init__.py index fe2897c4798..8f4b2fb9d98 100644 --- a/apiserver/plane/api/views/__init__.py +++ b/apiserver/plane/api/views/__init__.py @@ -53,7 +53,7 @@ WorkspaceLabelsEndpoint, LeaveWorkspaceEndpoint, ) -from .state import StateViewSet, StateListEndpoint +from .state import StateViewSet from .view import GlobalViewViewSet, GlobalViewIssuesViewSet, IssueViewViewSet, IssueViewFavoriteViewSet from .cycle import ( CycleViewSet, diff --git a/apiserver/plane/api/views/state.py b/apiserver/plane/api/views/state.py index 741aa2cbe66..74812f93d0f 100644 --- a/apiserver/plane/api/views/state.py +++ b/apiserver/plane/api/views/state.py @@ -47,16 +47,17 @@ def create(self, request, slug, project_id): return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) def list(self, request, slug, project_id): - state_dict = dict() states = StateSerializer(self.get_queryset(), many=True).data - - for key, value in groupby( - sorted(states, key=lambda state: state["group"]), - lambda state: state.get("group"), - ): - state_dict[str(key)] = list(value) - - return Response(state_dict, status=status.HTTP_200_OK) + grouped = request.GET.get("grouped", False) + if grouped == "true": + state_dict = {} + for key, value in groupby( + sorted(states, key=lambda state: state["group"]), + lambda state: state.get("group"), + ): + state_dict[str(key)] = list(value) + return Response(state_dict, status=status.HTTP_200_OK) + return Response(states, status=status.HTTP_200_OK) def destroy(self, request, slug, project_id, pk): state = State.objects.get( @@ -80,16 +81,3 @@ def destroy(self, request, slug, project_id, pk): state.delete() return Response(status=status.HTTP_204_NO_CONTENT) - - -class StateListEndpoint(BaseAPIView): - permission_classes = [ - ProjectEntityPermission, - ] - - def get(self, request, slug, project_id): - states = State.objects.filter( - workspace__slug=slug, project_id=project_id - ) - serializer = StateSerializer(states, many=True) - return Response(serializer.data, status=status.HTTP_200_OK) From dc8ae35551a55a8e7ffcd1e637b93bf8d4c6cf72 Mon Sep 17 00:00:00 2001 From: pablohashescobar Date: Wed, 8 Nov 2023 19:11:43 +0530 Subject: [PATCH 3/3] dev: mark default state endpoint --- apiserver/plane/api/urls/state.py | 10 +++++++++- apiserver/plane/api/views/state.py | 10 ++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/apiserver/plane/api/urls/state.py b/apiserver/plane/api/urls/state.py index bcfd80cd7cd..94aa55f24af 100644 --- a/apiserver/plane/api/urls/state.py +++ b/apiserver/plane/api/urls/state.py @@ -20,11 +20,19 @@ StateViewSet.as_view( { "get": "retrieve", - "put": "update", "patch": "partial_update", "delete": "destroy", } ), name="project-state", ), + path( + "workspaces//projects//states//mark-default/", + StateViewSet.as_view( + { + "post": "mark_as_default", + } + ), + name="project-state", + ), ] diff --git a/apiserver/plane/api/views/state.py b/apiserver/plane/api/views/state.py index 74812f93d0f..dbb6e1d7172 100644 --- a/apiserver/plane/api/views/state.py +++ b/apiserver/plane/api/views/state.py @@ -59,6 +59,16 @@ def list(self, request, slug, project_id): return Response(state_dict, status=status.HTTP_200_OK) return Response(states, status=status.HTTP_200_OK) + def mark_as_default(self, request, slug, project_id, pk): + # Select all the states which are marked as default + _ = State.objects.filter( + workspace__slug=slug, project_id=project_id, default=True + ).update(default=False) + _ = State.objects.filter( + workspace__slug=slug, project_id=project_id, pk=pk + ).update(default=True) + return Response(status=status.HTTP_204_NO_CONTENT) + def destroy(self, request, slug, project_id, pk): state = State.objects.get( ~Q(name="Triage"),