webhooks/bitbucket2: Include title in message body if not in topic.

This is a follow-up in response to Tim's comments on #9951.

In instances where all messages from a BitBucket integration are
grouped under one user specified topic (specified in the URL), we
should include the title of the PR in the message body, since
the availability of a user-specified topic precludes us from
including it in the topic itself (which was the default behaviour).
This commit is contained in:
Eeshan Garg 2018-07-24 20:27:45 -02:30 committed by Tim Abbott
parent 85b555b1a8
commit 4a54e8ac17
2 changed files with 104 additions and 27 deletions

View File

@ -91,6 +91,12 @@ class Bitbucket2HookTests(WebhookTestCase):
expected_message = u"kolaszek created [Issue #1](https://bitbucket.org/kolaszek/repository-name/issues/2/bug)(assigned to kolaszek)\n\n~~~ quote\nSuch a bug\n~~~"
self.send_and_test_stream_message('issue_created', self.EXPECTED_SUBJECT_ISSUE_EVENTS, expected_message)
def test_bitbucket2_on_issue_created_with_custom_topic_in_url(self) -> None:
self.url = self.build_webhook_url(topic="notifications")
expected_subject = u"notifications"
expected_message = u"kolaszek created [Issue #1 Bug](https://bitbucket.org/kolaszek/repository-name/issues/2/bug)(assigned to kolaszek)\n\n~~~ quote\nSuch a bug\n~~~"
self.send_and_test_stream_message('issue_created', expected_subject, expected_message)
def test_bitbucket2_on_issue_updated_event(self) -> None:
expected_message = u"kolaszek updated [Issue #1](https://bitbucket.org/kolaszek/repository-name/issues/2/bug)"
self.send_and_test_stream_message('issue_updated', self.EXPECTED_SUBJECT_ISSUE_EVENTS, expected_message)
@ -99,6 +105,12 @@ class Bitbucket2HookTests(WebhookTestCase):
expected_message = u"kolaszek [commented](https://bitbucket.org/kolaszek/repository-name/issues/2#comment-28973596) on [Issue #1](https://bitbucket.org/kolaszek/repository-name/issues/2/bug)"
self.send_and_test_stream_message('issue_commented', self.EXPECTED_SUBJECT_ISSUE_EVENTS, expected_message)
def test_bitbucket2_on_issue_commented_with_custom_topic_in_url(self) -> None:
self.url = self.build_webhook_url(topic="notifications")
expected_subject = u"notifications"
expected_message = u"kolaszek [commented](https://bitbucket.org/kolaszek/repository-name/issues/2#comment-28973596) on [Issue #1 Bug](https://bitbucket.org/kolaszek/repository-name/issues/2/bug)"
self.send_and_test_stream_message('issue_commented', expected_subject, expected_message)
def test_bitbucket2_on_pull_request_created_event(self) -> None:
expected_message = u"kolaszek created [PR #1](https://bitbucket.org/kolaszek/repository-name/pull-requests/1)(assigned to tkolek)\nfrom `new-branch` to `master`\n\n~~~ quote\ndescription\n~~~"
kwargs = {
@ -106,6 +118,15 @@ class Bitbucket2HookTests(WebhookTestCase):
}
self.send_and_test_stream_message('pull_request_created_or_updated', self.EXPECTED_SUBJECT_PR_EVENTS, expected_message, **kwargs)
def test_bitbucket2_on_pull_request_created_with_custom_topic_in_url(self) -> None:
self.url = self.build_webhook_url(topic="notifications")
expected_subject = u"notifications"
expected_message = u"kolaszek created [PR #1 new commit](https://bitbucket.org/kolaszek/repository-name/pull-requests/1)(assigned to tkolek)\nfrom `new-branch` to `master`\n\n~~~ quote\ndescription\n~~~"
kwargs = {
"HTTP_X_EVENT_KEY": 'pullrequest:created'
}
self.send_and_test_stream_message('pull_request_created_or_updated', expected_subject, expected_message, **kwargs)
def test_bitbucket2_on_pull_request_updated_event(self) -> None:
expected_message = u"kolaszek updated [PR #1](https://bitbucket.org/kolaszek/repository-name/pull-requests/1)(assigned to tkolek)\nfrom `new-branch` to `master`\n\n~~~ quote\ndescription\n~~~"
kwargs = {
@ -120,6 +141,15 @@ class Bitbucket2HookTests(WebhookTestCase):
}
self.send_and_test_stream_message('pull_request_approved_or_unapproved', self.EXPECTED_SUBJECT_PR_EVENTS, expected_message, **kwargs)
def test_bitbucket2_on_pull_request_approved_with_custom_topic_in_url(self) -> None:
self.url = self.build_webhook_url(topic="notifications")
expected_subject = u"notifications"
expected_message = u"kolaszek approved [PR #1 new commit](https://bitbucket.org/kolaszek/repository-name/pull-requests/1)"
kwargs = {
"HTTP_X_EVENT_KEY": 'pullrequest:approved'
}
self.send_and_test_stream_message('pull_request_approved_or_unapproved', expected_subject, expected_message, **kwargs)
def test_bitbucket2_on_pull_request_unapproved_event(self) -> None:
expected_message = u"kolaszek unapproved [PR #1](https://bitbucket.org/kolaszek/repository-name/pull-requests/1)"
kwargs = {
@ -148,6 +178,15 @@ class Bitbucket2HookTests(WebhookTestCase):
}
self.send_and_test_stream_message('pull_request_comment_action', self.EXPECTED_SUBJECT_PR_EVENTS, expected_message, **kwargs)
def test_bitbucket2_on_pull_request_comment_created_with_custom_topic_in_url(self) -> None:
self.url = self.build_webhook_url(topic="notifications")
expected_subject = u"notifications"
expected_message = u"kolaszek [commented](https://bitbucket.org/kolaszek/repository-name/pull-requests/3/_/diff#comment-20576503) on [PR #1 new commit](https://bitbucket.org/kolaszek/repository-name/pull-requests/3)\n\n~~~ quote\nComment1\n~~~"
kwargs = {
"HTTP_X_EVENT_KEY": 'pullrequest:comment_created'
}
self.send_and_test_stream_message('pull_request_comment_action', expected_subject, expected_message, **kwargs)
def test_bitbucket2_on_pull_request_comment_updated_event(self) -> None:
expected_message = u"kolaszek updated a [comment](https://bitbucket.org/kolaszek/repository-name/pull-requests/3/_/diff#comment-20576503) on [PR #1](https://bitbucket.org/kolaszek/repository-name/pull-requests/3)\n\n~~~ quote\nComment1\n~~~"
kwargs = {
@ -155,6 +194,15 @@ class Bitbucket2HookTests(WebhookTestCase):
}
self.send_and_test_stream_message('pull_request_comment_action', self.EXPECTED_SUBJECT_PR_EVENTS, expected_message, **kwargs)
def test_bitbucket2_on_pull_request_comment_updated_with_custom_topic_in_url(self) -> None:
self.url = self.build_webhook_url(topic="notifications")
expected_subject = u"notifications"
expected_message = u"kolaszek updated a [comment](https://bitbucket.org/kolaszek/repository-name/pull-requests/3/_/diff#comment-20576503) on [PR #1 new commit](https://bitbucket.org/kolaszek/repository-name/pull-requests/3)\n\n~~~ quote\nComment1\n~~~"
kwargs = {
"HTTP_X_EVENT_KEY": 'pullrequest:comment_updated'
}
self.send_and_test_stream_message('pull_request_comment_action', expected_subject, expected_message, **kwargs)
def test_bitbucket2_on_pull_request_comment_deleted_event(self) -> None:
expected_message = u"kolaszek deleted a [comment](https://bitbucket.org/kolaszek/repository-name/pull-requests/3/_/diff#comment-20576503) on [PR #1](https://bitbucket.org/kolaszek/repository-name/pull-requests/3)\n\n~~~ quote\nComment1\n~~~"
kwargs = {

View File

@ -2,6 +2,7 @@
import re
from functools import partial
from typing import Any, Callable, Dict, List, Optional
from inspect import signature
from django.http import HttpRequest, HttpResponse
from django.utils.translation import ugettext as _
@ -45,13 +46,10 @@ PULL_REQUEST_SUPPORTED_ACTIONS = [
@has_request_variables
def api_bitbucket2_webhook(request: HttpRequest, user_profile: UserProfile,
payload: Dict[str, Any]=REQ(argument_type='body'),
branches: Optional[str]=REQ(default=None)) -> HttpResponse:
branches: Optional[str]=REQ(default=None),
user_specified_topic: Optional[str]=REQ("topic", default=None)) -> HttpResponse:
type = get_type(request, payload)
if type != 'push':
subject = get_subject_based_on_type(payload, type)
body = get_body_based_on_type(type)(payload)
check_send_webhook_message(request, user_profile, subject, body)
else:
if type == 'push':
# ignore push events with no changes
if not payload['push']['changes']:
return json_success()
@ -59,10 +57,22 @@ def api_bitbucket2_webhook(request: HttpRequest, user_profile: UserProfile,
if branch and branches:
if branches.find(branch) == -1:
return json_success()
subjects = get_push_subjects(payload)
bodies_list = get_push_bodies(payload)
for body, subject in zip(bodies_list, subjects):
check_send_webhook_message(request, user_profile, subject, body)
subject = get_subject_based_on_type(payload, type)
body_function = get_body_based_on_type(type)
if 'include_title' in signature(body_function).parameters:
body = body_function(
payload,
include_title=user_specified_topic is not None
)
else:
body = body_function(payload)
if type != 'push':
check_send_webhook_message(request, user_profile, subject, body)
else:
for b, s in zip(body, subject):
check_send_webhook_message(request, user_profile, s, b)
return json_success()
@ -91,7 +101,7 @@ def get_subject(payload: Dict[str, Any]) -> str:
assert(payload['repository'] is not None)
return BITBUCKET_SUBJECT_TEMPLATE.format(repository_name=get_repository_name(payload['repository']))
def get_subject_based_on_type(payload: Dict[str, Any], type: str) -> str:
def get_subject_based_on_type(payload: Dict[str, Any], type: str) -> Any:
if type.startswith('pull_request'):
return SUBJECT_WITH_PR_OR_ISSUE_INFO_TEMPLATE.format(
repo=get_repository_name(payload['repository']),
@ -106,6 +116,8 @@ def get_subject_based_on_type(payload: Dict[str, Any], type: str) -> str:
id=payload['issue']['id'],
title=payload['issue']['title']
)
if type == 'push':
return get_push_subjects(payload)
return get_subject(payload)
def get_type(request: HttpRequest, payload: Dict[str, Any]) -> str:
@ -140,9 +152,8 @@ def get_type(request: HttpRequest, payload: Dict[str, Any]) -> str:
raise UnexpectedWebhookEventType('BitBucket2', event_key)
def get_body_based_on_type(type: str) -> Callable[[Dict[str, Any]], str]:
def get_body_based_on_type(type: str) -> Any:
fn = GET_SINGLE_MESSAGE_BODY_DEPENDING_ON_TYPE_MAPPER.get(type)
assert callable(fn) # type parameter should be pre-checked, so not None
return fn
def get_push_bodies(payload: Dict[str, Any]) -> List[str]:
@ -230,11 +241,13 @@ def get_commit_status_changed_body(payload: Dict[str, Any]) -> str:
status=payload['commit_status']['state']
)
def get_issue_commented_body(payload: Dict[str, Any]) -> str:
def get_issue_commented_body(payload: Dict[str, Any],
include_title: Optional[bool]=False) -> str:
action = '[commented]({}) on'.format(payload['comment']['links']['html']['href'])
return get_issue_action_body(payload, action)
return get_issue_action_body(payload, action, include_title)
def get_issue_action_body(payload: Dict[str, Any], action: str) -> str:
def get_issue_action_body(payload: Dict[str, Any], action: str,
include_title: Optional[bool]=False) -> str:
issue = payload['issue']
assignee = None
message = None
@ -249,19 +262,23 @@ def get_issue_action_body(payload: Dict[str, Any], action: str) -> str:
issue['links']['html']['href'],
issue['id'],
message,
assignee
assignee,
title=issue['title'] if include_title else None
)
def get_pull_request_action_body(payload: Dict[str, Any], action: str) -> str:
def get_pull_request_action_body(payload: Dict[str, Any], action: str,
include_title: Optional[bool]=False) -> str:
pull_request = payload['pullrequest']
return get_pull_request_event_message(
get_user_username(payload),
action,
get_pull_request_url(pull_request),
pull_request.get('id')
pull_request.get('id'),
title=pull_request['title'] if include_title else None
)
def get_pull_request_created_or_updated_body(payload: Dict[str, Any], action: str) -> str:
def get_pull_request_created_or_updated_body(payload: Dict[str, Any], action: str,
include_title: Optional[bool]=False) -> str:
pull_request = payload['pullrequest']
assignee = None
if pull_request.get('reviewers'):
@ -275,25 +292,36 @@ def get_pull_request_created_or_updated_body(payload: Dict[str, Any], action: st
target_branch=pull_request['source']['branch']['name'],
base_branch=pull_request['destination']['branch']['name'],
message=pull_request['description'],
assignee=assignee
assignee=assignee,
title=pull_request['title'] if include_title else None
)
def get_pull_request_comment_created_action_body(payload: Dict[str, Any]) -> str:
def get_pull_request_comment_created_action_body(
payload: Dict[str, Any],
include_title: Optional[bool]=False
) -> str:
action = '[commented]({})'.format(payload['comment']['links']['html']['href'])
return get_pull_request_comment_action_body(payload, action)
return get_pull_request_comment_action_body(payload, action, include_title)
def get_pull_request_deleted_or_updated_comment_action_body(payload: Dict[str, Any], action: str) -> str:
def get_pull_request_deleted_or_updated_comment_action_body(
payload: Dict[str, Any], action: str,
include_title: Optional[bool]=False
) -> str:
action = "{} a [comment]({})".format(action, payload['comment']['links']['html']['href'])
return get_pull_request_comment_action_body(payload, action)
return get_pull_request_comment_action_body(payload, action, include_title)
def get_pull_request_comment_action_body(payload: Dict[str, Any], action: str) -> str:
def get_pull_request_comment_action_body(
payload: Dict[str, Any], action: str,
include_title: Optional[bool]=False
) -> str:
action += ' on'
return get_pull_request_event_message(
get_user_username(payload),
action,
payload['pullrequest']['links']['html']['href'],
payload['pullrequest']['id'],
message=payload['comment']['content']['raw']
message=payload['comment']['content']['raw'],
title=payload['pullrequest']['title'] if include_title else None
)
def get_push_tag_body(payload: Dict[str, Any], change: Dict[str, Any]) -> str:
@ -385,5 +413,6 @@ GET_SINGLE_MESSAGE_BODY_DEPENDING_ON_TYPE_MAPPER = {
action='updated'),
'pull_request_comment_deleted': partial(get_pull_request_deleted_or_updated_comment_action_body,
action='deleted'),
'push': get_push_bodies,
'repo:updated': get_repo_updated_body,
}