chore: replaced v3 issues endpoints (#2945)

* chore: removed v3 endpoints

* chore: replace v3 issues to normal issues endpoints

* build-error: Bulid error is new issue structure

---------

Co-authored-by: NarayanBavisetti <narayan3119@gmail.com>
This commit is contained in:
guru_sainath 2023-11-29 20:26:43 +05:30 committed by sriram veeraghanta
parent b2a948dcae
commit 804313413b
29 changed files with 47 additions and 552 deletions

View File

@ -7,7 +7,6 @@ from plane.app.views import (
CycleDateCheckEndpoint, CycleDateCheckEndpoint,
CycleFavoriteViewSet, CycleFavoriteViewSet,
TransferCycleIssueEndpoint, TransferCycleIssueEndpoint,
CycleIssueGroupedEndpoint,
) )
@ -44,11 +43,6 @@ urlpatterns = [
), ),
name="project-issue-cycle", name="project-issue-cycle",
), ),
path(
"v3/workspaces/<str:slug>/projects/<uuid:project_id>/cycles/<uuid:cycle_id>/cycle-issues/",
CycleIssueGroupedEndpoint.as_view(),
name="project-issue-cycle",
),
path( path(
"workspaces/<str:slug>/projects/<uuid:project_id>/cycles/<uuid:cycle_id>/cycle-issues/<uuid:pk>/", "workspaces/<str:slug>/projects/<uuid:project_id>/cycles/<uuid:cycle_id>/cycle-issues/<uuid:pk>/",
CycleIssueViewSet.as_view( CycleIssueViewSet.as_view(

View File

@ -3,8 +3,6 @@ from django.urls import path
from plane.app.views import ( from plane.app.views import (
IssueViewSet, IssueViewSet,
IssueListEndpoint,
IssueListGroupedEndpoint,
LabelViewSet, LabelViewSet,
BulkCreateIssueLabelsEndpoint, BulkCreateIssueLabelsEndpoint,
BulkDeleteIssuesEndpoint, BulkDeleteIssuesEndpoint,
@ -37,16 +35,6 @@ 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

@ -7,7 +7,6 @@ from plane.app.views import (
ModuleLinkViewSet, ModuleLinkViewSet,
ModuleFavoriteViewSet, ModuleFavoriteViewSet,
BulkImportModulesEndpoint, BulkImportModulesEndpoint,
ModuleIssueGroupedEndpoint,
) )
@ -44,11 +43,6 @@ urlpatterns = [
), ),
name="project-module-issues", name="project-module-issues",
), ),
path(
"v3/workspaces/<str:slug>/projects/<uuid:project_id>/modules/<uuid:module_id>/module-issues/",
ModuleIssueGroupedEndpoint.as_view(),
name="project-issue-cycle",
),
path( path(
"workspaces/<str:slug>/projects/<uuid:project_id>/modules/<uuid:module_id>/module-issues/<uuid:pk>/", "workspaces/<str:slug>/projects/<uuid:project_id>/modules/<uuid:module_id>/module-issues/<uuid:pk>/",
ModuleIssueViewSet.as_view( ModuleIssueViewSet.as_view(

View File

@ -5,8 +5,7 @@ from plane.app.views import (
IssueViewViewSet, IssueViewViewSet,
GlobalViewViewSet, GlobalViewViewSet,
GlobalViewIssuesViewSet, GlobalViewIssuesViewSet,
IssueViewFavoriteViewSet, IssueViewFavoriteViewSet,
GlobalViewIssuesGroupedEndpoint
) )
@ -64,11 +63,6 @@ urlpatterns = [
), ),
name="global-view-issues", name="global-view-issues",
), ),
path(
"v3/workspaces/<str:slug>/issues/",
GlobalViewIssuesGroupedEndpoint.as_view(),
name="global-view-issues",
),
path( path(
"workspaces/<str:slug>/projects/<uuid:project_id>/user-favorite-views/", "workspaces/<str:slug>/projects/<uuid:project_id>/user-favorite-views/",
IssueViewFavoriteViewSet.as_view( IssueViewFavoriteViewSet.as_view(

View File

@ -18,7 +18,6 @@ from plane.app.views import (
WorkspaceUserProfileEndpoint, WorkspaceUserProfileEndpoint,
WorkspaceUserProfileIssuesEndpoint, WorkspaceUserProfileIssuesEndpoint,
WorkspaceLabelsEndpoint, WorkspaceLabelsEndpoint,
WorkspaceUserProfileIssuesGroupedEndpoint
) )
@ -190,11 +189,6 @@ urlpatterns = [
WorkspaceUserProfileIssuesEndpoint.as_view(), WorkspaceUserProfileIssuesEndpoint.as_view(),
name="workspace-user-profile-issues", name="workspace-user-profile-issues",
), ),
path(
"v3/workspaces/<str:slug>/user-issues/<uuid:user_id>/",
WorkspaceUserProfileIssuesGroupedEndpoint.as_view(),
name="workspace-user-profile-issues",
),
path( path(
"workspaces/<str:slug>/labels/", "workspaces/<str:slug>/labels/",
WorkspaceLabelsEndpoint.as_view(), WorkspaceLabelsEndpoint.as_view(),

View File

@ -44,7 +44,6 @@ from .workspace import (
WorkspaceUserProfileEndpoint, WorkspaceUserProfileEndpoint,
WorkspaceUserProfileIssuesEndpoint, WorkspaceUserProfileIssuesEndpoint,
WorkspaceLabelsEndpoint, WorkspaceLabelsEndpoint,
WorkspaceUserProfileIssuesGroupedEndpoint
) )
from .state import StateViewSet from .state import StateViewSet
from .view import ( from .view import (
@ -52,7 +51,6 @@ from .view import (
GlobalViewIssuesViewSet, GlobalViewIssuesViewSet,
IssueViewViewSet, IssueViewViewSet,
IssueViewFavoriteViewSet, IssueViewFavoriteViewSet,
GlobalViewIssuesGroupedEndpoint
) )
from .cycle import ( from .cycle import (
CycleViewSet, CycleViewSet,
@ -60,13 +58,10 @@ from .cycle import (
CycleDateCheckEndpoint, CycleDateCheckEndpoint,
CycleFavoriteViewSet, CycleFavoriteViewSet,
TransferCycleIssueEndpoint, TransferCycleIssueEndpoint,
CycleIssueGroupedEndpoint,
) )
from .asset import FileAssetEndpoint, UserAssetsEndpoint, FileAssetViewSet from .asset import FileAssetEndpoint, UserAssetsEndpoint, FileAssetViewSet
from .issue import ( from .issue import (
IssueViewSet, IssueViewSet,
IssueListEndpoint,
IssueListGroupedEndpoint,
WorkSpaceIssuesEndpoint, WorkSpaceIssuesEndpoint,
IssueActivityEndpoint, IssueActivityEndpoint,
IssueCommentViewSet, IssueCommentViewSet,
@ -108,7 +103,6 @@ from .module import (
ModuleIssueViewSet, ModuleIssueViewSet,
ModuleLinkViewSet, ModuleLinkViewSet,
ModuleFavoriteViewSet, ModuleFavoriteViewSet,
ModuleIssueGroupedEndpoint,
) )
from .api import ApiTokenEndpoint from .api import ApiTokenEndpoint

View File

@ -539,9 +539,8 @@ class CycleIssueViewSet(WebhookMixin, BaseViewSet):
@method_decorator(gzip_page) @method_decorator(gzip_page)
def list(self, request, slug, project_id, cycle_id): def list(self, request, slug, project_id, cycle_id):
fields = [field for field in request.GET.get("fields", "").split(",") if field]
order_by = request.GET.get("order_by", "created_at") order_by = request.GET.get("order_by", "created_at")
group_by = request.GET.get("group_by", False)
sub_group_by = request.GET.get("sub_group_by", False)
filters = issue_filters(request.query_params, "GET") filters = issue_filters(request.query_params, "GET")
issues = ( issues = (
Issue.issue_objects.filter(issue_cycle__cycle_id=cycle_id) Issue.issue_objects.filter(issue_cycle__cycle_id=cycle_id)
@ -576,24 +575,9 @@ class CycleIssueViewSet(WebhookMixin, BaseViewSet):
) )
) )
issues_data = IssueStateSerializer(issues, many=True).data issues = IssueStateSerializer(issues, many=True, fields=fields if fields else None).data
issue_dict = {str(issue["id"]): issue for issue in issues}
if sub_group_by and sub_group_by == group_by: return Response(issue_dict, status=status.HTTP_200_OK)
return Response(
{"error": "Group by and sub group by cannot be same"},
status=status.HTTP_400_BAD_REQUEST,
)
if group_by:
grouped_results = group_results(issues_data, group_by, sub_group_by)
return Response(
grouped_results,
status=status.HTTP_200_OK,
)
return Response(
issues_data, status=status.HTTP_200_OK
)
def create(self, request, slug, project_id, cycle_id): def create(self, request, slug, project_id, cycle_id):
issues = request.data.get("issues", []) issues = request.data.get("issues", [])
@ -709,56 +693,6 @@ class CycleIssueViewSet(WebhookMixin, BaseViewSet):
return Response(status=status.HTTP_204_NO_CONTENT) return Response(status=status.HTTP_204_NO_CONTENT)
class CycleIssueGroupedEndpoint(BaseAPIView):
permission_classes = [
ProjectEntityPermission,
]
def get(self, request, slug, project_id, cycle_id):
filters = issue_filters(request.query_params, "GET")
fields = [field for field in request.GET.get("fields", "").split(",") if field]
issues = (
Issue.issue_objects.filter(issue_cycle__cycle_id=cycle_id)
.annotate(
sub_issues_count=Issue.issue_objects.filter(parent=OuterRef("id"))
.order_by()
.annotate(count=Func(F("id"), function="Count"))
.values("count")
)
.annotate(bridge_id=F("issue_cycle__id"))
.filter(project_id=project_id)
.filter(workspace__slug=slug)
.select_related("project")
.select_related("workspace")
.select_related("state")
.select_related("parent")
.prefetch_related("assignees")
.prefetch_related("labels")
.filter(**filters)
.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")
)
)
issues = IssueStateSerializer(issues, 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 CycleDateCheckEndpoint(BaseAPIView): class CycleDateCheckEndpoint(BaseAPIView):
permission_classes = [ permission_classes = [
ProjectEntityPermission, ProjectEntityPermission,

View File

@ -133,6 +133,7 @@ class IssueViewSet(WebhookMixin, BaseViewSet):
@method_decorator(gzip_page) @method_decorator(gzip_page)
def list(self, request, slug, project_id): def list(self, request, slug, project_id):
fields = [field for field in request.GET.get("fields", "").split(",") if field]
filters = issue_filters(request.query_params, "GET") filters = issue_filters(request.query_params, "GET")
# Custom ordering for priority and state # Custom ordering for priority and state
@ -216,25 +217,9 @@ class IssueViewSet(WebhookMixin, BaseViewSet):
else: else:
issue_queryset = issue_queryset.order_by(order_by_param) issue_queryset = issue_queryset.order_by(order_by_param)
issues = IssueLiteSerializer(issue_queryset, many=True).data issues = IssueLiteSerializer(issue_queryset, many=True, fields=fields if fields else None).data
issue_dict = {str(issue["id"]): issue for issue in issues}
## Grouping the results return Response(issue_dict, status=status.HTTP_200_OK)
group_by = request.GET.get("group_by", False)
sub_group_by = request.GET.get("sub_group_by", False)
if sub_group_by and sub_group_by == group_by:
return Response(
{"error": "Group by and sub group by cannot be same"},
status=status.HTTP_400_BAD_REQUEST,
)
if group_by:
grouped_results = group_results(issues, group_by, sub_group_by)
return Response(
grouped_results,
status=status.HTTP_200_OK,
)
return Response(issues, status=status.HTTP_200_OK)
def create(self, request, slug, project_id): def create(self, request, slug, project_id):
project = Project.objects.get(pk=project_id) project = Project.objects.get(pk=project_id)
@ -312,114 +297,6 @@ class IssueViewSet(WebhookMixin, 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")
archive = request.GET.get("archived", False)
draft = request.GET.get("draft", False)
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)
.filter(is_draft=bool(draft))
.filter(~Q(archived_at__isnull=bool(archive)))
.filter(
models.Q(issue_inbox__status=1)
| models.Q(issue_inbox__status=-1)
| models.Q(issue_inbox__status=2)
| models.Q(issue_inbox__isnull=True)
)
.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):
@ -1094,6 +971,7 @@ class IssueArchiveViewSet(BaseViewSet):
@method_decorator(gzip_page) @method_decorator(gzip_page)
def list(self, request, slug, project_id): def list(self, request, slug, project_id):
fields = [field for field in request.GET.get("fields", "").split(",") if field]
filters = issue_filters(request.query_params, "GET") filters = issue_filters(request.query_params, "GET")
show_sub_issues = request.GET.get("show_sub_issues", "true") show_sub_issues = request.GET.get("show_sub_issues", "true")
@ -1184,14 +1062,9 @@ class IssueArchiveViewSet(BaseViewSet):
else issue_queryset.filter(parent__isnull=True) else issue_queryset.filter(parent__isnull=True)
) )
issues = IssueLiteSerializer(issue_queryset, many=True).data issues = IssueLiteSerializer(issue_queryset, many=True, fields=fields if fields else None).data
issue_dict = {str(issue["id"]): issue for issue in issues}
## Grouping the results return Response(issue_dict, status=status.HTTP_200_OK)
group_by = request.GET.get("group_by", False)
if group_by:
return Response(group_results(issues, group_by), status=status.HTTP_200_OK)
return Response(issues, status=status.HTTP_200_OK)
def retrieve(self, request, slug, project_id, pk=None): def retrieve(self, request, slug, project_id, pk=None):
issue = Issue.objects.get( issue = Issue.objects.get(
@ -1591,6 +1464,7 @@ class IssueDraftViewSet(BaseViewSet):
@method_decorator(gzip_page) @method_decorator(gzip_page)
def list(self, request, slug, project_id): def list(self, request, slug, project_id):
filters = issue_filters(request.query_params, "GET") filters = issue_filters(request.query_params, "GET")
fields = [field for field in request.GET.get("fields", "").split(",") if field]
# Custom ordering for priority and state # Custom ordering for priority and state
priority_order = ["urgent", "high", "medium", "low", "none"] priority_order = ["urgent", "high", "medium", "low", "none"]
@ -1673,18 +1547,9 @@ class IssueDraftViewSet(BaseViewSet):
else: else:
issue_queryset = issue_queryset.order_by(order_by_param) issue_queryset = issue_queryset.order_by(order_by_param)
issues = IssueLiteSerializer(issue_queryset, many=True).data issues = IssueLiteSerializer(issue_queryset, many=True, fields=fields if fields else None).data
issue_dict = {str(issue["id"]): issue for issue in issues}
## Grouping the results return Response(issue_dict, status=status.HTTP_200_OK)
group_by = request.GET.get("group_by", False)
if group_by:
grouped_results = group_results(issues, group_by)
return Response(
grouped_results,
status=status.HTTP_200_OK,
)
return Response(issues, status=status.HTTP_200_OK)
def create(self, request, slug, project_id): def create(self, request, slug, project_id):
project = Project.objects.get(pk=project_id) project = Project.objects.get(pk=project_id)

View File

@ -324,9 +324,8 @@ class ModuleIssueViewSet(WebhookMixin, BaseViewSet):
@method_decorator(gzip_page) @method_decorator(gzip_page)
def list(self, request, slug, project_id, module_id): def list(self, request, slug, project_id, module_id):
fields = [field for field in request.GET.get("fields", "").split(",") if field]
order_by = request.GET.get("order_by", "created_at") order_by = request.GET.get("order_by", "created_at")
group_by = request.GET.get("group_by", False)
sub_group_by = request.GET.get("sub_group_by", False)
filters = issue_filters(request.query_params, "GET") filters = issue_filters(request.query_params, "GET")
issues = ( issues = (
Issue.issue_objects.filter(issue_module__module_id=module_id) Issue.issue_objects.filter(issue_module__module_id=module_id)
@ -360,24 +359,9 @@ class ModuleIssueViewSet(WebhookMixin, BaseViewSet):
.values("count") .values("count")
) )
) )
issues_data = IssueStateSerializer(issues, many=True).data issues = IssueStateSerializer(issues, many=True, fields=fields if fields else None).data
issue_dict = {str(issue["id"]): issue for issue in issues}
if sub_group_by and sub_group_by == group_by: return Response(issue_dict, status=status.HTTP_200_OK)
return Response(
{"error": "Group by and sub group by cannot be same"},
status=status.HTTP_400_BAD_REQUEST,
)
if group_by:
grouped_results = group_results(issues_data, group_by, sub_group_by)
return Response(
grouped_results,
status=status.HTTP_200_OK,
)
return Response(
issues_data, status=status.HTTP_200_OK
)
def create(self, request, slug, project_id, module_id): def create(self, request, slug, project_id, module_id):
issues = request.data.get("issues", []) issues = request.data.get("issues", [])
@ -482,55 +466,6 @@ class ModuleIssueViewSet(WebhookMixin, BaseViewSet):
return Response(status=status.HTTP_204_NO_CONTENT) return Response(status=status.HTTP_204_NO_CONTENT)
class ModuleIssueGroupedEndpoint(BaseAPIView):
permission_classes = [
ProjectEntityPermission,
]
def get(self, request, slug, project_id, module_id):
filters = issue_filters(request.query_params, "GET")
fields = [field for field in request.GET.get("fields", "").split(",") if field]
issues = (
Issue.issue_objects.filter(issue_module__module_id=module_id)
.annotate(
sub_issues_count=Issue.issue_objects.filter(parent=OuterRef("id"))
.order_by()
.annotate(count=Func(F("id"), function="Count"))
.values("count")
)
.annotate(bridge_id=F("issue_module__id"))
.filter(project_id=project_id)
.filter(workspace__slug=slug)
.select_related("project")
.select_related("workspace")
.select_related("state")
.select_related("parent")
.prefetch_related("assignees")
.prefetch_related("labels")
.filter(**filters)
.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")
)
)
issues = IssueStateSerializer(issues, 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 ModuleLinkViewSet(BaseViewSet): class ModuleLinkViewSet(BaseViewSet):
permission_classes = [ permission_classes = [
ProjectEntityPermission, ProjectEntityPermission,

View File

@ -95,6 +95,7 @@ class GlobalViewIssuesViewSet(BaseViewSet):
@method_decorator(gzip_page) @method_decorator(gzip_page)
def list(self, request, slug): def list(self, request, slug):
filters = issue_filters(request.query_params, "GET") filters = issue_filters(request.query_params, "GET")
fields = [field for field in request.GET.get("fields", "").split(",") if field]
# Custom ordering for priority and state # Custom ordering for priority and state
priority_order = ["urgent", "high", "medium", "low", "none"] priority_order = ["urgent", "high", "medium", "low", "none"]
@ -177,73 +178,6 @@ class GlobalViewIssuesViewSet(BaseViewSet):
) )
else: else:
issue_queryset = issue_queryset.order_by(order_by_param) issue_queryset = issue_queryset.order_by(order_by_param)
issues = IssueLiteSerializer(issue_queryset, many=True).data
## Grouping the results
group_by = request.GET.get("group_by", False)
sub_group_by = request.GET.get("sub_group_by", False)
if sub_group_by and sub_group_by == group_by:
return Response(
{"error": "Group by and sub group by cannot be same"},
status=status.HTTP_400_BAD_REQUEST,
)
if group_by:
grouped_results = group_results(issues, group_by, sub_group_by)
return Response(
grouped_results,
status=status.HTTP_200_OK,
)
return Response(issues, status=status.HTTP_200_OK)
class GlobalViewIssuesGroupedEndpoint(BaseAPIView):
permission_classes = [
WorkspaceEntityPermission,
]
def get(self, request, slug):
filters = issue_filters(request.query_params, "GET")
fields = [field for field in request.GET.get("fields", "").split(",") if field]
issue_queryset = (
Issue.issue_objects.annotate(
sub_issues_count=Issue.issue_objects.filter(parent=OuterRef("id"))
.order_by()
.annotate(count=Func(F("id"), function="Count"))
.values("count")
)
.filter(workspace__slug=self.kwargs.get("slug"))
.filter(project__project_projectmember__member=self.request.user)
.filter(**filters)
.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"),
)
)
.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")
)
)
issues = IssueLiteSerializer(issue_queryset, many=True, fields=fields if fields else None).data issues = IssueLiteSerializer(issue_queryset, many=True, fields=fields if fields else None).data
issue_dict = {str(issue["id"]): issue for issue in issues} issue_dict = {str(issue["id"]): issue for issue in issues}
@ -252,6 +186,7 @@ class GlobalViewIssuesGroupedEndpoint(BaseAPIView):
status=status.HTTP_200_OK, status=status.HTTP_200_OK,
) )
class IssueViewViewSet(BaseViewSet): class IssueViewViewSet(BaseViewSet):
serializer_class = IssueViewSerializer serializer_class = IssueViewSerializer
model = IssueView model = IssueView

