ФУНКЦИИ - МЕЖПРОЕКТНАЯ КОММУНИКАЦИЯ: web-доступ к внешним контурам и исправление модалки запроса

This commit is contained in:
DCCONSTRUCTIONS
2026-04-19 00:07:10 +03:00
parent 6d67571b27
commit 3fe3539614
21 changed files with 707 additions and 42 deletions
@@ -56,6 +56,8 @@ from .intake import (
from .external_contours import (
ExternalContourRequestCreateSerializer,
ExternalContourRequestSerializer,
ExternalContourTargetOptionsSerializer,
ExternalContourTargetProjectSerializer,
)
from .estimate import EstimateSerializer, EstimatePointSerializer
from .asset import (
@@ -9,7 +9,8 @@ from .issue import IssueSerializer
from .project import ProjectLiteSerializer
from .state import StateLiteSerializer
from .user import UserLiteSerializer
from plane.db.models import IntakeIssue, Issue
from plane.app.serializers.issue import LabelSerializer
from plane.db.models import IntakeIssue, Issue, Label, Project
class ExternalContourIssuePayloadSerializer(serializers.Serializer):
@@ -26,6 +27,21 @@ class ExternalContourRequestCreateSerializer(serializers.Serializer):
issue = ExternalContourIssuePayloadSerializer()
class ExternalContourTargetProjectSerializer(BaseSerializer):
inbox_view = serializers.BooleanField(read_only=True, source="intake_view")
class Meta:
model = Project
fields = ["id", "identifier", "name", "logo_props", "inbox_view"]
read_only_fields = fields
class ExternalContourTargetOptionsSerializer(serializers.Serializer):
project = ExternalContourTargetProjectSerializer(read_only=True)
member_ids = serializers.ListField(child=serializers.UUIDField(), read_only=True)
labels = LabelSerializer(many=True, read_only=True)
class ExternalContourIssueSerializer(BaseSerializer):
assignee_ids = serializers.SerializerMethodField()
assignee_details = serializers.SerializerMethodField()
@@ -4,7 +4,12 @@
from django.urls import path
from plane.api.views import ExternalContourDetailAPIEndpoint, ExternalContourListCreateAPIEndpoint
from plane.api.views import (
ExternalContourDetailAPIEndpoint,
ExternalContourListCreateAPIEndpoint,
ExternalContourTargetOptionsAPIEndpoint,
ExternalContourTargetProjectListAPIEndpoint,
)
urlpatterns = [
path(
@@ -12,6 +17,16 @@ urlpatterns = [
ExternalContourListCreateAPIEndpoint.as_view(http_method_names=["get", "post"]),
name="external-contours",
),
path(
"workspaces/<str:slug>/projects/<uuid:project_id>/external-contours/targets/",
ExternalContourTargetProjectListAPIEndpoint.as_view(http_method_names=["get"]),
name="external-contour-targets",
),
path(
"workspaces/<str:slug>/projects/<uuid:project_id>/external-contours/targets/<uuid:target_project_id>/options/",
ExternalContourTargetOptionsAPIEndpoint.as_view(http_method_names=["get"]),
name="external-contour-target-options",
),
path(
"workspaces/<str:slug>/projects/<uuid:project_id>/external-contours/<uuid:request_id>/",
ExternalContourDetailAPIEndpoint.as_view(http_method_names=["get"]),
@@ -58,6 +58,8 @@ from .intake import (
from .external_contours import (
ExternalContourListCreateAPIEndpoint,
ExternalContourDetailAPIEndpoint,
ExternalContourTargetProjectListAPIEndpoint,
ExternalContourTargetOptionsAPIEndpoint,
)
from .asset import UserAssetEndpoint, UserServerAssetEndpoint, GenericAssetEndpoint
@@ -9,9 +9,11 @@ from rest_framework.response import Response
from plane.api.serializers import (
ExternalContourRequestCreateSerializer,
ExternalContourRequestSerializer,
ExternalContourTargetOptionsSerializer,
ExternalContourTargetProjectSerializer,
)
from plane.app.permissions import ProjectLitePermission
from plane.db.models import Intake, IntakeIssue, Project, State, StateGroup
from plane.db.models import Intake, IntakeIssue, Label, Project, ProjectMember, State, StateGroup
from plane.api.serializers.issue import IssueSerializer as IssueCreateSerializer
from .base import BaseAPIView
from plane.db.models.intake import IntakeIssueStatus, SourceType
@@ -74,6 +76,12 @@ class ExternalContourListCreateAPIEndpoint(BaseAPIView):
if str(target_project.id) == str(source_project.id):
return Response({"error": "Target project must differ from source project"}, status=status.HTTP_400_BAD_REQUEST)
if not target_project.intake_view:
return Response(
{"error": "Target project is not enabled for external contour routing"},
status=status.HTTP_400_BAD_REQUEST,
)
triage_state = State.triage_objects.filter(project=target_project).first()
if not triage_state:
triage_state = State.objects.create(
@@ -158,6 +166,79 @@ class ExternalContourListCreateAPIEndpoint(BaseAPIView):
return Response(response_serializer.data, status=status.HTTP_201_CREATED)
class ExternalContourTargetProjectListAPIEndpoint(BaseAPIView):
permission_classes = [ProjectLitePermission]
serializer_class = ExternalContourTargetProjectSerializer
def get_source_project(self, slug, project_id):
return get_object_or_404(Project, workspace__slug=slug, pk=project_id)
def get_queryset(self):
source_project = self.get_source_project(self.kwargs.get("slug"), self.kwargs.get("project_id"))
return (
Project.objects.filter(
workspace_id=source_project.workspace_id,
archived_at__isnull=True,
intake_view=True,
)
.exclude(pk=source_project.id)
.order_by("name")
)
def get(self, request, slug, project_id):
serializer = ExternalContourTargetProjectSerializer(self.get_queryset(), many=True)
return Response(serializer.data, status=status.HTTP_200_OK)
class ExternalContourTargetOptionsAPIEndpoint(BaseAPIView):
permission_classes = [ProjectLitePermission]
serializer_class = ExternalContourTargetOptionsSerializer
def get_source_project(self, slug, project_id):
return get_object_or_404(Project, workspace__slug=slug, pk=project_id)
def get_target_project(self, source_project, target_project_id):
return get_object_or_404(
Project,
workspace_id=source_project.workspace_id,
pk=target_project_id,
archived_at__isnull=True,
intake_view=True,
)
def get(self, request, slug, project_id, target_project_id):
source_project = self.get_source_project(slug, project_id)
target_project = self.get_target_project(source_project, target_project_id)
if str(target_project.id) == str(source_project.id):
return Response({"error": "Target project must differ from source project"}, status=status.HTTP_400_BAD_REQUEST)
member_ids = list(
ProjectMember.objects.filter(
project=target_project,
workspace_id=target_project.workspace_id,
is_active=True,
member__is_bot=False,
member__member_workspace__workspace_id=target_project.workspace_id,
member__member_workspace__is_active=True,
)
.order_by("member__display_name", "member__email")
.values_list("member_id", flat=True)
.distinct()
)
labels = Label.objects.filter(project=target_project).order_by("sort_order", "name")
serializer = ExternalContourTargetOptionsSerializer(
{
"project": target_project,
"member_ids": member_ids,
"labels": labels,
}
)
return Response(serializer.data, status=status.HTTP_200_OK)
class ExternalContourDetailAPIEndpoint(BaseAPIView):
permission_classes = [ProjectLitePermission]
serializer_class = ExternalContourRequestSerializer