ФУНКЦИИ - МЕЖПРОЕКТНАЯ КОММУНИКАЦИЯ: source-side принятие и возврат внешнего запроса
This commit is contained in:
@@ -3,11 +3,13 @@
|
||||
# See the LICENSE file for details.
|
||||
|
||||
from django.shortcuts import get_object_or_404
|
||||
from django.utils import timezone
|
||||
from rest_framework import status
|
||||
from rest_framework.response import Response
|
||||
|
||||
from plane.api.serializers import (
|
||||
ExternalContourRequestCreateSerializer,
|
||||
ExternalContourRequestDecisionSerializer,
|
||||
ExternalContourRequestSerializer,
|
||||
ExternalContourTargetOptionsSerializer,
|
||||
ExternalContourTargetProjectSerializer,
|
||||
@@ -26,6 +28,15 @@ class ExternalContourListCreateEndpoint(BaseAPIView):
|
||||
def get_source_project(self, slug, project_id):
|
||||
return get_object_or_404(Project, workspace__slug=slug, pk=project_id)
|
||||
|
||||
def get_default_target_state(self, target_project):
|
||||
return (
|
||||
State.objects.filter(project=target_project, default=True)
|
||||
.exclude(group=StateGroup.TRIAGE.value)
|
||||
.first()
|
||||
) or State.objects.filter(project=target_project).exclude(group=StateGroup.TRIAGE.value).order_by(
|
||||
"sequence", "created_at"
|
||||
).first()
|
||||
|
||||
def get_queryset(self):
|
||||
return (
|
||||
IntakeIssue.objects.filter(
|
||||
@@ -93,11 +104,7 @@ class ExternalContourListCreateEndpoint(BaseAPIView):
|
||||
default=False,
|
||||
)
|
||||
|
||||
target_default_state = (
|
||||
State.objects.filter(project=target_project, default=True)
|
||||
.exclude(group=StateGroup.TRIAGE.value)
|
||||
.first()
|
||||
) or State.objects.filter(project=target_project).exclude(group=StateGroup.TRIAGE.value).order_by("sequence", "created_at").first()
|
||||
target_default_state = self.get_default_target_state(target_project)
|
||||
|
||||
if not target_default_state:
|
||||
return Response({"error": "Target project has no available workflow state"}, status=status.HTTP_400_BAD_REQUEST)
|
||||
@@ -264,3 +271,84 @@ class ExternalContourDetailEndpoint(BaseAPIView):
|
||||
contour_request = get_object_or_404(self.get_queryset())
|
||||
serializer = ExternalContourRequestSerializer(contour_request)
|
||||
return Response(serializer.data, status=status.HTTP_200_OK)
|
||||
|
||||
|
||||
class ExternalContourDecisionEndpoint(BaseAPIView):
|
||||
permission_classes = [ProjectLitePermission]
|
||||
serializer_class = ExternalContourRequestSerializer
|
||||
|
||||
def get_queryset(self):
|
||||
return (
|
||||
IntakeIssue.objects.filter(
|
||||
workspace__slug=self.kwargs.get("slug"),
|
||||
extra__bridge="external-contours",
|
||||
extra__source_project_id=str(self.kwargs.get("project_id")),
|
||||
pk=self.kwargs.get("request_id"),
|
||||
)
|
||||
.select_related(
|
||||
"issue",
|
||||
"issue__state",
|
||||
"issue__project",
|
||||
"issue__created_by",
|
||||
)
|
||||
.prefetch_related("issue__issue_assignee__assignee", "issue__label_issue__label")
|
||||
)
|
||||
|
||||
def post(self, request, slug, project_id, request_id):
|
||||
contour_request = get_object_or_404(self.get_queryset())
|
||||
serializer = ExternalContourRequestDecisionSerializer(data=request.data)
|
||||
serializer.is_valid(raise_exception=True)
|
||||
|
||||
action = serializer.validated_data["action"]
|
||||
issue = contour_request.issue
|
||||
|
||||
if not issue or not issue.state or issue.state.group not in [StateGroup.COMPLETED.value, StateGroup.CANCELLED.value]:
|
||||
return Response(
|
||||
{"error": "Source decision is available only after the target contour finishes processing"},
|
||||
status=status.HTTP_400_BAD_REQUEST,
|
||||
)
|
||||
|
||||
if action == "accept":
|
||||
contour_request.extra = {
|
||||
**contour_request.extra,
|
||||
"source_decision": "accepted",
|
||||
"source_decision_at": timezone.now().isoformat(),
|
||||
"source_decision_by_name": request.user.display_name,
|
||||
}
|
||||
contour_request.save(update_fields=["extra", "updated_at"])
|
||||
else:
|
||||
target_default_state = (
|
||||
State.objects.filter(project=issue.project, default=True)
|
||||
.exclude(group=StateGroup.TRIAGE.value)
|
||||
.first()
|
||||
) or State.objects.filter(project=issue.project).exclude(group=StateGroup.TRIAGE.value).order_by(
|
||||
"sequence", "created_at"
|
||||
).first()
|
||||
|
||||
if not target_default_state:
|
||||
return Response({"error": "Target project has no available workflow state"}, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
issue.state = target_default_state
|
||||
issue.save(update_fields=["state", "updated_at"])
|
||||
|
||||
extra = dict(contour_request.extra or {})
|
||||
extra.pop("source_decision", None)
|
||||
extra.pop("source_decision_at", None)
|
||||
extra.pop("source_decision_by_name", None)
|
||||
extra["last_reopened_at"] = issue.updated_at.isoformat() if issue.updated_at else None
|
||||
extra["last_reopened_by_name"] = request.user.display_name
|
||||
contour_request.extra = extra
|
||||
contour_request.save(update_fields=["extra", "updated_at"])
|
||||
|
||||
contour_request.refresh_from_db()
|
||||
serializer = ExternalContourRequestSerializer(
|
||||
IntakeIssue.objects.select_related(
|
||||
"issue",
|
||||
"issue__state",
|
||||
"issue__project",
|
||||
"issue__created_by",
|
||||
)
|
||||
.prefetch_related("issue__issue_assignee__assignee", "issue__label_issue__label")
|
||||
.get(pk=contour_request.id)
|
||||
)
|
||||
return Response(serializer.data, status=status.HTTP_200_OK)
|
||||
|
||||
Reference in New Issue
Block a user