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
This commit is contained in:
Nikhil 2023-07-24 12:08:47 +05:30 committed by GitHub
parent e357283789
commit 73b38f4db9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 67 additions and 22 deletions

View File

@ -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 ##TODO: Find a better way to write this serializer
## Find a better approach to save manytomany? ## Find a better approach to save manytomany?
class IssueCreateSerializer(BaseSerializer): class IssueCreateSerializer(BaseSerializer):
@ -335,19 +349,31 @@ class IssueLabelSerializer(BaseSerializer):
class BlockedIssueSerializer(BaseSerializer): class BlockedIssueSerializer(BaseSerializer):
blocked_issue_detail = IssueFlatSerializer(source="block", read_only=True) blocked_issue_detail = IssueProjectLiteSerializer(source="block", read_only=True)
class Meta: class Meta:
model = IssueBlocker model = IssueBlocker
fields = "__all__" fields = [
"blocked_issue_detail",
"blocked_by",
"block",
]
read_only_fields = fields
class BlockerIssueSerializer(BaseSerializer): 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: class Meta:
model = IssueBlocker model = IssueBlocker
fields = "__all__" fields = [
"blocker_issue_detail",
"blocked_by",
"block",
]
read_only_fields = fields
class IssueAssigneeSerializer(BaseSerializer): 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 # Issue Serializer with state details
class IssueStateSerializer(BaseSerializer): class IssueStateSerializer(BaseSerializer):
label_details = LabelLiteSerializer(read_only=True, source="labels", many=True) label_details = LabelLiteSerializer(read_only=True, source="labels", many=True)
@ -479,7 +520,7 @@ class IssueStateSerializer(BaseSerializer):
class IssueSerializer(BaseSerializer): class IssueSerializer(BaseSerializer):
project_detail = ProjectLiteSerializer(read_only=True, source="project") project_detail = ProjectLiteSerializer(read_only=True, source="project")
state_detail = StateSerializer(read_only=True, source="state") 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) label_details = LabelSerializer(read_only=True, source="labels", many=True)
assignee_details = UserLiteSerializer(read_only=True, source="assignees", many=True) assignee_details = UserLiteSerializer(read_only=True, source="assignees", many=True)
# List of issues blocked by this issue # List of issues blocked by this issue

View File

@ -629,7 +629,7 @@ class SubIssuesEndpoint(BaseAPIView):
try: try:
sub_issues = ( sub_issues = (
Issue.issue_objects.filter( 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("project")
.select_related("workspace") .select_related("workspace")
@ -661,7 +661,7 @@ class SubIssuesEndpoint(BaseAPIView):
state_distribution = ( state_distribution = (
State.objects.filter( State.objects.filter(
~Q(name="Triage"), workspace__slug=slug, project_id=project_id ~Q(name="Triage"), workspace__slug=slug
) )
.annotate( .annotate(
state_count=Count( state_count=Count(

View File

@ -206,6 +206,7 @@ class IssueSearchEndpoint(BaseAPIView):
def get(self, request, slug, project_id): def get(self, request, slug, project_id):
try: try:
query = request.query_params.get("search", False) query = request.query_params.get("search", False)
workspace_search = request.query_params.get("workspace_search", "false")
parent = request.query_params.get("parent", "false") parent = request.query_params.get("parent", "false")
blocker_blocked_by = request.query_params.get("blocker_blocked_by", "false") blocker_blocked_by = request.query_params.get("blocker_blocked_by", "false")
cycle = request.query_params.get("cycle", "false") cycle = request.query_params.get("cycle", "false")
@ -216,10 +217,12 @@ class IssueSearchEndpoint(BaseAPIView):
issues = Issue.issue_objects.filter( issues = Issue.issue_objects.filter(
workspace__slug=slug, workspace__slug=slug,
project_id=project_id,
project__project_projectmember__member=self.request.user, project__project_projectmember__member=self.request.user,
) )
if workspace_search == "false":
issues = issues.filter(project_id=project_id)
if query: if query:
issues = search_issues(query, issues) issues = search_issues(query, issues)
@ -257,6 +260,7 @@ class IssueSearchEndpoint(BaseAPIView):
"name", "name",
"id", "id",
"sequence_id", "sequence_id",
"project__name",
"project__identifier", "project__identifier",
"project_id", "project_id",
"workspace__slug", "workspace__slug",

View File

@ -70,7 +70,7 @@ def track_parent(
issue_id=issue_id, issue_id=issue_id,
actor=actor, actor=actor,
verb="updated", 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, new_value=None,
field="parent", field="parent",
project=project, project=project,
@ -88,10 +88,10 @@ def track_parent(
issue_id=issue_id, issue_id=issue_id,
actor=actor, actor=actor,
verb="updated", 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 if old_parent is not None
else 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", field="parent",
project=project, project=project,
workspace=project.workspace, workspace=project.workspace,
@ -415,11 +415,11 @@ def track_blocks(
actor=actor, actor=actor,
verb="updated", verb="updated",
old_value="", old_value="",
new_value=f"{project.identifier}-{issue.sequence_id}", new_value=f"{issue.project.identifier}-{issue.sequence_id}",
field="blocks", field="blocks",
project=project, project=project,
workspace=project.workspace, 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, new_identifier=issue.id,
) )
) )
@ -436,12 +436,12 @@ def track_blocks(
issue_id=issue_id, issue_id=issue_id,
actor=actor, actor=actor,
verb="updated", verb="updated",
old_value=f"{project.identifier}-{issue.sequence_id}", old_value=f"{issue.project.identifier}-{issue.sequence_id}",
new_value="", new_value="",
field="blocks", field="blocks",
project=project, project=project,
workspace=project.workspace, 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, old_identifier=issue.id,
) )
) )
@ -477,11 +477,11 @@ def track_blockings(
actor=actor, actor=actor,
verb="updated", verb="updated",
old_value="", old_value="",
new_value=f"{project.identifier}-{issue.sequence_id}", new_value=f"{issue.project.identifier}-{issue.sequence_id}",
field="blocking", field="blocking",
project=project, project=project,
workspace=project.workspace, 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, new_identifier=issue.id,
) )
) )
@ -498,12 +498,12 @@ def track_blockings(
issue_id=issue_id, issue_id=issue_id,
actor=actor, actor=actor,
verb="updated", verb="updated",
old_value=f"{project.identifier}-{issue.sequence_id}", old_value=f"{issue.project.identifier}-{issue.sequence_id}",
new_value="", new_value="",
field="blocking", field="blocking",
project=project, project=project,
workspace=project.workspace, 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, old_identifier=issue.id,
) )
) )
@ -1034,7 +1034,7 @@ def issue_activity(
"module.activity.created", "module.activity.created",
"module.activity.deleted", "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: if issue is not None:
issue.updated_at = timezone.now() issue.updated_at = timezone.now()
@ -1122,7 +1122,7 @@ def issue_activity(
issue_subscribers = issue_subscribers + issue_assignees 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 # Add bot filtering
if ( if (
@ -1149,7 +1149,7 @@ def issue_activity(
"issue": { "issue": {
"id": str(issue_id), "id": str(issue_id),
"name": str(issue.name), "name": str(issue.name),
"identifier": str(project.identifier), "identifier": str(issue.project.identifier),
"sequence_id": issue.sequence_id, "sequence_id": issue.sequence_id,
"state_name": issue.state.name, "state_name": issue.state.name,
"state_group": issue.state.group, "state_group": issue.state.group,