US #55: Custom fields - Create model to save the custom attributes values and update the history

remotes/origin/enhancement/email-actions
David Barragán Merino 2015-02-05 18:08:09 +01:00
parent ebc3388d03
commit 2893213932
23 changed files with 2310 additions and 807 deletions

View File

@ -14,9 +14,17 @@
# You should have received a copy of the GNU Affero General Public License # You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
from django.utils.translation import ugettext_lazy as _
from taiga.base.api import ModelCrudViewSet from taiga.base.api import ModelCrudViewSet
from taiga.base.api.viewsets import ModelViewSet
from taiga.base import exceptions as exc
from taiga.base import filters from taiga.base import filters
from taiga.base import response
from taiga.projects.mixins.ordering import BulkUpdateOrderMixin from taiga.projects.mixins.ordering import BulkUpdateOrderMixin
from taiga.projects.history.mixins import HistoryResourceMixin
from taiga.projects.occ.mixins import OCCResourceMixin
from . import models from . import models
from . import serializers from . import serializers
@ -24,6 +32,10 @@ from . import permissions
from . import services from . import services
######################################################
# Custom Attribute ViewSets
#######################################################
class UserStoryCustomAttributeViewSet(ModelCrudViewSet, BulkUpdateOrderMixin): class UserStoryCustomAttributeViewSet(ModelCrudViewSet, BulkUpdateOrderMixin):
model = models.UserStoryCustomAttribute model = models.UserStoryCustomAttribute
serializer_class = serializers.UserStoryCustomAttributeSerializer serializer_class = serializers.UserStoryCustomAttributeSerializer
@ -55,3 +67,45 @@ class IssueCustomAttributeViewSet(ModelCrudViewSet, BulkUpdateOrderMixin):
bulk_update_param = "bulk_issue_custom_attributes" bulk_update_param = "bulk_issue_custom_attributes"
bulk_update_perm = "change_issue_custom_attributes" bulk_update_perm = "change_issue_custom_attributes"
bulk_update_order_action = services.bulk_update_issue_custom_attribute_order bulk_update_order_action = services.bulk_update_issue_custom_attribute_order
######################################################
# Custom Attributes Values ViewSets
#######################################################
class BaseCustomAttributesValuesViewSet(OCCResourceMixin, HistoryResourceMixin, ModelViewSet):
def list(self, request, *args, **kwargs):
return response.NotFound()
def post_delete(self, obj):
# NOTE: When destroy a custom attributes values object, the
# content_object change after and not before
self.persist_history_snapshot(obj, delete=True)
super().pre_delete(obj)
def get_object_for_snapshot(self, obj):
return getattr(obj, self.content_object)
class UserStoryCustomAttributesValuesViewSet(BaseCustomAttributesValuesViewSet):
model = models.UserStoryCustomAttributesValues
serializer_class = serializers.UserStoryCustomAttributesValuesSerializer
permission_classes = (permissions.UserStoryCustomAttributesValuesPermission,)
lookup_field = "user_story_id"
content_object = "user_story"
class TaskCustomAttributesValuesViewSet(BaseCustomAttributesValuesViewSet):
model = models.TaskCustomAttributesValues
serializer_class = serializers.TaskCustomAttributesValuesSerializer
permission_classes = (permissions.TaskCustomAttributesValuesPermission,)
lockup_fields = "task_id"
content_object = "task"
class IssueCustomAttributesValuesViewSet(BaseCustomAttributesValuesViewSet):
model = models.IssueCustomAttributesValues
serializer_class = serializers.IssueCustomAttributesValuesSerializer
permission_classes = (permissions.IssueCustomAttributesValuesPermission,)
lockup_fields = "issue_id"
content_object = "issue"

View File

@ -0,0 +1,66 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
import django_pgjson.fields
class Migration(migrations.Migration):
dependencies = [
('tasks', '0005_auto_20150114_0954'),
('userstories', '0009_remove_userstory_is_archived'),
('issues', '0004_auto_20150114_0954'),
('custom_attributes', '0001_initial'),
]
operations = [
migrations.CreateModel(
name='IssueCustomAttributesValues',
fields=[
('id', models.AutoField(serialize=False, verbose_name='ID', auto_created=True, primary_key=True)),
('version', models.IntegerField(default=1, verbose_name='version')),
('values', django_pgjson.fields.JsonField(default={}, verbose_name='values')),
('issue', models.OneToOneField(related_name='custom_attributes_values', to='issues.Issue', verbose_name='issue')),
],
options={
'ordering': ['id'],
'verbose_name_plural': 'issue custom attributes values',
'verbose_name': 'issue ustom attributes values',
'abstract': False,
},
bases=(models.Model,),
),
migrations.CreateModel(
name='TaskCustomAttributesValues',
fields=[
('id', models.AutoField(serialize=False, verbose_name='ID', auto_created=True, primary_key=True)),
('version', models.IntegerField(default=1, verbose_name='version')),
('values', django_pgjson.fields.JsonField(default={}, verbose_name='values')),
('task', models.OneToOneField(related_name='custom_attributes_values', to='tasks.Task', verbose_name='task')),
],
options={
'ordering': ['id'],
'verbose_name_plural': 'task custom attributes values',
'verbose_name': 'task ustom attributes values',
'abstract': False,
},
bases=(models.Model,),
),
migrations.CreateModel(
name='UserStoryCustomAttributesValues',
fields=[
('id', models.AutoField(serialize=False, verbose_name='ID', auto_created=True, primary_key=True)),
('version', models.IntegerField(default=1, verbose_name='version')),
('values', django_pgjson.fields.JsonField(default={}, verbose_name='values')),
('user_story', models.OneToOneField(related_name='custom_attributes_values', to='userstories.UserStory', verbose_name='user story')),
],
options={
'ordering': ['id'],
'verbose_name_plural': 'user story custom attributes values',
'verbose_name': 'user story ustom attributes values',
'abstract': False,
},
bases=(models.Model,),
),
]

View File

@ -18,9 +18,13 @@ from django.db import models
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from django.utils import timezone from django.utils import timezone
from django_pgjson.fields import JsonField
from taiga.projects.occ.mixins import OCCModelMixin
###################################################### ######################################################
# Base Model Class # Custom Attribute Models
####################################################### #######################################################
class AbstractCustomAttribute(models.Model): class AbstractCustomAttribute(models.Model):
@ -51,11 +55,6 @@ class AbstractCustomAttribute(models.Model):
return super().save(*args, **kwargs) return super().save(*args, **kwargs)
######################################################
# Custom Field Models
#######################################################
class UserStoryCustomAttribute(AbstractCustomAttribute): class UserStoryCustomAttribute(AbstractCustomAttribute):
class Meta(AbstractCustomAttribute.Meta): class Meta(AbstractCustomAttribute.Meta):
verbose_name = "user story custom attribute" verbose_name = "user story custom attribute"
@ -72,3 +71,60 @@ class IssueCustomAttribute(AbstractCustomAttribute):
class Meta(AbstractCustomAttribute.Meta): class Meta(AbstractCustomAttribute.Meta):
verbose_name = "issue custom attribute" verbose_name = "issue custom attribute"
verbose_name_plural = "issue custom attributes" verbose_name_plural = "issue custom attributes"
######################################################
# Custom Attributes Values Models
#######################################################
class AbstractCustomAttributesValues(OCCModelMixin, models.Model):
values = JsonField(null=False, blank=False, default={}, verbose_name=_("values"))
class Meta:
abstract = True
ordering = ["id"]
class UserStoryCustomAttributesValues(AbstractCustomAttributesValues):
user_story = models.OneToOneField("userstories.UserStory",
null=False, blank=False, related_name="custom_attributes_values",
verbose_name=_("user story"))
class Meta(AbstractCustomAttributesValues.Meta):
verbose_name = "user story ustom attributes values"
verbose_name_plural = "user story custom attributes values"
@property
def project(self):
# NOTE: This property simplifies checking permissions
return self.user_story.project
class TaskCustomAttributesValues(AbstractCustomAttributesValues):
task = models.OneToOneField("tasks.Task",
null=False, blank=False, related_name="custom_attributes_values",
verbose_name=_("task"))
class Meta(AbstractCustomAttributesValues.Meta):
verbose_name = "task ustom attributes values"
verbose_name_plural = "task custom attributes values"
@property
def project(self):
# NOTE: This property simplifies checking permissions
return self.task.project
class IssueCustomAttributesValues(AbstractCustomAttributesValues):
issue = models.OneToOneField("issues.Issue",
null=False, blank=False, related_name="custom_attributes_values",
verbose_name=_("issue"))
class Meta(AbstractCustomAttributesValues.Meta):
verbose_name = "issue ustom attributes values"
verbose_name_plural = "issue custom attributes values"
@property
def project(self):
# NOTE: This property simplifies checking permissions
return self.issue.project

View File

@ -18,9 +18,16 @@ from taiga.base.api.permissions import TaigaResourcePermission
from taiga.base.api.permissions import HasProjectPerm from taiga.base.api.permissions import HasProjectPerm
from taiga.base.api.permissions import IsProjectOwner from taiga.base.api.permissions import IsProjectOwner
from taiga.base.api.permissions import AllowAny from taiga.base.api.permissions import AllowAny
from taiga.base.api.permissions import IsSuperUser
######################################################
# Custom Attribute Permissions
#######################################################
class UserStoryCustomAttributePermission(TaigaResourcePermission): class UserStoryCustomAttributePermission(TaigaResourcePermission):
enought_perms = IsProjectOwner() | IsSuperUser()
global_perms = None
retrieve_perms = HasProjectPerm('view_project') retrieve_perms = HasProjectPerm('view_project')
create_perms = IsProjectOwner() create_perms = IsProjectOwner()
update_perms = IsProjectOwner() update_perms = IsProjectOwner()
@ -30,6 +37,8 @@ class UserStoryCustomAttributePermission(TaigaResourcePermission):
class TaskCustomAttributePermission(TaigaResourcePermission): class TaskCustomAttributePermission(TaigaResourcePermission):
enought_perms = IsProjectOwner() | IsSuperUser()
global_perms = None
retrieve_perms = HasProjectPerm('view_project') retrieve_perms = HasProjectPerm('view_project')
create_perms = IsProjectOwner() create_perms = IsProjectOwner()
update_perms = IsProjectOwner() update_perms = IsProjectOwner()
@ -39,9 +48,42 @@ class TaskCustomAttributePermission(TaigaResourcePermission):
class IssueCustomAttributePermission(TaigaResourcePermission): class IssueCustomAttributePermission(TaigaResourcePermission):
enought_perms = IsProjectOwner() | IsSuperUser()
global_perms = None
retrieve_perms = HasProjectPerm('view_project') retrieve_perms = HasProjectPerm('view_project')
create_perms = IsProjectOwner() create_perms = IsProjectOwner()
update_perms = IsProjectOwner() update_perms = IsProjectOwner()
destroy_perms = IsProjectOwner() destroy_perms = IsProjectOwner()
list_perms = AllowAny() list_perms = AllowAny()
bulk_update_order_perms = IsProjectOwner() bulk_update_order_perms = IsProjectOwner()
######################################################
# Custom Attributes Values Permissions
#######################################################
class UserStoryCustomAttributesValuesPermission(TaigaResourcePermission):
enought_perms = IsProjectOwner() | IsSuperUser()
global_perms = None
retrieve_perms = HasProjectPerm('view_us')
create_perms = HasProjectPerm('add_us')
update_perms = HasProjectPerm('modify_us')
destroy_perms = HasProjectPerm('delete_us')
class TaskCustomAttributesValuesPermission(TaigaResourcePermission):
enought_perms = IsProjectOwner() | IsSuperUser()
global_perms = None
retrieve_perms = HasProjectPerm('view_tasks')
create_perms = HasProjectPerm('add_task')
update_perms = HasProjectPerm('modify_task')
destroy_perms = HasProjectPerm('delete_task')
class IssueCustomAttributesValuesPermission(TaigaResourcePermission):
enought_perms = IsProjectOwner() | IsSuperUser()
global_perms = None
retrieve_perms = HasProjectPerm('view_issues')
create_perms = HasProjectPerm('add_issue')
update_perms = HasProjectPerm('modify_issue')
destroy_perms = HasProjectPerm('delete_issue')

View File

@ -14,20 +14,26 @@
# You should have received a copy of the GNU Affero General Public License # You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
from django.apps import apps
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from rest_framework.serializers import ValidationError from rest_framework.serializers import ValidationError
from taiga.base.serializers import ModelSerializer from taiga.base.serializers import ModelSerializer
from taiga.base.serializers import JsonField
from . import models from . import models
###################################################### ######################################################
# Base Serializer Class # Custom Attribute Serializer
####################################################### #######################################################
class BaseCustomAttributeSerializer(ModelSerializer): class BaseCustomAttributeSerializer(ModelSerializer):
class Meta:
read_only_fields = ('id', 'created_date', 'modified_date')
def validate(self, data): def validate(self, data):
""" """
Check the name is not duplicated in the project. Check when: Check the name is not duplicated in the project. Check when:
@ -49,20 +55,78 @@ class BaseCustomAttributeSerializer(ModelSerializer):
return data return data
######################################################
# Custom Field Serializers
#######################################################
class UserStoryCustomAttributeSerializer(BaseCustomAttributeSerializer): class UserStoryCustomAttributeSerializer(BaseCustomAttributeSerializer):
class Meta: class Meta(BaseCustomAttributeSerializer.Meta):
model = models.UserStoryCustomAttribute model = models.UserStoryCustomAttribute
class TaskCustomAttributeSerializer(BaseCustomAttributeSerializer): class TaskCustomAttributeSerializer(BaseCustomAttributeSerializer):
class Meta: class Meta(BaseCustomAttributeSerializer.Meta):
model = models.TaskCustomAttribute model = models.TaskCustomAttribute
class IssueCustomAttributeSerializer(BaseCustomAttributeSerializer): class IssueCustomAttributeSerializer(BaseCustomAttributeSerializer):
class Meta: class Meta(BaseCustomAttributeSerializer.Meta):
model = models.IssueCustomAttribute model = models.IssueCustomAttribute
######################################################
# Custom Attribute Serializer
#######################################################
class BaseCustomAttributesValuesSerializer:
values = JsonField(source="values", label="values", required=True)
_custom_attribute_model = None
_container_field = None
def validate_values(self, attrs, source):
# values must be a dict
data_values = attrs.get("values", None)
if self.object:
data_values = (data_values or self.object.values)
if type(data_values) is not dict:
raise ValidationError(_("Invalid content. It must be {\"key\": \"value\",...}"))
# Values keys must be in the container object project
data_container = attrs.get(self._container_field, None)
if data_container:
project_id = data_container.project_id
elif self.object:
project_id = getattr(self.object, self._container_field).project_id
else:
project_id = None
values_ids = list(data_values.keys())
qs = self._custom_attribute_model.objects.filter(project=project_id,
id__in=values_ids)
if qs.count() != len(values_ids):
raise ValidationError(_("It contain invalid custom fields."))
return attrs
class UserStoryCustomAttributesValuesSerializer(BaseCustomAttributesValuesSerializer, ModelSerializer):
_custom_attribute_model = models.UserStoryCustomAttribute
_container_model = "userstories.UserStory"
_container_field = "user_story"
class Meta:
model = models.UserStoryCustomAttributesValues
class TaskCustomAttributesValuesSerializer(BaseCustomAttributesValuesSerializer, ModelSerializer):
_custom_attribute_model = models.TaskCustomAttribute
_container_field = "task"
class Meta:
model = models.TaskCustomAttributesValues
class IssueCustomAttributesValuesSerializer(BaseCustomAttributesValuesSerializer, ModelSerializer):
_custom_attribute_model = models.IssueCustomAttribute
_container_field = "issue"
class Meta:
model = models.IssueCustomAttributesValues

