diff --git a/apiserver/plane/app/serializers/issue.py b/apiserver/plane/app/serializers/issue.py index 1b754ea6af4..b9306a728b6 100644 --- a/apiserver/plane/app/serializers/issue.py +++ b/apiserver/plane/app/serializers/issue.py @@ -111,25 +111,23 @@ def to_representation(self, instance): data["label_ids"] = label_ids if label_ids else [] return data - def validate(self, data): + def validate(self, attrs): if ( - data.get("start_date", None) is not None - and data.get("target_date", None) is not None - and data.get("start_date", None) > data.get("target_date", None) + attrs.get("start_date", None) is not None + and attrs.get("target_date", None) is not None + and attrs.get("start_date", None) > attrs.get("target_date", None) ): raise serializers.ValidationError("Start date cannot exceed target date") - return data - def get_valid_assignees(self, assignees, project_id): - if not assignees: - return [] + if attrs.get("assignee_ids", []): + attrs["assignee_ids"] = ProjectMember.objects.filter( + project_id=self.context["project_id"], + role__gte=15, + is_active=True, + member_id__in=attrs["assignee_ids"], + ).values_list("member_id", flat=True) - return ProjectMember.objects.filter( - project_id=project_id, - role__gte=15, - is_active=True, - member_id__in=assignees - ).values_list('member_id', flat=True) + return attrs def create(self, validated_data): assignees = validated_data.pop("assignee_ids", None) @@ -146,20 +144,19 @@ def create(self, validated_data): created_by_id = issue.created_by_id updated_by_id = issue.updated_by_id - valid_assignee_ids = self.get_valid_assignees(assignees, project_id) - if valid_assignee_ids is not None and len(valid_assignee_ids): + if assignees is not None and len(assignees): try: IssueAssignee.objects.bulk_create( [ IssueAssignee( - assignee_id=user_id, + assignee_id=assignee_id, issue=issue, project_id=project_id, workspace_id=workspace_id, created_by_id=created_by_id, updated_by_id=updated_by_id, ) - for user_id in valid_assignee_ids + for assignee_id in assignees ], batch_size=10, ) @@ -167,12 +164,15 @@ def create(self, validated_data): pass else: # Then assign it to default assignee, if it is a valid assignee - if default_assignee_id is not None and ProjectMember.objects.filter( - member_id=default_assignee_id, - project_id=project_id, - role__gte=15, - is_active=True - ).exists(): + if ( + default_assignee_id is not None + and ProjectMember.objects.filter( + member_id=default_assignee_id, + project_id=project_id, + role__gte=15, + is_active=True, + ).exists() + ): try: IssueAssignee.objects.create( assignee_id=default_assignee_id, @@ -216,21 +216,20 @@ def update(self, instance, validated_data): created_by_id = instance.created_by_id updated_by_id = instance.updated_by_id - valid_assignee_ids = self.get_valid_assignees(assignees, project_id) - if valid_assignee_ids is not None: + if assignees is not None: IssueAssignee.objects.filter(issue=instance).delete() try: IssueAssignee.objects.bulk_create( [ IssueAssignee( - assignee_id=user_id, + assignee_id=assignee_id, issue=instance, project_id=project_id, workspace_id=workspace_id, created_by_id=created_by_id, updated_by_id=updated_by_id, ) - for user_id in valid_assignee_ids + for assignee_id in assignees ], batch_size=10, ignore_conflicts=True, diff --git a/apiserver/plane/app/views/intake/base.py b/apiserver/plane/app/views/intake/base.py index 631fe80da1e..fb10bc002c0 100644 --- a/apiserver/plane/app/views/intake/base.py +++ b/apiserver/plane/app/views/intake/base.py @@ -178,7 +178,9 @@ def list(self, request, slug, project_id): workspace__slug=slug, project_id=project_id ).first() if not intake: - return Response({"error": "Intake not found"}, status=status.HTTP_404_NOT_FOUND) + return Response( + {"error": "Intake not found"}, status=status.HTTP_404_NOT_FOUND + ) project = Project.objects.get(pk=project_id) filters = issue_filters(request.GET, "GET", "issue__") @@ -385,7 +387,7 @@ def partial_update(self, request, slug, project_id, pk): } issue_serializer = IssueCreateSerializer( - issue, data=issue_data, partial=True + issue, data=issue_data, partial=True, context={"project_id": project_id} ) if issue_serializer.is_valid(): diff --git a/apiserver/plane/app/views/issue/base.py b/apiserver/plane/app/views/issue/base.py index 48ea2f6bc35..79ffe35d8be 100644 --- a/apiserver/plane/app/views/issue/base.py +++ b/apiserver/plane/app/views/issue/base.py @@ -635,7 +635,9 @@ def partial_update(self, request, slug, project_id, pk=None): ) requested_data = json.dumps(self.request.data, cls=DjangoJSONEncoder) - serializer = IssueCreateSerializer(issue, data=request.data, partial=True) + serializer = IssueCreateSerializer( + issue, data=request.data, partial=True, context={"project_id": project_id} + ) if serializer.is_valid(): serializer.save() issue_activity.delay( @@ -1099,7 +1101,6 @@ def post(self, request, slug, project_id): class IssueMetaEndpoint(BaseAPIView): - @allow_permission([ROLE.ADMIN, ROLE.MEMBER, ROLE.GUEST], level="PROJECT") def get(self, request, slug, project_id, issue_id): issue = Issue.issue_objects.only("sequence_id", "project__identifier").get( @@ -1115,14 +1116,12 @@ def get(self, request, slug, project_id, issue_id): class IssueDetailIdentifierEndpoint(BaseAPIView): - def strict_str_to_int(self, s): - if not s.isdigit() and not (s.startswith('-') and s[1:].isdigit()): + if not s.isdigit() and not (s.startswith("-") and s[1:].isdigit()): raise ValueError("Invalid integer string") return int(s) def get(self, request, slug, project_identifier, issue_identifier): - # Check if the issue identifier is a valid integer try: issue_identifier = self.strict_str_to_int(issue_identifier) @@ -1134,8 +1133,7 @@ def get(self, request, slug, project_identifier, issue_identifier): # Fetch the project project = Project.objects.get( - identifier__iexact=project_identifier, - workspace__slug=slug, + identifier__iexact=project_identifier, workspace__slug=slug ) # Check if the user is a member of the project @@ -1237,8 +1235,8 @@ def get(self, request, slug, project_identifier, issue_identifier): .annotate( is_subscribed=Exists( IssueSubscriber.objects.filter( - workspace__slug=slug, - project_id=project.id, + workspace__slug=slug, + project_id=project.id, issue__sequence_id=issue_identifier, subscriber=request.user, ) diff --git a/apiserver/plane/space/views/intake.py b/apiserver/plane/space/views/intake.py index bfce3a8bbe0..644a2de3af9 100644 --- a/apiserver/plane/space/views/intake.py +++ b/apiserver/plane/space/views/intake.py @@ -12,7 +12,7 @@ # Module imports from .base import BaseViewSet -from plane.db.models import IntakeIssue, Issue, State, IssueLink, FileAsset, DeployBoard +from plane.db.models import IntakeIssue, Issue, IssueLink, FileAsset, DeployBoard from plane.app.serializers import ( IssueSerializer, IntakeIssueSerializer, @@ -202,7 +202,12 @@ def partial_update(self, request, anchor, intake_id, pk): "description": issue_data.get("description", issue.description), } - issue_serializer = IssueCreateSerializer(issue, data=issue_data, partial=True) + issue_serializer = IssueCreateSerializer( + issue, + data=issue_data, + partial=True, + context={"project_id": project_deploy_board.project_id}, + ) if issue_serializer.is_valid(): current_instance = issue