US #55: Custom fields - Create API end point

remotes/origin/enhancement/email-actions
David Barragán Merino 2015-02-04 19:51:39 +01:00
parent 38d4eacd76
commit c17157f8ec
5 changed files with 295 additions and 40 deletions

View File

@ -0,0 +1,57 @@
# 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 taiga.base.api import ModelCrudViewSet
from taiga.base import filters
from taiga.projects.mixins.ordering import BulkUpdateOrderMixin
from . import models
from . import serializers
from . import permissions
from . import services
class UserStoryCustomAttributeViewSet(ModelCrudViewSet, BulkUpdateOrderMixin):
model = models.UserStoryCustomAttribute
serializer_class = serializers.UserStoryCustomAttributeSerializer
permission_classes = (permissions.UserStoryCustomAttributePermission,)
filter_backends = (filters.CanViewProjectFilterBackend,)
filter_fields = ("project",)
bulk_update_param = "bulk_userstory_custom_attributes"
bulk_update_perm = "change_userstory_custom_attributes"
bulk_update_order_action = services.bulk_update_userstory_custom_attribute_order
class TaskCustomAttributeViewSet(ModelCrudViewSet, BulkUpdateOrderMixin):
model = models.TaskCustomAttribute
serializer_class = serializers.TaskCustomAttributeSerializer
permission_classes = (permissions.TaskCustomAttributePermission,)
filter_backends = (filters.CanViewProjectFilterBackend,)
filter_fields = ("project",)
bulk_update_param = "bulk_task_custom_attributes"
bulk_update_perm = "change_task_custom_attributes"
bulk_update_order_action = services.bulk_update_task_custom_attribute_order
class IssueCustomAttributeViewSet(ModelCrudViewSet, BulkUpdateOrderMixin):
model = models.IssueCustomAttribute
serializer_class = serializers.IssueCustomAttributeSerializer
permission_classes = (permissions.IssueCustomAttributePermission,)
filter_backends = (filters.CanViewProjectFilterBackend,)
filter_fields = ("project",)
bulk_update_param = "bulk_issue_custom_attributes"
bulk_update_perm = "change_issue_custom_attributes"
bulk_update_order_action = services.bulk_update_issue_custom_attribute_order

View File

@ -0,0 +1,47 @@
# 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 taiga.base.api.permissions import TaigaResourcePermission
from taiga.base.api.permissions import HasProjectPerm
from taiga.base.api.permissions import IsProjectOwner
from taiga.base.api.permissions import AllowAny
class UserStoryCustomAttributePermission(TaigaResourcePermission):
retrieve_perms = HasProjectPerm('view_project')
create_perms = IsProjectOwner()
update_perms = IsProjectOwner()
destroy_perms = IsProjectOwner()
list_perms = AllowAny()
bulk_update_order_perms = IsProjectOwner()
class TaskCustomAttributePermission(TaigaResourcePermission):
retrieve_perms = HasProjectPerm('view_project')
create_perms = IsProjectOwner()
update_perms = IsProjectOwner()
destroy_perms = IsProjectOwner()
list_perms = AllowAny()
bulk_update_order_perms = IsProjectOwner()
class IssueCustomAttributePermission(TaigaResourcePermission):
retrieve_perms = HasProjectPerm('view_project')
create_perms = IsProjectOwner()
update_perms = IsProjectOwner()
destroy_perms = IsProjectOwner()
list_perms = AllowAny()
bulk_update_order_perms = IsProjectOwner()

View File

@ -0,0 +1,68 @@
# 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.utils.translation import ugettext_lazy as _
from rest_framework.serializers import ValidationError
from taiga.base.serializers import ModelSerializer
from . import models
######################################################
# Base Serializer Class
#######################################################
class BaseCustomAttributeSerializer(ModelSerializer):
def validate(self, data):
"""
Check the name is not duplicated in the project. Check when:
- create a new one
- update the name
- update the project (move to another project)
"""
data_name = data.get("name", None)
data_project = data.get("project", None)
if self.object:
data_name = data_name or self.object.name
data_project = data_project or self.object.project
model = self.Meta.model
qs = model.objects.filter(project=data_project, name=data_name)
if qs.exists():
raise ValidationError(_("There is a custom field with the same name in this project."))
return data
######################################################
# Custom Field Serializers
#######################################################
class UserStoryCustomAttributeSerializer(BaseCustomAttributeSerializer):
class Meta:
model = models.UserStoryCustomAttribute
class TaskCustomAttributeSerializer(BaseCustomAttributeSerializer):
class Meta:
model = models.TaskCustomAttribute
class IssueCustomAttributeSerializer(BaseCustomAttributeSerializer):
class Meta:
model = models.IssueCustomAttribute