View File

@ -14,9 +14,13 @@
# You should have received a copy of the GNU Affero General Public License # You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
from contextlib import suppress
from functools import partial from functools import partial
from django.apps import apps from django.apps import apps
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
from django.core.exceptions import ObjectDoesNotExist
from taiga.base.utils.iterators import as_tuple from taiga.base.utils.iterators import as_tuple
from taiga.base.utils.iterators import as_dict from taiga.base.utils.iterators import as_dict
from taiga.mdrender.service import render as mdrender from taiga.mdrender.service import render as mdrender
@ -181,6 +185,42 @@ def extract_attachments(obj) -> list:
"order": attach.order} "order": attach.order}
@as_tuple
def extract_user_story_custom_attributes(obj) -> list:
with suppress(ObjectDoesNotExist):
custom_attributes_values = obj.custom_attributes_values.values
for attr in obj.project.userstorycustomattributes.all():
with suppress(KeyError):
value = custom_attributes_values[str(attr.id)]
yield {"id": attr.id,
"name": attr.name,
"values": value}
@as_tuple
def extract_task_custom_attributes(obj) -> list:
with suppress(ObjectDoesNotExist):
custom_attributes_values = obj.custom_attributes_values.values
for attr in obj.project.taskcustomattributes.all():
with suppress(KeyError):
value = custom_attributes_values[str(attr.id)]
yield {"id": attr.id,
"name": attr.name,
"values": value}
@as_tuple
def extract_issue_custom_attributes(obj) -> list:
with suppress(ObjectDoesNotExist):
custom_attributes_values = obj.custom_attributes_values.values
for attr in obj.project.issuecustomattributes.all():
with suppress(KeyError):
value = custom_attributes_values[str(attr.id)]
yield {"id": attr.id,
"name": attr.name,
"values": value}
def project_freezer(project) -> dict: def project_freezer(project) -> dict:
fields = ("name", fields = ("name",
"slug", "slug",
@ -243,6 +283,7 @@ def userstory_freezer(us) -> dict:
"is_blocked": us.is_blocked, "is_blocked": us.is_blocked,
"blocked_note": us.blocked_note, "blocked_note": us.blocked_note,
"blocked_note_html": mdrender(us.project, us.blocked_note), "blocked_note_html": mdrender(us.project, us.blocked_note),
"custom_attributes": extract_user_story_custom_attributes(us),
} }
return snapshot return snapshot
@ -267,6 +308,7 @@ def issue_freezer(issue) -> dict:
"is_blocked": issue.is_blocked, "is_blocked": issue.is_blocked,
"blocked_note": issue.blocked_note, "blocked_note": issue.blocked_note,
"blocked_note_html": mdrender(issue.project, issue.blocked_note), "blocked_note_html": mdrender(issue.project, issue.blocked_note),
"custom_attributes": extract_issue_custom_attributes(issue),
} }
return snapshot return snapshot
@ -292,6 +334,7 @@ def task_freezer(task) -> dict:
"is_blocked": task.is_blocked, "is_blocked": task.is_blocked,
"blocked_note": task.blocked_note, "blocked_note": task.blocked_note,
"blocked_note_html": mdrender(task.project, task.blocked_note), "blocked_note_html": mdrender(task.project, task.blocked_note),
"custom_attributes": extract_task_custom_attributes(task),
} }
return snapshot return snapshot

View File

@ -197,6 +197,35 @@ class HistoryEntry(models.Model):
if attachments["new"] or attachments["changed"] or attachments["deleted"]: if attachments["new"] or attachments["changed"] or attachments["deleted"]:
value = attachments value = attachments
elif key == "custom_attributes":
custom_attributes = {
"new": [],
"changed": [],
"deleted": [],
}
oldcustattrs = {x["id"]:x for x in self.diff["custom_attributes"][0]}
newcustattrs = {x["id"]:x for x in self.diff["custom_attributes"][1]}
for aid in set(tuple(oldcustattrs.keys()) + tuple(newcustattrs.keys())):
if aid in oldcustattrs and aid in newcustattrs:
changes = make_diff_from_dicts(oldcustattrs[aid], newcustattrs[aid],
excluded_keys=("name"))
if changes:
change = {
"name": newcustattrs.get(aid, {}).get("name", ""),
"changes": changes
}
custom_attributes["changed"].append(change)
elif aid in oldcustattrs and aid not in newcustattrs:
custom_attributes["deleted"].append(oldcustattrs[aid])
elif aid not in oldcustattrs and aid in newcustattrs:
custom_attributes["new"].append(newcustattrs[aid])
if custom_attributes["new"] or custom_attributes["changed"] or custom_attributes["deleted"]:
value = custom_attributes
elif key in self.values: elif key in self.values:
value = [resolve_value(key, x) for x in self.diff[key]] value = [resolve_value(key, x) for x in self.diff[key]]
else: else:

View File

