From 3bae896199d260188e087ea65d3d29157e7798c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Espino?= Date: Tue, 30 Jun 2015 17:30:06 +0200 Subject: [PATCH] Add gitlab integration with commets webhook --- CHANGELOG.md | 1 + taiga/hooks/gitlab/api.py | 1 + taiga/hooks/gitlab/event_hooks.py | 47 ++++++++- tests/integration/test_hooks_gitlab.py | 129 +++++++++++++++++++++++++ 4 files changed, 177 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 924f2ebb..d22da965 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ - Now every user that coments USs, Issues or Tasks will be involved in it (add author to the watchers list). - Fix the compatibility with BitBucket webhooks and add issues and issues comments integration. - Add custom videoconference system. +- Add support for comments in the Gitlab webhooks integration. ### Misc - API: Mixin fields 'users', 'members' and 'memberships' in ProjectDetailSerializer diff --git a/taiga/hooks/gitlab/api.py b/taiga/hooks/gitlab/api.py index 48d70fe7..03ae3a06 100644 --- a/taiga/hooks/gitlab/api.py +++ b/taiga/hooks/gitlab/api.py @@ -30,6 +30,7 @@ class GitLabViewSet(BaseWebhookApiViewSet): event_hook_classes = { "push": event_hooks.PushEventHook, "issue": event_hooks.IssuesEventHook, + "note": event_hooks.IssueCommentEventHook, } def _validate_signature(self, project, request): diff --git a/taiga/hooks/gitlab/event_hooks.py b/taiga/hooks/gitlab/event_hooks.py index 84079121..fdc83066 100644 --- a/taiga/hooks/gitlab/event_hooks.py +++ b/taiga/hooks/gitlab/event_hooks.py @@ -89,7 +89,7 @@ class PushEventHook(BaseEventHook): def replace_gitlab_references(project_url, wiki_text): - if wiki_text == None: + if wiki_text is None: wiki_text = "" template = "\g<1>[GitLab#\g<2>]({}/issues/\g<2>)\g<3>".format(project_url) @@ -127,3 +127,48 @@ class IssuesEventHook(BaseEventHook): snapshot = take_snapshot(issue, comment=_("Created from GitLab"), user=get_gitlab_user(None)) send_notifications(issue, history=snapshot) + + +class IssueCommentEventHook(BaseEventHook): + def process_event(self): + if self.payload.get('object_attributes', {}).get("noteable_type", None) != "Issue": + return + + number = self.payload.get('issue', {}).get('iid', None) + subject = self.payload.get('issue', {}).get('title', None) + + project_url = self.payload.get('repository', {}).get('homepage', None) + + gitlab_url = os.path.join(project_url, "issues", str(number)) + gitlab_user_name = self.payload.get('user', {}).get('username', None) + gitlab_user_url = os.path.join(os.path.dirname(os.path.dirname(project_url)), "u", gitlab_user_name) + + comment_message = self.payload.get('object_attributes', {}).get('note', None) + comment_message = replace_gitlab_references(project_url, comment_message) + + user = get_gitlab_user(None) + + if not all([comment_message, gitlab_url, project_url]): + raise ActionSyntaxException(_("Invalid issue comment information")) + + issues = Issue.objects.filter(external_reference=["gitlab", gitlab_url]) + tasks = Task.objects.filter(external_reference=["gitlab", gitlab_url]) + uss = UserStory.objects.filter(external_reference=["gitlab", gitlab_url]) + + for item in list(issues) + list(tasks) + list(uss): + if number and subject and gitlab_user_name and gitlab_user_url: + comment = _("Comment by [@{gitlab_user_name}]({gitlab_user_url} " + "\"See @{gitlab_user_name}'s GitLab profile\") " + "from GitLab.\nOrigin GitLab issue: [gh#{number} - {subject}]({gitlab_url} " + "\"Go to 'gh#{number} - {subject}'\")\n\n" + "{message}").format(gitlab_user_name=gitlab_user_name, + gitlab_user_url=gitlab_user_url, + number=number, + subject=subject, + gitlab_url=gitlab_url, + message=comment_message) + else: + comment = _("Comment From GitLab:\n\n{message}").format(message=comment_message) + + snapshot = take_snapshot(item, comment=comment, user=user) + send_notifications(item, history=snapshot) diff --git a/tests/integration/test_hooks_gitlab.py b/tests/integration/test_hooks_gitlab.py index 39aa5485..eac03668 100644 --- a/tests/integration/test_hooks_gitlab.py +++ b/tests/integration/test_hooks_gitlab.py @@ -13,6 +13,7 @@ from taiga.projects.issues.models import Issue from taiga.projects.tasks.models import Task from taiga.projects.userstories.models import UserStory from taiga.projects.models import Membership +from taiga.projects.history.services import get_history_queryset_by_model_instance, take_snapshot from taiga.projects.notifications.choices import NotifyLevel from taiga.projects.notifications.models import NotifyPolicy from taiga.projects import services @@ -384,6 +385,134 @@ def test_issues_event_bad_issue(client): assert len(mail.outbox) == 0 +def test_issue_comment_event_on_existing_issue_task_and_us(client): + project = f.ProjectFactory() + role = f.RoleFactory(project=project, permissions=["view_tasks", "view_issues", "view_us"]) + f.MembershipFactory(project=project, role=role, user=project.owner) + user = f.UserFactory() + + issue = f.IssueFactory.create(external_reference=["gitlab", "http://gitlab.com/test/project/issues/11"], owner=project.owner, project=project) + take_snapshot(issue, user=user) + task = f.TaskFactory.create(external_reference=["gitlab", "http://gitlab.com/test/project/issues/11"], owner=project.owner, project=project) + take_snapshot(task, user=user) + us = f.UserStoryFactory.create(external_reference=["gitlab", "http://gitlab.com/test/project/issues/11"], owner=project.owner, project=project) + take_snapshot(us, user=user) + + payload = { + "user": { + "username": "test" + }, + "issue": { + "iid": "11", + "title": "test-title", + }, + "object_attributes": { + "noteable_type": "Issue", + "note": "Test body", + }, + "repository": { + "homepage": "http://gitlab.com/test/project", + }, + } + + + mail.outbox = [] + + assert get_history_queryset_by_model_instance(issue).count() == 0 + assert get_history_queryset_by_model_instance(task).count() == 0 + assert get_history_queryset_by_model_instance(us).count() == 0 + + ev_hook = event_hooks.IssueCommentEventHook(issue.project, payload) + ev_hook.process_event() + + issue_history = get_history_queryset_by_model_instance(issue) + assert issue_history.count() == 1 + assert "Test body" in issue_history[0].comment + + task_history = get_history_queryset_by_model_instance(task) + assert task_history.count() == 1 + assert "Test body" in issue_history[0].comment + + us_history = get_history_queryset_by_model_instance(us) + assert us_history.count() == 1 + assert "Test body" in issue_history[0].comment + + assert len(mail.outbox) == 3 + + +def test_issue_comment_event_on_not_existing_issue_task_and_us(client): + issue = f.IssueFactory.create(external_reference=["gitlab", "10"]) + take_snapshot(issue, user=issue.owner) + task = f.TaskFactory.create(project=issue.project, external_reference=["gitlab", "10"]) + take_snapshot(task, user=task.owner) + us = f.UserStoryFactory.create(project=issue.project, external_reference=["gitlab", "10"]) + take_snapshot(us, user=us.owner) + + payload = { + "user": { + "username": "test" + }, + "issue": { + "iid": "99999", + "title": "test-title", + }, + "object_attributes": { + "noteable_type": "Issue", + "note": "test comment", + }, + "repository": { + "homepage": "test", + }, + } + + mail.outbox = [] + + assert get_history_queryset_by_model_instance(issue).count() == 0 + assert get_history_queryset_by_model_instance(task).count() == 0 + assert get_history_queryset_by_model_instance(us).count() == 0 + + ev_hook = event_hooks.IssueCommentEventHook(issue.project, payload) + ev_hook.process_event() + + assert get_history_queryset_by_model_instance(issue).count() == 0 + assert get_history_queryset_by_model_instance(task).count() == 0 + assert get_history_queryset_by_model_instance(us).count() == 0 + + assert len(mail.outbox) == 0 + + +def test_issues_event_bad_comment(client): + issue = f.IssueFactory.create(external_reference=["gitlab", "10"]) + take_snapshot(issue, user=issue.owner) + + payload = { + "user": { + "username": "test" + }, + "issue": { + "iid": "10", + "title": "test-title", + }, + "object_attributes": { + "noteable_type": "Issue", + }, + "repository": { + "homepage": "test", + }, + } + ev_hook = event_hooks.IssueCommentEventHook(issue.project, payload) + + mail.outbox = [] + + with pytest.raises(ActionSyntaxException) as excinfo: + ev_hook.process_event() + + assert str(excinfo.value) == "Invalid issue comment information" + + assert Issue.objects.count() == 1 + assert len(mail.outbox) == 0 + + def test_api_get_project_modules(client): project = f.create_project() f.MembershipFactory(project=project, user=project.owner, is_owner=True)