View File

@ -0,0 +1,69 @@
# 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.db import transaction
from django.db import connection
@transaction.atomic
def bulk_update_userstory_custom_attribute_order(project, user, data):
cursor = connection.cursor()
sql = """
prepare bulk_update_order as update custom_attributes_userstorycustomattribute set "order" = $1
where custom_attributes_userstorycustomattribute.id = $2 and
custom_attributes_userstorycustomattribute.project_id = $3;
"""
cursor.execute(sql)
for id, order in data:
cursor.execute("EXECUTE bulk_update_order (%s, %s, %s);",
(order, id, project.id))
cursor.execute("DEALLOCATE bulk_update_order")
cursor.close()
@transaction.atomic
def bulk_update_task_custom_attribute_order(project, user, data):
cursor = connection.cursor()
sql = """
prepare bulk_update_order as update custom_attributes_taskcustomattribute set "order" = $1
where custom_attributes_taskcustomattribute.id = $2 and
custom_attributes_taskcustomattribute.project_id = $3;
"""
cursor.execute(sql)
for id, order in data:
cursor.execute("EXECUTE bulk_update_order (%s, %s, %s);",
(order, id, project.id))
cursor.execute("DEALLOCATE bulk_update_order")
cursor.close()
@transaction.atomic
def bulk_update_issue_custom_attribute_order(project, user, data):
cursor = connection.cursor()
sql = """
prepare bulk_update_order as update custom_attributes_issuecustomattribute set "order" = $1
where custom_attributes_issuecustomattribute.id = $2 and
custom_attributes_issuecustomattribute.project_id = $3;
"""
cursor.execute(sql)
for id, order in data:
cursor.execute("EXECUTE bulk_update_order (%s, %s, %s);",
(order, id, project.id))
cursor.execute("DEALLOCATE bulk_update_order")
cursor.close()

View File

