From 73b38f4db97900706e796313f5dc65a8d34b0e12 Mon Sep 17 00:00:00 2001 From: Nikhil <118773738+pablohashescobar@users.noreply.github.com> Date: Mon, 24 Jul 2023 12:08:47 +0530 Subject: [PATCH] feat: cross project issue linking (#1609) * dev: sub issue listing * feat: cross project issue linking * dev: project search * dev: workspace search logic * dev: return state and project details for parent issues * dev: issue state flat serializer * dev: id for lite serializer * dev: project name in for the response issues * dev: issue cross project * dev: issue project identifiers * dev: blocked and blocked by activity --- apiserver/plane/api/serializers/issue.py | 51 +++++++++++++++++-- apiserver/plane/api/views/issue.py | 4 +- apiserver/plane/api/views/search.py | 6 ++- .../plane/bgtasks/issue_activites_task.py | 28 +++++----- 4 files changed, 67 insertions(+), 22 deletions(-) diff --git a/apiserver/plane/api/serializers/issue.py b/apiserver/plane/api/serializers/issue.py index 7aeee7d70..6a8ccee84 100644 --- a/apiserver/plane/api/serializers/issue.py +++ b/apiserver/plane/api/serializers/issue.py @@ -50,6 +50,20 @@ class IssueFlatSerializer(BaseSerializer): ] +class IssueProjectLiteSerializer(BaseSerializer): + project_detail = ProjectLiteSerializer(source="project", read_only=True) + + class Meta: + model = Issue + fields = [ + "id", + "project_detail", + "name", + "sequence_id", + ] + read_only_fields = fields + + ##TODO: Find a better way to write this serializer ## Find a better approach to save manytomany? class IssueCreateSerializer(BaseSerializer): @@ -335,19 +349,31 @@ class IssueLabelSerializer(BaseSerializer): class BlockedIssueSerializer(BaseSerializer): - blocked_issue_detail = IssueFlatSerializer(source="block", read_only=True) + blocked_issue_detail = IssueProjectLiteSerializer(source="block", read_only=True) class Meta: model = IssueBlocker - fields = "__all__" + fields = [ + "blocked_issue_detail", + "blocked_by", + "block", + ] + read_only_fields = fields class BlockerIssueSerializer(BaseSerializer): - blocker_issue_detail = IssueFlatSerializer(source="blocked_by", read_only=True) + blocker_issue_detail = IssueProjectLiteSerializer( + source="blocked_by", read_only=True + ) class Meta: model = IssueBlocker - fields = "__all__" + fields = [ + "blocker_issue_detail", + "blocked_by", + "block", + ] + read_only_fields = fields class IssueAssigneeSerializer(BaseSerializer): @@ -460,6 +486,21 @@ class IssueAttachmentSerializer(BaseSerializer): ] +class IssueStateFlatSerializer(BaseSerializer): + state_detail = StateLiteSerializer(read_only=True, source="state") + project_detail = ProjectLiteSerializer(read_only=True, source="project") + + class Meta: + model = Issue + fields = [ + "id", + "sequence_id", + "name", + "state_detail", + "project_detail", + ] + + # Issue Serializer with state details class IssueStateSerializer(BaseSerializer): label_details = LabelLiteSerializer(read_only=True, source="labels", many=True) @@ -479,7 +520,7 @@ class IssueStateSerializer(BaseSerializer): class IssueSerializer(BaseSerializer): project_detail = ProjectLiteSerializer(read_only=True, source="project") state_detail = StateSerializer(read_only=True, source="state") - parent_detail = IssueFlatSerializer(read_only=True, source="parent") + parent_detail = IssueStateFlatSerializer(read_only=True, source="parent") label_details = LabelSerializer(read_only=True, source="labels", many=True) assignee_details = UserLiteSerializer(read_only=True, source="assignees", many=True) # List of issues blocked by this issue diff --git a/apiserver/plane/api/views/issue.py b/apiserver/plane/api/views/issue.py index aab926fd2..02c8ed46a 100644 --- a/apiserver/plane/api/views/issue.py +++ b/apiserver/plane/api/views/issue.py @@ -629,7 +629,7 @@ class SubIssuesEndpoint(BaseAPIView): try: sub_issues = ( Issue.issue_objects.filter( - parent_id=issue_id, workspace__slug=slug, project_id=project_id + parent_id=issue_id, workspace__slug=slug ) .select_related("project") .select_related("workspace") @@ -661,7 +661,7 @@ class SubIssuesEndpoint(BaseAPIView): state_distribution = ( State.objects.filter( - ~Q(name="Triage"), workspace__slug=slug, project_id=project_id + ~Q(name="Triage"), workspace__slug=slug ) .annotate( state_count=Count( diff --git a/apiserver/plane/api/views/search.py b/apiserver/plane/api/views/search.py index 51925dd7b..b7ad654e2 100644 --- a/apiserver/plane/api/views/search.py +++ b/apiserver/plane/api/views/search.py @@ -206,6 +206,7 @@ class IssueSearchEndpoint(BaseAPIView): def get(self, request, slug, project_id): try: query = request.query_params.get("search", False) + workspace_search = request.query_params.get("workspace_search", "false") parent = request.query_params.get("parent", "false") blocker_blocked_by = request.query_params.get("blocker_blocked_by", "false") cycle = request.query_params.get("cycle", "false") @@ -216,10 +217,12 @@ class IssueSearchEndpoint(BaseAPIView): issues = Issue.issue_objects.filter( workspace__slug=slug, - project_id=project_id, project__project_projectmember__member=self.request.user, ) + if workspace_search == "false": + issues = issues.filter(project_id=project_id) + if query: issues = search_issues(query, issues) @@ -257,6 +260,7 @@ class IssueSearchEndpoint(BaseAPIView): "name", "id", "sequence_id", + "project__name", "project__identifier", "project_id", "workspace__slug", diff --git a/apiserver/plane/bgtasks/issue_activites_task.py b/apiserver/plane/bgtasks/issue_activites_task.py index 105a05b56..49b1a4145 100644 --- a/apiserver/plane/bgtasks/issue_activites_task.py +++ b/apiserver/plane/bgtasks/issue_activites_task.py @@ -70,7 +70,7 @@ def track_parent( issue_id=issue_id, actor=actor, verb="updated", - old_value=f"{project.identifier}-{old_parent.sequence_id}", + old_value=f"{old_parent.project.identifier}-{old_parent.sequence_id}", new_value=None, field="parent", project=project, @@ -88,10 +88,10 @@ def track_parent( issue_id=issue_id, actor=actor, verb="updated", - old_value=f"{project.identifier}-{old_parent.sequence_id}" + old_value=f"{old_parent.project.identifier}-{old_parent.sequence_id}" if old_parent is not None else None, - new_value=f"{project.identifier}-{new_parent.sequence_id}", + new_value=f"{new_parent.project.identifier}-{new_parent.sequence_id}", field="parent", project=project, workspace=project.workspace, @@ -415,11 +415,11 @@ def track_blocks( actor=actor, verb="updated", old_value="", - new_value=f"{project.identifier}-{issue.sequence_id}", + new_value=f"{issue.project.identifier}-{issue.sequence_id}", field="blocks", project=project, workspace=project.workspace, - comment=f"{actor.email} added blocking issue {project.identifier}-{issue.sequence_id}", + comment=f"{actor.email} added blocking issue {issue.project.identifier}-{issue.sequence_id}", new_identifier=issue.id, ) ) @@ -436,12 +436,12 @@ def track_blocks( issue_id=issue_id, actor=actor, verb="updated", - old_value=f"{project.identifier}-{issue.sequence_id}", + old_value=f"{issue.project.identifier}-{issue.sequence_id}", new_value="", field="blocks", project=project, workspace=project.workspace, - comment=f"{actor.email} removed blocking issue {project.identifier}-{issue.sequence_id}", + comment=f"{actor.email} removed blocking issue {issue.project.identifier}-{issue.sequence_id}", old_identifier=issue.id, ) ) @@ -477,11 +477,11 @@ def track_blockings( actor=actor, verb="updated", old_value="", - new_value=f"{project.identifier}-{issue.sequence_id}", + new_value=f"{issue.project.identifier}-{issue.sequence_id}", field="blocking", project=project, workspace=project.workspace, - comment=f"{actor.email} added blocked by issue {project.identifier}-{issue.sequence_id}", + comment=f"{actor.email} added blocked by issue {issue.project.identifier}-{issue.sequence_id}", new_identifier=issue.id, ) ) @@ -498,12 +498,12 @@ def track_blockings( issue_id=issue_id, actor=actor, verb="updated", - old_value=f"{project.identifier}-{issue.sequence_id}", + old_value=f"{issue.project.identifier}-{issue.sequence_id}", new_value="", field="blocking", project=project, workspace=project.workspace, - comment=f"{actor.email} removed blocked by issue {project.identifier}-{issue.sequence_id}", + comment=f"{actor.email} removed blocked by issue {issue.project.identifier}-{issue.sequence_id}", old_identifier=issue.id, ) ) @@ -1034,7 +1034,7 @@ def issue_activity( "module.activity.created", "module.activity.deleted", ]: - issue = Issue.objects.filter(pk=issue_id, project_id=project_id).first() + issue = Issue.objects.filter(pk=issue_id).first() if issue is not None: issue.updated_at = timezone.now() @@ -1122,7 +1122,7 @@ def issue_activity( issue_subscribers = issue_subscribers + issue_assignees - issue = Issue.objects.filter(pk=issue_id, project_id=project_id).first() + issue = Issue.objects.filter(pk=issue_id).first() # Add bot filtering if ( @@ -1149,7 +1149,7 @@ def issue_activity( "issue": { "id": str(issue_id), "name": str(issue.name), - "identifier": str(project.identifier), + "identifier": str(issue.project.identifier), "sequence_id": issue.sequence_id, "state_name": issue.state.name, "state_group": issue.state.group,