Support for updating epic status via commit message

remotes/origin/issue/4795/notification_even_they_are_disabled
Alejandro Alonso 2016-09-15 12:20:48 +02:00
parent 564cd43504
commit 4e50135234
8 changed files with 176 additions and 13 deletions

View File

@ -20,7 +20,8 @@ import re
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
from django.contrib.auth import get_user_model 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.issues.models import Issue
from taiga.projects.tasks.models import Task from taiga.projects.tasks.models import Task
from taiga.projects.userstories.models import UserStory from taiga.projects.userstories.models import UserStory
@ -189,7 +190,10 @@ class BasePushEventHook(BaseEventHook):
return _simple_status_change_message.format(platform=self.platform) return _simple_status_change_message.format(platform=self.platform)
def get_item_classes(self, ref): 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 modelClass = Issue
statusClass = IssueStatus statusClass = IssueStatus
elif Task.objects.filter(project=self.project, ref=ref).exists(): elif Task.objects.filter(project=self.project, ref=ref).exists():

View File

@ -115,7 +115,11 @@ class RelatedUserStory(WatchedModelMixin, models.Model):
@property @property
def project_id(self): def project_id(self):
return self.epic.project_id return self.epic.project_id
@property
def owner(self):
return self.epic.owner
@property @property
def owner_id(self): def owner_id(self):
return self.epic.owner_id return self.epic.owner_id

View File

@ -80,7 +80,7 @@ _values_impl_map = {}
# Not important fields for models (history entries with only # Not important fields for models (history entries with only
# this fields are marked as hidden). # this fields are marked as hidden).
_not_important_fields = { _not_important_fields = {
"epics.epic": frozenset(["epics_order"]), "epics.epic": frozenset(["epics_order", "user_stories"]),
"userstories.userstory": frozenset(["backlog_order", "sprint_order", "kanban_order"]), "userstories.userstory": frozenset(["backlog_order", "sprint_order", "kanban_order"]),
"tasks.task": frozenset(["us_order", "taskboard_order"]), "tasks.task": frozenset(["us_order", "taskboard_order"]),
} }

View File

@ -242,31 +242,31 @@ def test_epic_related_userstories_create(client, data):
] ]
create_data = json.dumps({ create_data = json.dumps({
"user_story": data.public_us.id, "user_story": f.UserStoryFactory(project=data.public_project).id,
"epic": data.public_epic.id "epic": data.public_epic.id
}) })
url = reverse('epics-related-userstories-list', args=[data.public_epic.pk]) url = reverse('epics-related-userstories-list', args=[data.public_epic.pk])
results = helper_test_http_method(client, 'post', url, create_data, users) 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({ create_data = json.dumps({
"user_story": data.private_us1.id, "user_story": f.UserStoryFactory(project=data.private_project1).id,
"epic": data.private_epic1.id "epic": data.private_epic1.id
}) })
url = reverse('epics-related-userstories-list', args=[data.private_epic1.pk]) url = reverse('epics-related-userstories-list', args=[data.private_epic1.pk])
results = helper_test_http_method(client, 'post', url, create_data, users) 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({ create_data = json.dumps({
"user_story": data.private_us2.id, "user_story": f.UserStoryFactory(project=data.private_project2).id,
"epic": data.private_epic2.id "epic": data.private_epic2.id
}) })
url = reverse('epics-related-userstories-list', args=[data.private_epic2.pk]) url = reverse('epics-related-userstories-list', args=[data.private_epic2.pk])
results = helper_test_http_method(client, 'post', url, create_data, users) 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({ create_data = json.dumps({
"user_story": data.blocked_us.id, "user_story": f.UserStoryFactory(project=data.blocked_project).id,
"epic": data.blocked_epic.id "epic": data.blocked_epic.id
}) })
url = reverse('epics-related-userstories-list', args=[data.blocked_epic.pk]) 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({ 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) results = helper_test_http_method(client, 'post', public_url, bulk_data, users)
assert results == [401, 403, 403, 200, 200] 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) results = helper_test_http_method(client, 'post', private_url1, bulk_data, users)
assert results == [401, 403, 403, 200, 200] 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) results = helper_test_http_method(client, 'post', private_url2, bulk_data, users)
assert results == [401, 403, 403, 200, 200] 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) results = helper_test_http_method(client, 'post', blocked_url, bulk_data, users)
assert results == [401, 403, 403, 451, 451] assert results == [401, 403, 403, 451, 451]

