Fix tasks validators for bulk operations

remotes/origin/issue/4795/notification_even_they_are_disabled
David Barragán Merino 2016-08-02 15:14:07 +02:00
parent d1f7158125
commit 3187bdfed9
2 changed files with 304 additions and 19 deletions

View File

@ -22,23 +22,18 @@ from taiga.base.api import serializers
from taiga.base.api import validators from taiga.base.api import validators
from taiga.base.exceptions import ValidationError from taiga.base.exceptions import ValidationError
from taiga.base.fields import PgArrayField 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.mixins import EditableWatchedResourceSerializer
from taiga.projects.notifications.validators import WatchersValidator from taiga.projects.notifications.validators import WatchersValidator
from taiga.projects.tagging.fields import TagsAndTagsColorsField from taiga.projects.tagging.fields import TagsAndTagsColorsField
from taiga.projects.userstories.models import UserStory
from taiga.projects.validators import ProjectExistsValidator from taiga.projects.validators import ProjectExistsValidator
from . import models 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): class TaskValidator(WatchersValidator, EditableWatchedResourceSerializer, validators.ModelValidator):
tags = TagsAndTagsColorsField(default=[], required=False) tags = TagsAndTagsColorsField(default=[], required=False)
external_reference = PgArrayField(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') read_only_fields = ('id', 'ref', 'created_date', 'modified_date', 'owner')
class TasksBulkValidator(ProjectExistsValidator, MilestoneExistsValidator, class TasksBulkValidator(ProjectExistsValidator, validators.Validator):
TaskExistsValidator, validators.Validator):
project_id = serializers.IntegerField() project_id = serializers.IntegerField()
sprint_id = serializers.IntegerField() sprint_id = serializers.IntegerField()
status_id = serializers.IntegerField(required=False) status_id = serializers.IntegerField(required=False)
us_id = serializers.IntegerField(required=False) us_id = serializers.IntegerField(required=False)
bulk_tasks = serializers.CharField() 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 # Order bulk validators
class _TaskOrderBulkValidator(TaskExistsValidator, validators.Validator): class _TaskOrderBulkValidator(validators.Validator):
task_id = serializers.IntegerField() task_id = serializers.IntegerField()
order = serializers.IntegerField() order = serializers.IntegerField()
class UpdateTasksOrderBulkValidator(ProjectExistsValidator, validators.Validator): class UpdateTasksOrderBulkValidator(ProjectExistsValidator, validators.Validator):
project_id = serializers.IntegerField() project_id = serializers.IntegerField()
milestone_id = serializers.IntegerField(required=False)
status_id = serializers.IntegerField(required=False) status_id = serializers.IntegerField(required=False)
us_id = serializers.IntegerField(required=False) us_id = serializers.IntegerField(required=False)
milestone_id = serializers.IntegerField(required=False)
bulk_tasks = _TaskOrderBulkValidator(many=True) 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

View File

@ -85,10 +85,16 @@ def test_create_task_without_default_values(client):
assert response.data['status'] == None assert response.data['status'] == None
def test_api_create_in_bulk_with_status(client): def test_api_create_in_bulk_with_status_milestone_userstory(client):
us = f.create_userstory() user = f.UserFactory.create()
f.MembershipFactory.create(project=us.project, user=us.owner, is_admin=True) project = f.ProjectFactory.create(owner=user, default_task_status=None)
us.project.default_task_status = f.TaskStatusFactory.create(project=us.project) 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") url = reverse("tasks-bulk-create")
data = { data = {
"bulk_tasks": "Story #1\nStory #2", "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 "status_id": us.project.default_task_status.id
} }
client.login(us.owner) client.login(user)
response = client.json.post(url, json.dumps(data)) response = client.json.post(url, json.dumps(data))
assert response.status_code == 200 assert response.status_code == 200
assert response.data[0]["status"] == us.project.default_task_status.id 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): def test_api_create_invalid_task(client):
# Associated to a milestone and a user story. # Associated to a milestone and a user story.
# But the User Story is not associated with the milestone # 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 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): def test_get_invalid_csv(client):
url = reverse("tasks-csv") url = reverse("tasks-csv")