From 7c8cbc4ead92195fcd75ff269d36b7976bca28f9 Mon Sep 17 00:00:00 2001 From: Sangeetha Date: Mon, 24 Nov 2025 21:22:17 +0530 Subject: [PATCH] [WEB-4428] fix: duplicate labels with case insensitive (#7388) Co-authored-by: sriramveeraghanta --- apps/api/plane/app/serializers/issue.py | 13 ++++++ apps/api/plane/app/views/issue/label.py | 19 ++++++-- .../tests/unit/serializers/test_label.py | 43 +++++++++++++++++++ 3 files changed, 72 insertions(+), 3 deletions(-) create mode 100644 apps/api/plane/tests/unit/serializers/test_label.py diff --git a/apps/api/plane/app/serializers/issue.py b/apps/api/plane/app/serializers/issue.py index 583b62fd6..21a286d5e 100644 --- a/apps/api/plane/app/serializers/issue.py +++ b/apps/api/plane/app/serializers/issue.py @@ -364,6 +364,19 @@ class LabelSerializer(BaseSerializer): ] read_only_fields = ["workspace", "project"] + def validate_name(self, value): + project_id = self.context.get("project_id") + + label = Label.objects.filter(project_id=project_id, name__iexact=value) + + if self.instance: + label = label.exclude(id=self.instance.pk) + + if label.exists(): + raise serializers.ValidationError(detail="LABEL_NAME_ALREADY_EXISTS") + + return value + class LabelLiteSerializer(BaseSerializer): class Meta: diff --git a/apps/api/plane/app/views/issue/label.py b/apps/api/plane/app/views/issue/label.py index 85e79c011..ad0a29080 100644 --- a/apps/api/plane/app/views/issue/label.py +++ b/apps/api/plane/app/views/issue/label.py @@ -39,7 +39,9 @@ class LabelViewSet(BaseViewSet): @allow_permission([ROLE.ADMIN]) def create(self, request, slug, project_id): try: - serializer = LabelSerializer(data=request.data) + serializer = LabelSerializer( + data=request.data, context={"project_id": project_id} + ) if serializer.is_valid(): serializer.save(project_id=project_id) return Response(serializer.data, status=status.HTTP_201_CREATED) @@ -64,8 +66,18 @@ class LabelViewSet(BaseViewSet): {"error": "Label with the same name already exists in the project"}, status=status.HTTP_400_BAD_REQUEST, ) - # call the parent method to perform the update - return super().partial_update(request, *args, **kwargs) + + serializer = LabelSerializer( + instance=self.get_object(), + data=request.data, + context={"project_id": kwargs["project_id"]}, + partial=True, + ) + + if serializer.is_valid(): + serializer.save() + return Response(serializer.data) + return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) @invalidate_cache(path="/api/workspaces/:slug/labels/", url_params=True, user=False) @allow_permission([ROLE.ADMIN]) @@ -77,6 +89,7 @@ class BulkCreateIssueLabelsEndpoint(BaseAPIView): @allow_permission([ROLE.ADMIN]) def post(self, request, slug, project_id): label_data = request.data.get("label_data", []) + project = Project.objects.get(pk=project_id) labels = Label.objects.bulk_create( diff --git a/apps/api/plane/tests/unit/serializers/test_label.py b/apps/api/plane/tests/unit/serializers/test_label.py new file mode 100644 index 000000000..91cde1c4a --- /dev/null +++ b/apps/api/plane/tests/unit/serializers/test_label.py @@ -0,0 +1,43 @@ +import pytest +from plane.app.serializers import LabelSerializer +from plane.db.models import Project, Label + + +@pytest.mark.unit +class TestLabelSerializer: + """Test the LabelSerializer""" + + @pytest.mark.django_db + def test_label_serializer_create_valid_data(self, db, workspace): + """Test creating a label with valid data""" + project = Project.objects.create( + name="Test Project", identifier="TEST", workspace=workspace + ) + + serializer = LabelSerializer( + data={"name": "Test Label"}, + context={"project_id": project.id}, + ) + assert serializer.is_valid() + assert serializer.errors == {} + serializer.save(project_id=project.id) + + label = Label.objects.all().first() + assert label.name == "Test Label" + assert label.project == project + assert label + + @pytest.mark.django_db + def test_label_serializer_create_duplicate_name(self, db, workspace): + """Test creating a label with a duplicate name""" + project = Project.objects.create( + name="Test Project", identifier="TEST", workspace=workspace + ) + + Label.objects.create(name="Test Label", project=project) + + serializer = LabelSerializer( + data={"name": "Test Label"}, context={"project_id": project.id} + ) + assert not serializer.is_valid() + assert serializer.errors == {"name": ["LABEL_NAME_ALREADY_EXISTS"]}