Fix tasks validators for bulk operations
parent
d1f7158125
commit
3187bdfed9
|
@ -22,23 +22,18 @@ from taiga.base.api import serializers
|
|||
from taiga.base.api import validators
|
||||
from taiga.base.exceptions import ValidationError
|
||||
from taiga.base.fields import PgArrayField
|
||||
from taiga.projects.milestones.validators import MilestoneExistsValidator
|
||||
from taiga.projects.milestones.models import Milestone
|
||||
from taiga.projects.models import TaskStatus
|
||||
from taiga.projects.notifications.mixins import EditableWatchedResourceSerializer
|
||||
from taiga.projects.notifications.validators import WatchersValidator
|
||||
from taiga.projects.tagging.fields import TagsAndTagsColorsField
|
||||
from taiga.projects.userstories.models import UserStory
|
||||
from taiga.projects.validators import ProjectExistsValidator
|
||||
|
||||
|
||||
from . import models
|
||||
|
||||
|
||||
class TaskExistsValidator:
|
||||
def validate_task_id(self, attrs, source):
|
||||
value = attrs[source]
|
||||
if not models.Task.objects.filter(pk=value).exists():
|
||||
msg = _("There's no task with that id")
|
||||
raise ValidationError(msg)
|
||||
return attrs
|
||||
|
||||
|
||||
class TaskValidator(WatchersValidator, EditableWatchedResourceSerializer, validators.ModelValidator):
|
||||
tags = TagsAndTagsColorsField(default=[], required=False)
|
||||
external_reference = PgArrayField(required=False)
|
||||
|
@ -48,25 +43,72 @@ class TaskValidator(WatchersValidator, EditableWatchedResourceSerializer, valida
|
|||
read_only_fields = ('id', 'ref', 'created_date', 'modified_date', 'owner')
|
||||
|
||||
|
||||
class TasksBulkValidator(ProjectExistsValidator, MilestoneExistsValidator,
|
||||
TaskExistsValidator, validators.Validator):
|
||||
class TasksBulkValidator(ProjectExistsValidator, validators.Validator):
|
||||
project_id = serializers.IntegerField()
|
||||
sprint_id = serializers.IntegerField()
|
||||
status_id = serializers.IntegerField(required=False)
|
||||
us_id = serializers.IntegerField(required=False)
|
||||
bulk_tasks = serializers.CharField()
|
||||
|
||||
def validate_sprint_id(self, attrs, source):
|
||||
filters = {"project__id": attrs["project_id"]}
|
||||
filters["id"] = attrs["sprint_id"]
|
||||
|
||||
if not Milestone.objects.filter(**filters).exists():
|
||||
raise ValidationError(_("Invalid sprint id."))
|
||||
|
||||
return attrs
|
||||
|
||||
def validate_status_id(self, attrs, source):
|
||||
filters = {"project__id": attrs["project_id"]}
|
||||
filters["id"] = attrs["status_id"]
|
||||
|
||||
if not TaskStatus.objects.filter(**filters).exists():
|
||||
raise ValidationError(_("Invalid task status id."))
|
||||
|
||||
return attrs
|
||||
|
||||
def validate_us_id(self, attrs, source):
|
||||
filters = {"project__id": attrs["project_id"]}
|
||||
|
||||
if "sprint_id" in attrs:
|
||||
filters["milestone__id"] = attrs["sprint_id"]
|
||||
|
||||
filters["id"] = attrs["us_id"]
|
||||
|
||||
if not UserStory.objects.filter(**filters).exists():
|
||||
raise ValidationError(_("Invalid sprint id."))
|
||||
|
||||
return attrs
|
||||
|
||||
|
||||
# Order bulk validators
|
||||
|
||||
class _TaskOrderBulkValidator(TaskExistsValidator, validators.Validator):
|
||||
class _TaskOrderBulkValidator(validators.Validator):
|
||||
task_id = serializers.IntegerField()
|
||||
order = serializers.IntegerField()
|
||||
|
||||
|
||||
class UpdateTasksOrderBulkValidator(ProjectExistsValidator, validators.Validator):
|
||||
project_id = serializers.IntegerField()
|
||||
milestone_id = serializers.IntegerField(required=False)
|
||||
status_id = serializers.IntegerField(required=False)
|
||||
us_id = serializers.IntegerField(required=False)
|
||||
milestone_id = serializers.IntegerField(required=False)
|
||||
bulk_tasks = _TaskOrderBulkValidator(many=True)
|
||||
|
||||
def validate(self, data):
|
||||
filters = {"project__id": data["project_id"]}
|
||||
if "status_id" in data:
|
||||
filters["status__id"] = data["status_id"]
|
||||
if "us_id" in data:
|
||||
filters["user_story__id"] = data["us_id"]
|
||||
if "milestone_id" in data:
|
||||
filters["milestone__id"] = data["milestone_id"]
|
||||
|
||||
filters["id__in"] = [t["task_id"] for t in data["bulk_tasks"]]
|
||||
|
||||
if models.Task.objects.filter(**filters).count() != len(filters["id__in"]):
|
||||
raise ValidationError(_("Invalid task ids. All tasks must belong to the same project and, "
|
||||
"if it exists, to the same status, user story and/or milestone."))
|
||||
|
||||
return data
|
||||
|
|
|
@ -85,10 +85,16 @@ def test_create_task_without_default_values(client):
|
|||
assert response.data['status'] == None
|
||||
|
||||
|
||||
def test_api_create_in_bulk_with_status(client):
|
||||
us = f.create_userstory()
|
||||
f.MembershipFactory.create(project=us.project, user=us.owner, is_admin=True)
|
||||
us.project.default_task_status = f.TaskStatusFactory.create(project=us.project)
|
||||
def test_api_create_in_bulk_with_status_milestone_userstory(client):
|
||||
user = f.UserFactory.create()
|
||||
project = f.ProjectFactory.create(owner=user, default_task_status=None)
|
||||
f.MembershipFactory.create(project=project, user=user, is_admin=True)
|
||||
|
||||
project.default_task_status = f.TaskStatusFactory.create(project=project)
|
||||
project.save()
|
||||
milestone = f.MilestoneFactory(project=project)
|
||||
us = f.create_userstory(project=project, milestone=milestone)
|
||||
|
||||
url = reverse("tasks-bulk-create")
|
||||
data = {
|
||||
"bulk_tasks": "Story #1\nStory #2",
|
||||
|
@ -98,13 +104,141 @@ def test_api_create_in_bulk_with_status(client):
|
|||
"status_id": us.project.default_task_status.id
|
||||
}
|
||||
|
||||
client.login(us.owner)
|
||||
client.login(user)
|
||||
response = client.json.post(url, json.dumps(data))
|
||||
|
||||
assert response.status_code == 200
|
||||
assert response.data[0]["status"] == us.project.default_task_status.id
|
||||
|
||||
|
||||
def test_api_create_in_bulk_with_status_milestone(client):
|
||||
user = f.UserFactory.create()
|
||||
project = f.ProjectFactory.create(owner=user, default_task_status=None)
|
||||
f.MembershipFactory.create(project=project, user=user, is_admin=True)
|
||||
|
||||
project.default_task_status = f.TaskStatusFactory.create(project=project)
|
||||
project.save()
|
||||
milestone = f.MilestoneFactory(project=project)
|
||||
us = f.create_userstory(project=project, milestone=milestone)
|
||||
|
||||
url = reverse("tasks-bulk-create")
|
||||
data = {
|
||||
"bulk_tasks": "Story #1\nStory #2",
|
||||
"project_id": us.project.id,
|
||||
"sprint_id": us.milestone.id,
|
||||
"status_id": us.project.default_task_status.id
|
||||
}
|
||||
|
||||
client.login(user)
|
||||
response = client.json.post(url, json.dumps(data))
|
||||
|
||||
assert response.status_code == 200
|
||||
assert response.data[0]["status"] == us.project.default_task_status.id
|
||||
|
||||
|
||||
def test_api_create_in_bulk_with_invalid_status(client):
|
||||
user = f.UserFactory.create()
|
||||
project = f.ProjectFactory.create(owner=user, default_task_status=None)
|
||||
f.MembershipFactory.create(project=project, user=user, is_admin=True)
|
||||
|
||||
project.default_task_status = f.TaskStatusFactory.create(project=project)
|
||||
project.save()
|
||||
milestone = f.MilestoneFactory(project=project)
|
||||
us = f.create_userstory(project=project, milestone=milestone)
|
||||
|
||||
status = f.TaskStatusFactory.create()
|
||||
|
||||
|
||||
url = reverse("tasks-bulk-create")
|
||||
data = {
|
||||
"bulk_tasks": "Story #1\nStory #2",
|
||||
"us_id": us.id,
|
||||
"project_id": project.id,
|
||||
"sprint_id": milestone.id,
|
||||
"status_id": status.id
|
||||
}
|
||||
|
||||
client.login(user)
|
||||
response = client.json.post(url, json.dumps(data))
|
||||
|
||||
assert response.status_code == 400
|
||||
|
||||
|
||||
def test_api_create_in_bulk_with_invalid_milestone(client):
|
||||
user = f.UserFactory.create()
|
||||
project = f.ProjectFactory.create(owner=user, default_task_status=None)
|
||||
f.MembershipFactory.create(project=project, user=user, is_admin=True)
|
||||
|
||||
project.default_task_status = f.TaskStatusFactory.create(project=project)
|
||||
project.save()
|
||||
milestone = f.MilestoneFactory()
|
||||
us = f.create_userstory(project=project)
|
||||
|
||||
url = reverse("tasks-bulk-create")
|
||||
data = {
|
||||
"bulk_tasks": "Story #1\nStory #2",
|
||||
"us_id": us.id,
|
||||
"project_id": project.id,
|
||||
"sprint_id": milestone.id,
|
||||
"status_id": project.default_task_status.id
|
||||
}
|
||||
|
||||
client.login(user)
|
||||
response = client.json.post(url, json.dumps(data))
|
||||
|
||||
assert response.status_code == 400
|
||||
|
||||
|
||||
def test_api_create_in_bulk_with_invalid_userstory_1(client):
|
||||
user = f.UserFactory.create()
|
||||
project = f.ProjectFactory.create(owner=user, default_task_status=None)
|
||||
f.MembershipFactory.create(project=project, user=user, is_admin=True)
|
||||
|
||||
project.default_task_status = f.TaskStatusFactory.create(project=project)
|
||||
project.save()
|
||||
milestone = f.MilestoneFactory(project=project)
|
||||
us = f.create_userstory()
|
||||
|
||||
url = reverse("tasks-bulk-create")
|
||||
data = {
|
||||
"bulk_tasks": "Story #1\nStory #2",
|
||||
"us_id": us.id,
|
||||
"project_id": project.id,
|
||||
"sprint_id": milestone.id,
|
||||
"status_id": project.default_task_status.id
|
||||
}
|
||||
|
||||
client.login(user)
|
||||
response = client.json.post(url, json.dumps(data))
|
||||
|
||||
assert response.status_code == 400
|
||||
|
||||
|
||||
def test_api_create_in_bulk_with_invalid_userstory_2(client):
|
||||
user = f.UserFactory.create()
|
||||
project = f.ProjectFactory.create(owner=user, default_task_status=None)
|
||||
f.MembershipFactory.create(project=project, user=user, is_admin=True)
|
||||
|
||||
project.default_task_status = f.TaskStatusFactory.create(project=project)
|
||||
project.save()
|
||||
milestone = f.MilestoneFactory(project=project)
|
||||
us = f.create_userstory(project=project)
|
||||
|
||||
url = reverse("tasks-bulk-create")
|
||||
data = {
|
||||
"bulk_tasks": "Story #1\nStory #2",
|
||||
"us_id": us.id,
|
||||
"project_id": us.project.id,
|
||||
"sprint_id": milestone.id,
|
||||
"status_id": us.project.default_task_status.id
|
||||
}
|
||||
|
||||
client.login(user)
|
||||
response = client.json.post(url, json.dumps(data))
|
||||
|
||||
assert response.status_code == 400
|
||||
|
||||
|
||||
def test_api_create_invalid_task(client):
|
||||
# Associated to a milestone and a user story.
|
||||
# But the User Story is not associated with the milestone
|
||||
|
@ -152,6 +286,115 @@ def test_api_update_order_in_bulk(client):
|
|||
assert response2.status_code == 200, response2.data
|
||||
|
||||
|
||||
def test_api_update_order_in_bulk_invalid_tasks(client):
|
||||
project = f.create_project()
|
||||
f.MembershipFactory.create(project=project, user=project.owner, is_admin=True)
|
||||
task1 = f.create_task(project=project)
|
||||
task2 = f.create_task(project=project)
|
||||
task3 = f.create_task()
|
||||
|
||||
url1 = reverse("tasks-bulk-update-taskboard-order")
|
||||
url2 = reverse("tasks-bulk-update-us-order")
|
||||
|
||||
data = {
|
||||
"project_id": project.id,
|
||||
"bulk_tasks": [{"task_id": task1.id, "order": 1},
|
||||
{"task_id": task2.id, "order": 2},
|
||||
{"task_id": task3.id, "order": 3}]
|
||||
}
|
||||
|
||||
client.login(project.owner)
|
||||
|
||||
response1 = client.json.post(url1, json.dumps(data))
|
||||
response2 = client.json.post(url2, json.dumps(data))
|
||||
|
||||
assert response1.status_code == 400, response1.data
|
||||
assert response2.status_code == 400, response2.data
|
||||
|
||||
|
||||
def test_api_update_order_in_bulk_invalid_status(client):
|
||||
project = f.create_project()
|
||||
f.MembershipFactory.create(project=project, user=project.owner, is_admin=True)
|
||||
task1 = f.create_task(project=project)
|
||||
task2 = f.create_task(project=project, status=task1.status)
|
||||
task3 = f.create_task(project=project)
|
||||
|
||||
url1 = reverse("tasks-bulk-update-taskboard-order")
|
||||
url2 = reverse("tasks-bulk-update-us-order")
|
||||
|
||||
data = {
|
||||
"project_id": project.id,
|
||||
"status_id": task1.status.id,
|
||||
"bulk_tasks": [{"task_id": task1.id, "order": 1},
|
||||
{"task_id": task2.id, "order": 2},
|
||||
{"task_id": task3.id, "order": 3}]
|
||||
}
|
||||
|
||||
client.login(project.owner)
|
||||
|
||||
response1 = client.json.post(url1, json.dumps(data))
|
||||
response2 = client.json.post(url2, json.dumps(data))
|
||||
|
||||
assert response1.status_code == 400, response1.data
|
||||
assert response2.status_code == 400, response2.data
|
||||
|
||||
|
||||
def test_api_update_order_in_bulk_invalid_milestone(client):
|
||||
project = f.create_project()
|
||||
f.MembershipFactory.create(project=project, user=project.owner, is_admin=True)
|
||||
mil1 = f.MilestoneFactory.create(project=project)
|
||||
task1 = f.create_task(project=project, milestone=mil1)
|
||||
task2 = f.create_task(project=project, milestone=mil1)
|
||||
task3 = f.create_task(project=project)
|
||||
|
||||
url1 = reverse("tasks-bulk-update-taskboard-order")
|
||||
url2 = reverse("tasks-bulk-update-us-order")
|
||||
|
||||
data = {
|
||||
"project_id": project.id,
|
||||
"milestone_id": mil1.id,
|
||||
"bulk_tasks": [{"task_id": task1.id, "order": 1},
|
||||
{"task_id": task2.id, "order": 2},
|
||||
{"task_id": task3.id, "order": 3}]
|
||||
}
|
||||
|
||||
client.login(project.owner)
|
||||
|
||||
response1 = client.json.post(url1, json.dumps(data))
|
||||
response2 = client.json.post(url2, json.dumps(data))
|
||||
|
||||
assert response1.status_code == 400, response1.data
|
||||
assert response2.status_code == 400, response2.data
|
||||
|
||||
|
||||
def test_api_update_order_in_bulk_invalid_user_story(client):
|
||||
project = f.create_project()
|
||||
f.MembershipFactory.create(project=project, user=project.owner, is_admin=True)
|
||||
us1 = f.create_userstory(project=project)
|
||||
task1 = f.create_task(project=project, user_story=us1)
|
||||
task2 = f.create_task(project=project, user_story=us1)
|
||||
task3 = f.create_task(project=project)
|
||||
|
||||
url1 = reverse("tasks-bulk-update-taskboard-order")
|
||||
url2 = reverse("tasks-bulk-update-us-order")
|
||||
|
||||
data = {
|
||||
"project_id": project.id,
|
||||
"us_id": us1.id,
|
||||
"bulk_tasks": [{"task_id": task1.id, "order": 1},
|
||||
{"task_id": task2.id, "order": 2},
|
||||
{"task_id": task3.id, "order": 3}]
|
||||
}
|
||||
|
||||
client.login(project.owner)
|
||||
|
||||
response1 = client.json.post(url1, json.dumps(data))
|
||||
response2 = client.json.post(url2, json.dumps(data))
|
||||
|
||||
assert response1.status_code == 400, response1.data
|
||||
assert response2.status_code == 400, response2.data
|
||||
|
||||
|
||||
def test_get_invalid_csv(client):
|
||||
url = reverse("tasks-csv")
|
||||
|
||||
|
|
Loading…
Reference in New Issue