diff --git a/taiga/webhooks/serializers.py b/taiga/webhooks/serializers.py index 3525c973..7f6736f4 100644 --- a/taiga/webhooks/serializers.py +++ b/taiga/webhooks/serializers.py @@ -185,6 +185,26 @@ class RolePointsSerializer(serializers.LightSerializer): return obj.points.value +class EpicStatusSerializer(serializers.LightSerializer): + id = Field(attr="pk") + name = MethodField() + slug = MethodField() + color = MethodField() + is_closed = MethodField() + + def get_name(self, obj): + return obj.name + + def get_slug(self, obj): + return obj.slug + + def get_color(self, obj): + return obj.color + + def get_is_closed(self, obj): + return obj.is_closed + + class UserStoryStatusSerializer(serializers.LightSerializer): id = Field(attr="pk") name = MethodField() @@ -445,3 +465,51 @@ class WikiPageSerializer(serializers.LightSerializer): def get_permalink(self, obj): return resolve_front_url("wiki", obj.project.slug, obj.slug) + + +######################################################################## +# Epic +######################################################################## + +class EpicSerializer(CustomAttributesValuesWebhookSerializerMixin, serializers.LightSerializer): + id = Field() + ref = Field() + created_date = Field() + modified_date = Field() + subject = Field() + watchers = MethodField() + description = Field() + tags = Field() + permalink = serializers.SerializerMethodField("get_permalink") + project = ProjectSerializer() + owner = UserSerializer() + assigned_to = UserSerializer() + status = EpicStatusSerializer() + epics_order = Field() + color = Field() + client_requirement = Field() + team_requirement = Field() + client_requirement = Field() + team_requirement = Field() + + def get_permalink(self, obj): + return resolve_front_url("epic", obj.project.slug, obj.ref) + + def custom_attributes_queryset(self, project): + return project.epiccustomattributes.all() + + def get_watchers(self, obj): + return list(obj.get_watchers().values_list("id", flat=True)) + + +class EpicRelatedUserStorySerializer(serializers.LightSerializer): + id = Field() + user_story = MethodField() + epic = MethodField() + order = Field() + + def get_user_story(self, obj): + return UserStorySerializer(obj.user_story).data + + def get_epic(self, obj): + return EpicSerializer(obj.epic).data diff --git a/taiga/webhooks/tasks.py b/taiga/webhooks/tasks.py index 334cd52d..75e3caad 100644 --- a/taiga/webhooks/tasks.py +++ b/taiga/webhooks/tasks.py @@ -25,7 +25,8 @@ from taiga.base.api.renderers import UnicodeJSONRenderer from taiga.base.utils.db import get_typename_for_model_instance from taiga.celery import app -from .serializers import (UserStorySerializer, IssueSerializer, TaskSerializer, +from .serializers import (EpicSerializer, EpicRelatedUserStorySerializer, + UserStorySerializer, IssueSerializer, TaskSerializer, WikiPageSerializer, MilestoneSerializer, HistoryEntrySerializer, UserSerializer) from .models import WebhookLog @@ -33,8 +34,11 @@ from .models import WebhookLog def _serialize(obj): content_type = get_typename_for_model_instance(obj) - - if content_type == "userstories.userstory": + if content_type == "epics.epic": + return EpicSerializer(obj).data + elif content_type == "epics.relateduserstory": + return EpicRelatedUserStorySerializer(obj).data + elif content_type == "userstories.userstory": return UserStorySerializer(obj).data elif content_type == "issues.issue": return IssueSerializer(obj).data diff --git a/tests/integration/test_webhooks_epics.py b/tests/integration/test_webhooks_epics.py new file mode 100644 index 00000000..f77dc829 --- /dev/null +++ b/tests/integration/test_webhooks_epics.py @@ -0,0 +1,319 @@ +# -*- coding: utf-8 -*- +# Copyright (C) 2014-2016 Andrey Antukh +# Copyright (C) 2014-2016 Jesús Espino +# Copyright (C) 2014-2016 David Barragán +# Copyright (C) 2014-2016 Alejandro Alonso +# Copyright (C) 2014-2016 Anler Hernández +# 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 . + +import pytest +from unittest.mock import patch + +from .. import factories as f + +from taiga.projects.history import services + + +pytestmark = pytest.mark.django_db(transaction=True) + + +def test_webhooks_when_create_epic(settings): + settings.WEBHOOKS_ENABLED = True + project = f.ProjectFactory() + f.WebhookFactory.create(project=project) + f.WebhookFactory.create(project=project) + + obj = f.EpicFactory.create(project=project) + + with patch('taiga.webhooks.tasks._send_request') as send_request_mock: + services.take_snapshot(obj, user=obj.owner) + assert send_request_mock.call_count == 2 + + (webhook_id, url, key, data) = send_request_mock.call_args[0] + assert data["action"] == "create" + assert data["type"] == "epic" + assert data["by"]["id"] == obj.owner.id + assert "date" in data + assert data["data"]["id"] == obj.id + + +def test_webhooks_when_update_epic(settings): + settings.WEBHOOKS_ENABLED = True + project = f.ProjectFactory() + f.WebhookFactory.create(project=project) + f.WebhookFactory.create(project=project) + + obj = f.EpicFactory.create(project=project) + + with patch('taiga.webhooks.tasks._send_request') as send_request_mock: + services.take_snapshot(obj, user=obj.owner) + assert send_request_mock.call_count == 2 + + obj.subject = "test webhook update" + obj.save() + + with patch('taiga.webhooks.tasks._send_request') as send_request_mock: + services.take_snapshot(obj, user=obj.owner, comment="test_comment") + assert send_request_mock.call_count == 2 + + (webhook_id, url, key, data) = send_request_mock.call_args[0] + assert data["action"] == "change" + assert data["type"] == "epic" + assert data["by"]["id"] == obj.owner.id + assert "date" in data + assert data["data"]["id"] == obj.id + assert data["data"]["subject"] == obj.subject + assert data["change"]["comment"] == "test_comment" + assert data["change"]["diff"]["subject"]["to"] == data["data"]["subject"] + assert data["change"]["diff"]["subject"]["from"] != data["data"]["subject"] + + +def test_webhooks_when_delete_epic(settings): + settings.WEBHOOKS_ENABLED = True + project = f.ProjectFactory() + f.WebhookFactory.create(project=project) + f.WebhookFactory.create(project=project) + + obj = f.EpicFactory.create(project=project) + + with patch('taiga.webhooks.tasks._send_request') as send_request_mock: + services.take_snapshot(obj, user=obj.owner, delete=True) + assert send_request_mock.call_count == 2 + + (webhook_id, url, key, data) = send_request_mock.call_args[0] + assert data["action"] == "delete" + assert data["type"] == "epic" + assert data["by"]["id"] == obj.owner.id + assert "date" in data + assert "data" in data + + +def test_webhooks_when_update_epic_attachments(settings): + settings.WEBHOOKS_ENABLED = True + project = f.ProjectFactory() + f.WebhookFactory.create(project=project) + f.WebhookFactory.create(project=project) + + obj = f.EpicFactory.create(project=project) + + with patch('taiga.webhooks.tasks._send_request') as send_request_mock: + services.take_snapshot(obj, user=obj.owner) + assert send_request_mock.call_count == 2 + + # Create attachments + attachment1 = f.EpicAttachmentFactory(project=obj.project, content_object=obj, owner=obj.owner) + attachment2 = f.EpicAttachmentFactory(project=obj.project, content_object=obj, owner=obj.owner) + + with patch('taiga.webhooks.tasks._send_request') as send_request_mock: + services.take_snapshot(obj, user=obj.owner, comment="test_comment") + assert send_request_mock.call_count == 2 + + (webhook_id, url, key, data) = send_request_mock.call_args[0] + assert data["action"] == "change" + assert data["type"] == "epic" + assert data["by"]["id"] == obj.owner.id + assert "date" in data + assert data["data"]["id"] == obj.id + assert data["change"]["comment"] == "test_comment" + assert len(data["change"]["diff"]["attachments"]["new"]) == 2 + assert len(data["change"]["diff"]["attachments"]["changed"]) == 0 + assert len(data["change"]["diff"]["attachments"]["deleted"]) == 0 + + # Update attachment + attachment1.description = "new attachment description" + attachment1.save() + + with patch('taiga.webhooks.tasks._send_request') as send_request_mock: + services.take_snapshot(obj, user=obj.owner, comment="test_comment") + assert send_request_mock.call_count == 2 + + (webhook_id, url, key, data) = send_request_mock.call_args[0] + assert data["action"] == "change" + assert data["type"] == "epic" + assert data["by"]["id"] == obj.owner.id + assert "date" in data + assert data["data"]["id"] == obj.id + assert data["change"]["comment"] == "test_comment" + assert len(data["change"]["diff"]["attachments"]["new"]) == 0 + assert len(data["change"]["diff"]["attachments"]["changed"]) == 1 + assert len(data["change"]["diff"]["attachments"]["deleted"]) == 0 + + # Delete attachment + attachment2.delete() + + with patch('taiga.webhooks.tasks._send_request') as send_request_mock: + services.take_snapshot(obj, user=obj.owner, comment="test_comment") + assert send_request_mock.call_count == 2 + + (webhook_id, url, key, data) = send_request_mock.call_args[0] + assert data["action"] == "change" + assert data["type"] == "epic" + assert data["by"]["id"] == obj.owner.id + assert "date" in data + assert data["data"]["id"] == obj.id + assert data["change"]["comment"] == "test_comment" + assert len(data["change"]["diff"]["attachments"]["new"]) == 0 + assert len(data["change"]["diff"]["attachments"]["changed"]) == 0 + assert len(data["change"]["diff"]["attachments"]["deleted"]) == 1 + + +def test_webhooks_when_update_epic_custom_attributes(settings): + settings.WEBHOOKS_ENABLED = True + project = f.ProjectFactory() + f.WebhookFactory.create(project=project) + f.WebhookFactory.create(project=project) + + obj = f.EpicFactory.create(project=project) + + custom_attr_1 = f.EpicCustomAttributeFactory(project=obj.project) + ct1_id = "{}".format(custom_attr_1.id) + custom_attr_2 = f.EpicCustomAttributeFactory(project=obj.project) + ct2_id = "{}".format(custom_attr_2.id) + + with patch('taiga.webhooks.tasks._send_request') as send_request_mock: + services.take_snapshot(obj, user=obj.owner) + assert send_request_mock.call_count == 2 + + # Create custom attributes + obj.custom_attributes_values.attributes_values = { + ct1_id: "test_1_updated", + ct2_id: "test_2_updated" + } + obj.custom_attributes_values.save() + + with patch('taiga.webhooks.tasks._send_request') as send_request_mock: + services.take_snapshot(obj, user=obj.owner, comment="test_comment") + assert send_request_mock.call_count == 2 + + (webhook_id, url, key, data) = send_request_mock.call_args[0] + assert data["action"] == "change" + assert data["type"] == "epic" + assert data["by"]["id"] == obj.owner.id + assert "date" in data + assert data["data"]["id"] == obj.id + assert data["change"]["comment"] == "test_comment" + assert len(data["change"]["diff"]["custom_attributes"]["new"]) == 2 + assert len(data["change"]["diff"]["custom_attributes"]["changed"]) == 0 + assert len(data["change"]["diff"]["custom_attributes"]["deleted"]) == 0 + + # Update custom attributes + obj.custom_attributes_values.attributes_values[ct1_id] = "test_2_updated" + obj.custom_attributes_values.save() + + with patch('taiga.webhooks.tasks._send_request') as send_request_mock: + services.take_snapshot(obj, user=obj.owner, comment="test_comment") + assert send_request_mock.call_count == 2 + + (webhook_id, url, key, data) = send_request_mock.call_args[0] + assert data["action"] == "change" + assert data["type"] == "epic" + assert data["by"]["id"] == obj.owner.id + assert "date" in data + assert data["data"]["id"] == obj.id + assert data["change"]["comment"] == "test_comment" + assert len(data["change"]["diff"]["custom_attributes"]["new"]) == 0 + assert len(data["change"]["diff"]["custom_attributes"]["changed"]) == 1 + assert len(data["change"]["diff"]["custom_attributes"]["deleted"]) == 0 + + # Delete custom attributes + del obj.custom_attributes_values.attributes_values[ct1_id] + obj.custom_attributes_values.save() + + with patch('taiga.webhooks.tasks._send_request') as send_request_mock: + services.take_snapshot(obj, user=obj.owner, comment="test_comment") + assert send_request_mock.call_count == 2 + + (webhook_id, url, key, data) = send_request_mock.call_args[0] + assert data["action"] == "change" + assert data["type"] == "epic" + assert data["by"]["id"] == obj.owner.id + assert "date" in data + assert data["data"]["id"] == obj.id + assert data["change"]["comment"] == "test_comment" + assert len(data["change"]["diff"]["custom_attributes"]["new"]) == 0 + assert len(data["change"]["diff"]["custom_attributes"]["changed"]) == 0 + assert len(data["change"]["diff"]["custom_attributes"]["deleted"]) == 1 + + +def test_webhooks_when_create_epic_related_userstory(settings): + settings.WEBHOOKS_ENABLED = True + project = f.ProjectFactory() + f.WebhookFactory.create(project=project) + f.WebhookFactory.create(project=project) + + epic = f.EpicFactory.create(project=project) + obj = f.RelatedUserStory.create(epic=epic) + + with patch('taiga.webhooks.tasks._send_request') as send_request_mock: + services.take_snapshot(obj, user=epic.owner) + assert send_request_mock.call_count == 2 + + (webhook_id, url, key, data) = send_request_mock.call_args[0] + assert data["action"] == "create" + assert data["type"] == "relateduserstory" + assert data["by"]["id"] == epic.owner.id + assert "date" in data + assert data["data"]["id"] == obj.id + + +def test_webhooks_when_update_epic_related_userstory(settings): + settings.WEBHOOKS_ENABLED = True + project = f.ProjectFactory() + f.WebhookFactory.create(project=project) + f.WebhookFactory.create(project=project) + + epic = f.EpicFactory.create(project=project) + obj = f.RelatedUserStory.create(epic=epic, order=33) + + with patch('taiga.webhooks.tasks._send_request') as send_request_mock: + services.take_snapshot(obj, user=epic.owner) + assert send_request_mock.call_count == 2 + + obj.order = 66 + obj.save() + + with patch('taiga.webhooks.tasks._send_request') as send_request_mock: + services.take_snapshot(obj, user=epic.owner) + assert send_request_mock.call_count == 2 + + (webhook_id, url, key, data) = send_request_mock.call_args[0] + assert data["action"] == "change" + assert data["type"] == "relateduserstory" + assert data["by"]["id"] == epic.owner.id + assert "date" in data + assert data["data"]["id"] == obj.id + assert data["data"]["order"] == obj.order + assert data["change"]["diff"]["order"]["to"] == 66 + assert data["change"]["diff"]["order"]["from"] == 33 + + +def test_webhooks_when_delete_epic_related_userstory(settings): + settings.WEBHOOKS_ENABLED = True + project = f.ProjectFactory() + f.WebhookFactory.create(project=project) + f.WebhookFactory.create(project=project) + + epic = f.EpicFactory.create(project=project) + obj = f.RelatedUserStory.create(epic=epic, order=33) + + with patch('taiga.webhooks.tasks._send_request') as send_request_mock: + services.take_snapshot(obj, user=epic.owner, delete=True) + assert send_request_mock.call_count == 2 + + (webhook_id, url, key, data) = send_request_mock.call_args[0] + assert data["action"] == "delete" + assert data["type"] == "relateduserstory" + assert data["by"]["id"] == obj.owner.id + assert "date" in data + assert "data" in data