View File

@ -1207,6 +1207,7 @@ class WorkspaceUserProfileIssuesEndpoint(BaseAPIView):
] ]
def get(self, request, slug, user_id): def get(self, request, slug, user_id):
fields = [field for field in request.GET.get("fields", "").split(",") if field]
filters = issue_filters(request.query_params, "GET") filters = issue_filters(request.query_params, "GET")
# Custom ordering for priority and state # Custom ordering for priority and state
@ -1308,75 +1309,11 @@ class WorkspaceUserProfileIssuesEndpoint(BaseAPIView):
else: else:
issue_queryset = issue_queryset.order_by(order_by_param) issue_queryset = issue_queryset.order_by(order_by_param)
issues = IssueLiteSerializer(issue_queryset, many=True).data
## Grouping the results
group_by = request.GET.get("group_by", False)
if group_by:
grouped_results = group_results(issues, group_by)
return Response(
grouped_results,
status=status.HTTP_200_OK,
)
return Response(issues, status=status.HTTP_200_OK)
class WorkspaceUserProfileIssuesGroupedEndpoint(BaseAPIView):
permission_classes = [
WorkspaceViewerPermission,
]
def get(self, request, slug, user_id):
filters = issue_filters(request.query_params, "GET")
fields = [field for field in request.GET.get("fields", "").split(",") if field]
issue_queryset = (
Issue.issue_objects.filter(
Q(assignees__in=[user_id])
| Q(created_by_id=user_id)
| Q(issue_subscribers__subscriber_id=user_id),
workspace__slug=slug,
project__project_projectmember__member=request.user,
)
.filter(**filters)
.annotate(
sub_issues_count=Issue.issue_objects.filter(parent=OuterRef("id"))
.order_by()
.annotate(count=Func(F("id"), function="Count"))
.values("count")
)
.select_related("project", "workspace", "state", "parent")
.prefetch_related("assignees", "labels")
.prefetch_related(
Prefetch(
"issue_reactions",
queryset=IssueReaction.objects.select_related("actor"),
)
)
.order_by("-created_at")
.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( issues = IssueLiteSerializer(
issue_queryset, many=True, fields=fields if fields else None issue_queryset, many=True, fields=fields if fields else None
).data ).data
issue_dict = {str(issue["id"]): issue for issue in issues} issue_dict = {str(issue["id"]): issue for issue in issues}
return Response( return Response(issue_dict, status=status.HTTP_200_OK)
issue_dict,
status=status.HTTP_200_OK,
)
class WorkspaceLabelsEndpoint(BaseAPIView): class WorkspaceLabelsEndpoint(BaseAPIView):

View File

@ -101,8 +101,8 @@ export const BulkDeleteIssuesModal: React.FC<Props> = (props) => {
const filteredIssues: IIssue[] = const filteredIssues: IIssue[] =
query === "" query === ""
? issues ?? [] ? Object.values(issues ?? {})
: issues?.filter( : Object.values(issues ?? {})?.filter(
(issue) => (issue) =>
issue.name.toLowerCase().includes(query.toLowerCase()) || issue.name.toLowerCase().includes(query.toLowerCase()) ||
`${issue.project_detail.identifier}-${issue.sequence_id}`.toLowerCase().includes(query.toLowerCase()) `${issue.project_detail.identifier}-${issue.sequence_id}`.toLowerCase().includes(query.toLowerCase())

View File

@ -40,7 +40,7 @@ export const SelectDuplicateInboxIssueModal: React.FC<Props> = (props) => {
? () => ? () =>
issueService issueService
.getIssues(workspaceSlug as string, projectId as string) .getIssues(workspaceSlug as string, projectId as string)
.then((res) => res.filter((issue) => issue.id !== issueId)) .then((res) => Object.values(res ?? {}).filter((issue) => issue.id !== issueId))
: null : null
); );

View File

@ -51,27 +51,12 @@ export class CycleService extends APIService {
}); });
} }
async getV3CycleIssues(
workspaceSlug: string,
projectId: string,
cycleId: string,
queries?: any
): Promise<IIssueResponse> {
return this.get(`/api/v3/workspaces/${workspaceSlug}/projects/${projectId}/cycles/${cycleId}/cycle-issues/`, {
params: queries,
})
.then((response) => response?.data)
.catch((error) => {
throw error?.response?.data;
});
}
async getCycleIssuesWithParams( async getCycleIssuesWithParams(
workspaceSlug: string, workspaceSlug: string,
projectId: string, projectId: string,
cycleId: string, cycleId: string,
queries?: any queries?: any
): Promise<IIssue[] | { [key: string]: IIssue[] }> { ): Promise<IIssueResponse> {
return this.get(`/api/workspaces/${workspaceSlug}/projects/${projectId}/cycles/${cycleId}/cycle-issues/`, { return this.get(`/api/workspaces/${workspaceSlug}/projects/${projectId}/cycles/${cycleId}/cycle-issues/`, {
params: queries, params: queries,
}) })

View File

@ -19,16 +19,8 @@ export class IssueService extends APIService {
}); });
} }
async getIssues(workspaceSlug: string, projectId: string): Promise<IIssue[]> { async getIssues(workspaceSlug: string, projectId: string, queries?: any): Promise<IIssueResponse> {
return this.get(`/api/workspaces/${workspaceSlug}/projects/${projectId}/issues/`) return this.get(`/api/workspaces/${workspaceSlug}/projects/${projectId}/issues/`, {
.then((response) => response?.data)
.catch((error) => {
throw error?.response?.data;
});
}
async getV3Issues(workspaceSlug: string, projectId: string, queries?: any): Promise<IIssueResponse> {
return this.get(`/api/v3/workspaces/${workspaceSlug}/projects/${projectId}/issues/`, {
params: queries, params: queries,
}) })
.then((response) => response?.data) .then((response) => response?.data)

View File

@ -9,17 +9,7 @@ export class IssueArchiveService extends APIService {
async getArchivedIssues(workspaceSlug: string, projectId: string, queries?: any): Promise<any> { async getArchivedIssues(workspaceSlug: string, projectId: string, queries?: any): Promise<any> {
return this.get(`/api/workspaces/${workspaceSlug}/projects/${projectId}/archived-issues/`, { return this.get(`/api/workspaces/${workspaceSlug}/projects/${projectId}/archived-issues/`, {
params: queries, params: { ...queries },
})
.then((response) => response?.data)
.catch((error) => {
throw error?.response?.data;
});
}
async getV3ArchivedIssues(workspaceSlug: string, projectId: string, queries?: any): Promise<any> {
return this.get(`/api/v3/workspaces/${workspaceSlug}/projects/${projectId}/issues/`, {
params: { ...queries, archived: true },
}) })
.then((response) => response?.data) .then((response) => response?.data)
.catch((error) => { .catch((error) => {

View File

@ -1,24 +1,15 @@
import { APIService } from "services/api.service"; import { APIService } from "services/api.service";
// helpers // helpers
import { API_BASE_URL } from "helpers/common.helper"; import { API_BASE_URL } from "helpers/common.helper";
import { IIssueResponse } from "store/issues/types";
export class IssueDraftService extends APIService { export class IssueDraftService extends APIService {
constructor() { constructor() {
super(API_BASE_URL); super(API_BASE_URL);
} }
async getDraftIssues(workspaceSlug: string, projectId: string, params?: any): Promise<any> { async getDraftIssues(workspaceSlug: string, projectId: string, query?: any): Promise<IIssueResponse> {
return this.get(`/api/workspaces/${workspaceSlug}/projects/${projectId}/issue-drafts/`, { return this.get(`/api/workspaces/${workspaceSlug}/projects/${projectId}/issue-drafts/`, {
params,
})
.then((response) => response?.data)
.catch((error) => {
throw error?.response;
});
}
async getV3DraftIssues(workspaceSlug: string, projectId: string, query?: any): Promise<any> {
return this.get(`/api/v3/workspaces/${workspaceSlug}/projects/${projectId}/issues/?draft=true`, {
params: { ...query }, params: { ...query },
}) })
.then((response) => response?.data) .then((response) => response?.data)

View File

@ -63,21 +63,13 @@ export class ModuleService extends APIService {
}); });
} }
async getModuleIssues(workspaceSlug: string, projectId: string, moduleId: string): Promise<IIssue[]> { async getModuleIssues(
return this.get(`/api/workspaces/${workspaceSlug}/projects/${projectId}/modules/${moduleId}/module-issues/`)
.then((response) => response?.data)
.catch((error) => {
throw error?.response?.data;
});
}
async getV3ModuleIssues(
workspaceSlug: string, workspaceSlug: string,
projectId: string, projectId: string,
moduleId: string, moduleId: string,
queries?: any queries?: any
): Promise<IIssueResponse> { ): Promise<IIssueResponse> {
return this.get(`/api/v3/workspaces/${workspaceSlug}/projects/${projectId}/modules/${moduleId}/module-issues/`, { return this.get(`/api/workspaces/${workspaceSlug}/projects/${projectId}/modules/${moduleId}/module-issues/`, {
params: queries, params: queries,
}) })
.then((response) => response?.data) .then((response) => response?.data)

View File

@ -152,16 +152,7 @@ export class UserService extends APIService {
}); });
} }
async getUserProfileIssues( async getUserProfileIssues(workspaceSlug: string, userId: string, params: any): Promise<IIssueResponse> {
workspaceSlug: string,
userId: string,
params: any
): Promise<
| {
[key: string]: IIssue[];
}
| IIssue[]
> {
return this.get(`/api/workspaces/${workspaceSlug}/user-issues/${userId}/`, { return this.get(`/api/workspaces/${workspaceSlug}/user-issues/${userId}/`, {
params, params,
}) })
@ -171,16 +162,6 @@ export class UserService extends APIService {
}); });
} }
async getV3UserProfileIssues(workspaceSlug: string, userId: string, params: any): Promise<IIssueResponse> {
return this.get(`/api/v3/workspaces/${workspaceSlug}/user-issues/${userId}/`, {
params,
})
.then((response) => response?.data)
.catch((error) => {
throw error?.response?.data;
});
}
async deactivateAccount() { async deactivateAccount() {
return this.delete(`/api/users/me/`) return this.delete(`/api/users/me/`)
.then((response) => response?.data) .then((response) => response?.data)

View File

@ -233,7 +233,7 @@ export class CycleStore implements ICycleStore {
const _activeCycleIssues = { const _activeCycleIssues = {
...this.active_cycle_issues, ...this.active_cycle_issues,
[cycleId]: _cycleIssues as IIssue[], [cycleId]: Object.values(_cycleIssues) as IIssue[],
}; };
runInAction(() => { runInAction(() => {

View File

@ -39,7 +39,7 @@ export class DraftIssuesStore {
const issues = Array.isArray(issuesResponse) ? { allIssues: issuesResponse } : issuesResponse; const issues = Array.isArray(issuesResponse) ? { allIssues: issuesResponse } : issuesResponse;
runInAction(() => { runInAction(() => {
this.issues = issues; this.issues = Object.values(issues) as any;
this.isIssuesLoading = false; this.isIssuesLoading = false;
}); });
} catch (error) { } catch (error) {

View File

@ -159,7 +159,7 @@ export class GlobalIssuesStore extends IssueBaseStore implements IGlobalIssuesSt
else if (this.currentUserIssueTab === "subscribed") else if (this.currentUserIssueTab === "subscribed")
params = params ? { ...params, subscriber: userId } : { subscriber: userId }; params = params ? { ...params, subscriber: userId } : { subscriber: userId };
const response = await this.userService.getV3UserProfileIssues(workspaceSlug, userId, params); const response = await this.userService.getUserProfileIssues(workspaceSlug, userId, params);
if (!this.currentUserIssueTab) return; if (!this.currentUserIssueTab) return;

View File

@ -173,7 +173,7 @@ export class ProfileIssuesStore extends IssueBaseStore implements IProfileIssues
else if (this.currentUserIssueTab === "subscribed") else if (this.currentUserIssueTab === "subscribed")
params = params ? { ...params, subscriber: userId } : { subscriber: userId }; params = params ? { ...params, subscriber: userId } : { subscriber: userId };
const response = await this.userService.getV3UserProfileIssues(workspaceSlug, userId, params); const response = await this.userService.getUserProfileIssues(workspaceSlug, userId, params);
if (!this.currentUserIssueTab) return; if (!this.currentUserIssueTab) return;

View File

@ -100,7 +100,7 @@ export class ProjectArchivedIssuesStore extends IssueBaseStore implements IProje
this.loader = loadType; this.loader = loadType;
const params = this.rootStore?.projectArchivedIssuesFilter?.appliedFilters; const params = this.rootStore?.projectArchivedIssuesFilter?.appliedFilters;
const response = await this.archivedIssueService.getV3ArchivedIssues(workspaceSlug, projectId, params); const response = await this.archivedIssueService.getArchivedIssues(workspaceSlug, projectId, params);
const _issues = { ...this.issues, [projectId]: { ...response } }; const _issues = { ...this.issues, [projectId]: { ...response } };

View File

@ -168,7 +168,7 @@ export class CycleIssuesStore extends IssueBaseStore implements ICycleIssuesStor
this.currentProjectId = projectId; this.currentProjectId = projectId;
const params = this.rootStore?.cycleIssuesFilter?.appliedFilters; const params = this.rootStore?.cycleIssuesFilter?.appliedFilters;
const response = await this.cycleService.getV3CycleIssues(workspaceSlug, projectId, cycleId, params); const response = await this.cycleService.getCycleIssuesWithParams(workspaceSlug, projectId, cycleId, params);
const _issues = { ...this.issues, [cycleId]: { ...response } }; const _issues = { ...this.issues, [cycleId]: { ...response } };

View File

@ -107,7 +107,7 @@ export class ProjectDraftIssuesStore extends IssueBaseStore implements IProjectD
this.loader = loadType; this.loader = loadType;
const params = this.rootStore?.projectDraftIssuesFilter?.appliedFilters; const params = this.rootStore?.projectDraftIssuesFilter?.appliedFilters;
const response = await this.issueDraftService.getV3DraftIssues(workspaceSlug, projectId, params); const response = await this.issueDraftService.getDraftIssues(workspaceSlug, projectId, params);
const _issues = { ...this.issues, [projectId]: { ...response } }; const _issues = { ...this.issues, [projectId]: { ...response } };

View File

@ -166,7 +166,7 @@ export class ModuleIssuesStore extends IssueBaseStore implements IModuleIssuesSt
this.loader = loadType; this.loader = loadType;
const params = this.rootStore?.moduleIssuesFilter?.appliedFilters; const params = this.rootStore?.moduleIssuesFilter?.appliedFilters;
const response = await this.moduleService.getV3ModuleIssues(workspaceSlug, projectId, moduleId, params); const response = await this.moduleService.getModuleIssues(workspaceSlug, projectId, moduleId, params);
const _issues = { ...this.issues, [moduleId]: { ...response } }; const _issues = { ...this.issues, [moduleId]: { ...response } };

View File

@ -112,7 +112,7 @@ export class ViewIssuesStore extends IssueBaseStore implements IViewIssuesStore
this.loader = loadType; this.loader = loadType;
const params = this.rootStore?.viewIssuesFilter?.appliedFilters; const params = this.rootStore?.viewIssuesFilter?.appliedFilters;
const response = await this.issueService.getV3Issues(workspaceSlug, projectId, params); const response = await this.issueService.getIssues(workspaceSlug, projectId, params);
const _issues = { ...this.issues, [projectId]: { ...response } }; const _issues = { ...this.issues, [projectId]: { ...response } };

View File

@ -112,7 +112,7 @@ export class ProjectIssuesStore extends IssueBaseStore implements IProjectIssues
this.loader = loadType; this.loader = loadType;
const params = this.rootStore?.projectIssuesFilter?.appliedFilters; const params = this.rootStore?.projectIssuesFilter?.appliedFilters;
const response = await this.issueService.getV3Issues(workspaceSlug, projectId, params); const response = await this.issueService.getIssues(workspaceSlug, projectId, params);
const _issues = { ...this.issues, [projectId]: { ...response } }; const _issues = { ...this.issues, [projectId]: { ...response } };