@ -6,7 +6,8 @@
"backlog_order", "backlog_order",
"kanban_order", "kanban_order",
"taskboard_order", "taskboard_order",
"us_order" "us_order",
"custom_attributes"
] %} ] %}
{% for field_name, values in changed_fields.items() %} {% for field_name, values in changed_fields.items() %}
@ -80,9 +81,7 @@
<tr> <tr>
<td colspan="2"> <td colspan="2">
<h3>{{ _("Deleted attachment") }}</h3> <h3>{{ _("Deleted attachment") }}</h3>
{% if att.changes.description %}
<p>{{ att.filename|linebreaksbr }}</p> <p>{{ att.filename|linebreaksbr }}</p>
{% endif %}
</td> </td>
</tr> </tr>
{% endfor %} {% endfor %}
@ -155,7 +154,6 @@
</tr> </tr>
{# * #} {# * #}
{% else %} {% else %}
<tr> <tr>
<td valign="middle" rowspan="2" class="update-row-name"> <td valign="middle" rowspan="2" class="update-row-name">
<h3>{{ verbose_name(obj_class, field_name) }}</h3> <h3>{{ verbose_name(obj_class, field_name) }}</h3>
@ -172,5 +170,52 @@
</td> </td>
</tr> </tr>
{% endif %} {% endif %}
{% elif field_name == "custom_attributes" %}
{# CUSTOM ATTRIBUTES #}
{% if values.new %}
{% for attr in values['new']%}
<tr>
<td valign="middle" rowspan="2" class="update-row-name">
<h3>{{ attr.name }}</h3>
</td>
</tr>
<tr>
<td valign="top">
<span>{{ _("to") }}</span><br>
<strong>{{ attr.value|linebreaksbr }}</strong>
</td>
</tr>
{% endfor %}
{% endif %}
{% if values.changed %}
{% for attr in values['changed'] %}
<tr>
<td valign="middle" rowspan="2" class="update-row-name">
<h3>{{ attr.name }}</h3>
</td>
<td valign="top" class="update-row-from">
<span>{{ _("from") }}</span><br>
<strong>{{ attr.changes.value.0|linebreaksbr }}</strong>
</td>
</tr>
<tr>
<td valign="top">
<span>{{ _("to") }}</span><br>
<strong>{{ attr.changes.value.1|linebreaksbr }}</strong>
</td>
</tr>
{% endfor %}
{% endif %}
{% if values.deleted %}
{% for attr in values['deleted']%}
<tr>
<td colspan="2">
<h3>{{ attr.name }}</h3>
<p>{{ _("-deleted-") }}</p>
</td>
</tr>
{% endfor %}
{% endif %}
{% endif %} {% endif %}
{% endfor %} {% endfor %}

View File

@ -8,7 +8,8 @@
"taskboard_order", "taskboard_order",
"us_order", "us_order",
"blocked_note_diff", "blocked_note_diff",
"blocked_note_html" "blocked_note_html",
"custom_attributes"
] %} ] %}
{% for field_name, values in changed_fields.items() %} {% for field_name, values in changed_fields.items() %}
{% if field_name not in excluded_fields %} {% if field_name not in excluded_fields %}
@ -18,6 +19,7 @@
{% for role, points in values.items() %} {% for role, points in values.items() %}
* {{ role }} {{ _("to:") }} {{ points.1 }} {{ _("from:") }} {{ points.0 }} * {{ role }} {{ _("to:") }} {{ points.1 }} {{ _("from:") }} {{ points.0 }}
{% endfor %} {% endfor %}
{# ATTACHMENTS #} {# ATTACHMENTS #}
{% elif field_name == "attachments" %} {% elif field_name == "attachments" %}
{% if values.new %} {% if values.new %}
@ -40,6 +42,7 @@
- {{ att.filename }} - {{ att.filename }}
{% endfor %} {% endfor %}
{% endif %} {% endif %}
{# TAGS AND WATCHERS #} {# TAGS AND WATCHERS #}
{% elif field_name in ["tags", "watchers"] %} {% elif field_name in ["tags", "watchers"] %}
{% set values_from = values.0 or [] %} {% set values_from = values.0 or [] %}
@ -53,6 +56,36 @@
{% if values_removed %} {% if values_removed %}
* {{ _("removed:") }} {{ ', '.join(values_removed) }} * {{ _("removed:") }} {{ ', '.join(values_removed) }}
{% endif %} {% endif %}
{# * #}
{% else %}
* {{ _("From:") }} {{ values.0 }}
* {{ _("To:") }} {{ values.1 }}
{% endif %}
{% elif field_name == "custom_attributes" %}
{# CUSTOM ATTRIBUTES #}
{% elif field_name == "attachments" %}
{% if values.new %}
{% for attr in values['new']%}
- {{ attr.name }}:
* {{ attr.value }}
{% endfor %}
{% endif %}
{% if values.changed %}
{% for attr in values['changed'] %}
- {{ attr.name }}:
* {{ _("From:") }} {{ attr.changes.value.0 }}
* {{ _("To:") }} {{ attr.changes.value.1 }}
{% endfor %}
{% endif %}
{% if values.deleted %}
{% for attr in values['deleted']%}
- {{ attr.name }}: {{ _("-deleted-") }}
* {{ attr.value }}
{% endfor %}
{% endif %} {% endif %}
{% endif %} {% endif %}
{% endfor %} {% endfor %}

View File

@ -22,10 +22,10 @@ from django.utils import timezone
from djorm_pgarray.fields import TextArrayField from djorm_pgarray.fields import TextArrayField
from taiga.base.tags import TaggedMixin
from taiga.projects.occ import OCCModelMixin from taiga.projects.occ import OCCModelMixin
from taiga.projects.notifications.mixins import WatchedModelMixin from taiga.projects.notifications.mixins import WatchedModelMixin
from taiga.projects.mixins.blocked import BlockedMixin from taiga.projects.mixins.blocked import BlockedMixin
from taiga.base.tags import TaggedMixin
class RolePoints(models.Model): class RolePoints(models.Model):

View File

@ -14,15 +14,19 @@
# You should have received a copy of the GNU Affero General Public License # You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
import json
from django.apps import apps from django.apps import apps
from rest_framework import serializers from rest_framework import serializers
from taiga.base.serializers import (Serializer, TagsField, NeighborsSerializerMixin, from taiga.base.serializers import Serializer
PgArrayField, ModelSerializer) from taiga.base.serializers import TagsField
from taiga.base.serializers import NeighborsSerializerMixin
from taiga.base.serializers import PgArrayField
from taiga.base.serializers import ModelSerializer
from taiga.base.utils import json
from taiga.mdrender.service import render as mdrender from taiga.mdrender.service import render as mdrender
from taiga.projects.validators import ProjectExistsValidator, UserStoryStatusExistsValidator from taiga.projects.validators import ProjectExistsValidator
from taiga.projects.validators import UserStoryStatusExistsValidator
from taiga.projects.userstories.validators import UserStoryExistsValidator from taiga.projects.userstories.validators import UserStoryExistsValidator
from taiga.projects.notifications.validators import WatchersValidator from taiga.projects.notifications.validators import WatchersValidator
@ -92,7 +96,6 @@ class UserStorySerializer(WatchersValidator, ModelSerializer):
class UserStoryNeighborsSerializer(NeighborsSerializerMixin, UserStorySerializer): class UserStoryNeighborsSerializer(NeighborsSerializerMixin, UserStorySerializer):
def serialize_neighbor(self, neighbor): def serialize_neighbor(self, neighbor):
return NeighborUserStorySerializer(neighbor).data return NeighborUserStorySerializer(neighbor).data
@ -104,8 +107,7 @@ class NeighborUserStorySerializer(ModelSerializer):
depth = 0 depth = 0
class UserStoriesBulkSerializer(ProjectExistsValidator, UserStoryStatusExistsValidator, class UserStoriesBulkSerializer(ProjectExistsValidator, UserStoryStatusExistsValidator, Serializer):
Serializer):
project_id = serializers.IntegerField() project_id = serializers.IntegerField()
status_id = serializers.IntegerField(required=False) status_id = serializers.IntegerField(required=False)
bulk_stories = serializers.CharField() bulk_stories = serializers.CharField()
@ -118,8 +120,6 @@ class _UserStoryOrderBulkSerializer(UserStoryExistsValidator, Serializer):
order = serializers.IntegerField() order = serializers.IntegerField()
class UpdateUserStoriesOrderBulkSerializer(ProjectExistsValidator, class UpdateUserStoriesOrderBulkSerializer(ProjectExistsValidator, UserStoryStatusExistsValidator, Serializer):
UserStoryStatusExistsValidator,
Serializer):
project_id = serializers.IntegerField() project_id = serializers.IntegerField()
bulk_stories = _UserStoryOrderBulkSerializer(many=True) bulk_stories = _UserStoryOrderBulkSerializer(many=True)

View File

@ -71,6 +71,9 @@ router.register(r"severities",SeverityViewSet , base_name="severities")
from taiga.projects.custom_attributes.api import UserStoryCustomAttributeViewSet from taiga.projects.custom_attributes.api import UserStoryCustomAttributeViewSet
from taiga.projects.custom_attributes.api import TaskCustomAttributeViewSet from taiga.projects.custom_attributes.api import TaskCustomAttributeViewSet
from taiga.projects.custom_attributes.api import IssueCustomAttributeViewSet from taiga.projects.custom_attributes.api import IssueCustomAttributeViewSet
from taiga.projects.custom_attributes.api import UserStoryCustomAttributesValuesViewSet
from taiga.projects.custom_attributes.api import TaskCustomAttributesValuesViewSet
from taiga.projects.custom_attributes.api import IssueCustomAttributesValuesViewSet
router.register(r"userstory-custom-attributes", UserStoryCustomAttributeViewSet, router.register(r"userstory-custom-attributes", UserStoryCustomAttributeViewSet,
base_name="userstory-custom-attributes") base_name="userstory-custom-attributes")
@ -79,6 +82,13 @@ router.register(r"task-custom-attributes", TaskCustomAttributeViewSet,
router.register(r"issue-custom-attributes", IssueCustomAttributeViewSet, router.register(r"issue-custom-attributes", IssueCustomAttributeViewSet,
base_name="issue-custom-attributes") base_name="issue-custom-attributes")
router.register(r"userstories/custom-attributes-values", UserStoryCustomAttributesValuesViewSet,
base_name="userstory-custom-attributes-values")
router.register(r"tasks/custom-attributes-values", TaskCustomAttributesValuesViewSet,
base_name="task-custom-attributes-values")
router.register(r"issues/custom-attributes-values", IssueCustomAttributesValuesViewSet,
base_name="issue-custom-attributes-values")
# Search # Search
from taiga.searches.api import SearchViewSet from taiga.searches.api import SearchViewSet

View File

@ -384,6 +384,33 @@ class IssueCustomAttributeFactory(Factory):
project = factory.SubFactory("tests.factories.ProjectFactory") project = factory.SubFactory("tests.factories.ProjectFactory")
class UserStoryCustomAttributesValuesFactory(Factory):
class Meta:
model = "custom_attributes.UserStoryCustomAttributesValues"
strategy = factory.CREATE_STRATEGY
values = {}
user_story = factory.SubFactory("tests.factories.UserStoryFactory")
class TaskCustomAttributesValuesFactory(Factory):
class Meta:
model = "custom_attributes.TaskCustomAttributesValues"
strategy = factory.CREATE_STRATEGY
values = {}
task = factory.SubFactory("tests.factories.TaskFactory")
class IssueCustomAttributesValuesFactory(Factory):
class Meta:
model = "custom_attributes.IssueCustomAttributesValues"
strategy = factory.CREATE_STRATEGY
values = {}
issue = factory.SubFactory("tests.factories.IssueFactory")
# class FanFactory(Factory): # class FanFactory(Factory):
# project = factory.SubFactory("tests.factories.ProjectFactory") # project = factory.SubFactory("tests.factories.ProjectFactory")
# user = factory.SubFactory("tests.factories.UserFactory") # user = factory.SubFactory("tests.factories.UserFactory")

View File

@ -1,586 +0,0 @@
# Copyright (C) 2015 Andrey Antukh <niwi@niwi.be>
# Copyright (C) 2015 Jesús Espino <jespinog@gmail.com>
# Copyright (C) 2015 David Barragán <bameda@dbarragan.com>
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from django.core.urlresolvers import reverse
from taiga.base.utils import json
from taiga.projects.custom_attributes import serializers
from taiga.permissions.permissions import MEMBERS_PERMISSIONS
from tests import factories as f
from tests.utils import helper_test_http_method
import pytest
pytestmark = pytest.mark.django_db
@pytest.fixture
def data():
m = type("Models", (object,), {})
m.registered_user = f.UserFactory.create()
m.project_member_with_perms = f.UserFactory.create()
m.project_member_without_perms = f.UserFactory.create()
m.project_owner = f.UserFactory.create()
m.other_user = f.UserFactory.create()
m.superuser = f.UserFactory.create(is_superuser=True)
m.public_project = f.ProjectFactory(is_private=False,
anon_permissions=['view_project'],
public_permissions=['view_project'],
owner=m.project_owner)
m.private_project1 = f.ProjectFactory(is_private=True,
anon_permissions=['view_project'],
public_permissions=['view_project'],
owner=m.project_owner)
m.private_project2 = f.ProjectFactory(is_private=True,
anon_permissions=[],
public_permissions=[],
owner=m.project_owner)
m.public_membership = f.MembershipFactory(project=m.public_project,
user=m.project_member_with_perms,
email=m.project_member_with_perms.email,
role__project=m.public_project,
role__permissions=list(map(lambda x: x[0], MEMBERS_PERMISSIONS)))
m.private_membership1 = f.MembershipFactory(project=m.private_project1,
user=m.project_member_with_perms,
email=m.project_member_with_perms.email,
role__project=m.private_project1,
role__permissions=list(map(lambda x: x[0], MEMBERS_PERMISSIONS)))
f.MembershipFactory(project=m.private_project1,
user=m.project_member_without_perms,
email=m.project_member_without_perms.email,
role__project=m.private_project1,
role__permissions=[])
m.private_membership2 = f.MembershipFactory(project=m.private_project2,
user=m.project_member_with_perms,
email=m.project_member_with_perms.email,
role__project=m.private_project2,
role__permissions=list(map(lambda x: x[0], MEMBERS_PERMISSIONS)))
f.MembershipFactory(project=m.private_project2,
user=m.project_member_without_perms,
email=m.project_member_without_perms.email,
role__project=m.private_project2,
role__permissions=[])
f.MembershipFactory(project=m.public_project,
user=m.project_owner,
is_owner=True)
f.MembershipFactory(project=m.private_project1,
user=m.project_owner,
is_owner=True)
f.MembershipFactory(project=m.private_project2,
user=m.project_owner,
is_owner=True)
m.public_userstory_ca = f.UserStoryCustomAttributeFactory(project=m.public_project)
m.private_userstory_ca1 = f.UserStoryCustomAttributeFactory(project=m.private_project1)
m.private_userstory_ca2 = f.UserStoryCustomAttributeFactory(project=m.private_project2)
m.public_task_ca = f.TaskCustomAttributeFactory(project=m.public_project)
m.private_task_ca1 = f.TaskCustomAttributeFactory(project=m.private_project1)
m.private_task_ca2 = f.TaskCustomAttributeFactory(project=m.private_project2)
m.public_issue_ca = f.IssueCustomAttributeFactory(project=m.public_project)
m.private_issue_ca1 = f.IssueCustomAttributeFactory(project=m.private_project1)
m.private_issue_ca2 = f.IssueCustomAttributeFactory(project=m.private_project2)
return m
#########################################################
# User Story Custom Fields
#########################################################
def test_userstory_status_retrieve(client, data):
public_url = reverse('userstory-custom-attributes-detail', kwargs={"pk": data.public_userstory_ca.pk})
private1_url = reverse('userstory-custom-attributes-detail', kwargs={"pk": data.private_userstory_ca1.pk})
private2_url = reverse('userstory-custom-attributes-detail', kwargs={"pk": data.private_userstory_ca2.pk})
users = [
None,
data.registered_user,
data.project_member_without_perms,
data.project_member_with_perms,
data.project_owner
]
results = helper_test_http_method(client, 'get', public_url, None, users)
assert results == [200, 200, 200, 200, 200]
results = helper_test_http_method(client, 'get', private1_url, None, users)
assert results == [200, 200, 200, 200, 200]
results = helper_test_http_method(client, 'get', private2_url, None, users)
assert results == [401, 403, 403, 200, 200]
def test_userstory_status_update(client, data):
public_url = reverse('userstory-custom-attributes-detail', kwargs={"pk": data.public_userstory_ca.pk})
private1_url = reverse('userstory-custom-attributes-detail', kwargs={"pk": data.private_userstory_ca1.pk})
private2_url = reverse('userstory-custom-attributes-detail', kwargs={"pk": data.private_userstory_ca2.pk})
users = [
None,
data.registered_user,
data.project_member_without_perms,
data.project_member_with_perms,
data.project_owner
]
userstory_ca_data = serializers.UserStoryCustomAttributeSerializer(data.public_userstory_ca).data
userstory_ca_data["name"] = "test"
userstory_ca_data = json.dumps(userstory_ca_data)
results = helper_test_http_method(client, 'put', public_url, userstory_ca_data, users)
assert results == [401, 403, 403, 403, 200]
userstory_ca_data = serializers.UserStoryCustomAttributeSerializer(data.private_userstory_ca1).data
userstory_ca_data["name"] = "test"
userstory_ca_data = json.dumps(userstory_ca_data)
results = helper_test_http_method(client, 'put', private1_url, userstory_ca_data, users)
assert results == [401, 403, 403, 403, 200]
userstory_ca_data = serializers.UserStoryCustomAttributeSerializer(data.private_userstory_ca2).data
userstory_ca_data["name"] = "test"
userstory_ca_data = json.dumps(userstory_ca_data)
results = helper_test_http_method(client, 'put', private2_url, userstory_ca_data, users)
assert results == [401, 403, 403, 403, 200]
def test_userstory_status_delete(client, data):
public_url = reverse('userstory-custom-attributes-detail', kwargs={"pk": data.public_userstory_ca.pk})
private1_url = reverse('userstory-custom-attributes-detail', kwargs={"pk": data.private_userstory_ca1.pk})
private2_url = reverse('userstory-custom-attributes-detail', kwargs={"pk": data.private_userstory_ca2.pk})
users = [
None,
data.registered_user,
data.project_member_without_perms,
data.project_member_with_perms,
data.project_owner
]
results = helper_test_http_method(client, 'delete', public_url, None, users)
assert results == [401, 403, 403, 403, 204]
results = helper_test_http_method(client, 'delete', private1_url, None, users)
assert results == [401, 403, 403, 403, 204]
results = helper_test_http_method(client, 'delete', private2_url, None, users)
assert results == [401, 403, 403, 403, 204]
def test_userstory_status_list(client, data):
url = reverse('userstory-custom-attributes-list')
response = client.json.get(url)
assert len(response.data) == 2
assert response.status_code == 200
client.login(data.registered_user)
response = client.json.get(url)
assert len(response.data) == 2
assert response.status_code == 200
client.login(data.project_member_without_perms)
response = client.json.get(url)
assert len(response.data) == 2
assert response.status_code == 200
client.login(data.project_member_with_perms)
response = client.json.get(url)
assert len(response.data) == 3
assert response.status_code == 200
client.login(data.project_owner)
response = client.json.get(url)
assert len(response.data) == 3
assert response.status_code == 200
def test_userstory_status_patch(client, data):
public_url = reverse('userstory-custom-attributes-detail', kwargs={"pk": data.public_userstory_ca.pk})
private1_url = reverse('userstory-custom-attributes-detail', kwargs={"pk": data.private_userstory_ca1.pk})
private2_url = reverse('userstory-custom-attributes-detail', kwargs={"pk": data.private_userstory_ca2.pk})
users = [
None,
data.registered_user,
data.project_member_without_perms,
data.project_member_with_perms,
data.project_owner
]
results = helper_test_http_method(client, 'patch', public_url, '{"name": "Test"}', users)
assert results == [401, 403, 403, 403, 200]
results = helper_test_http_method(client, 'patch', private1_url, '{"name": "Test"}', users)
assert results == [401, 403, 403, 403, 200]
results = helper_test_http_method(client, 'patch', private2_url, '{"name": "Test"}', users)
assert results == [401, 403, 403, 403, 200]
def test_userstory_status_action_bulk_update_order(client, data):
url = reverse('userstory-custom-attributes-bulk-update-order')
users = [
None,
data.registered_user,
data.project_member_without_perms,
data.project_member_with_perms,
data.project_owner
]
post_data = json.dumps({
"bulk_userstory_custom_attributes": [(1,2)],
"project": data.public_project.pk
})
results = helper_test_http_method(client, 'post', url, post_data, users)
assert results == [401, 403, 403, 403, 204]
post_data = json.dumps({
"bulk_userstory_custom_attributes": [(1,2)],
"project": data.private_project1.pk
})
results = helper_test_http_method(client, 'post', url, post_data, users)
assert results == [401, 403, 403, 403, 204]
post_data = json.dumps({
"bulk_userstory_custom_attributes": [(1,2)],
"project": data.private_project2.pk
})
results = helper_test_http_method(client, 'post', url, post_data, users)
assert results == [401, 403, 403, 403, 204]
#########################################################
# Task Custom Fields
#########################################################
def test_task_status_retrieve(client, data):
public_url = reverse('task-custom-attributes-detail', kwargs={"pk": data.public_task_ca.pk})
private1_url = reverse('task-custom-attributes-detail', kwargs={"pk": data.private_task_ca1.pk})
private2_url = reverse('task-custom-attributes-detail', kwargs={"pk": data.private_task_ca2.pk})
users = [
None,
data.registered_user,
data.project_member_without_perms,
data.project_member_with_perms,
data.project_owner
]
results = helper_test_http_method(client, 'get', public_url, None, users)
assert results == [200, 200, 200, 200, 200]
results = helper_test_http_method(client, 'get', private1_url, None, users)
assert results == [200, 200, 200, 200, 200]
results = helper_test_http_method(client, 'get', private2_url, None, users)
assert results == [401, 403, 403, 200, 200]
def test_task_status_update(client, data):
public_url = reverse('task-custom-attributes-detail', kwargs={"pk": data.public_task_ca.pk})
private1_url = reverse('task-custom-attributes-detail', kwargs={"pk": data.private_task_ca1.pk})
private2_url = reverse('task-custom-attributes-detail', kwargs={"pk": data.private_task_ca2.pk})
users = [
None,
data.registered_user,
data.project_member_without_perms,
data.project_member_with_perms,
data.project_owner
]
task_ca_data = serializers.TaskCustomAttributeSerializer(data.public_task_ca).data
task_ca_data["name"] = "test"
task_ca_data = json.dumps(task_ca_data)
results = helper_test_http_method(client, 'put', public_url, task_ca_data, users)
assert results == [401, 403, 403, 403, 200]
task_ca_data = serializers.TaskCustomAttributeSerializer(data.private_task_ca1).data
task_ca_data["name"] = "test"
task_ca_data = json.dumps(task_ca_data)
results = helper_test_http_method(client, 'put', private1_url, task_ca_data, users)
assert results == [401, 403, 403, 403, 200]
task_ca_data = serializers.TaskCustomAttributeSerializer(data.private_task_ca2).data
task_ca_data["name"] = "test"
task_ca_data = json.dumps(task_ca_data)
results = helper_test_http_method(client, 'put', private2_url, task_ca_data, users)
assert results == [401, 403, 403, 403, 200]
def test_task_status_delete(client, data):
public_url = reverse('task-custom-attributes-detail', kwargs={"pk": data.public_task_ca.pk})
private1_url = reverse('task-custom-attributes-detail', kwargs={"pk": data.private_task_ca1.pk})
private2_url = reverse('task-custom-attributes-detail', kwargs={"pk": data.private_task_ca2.pk})
users = [
None,
data.registered_user,
data.project_member_without_perms,
data.project_member_with_perms,
data.project_owner
]
results = helper_test_http_method(client, 'delete', public_url, None, users)
assert results == [401, 403, 403, 403, 204]
results = helper_test_http_method(client, 'delete', private1_url, None, users)
assert results == [401, 403, 403, 403, 204]
results = helper_test_http_method(client, 'delete', private2_url, None, users)
assert results == [401, 403, 403, 403, 204]
def test_task_status_list(client, data):
url = reverse('task-custom-attributes-list')
response = client.json.get(url)
assert len(response.data) == 2
assert response.status_code == 200
client.login(data.registered_user)
response = client.json.get(url)
assert len(response.data) == 2
assert response.status_code == 200
client.login(data.project_member_without_perms)
response = client.json.get(url)
assert len(response.data) == 2
assert response.status_code == 200
client.login(data.project_member_with_perms)
response = client.json.get(url)
assert len(response.data) == 3
assert response.status_code == 200
client.login(data.project_owner)
response = client.json.get(url)
assert len(response.data) == 3
assert response.status_code == 200
def test_task_status_patch(client, data):
public_url = reverse('task-custom-attributes-detail', kwargs={"pk": data.public_task_ca.pk})
private1_url = reverse('task-custom-attributes-detail', kwargs={"pk": data.private_task_ca1.pk})
private2_url = reverse('task-custom-attributes-detail', kwargs={"pk": data.private_task_ca2.pk})
users = [
None,
data.registered_user,
data.project_member_without_perms,
data.project_member_with_perms,
data.project_owner
]
results = helper_test_http_method(client, 'patch', public_url, '{"name": "Test"}', users)
assert results == [401, 403, 403, 403, 200]
results = helper_test_http_method(client, 'patch', private1_url, '{"name": "Test"}', users)
assert results == [401, 403, 403, 403, 200]
results = helper_test_http_method(client, 'patch', private2_url, '{"name": "Test"}', users)
assert results == [401, 403, 403, 403, 200]
def test_task_status_action_bulk_update_order(client, data):
url = reverse('task-custom-attributes-bulk-update-order')
users = [
None,
data.registered_user,
data.project_member_without_perms,
data.project_member_with_perms,
data.project_owner
]
post_data = json.dumps({
"bulk_task_custom_attributes": [(1,2)],
"project": data.public_project.pk
})
results = helper_test_http_method(client, 'post', url, post_data, users)
assert results == [401, 403, 403, 403, 204]
post_data = json.dumps({
"bulk_task_custom_attributes": [(1,2)],
"project": data.private_project1.pk
})
results = helper_test_http_method(client, 'post', url, post_data, users)
assert results == [401, 403, 403, 403, 204]
post_data = json.dumps({
"bulk_task_custom_attributes": [(1,2)],
"project": data.private_project2.pk
})
results = helper_test_http_method(client, 'post', url, post_data, users)
assert results == [401, 403, 403, 403, 204]
#########################################################
# Issue Custom Fields
#########################################################
def test_issue_status_retrieve(client, data):
public_url = reverse('issue-custom-attributes-detail', kwargs={"pk": data.public_issue_ca.pk})
private1_url = reverse('issue-custom-attributes-detail', kwargs={"pk": data.private_issue_ca1.pk})
private2_url = reverse('issue-custom-attributes-detail', kwargs={"pk": data.private_issue_ca2.pk})
users = [
None,
data.registered_user,
data.project_member_without_perms,
data.project_member_with_perms,
data.project_owner
]
results = helper_test_http_method(client, 'get', public_url, None, users)
assert results == [200, 200, 200, 200, 200]
results = helper_test_http_method(client, 'get', private1_url, None, users)
assert results == [200, 200, 200, 200, 200]
results = helper_test_http_method(client, 'get', private2_url, None, users)
assert results == [401, 403, 403, 200, 200]
def test_issue_status_update(client, data):
public_url = reverse('issue-custom-attributes-detail', kwargs={"pk": data.public_issue_ca.pk})
private1_url = reverse('issue-custom-attributes-detail', kwargs={"pk": data.private_issue_ca1.pk})
private2_url = reverse('issue-custom-attributes-detail', kwargs={"pk": data.private_issue_ca2.pk})
users = [
None,
data.registered_user,
data.project_member_without_perms,
data.project_member_with_perms,
data.project_owner
]
issue_ca_data = serializers.IssueCustomAttributeSerializer(data.public_issue_ca).data
issue_ca_data["name"] = "test"
issue_ca_data = json.dumps(issue_ca_data)
results = helper_test_http_method(client, 'put', public_url, issue_ca_data, users)
assert results == [401, 403, 403, 403, 200]
issue_ca_data = serializers.IssueCustomAttributeSerializer(data.private_issue_ca1).data
issue_ca_data["name"] = "test"
issue_ca_data = json.dumps(issue_ca_data)
results = helper_test_http_method(client, 'put', private1_url, issue_ca_data, users)
assert results == [401, 403, 403, 403, 200]
issue_ca_data = serializers.IssueCustomAttributeSerializer(data.private_issue_ca2).data
issue_ca_data["name"] = "test"
issue_ca_data = json.dumps(issue_ca_data)
results = helper_test_http_method(client, 'put', private2_url, issue_ca_data, users)
assert results == [401, 403, 403, 403, 200]
def test_issue_status_delete(client, data):
public_url = reverse('issue-custom-attributes-detail', kwargs={"pk": data.public_issue_ca.pk})
private1_url = reverse('issue-custom-attributes-detail', kwargs={"pk": data.private_issue_ca1.pk})
private2_url = reverse('issue-custom-attributes-detail', kwargs={"pk": data.private_issue_ca2.pk})
users = [
None,
data.registered_user,
data.project_member_without_perms,
data.project_member_with_perms,
data.project_owner
]
results = helper_test_http_method(client, 'delete', public_url, None, users)
assert results == [401, 403, 403, 403, 204]
results = helper_test_http_method(client, 'delete', private1_url, None, users)
assert results == [401, 403, 403, 403, 204]
results = helper_test_http_method(client, 'delete', private2_url, None, users)
assert results == [401, 403, 403, 403, 204]
def test_issue_status_list(client, data):
url = reverse('issue-custom-attributes-list')
response = client.json.get(url)
assert len(response.data) == 2
assert response.status_code == 200
client.login(data.registered_user)
response = client.json.get(url)
assert len(response.data) == 2
assert response.status_code == 200
client.login(data.project_member_without_perms)
response = client.json.get(url)
assert len(response.data) == 2
assert response.status_code == 200
client.login(data.project_member_with_perms)
response = client.json.get(url)
assert len(response.data) == 3
assert response.status_code == 200
client.login(data.project_owner)
response = client.json.get(url)
assert len(response.data) == 3
assert response.status_code == 200
def test_issue_status_patch(client, data):
public_url = reverse('issue-custom-attributes-detail', kwargs={"pk": data.public_issue_ca.pk})
private1_url = reverse('issue-custom-attributes-detail', kwargs={"pk": data.private_issue_ca1.pk})
private2_url = reverse('issue-custom-attributes-detail', kwargs={"pk": data.private_issue_ca2.pk})
users = [
None,
data.registered_user,
data.project_member_without_perms,
data.project_member_with_perms,
data.project_owner
]
results = helper_test_http_method(client, 'patch', public_url, '{"name": "Test"}', users)
assert results == [401, 403, 403, 403, 200]
results = helper_test_http_method(client, 'patch', private1_url, '{"name": "Test"}', users)
assert results == [401, 403, 403, 403, 200]
results = helper_test_http_method(client, 'patch', private2_url, '{"name": "Test"}', users)
assert results == [401, 403, 403, 403, 200]
def test_issue_status_action_bulk_update_order(client, data):
url = reverse('issue-custom-attributes-bulk-update-order')
users = [
None,
data.registered_user,
data.project_member_without_perms,
data.project_member_with_perms,
data.project_owner
]
post_data = json.dumps({
"bulk_issue_custom_attributes": [(1,2)],
"project": data.public_project.pk
})
results = helper_test_http_method(client, 'post', url, post_data, users)
assert results == [401, 403, 403, 403, 204]
post_data = json.dumps({
"bulk_issue_custom_attributes": [(1,2)],
"project": data.private_project1.pk
})
results = helper_test_http_method(client, 'post', url, post_data, users)
assert results == [401, 403, 403, 403, 204]
post_data = json.dumps({
"bulk_issue_custom_attributes": [(1,2)],
"project": data.private_project2.pk
})
results = helper_test_http_method(client, 'post', url, post_data, users)
assert results == [401, 403, 403, 403, 204]

View File

@ -0,0 +1,428 @@
# Copyright (C) 2015 Andrey Antukh <niwi@niwi.be>
# Copyright (C) 2015 Jesús Espino <jespinog@gmail.com>
# Copyright (C) 2015 David Barragán <bameda@dbarragan.com>
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from django.core.urlresolvers import reverse
from taiga.base.utils import json
from taiga.projects.custom_attributes import serializers
from taiga.permissions.permissions import MEMBERS_PERMISSIONS
from tests import factories as f
from tests.utils import helper_test_http_method
import pytest
pytestmark = pytest.mark.django_db(transaction=True)
@pytest.fixture
def data():
m = type("Models", (object,), {})
m.registered_user = f.UserFactory.create()
m.project_member_with_perms = f.UserFactory.create()
m.project_member_without_perms = f.UserFactory.create()
m.project_owner = f.UserFactory.create()
m.other_user = f.UserFactory.create()
m.superuser = f.UserFactory.create(is_superuser=True)
m.public_project = f.ProjectFactory(is_private=False,
anon_permissions=['view_project'],
public_permissions=['view_project'],
owner=m.project_owner)
m.private_project1 = f.ProjectFactory(is_private=True,
anon_permissions=['view_project'],
public_permissions=['view_project'],
owner=m.project_owner)
m.private_project2 = f.ProjectFactory(is_private=True,
anon_permissions=[],
public_permissions=[],
owner=m.project_owner)
m.public_membership = f.MembershipFactory(project=m.public_project,
user=m.project_member_with_perms,
email=m.project_member_with_perms.email,
role__project=m.public_project,
role__permissions=list(map(lambda x: x[0], MEMBERS_PERMISSIONS)))
m.private_membership1 = f.MembershipFactory(project=m.private_project1,
user=m.project_member_with_perms,
email=m.project_member_with_perms.email,
role__project=m.private_project1,
role__permissions=list(map(lambda x: x[0], MEMBERS_PERMISSIONS)))
f.MembershipFactory(project=m.private_project1,
user=m.project_member_without_perms,
email=m.project_member_without_perms.email,
role__project=m.private_project1,
role__permissions=[])
m.private_membership2 = f.MembershipFactory(project=m.private_project2,
user=m.project_member_with_perms,
email=m.project_member_with_perms.email,
role__project=m.private_project2,
role__permissions=list(map(lambda x: x[0], MEMBERS_PERMISSIONS)))
f.MembershipFactory(project=m.private_project2,
user=m.project_member_without_perms,
email=m.project_member_without_perms.email,
role__project=m.private_project2,
role__permissions=[])
f.MembershipFactory(project=m.public_project,
user=m.project_owner,
is_owner=True)
f.MembershipFactory(project=m.private_project1,
user=m.project_owner,
is_owner=True)
f.MembershipFactory(project=m.private_project2,
user=m.project_owner,
is_owner=True)
m.public_issue_ca = f.IssueCustomAttributeFactory(project=m.public_project)
m.private_issue_ca1 = f.IssueCustomAttributeFactory(project=m.private_project1)
m.private_issue_ca2 = f.IssueCustomAttributeFactory(project=m.private_project2)
#m.public_issue = f.IssueFactory(project=m.public_project, owner=m.project_owner)
#m.private_issue1 = f.IssueFactory(project=m.private_project1, owner=m.project_owner)
#m.private_issue2 = f.IssueFactory(project=m.private_project2, owner=m.project_owner)
#m.public_issue_cav = f.IssueCustomAttributesValuesFactory(project=m.public_project,
# issue=f.IssueFactory(project=m.public_project,
# owner=m.project_owner),
# values={str(m.public_issue_ca.id):"test"})
#m.private_issue_cav1 = f.IssueCustomAttributesValuesFactory(project=m.private_project1,
# issue=f.IssueFactory(project=m.private_project1,
# owner=m.project_owner),
# values={str(m.private_issue_ca1.id):"test"})
#m.private_issue_cav2 = f.IssueCustomAttributesValuesFactory(project=m.private_project2,
# issue=f.IssueFactory(project=m.private_project2,
# owner=m.project_owner),
# values={str(m.private_issue_ca2.id):"test"})
return m
#########################################################
# Issue Custom Attribute
#########################################################
def test_issue_custom_attribute_retrieve(client, data):
public_url = reverse('issue-custom-attributes-detail', kwargs={"pk": data.public_issue_ca.pk})
private1_url = reverse('issue-custom-attributes-detail', kwargs={"pk": data.private_issue_ca1.pk})
private2_url = reverse('issue-custom-attributes-detail', kwargs={"pk": data.private_issue_ca2.pk})
users = [
None,
data.registered_user,
data.project_member_without_perms,
data.project_member_with_perms,
data.project_owner
]
results = helper_test_http_method(client, 'get', public_url, None, users)
assert results == [200, 200, 200, 200, 200]
results = helper_test_http_method(client, 'get', private1_url, None, users)
assert results == [200, 200, 200, 200, 200]
results = helper_test_http_method(client, 'get', private2_url, None, users)
assert results == [401, 403, 403, 200, 200]
def test_issue_custom_attribute_create(client, data):
public_url = reverse('issue-custom-attributes-list')
private1_url = reverse('issue-custom-attributes-list')
private2_url = reverse('issue-custom-attributes-list')
users = [
None,
data.registered_user,
data.project_member_without_perms,
data.project_member_with_perms,
data.project_owner
]
issue_ca_data = {"name": "test-new", "project": data.public_project.id}
issue_ca_data = json.dumps(issue_ca_data)
results = helper_test_http_method(client, 'post', public_url, issue_ca_data, users)
assert results == [401, 403, 403, 403, 201]
issue_ca_data = {"name": "test-new", "project": data.private_project1.id}
issue_ca_data = json.dumps(issue_ca_data)
results = helper_test_http_method(client, 'post', private1_url, issue_ca_data, users)
assert results == [401, 403, 403, 403, 201]
issue_ca_data = {"name": "test-new", "project": data.private_project2.id}
issue_ca_data = json.dumps(issue_ca_data)
results = helper_test_http_method(client, 'post', private2_url, issue_ca_data, users)
assert results == [401, 403, 403, 403, 201]
def test_issue_custom_attribute_update(client, data):
public_url = reverse('issue-custom-attributes-detail', kwargs={"pk": data.public_issue_ca.pk})
private1_url = reverse('issue-custom-attributes-detail', kwargs={"pk": data.private_issue_ca1.pk})
private2_url = reverse('issue-custom-attributes-detail', kwargs={"pk": data.private_issue_ca2.pk})
users = [
None,
data.registered_user,
data.project_member_without_perms,
data.project_member_with_perms,
data.project_owner
]
issue_ca_data = serializers.IssueCustomAttributeSerializer(data.public_issue_ca).data
issue_ca_data["name"] = "test"
issue_ca_data = json.dumps(issue_ca_data)
results = helper_test_http_method(client, 'put', public_url, issue_ca_data, users)
assert results == [401, 403, 403, 403, 200]
issue_ca_data = serializers.IssueCustomAttributeSerializer(data.private_issue_ca1).data
issue_ca_data["name"] = "test"
issue_ca_data = json.dumps(issue_ca_data)
results = helper_test_http_method(client, 'put', private1_url, issue_ca_data, users)
assert results == [401, 403, 403, 403, 200]
issue_ca_data = serializers.IssueCustomAttributeSerializer(data.private_issue_ca2).data
issue_ca_data["name"] = "test"
issue_ca_data = json.dumps(issue_ca_data)
results = helper_test_http_method(client, 'put', private2_url, issue_ca_data, users)
assert results == [401, 403, 403, 403, 200]
def test_issue_custom_attribute_delete(client, data):
public_url = reverse('issue-custom-attributes-detail', kwargs={"pk": data.public_issue_ca.pk})
private1_url = reverse('issue-custom-attributes-detail', kwargs={"pk": data.private_issue_ca1.pk})
private2_url = reverse('issue-custom-attributes-detail', kwargs={"pk": data.private_issue_ca2.pk})
users = [
None,
data.registered_user,
data.project_member_without_perms,
data.project_member_with_perms,
data.project_owner
]
results = helper_test_http_method(client, 'delete', public_url, None, users)
assert results == [401, 403, 403, 403, 204]
results = helper_test_http_method(client, 'delete', private1_url, None, users)
assert results == [401, 403, 403, 403, 204]
results = helper_test_http_method(client, 'delete', private2_url, None, users)
assert results == [401, 403, 403, 403, 204]
def test_issue_custom_attribute_list(client, data):
url = reverse('issue-custom-attributes-list')
response = client.json.get(url)
assert len(response.data) == 2
assert response.status_code == 200
client.login(data.registered_user)
response = client.json.get(url)
assert len(response.data) == 2
assert response.status_code == 200
client.login(data.project_member_without_perms)
response = client.json.get(url)
assert len(response.data) == 2
assert response.status_code == 200
client.login(data.project_member_with_perms)
response = client.json.get(url)
assert len(response.data) == 3
assert response.status_code == 200
client.login(data.project_owner)
response = client.json.get(url)
assert len(response.data) == 3
assert response.status_code == 200
def test_issue_custom_attribute_patch(client, data):
public_url = reverse('issue-custom-attributes-detail', kwargs={"pk": data.public_issue_ca.pk})
private1_url = reverse('issue-custom-attributes-detail', kwargs={"pk": data.private_issue_ca1.pk})
private2_url = reverse('issue-custom-attributes-detail', kwargs={"pk": data.private_issue_ca2.pk})
users = [
None,
data.registered_user,
data.project_member_without_perms,
data.project_member_with_perms,
data.project_owner
]
results = helper_test_http_method(client, 'patch', public_url, '{"name": "Test"}', users)
assert results == [401, 403, 403, 403, 200]
results = helper_test_http_method(client, 'patch', private1_url, '{"name": "Test"}', users)
assert results == [401, 403, 403, 403, 200]
results = helper_test_http_method(client, 'patch', private2_url, '{"name": "Test"}', users)
assert results == [401, 403, 403, 403, 200]
def test_issue_custom_attribute_action_bulk_update_order(client, data):
url = reverse('issue-custom-attributes-bulk-update-order')
users = [
None,
data.registered_user,
data.project_member_without_perms,
data.project_member_with_perms,
data.project_owner
]
post_data = json.dumps({
"bulk_issue_custom_attributes": [(1,2)],
"project": data.public_project.pk
})
results = helper_test_http_method(client, 'post', url, post_data, users)
assert results == [401, 403, 403, 403, 204]
post_data = json.dumps({
"bulk_issue_custom_attributes": [(1,2)],
"project": data.private_project1.pk
})
results = helper_test_http_method(client, 'post', url, post_data, users)
assert results == [401, 403, 403, 403, 204]
post_data = json.dumps({
"bulk_issue_custom_attributes": [(1,2)],
"project": data.private_project2.pk
})
results = helper_test_http_method(client, 'post', url, post_data, users)
assert results == [401, 403, 403, 403, 204]
#########################################################
# Issue Custom Attributes Values
#########################################################
#def test_issue_custom_attributes_values_retrieve(client, data):
# public_url = reverse('issue-custom-attributes-values-detail', args=[data.public_issue_cav.issue.id])
# private1_url = reverse('issue-custom-attributes-values-detail', args=[data.private_issue_cav1.issue.id])
# private2_url = reverse('issue-custom-attributes-values-detail', args=[data.private_issue_cav2.issue.id])
# users = [
# None,
# data.registered_user,
# data.project_member_without_perms,
# data.project_member_with_perms,
# data.project_owner
# ]
#
# results = helper_test_http_method(client, 'get', public_url, None, users)
# assert results == [200, 200, 200, 200, 200]
# results = helper_test_http_method(client, 'get', private1_url, None, users)
# assert results == [200, 200, 200, 200, 200]
# results = helper_test_http_method(client, 'get', private2_url, None, users)
# assert results == [401, 403, 403, 200, 200]
#
#
#def test_issue_custom_attributes_values_update(client, data):
# public_url = reverse('issue-custom-attributes-values-detail', args=[data.public_issue_cav.issue.id])
# private1_url = reverse('issue-custom-attributes-values-detail', args=[data.private_issue_cav1.issue.id])
# private2_url = reverse('issue-custom-attributes-values-detail', args=[data.private_issue_cav2.issue.id])
#
# users = [
# None,
# data.registered_user,
# data.project_member_without_perms,
# data.project_member_with_perms,
# data.project_owner
# ]
#
# issue_cav_data = serializers.IssueCustomAttributesValuesSerializer(data.public_issue_cav).data
# issue_cav_data["values"] = '{{"{}":"test-update"}}'.format(data.public_issue_ca.id)
# issue_cav_data = json.dumps(issue_cav_data)
# results = helper_test_http_method(client, 'put', public_url, issue_cav_data, users)
# assert results == [401, 403, 403, 403, 200]
#
# issue_cav_data = serializers.IssueCustomAttributesValuesSerializer(data.private_issue_cav1).data
# issue_cav_data["values"] = '{{"{}":"test-update"}}'.format(data.private_issue_ca1.id)
# issue_cav_data = json.dumps(issue_cav_data)
# results = helper_test_http_method(client, 'put', private1_url, issue_cav_data, users)
# assert results == [401, 403, 403, 403, 200]
#
# issue_cav_data = serializers.IssueCustomAttributesValuesSerializer(data.private_issue_cav2).data
# issue_cav_data["values"] = '{{"{}":"test-update"}}'.format(data.private_issue_ca2.id)
# issue_cav_data = json.dumps(issue_cav_data)
# results = helper_test_http_method(client, 'put', private2_url, issue_cav_data, users)
# assert results == [401, 403, 403, 403, 200]
#
#
#def test_issue_custom_attributes_values_delete(client, data):
# public_url = reverse('issue-custom-attributes-values-detail', args=[data.public_issue_cav.issue.id])
# private1_url = reverse('issue-custom-attributes-values-detail', args=[data.private_issue_cav1.issue.id])
# private2_url = reverse('issue-custom-attributes-values-detail', args=[data.private_issue_cav2.issue.id])
#
# users = [
# None,
# data.registered_user,
# data.project_member_without_perms,
# data.project_member_with_perms,
# data.project_owner
# ]
#
# results = helper_test_http_method(client, 'delete', public_url, None, users)
# assert results == [401, 403, 403, 403, 204]
# results = helper_test_http_method(client, 'delete', private1_url, None, users)
# assert results == [401, 403, 403, 403, 204]
# results = helper_test_http_method(client, 'delete', private2_url, None, users)
# assert results == [401, 403, 403, 403, 204]
#
#
#def test_issue_custom_attributes_values_list(client, data):
# url = reverse('issue-custom-attributes-values-list')
#
# response = client.json.get(url)
# assert response.status_code == 404
#
# client.login(data.registered_user)
# response = client.json.get(url)
# assert response.status_code == 404
#
# client.login(data.project_member_without_perms)
# response = client.json.get(url)
# assert response.status_code == 404
#
# client.login(data.project_member_with_perms)
# response = client.json.get(url)
# assert response.status_code == 404
#
# client.login(data.project_owner)
# response = client.json.get(url)
# assert response.status_code == 404
#
#
#def test_issue_custom_attributes_values_patch(client, data):
# public_url = reverse('issue-custom-attributes-values-detail', args=[data.public_issue_cav.issue.id])
# private1_url = reverse('issue-custom-attributes-values-detail', args=[data.private_issue_cav1.issue.id])
# private2_url = reverse('issue-custom-attributes-values-detail', args=[data.private_issue_cav2.issue.id])
#
# users = [
# None,
# data.registered_user,
# data.project_member_without_perms,
# data.project_member_with_perms,
# data.project_owner
# ]
#
# results = helper_test_http_method(client, 'patch', public_url,
# '{{"values": {{"{}": "test-update"}}, "version": 1}}'.format(data.public_issue_ca.id), users)
# assert results == [401, 403, 403, 403, 200]
# results = helper_test_http_method(client, 'patch', private1_url,
# '{{"values": {{"{}": "test-update"}}, "version": 1}}'.format(data.private_issue_ca1.id), users)
# assert results == [401, 403, 403, 403, 200]
# results = helper_test_http_method(client, 'patch', private2_url,
# '{{"values": {{"{}": "test-update"}}, "version": 1}}'.format(data.private_issue_ca2.id), users)
# assert results == [401, 403, 403, 403, 200]

View File

@ -0,0 +1,287 @@
# Copyright (C) 2015 Andrey Antukh <niwi@niwi.be>
# Copyright (C) 2015 Jesús Espino <jespinog@gmail.com>
# Copyright (C) 2015 David Barragán <bameda@dbarragan.com>
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from django.core.urlresolvers import reverse
from taiga.base.utils import json
from taiga.projects.custom_attributes import serializers
from taiga.permissions.permissions import MEMBERS_PERMISSIONS
from tests import factories as f
from tests.utils import helper_test_http_method
import pytest
pytestmark = pytest.mark.django_db(transaction=True)
@pytest.fixture
def data():
m = type("Models", (object,), {})
m.registered_user = f.UserFactory.create()
m.project_member_with_perms = f.UserFactory.create()
m.project_member_without_perms = f.UserFactory.create()
m.project_owner = f.UserFactory.create()
m.other_user = f.UserFactory.create()
m.superuser = f.UserFactory.create(is_superuser=True)
m.public_project = f.ProjectFactory(is_private=False,
anon_permissions=['view_project'],
public_permissions=['view_project'],
owner=m.project_owner)
m.private_project1 = f.ProjectFactory(is_private=True,
anon_permissions=['view_project'],
public_permissions=['view_project'],
owner=m.project_owner)
m.private_project2 = f.ProjectFactory(is_private=True,
anon_permissions=[],
public_permissions=[],
owner=m.project_owner)
m.public_membership = f.MembershipFactory(project=m.public_project,
user=m.project_member_with_perms,
email=m.project_member_with_perms.email,
role__project=m.public_project,
role__permissions=list(map(lambda x: x[0], MEMBERS_PERMISSIONS)))
m.private_membership1 = f.MembershipFactory(project=m.private_project1,
user=m.project_member_with_perms,
email=m.project_member_with_perms.email,
role__project=m.private_project1,
role__permissions=list(map(lambda x: x[0], MEMBERS_PERMISSIONS)))
f.MembershipFactory(project=m.private_project1,
user=m.project_member_without_perms,
email=m.project_member_without_perms.email,
role__project=m.private_project1,
role__permissions=[])
m.private_membership2 = f.MembershipFactory(project=m.private_project2,
user=m.project_member_with_perms,
email=m.project_member_with_perms.email,
role__project=m.private_project2,
role__permissions=list(map(lambda x: x[0], MEMBERS_PERMISSIONS)))
f.MembershipFactory(project=m.private_project2,
user=m.project_member_without_perms,
email=m.project_member_without_perms.email,
role__project=m.private_project2,
role__permissions=[])
f.MembershipFactory(project=m.public_project,
user=m.project_owner,
is_owner=True)
f.MembershipFactory(project=m.private_project1,
user=m.project_owner,
is_owner=True)
f.MembershipFactory(project=m.private_project2,
user=m.project_owner,
is_owner=True)
m.public_task_ca = f.TaskCustomAttributeFactory(project=m.public_project)
m.private_task_ca1 = f.TaskCustomAttributeFactory(project=m.private_project1)
m.private_task_ca2 = f.TaskCustomAttributeFactory(project=m.private_project2)
return m
#########################################################
# Task Custom Attribute
#########################################################
def test_task_custom_attribute_retrieve(client, data):
public_url = reverse('task-custom-attributes-detail', kwargs={"pk": data.public_task_ca.pk})
private1_url = reverse('task-custom-attributes-detail', kwargs={"pk": data.private_task_ca1.pk})
private2_url = reverse('task-custom-attributes-detail', kwargs={"pk": data.private_task_ca2.pk})
users = [
None,
data.registered_user,
data.project_member_without_perms,
data.project_member_with_perms,
data.project_owner
]
results = helper_test_http_method(client, 'get', public_url, None, users)
assert results == [200, 200, 200, 200, 200]
results = helper_test_http_method(client, 'get', private1_url, None, users)
assert results == [200, 200, 200, 200, 200]
results = helper_test_http_method(client, 'get', private2_url, None, users)
assert results == [401, 403, 403, 200, 200]
def test_task_custom_attribute_create(client, data):
public_url = reverse('task-custom-attributes-list')
private1_url = reverse('task-custom-attributes-list')
private2_url = reverse('task-custom-attributes-list')
users = [
None,
data.registered_user,
data.project_member_without_perms,
data.project_member_with_perms,
data.project_owner
]
task_ca_data = {"name": "test-new", "project": data.public_project.id}
task_ca_data = json.dumps(task_ca_data)
results = helper_test_http_method(client, 'post', public_url, task_ca_data, users)
assert results == [401, 403, 403, 403, 201]
task_ca_data = {"name": "test-new", "project": data.private_project1.id}
task_ca_data = json.dumps(task_ca_data)
results = helper_test_http_method(client, 'post', private1_url, task_ca_data, users)
assert results == [401, 403, 403, 403, 201]
task_ca_data = {"name": "test-new", "project": data.private_project2.id}
task_ca_data = json.dumps(task_ca_data)
results = helper_test_http_method(client, 'post', private2_url, task_ca_data, users)
assert results == [401, 403, 403, 403, 201]
def test_task_custom_attribute_update(client, data):
public_url = reverse('task-custom-attributes-detail', kwargs={"pk": data.public_task_ca.pk})
private1_url = reverse('task-custom-attributes-detail', kwargs={"pk": data.private_task_ca1.pk})
private2_url = reverse('task-custom-attributes-detail', kwargs={"pk": data.private_task_ca2.pk})
users = [
None,
data.registered_user,
data.project_member_without_perms,
data.project_member_with_perms,
data.project_owner
]
task_ca_data = serializers.TaskCustomAttributeSerializer(data.public_task_ca).data
task_ca_data["name"] = "test"
task_ca_data = json.dumps(task_ca_data)
results = helper_test_http_method(client, 'put', public_url, task_ca_data, users)
assert results == [401, 403, 403, 403, 200]
task_ca_data = serializers.TaskCustomAttributeSerializer(data.private_task_ca1).data
task_ca_data["name"] = "test"
task_ca_data = json.dumps(task_ca_data)
results = helper_test_http_method(client, 'put', private1_url, task_ca_data, users)
assert results == [401, 403, 403, 403, 200]
task_ca_data = serializers.TaskCustomAttributeSerializer(data.private_task_ca2).data
task_ca_data["name"] = "test"
task_ca_data = json.dumps(task_ca_data)
results = helper_test_http_method(client, 'put', private2_url, task_ca_data, users)
assert results == [401, 403, 403, 403, 200]
def test_task_custom_attribute_delete(client, data):
public_url = reverse('task-custom-attributes-detail', kwargs={"pk": data.public_task_ca.pk})
private1_url = reverse('task-custom-attributes-detail', kwargs={"pk": data.private_task_ca1.pk})
private2_url = reverse('task-custom-attributes-detail', kwargs={"pk": data.private_task_ca2.pk})
users = [
None,
data.registered_user,
data.project_member_without_perms,
data.project_member_with_perms,
data.project_owner
]
results = helper_test_http_method(client, 'delete', public_url, None, users)
assert results == [401, 403, 403, 403, 204]
results = helper_test_http_method(client, 'delete', private1_url, None, users)
assert results == [401, 403, 403, 403, 204]
results = helper_test_http_method(client, 'delete', private2_url, None, users)
assert results == [401, 403, 403, 403, 204]
def test_task_custom_attribute_list(client, data):
url = reverse('task-custom-attributes-list')
response = client.json.get(url)
assert len(response.data) == 2
assert response.status_code == 200
client.login(data.registered_user)
response = client.json.get(url)
assert len(response.data) == 2
assert response.status_code == 200
client.login(data.project_member_without_perms)
response = client.json.get(url)
assert len(response.data) == 2
assert response.status_code == 200
client.login(data.project_member_with_perms)
response = client.json.get(url)
assert len(response.data) == 3
assert response.status_code == 200
client.login(data.project_owner)
response = client.json.get(url)
assert len(response.data) == 3
assert response.status_code == 200
def test_task_custom_attribute_patch(client, data):
public_url = reverse('task-custom-attributes-detail', kwargs={"pk": data.public_task_ca.pk})
private1_url = reverse('task-custom-attributes-detail', kwargs={"pk": data.private_task_ca1.pk})
private2_url = reverse('task-custom-attributes-detail', kwargs={"pk": data.private_task_ca2.pk})
users = [
None,
data.registered_user,
data.project_member_without_perms,
data.project_member_with_perms,
data.project_owner
]
results = helper_test_http_method(client, 'patch', public_url, '{"name": "Test"}', users)
assert results == [401, 403, 403, 403, 200]
results = helper_test_http_method(client, 'patch', private1_url, '{"name": "Test"}', users)
assert results == [401, 403, 403, 403, 200]
results = helper_test_http_method(client, 'patch', private2_url, '{"name": "Test"}', users)
assert results == [401, 403, 403, 403, 200]
def test_task_custom_attribute_action_bulk_update_order(client, data):
url = reverse('task-custom-attributes-bulk-update-order')
users = [
None,
data.registered_user,
data.project_member_without_perms,
data.project_member_with_perms,
data.project_owner
]
post_data = json.dumps({
"bulk_task_custom_attributes": [(1,2)],
"project": data.public_project.pk
})
results = helper_test_http_method(client, 'post', url, post_data, users)
assert results == [401, 403, 403, 403, 204]
post_data = json.dumps({
"bulk_task_custom_attributes": [(1,2)],
"project": data.private_project1.pk
})
results = helper_test_http_method(client, 'post', url, post_data, users)
assert results == [401, 403, 403, 403, 204]
post_data = json.dumps({
"bulk_task_custom_attributes": [(1,2)],
"project": data.private_project2.pk
})
results = helper_test_http_method(client, 'post', url, post_data, users)
assert results == [401, 403, 403, 403, 204]

View File

@ -0,0 +1,287 @@
# Copyright (C) 2015 Andrey Antukh <niwi@niwi.be>
# Copyright (C) 2015 Jesús Espino <jespinog@gmail.com>
# Copyright (C) 2015 David Barragán <bameda@dbarragan.com>
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from django.core.urlresolvers import reverse
from taiga.base.utils import json
from taiga.projects.custom_attributes import serializers
from taiga.permissions.permissions import MEMBERS_PERMISSIONS
from tests import factories as f
from tests.utils import helper_test_http_method
import pytest
pytestmark = pytest.mark.django_db(transaction=True)
@pytest.fixture
def data():
m = type("Models", (object,), {})
m.registered_user = f.UserFactory.create()
m.project_member_with_perms = f.UserFactory.create()
m.project_member_without_perms = f.UserFactory.create()
m.project_owner = f.UserFactory.create()
m.other_user = f.UserFactory.create()
m.superuser = f.UserFactory.create(is_superuser=True)
m.public_project = f.ProjectFactory(is_private=False,
anon_permissions=['view_project'],
public_permissions=['view_project'],
owner=m.project_owner)
m.private_project1 = f.ProjectFactory(is_private=True,
anon_permissions=['view_project'],
public_permissions=['view_project'],
owner=m.project_owner)
m.private_project2 = f.ProjectFactory(is_private=True,
anon_permissions=[],
public_permissions=[],
owner=m.project_owner)
m.public_membership = f.MembershipFactory(project=m.public_project,
user=m.project_member_with_perms,
email=m.project_member_with_perms.email,
role__project=m.public_project,
role__permissions=list(map(lambda x: x[0], MEMBERS_PERMISSIONS)))
m.private_membership1 = f.MembershipFactory(project=m.private_project1,
user=m.project_member_with_perms,
email=m.project_member_with_perms.email,
role__project=m.private_project1,
role__permissions=list(map(lambda x: x[0], MEMBERS_PERMISSIONS)))
f.MembershipFactory(project=m.private_project1,
user=m.project_member_without_perms,
email=m.project_member_without_perms.email,
role__project=m.private_project1,
role__permissions=[])
m.private_membership2 = f.MembershipFactory(project=m.private_project2,
user=m.project_member_with_perms,
email=m.project_member_with_perms.email,
role__project=m.private_project2,
role__permissions=list(map(lambda x: x[0], MEMBERS_PERMISSIONS)))
f.MembershipFactory(project=m.private_project2,
user=m.project_member_without_perms,
email=m.project_member_without_perms.email,
role__project=m.private_project2,
role__permissions=[])
f.MembershipFactory(project=m.public_project,
user=m.project_owner,
is_owner=True)
f.MembershipFactory(project=m.private_project1,
user=m.project_owner,
is_owner=True)
f.MembershipFactory(project=m.private_project2,
user=m.project_owner,
is_owner=True)
m.public_userstory_ca = f.UserStoryCustomAttributeFactory(project=m.public_project)
m.private_userstory_ca1 = f.UserStoryCustomAttributeFactory(project=m.private_project1)
m.private_userstory_ca2 = f.UserStoryCustomAttributeFactory(project=m.private_project2)
return m
#########################################################
# User Story Custom Attribute
#########################################################
def test_userstory_custom_attribute_retrieve(client, data):
public_url = reverse('userstory-custom-attributes-detail', kwargs={"pk": data.public_userstory_ca.pk})
private1_url = reverse('userstory-custom-attributes-detail', kwargs={"pk": data.private_userstory_ca1.pk})
private2_url = reverse('userstory-custom-attributes-detail', kwargs={"pk": data.private_userstory_ca2.pk})
users = [
None,
data.registered_user,
data.project_member_without_perms,
data.project_member_with_perms,
data.project_owner
]
results = helper_test_http_method(client, 'get', public_url, None, users)
assert results == [200, 200, 200, 200, 200]
results = helper_test_http_method(client, 'get', private1_url, None, users)
assert results == [200, 200, 200, 200, 200]
results = helper_test_http_method(client, 'get', private2_url, None, users)
assert results == [401, 403, 403, 200, 200]
def test_userstory_custom_attribute_create(client, data):
public_url = reverse('userstory-custom-attributes-list')
private1_url = reverse('userstory-custom-attributes-list')
private2_url = reverse('userstory-custom-attributes-list')
users = [
None,
data.registered_user,
data.project_member_without_perms,
data.project_member_with_perms,
data.project_owner
]
userstory_ca_data = {"name": "test-new", "project": data.public_project.id}
userstory_ca_data = json.dumps(userstory_ca_data)
results = helper_test_http_method(client, 'post', public_url, userstory_ca_data, users)
assert results == [401, 403, 403, 403, 201]
userstory_ca_data = {"name": "test-new", "project": data.private_project1.id}
userstory_ca_data = json.dumps(userstory_ca_data)
results = helper_test_http_method(client, 'post', private1_url, userstory_ca_data, users)
assert results == [401, 403, 403, 403, 201]
userstory_ca_data = {"name": "test-new", "project": data.private_project2.id}
userstory_ca_data = json.dumps(userstory_ca_data)
results = helper_test_http_method(client, 'post', private2_url, userstory_ca_data, users)
assert results == [401, 403, 403, 403, 201]
def test_userstory_custom_attribute_update(client, data):
public_url = reverse('userstory-custom-attributes-detail', kwargs={"pk": data.public_userstory_ca.pk})
private1_url = reverse('userstory-custom-attributes-detail', kwargs={"pk": data.private_userstory_ca1.pk})
private2_url = reverse('userstory-custom-attributes-detail', kwargs={"pk": data.private_userstory_ca2.pk})
users = [
None,
data.registered_user,
data.project_member_without_perms,
data.project_member_with_perms,
data.project_owner
]
userstory_ca_data = serializers.UserStoryCustomAttributeSerializer(data.public_userstory_ca).data
userstory_ca_data["name"] = "test"
userstory_ca_data = json.dumps(userstory_ca_data)
results = helper_test_http_method(client, 'put', public_url, userstory_ca_data, users)
assert results == [401, 403, 403, 403, 200]
userstory_ca_data = serializers.UserStoryCustomAttributeSerializer(data.private_userstory_ca1).data
userstory_ca_data["name"] = "test"
userstory_ca_data = json.dumps(userstory_ca_data)
results = helper_test_http_method(client, 'put', private1_url, userstory_ca_data, users)
assert results == [401, 403, 403, 403, 200]
userstory_ca_data = serializers.UserStoryCustomAttributeSerializer(data.private_userstory_ca2).data
userstory_ca_data["name"] = "test"
userstory_ca_data = json.dumps(userstory_ca_data)
results = helper_test_http_method(client, 'put', private2_url, userstory_ca_data, users)
assert results == [401, 403, 403, 403, 200]
def test_userstory_custom_attribute_delete(client, data):
public_url = reverse('userstory-custom-attributes-detail', kwargs={"pk": data.public_userstory_ca.pk})
private1_url = reverse('userstory-custom-attributes-detail', kwargs={"pk": data.private_userstory_ca1.pk})
private2_url = reverse('userstory-custom-attributes-detail', kwargs={"pk": data.private_userstory_ca2.pk})
users = [
None,
data.registered_user,
data.project_member_without_perms,
data.project_member_with_perms,
data.project_owner
]
results = helper_test_http_method(client, 'delete', public_url, None, users)
assert results == [401, 403, 403, 403, 204]
results = helper_test_http_method(client, 'delete', private1_url, None, users)
assert results == [401, 403, 403, 403, 204]
results = helper_test_http_method(client, 'delete', private2_url, None, users)
assert results == [401, 403, 403, 403, 204]
def test_userstory_custom_attribute_list(client, data):
url = reverse('userstory-custom-attributes-list')
response = client.json.get(url)
assert len(response.data) == 2
assert response.status_code == 200
client.login(data.registered_user)
response = client.json.get(url)
assert len(response.data) == 2
assert response.status_code == 200
client.login(data.project_member_without_perms)
response = client.json.get(url)
assert len(response.data) == 2
assert response.status_code == 200
client.login(data.project_member_with_perms)
response = client.json.get(url)
assert len(response.data) == 3
assert response.status_code == 200
client.login(data.project_owner)
response = client.json.get(url)
assert len(response.data) == 3
assert response.status_code == 200
def test_userstory_custom_attribute_patch(client, data):
public_url = reverse('userstory-custom-attributes-detail', kwargs={"pk": data.public_userstory_ca.pk})
private1_url = reverse('userstory-custom-attributes-detail', kwargs={"pk": data.private_userstory_ca1.pk})
private2_url = reverse('userstory-custom-attributes-detail', kwargs={"pk": data.private_userstory_ca2.pk})
users = [
None,
data.registered_user,
data.project_member_without_perms,
data.project_member_with_perms,
data.project_owner
]
results = helper_test_http_method(client, 'patch', public_url, '{"name": "Test"}', users)
assert results == [401, 403, 403, 403, 200]
results = helper_test_http_method(client, 'patch', private1_url, '{"name": "Test"}', users)
assert results == [401, 403, 403, 403, 200]
results = helper_test_http_method(client, 'patch', private2_url, '{"name": "Test"}', users)
assert results == [401, 403, 403, 403, 200]
def test_userstory_custom_attribute_action_bulk_update_order(client, data):
url = reverse('userstory-custom-attributes-bulk-update-order')
users = [
None,
data.registered_user,
data.project_member_without_perms,
data.project_member_with_perms,
data.project_owner
]
post_data = json.dumps({
"bulk_userstory_custom_attributes": [(1,2)],
"project": data.public_project.pk
})
results = helper_test_http_method(client, 'post', url, post_data, users)
assert results == [401, 403, 403, 403, 204]
post_data = json.dumps({
"bulk_userstory_custom_attributes": [(1,2)],
"project": data.private_project1.pk
})
results = helper_test_http_method(client, 'post', url, post_data, users)
assert results == [401, 403, 403, 403, 204]
post_data = json.dumps({
"bulk_userstory_custom_attributes": [(1,2)],
"project": data.private_project2.pk
})
results = helper_test_http_method(client, 'post', url, post_data, users)
assert results == [401, 403, 403, 403, 204]

View File

@ -1,189 +0,0 @@
# Copyright (C) 2015 Andrey Antukh <niwi@niwi.be>
# Copyright (C) 2015 Jesús Espino <jespinog@gmail.com>
# Copyright (C) 2015 David Barragán <bameda@dbarragan.com>
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from django.core.urlresolvers import reverse
from taiga.base.utils import json
from .. import factories as f
import pytest
pytestmark = pytest.mark.django_db
#########################################################
# User Story Custom Fields
#########################################################
def test_userstory_custom_attribute_duplicate_name_error_on_create(client):
custom_attr_1 = f.UserStoryCustomAttributeFactory()
member = f.MembershipFactory(user=custom_attr_1.project.owner,
project=custom_attr_1.project,
is_owner=True)
url = reverse("userstory-custom-attributes-list")
data = {"name": custom_attr_1.name,
"project": custom_attr_1.project.pk}
client.login(member.user)
response = client.json.post(url, json.dumps(data))
assert response.status_code == 400
def test_userstory_custom_attribute_duplicate_name_error_on_update(client):
custom_attr_1 = f.UserStoryCustomAttributeFactory()
custom_attr_2 = f.UserStoryCustomAttributeFactory(project=custom_attr_1.project)
member = f.MembershipFactory(user=custom_attr_1.project.owner,
project=custom_attr_1.project,
is_owner=True)
url = reverse("userstory-custom-attributes-detail", kwargs={"pk": custom_attr_2.pk})
data = {"name": custom_attr_1.name}
client.login(member.user)
response = client.json.patch(url, json.dumps(data))
assert response.status_code == 400
def test_userstory_custom_attribute_duplicate_name_error_on_move_between_projects(client):
custom_attr_1 = f.UserStoryCustomAttributeFactory()
custom_attr_2 = f.UserStoryCustomAttributeFactory(name=custom_attr_1.name)
member = f.MembershipFactory(user=custom_attr_1.project.owner,
project=custom_attr_1.project,
is_owner=True)
f.MembershipFactory(user=custom_attr_1.project.owner,
project=custom_attr_2.project,
is_owner=True)
url = reverse("userstory-custom-attributes-detail", kwargs={"pk": custom_attr_2.pk})
data = {"project": custom_attr_1.project.pk}
client.login(member.user)
response = client.json.patch(url, json.dumps(data))
assert response.status_code == 400
#########################################################
# Task Custom Fields
#########################################################
def test_task_custom_attribute_duplicate_name_error_on_create(client):
custom_attr_1 = f.TaskCustomAttributeFactory()
member = f.MembershipFactory(user=custom_attr_1.project.owner,
project=custom_attr_1.project,
is_owner=True)
url = reverse("task-custom-attributes-list")
data = {"name": custom_attr_1.name,
"project": custom_attr_1.project.pk}
client.login(member.user)
response = client.json.post(url, json.dumps(data))
assert response.status_code == 400
def test_task_custom_attribute_duplicate_name_error_on_update(client):
custom_attr_1 = f.TaskCustomAttributeFactory()
custom_attr_2 = f.TaskCustomAttributeFactory(project=custom_attr_1.project)
member = f.MembershipFactory(user=custom_attr_1.project.owner,
project=custom_attr_1.project,
is_owner=True)
url = reverse("task-custom-attributes-detail", kwargs={"pk": custom_attr_2.pk})
data = {"name": custom_attr_1.name}
client.login(member.user)
response = client.json.patch(url, json.dumps(data))
assert response.status_code == 400
def test_task_custom_attribute_duplicate_name_error_on_move_between_projects(client):
custom_attr_1 = f.TaskCustomAttributeFactory()
custom_attr_2 = f.TaskCustomAttributeFactory(name=custom_attr_1.name)
member = f.MembershipFactory(user=custom_attr_1.project.owner,
project=custom_attr_1.project,
is_owner=True)
f.MembershipFactory(user=custom_attr_1.project.owner,
project=custom_attr_2.project,
is_owner=True)
url = reverse("task-custom-attributes-detail", kwargs={"pk": custom_attr_2.pk})
data = {"project": custom_attr_1.project.pk}
client.login(member.user)
response = client.json.patch(url, json.dumps(data))
assert response.status_code == 400
#########################################################
# Issue Custom Fields
#########################################################
def test_issue_custom_attribute_duplicate_name_error_on_create(client):
custom_attr_1 = f.IssueCustomAttributeFactory()
member = f.MembershipFactory(user=custom_attr_1.project.owner,
project=custom_attr_1.project,
is_owner=True)
url = reverse("issue-custom-attributes-list")
data = {"name": custom_attr_1.name,
"project": custom_attr_1.project.pk}
client.login(member.user)
response = client.json.post(url, json.dumps(data))
assert response.status_code == 400
def test_issue_custom_attribute_duplicate_name_error_on_update(client):
custom_attr_1 = f.IssueCustomAttributeFactory()
custom_attr_2 = f.IssueCustomAttributeFactory(project=custom_attr_1.project)
member = f.MembershipFactory(user=custom_attr_1.project.owner,
project=custom_attr_1.project,
is_owner=True)
url = reverse("issue-custom-attributes-detail", kwargs={"pk": custom_attr_2.pk})
data = {"name": custom_attr_1.name}
client.login(member.user)
response = client.json.patch(url, json.dumps(data))
assert response.status_code == 400
def test_issue_custom_attribute_duplicate_name_error_on_move_between_projects(client):
custom_attr_1 = f.IssueCustomAttributeFactory()
custom_attr_2 = f.IssueCustomAttributeFactory(name=custom_attr_1.name)
member = f.MembershipFactory(user=custom_attr_1.project.owner,
project=custom_attr_1.project,
is_owner=True)
f.MembershipFactory(user=custom_attr_1.project.owner,
project=custom_attr_2.project,
is_owner=True)
url = reverse("issue-custom-attributes-detail", kwargs={"pk": custom_attr_2.pk})
data = {"project": custom_attr_1.project.pk}
client.login(member.user)
response = client.json.patch(url, json.dumps(data))
assert response.status_code == 400

View File

@ -0,0 +1,271 @@
# Copyright (C) 2015 Andrey Antukh <niwi@niwi.be>
# Copyright (C) 2015 Jesús Espino <jespinog@gmail.com>
# Copyright (C) 2015 David Barragán <bameda@dbarragan.com>
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from django.core.urlresolvers import reverse
from taiga.base.utils import json
from .. import factories as f
import pytest
pytestmark = pytest.mark.django_db(transaction=True)
#########################################################
# Issue Custom Attributes
#########################################################
def test_issue_custom_attribute_duplicate_name_error_on_create(client):
custom_attr_1 = f.IssueCustomAttributeFactory()
member = f.MembershipFactory(user=custom_attr_1.project.owner,
project=custom_attr_1.project,
is_owner=True)
url = reverse("issue-custom-attributes-list")
data = {"name": custom_attr_1.name,
"project": custom_attr_1.project.pk}
client.login(member.user)
response = client.json.post(url, json.dumps(data))
assert response.status_code == 400
def test_issue_custom_attribute_duplicate_name_error_on_update(client):
custom_attr_1 = f.IssueCustomAttributeFactory()
custom_attr_2 = f.IssueCustomAttributeFactory(project=custom_attr_1.project)
member = f.MembershipFactory(user=custom_attr_1.project.owner,
project=custom_attr_1.project,
is_owner=True)
url = reverse("issue-custom-attributes-detail", kwargs={"pk": custom_attr_2.pk})
data = {"name": custom_attr_1.name}
client.login(member.user)
response = client.json.patch(url, json.dumps(data))
assert response.status_code == 400
def test_issue_custom_attribute_duplicate_name_error_on_move_between_projects(client):
custom_attr_1 = f.IssueCustomAttributeFactory()
custom_attr_2 = f.IssueCustomAttributeFactory(name=custom_attr_1.name)
member = f.MembershipFactory(user=custom_attr_1.project.owner,
project=custom_attr_1.project,
is_owner=True)
f.MembershipFactory(user=custom_attr_1.project.owner,
project=custom_attr_2.project,
is_owner=True)
url = reverse("issue-custom-attributes-detail", kwargs={"pk": custom_attr_2.pk})
data = {"project": custom_attr_1.project.pk}
client.login(member.user)
response = client.json.patch(url, json.dumps(data))
assert response.status_code == 400
#########################################################
# Issue Custom Attributes Values
#########################################################
def test_issue_custom_attributes_values_list(client):
member = f.MembershipFactory(is_owner=True)
url = reverse("issue-custom-attributes-values-list")
client.login(member.user)
response = client.json.get(url)
assert response.status_code == 404
def test_issue_custom_attributes_values_create(client):
issue = f.IssueFactory()
member = f.MembershipFactory(user=issue.project.owner,
project=issue.project,
is_owner=True)
custom_attr_1 = f.IssueCustomAttributeFactory(project=issue.project)
ct1_id = "{}".format(custom_attr_1.id)
custom_attr_2 = f.IssueCustomAttributeFactory(project=issue.project)
ct2_id = "{}".format(custom_attr_2.id)
url = reverse("issue-custom-attributes-values-list")
data = {
"issue": issue.id,
"values": {
ct1_id: "test_1",
ct2_id: "test_2"
},
}
client.login(member.user)
response = client.json.post(url, json.dumps(data))
assert response.status_code == 201
assert json.loads(response.data["values"]) == data["values"]
issue = issue.__class__.objects.get(id=issue.id)
assert issue.custom_attributes_values.values == data["values"]
def test_issue_custom_attributes_values_create_with_error_invalid_key(client):
issue = f.IssueFactory()
member = f.MembershipFactory(user=issue.project.owner,
project=issue.project,
is_owner=True)
custom_attr_1 = f.IssueCustomAttributeFactory(project=issue.project)
ct1_id = "{}".format(custom_attr_1.id)
custom_attr_2 = f.IssueCustomAttributeFactory(project=issue.project)
url = reverse("issue-custom-attributes-values-list")
data = {
"issue": issue.id,
"values": {
ct1_id: "test_1",
"123456": "test_2"
},
}
client.login(member.user)
response = client.json.post(url, json.dumps(data))
assert response.status_code == 400
def test_issue_custom_attributes_values_update(client):
issue = f.IssueFactory()
member = f.MembershipFactory(user=issue.project.owner,
project=issue.project,
is_owner=True)
custom_attr_1 = f.IssueCustomAttributeFactory(project=issue.project)
ct1_id = "{}".format(custom_attr_1.id)
custom_attr_2 = f.IssueCustomAttributeFactory(project=issue.project)
ct2_id = "{}".format(custom_attr_2.id)
custom_attrs_val = f.IssueCustomAttributesValuesFactory(
issue=issue,
values= {
ct1_id: "test_1",
ct2_id: "test_2"
},
)
url = reverse("issue-custom-attributes-values-detail", args=[issue.id])
data = {
"values": {
ct1_id: "test_1_updated",
ct2_id: "test_2_updated"
},
"version": custom_attrs_val.version
}
client.login(member.user)
response = client.json.patch(url, json.dumps(data))
assert response.status_code == 200
assert json.loads(response.data["values"]) == data["values"]
issue = issue.__class__.objects.get(id=issue.id)
assert issue.custom_attributes_values.values == data["values"]
def test_issue_custom_attributes_values_update_with_error_invalid_key(client):
issue = f.IssueFactory()
member = f.MembershipFactory(user=issue.project.owner,
project=issue.project,
is_owner=True)
custom_attr_1 = f.IssueCustomAttributeFactory(project=issue.project)
ct1_id = "{}".format(custom_attr_1.id)
custom_attr_2 = f.IssueCustomAttributeFactory(project=issue.project)
ct2_id = "{}".format(custom_attr_2.id)
custom_attrs_val = f.IssueCustomAttributesValuesFactory(
issue=issue,
values= {
ct1_id: "test_1",
ct2_id: "test_2"
},
)
url = reverse("issue-custom-attributes-values-detail", args=[issue.id])
data = {
"values": {
ct1_id: "test_1_updated",
"123456": "test_2_updated"
},
"version": custom_attrs_val.version
}
client.login(member.user)
response = client.json.patch(url, json.dumps(data))
assert response.status_code == 400
def test_issue_custom_attributes_values_delete(client):
issue = f.IssueFactory()
member = f.MembershipFactory(user=issue.project.owner,
project=issue.project,
is_owner=True)
custom_attr_1 = f.IssueCustomAttributeFactory(project=issue.project)
ct1_id = "{}".format(custom_attr_1.id)
custom_attr_2 = f.IssueCustomAttributeFactory(project=issue.project)
ct2_id = "{}".format(custom_attr_2.id)
url = reverse("issue-custom-attributes-values-detail", args=[issue.id])
custom_attrs_val = f.IssueCustomAttributesValuesFactory(
issue=issue,
values= {
ct1_id: "test_1",
ct2_id: "test_2"
},
)
client.login(member.user)
response = client.json.delete(url)
assert response.status_code == 204
assert issue.__class__.objects.filter(id=issue.id).exists()
assert not custom_attrs_val.__class__.objects.filter(id=custom_attrs_val.id).exists()
def test_issue_custom_attributes_values_delete_us(client):
issue = f.IssueFactory()
member = f.MembershipFactory(user=issue.project.owner,
project=issue.project,
is_owner=True)
custom_attr_1 = f.IssueCustomAttributeFactory(project=issue.project)
ct1_id = "{}".format(custom_attr_1.id)
custom_attr_2 = f.IssueCustomAttributeFactory(project=issue.project)
ct2_id = "{}".format(custom_attr_2.id)
url = reverse("issues-detail", args=[issue.id])
custom_attrs_val = f.IssueCustomAttributesValuesFactory(
issue=issue,
values= {
ct1_id: "test_1",
ct2_id: "test_2"
},
)
client.login(member.user)
response = client.json.delete(url)
assert response.status_code == 204
assert not issue.__class__.objects.filter(id=issue.id).exists()
assert not custom_attrs_val.__class__.objects.filter(id=custom_attrs_val.id).exists()

View File

@ -0,0 +1,268 @@
# Copyright (C) 2015 Andrey Antukh <niwi@niwi.be>
# Copyright (C) 2015 Jesús Espino <jespinog@gmail.com>
# Copyright (C) 2015 David Barragán <bameda@dbarragan.com>
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from django.core.urlresolvers import reverse
from taiga.base.utils import json
from .. import factories as f
import pytest
pytestmark = pytest.mark.django_db(transaction=True)
#########################################################
# Task Custom Attributes
#########################################################
def test_task_custom_attribute_duplicate_name_error_on_create(client):
custom_attr_1 = f.TaskCustomAttributeFactory()
member = f.MembershipFactory(user=custom_attr_1.project.owner,
project=custom_attr_1.project,
is_owner=True)
url = reverse("task-custom-attributes-list")
data = {"name": custom_attr_1.name,
"project": custom_attr_1.project.pk}
client.login(member.user)
response = client.json.post(url, json.dumps(data))
assert response.status_code == 400
def test_task_custom_attribute_duplicate_name_error_on_update(client):
custom_attr_1 = f.TaskCustomAttributeFactory()
custom_attr_2 = f.TaskCustomAttributeFactory(project=custom_attr_1.project)
member = f.MembershipFactory(user=custom_attr_1.project.owner,
project=custom_attr_1.project,
is_owner=True)
url = reverse("task-custom-attributes-detail", kwargs={"pk": custom_attr_2.pk})
data = {"name": custom_attr_1.name}
client.login(member.user)
response = client.json.patch(url, json.dumps(data))
assert response.status_code == 400
def test_task_custom_attribute_duplicate_name_error_on_move_between_projects(client):
custom_attr_1 = f.TaskCustomAttributeFactory()
custom_attr_2 = f.TaskCustomAttributeFactory(name=custom_attr_1.name)
member = f.MembershipFactory(user=custom_attr_1.project.owner,
project=custom_attr_1.project,
is_owner=True)
f.MembershipFactory(user=custom_attr_1.project.owner,
project=custom_attr_2.project,
is_owner=True)
url = reverse("task-custom-attributes-detail", kwargs={"pk": custom_attr_2.pk})
data = {"project": custom_attr_1.project.pk}
client.login(member.user)
response = client.json.patch(url, json.dumps(data))
assert response.status_code == 400
#########################################################
# Task Custom Attributes Values
#########################################################
def test_task_custom_attributes_values_list(client):
member = f.MembershipFactory(is_owner=True)
url = reverse("task-custom-attributes-values-list")
client.login(member.user)
response = client.json.get(url)
assert response.status_code == 404
def test_task_custom_attributes_values_create(client):
task = f.TaskFactory()
member = f.MembershipFactory(user=task.project.owner,
project=task.project,
is_owner=True)
custom_attr_1 = f.TaskCustomAttributeFactory(project=task.project)
ct1_id = "{}".format(custom_attr_1.id)
custom_attr_2 = f.TaskCustomAttributeFactory(project=task.project)
ct2_id = "{}".format(custom_attr_2.id)
url = reverse("task-custom-attributes-values-list")
data = {
"task": task.id,
"values": {
ct1_id: "test_1",
ct2_id: "test_2"
},
}
client.login(member.user)
response = client.json.post(url, json.dumps(data))
assert response.status_code == 201
assert json.loads(response.data["values"]) == data["values"]
task = task.__class__.objects.get(id=task.id)
assert task.custom_attributes_values.values == data["values"]
def test_task_custom_attributes_values_create_with_error_invalid_key(client):
task = f.TaskFactory()
member = f.MembershipFactory(user=task.project.owner,
project=task.project,
is_owner=True)
custom_attr_1 = f.TaskCustomAttributeFactory(project=task.project)
ct1_id = "{}".format(custom_attr_1.id)
custom_attr_2 = f.TaskCustomAttributeFactory(project=task.project)
url = reverse("task-custom-attributes-values-list")
data = {
"task": task.id,
"values": {
ct1_id: "test_1",
"123456": "test_2"
},
}
client.login(member.user)
response = client.json.post(url, json.dumps(data))
assert response.status_code == 400
def test_task_custom_attributes_values_update(client):
task = f.TaskFactory()
member = f.MembershipFactory(user=task.project.owner,
project=task.project,
is_owner=True)
custom_attr_1 = f.TaskCustomAttributeFactory(project=task.project)
ct1_id = "{}".format(custom_attr_1.id)
custom_attr_2 = f.TaskCustomAttributeFactory(project=task.project)
ct2_id = "{}".format(custom_attr_2.id)
custom_attrs_val = f.TaskCustomAttributesValuesFactory(
task=task,
values= {
ct1_id: "test_1",
ct2_id: "test_2"
},
)
url = reverse("task-custom-attributes-values-detail", args=[task.id])
data = {
"values": {
ct1_id: "test_1_updated",
ct2_id: "test_2_updated"
},
"version": custom_attrs_val.version
}
client.login(member.user)
response = client.json.patch(url, json.dumps(data))
assert response.status_code == 200
assert json.loads(response.data["values"]) == data["values"]
task = task.__class__.objects.get(id=task.id)
assert task.custom_attributes_values.values == data["values"]
def test_task_custom_attributes_values_update_with_error_invalid_key(client):
task = f.TaskFactory()
member = f.MembershipFactory(user=task.project.owner,
project=task.project,
is_owner=True)
custom_attr_1 = f.TaskCustomAttributeFactory(project=task.project)
ct1_id = "{}".format(custom_attr_1.id)
custom_attr_2 = f.TaskCustomAttributeFactory(project=task.project)
ct2_id = "{}".format(custom_attr_2.id)
custom_attrs_val = f.TaskCustomAttributesValuesFactory(
task=task,
values= {
ct1_id: "test_1",
ct2_id: "test_2"
},
)
url = reverse("task-custom-attributes-values-detail", args=[task.id])
data = {
"values": {
ct1_id: "test_1_updated",
"123456": "test_2_updated"
},
"version": custom_attrs_val.version
}
client.login(member.user)
response = client.json.patch(url, json.dumps(data))
assert response.status_code == 400
def test_task_custom_attributes_values_delete(client):
task = f.TaskFactory()
member = f.MembershipFactory(user=task.project.owner,
project=task.project,
is_owner=True)
custom_attr_1 = f.TaskCustomAttributeFactory(project=task.project)
ct1_id = "{}".format(custom_attr_1.id)
custom_attr_2 = f.TaskCustomAttributeFactory(project=task.project)
ct2_id = "{}".format(custom_attr_2.id)
url = reverse("task-custom-attributes-values-detail", args=[task.id])
custom_attrs_val = f.TaskCustomAttributesValuesFactory(
task=task,
values= {
ct1_id: "test_1",
ct2_id: "test_2"
},
)
client.login(member.user)
response = client.json.delete(url)
assert response.status_code == 204
assert task.__class__.objects.filter(id=task.id).exists()
assert not custom_attrs_val.__class__.objects.filter(id=custom_attrs_val.id).exists()
def test_task_custom_attributes_values_delete_us(client):
task = f.TaskFactory()
member = f.MembershipFactory(user=task.project.owner,
project=task.project,
is_owner=True)
custom_attr_1 = f.TaskCustomAttributeFactory(project=task.project)
ct1_id = "{}".format(custom_attr_1.id)
custom_attr_2 = f.TaskCustomAttributeFactory(project=task.project)
ct2_id = "{}".format(custom_attr_2.id)
url = reverse("tasks-detail", args=[task.id])
custom_attrs_val = f.TaskCustomAttributesValuesFactory(
task=task,
values= {
ct1_id: "test_1",
ct2_id: "test_2"
},
)
client.login(member.user)
response = client.json.delete(url)
assert response.status_code == 204
assert not task.__class__.objects.filter(id=task.id).exists()
assert not custom_attrs_val.__class__.objects.filter(id=custom_attrs_val.id).exists()

View File

@ -0,0 +1,268 @@
# Copyright (C) 2015 Andrey Antukh <niwi@niwi.be>
# Copyright (C) 2015 Jesús Espino <jespinog@gmail.com>
# Copyright (C) 2015 David Barragán <bameda@dbarragan.com>
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from django.core.urlresolvers import reverse
from taiga.base.utils import json
from .. import factories as f
import pytest
pytestmark = pytest.mark.django_db(transaction=True)
#########################################################
# User Story Custom Attributes
#########################################################
def test_userstory_custom_attribute_duplicate_name_error_on_create(client):
custom_attr_1 = f.UserStoryCustomAttributeFactory()
member = f.MembershipFactory(user=custom_attr_1.project.owner,
project=custom_attr_1.project,
is_owner=True)
url = reverse("userstory-custom-attributes-list")
data = {"name": custom_attr_1.name,
"project": custom_attr_1.project.pk}
client.login(member.user)
response = client.json.post(url, json.dumps(data))
assert response.status_code == 400
def test_userstory_custom_attribute_duplicate_name_error_on_update(client):
custom_attr_1 = f.UserStoryCustomAttributeFactory()
custom_attr_2 = f.UserStoryCustomAttributeFactory(project=custom_attr_1.project)
member = f.MembershipFactory(user=custom_attr_1.project.owner,
project=custom_attr_1.project,
is_owner=True)
url = reverse("userstory-custom-attributes-detail", kwargs={"pk": custom_attr_2.pk})
data = {"name": custom_attr_1.name}
client.login(member.user)
response = client.json.patch(url, json.dumps(data))
assert response.status_code == 400
def test_userstory_custom_attribute_duplicate_name_error_on_move_between_projects(client):
custom_attr_1 = f.UserStoryCustomAttributeFactory()
custom_attr_2 = f.UserStoryCustomAttributeFactory(name=custom_attr_1.name)
member = f.MembershipFactory(user=custom_attr_1.project.owner,
project=custom_attr_1.project,
is_owner=True)
f.MembershipFactory(user=custom_attr_1.project.owner,
project=custom_attr_2.project,
is_owner=True)
url = reverse("userstory-custom-attributes-detail", kwargs={"pk": custom_attr_2.pk})
data = {"project": custom_attr_1.project.pk}
client.login(member.user)
response = client.json.patch(url, json.dumps(data))
assert response.status_code == 400
#########################################################
# User Story Custom Attributes Values
#########################################################
def test_userstory_custom_attributes_values_list(client):
member = f.MembershipFactory(is_owner=True)
url = reverse("userstory-custom-attributes-values-list")
client.login(member.user)
response = client.json.get(url)
assert response.status_code == 404
def test_userstory_custom_attributes_values_create(client):
user_story = f.UserStoryFactory()
member = f.MembershipFactory(user=user_story.project.owner,
project=user_story.project,
is_owner=True)
custom_attr_1 = f.UserStoryCustomAttributeFactory(project=user_story.project)
ct1_id = "{}".format(custom_attr_1.id)
custom_attr_2 = f.UserStoryCustomAttributeFactory(project=user_story.project)
ct2_id = "{}".format(custom_attr_2.id)
url = reverse("userstory-custom-attributes-values-list")
data = {
"user_story": user_story.id,
"values": {
ct1_id: "test_1",
ct2_id: "test_2"
},
}
client.login(member.user)
response = client.json.post(url, json.dumps(data))
assert response.status_code == 201
assert json.loads(response.data["values"]) == data["values"]
user_story = user_story.__class__.objects.get(id=user_story.id)
assert user_story.custom_attributes_values.values == data["values"]
def test_userstory_custom_attributes_values_create_with_error_invalid_key(client):
user_story = f.UserStoryFactory()
member = f.MembershipFactory(user=user_story.project.owner,
project=user_story.project,
is_owner=True)
custom_attr_1 = f.UserStoryCustomAttributeFactory(project=user_story.project)
ct1_id = "{}".format(custom_attr_1.id)
custom_attr_2 = f.UserStoryCustomAttributeFactory(project=user_story.project)
url = reverse("userstory-custom-attributes-values-list")
data = {
"user_story": user_story.id,
"values": {
ct1_id: "test_1",
"123456": "test_2"
},
}
client.login(member.user)
response = client.json.post(url, json.dumps(data))
assert response.status_code == 400
def test_userstory_custom_attributes_values_update(client):
user_story = f.UserStoryFactory()
member = f.MembershipFactory(user=user_story.project.owner,
project=user_story.project,
is_owner=True)
custom_attr_1 = f.UserStoryCustomAttributeFactory(project=user_story.project)
ct1_id = "{}".format(custom_attr_1.id)
custom_attr_2 = f.UserStoryCustomAttributeFactory(project=user_story.project)
ct2_id = "{}".format(custom_attr_2.id)
custom_attrs_val = f.UserStoryCustomAttributesValuesFactory(
user_story=user_story,
values= {
ct1_id: "test_1",
ct2_id: "test_2"
},
)
url = reverse("userstory-custom-attributes-values-detail", args=[user_story.id])
data = {
"values": {
ct1_id: "test_1_updated",
ct2_id: "test_2_updated"
},
"version": custom_attrs_val.version
}
client.login(member.user)
response = client.json.patch(url, json.dumps(data))
assert response.status_code == 200
assert json.loads(response.data["values"]) == data["values"]
user_story = user_story.__class__.objects.get(id=user_story.id)
assert user_story.custom_attributes_values.values == data["values"]
def test_userstory_custom_attributes_values_update_with_error_invalid_key(client):
user_story = f.UserStoryFactory()
member = f.MembershipFactory(user=user_story.project.owner,
project=user_story.project,
is_owner=True)
custom_attr_1 = f.UserStoryCustomAttributeFactory(project=user_story.project)
ct1_id = "{}".format(custom_attr_1.id)
custom_attr_2 = f.UserStoryCustomAttributeFactory(project=user_story.project)
ct2_id = "{}".format(custom_attr_2.id)
custom_attrs_val = f.UserStoryCustomAttributesValuesFactory(
user_story=user_story,
values= {
ct1_id: "test_1",
ct2_id: "test_2"
},
)
url = reverse("userstory-custom-attributes-values-detail", args=[user_story.id])
data = {
"values": {
ct1_id: "test_1_updated",
"123456": "test_2_updated"
},
"version": custom_attrs_val.version
}
client.login(member.user)
response = client.json.patch(url, json.dumps(data))
assert response.status_code == 400
def test_userstory_custom_attributes_values_delete(client):
user_story = f.UserStoryFactory()
member = f.MembershipFactory(user=user_story.project.owner,
project=user_story.project,
is_owner=True)
custom_attr_1 = f.UserStoryCustomAttributeFactory(project=user_story.project)
ct1_id = "{}".format(custom_attr_1.id)
custom_attr_2 = f.UserStoryCustomAttributeFactory(project=user_story.project)
ct2_id = "{}".format(custom_attr_2.id)
url = reverse("userstory-custom-attributes-values-detail", args=[user_story.id])
custom_attrs_val = f.UserStoryCustomAttributesValuesFactory(
user_story=user_story,
values= {
ct1_id: "test_1",
ct2_id: "test_2"
},
)
client.login(member.user)
response = client.json.delete(url)
assert response.status_code == 204
assert user_story.__class__.objects.filter(id=user_story.id).exists()
assert not custom_attrs_val.__class__.objects.filter(id=custom_attrs_val.id).exists()
def test_userstory_custom_attributes_values_delete_us(client):
user_story = f.UserStoryFactory()
member = f.MembershipFactory(user=user_story.project.owner,
project=user_story.project,
is_owner=True)
custom_attr_1 = f.UserStoryCustomAttributeFactory(project=user_story.project)
ct1_id = "{}".format(custom_attr_1.id)
custom_attr_2 = f.UserStoryCustomAttributeFactory(project=user_story.project)
ct2_id = "{}".format(custom_attr_2.id)
url = reverse("userstories-detail", args=[user_story.id])
custom_attrs_val = f.UserStoryCustomAttributesValuesFactory(
user_story=user_story,
values= {
ct1_id: "test_1",
ct2_id: "test_2"
},
)
client.login(member.user)
response = client.json.delete(url)
assert response.status_code == 204
assert not user_story.__class__.objects.filter(id=user_story.id).exists()
assert not custom_attrs_val.__class__.objects.filter(id=custom_attrs_val.id).exists()