mirror of
https://github.com/makeplane/plane.git
synced 2026-06-13 19:19:54 +00:00
[SILO-1158] chore: add context for project in relations API (#8860)
* add context for project in relations API * modify issue relation serializer
This commit is contained in:
@@ -480,44 +480,52 @@ class IssueLinkSerializer(BaseSerializer):
|
||||
]
|
||||
|
||||
|
||||
class IssueRelationRefSerializer(serializers.Serializer):
|
||||
"""Project-scoped reference to a related work item."""
|
||||
|
||||
project_id = serializers.UUIDField(help_text="Project containing the related work item")
|
||||
issue_id = serializers.UUIDField(help_text="ID of the related work item")
|
||||
|
||||
|
||||
class IssueRelationResponseSerializer(serializers.Serializer):
|
||||
"""
|
||||
Serializer for issue relations response showing grouped relation types.
|
||||
|
||||
Returns issue IDs organized by relation type for efficient client-side processing.
|
||||
Each list contains project_id and issue_id pairs so clients can resolve
|
||||
cross-project relations.
|
||||
"""
|
||||
|
||||
blocking = serializers.ListField(
|
||||
child=serializers.UUIDField(),
|
||||
help_text="List of issue IDs that are blocking this issue",
|
||||
child=IssueRelationRefSerializer(),
|
||||
help_text="Work items blocking this issue",
|
||||
)
|
||||
blocked_by = serializers.ListField(
|
||||
child=serializers.UUIDField(),
|
||||
help_text="List of issue IDs that this issue is blocked by",
|
||||
child=IssueRelationRefSerializer(),
|
||||
help_text="Work items this issue is blocked by",
|
||||
)
|
||||
duplicate = serializers.ListField(
|
||||
child=serializers.UUIDField(),
|
||||
help_text="List of issue IDs that are duplicates of this issue",
|
||||
child=IssueRelationRefSerializer(),
|
||||
help_text="Duplicate work items",
|
||||
)
|
||||
relates_to = serializers.ListField(
|
||||
child=serializers.UUIDField(),
|
||||
help_text="List of issue IDs that relate to this issue",
|
||||
child=IssueRelationRefSerializer(),
|
||||
help_text="Related work items",
|
||||
)
|
||||
start_after = serializers.ListField(
|
||||
child=serializers.UUIDField(),
|
||||
help_text="List of issue IDs that start after this issue",
|
||||
child=IssueRelationRefSerializer(),
|
||||
help_text="Work items that start after this issue",
|
||||
)
|
||||
start_before = serializers.ListField(
|
||||
child=serializers.UUIDField(),
|
||||
help_text="List of issue IDs that start before this issue",
|
||||
child=IssueRelationRefSerializer(),
|
||||
help_text="Work items that start before this issue",
|
||||
)
|
||||
finish_after = serializers.ListField(
|
||||
child=serializers.UUIDField(),
|
||||
help_text="List of issue IDs that finish after this issue",
|
||||
child=IssueRelationRefSerializer(),
|
||||
help_text="Work items that finish after this issue",
|
||||
)
|
||||
finish_before = serializers.ListField(
|
||||
child=serializers.UUIDField(),
|
||||
help_text="List of issue IDs that finish before this issue",
|
||||
child=IssueRelationRefSerializer(),
|
||||
help_text="Work items that finish before this issue",
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -23,11 +23,8 @@ from django.db.models import (
|
||||
Value,
|
||||
When,
|
||||
Subquery,
|
||||
UUIDField,
|
||||
)
|
||||
from django.db.models.functions import Coalesce
|
||||
from django.contrib.postgres.aggregates import ArrayAgg
|
||||
from django.contrib.postgres.fields import ArrayField
|
||||
|
||||
from django.utils import timezone
|
||||
from django.conf import settings
|
||||
|
||||
@@ -2292,14 +2289,35 @@ class IssueRelationListCreateAPIEndpoint(BaseAPIView):
|
||||
name="Work Item Relations Response",
|
||||
value={
|
||||
"blocking": [
|
||||
"550e8400-e29b-41d4-a716-446655440000",
|
||||
"550e8400-e29b-41d4-a716-446655440001",
|
||||
{
|
||||
"project_id": "550e8400-e29b-41d4-a716-446655440010",
|
||||
"issue_id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
},
|
||||
{
|
||||
"project_id": "550e8400-e29b-41d4-a716-446655440010",
|
||||
"issue_id": "550e8400-e29b-41d4-a716-446655440001",
|
||||
},
|
||||
],
|
||||
"blocked_by": [
|
||||
{
|
||||
"project_id": "550e8400-e29b-41d4-a716-446655440011",
|
||||
"issue_id": "550e8400-e29b-41d4-a716-446655440002",
|
||||
},
|
||||
],
|
||||
"blocked_by": ["550e8400-e29b-41d4-a716-446655440002"],
|
||||
"duplicate": [],
|
||||
"relates_to": ["550e8400-e29b-41d4-a716-446655440003"],
|
||||
"relates_to": [
|
||||
{
|
||||
"project_id": "550e8400-e29b-41d4-a716-446655440010",
|
||||
"issue_id": "550e8400-e29b-41d4-a716-446655440003",
|
||||
},
|
||||
],
|
||||
"start_after": [],
|
||||
"start_before": ["550e8400-e29b-41d4-a716-446655440004"],
|
||||
"start_before": [
|
||||
{
|
||||
"project_id": "550e8400-e29b-41d4-a716-446655440012",
|
||||
"issue_id": "550e8400-e29b-41d4-a716-446655440004",
|
||||
},
|
||||
],
|
||||
"finish_after": [],
|
||||
"finish_before": [],
|
||||
},
|
||||
@@ -2316,42 +2334,81 @@ class IssueRelationListCreateAPIEndpoint(BaseAPIView):
|
||||
Retrieve all relationships for a work item organized by relation type.
|
||||
Returns a structured response with relations grouped by type.
|
||||
"""
|
||||
empty_uuid_array = Value([], output_field=ArrayField(UUIDField()))
|
||||
|
||||
def _agg_ids(field, **filter_kwargs):
|
||||
return Coalesce(
|
||||
ArrayAgg(field, filter=Q(**filter_kwargs), distinct=True),
|
||||
empty_uuid_array,
|
||||
)
|
||||
|
||||
issue_relation_qs = IssueRelation.objects.filter(
|
||||
relations = IssueRelation.objects.filter(
|
||||
Q(issue_id=issue_id) | Q(related_issue_id=issue_id),
|
||||
workspace__slug=slug,
|
||||
)
|
||||
|
||||
relation_ids = issue_relation_qs.aggregate(
|
||||
blocking_ids=_agg_ids("issue_id", relation_type="blocked_by", related_issue_id=issue_id),
|
||||
blocked_by_ids=_agg_ids("related_issue_id", relation_type="blocked_by", issue_id=issue_id),
|
||||
duplicate_ids=_agg_ids("related_issue_id", relation_type="duplicate", issue_id=issue_id),
|
||||
duplicate_ids_related=_agg_ids("issue_id", relation_type="duplicate", related_issue_id=issue_id),
|
||||
relates_to_ids=_agg_ids("related_issue_id", relation_type="relates_to", issue_id=issue_id),
|
||||
relates_to_ids_related=_agg_ids("issue_id", relation_type="relates_to", related_issue_id=issue_id),
|
||||
start_after_ids=_agg_ids("issue_id", relation_type="start_before", related_issue_id=issue_id),
|
||||
start_before_ids=_agg_ids("related_issue_id", relation_type="start_before", issue_id=issue_id),
|
||||
finish_after_ids=_agg_ids("issue_id", relation_type="finish_before", related_issue_id=issue_id),
|
||||
finish_before_ids=_agg_ids("related_issue_id", relation_type="finish_before", issue_id=issue_id),
|
||||
).values(
|
||||
"relation_type",
|
||||
"issue_id",
|
||||
"related_issue_id",
|
||||
issue_project_id=F("issue__project_id"),
|
||||
related_issue_project_id=F("related_issue__project_id"),
|
||||
)
|
||||
|
||||
response_data = {
|
||||
"blocking": relation_ids["blocking_ids"],
|
||||
"blocked_by": relation_ids["blocked_by_ids"],
|
||||
"duplicate": list(set(relation_ids["duplicate_ids"] + relation_ids["duplicate_ids_related"])),
|
||||
"relates_to": list(set(relation_ids["relates_to_ids"] + relation_ids["relates_to_ids_related"])),
|
||||
"start_after": relation_ids["start_after_ids"],
|
||||
"start_before": relation_ids["start_before_ids"],
|
||||
"finish_after": relation_ids["finish_after_ids"],
|
||||
"finish_before": relation_ids["finish_before_ids"],
|
||||
"blocking": [],
|
||||
"blocked_by": [],
|
||||
"duplicate": [],
|
||||
"relates_to": [],
|
||||
"start_after": [],
|
||||
"start_before": [],
|
||||
"finish_after": [],
|
||||
"finish_before": [],
|
||||
}
|
||||
seen_duplicate = set()
|
||||
seen_relates_to = set()
|
||||
|
||||
for rel in relations:
|
||||
rt = rel["relation_type"]
|
||||
if rt == "blocked_by":
|
||||
if str(rel["related_issue_id"]) == str(issue_id):
|
||||
response_data["blocking"].append(
|
||||
{"project_id": str(rel["issue_project_id"]), "issue_id": str(rel["issue_id"])}
|
||||
)
|
||||
if str(rel["issue_id"]) == str(issue_id):
|
||||
response_data["blocked_by"].append(
|
||||
{"project_id": str(rel["related_issue_project_id"]), "issue_id": str(rel["related_issue_id"])}
|
||||
)
|
||||
elif rt == "duplicate":
|
||||
if str(rel["issue_id"]) == str(issue_id) and rel["related_issue_id"] not in seen_duplicate:
|
||||
seen_duplicate.add(rel["related_issue_id"])
|
||||
response_data["duplicate"].append(
|
||||
{"project_id": str(rel["related_issue_project_id"]), "issue_id": str(rel["related_issue_id"])}
|
||||
)
|
||||
if str(rel["related_issue_id"]) == str(issue_id) and rel["issue_id"] not in seen_duplicate:
|
||||
seen_duplicate.add(rel["issue_id"])
|
||||
response_data["duplicate"].append(
|
||||
{"project_id": str(rel["issue_project_id"]), "issue_id": str(rel["issue_id"])}
|
||||
)
|
||||
elif rt == "relates_to":
|
||||
if str(rel["issue_id"]) == str(issue_id) and rel["related_issue_id"] not in seen_relates_to:
|
||||
seen_relates_to.add(rel["related_issue_id"])
|
||||
response_data["relates_to"].append(
|
||||
{"project_id": str(rel["related_issue_project_id"]), "issue_id": str(rel["related_issue_id"])}
|
||||
)
|
||||
if str(rel["related_issue_id"]) == str(issue_id) and rel["issue_id"] not in seen_relates_to:
|
||||
seen_relates_to.add(rel["issue_id"])
|
||||
response_data["relates_to"].append(
|
||||
{"project_id": str(rel["issue_project_id"]), "issue_id": str(rel["issue_id"])}
|
||||
)
|
||||
elif rt == "start_before":
|
||||
if str(rel["related_issue_id"]) == str(issue_id):
|
||||
response_data["start_after"].append(
|
||||
{"project_id": str(rel["issue_project_id"]), "issue_id": str(rel["issue_id"])}
|
||||
)
|
||||
if str(rel["issue_id"]) == str(issue_id):
|
||||
response_data["start_before"].append(
|
||||
{"project_id": str(rel["related_issue_project_id"]), "issue_id": str(rel["related_issue_id"])}
|
||||
)
|
||||
elif rt == "finish_before":
|
||||
if str(rel["related_issue_id"]) == str(issue_id):
|
||||
response_data["finish_after"].append(
|
||||
{"project_id": str(rel["issue_project_id"]), "issue_id": str(rel["issue_id"])}
|
||||
)
|
||||
if str(rel["issue_id"]) == str(issue_id):
|
||||
response_data["finish_before"].append(
|
||||
{"project_id": str(rel["related_issue_project_id"]), "issue_id": str(rel["related_issue_id"])}
|
||||
)
|
||||
|
||||
return Response(response_data, status=status.HTTP_200_OK)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user