feat: issues v2 endpoint (#2713)

* feat: issue v2 listing endpoint

* dev: issues v3 endpoint

* dev: add permission in the grouped endpoint

* dev: update grouped endpoint
This commit is contained in:
Nikhil 2023-11-09 18:24:26 +05:30 committed by GitHub
parent 630e21b954
commit 446981422e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 125 additions and 4 deletions

View File

@ -5,7 +5,7 @@ from django.utils import timezone
from rest_framework import serializers from rest_framework import serializers
# Module imports # Module imports
from .base import BaseSerializer from .base import BaseSerializer, DynamicBaseSerializer
from .user import UserLiteSerializer from .user import UserLiteSerializer
from .state import StateSerializer, StateLiteSerializer from .state import StateSerializer, StateLiteSerializer
from .project import ProjectLiteSerializer from .project import ProjectLiteSerializer
@ -548,7 +548,7 @@ class IssueSerializer(BaseSerializer):
] ]
class IssueLiteSerializer(BaseSerializer): class IssueLiteSerializer(DynamicBaseSerializer):
workspace_detail = WorkspaceLiteSerializer(read_only=True, source="workspace") workspace_detail = WorkspaceLiteSerializer(read_only=True, source="workspace")
project_detail = ProjectLiteSerializer(read_only=True, source="project") project_detail = ProjectLiteSerializer(read_only=True, source="project")
state_detail = StateLiteSerializer(read_only=True, source="state") state_detail = StateLiteSerializer(read_only=True, source="state")

View File

@ -3,6 +3,8 @@ from django.urls import path
from plane.api.views import ( from plane.api.views import (
IssueViewSet, IssueViewSet,
IssueListEndpoint,
IssueListGroupedEndpoint,
LabelViewSet, LabelViewSet,
BulkCreateIssueLabelsEndpoint, BulkCreateIssueLabelsEndpoint,
BulkDeleteIssuesEndpoint, BulkDeleteIssuesEndpoint,
@ -35,6 +37,16 @@ urlpatterns = [
), ),
name="project-issue", name="project-issue",
), ),
path(
"v2/workspaces/<str:slug>/projects/<uuid:project_id>/issues/",
IssueListEndpoint.as_view(),
name="project-issue",
),
path(
"v3/workspaces/<str:slug>/projects/<uuid:project_id>/issues/",
IssueListGroupedEndpoint.as_view(),
name="project-issue",
),
path( path(
"workspaces/<str:slug>/projects/<uuid:project_id>/issues/<uuid:pk>/", "workspaces/<str:slug>/projects/<uuid:project_id>/issues/<uuid:pk>/",
IssueViewSet.as_view( IssueViewSet.as_view(

View File

@ -54,7 +54,12 @@ from .workspace import (
LeaveWorkspaceEndpoint, LeaveWorkspaceEndpoint,
) )
from .state import StateViewSet from .state import StateViewSet
from .view import GlobalViewViewSet, GlobalViewIssuesViewSet, IssueViewViewSet, IssueViewFavoriteViewSet from .view import (
GlobalViewViewSet,
GlobalViewIssuesViewSet,
IssueViewViewSet,
IssueViewFavoriteViewSet,
)
from .cycle import ( from .cycle import (
CycleViewSet, CycleViewSet,
CycleIssueViewSet, CycleIssueViewSet,
@ -65,6 +70,8 @@ from .cycle import (
from .asset import FileAssetEndpoint, UserAssetsEndpoint from .asset import FileAssetEndpoint, UserAssetsEndpoint
from .issue import ( from .issue import (
IssueViewSet, IssueViewSet,
IssueListEndpoint,
IssueListGroupedEndpoint,
WorkSpaceIssuesEndpoint, WorkSpaceIssuesEndpoint,
IssueActivityEndpoint, IssueActivityEndpoint,
IssueCommentViewSet, IssueCommentViewSet,
@ -162,7 +169,11 @@ from .analytic import (
DefaultAnalyticsEndpoint, DefaultAnalyticsEndpoint,
) )
from .notification import NotificationViewSet, UnreadNotificationEndpoint, MarkAllReadNotificationViewSet from .notification import (
NotificationViewSet,
UnreadNotificationEndpoint,
MarkAllReadNotificationViewSet,
)
from .exporter import ExportIssuesEndpoint from .exporter import ExportIssuesEndpoint

View File

@ -312,6 +312,104 @@ class IssueViewSet(BaseViewSet):
return Response(status=status.HTTP_204_NO_CONTENT) return Response(status=status.HTTP_204_NO_CONTENT)
class IssueListEndpoint(BaseAPIView):
permission_classes = [
ProjectEntityPermission,
]
def get(self, request, slug, project_id):
fields = [field for field in request.GET.get("fields", "").split(",") if field]
filters = issue_filters(request.query_params, "GET")
issue_queryset = (
Issue.objects.filter(workspace__slug=slug, project_id=project_id)
.select_related("project")
.select_related("workspace")
.select_related("state")
.select_related("parent")
.prefetch_related("assignees")
.prefetch_related("labels")
.prefetch_related(
Prefetch(
"issue_reactions",
queryset=IssueReaction.objects.select_related("actor"),
)
)
.filter(**filters)
.annotate(cycle_id=F("issue_cycle__cycle_id"))
.annotate(module_id=F("issue_module__module_id"))
.annotate(
link_count=IssueLink.objects.filter(issue=OuterRef("id"))
.order_by()
.annotate(count=Func(F("id"), function="Count"))
.values("count")
)
.annotate(
attachment_count=IssueAttachment.objects.filter(issue=OuterRef("id"))
.order_by()
.annotate(count=Func(F("id"), function="Count"))
.values("count")
)
.distinct()
)
serializer = IssueLiteSerializer(
issue_queryset, many=True, fields=fields if fields else None
)
return Response(serializer.data, status=status.HTTP_200_OK)
class IssueListGroupedEndpoint(BaseAPIView):
permission_classes = [
ProjectEntityPermission,
]
def get(self, request, slug, project_id):
filters = issue_filters(request.query_params, "GET")
fields = [field for field in request.GET.get("fields", "").split(",") if field]
issue_queryset = (
Issue.objects.filter(workspace__slug=slug, project_id=project_id)
.select_related("project")
.select_related("workspace")
.select_related("state")
.select_related("parent")
.prefetch_related("assignees")
.prefetch_related("labels")
.prefetch_related(
Prefetch(
"issue_reactions",
queryset=IssueReaction.objects.select_related("actor"),
)
)
.filter(**filters)
.annotate(cycle_id=F("issue_cycle__cycle_id"))
.annotate(module_id=F("issue_module__module_id"))
.annotate(
link_count=IssueLink.objects.filter(issue=OuterRef("id"))
.order_by()
.annotate(count=Func(F("id"), function="Count"))
.values("count")
)
.annotate(
attachment_count=IssueAttachment.objects.filter(issue=OuterRef("id"))
.order_by()
.annotate(count=Func(F("id"), function="Count"))
.values("count")
)
.distinct()
)
issues = IssueLiteSerializer(issue_queryset, many=True, fields=fields if fields else None).data
issue_dict = {str(issue["id"]): issue for issue in issues}
return Response(
issue_dict,
status=status.HTTP_200_OK,
)
class UserWorkSpaceIssues(BaseAPIView): class UserWorkSpaceIssues(BaseAPIView):
@method_decorator(gzip_page) @method_decorator(gzip_page)
def get(self, request, slug): def get(self, request, slug):