Merge pull request #925 from taigaio/issue/4910/validate_assigned_to

Fix Issue #5910: Create validator for assign_to attr
remotes/origin/github-import
David Barragán Merino 2017-01-30 12:49:53 +01:00 committed by GitHub
commit 65374df43f
10 changed files with 506 additions and 9 deletions

View File

@ -22,10 +22,10 @@ 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.mixins.validators import AssignedToValidator
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.validators import UserStoryExistsValidator
from taiga.projects.validators import ProjectExistsValidator
from . import models
@ -39,7 +39,8 @@ class EpicExistsValidator:
return attrs
class EpicValidator(WatchersValidator, EditableWatchedResourceSerializer, validators.ModelValidator):
class EpicValidator(AssignedToValidator, WatchersValidator, EditableWatchedResourceSerializer,
validators.ModelValidator):
tags = TagsAndTagsColorsField(default=[], required=False)
external_reference = PgArrayField(required=False)
@ -61,7 +62,6 @@ class CreateRelatedUserStoriesBulkValidator(ProjectExistsValidator, EpicExistsVa
bulk_userstories = serializers.CharField()
class EpicRelatedUserStoryValidator(validators.ModelValidator):
class Meta:
model = models.RelatedUserStory

View File

@ -19,6 +19,7 @@
from taiga.base.api import serializers
from taiga.base.api import validators
from taiga.base.fields import PgArrayField
from taiga.projects.mixins.validators import AssignedToValidator
from taiga.projects.notifications.mixins import EditableWatchedResourceSerializer
from taiga.projects.notifications.validators import WatchersValidator
from taiga.projects.tagging.fields import TagsAndTagsColorsField
@ -27,7 +28,7 @@ from taiga.projects.validators import ProjectExistsValidator
from . import models
class IssueValidator(WatchersValidator, EditableWatchedResourceSerializer,
class IssueValidator(AssignedToValidator, WatchersValidator, EditableWatchedResourceSerializer,
validators.ModelValidator):
tags = TagsAndTagsColorsField(default=[], required=False)

View File

@ -0,0 +1,21 @@
from django.utils.translation import ugettext as _
from taiga.base.exceptions import ValidationError
from taiga.projects.models import Membership
class AssignedToValidator:
def validate_assigned_to(self, attrs, source):
assigned_to = attrs[source]
project = (attrs.get("project", None) or
getattr(self.object, "project", None))
if assigned_to and project:
filters = {
"project_id": project.id,
"user_id": assigned_to.id
}
if not Membership.objects.filter(**filters).exists():
raise ValidationError(_("The user must be a project member."))
return attrs

View File

@ -23,6 +23,7 @@ from taiga.base.api import validators
from taiga.base.exceptions import ValidationError
from taiga.base.fields import PgArrayField
from taiga.projects.milestones.models import Milestone
from taiga.projects.mixins.validators import AssignedToValidator
from taiga.projects.models import TaskStatus
from taiga.projects.notifications.mixins import EditableWatchedResourceSerializer
from taiga.projects.notifications.validators import WatchersValidator
@ -33,7 +34,8 @@ from taiga.projects.validators import ProjectExistsValidator
from . import models
class TaskValidator(WatchersValidator, EditableWatchedResourceSerializer, validators.ModelValidator):
class TaskValidator(AssignedToValidator, WatchersValidator, EditableWatchedResourceSerializer,
validators.ModelValidator):
tags = TagsAndTagsColorsField(default=[], required=False)
external_reference = PgArrayField(required=False)

View File

@ -23,7 +23,9 @@ from taiga.base.api import validators
from taiga.base.exceptions import ValidationError
from taiga.base.fields import PgArrayField
from taiga.base.fields import PickledObjectField
from taiga.base.utils import json
from taiga.projects.milestones.models import Milestone
from taiga.projects.mixins.validators import AssignedToValidator
from taiga.projects.models import UserStoryStatus
from taiga.projects.notifications.mixins import EditableWatchedResourceSerializer
from taiga.projects.notifications.validators import WatchersValidator
@ -33,8 +35,6 @@ from taiga.projects.validators import ProjectExistsValidator
from . import models
import json
class UserStoryExistsValidator:
def validate_us_id(self, attrs, source):
@ -55,7 +55,8 @@ class RolePointsField(serializers.WritableField):
return json.loads(obj)
class UserStoryValidator(WatchersValidator, EditableWatchedResourceSerializer, validators.ModelValidator):
class UserStoryValidator(AssignedToValidator, WatchersValidator,
EditableWatchedResourceSerializer, validators.ModelValidator):
tags = TagsAndTagsColorsField(default=[], required=False)
external_reference = PgArrayField(required=False)
points = RolePointsField(source="role_points", required=False)

View File

@ -707,6 +707,7 @@ def create_project(**kwargs):
project.default_issue_type = IssueTypeFactory.create(project=project)
project.default_us_status = UserStoryStatusFactory.create(project=project)
project.default_task_status = TaskStatusFactory.create(project=project)
project.default_epic_status = EpicStatusFactory.create(project=project)
project.save()

View File

@ -25,8 +25,10 @@ from unittest import mock
from django.core.urlresolvers import reverse
from taiga.base.utils import json
from taiga.permissions.choices import MEMBERS_PERMISSIONS, ANON_PERMISSIONS
from taiga.projects.epics import services
from taiga.projects.epics import models
from taiga.projects.occ import OCCResourceMixin
from .. import factories as f
@ -162,3 +164,119 @@ def test_unset_related_userstory(client):
response = client.delete(url)
assert response.status_code == 204
assert not models.RelatedUserStory.objects.filter(id=related_us.id).exists()
def test_api_validator_assigned_to_when_update_epics(client):
project = f.create_project(anon_permissions=list(map(lambda x: x[0], ANON_PERMISSIONS)),
public_permissions=list(map(lambda x: x[0], ANON_PERMISSIONS)))
project_member_owner = f.MembershipFactory.create(project=project,
user=project.owner,
is_admin=True,
role__project=project,
role__permissions=list(map(lambda x: x[0], MEMBERS_PERMISSIONS)))
project_member = f.MembershipFactory.create(project=project,
is_admin=True,
role__project=project,
role__permissions=list(map(lambda x: x[0], MEMBERS_PERMISSIONS)))
project_no_member = f.MembershipFactory.create(is_admin=True)
epic = f.create_epic(project=project, owner=project.owner, status=project.epic_statuses.all()[0])
url = reverse('epics-detail', kwargs={"pk": epic.pk})
# assign
data = {
"assigned_to": project_member.user.id,
}
with mock.patch.object(OCCResourceMixin, "_validate_and_update_version"):
client.login(project.owner)
response = client.json.patch(url, json.dumps(data))
assert response.status_code == 200, response.data
assert "assigned_to" in response.data
assert response.data["assigned_to"] == project_member.user.id
# unassign
data = {
"assigned_to": None,
}
with mock.patch.object(OCCResourceMixin, "_validate_and_update_version"):
client.login(project.owner)
response = client.json.patch(url, json.dumps(data))
assert response.status_code == 200, response.data
assert "assigned_to" in response.data
assert response.data["assigned_to"] == None
# assign to invalid user
data = {
"assigned_to": project_no_member.user.id,
}
with mock.patch.object(OCCResourceMixin, "_validate_and_update_version"):
client.login(project.owner)
response = client.json.patch(url, json.dumps(data))
assert response.status_code == 400, response.data
def test_api_validator_assigned_to_when_create_epics(client):
project = f.create_project(anon_permissions=list(map(lambda x: x[0], ANON_PERMISSIONS)),
public_permissions=list(map(lambda x: x[0], ANON_PERMISSIONS)))
project_member_owner = f.MembershipFactory.create(project=project,
user=project.owner,
is_admin=True,
role__project=project,
role__permissions=list(map(lambda x: x[0], MEMBERS_PERMISSIONS)))
project_member = f.MembershipFactory.create(project=project,
is_admin=True,
role__project=project,
role__permissions=list(map(lambda x: x[0], MEMBERS_PERMISSIONS)))
project_no_member = f.MembershipFactory.create(is_admin=True)
url = reverse('epics-list')
# assign
data = {
"subject": "test",
"project": project.id,
"assigned_to": project_member.user.id,
}
with mock.patch.object(OCCResourceMixin, "_validate_and_update_version"):
client.login(project.owner)
response = client.json.post(url, json.dumps(data))
assert response.status_code == 201, response.data
assert "assigned_to" in response.data
assert response.data["assigned_to"] == project_member.user.id
# unassign
data = {
"subject": "test",
"project": project.id,
"assigned_to": None,
}
with mock.patch.object(OCCResourceMixin, "_validate_and_update_version"):
client.login(project.owner)
response = client.json.post(url, json.dumps(data))
assert response.status_code == 201, response.data
assert "assigned_to" in response.data
assert response.data["assigned_to"] == None
# assign to invalid user
data = {
"subject": "test",
"project": project.id,
"assigned_to": project_no_member.user.id,
}
with mock.patch.object(OCCResourceMixin, "_validate_and_update_version"):
client.login(project.owner)
response = client.json.post(url, json.dumps(data))
assert response.status_code == 400, response.data

View File

@ -28,8 +28,10 @@ from unittest import mock
from django.core.urlresolvers import reverse
from taiga.projects.issues import services, models
from taiga.base.utils import json
from taiga.permissions.choices import MEMBERS_PERMISSIONS, ANON_PERMISSIONS
from taiga.projects.issues import services, models
from taiga.projects.occ import OCCResourceMixin
from .. import factories as f
@ -592,3 +594,119 @@ def test_custom_fields_csv_generation():
assert row[23] == attr.name
row = next(reader)
assert row[23] == "val1"
def test_api_validator_assigned_to_when_update_issues(client):
project = f.create_project(anon_permissions=list(map(lambda x: x[0], ANON_PERMISSIONS)),
public_permissions=list(map(lambda x: x[0], ANON_PERMISSIONS)))
project_member_owner = f.MembershipFactory.create(project=project,
user=project.owner,
is_admin=True,
role__project=project,
role__permissions=list(map(lambda x: x[0], MEMBERS_PERMISSIONS)))
project_member = f.MembershipFactory.create(project=project,
is_admin=True,
role__project=project,
role__permissions=list(map(lambda x: x[0], MEMBERS_PERMISSIONS)))
project_no_member = f.MembershipFactory.create(is_admin=True)
issue = f.create_issue(project=project, owner=project.owner)
url = reverse('issues-detail', kwargs={"pk": issue.pk})
# assign
data = {
"assigned_to": project_member.user.id,
}
with mock.patch.object(OCCResourceMixin, "_validate_and_update_version"):
client.login(project.owner)
response = client.json.patch(url, json.dumps(data))
assert response.status_code == 200, response.data
assert "assigned_to" in response.data
assert response.data["assigned_to"] == project_member.user.id
# unassign
data = {
"assigned_to": None,
}
with mock.patch.object(OCCResourceMixin, "_validate_and_update_version"):
client.login(project.owner)
response = client.json.patch(url, json.dumps(data))
assert response.status_code == 200, response.data
assert "assigned_to" in response.data
assert response.data["assigned_to"] == None
# assign to invalid user
data = {
"assigned_to": project_no_member.user.id,
}
with mock.patch.object(OCCResourceMixin, "_validate_and_update_version"):
client.login(project.owner)
response = client.json.patch(url, json.dumps(data))
assert response.status_code == 400, response.data
def test_api_validator_assigned_to_when_create_issues(client):
project = f.create_project(anon_permissions=list(map(lambda x: x[0], ANON_PERMISSIONS)),
public_permissions=list(map(lambda x: x[0], ANON_PERMISSIONS)))
project_member_owner = f.MembershipFactory.create(project=project,
user=project.owner,
is_admin=True,
role__project=project,
role__permissions=list(map(lambda x: x[0], MEMBERS_PERMISSIONS)))
project_member = f.MembershipFactory.create(project=project,
is_admin=True,
role__project=project,
role__permissions=list(map(lambda x: x[0], MEMBERS_PERMISSIONS)))
project_no_member = f.MembershipFactory.create(is_admin=True)
url = reverse('issues-list')
# assign
data = {
"subject": "test",
"project": project.id,
"assigned_to": project_member.user.id,
}
with mock.patch.object(OCCResourceMixin, "_validate_and_update_version"):
client.login(project.owner)
response = client.json.post(url, json.dumps(data))
assert response.status_code == 201, response.data
assert "assigned_to" in response.data
assert response.data["assigned_to"] == project_member.user.id
# unassign
data = {
"subject": "test",
"project": project.id,
"assigned_to": None,
}
with mock.patch.object(OCCResourceMixin, "_validate_and_update_version"):
client.login(project.owner)
response = client.json.post(url, json.dumps(data))
assert response.status_code == 201, response.data
assert "assigned_to" in response.data
assert response.data["assigned_to"] == None
# assign to invalid user
data = {
"subject": "test",
"project": project.id,
"assigned_to": project_no_member.user.id,
}
with mock.patch.object(OCCResourceMixin, "_validate_and_update_version"):
client.login(project.owner)
response = client.json.post(url, json.dumps(data))
assert response.status_code == 400, response.data

View File

@ -29,6 +29,8 @@ from unittest import mock
from django.core.urlresolvers import reverse
from taiga.base.utils import json
from taiga.permissions.choices import MEMBERS_PERMISSIONS, ANON_PERMISSIONS
from taiga.projects.occ import OCCResourceMixin
from taiga.projects.tasks import services
from .. import factories as f
@ -861,3 +863,119 @@ def test_api_filters_data(client):
assert next(filter(lambda i: i['name'] == tag1, response.data["tags"]))["count"] == 2
assert next(filter(lambda i: i['name'] == tag2, response.data["tags"]))["count"] == 2
assert next(filter(lambda i: i['name'] == tag3, response.data["tags"]))["count"] == 1
def test_api_validator_assigned_to_when_update_tasks(client):
project = f.create_project(anon_permissions=list(map(lambda x: x[0], ANON_PERMISSIONS)),
public_permissions=list(map(lambda x: x[0], ANON_PERMISSIONS)))
project_member_owner = f.MembershipFactory.create(project=project,
user=project.owner,
is_admin=True,
role__project=project,
role__permissions=list(map(lambda x: x[0], MEMBERS_PERMISSIONS)))
project_member = f.MembershipFactory.create(project=project,
is_admin=True,
role__project=project,
role__permissions=list(map(lambda x: x[0], MEMBERS_PERMISSIONS)))
project_no_member = f.MembershipFactory.create(is_admin=True)
task = f.create_task(project=project, milestone__project=project, user_story=None, owner=project.owner)
url = reverse('tasks-detail', kwargs={"pk": task.pk})
# assign
data = {
"assigned_to": project_member.user.id,
}
with mock.patch.object(OCCResourceMixin, "_validate_and_update_version"):
client.login(project.owner)
response = client.json.patch(url, json.dumps(data))
assert response.status_code == 200, response.data
assert "assigned_to" in response.data
assert response.data["assigned_to"] == project_member.user.id
# unassign
data = {
"assigned_to": None,
}
with mock.patch.object(OCCResourceMixin, "_validate_and_update_version"):
client.login(project.owner)
response = client.json.patch(url, json.dumps(data))
assert response.status_code == 200, response.data
assert "assigned_to" in response.data
assert response.data["assigned_to"] == None
# assign to invalid user
data = {
"assigned_to": project_no_member.user.id,
}
with mock.patch.object(OCCResourceMixin, "_validate_and_update_version"):
client.login(project.owner)
response = client.json.patch(url, json.dumps(data))
assert response.status_code == 400, response.data
def test_api_validator_assigned_to_when_create_tasks(client):
project = f.create_project(anon_permissions=list(map(lambda x: x[0], ANON_PERMISSIONS)),
public_permissions=list(map(lambda x: x[0], ANON_PERMISSIONS)))
project_member_owner = f.MembershipFactory.create(project=project,
user=project.owner,
is_admin=True,
role__project=project,
role__permissions=list(map(lambda x: x[0], MEMBERS_PERMISSIONS)))
project_member = f.MembershipFactory.create(project=project,
is_admin=True,
role__project=project,
role__permissions=list(map(lambda x: x[0], MEMBERS_PERMISSIONS)))
project_no_member = f.MembershipFactory.create(is_admin=True)
url = reverse('tasks-list')
# assign
data = {
"subject": "test",
"project": project.id,
"assigned_to": project_member.user.id,
}
with mock.patch.object(OCCResourceMixin, "_validate_and_update_version"):
client.login(project.owner)
response = client.json.post(url, json.dumps(data))
assert response.status_code == 201, response.data
assert "assigned_to" in response.data
assert response.data["assigned_to"] == project_member.user.id
# unassign
data = {
"subject": "test",
"project": project.id,
"assigned_to": None,
}
with mock.patch.object(OCCResourceMixin, "_validate_and_update_version"):
client.login(project.owner)
response = client.json.post(url, json.dumps(data))
assert response.status_code == 201, response.data
assert "assigned_to" in response.data
assert response.data["assigned_to"] == None
# assign to invalid user
data = {
"subject": "test",
"project": project.id,
"assigned_to": project_no_member.user.id,
}
with mock.patch.object(OCCResourceMixin, "_validate_and_update_version"):
client.login(project.owner)
response = client.json.post(url, json.dumps(data))
assert response.status_code == 400, response.data

View File

@ -28,6 +28,8 @@ from unittest import mock
from django.core.urlresolvers import reverse
from taiga.base.utils import json
from taiga.permissions.choices import MEMBERS_PERMISSIONS, ANON_PERMISSIONS
from taiga.projects.occ import OCCResourceMixin
from taiga.projects.userstories import services, models
from .. import factories as f
@ -1021,3 +1023,118 @@ def test_get_user_stories_including_attachments(client):
response = client.get(url)
assert response.status_code == 200
assert len(response.data[0].get("attachments")) == 1
def test_api_validator_assigned_to_when_update_userstories(client):
project = f.create_project(anon_permissions=list(map(lambda x: x[0], ANON_PERMISSIONS)),
public_permissions=list(map(lambda x: x[0], ANON_PERMISSIONS)))
project_member_owner = f.MembershipFactory.create(project=project,
user=project.owner,
is_admin=True,
role__project=project,
role__permissions=list(map(lambda x: x[0], MEMBERS_PERMISSIONS)))
project_member = f.MembershipFactory.create(project=project,
is_admin=True,
role__project=project,
role__permissions=list(map(lambda x: x[0], MEMBERS_PERMISSIONS)))
project_no_member = f.MembershipFactory.create(is_admin=True)
userstory = f.create_userstory(project=project, owner=project.owner, status=project.us_statuses.all()[0])
url = reverse('userstories-detail', kwargs={"pk": userstory.pk})
# assign
data = {
"assigned_to": project_member.user.id,
}
with mock.patch.object(OCCResourceMixin, "_validate_and_update_version"):
client.login(project.owner)
response = client.json.patch(url, json.dumps(data))
assert response.status_code == 200, response.data
assert "assigned_to" in response.data
assert response.data["assigned_to"] == project_member.user.id
# unassign
data = {
"assigned_to": None,
}
with mock.patch.object(OCCResourceMixin, "_validate_and_update_version"):
client.login(project.owner)
response = client.json.patch(url, json.dumps(data))
assert response.status_code == 200, response.data
assert "assigned_to" in response.data
assert response.data["assigned_to"] == None
# assign to invalid user
data = {
"assigned_to": project_no_member.user.id,
}
with mock.patch.object(OCCResourceMixin, "_validate_and_update_version"):
client.login(project.owner)
response = client.json.patch(url, json.dumps(data))
assert response.status_code == 400, response.data
def test_api_validator_assigned_to_when_create_userstories(client):
project = f.create_project(anon_permissions=list(map(lambda x: x[0], ANON_PERMISSIONS)),
public_permissions=list(map(lambda x: x[0], ANON_PERMISSIONS)))
project_member_owner = f.MembershipFactory.create(project=project,
user=project.owner,
is_admin=True,
role__project=project,
role__permissions=list(map(lambda x: x[0], MEMBERS_PERMISSIONS)))
project_member = f.MembershipFactory.create(project=project,
is_admin=True,
role__project=project,
role__permissions=list(map(lambda x: x[0], MEMBERS_PERMISSIONS)))
project_no_member = f.MembershipFactory.create(is_admin=True)
url = reverse('userstories-list')
# assign
data = {
"subject": "test",
"project": project.id,
"assigned_to": project_member.user.id,
}
with mock.patch.object(OCCResourceMixin, "_validate_and_update_version"):
client.login(project.owner)
response = client.json.post(url, json.dumps(data))
assert response.status_code == 201, response.data
assert "assigned_to" in response.data
assert response.data["assigned_to"] == project_member.user.id
# unassign
data = {
"subject": "test",
"project": project.id,
"assigned_to": None,
}
with mock.patch.object(OCCResourceMixin, "_validate_and_update_version"):
client.login(project.owner)
response = client.json.post(url, json.dumps(data))
assert response.status_code == 201, response.data
assert "assigned_to" in response.data
assert response.data["assigned_to"] == None
# assign to invalid user
data = {
"subject": "test",
"project": project.id,
"assigned_to": project_no_member.user.id,
}
with mock.patch.object(OCCResourceMixin, "_validate_and_update_version"):
client.login(project.owner)
response = client.json.post(url, json.dumps(data))
assert response.status_code == 400, response.data