From 4e5013523482a4a27ebb557c0fe40c70b1b7c5b9 Mon Sep 17 00:00:00 2001 From: Alejandro Alonso Date: Thu, 15 Sep 2016 12:20:48 +0200 Subject: [PATCH] Support for updating epic status via commit message --- taiga/hooks/event_hooks.py | 8 ++- taiga/projects/epics/models.py | 6 +- taiga/projects/history/services.py | 2 +- ...est_epics_related_userstories_resources.py | 33 +++++++--- tests/integration/test_hooks_bitbucket.py | 63 +++++++++++++++++++ tests/integration/test_hooks_github.py | 21 +++++++ tests/integration/test_hooks_gitlab.py | 25 ++++++++ tests/integration/test_hooks_gogs.py | 31 +++++++++ 8 files changed, 176 insertions(+), 13 deletions(-) diff --git a/taiga/hooks/event_hooks.py b/taiga/hooks/event_hooks.py index 85ac892f..93deb518 100644 --- a/taiga/hooks/event_hooks.py +++ b/taiga/hooks/event_hooks.py @@ -20,7 +20,8 @@ import re from django.utils.translation import ugettext as _ from django.contrib.auth import get_user_model -from taiga.projects.models import IssueStatus, TaskStatus, UserStoryStatus +from taiga.projects.models import IssueStatus, TaskStatus, UserStoryStatus, EpicStatus +from taiga.projects.epics.models import Epic from taiga.projects.issues.models import Issue from taiga.projects.tasks.models import Task from taiga.projects.userstories.models import UserStory @@ -189,7 +190,10 @@ class BasePushEventHook(BaseEventHook): return _simple_status_change_message.format(platform=self.platform) def get_item_classes(self, ref): - if Issue.objects.filter(project=self.project, ref=ref).exists(): + if Epic.objects.filter(project=self.project, ref=ref).exists(): + modelClass = Epic + statusClass = EpicStatus + elif Issue.objects.filter(project=self.project, ref=ref).exists(): modelClass = Issue statusClass = IssueStatus elif Task.objects.filter(project=self.project, ref=ref).exists(): diff --git a/taiga/projects/epics/models.py b/taiga/projects/epics/models.py index 09003097..c2e26d20 100644 --- a/taiga/projects/epics/models.py +++ b/taiga/projects/epics/models.py @@ -115,7 +115,11 @@ class RelatedUserStory(WatchedModelMixin, models.Model): @property def project_id(self): return self.epic.project_id - + + @property + def owner(self): + return self.epic.owner + @property def owner_id(self): return self.epic.owner_id diff --git a/taiga/projects/history/services.py b/taiga/projects/history/services.py index 7247ccdd..1bf27dee 100644 --- a/taiga/projects/history/services.py +++ b/taiga/projects/history/services.py @@ -80,7 +80,7 @@ _values_impl_map = {} # Not important fields for models (history entries with only # this fields are marked as hidden). _not_important_fields = { - "epics.epic": frozenset(["epics_order"]), + "epics.epic": frozenset(["epics_order", "user_stories"]), "userstories.userstory": frozenset(["backlog_order", "sprint_order", "kanban_order"]), "tasks.task": frozenset(["us_order", "taskboard_order"]), } diff --git a/tests/integration/resources_permissions/test_epics_related_userstories_resources.py b/tests/integration/resources_permissions/test_epics_related_userstories_resources.py index d1924ec8..04dd28c1 100644 --- a/tests/integration/resources_permissions/test_epics_related_userstories_resources.py +++ b/tests/integration/resources_permissions/test_epics_related_userstories_resources.py @@ -242,31 +242,31 @@ def test_epic_related_userstories_create(client, data): ] create_data = json.dumps({ - "user_story": data.public_us.id, + "user_story": f.UserStoryFactory(project=data.public_project).id, "epic": data.public_epic.id }) url = reverse('epics-related-userstories-list', args=[data.public_epic.pk]) results = helper_test_http_method(client, 'post', url, create_data, users) - assert results == [401, 403, 403, 201, 201] + assert results == [401, 403, 403, 201, 400] create_data = json.dumps({ - "user_story": data.private_us1.id, + "user_story": f.UserStoryFactory(project=data.private_project1).id, "epic": data.private_epic1.id }) url = reverse('epics-related-userstories-list', args=[data.private_epic1.pk]) results = helper_test_http_method(client, 'post', url, create_data, users) - assert results == [401, 403, 403, 201, 201] + assert results == [401, 403, 403, 201, 400] create_data = json.dumps({ - "user_story": data.private_us2.id, + "user_story": f.UserStoryFactory(project=data.private_project2).id, "epic": data.private_epic2.id }) url = reverse('epics-related-userstories-list', args=[data.private_epic2.pk]) results = helper_test_http_method(client, 'post', url, create_data, users) - assert results == [401, 403, 403, 201, 201] + assert results == [401, 403, 403, 201, 400] create_data = json.dumps({ - "user_story": data.blocked_us.id, + "user_story": f.UserStoryFactory(project=data.blocked_project).id, "epic": data.blocked_epic.id }) url = reverse('epics-related-userstories-list', args=[data.blocked_epic.pk]) @@ -379,14 +379,29 @@ def test_bulk_create_related_userstories(client, data): ] bulk_data = json.dumps({ - "userstories": "test1\ntest2", + "bulk_userstories": "test1\ntest2", + "project_id": data.public_project.id }) - results = helper_test_http_method(client, 'post', public_url, bulk_data, users) assert results == [401, 403, 403, 200, 200] + + bulk_data = json.dumps({ + "bulk_userstories": "test1\ntest2", + "project_id": data.private_project1.id + }) results = helper_test_http_method(client, 'post', private_url1, bulk_data, users) assert results == [401, 403, 403, 200, 200] + + bulk_data = json.dumps({ + "bulk_userstories": "test1\ntest2", + "project_id": data.private_project2.id + }) results = helper_test_http_method(client, 'post', private_url2, bulk_data, users) assert results == [401, 403, 403, 200, 200] + + bulk_data = json.dumps({ + "bulk_userstories": "test1\ntest2", + "project_id": data.blocked_project.id + }) results = helper_test_http_method(client, 'post', blocked_url, bulk_data, users) assert results == [401, 403, 403, 451, 451] diff --git a/tests/integration/test_hooks_bitbucket.py b/tests/integration/test_hooks_bitbucket.py index 90023b38..6dbe06c3 100644 --- a/tests/integration/test_hooks_bitbucket.py +++ b/tests/integration/test_hooks_bitbucket.py @@ -31,6 +31,7 @@ from taiga.hooks.bitbucket import event_hooks from taiga.hooks.bitbucket.api import BitBucketViewSet from taiga.hooks.exceptions import ActionSyntaxException from taiga.projects import choices as project_choices +from taiga.projects.epics.models import Epic from taiga.projects.issues.models import Issue from taiga.projects.tasks.models import Task from taiga.projects.userstories.models import UserStory @@ -239,6 +240,38 @@ def test_push_event_detected(client): assert response.status_code == 204 +def test_push_event_epic_processing(client): + creation_status = f.EpicStatusFactory() + role = f.RoleFactory(project=creation_status.project, permissions=["view_epics"]) + f.MembershipFactory(project=creation_status.project, role=role, user=creation_status.project.owner) + new_status = f.EpicStatusFactory(project=creation_status.project) + epic = f.EpicFactory.create(status=creation_status, project=creation_status.project, owner=creation_status.project.owner) + payload = { + "actor": { + "user": { + "uuid": "{ce1054cd-3f43-49dc-8aea-d3085ee7ec9b}", + "username": "test-user", + "links": {"html": {"href": "http://bitbucket.com/test-user"}} + } + }, + "push": { + "changes": [ + { + "commits": [ + { "message": "test message test TG-%s #%s ok bye!" % (epic.ref, new_status.slug) } + ] + } + ] + } + } + mail.outbox = [] + ev_hook = event_hooks.PushEventHook(epic.project, payload) + ev_hook.process_event() + epic = Epic.objects.get(id=epic.id) + assert epic.status.id == new_status.id + assert len(mail.outbox) == 1 + + def test_push_event_issue_processing(client): creation_status = f.IssueStatusFactory() role = f.RoleFactory(project=creation_status.project, permissions=["view_issues"]) @@ -534,6 +567,36 @@ def test_push_event_task_bad_processing_non_existing_ref(client): assert len(mail.outbox) == 0 +def test_push_event_task_bad_processing_non_existing_ref(client): + issue_status = f.IssueStatusFactory() + payload = { + "actor": { + "user": { + "uuid": "{ce1054cd-3f43-49dc-8aea-d3085ee7ec9b}", + "username": "test-user", + "links": {"html": {"href": "http://bitbucket.com/test-user"}} + } + }, + "push": { + "changes": [ + { + "commits": [ + { "message": "test message test TG-6666666 #%s ok bye!" % (issue_status.slug) } + ] + } + ] + } + } + mail.outbox = [] + + ev_hook = event_hooks.PushEventHook(issue_status.project, payload) + with pytest.raises(ActionSyntaxException) as excinfo: + ev_hook.process_event() + + assert str(excinfo.value) == "The referenced element doesn't exist" + assert len(mail.outbox) == 0 + + def test_push_event_us_bad_processing_non_existing_status(client): user_story = f.UserStoryFactory.create() payload = { diff --git a/tests/integration/test_hooks_github.py b/tests/integration/test_hooks_github.py index 815aba49..1bcbcdbe 100644 --- a/tests/integration/test_hooks_github.py +++ b/tests/integration/test_hooks_github.py @@ -29,6 +29,7 @@ from taiga.hooks.github import event_hooks from taiga.hooks.github.api import GitHubViewSet from taiga.hooks.exceptions import ActionSyntaxException from taiga.projects import choices as project_choices +from taiga.projects.epics.models import Epic from taiga.projects.issues.models import Issue from taiga.projects.tasks.models import Task from taiga.projects.userstories.models import UserStory @@ -111,6 +112,26 @@ def test_push_event_detected(client): assert response.status_code == 204 +def test_push_event_epic_processing(client): + creation_status = f.EpicStatusFactory() + role = f.RoleFactory(project=creation_status.project, permissions=["view_epics"]) + f.MembershipFactory(project=creation_status.project, role=role, user=creation_status.project.owner) + new_status = f.EpicStatusFactory(project=creation_status.project) + epic = f.EpicFactory.create(status=creation_status, project=creation_status.project, owner=creation_status.project.owner) + payload = {"commits": [ + {"message": """test message + test TG-%s #%s ok + bye! + """ % (epic.ref, new_status.slug)}, + ]} + mail.outbox = [] + ev_hook = event_hooks.PushEventHook(epic.project, payload) + ev_hook.process_event() + epic = Epic.objects.get(id=epic.id) + assert epic.status.id == new_status.id + assert len(mail.outbox) == 1 + + def test_push_event_issue_processing(client): creation_status = f.IssueStatusFactory() role = f.RoleFactory(project=creation_status.project, permissions=["view_issues"]) diff --git a/tests/integration/test_hooks_gitlab.py b/tests/integration/test_hooks_gitlab.py index 5208a939..b40c94e6 100644 --- a/tests/integration/test_hooks_gitlab.py +++ b/tests/integration/test_hooks_gitlab.py @@ -30,6 +30,7 @@ from taiga.hooks.gitlab import event_hooks from taiga.hooks.gitlab.api import GitLabViewSet from taiga.hooks.exceptions import ActionSyntaxException from taiga.projects import choices as project_choices +from taiga.projects.epics.models import Epic from taiga.projects.issues.models import Issue from taiga.projects.tasks.models import Task from taiga.projects.userstories.models import UserStory @@ -446,6 +447,30 @@ def test_push_event_detected(client): assert response.status_code == 204 +def test_push_event_epic_processing(client): + creation_status = f.EpicStatusFactory() + role = f.RoleFactory(project=creation_status.project, permissions=["view_epics"]) + f.MembershipFactory(project=creation_status.project, role=role, user=creation_status.project.owner) + new_status = f.EpicStatusFactory(project=creation_status.project) + epic = f.EpicFactory.create(status=creation_status, project=creation_status.project, owner=creation_status.project.owner) + payload = deepcopy(push_base_payload) + payload["commits"] = [{ + "message": """test message + test TG-%s #%s ok + bye! + """ % (epic.ref, new_status.slug), + "id": "b6568db1bc1dcd7f8b4d5a946b0b91f9dacd7327", + "url": "http://example.com/mike/diaspora/commit/b6568db1bc1dcd7f8b4d5a946b0b91f9dacd7327", + }] + payload["total_commits_count"] = 1 + mail.outbox = [] + ev_hook = event_hooks.PushEventHook(epic.project, payload) + ev_hook.process_event() + epic = Epic.objects.get(id=epic.id) + assert epic.status.id == new_status.id + assert len(mail.outbox) == 1 + + def test_push_event_issue_processing(client): creation_status = f.IssueStatusFactory() role = f.RoleFactory(project=creation_status.project, permissions=["view_issues"]) diff --git a/tests/integration/test_hooks_gogs.py b/tests/integration/test_hooks_gogs.py index 290bc3f8..46c33dd9 100644 --- a/tests/integration/test_hooks_gogs.py +++ b/tests/integration/test_hooks_gogs.py @@ -29,6 +29,7 @@ from taiga.hooks.gogs import event_hooks from taiga.hooks.gogs.api import GogsViewSet from taiga.hooks.exceptions import ActionSyntaxException from taiga.projects import choices as project_choices +from taiga.projects.epics.models import Epic from taiga.projects.issues.models import Issue from taiga.projects.tasks.models import Task from taiga.projects.userstories.models import UserStory @@ -120,6 +121,36 @@ def test_push_event_detected(client): assert response.status_code == 204 +def test_push_event_epic_processing(client): + creation_status = f.EpicStatusFactory() + role = f.RoleFactory(project=creation_status.project, permissions=["view_epics"]) + f.MembershipFactory(project=creation_status.project, role=role, user=creation_status.project.owner) + new_status = f.EpicStatusFactory(project=creation_status.project) + epic = f.EpicFactory.create(status=creation_status, project=creation_status.project, owner=creation_status.project.owner) + payload = { + "commits": [ + { + "message": """test message + test TG-%s #%s ok + bye! + """ % (epic.ref, new_status.slug), + "author": { + "username": "test", + }, + } + ], + "repository": { + "url": "http://test-url/test/project" + } + } + mail.outbox = [] + ev_hook = event_hooks.PushEventHook(epic.project, payload) + ev_hook.process_event() + epic = Epic.objects.get(id=epic.id) + assert epic.status.id == new_status.id + assert len(mail.outbox) == 1 + + def test_push_event_issue_processing(client): creation_status = f.IssueStatusFactory() role = f.RoleFactory(project=creation_status.project, permissions=["view_issues"])