diff --git a/taiga/projects/userstories/validators.py b/taiga/projects/userstories/validators.py index ba470456..e82b1f75 100644 --- a/taiga/projects/userstories/validators.py +++ b/taiga/projects/userstories/validators.py @@ -36,15 +36,6 @@ from . import models import json -class UserStoryExistsValidator: - def validate_us_id(self, attrs, source): - value = attrs[source] - if not models.UserStory.objects.filter(pk=value).exists(): - msg = _("There's no user story with that id") - raise ValidationError(msg) - return attrs - - class RolePointsField(serializers.WritableField): def to_native(self, obj): return {str(o.role.id): o.points.id for o in obj.all()} @@ -76,7 +67,7 @@ class UserStoriesBulkValidator(ProjectExistsValidator, UserStoryStatusExistsVali # Order bulk validators -class _UserStoryOrderBulkValidator(UserStoryExistsValidator, validators.Validator): +class _UserStoryOrderBulkValidator(validators.Validator): us_id = serializers.IntegerField() order = serializers.IntegerField() @@ -88,10 +79,25 @@ class UpdateUserStoriesOrderBulkValidator(ProjectExistsValidator, UserStoryStatu milestone_id = serializers.IntegerField(required=False) bulk_stories = _UserStoryOrderBulkValidator(many=True) + def validate(self, data): + filters = {"project__id": data["project_id"]} + if "status_id" in data: + filters["status__id"] = data["status_id"] + if "milestone_id" in data: + filters["milestone__id"] = data["milestone_id"] + + filters["id__in"] = [us["us_id"] for us in data["bulk_stories"]] + + if models.UserStory.objects.filter(**filters).count() != len(filters["id__in"]): + raise ValidationError(_("Invalid user story ids. All stories must belong to the same project and, " + "if it exists, to the same status and milestone.")) + + return data + # Milestone bulk validators -class _UserStoryMilestoneBulkValidator(UserStoryExistsValidator, validators.Validator): +class _UserStoryMilestoneBulkValidator(validators.Validator): us_id = serializers.IntegerField() @@ -108,9 +114,9 @@ class UpdateMilestoneBulkValidator(ProjectExistsValidator, MilestoneExistsValida project = get_object_or_404(Project, pk=data["project_id"]) if project.user_stories.filter(id__in=user_story_ids).count() != len(user_story_ids): - raise ValidationError("all the user stories must be from the same project") + raise ValidationError(_("All the user stories must be from the same project")) if project.milestones.filter(id=data["milestone_id"]).count() != 1: - raise ValidationError("the milestone isn't valid for the project") + raise ValidationError(_("The milestone isn't valid for the project")) return data diff --git a/tests/integration/test_userstories.py b/tests/integration/test_userstories.py index 1d3984bd..bfb86abc 100644 --- a/tests/integration/test_userstories.py +++ b/tests/integration/test_userstories.py @@ -180,6 +180,96 @@ def test_api_update_orders_in_bulk(client): assert response3.status_code == 200, response3.data +def test_api_update_orders_in_bulk_invalid_userstories(client): + project = f.create_project() + f.MembershipFactory.create(project=project, user=project.owner, is_admin=True) + us1 = f.create_userstory(project=project) + us2 = f.create_userstory(project=project) + us3 = f.create_userstory() + + url1 = reverse("userstories-bulk-update-backlog-order") + url2 = reverse("userstories-bulk-update-kanban-order") + url3 = reverse("userstories-bulk-update-sprint-order") + + data = { + "project_id": project.id, + "bulk_stories": [{"us_id": us1.id, "order": 1}, + {"us_id": us2.id, "order": 2}, + {"us_id": us3.id, "order": 3}] + } + + client.login(project.owner) + + response1 = client.json.post(url1, json.dumps(data)) + response2 = client.json.post(url2, json.dumps(data)) + response3 = client.json.post(url3, json.dumps(data)) + + assert response1.status_code == 400, response1.data + assert response2.status_code == 400, response2.data + assert response3.status_code == 400, response3.data + + +def test_api_update_orders_in_bulk_invalid_status(client): + project = f.create_project() + f.MembershipFactory.create(project=project, user=project.owner, is_admin=True) + us1 = f.create_userstory(project=project) + us2 = f.create_userstory(project=project, status=us1.status) + us3 = f.create_userstory(project=project) + + url1 = reverse("userstories-bulk-update-backlog-order") + url2 = reverse("userstories-bulk-update-kanban-order") + url3 = reverse("userstories-bulk-update-sprint-order") + + data = { + "project_id": project.id, + "status_id": us1.status.id, + "bulk_stories": [{"us_id": us1.id, "order": 1}, + {"us_id": us2.id, "order": 2}, + {"us_id": us3.id, "order": 3}] + } + + client.login(project.owner) + + response1 = client.json.post(url1, json.dumps(data)) + response2 = client.json.post(url2, json.dumps(data)) + response3 = client.json.post(url3, json.dumps(data)) + + assert response1.status_code == 400, response1.data + assert response2.status_code == 400, response2.data + assert response3.status_code == 400, response3.data + + +def test_api_update_orders_in_bulk_invalid_milestione(client): + project = f.create_project() + f.MembershipFactory.create(project=project, user=project.owner, is_admin=True) + mil1 = f.MilestoneFactory.create(project=project) + us1 = f.create_userstory(project=project, milestone=mil1) + us2 = f.create_userstory(project=project, milestone=mil1) + us3 = f.create_userstory(project=project) + + url1 = reverse("userstories-bulk-update-backlog-order") + url2 = reverse("userstories-bulk-update-kanban-order") + url3 = reverse("userstories-bulk-update-sprint-order") + + data = { + "project_id": project.id, + "milestone_id": mil1.id, + "bulk_stories": [{"us_id": us1.id, "order": 1}, + {"us_id": us2.id, "order": 2}, + {"us_id": us3.id, "order": 3}] + } + + client.login(project.owner) + + response1 = client.json.post(url1, json.dumps(data)) + response2 = client.json.post(url2, json.dumps(data)) + response3 = client.json.post(url3, json.dumps(data)) + + assert response1.status_code == 400, response1.data + assert response2.status_code == 400, response2.data + assert response3.status_code == 400, response3.data + + def test_api_update_milestone_in_bulk(client): project = f.create_project() f.MembershipFactory.create(project=project, user=project.owner, is_admin=True) @@ -208,7 +298,6 @@ def test_api_update_milestone_in_bulk_invalid_milestone(client): f.MembershipFactory.create(project=project, user=project.owner, is_admin=True) us1 = f.create_userstory(project=project) us2 = f.create_userstory(project=project) - f.MilestoneFactory.create(project=project) m2 = f.MilestoneFactory.create() url = reverse("userstories-bulk-update-milestone")