forked from github/plane
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:
parent
e357283789
commit
73b38f4db9
@ -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
|
||||||
|
@ -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(
|
||||||
|
@ -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",
|
||||||
|
@ -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,
|
||||||
|
Loading…
Reference in New Issue
Block a user