@ -35,23 +35,10 @@ from taiga.userstorage.api import StorageEntriesViewSet
router.register(r"user-storage", StorageEntriesViewSet, base_name="user-storage") router.register(r"user-storage", StorageEntriesViewSet, base_name="user-storage")
# Resolver # Notify policies
from taiga.projects.references.api import ResolverViewSet from taiga.projects.notifications.api import NotifyPolicyViewSet
router.register(r"resolver", ResolverViewSet, base_name="resolver") router.register(r"notify-policies", NotifyPolicyViewSet, base_name="notifications")
# Search
from taiga.searches.api import SearchViewSet
router.register(r"search", SearchViewSet, base_name="search")
# Importer
from taiga.export_import.api import ProjectImporterViewSet, ProjectExporterViewSet
router.register(r"importer", ProjectImporterViewSet, base_name="importer")
router.register(r"exporter", ProjectExporterViewSet, base_name="exporter")
# Projects & Selectors # Projects & Selectors
@ -80,6 +67,31 @@ router.register(r"priorities", PriorityViewSet, base_name="priorities")
router.register(r"severities",SeverityViewSet , base_name="severities") router.register(r"severities",SeverityViewSet , base_name="severities")
# Custom Attributes
from taiga.projects.custom_attributes.api import UserStoryCustomAttributeViewSet
from taiga.projects.custom_attributes.api import TaskCustomAttributeViewSet
from taiga.projects.custom_attributes.api import IssueCustomAttributeViewSet
router.register(r"userstory-custom-attributes", UserStoryCustomAttributeViewSet,
base_name="userstory-custom-attributes")
router.register(r"task-custom-attributes", TaskCustomAttributeViewSet,
base_name="task-custom-attributes")
router.register(r"issue-custom-attributes", IssueCustomAttributeViewSet,
base_name="issue-custom-attributes")
# Search
from taiga.searches.api import SearchViewSet
router.register(r"search", SearchViewSet, base_name="search")
# Resolver
from taiga.projects.references.api import ResolverViewSet
router.register(r"resolver", ResolverViewSet, base_name="resolver")
# Attachments # Attachments
from taiga.projects.attachments.api import UserStoryAttachmentViewSet from taiga.projects.attachments.api import UserStoryAttachmentViewSet
from taiga.projects.attachments.api import IssueAttachmentViewSet from taiga.projects.attachments.api import IssueAttachmentViewSet
@ -93,11 +105,21 @@ router.register(r"issues/attachments", IssueAttachmentViewSet, base_name="issue-
router.register(r"wiki/attachments", WikiAttachmentViewSet, base_name="wiki-attachments") router.register(r"wiki/attachments", WikiAttachmentViewSet, base_name="wiki-attachments")
# Webhooks # Project components
from taiga.webhooks.api import WebhookViewSet, WebhookLogViewSet from taiga.projects.milestones.api import MilestoneViewSet
from taiga.projects.userstories.api import UserStoryViewSet
from taiga.projects.tasks.api import TaskViewSet
from taiga.projects.issues.api import IssueViewSet
from taiga.projects.issues.api import VotersViewSet
from taiga.projects.wiki.api import WikiViewSet, WikiLinkViewSet
router.register(r"webhooks", WebhookViewSet, base_name="webhooks") router.register(r"milestones", MilestoneViewSet, base_name="milestones")
router.register(r"webhooklogs", WebhookLogViewSet, base_name="webhooklogs") router.register(r"userstories", UserStoryViewSet, base_name="userstories")
router.register(r"tasks", TaskViewSet, base_name="tasks")
router.register(r"issues", IssueViewSet, base_name="issues")
router.register(r"issues/(?P<issue_id>\d+)/voters", VotersViewSet, base_name="issue-voters")
router.register(r"wiki", WikiViewSet, base_name="wiki")
router.register(r"wiki-links", WikiLinkViewSet, base_name="wiki-links")
# History & Components # History & Components
@ -120,27 +142,12 @@ router.register(r"timeline/user", UserTimeline, base_name="user-timeline")
router.register(r"timeline/project", ProjectTimeline, base_name="project-timeline") router.register(r"timeline/project", ProjectTimeline, base_name="project-timeline")
# Project components # Webhooks
from taiga.projects.milestones.api import MilestoneViewSet from taiga.webhooks.api import WebhookViewSet
from taiga.projects.userstories.api import UserStoryViewSet from taiga.webhooks.api import WebhookLogViewSet
from taiga.projects.tasks.api import TaskViewSet
from taiga.projects.issues.api import IssueViewSet
from taiga.projects.issues.api import VotersViewSet
from taiga.projects.wiki.api import WikiViewSet, WikiLinkViewSet
router.register(r"milestones", MilestoneViewSet, base_name="milestones") router.register(r"webhooks", WebhookViewSet, base_name="webhooks")
router.register(r"userstories", UserStoryViewSet, base_name="userstories") router.register(r"webhooklogs", WebhookLogViewSet, base_name="webhooklogs")
router.register(r"tasks", TaskViewSet, base_name="tasks")
router.register(r"issues", IssueViewSet, base_name="issues")
router.register(r"issues/(?P<issue_id>\d+)/voters", VotersViewSet, base_name="issue-voters")
router.register(r"wiki", WikiViewSet, base_name="wiki")
router.register(r"wiki-links", WikiLinkViewSet, base_name="wiki-links")
# Notify policies
from taiga.projects.notifications.api import NotifyPolicyViewSet
router.register(r"notify-policies", NotifyPolicyViewSet, base_name="notifications")
# GitHub webhooks # GitHub webhooks
@ -161,5 +168,12 @@ from taiga.hooks.bitbucket.api import BitBucketViewSet
router.register(r"bitbucket-hook", BitBucketViewSet, base_name="bitbucket-hook") router.register(r"bitbucket-hook", BitBucketViewSet, base_name="bitbucket-hook")
# Importer
from taiga.export_import.api import ProjectImporterViewSet, ProjectExporterViewSet
router.register(r"importer", ProjectImporterViewSet, base_name="importer")
router.register(r"exporter", ProjectExporterViewSet, base_name="exporter")
# feedback # feedback
# - see taiga.feedback.routers and taiga.feedback.apps # - see taiga.feedback.routers and taiga.feedback.apps