View File

@ -31,6 +31,7 @@ from taiga.hooks.bitbucket import event_hooks
from taiga.hooks.bitbucket.api import BitBucketViewSet from taiga.hooks.bitbucket.api import BitBucketViewSet
from taiga.hooks.exceptions import ActionSyntaxException from taiga.hooks.exceptions import ActionSyntaxException
from taiga.projects import choices as project_choices 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.issues.models import Issue
from taiga.projects.tasks.models import Task from taiga.projects.tasks.models import Task
from taiga.projects.userstories.models import UserStory from taiga.projects.userstories.models import UserStory
@ -239,6 +240,38 @@ def test_push_event_detected(client):
assert response.status_code == 204 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): def test_push_event_issue_processing(client):
creation_status = f.IssueStatusFactory() creation_status = f.IssueStatusFactory()
role = f.RoleFactory(project=creation_status.project, permissions=["view_issues"]) 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 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): def test_push_event_us_bad_processing_non_existing_status(client):
user_story = f.UserStoryFactory.create() user_story = f.UserStoryFactory.create()
payload = { payload = {

View File

@ -29,6 +29,7 @@ from taiga.hooks.github import event_hooks
from taiga.hooks.github.api import GitHubViewSet from taiga.hooks.github.api import GitHubViewSet
from taiga.hooks.exceptions import ActionSyntaxException from taiga.hooks.exceptions import ActionSyntaxException
from taiga.projects import choices as project_choices 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.issues.models import Issue
from taiga.projects.tasks.models import Task from taiga.projects.tasks.models import Task
from taiga.projects.userstories.models import UserStory from taiga.projects.userstories.models import UserStory
@ -111,6 +112,26 @@ def test_push_event_detected(client):
assert response.status_code == 204 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): def test_push_event_issue_processing(client):
creation_status = f.IssueStatusFactory() creation_status = f.IssueStatusFactory()
role = f.RoleFactory(project=creation_status.project, permissions=["view_issues"]) role = f.RoleFactory(project=creation_status.project, permissions=["view_issues"])

View File

@ -30,6 +30,7 @@ from taiga.hooks.gitlab import event_hooks
from taiga.hooks.gitlab.api import GitLabViewSet from taiga.hooks.gitlab.api import GitLabViewSet
from taiga.hooks.exceptions import ActionSyntaxException from taiga.hooks.exceptions import ActionSyntaxException
from taiga.projects import choices as project_choices 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.issues.models import Issue
from taiga.projects.tasks.models import Task from taiga.projects.tasks.models import Task
from taiga.projects.userstories.models import UserStory from taiga.projects.userstories.models import UserStory
@ -446,6 +447,30 @@ def test_push_event_detected(client):
assert response.status_code == 204 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): def test_push_event_issue_processing(client):
creation_status = f.IssueStatusFactory() creation_status = f.IssueStatusFactory()
role = f.RoleFactory(project=creation_status.project, permissions=["view_issues"]) role = f.RoleFactory(project=creation_status.project, permissions=["view_issues"])

View File

@ -29,6 +29,7 @@ from taiga.hooks.gogs import event_hooks
from taiga.hooks.gogs.api import GogsViewSet from taiga.hooks.gogs.api import GogsViewSet
from taiga.hooks.exceptions import ActionSyntaxException from taiga.hooks.exceptions import ActionSyntaxException
from taiga.projects import choices as project_choices 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.issues.models import Issue
from taiga.projects.tasks.models import Task from taiga.projects.tasks.models import Task
from taiga.projects.userstories.models import UserStory from taiga.projects.userstories.models import UserStory
@ -120,6 +121,36 @@ def test_push_event_detected(client):
assert response.status_code == 204 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): def test_push_event_issue_processing(client):
creation_status = f.IssueStatusFactory() creation_status = f.IssueStatusFactory()
role = f.RoleFactory(project=creation_status.project, permissions=["view_issues"]) role = f.RoleFactory(project=creation_status.project, permissions=["view_issues"])