integrations: restructure code and added better coverage.
This commit is contained in:
parent
f935770cbf
commit
d59ca21bfc
14 changed files with 646 additions and 372 deletions
|
|
@ -18,7 +18,10 @@
|
|||
|
||||
import logging
|
||||
|
||||
from rhodecode.events.base import RhodeCodeIntegrationEvent
|
||||
from rhodecode.events.base import (
|
||||
RhodeCodeIntegrationEvent,
|
||||
RhodecodeEvent
|
||||
)
|
||||
|
||||
from rhodecode.events.base import ( # pragma: no cover
|
||||
FtsBuild
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
# and proprietary license terms, please see https://rhodecode.com/licenses/
|
||||
import logging
|
||||
import datetime
|
||||
import typing
|
||||
|
||||
from zope.cachedescriptors.property import Lazy as LazyProperty
|
||||
from pyramid.threadlocal import get_current_request
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ import sys
|
|||
import logging
|
||||
|
||||
from rhodecode.integrations.registry import IntegrationTypeRegistry
|
||||
from rhodecode.integrations.types import webhook, slack, hipchat, email, base
|
||||
from rhodecode.integrations.types import webhook, slack, email, base
|
||||
from rhodecode.lib.exc_tracking import store_exception
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
|
@ -34,8 +34,6 @@ integration_type_registry.register_integration_type(
|
|||
webhook.WebhookIntegrationType)
|
||||
integration_type_registry.register_integration_type(
|
||||
slack.SlackIntegrationType)
|
||||
integration_type_registry.register_integration_type(
|
||||
hipchat.HipchatIntegrationType)
|
||||
integration_type_registry.register_integration_type(
|
||||
email.EmailIntegrationType)
|
||||
|
||||
|
|
|
|||
|
|
@ -20,17 +20,18 @@ import colander
|
|||
import string
|
||||
import collections
|
||||
import logging
|
||||
|
||||
import requests
|
||||
import urllib.request
|
||||
import urllib.parse
|
||||
import urllib.error
|
||||
from requests.adapters import HTTPAdapter
|
||||
from requests.packages.urllib3.util.retry import Retry
|
||||
from requests.packages.urllib3.util import Retry
|
||||
|
||||
from mako import exceptions
|
||||
|
||||
from rhodecode.lib.utils2 import safe_str
|
||||
from rhodecode.translation import _
|
||||
|
||||
from rhodecode.lib.str_utils import safe_str
|
||||
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
|
@ -238,157 +239,6 @@ class CommitParsingDataHandler(object):
|
|||
return branches_commits
|
||||
|
||||
|
||||
class WebhookDataHandler(CommitParsingDataHandler):
|
||||
name = 'webhook'
|
||||
|
||||
def __init__(self, template_url, headers):
|
||||
self.template_url = template_url
|
||||
self.headers = headers
|
||||
|
||||
def get_base_parsed_template(self, data):
|
||||
"""
|
||||
initially parses the passed in template with some common variables
|
||||
available on ALL calls
|
||||
"""
|
||||
# note: make sure to update the `WEBHOOK_URL_VARS` if this changes
|
||||
common_vars = {
|
||||
'repo_name': data['repo']['repo_name'],
|
||||
'repo_type': data['repo']['repo_type'],
|
||||
'repo_id': data['repo']['repo_id'],
|
||||
'repo_url': data['repo']['url'],
|
||||
'username': data['actor']['username'],
|
||||
'user_id': data['actor']['user_id'],
|
||||
'event_name': data['name']
|
||||
}
|
||||
|
||||
extra_vars = {}
|
||||
for extra_key, extra_val in data['repo']['extra_fields'].items():
|
||||
extra_vars[f'extra__{extra_key}'] = extra_val
|
||||
common_vars.update(extra_vars)
|
||||
|
||||
template_url = self.template_url.replace('${extra:', '${extra__')
|
||||
for k, v in common_vars.items():
|
||||
template_url = UrlTmpl(template_url).safe_substitute(**{k: v})
|
||||
return template_url
|
||||
|
||||
def repo_push_event_handler(self, event, data):
|
||||
url = self.get_base_parsed_template(data)
|
||||
url_calls = []
|
||||
|
||||
branches_commits = self.aggregate_branch_data(
|
||||
data['push']['branches'], data['push']['commits'])
|
||||
if '${branch}' in url or '${branch_head}' in url or '${commit_id}' in url:
|
||||
# call it multiple times, for each branch if used in variables
|
||||
for branch, commit_ids in branches_commits.items():
|
||||
branch_url = UrlTmpl(url).safe_substitute(branch=branch)
|
||||
|
||||
if '${branch_head}' in branch_url:
|
||||
# last commit in the aggregate is the head of the branch
|
||||
branch_head = commit_ids['branch_head']
|
||||
branch_url = UrlTmpl(branch_url).safe_substitute(branch_head=branch_head)
|
||||
|
||||
# call further down for each commit if used
|
||||
if '${commit_id}' in branch_url:
|
||||
for commit_data in commit_ids['commits']:
|
||||
commit_id = commit_data['raw_id']
|
||||
commit_url = UrlTmpl(branch_url).safe_substitute(commit_id=commit_id)
|
||||
# register per-commit call
|
||||
log.debug(
|
||||
'register %s call(%s) to url %s',
|
||||
self.name, event, commit_url)
|
||||
url_calls.append(
|
||||
(commit_url, self.headers, data))
|
||||
|
||||
else:
|
||||
# register per-branch call
|
||||
log.debug('register %s call(%s) to url %s',
|
||||
self.name, event, branch_url)
|
||||
url_calls.append((branch_url, self.headers, data))
|
||||
|
||||
else:
|
||||
log.debug('register %s call(%s) to url %s', self.name, event, url)
|
||||
url_calls.append((url, self.headers, data))
|
||||
|
||||
return url_calls
|
||||
|
||||
def repo_commit_comment_handler(self, event, data):
|
||||
url = self.get_base_parsed_template(data)
|
||||
log.debug('register %s call(%s) to url %s', self.name, event, url)
|
||||
comment_vars = [
|
||||
('commit_comment_id', data['comment']['comment_id']),
|
||||
('commit_comment_text', data['comment']['comment_text']),
|
||||
('commit_comment_type', data['comment']['comment_type']),
|
||||
|
||||
('commit_comment_f_path', data['comment']['comment_f_path']),
|
||||
('commit_comment_line_no', data['comment']['comment_line_no']),
|
||||
|
||||
('commit_comment_commit_id', data['commit']['commit_id']),
|
||||
('commit_comment_commit_branch', data['commit']['commit_branch']),
|
||||
('commit_comment_commit_message', data['commit']['commit_message']),
|
||||
]
|
||||
for k, v in comment_vars:
|
||||
url = UrlTmpl(url).safe_substitute(**{k: v})
|
||||
|
||||
return [(url, self.headers, data)]
|
||||
|
||||
def repo_commit_comment_edit_handler(self, event, data):
|
||||
url = self.get_base_parsed_template(data)
|
||||
log.debug('register %s call(%s) to url %s', self.name, event, url)
|
||||
comment_vars = [
|
||||
('commit_comment_id', data['comment']['comment_id']),
|
||||
('commit_comment_text', data['comment']['comment_text']),
|
||||
('commit_comment_type', data['comment']['comment_type']),
|
||||
|
||||
('commit_comment_f_path', data['comment']['comment_f_path']),
|
||||
('commit_comment_line_no', data['comment']['comment_line_no']),
|
||||
|
||||
('commit_comment_commit_id', data['commit']['commit_id']),
|
||||
('commit_comment_commit_branch', data['commit']['commit_branch']),
|
||||
('commit_comment_commit_message', data['commit']['commit_message']),
|
||||
]
|
||||
for k, v in comment_vars:
|
||||
url = UrlTmpl(url).safe_substitute(**{k: v})
|
||||
|
||||
return [(url, self.headers, data)]
|
||||
|
||||
def repo_create_event_handler(self, event, data):
|
||||
url = self.get_base_parsed_template(data)
|
||||
log.debug('register %s call(%s) to url %s', self.name, event, url)
|
||||
return [(url, self.headers, data)]
|
||||
|
||||
def pull_request_event_handler(self, event, data):
|
||||
url = self.get_base_parsed_template(data)
|
||||
log.debug('register %s call(%s) to url %s', self.name, event, url)
|
||||
pr_vars = [
|
||||
('pull_request_id', data['pullrequest']['pull_request_id']),
|
||||
('pull_request_title', data['pullrequest']['title']),
|
||||
('pull_request_url', data['pullrequest']['url']),
|
||||
('pull_request_shadow_url', data['pullrequest']['shadow_url']),
|
||||
('pull_request_commits_uid', data['pullrequest']['commits_uid']),
|
||||
]
|
||||
for k, v in pr_vars:
|
||||
url = UrlTmpl(url).safe_substitute(**{k: v})
|
||||
|
||||
return [(url, self.headers, data)]
|
||||
|
||||
def __call__(self, event, data):
|
||||
from rhodecode import events
|
||||
|
||||
if isinstance(event, events.RepoPushEvent):
|
||||
return self.repo_push_event_handler(event, data)
|
||||
elif isinstance(event, events.RepoCreateEvent):
|
||||
return self.repo_create_event_handler(event, data)
|
||||
elif isinstance(event, events.RepoCommitCommentEvent):
|
||||
return self.repo_commit_comment_handler(event, data)
|
||||
elif isinstance(event, events.RepoCommitCommentEditEvent):
|
||||
return self.repo_commit_comment_edit_handler(event, data)
|
||||
elif isinstance(event, events.PullRequestEvent):
|
||||
return self.pull_request_event_handler(event, data)
|
||||
else:
|
||||
raise ValueError(
|
||||
f'event type `{event.__class__}` has no handler defined')
|
||||
|
||||
|
||||
def get_auth(settings):
|
||||
from requests.auth import HTTPBasicAuth
|
||||
username = settings.get('username')
|
||||
|
|
|
|||
17
rhodecode/integrations/types/handlers/__init__.py
Normal file
17
rhodecode/integrations/types/handlers/__init__.py
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
# Copyright (C) 2012-2023 RhodeCode GmbH
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License, version 3
|
||||
# (only), as published by the Free Software Foundation.
|
||||
#
|
||||
# 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 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 <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
# This program is dual-licensed. If you wish to learn more about the
|
||||
# RhodeCode Enterprise Edition, including its added features, Support services,
|
||||
# and proprietary license terms, please see https://rhodecode.com/licenses/
|
||||
242
rhodecode/integrations/types/handlers/slack.py
Normal file
242
rhodecode/integrations/types/handlers/slack.py
Normal file
|
|
@ -0,0 +1,242 @@
|
|||
# Copyright (C) 2012-2023 RhodeCode GmbH
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License, version 3
|
||||
# (only), as published by the Free Software Foundation.
|
||||
#
|
||||
# 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 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 <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
# This program is dual-licensed. If you wish to learn more about the
|
||||
# RhodeCode Enterprise Edition, including its added features, Support services,
|
||||
# and proprietary license terms, please see https://rhodecode.com/licenses/
|
||||
import re
|
||||
import textwrap
|
||||
import dataclasses
|
||||
import logging
|
||||
import typing
|
||||
|
||||
from mako.template import Template
|
||||
|
||||
from rhodecode import events
|
||||
from rhodecode.integrations.types.base import CommitParsingDataHandler, render_with_traceback
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@dataclasses.dataclass
|
||||
class SlackData:
|
||||
title: str
|
||||
text: str
|
||||
fields: list[dict] | None = None
|
||||
overrides: dict | None = None
|
||||
|
||||
|
||||
def html_to_slack_links(message):
|
||||
return re.compile(r'<a .*?href=["\'](.+?)".*?>(.+?)</a>').sub(r'<\1|\2>', message)
|
||||
|
||||
|
||||
REPO_PUSH_TEMPLATE = Template('''
|
||||
<%
|
||||
def branch_text(branch):
|
||||
if branch:
|
||||
return 'on branch: <{}|{}>'.format(branch_commits['branch']['url'], branch_commits['branch']['name'])
|
||||
else:
|
||||
## case for SVN no branch push...
|
||||
return 'to trunk'
|
||||
%> \
|
||||
|
||||
% for branch, branch_commits in branches_commits.items():
|
||||
${len(branch_commits['commits'])} ${'commit' if len(branch_commits['commits']) == 1 else 'commits'} ${branch_text(branch)}
|
||||
% for commit in branch_commits['commits']:
|
||||
`<${commit['url']}|${commit['short_id']}>` - ${commit['message_html']|html_to_slack_links}
|
||||
% endfor
|
||||
% endfor
|
||||
''')
|
||||
|
||||
|
||||
class SlackDataHandler(CommitParsingDataHandler):
|
||||
name = 'slack'
|
||||
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
def __call__(self, event: events.RhodecodeEvent, data):
|
||||
if not isinstance(event, events.RhodecodeEvent):
|
||||
raise TypeError(f"event {event} is not subtype of events.RhodecodeEvent")
|
||||
|
||||
actor = data["actor"]["username"]
|
||||
default_title = f'*{actor}* caused a *{event.name}* event'
|
||||
default_text = f'*{actor}* caused a *{event.name}* event'
|
||||
|
||||
default_slack_data = SlackData(title=default_title, text=default_text)
|
||||
|
||||
if isinstance(event, events.PullRequestCommentEvent):
|
||||
return self.format_pull_request_comment_event(
|
||||
event, data, default_slack_data
|
||||
)
|
||||
elif isinstance(event, events.PullRequestCommentEditEvent):
|
||||
return self.format_pull_request_comment_event(
|
||||
event, data, default_slack_data
|
||||
)
|
||||
elif isinstance(event, events.PullRequestReviewEvent):
|
||||
return self.format_pull_request_review_event(event, data, default_slack_data)
|
||||
elif isinstance(event, events.PullRequestEvent):
|
||||
return self.format_pull_request_event(event, data, default_slack_data)
|
||||
elif isinstance(event, events.RepoPushEvent):
|
||||
return self.format_repo_push_event(event, data, default_slack_data)
|
||||
elif isinstance(event, events.RepoCreateEvent):
|
||||
return self.format_repo_create_event(event, data, default_slack_data)
|
||||
else:
|
||||
raise ValueError(
|
||||
f'event type `{event.__class__}` has no handler defined')
|
||||
|
||||
def format_pull_request_comment_event(self, event, data, slack_data):
|
||||
comment_text = data['comment']['text']
|
||||
if len(comment_text) > 200:
|
||||
comment_text = '<{comment_url}|{comment_text}...>'.format(
|
||||
comment_text=comment_text[:200],
|
||||
comment_url=data['comment']['url'],
|
||||
)
|
||||
|
||||
fields = None
|
||||
overrides = None
|
||||
status_text = None
|
||||
|
||||
if data['comment']['status']:
|
||||
status_color = {
|
||||
'approved': '#0ac878',
|
||||
'rejected': '#e85e4d'}.get(data['comment']['status'])
|
||||
|
||||
if status_color:
|
||||
overrides = {"color": status_color}
|
||||
|
||||
status_text = data['comment']['status']
|
||||
|
||||
if data['comment']['file']:
|
||||
fields = [
|
||||
{
|
||||
"title": "file",
|
||||
"value": data['comment']['file']
|
||||
},
|
||||
{
|
||||
"title": "line",
|
||||
"value": data['comment']['line']
|
||||
}
|
||||
]
|
||||
|
||||
template = Template(textwrap.dedent(r'''
|
||||
*${data['actor']['username']}* left ${data['comment']['type']} on pull request <${data['pullrequest']['url']}|#${data['pullrequest']['pull_request_id']}>:
|
||||
'''))
|
||||
title = render_with_traceback(
|
||||
template, data=data, comment=event.comment)
|
||||
|
||||
template = Template(textwrap.dedent(r'''
|
||||
*pull request title*: ${pr_title}
|
||||
% if status_text:
|
||||
*submitted status*: `${status_text}`
|
||||
% endif
|
||||
>>> ${comment_text}
|
||||
'''))
|
||||
text = render_with_traceback(
|
||||
template,
|
||||
comment_text=comment_text,
|
||||
pr_title=data['pullrequest']['title'],
|
||||
status_text=status_text)
|
||||
|
||||
slack_data.title = title
|
||||
slack_data.text = text
|
||||
slack_data.fields = fields
|
||||
slack_data.overrides = overrides
|
||||
|
||||
return slack_data
|
||||
|
||||
def format_pull_request_review_event(self, event, data, slack_data) -> SlackData:
|
||||
template = Template(textwrap.dedent(r'''
|
||||
*${data['actor']['username']}* changed status of pull request <${data['pullrequest']['url']}|#${data['pullrequest']['pull_request_id']} to `${data['pullrequest']['status']}`>:
|
||||
'''))
|
||||
title = render_with_traceback(template, data=data)
|
||||
|
||||
template = Template(textwrap.dedent(r'''
|
||||
*pull request title*: ${pr_title}
|
||||
'''))
|
||||
text = render_with_traceback(
|
||||
template,
|
||||
pr_title=data['pullrequest']['title'])
|
||||
|
||||
slack_data.title = title
|
||||
slack_data.text = text
|
||||
|
||||
return slack_data
|
||||
|
||||
def format_pull_request_event(self, event, data, slack_data) -> SlackData:
|
||||
action = {
|
||||
events.PullRequestCloseEvent: 'closed',
|
||||
events.PullRequestMergeEvent: 'merged',
|
||||
events.PullRequestUpdateEvent: 'updated',
|
||||
events.PullRequestCreateEvent: 'created',
|
||||
}.get(event.__class__, str(event.__class__))
|
||||
|
||||
template = Template(textwrap.dedent(r'''
|
||||
*${data['actor']['username']}* `${action}` pull request <${data['pullrequest']['url']}|#${data['pullrequest']['pull_request_id']}>:
|
||||
'''))
|
||||
title = render_with_traceback(template, data=data, action=action)
|
||||
|
||||
template = Template(textwrap.dedent(r'''
|
||||
*pull request title*: ${pr_title}
|
||||
%if data['pullrequest']['commits']:
|
||||
*commits*: ${len(data['pullrequest']['commits'])}
|
||||
%endif
|
||||
'''))
|
||||
text = render_with_traceback(
|
||||
template,
|
||||
pr_title=data['pullrequest']['title'],
|
||||
data=data)
|
||||
|
||||
slack_data.title = title
|
||||
slack_data.text = text
|
||||
|
||||
return slack_data
|
||||
|
||||
def format_repo_push_event(self, event, data, slack_data) -> SlackData:
|
||||
branches_commits = self.aggregate_branch_data(
|
||||
data['push']['branches'], data['push']['commits'])
|
||||
|
||||
template = Template(r'''
|
||||
*${data['actor']['username']}* pushed to repo <${data['repo']['url']}|${data['repo']['repo_name']}>:
|
||||
''')
|
||||
title = render_with_traceback(template, data=data)
|
||||
|
||||
text = render_with_traceback(
|
||||
REPO_PUSH_TEMPLATE,
|
||||
data=data,
|
||||
branches_commits=branches_commits,
|
||||
html_to_slack_links=html_to_slack_links,
|
||||
)
|
||||
|
||||
slack_data.title = title
|
||||
slack_data.text = text
|
||||
|
||||
return slack_data
|
||||
|
||||
def format_repo_create_event(self, event, data, slack_data) -> SlackData:
|
||||
template = Template(r'''
|
||||
*${data['actor']['username']}* created new repository ${data['repo']['repo_name']}:
|
||||
''')
|
||||
title = render_with_traceback(template, data=data)
|
||||
|
||||
template = Template(textwrap.dedent(r'''
|
||||
repo_url: ${data['repo']['url']}
|
||||
repo_type: ${data['repo']['repo_type']}
|
||||
'''))
|
||||
text = render_with_traceback(template, data=data)
|
||||
|
||||
slack_data.title = title
|
||||
slack_data.text = text
|
||||
|
||||
return slack_data
|
||||
175
rhodecode/integrations/types/handlers/webhook.py
Normal file
175
rhodecode/integrations/types/handlers/webhook.py
Normal file
|
|
@ -0,0 +1,175 @@
|
|||
# Copyright (C) 2012-2023 RhodeCode GmbH
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License, version 3
|
||||
# (only), as published by the Free Software Foundation.
|
||||
#
|
||||
# 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 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 <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
# This program is dual-licensed. If you wish to learn more about the
|
||||
# RhodeCode Enterprise Edition, including its added features, Support services,
|
||||
# and proprietary license terms, please see https://rhodecode.com/licenses/
|
||||
|
||||
import logging
|
||||
|
||||
from rhodecode import events
|
||||
from rhodecode.integrations.types.base import CommitParsingDataHandler, UrlTmpl
|
||||
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class WebhookDataHandler(CommitParsingDataHandler):
|
||||
name = 'webhook'
|
||||
|
||||
def __init__(self, template_url, headers):
|
||||
self.template_url = template_url
|
||||
self.headers = headers
|
||||
|
||||
def get_base_parsed_template(self, data):
|
||||
"""
|
||||
initially parses the passed in template with some common variables
|
||||
available on ALL calls
|
||||
"""
|
||||
# note: make sure to update the `WEBHOOK_URL_VARS` if this changes
|
||||
common_vars = {
|
||||
'repo_name': data['repo']['repo_name'],
|
||||
'repo_type': data['repo']['repo_type'],
|
||||
'repo_id': data['repo']['repo_id'],
|
||||
'repo_url': data['repo']['url'],
|
||||
'username': data['actor']['username'],
|
||||
'user_id': data['actor']['user_id'],
|
||||
'event_name': data['name']
|
||||
}
|
||||
|
||||
extra_vars = {}
|
||||
for extra_key, extra_val in data['repo']['extra_fields'].items():
|
||||
extra_vars[f'extra__{extra_key}'] = extra_val
|
||||
common_vars.update(extra_vars)
|
||||
|
||||
template_url = self.template_url.replace('${extra:', '${extra__')
|
||||
for k, v in common_vars.items():
|
||||
template_url = UrlTmpl(template_url).safe_substitute(**{k: v})
|
||||
return template_url
|
||||
|
||||
def repo_push_event_handler(self, event, data):
|
||||
url = self.get_base_parsed_template(data)
|
||||
url_calls = []
|
||||
|
||||
branches_commits = self.aggregate_branch_data(
|
||||
data['push']['branches'], data['push']['commits'])
|
||||
if '${branch}' in url or '${branch_head}' in url or '${commit_id}' in url:
|
||||
# call it multiple times, for each branch if used in variables
|
||||
for branch, commit_ids in branches_commits.items():
|
||||
branch_url = UrlTmpl(url).safe_substitute(branch=branch)
|
||||
|
||||
if '${branch_head}' in branch_url:
|
||||
# last commit in the aggregate is the head of the branch
|
||||
branch_head = commit_ids['branch_head']
|
||||
branch_url = UrlTmpl(branch_url).safe_substitute(branch_head=branch_head)
|
||||
|
||||
# call further down for each commit if used
|
||||
if '${commit_id}' in branch_url:
|
||||
for commit_data in commit_ids['commits']:
|
||||
commit_id = commit_data['raw_id']
|
||||
commit_url = UrlTmpl(branch_url).safe_substitute(commit_id=commit_id)
|
||||
# register per-commit call
|
||||
log.debug(
|
||||
'register %s call(%s) to url %s',
|
||||
self.name, event, commit_url)
|
||||
url_calls.append(
|
||||
(commit_url, self.headers, data))
|
||||
|
||||
else:
|
||||
# register per-branch call
|
||||
log.debug('register %s call(%s) to url %s',
|
||||
self.name, event, branch_url)
|
||||
url_calls.append((branch_url, self.headers, data))
|
||||
|
||||
else:
|
||||
log.debug('register %s call(%s) to url %s', self.name, event, url)
|
||||
url_calls.append((url, self.headers, data))
|
||||
|
||||
return url_calls
|
||||
|
||||
def repo_commit_comment_handler(self, event, data):
|
||||
url = self.get_base_parsed_template(data)
|
||||
log.debug('register %s call(%s) to url %s', self.name, event, url)
|
||||
comment_vars = [
|
||||
('commit_comment_id', data['comment']['comment_id']),
|
||||
('commit_comment_text', data['comment']['comment_text']),
|
||||
('commit_comment_type', data['comment']['comment_type']),
|
||||
|
||||
('commit_comment_f_path', data['comment']['comment_f_path']),
|
||||
('commit_comment_line_no', data['comment']['comment_line_no']),
|
||||
|
||||
('commit_comment_commit_id', data['commit']['commit_id']),
|
||||
('commit_comment_commit_branch', data['commit']['commit_branch']),
|
||||
('commit_comment_commit_message', data['commit']['commit_message']),
|
||||
]
|
||||
for k, v in comment_vars:
|
||||
url = UrlTmpl(url).safe_substitute(**{k: v})
|
||||
|
||||
return [(url, self.headers, data)]
|
||||
|
||||
def repo_commit_comment_edit_handler(self, event, data):
|
||||
url = self.get_base_parsed_template(data)
|
||||
log.debug('register %s call(%s) to url %s', self.name, event, url)
|
||||
comment_vars = [
|
||||
('commit_comment_id', data['comment']['comment_id']),
|
||||
('commit_comment_text', data['comment']['comment_text']),
|
||||
('commit_comment_type', data['comment']['comment_type']),
|
||||
|
||||
('commit_comment_f_path', data['comment']['comment_f_path']),
|
||||
('commit_comment_line_no', data['comment']['comment_line_no']),
|
||||
|
||||
('commit_comment_commit_id', data['commit']['commit_id']),
|
||||
('commit_comment_commit_branch', data['commit']['commit_branch']),
|
||||
('commit_comment_commit_message', data['commit']['commit_message']),
|
||||
]
|
||||
for k, v in comment_vars:
|
||||
url = UrlTmpl(url).safe_substitute(**{k: v})
|
||||
|
||||
return [(url, self.headers, data)]
|
||||
|
||||
def repo_create_event_handler(self, event, data):
|
||||
url = self.get_base_parsed_template(data)
|
||||
log.debug('register %s call(%s) to url %s', self.name, event, url)
|
||||
return [(url, self.headers, data)]
|
||||
|
||||
def pull_request_event_handler(self, event, data):
|
||||
url = self.get_base_parsed_template(data)
|
||||
log.debug('register %s call(%s) to url %s', self.name, event, url)
|
||||
pr_vars = [
|
||||
('pull_request_id', data['pullrequest']['pull_request_id']),
|
||||
('pull_request_title', data['pullrequest']['title']),
|
||||
('pull_request_url', data['pullrequest']['url']),
|
||||
('pull_request_shadow_url', data['pullrequest']['shadow_url']),
|
||||
('pull_request_commits_uid', data['pullrequest']['commits_uid']),
|
||||
]
|
||||
for k, v in pr_vars:
|
||||
url = UrlTmpl(url).safe_substitute(**{k: v})
|
||||
|
||||
return [(url, self.headers, data)]
|
||||
|
||||
def __call__(self, event, data):
|
||||
|
||||
if isinstance(event, events.RepoPushEvent):
|
||||
return self.repo_push_event_handler(event, data)
|
||||
elif isinstance(event, events.RepoCreateEvent):
|
||||
return self.repo_create_event_handler(event, data)
|
||||
elif isinstance(event, events.RepoCommitCommentEvent):
|
||||
return self.repo_commit_comment_handler(event, data)
|
||||
elif isinstance(event, events.RepoCommitCommentEditEvent):
|
||||
return self.repo_commit_comment_edit_handler(event, data)
|
||||
elif isinstance(event, events.PullRequestEvent):
|
||||
return self.pull_request_event_handler(event, data)
|
||||
else:
|
||||
raise ValueError(
|
||||
f'event type `{event.__class__}` has no handler defined')
|
||||
|
|
@ -17,15 +17,13 @@
|
|||
# and proprietary license terms, please see https://rhodecode.com/licenses/
|
||||
|
||||
|
||||
import re
|
||||
|
||||
import time
|
||||
import textwrap
|
||||
import logging
|
||||
|
||||
import deform
|
||||
import requests
|
||||
import deform # noqa
|
||||
import deform.widget
|
||||
import colander
|
||||
from mako.template import Template
|
||||
|
||||
from rhodecode import events
|
||||
from rhodecode.model.validation_schema.widgets import CheckboxChoiceWidgetDesc
|
||||
|
|
@ -34,36 +32,16 @@ from rhodecode.lib import helpers as h
|
|||
from rhodecode.lib.celerylib import run_task, async_task, RequestContextTask
|
||||
from rhodecode.lib.colander_utils import strip_whitespace
|
||||
from rhodecode.integrations.types.base import (
|
||||
IntegrationTypeBase, CommitParsingDataHandler, render_with_traceback,
|
||||
requests_retry_call)
|
||||
IntegrationTypeBase,
|
||||
requests_retry_call,
|
||||
)
|
||||
|
||||
from rhodecode.integrations.types.handlers.slack import SlackDataHandler, SlackData
|
||||
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def html_to_slack_links(message):
|
||||
return re.compile(r'<a .*?href=["\'](.+?)".*?>(.+?)</a>').sub(
|
||||
r'<\1|\2>', message)
|
||||
|
||||
|
||||
REPO_PUSH_TEMPLATE = Template('''
|
||||
<%
|
||||
def branch_text(branch):
|
||||
if branch:
|
||||
return 'on branch: <{}|{}>'.format(branch_commits['branch']['url'], branch_commits['branch']['name'])
|
||||
else:
|
||||
## case for SVN no branch push...
|
||||
return 'to trunk'
|
||||
%> \
|
||||
|
||||
% for branch, branch_commits in branches_commits.items():
|
||||
${len(branch_commits['commits'])} ${'commit' if len(branch_commits['commits']) == 1 else 'commits'} ${branch_text(branch)}
|
||||
% for commit in branch_commits['commits']:
|
||||
`<${commit['url']}|${commit['short_id']}>` - ${commit['message_html']|html_to_slack_links}
|
||||
% endfor
|
||||
% endfor
|
||||
''')
|
||||
|
||||
|
||||
class SlackSettingsSchema(colander.Schema):
|
||||
service = colander.SchemaNode(
|
||||
colander.String(),
|
||||
|
|
@ -111,7 +89,7 @@ class SlackSettingsSchema(colander.Schema):
|
|||
)
|
||||
|
||||
|
||||
class SlackIntegrationType(IntegrationTypeBase, CommitParsingDataHandler):
|
||||
class SlackIntegrationType(IntegrationTypeBase):
|
||||
key = 'slack'
|
||||
display_name = _('Slack')
|
||||
description = _('Send events such as repo pushes and pull requests to '
|
||||
|
|
@ -132,45 +110,6 @@ class SlackIntegrationType(IntegrationTypeBase, CommitParsingDataHandler):
|
|||
events.RepoCreateEvent,
|
||||
]
|
||||
|
||||
def send_event(self, event):
|
||||
log.debug('handling event %s with integration %s', event.name, self)
|
||||
|
||||
if event.__class__ not in self.valid_events:
|
||||
log.debug('event %r not present in valid event list (%s)', event, self.valid_events)
|
||||
return
|
||||
|
||||
if not self.event_enabled(event):
|
||||
return
|
||||
|
||||
data = event.as_dict()
|
||||
|
||||
# defaults
|
||||
title = '*%s* caused a *%s* event' % (
|
||||
data['actor']['username'], event.name)
|
||||
text = '*%s* caused a *%s* event' % (
|
||||
data['actor']['username'], event.name)
|
||||
fields = None
|
||||
overrides = None
|
||||
|
||||
if isinstance(event, events.PullRequestCommentEvent):
|
||||
(title, text, fields, overrides) \
|
||||
= self.format_pull_request_comment_event(event, data)
|
||||
elif isinstance(event, events.PullRequestCommentEditEvent):
|
||||
(title, text, fields, overrides) \
|
||||
= self.format_pull_request_comment_event(event, data)
|
||||
elif isinstance(event, events.PullRequestReviewEvent):
|
||||
title, text = self.format_pull_request_review_event(event, data)
|
||||
elif isinstance(event, events.PullRequestEvent):
|
||||
title, text = self.format_pull_request_event(event, data)
|
||||
elif isinstance(event, events.RepoPushEvent):
|
||||
title, text = self.format_repo_push_event(data)
|
||||
elif isinstance(event, events.RepoCreateEvent):
|
||||
title, text = self.format_repo_create_event(data)
|
||||
else:
|
||||
log.error('unhandled event type: %r', event)
|
||||
|
||||
run_task(post_text_to_slack, self.settings, title, text, fields, overrides)
|
||||
|
||||
def settings_schema(self):
|
||||
schema = SlackSettingsSchema()
|
||||
schema.add(colander.SchemaNode(
|
||||
|
|
@ -186,137 +125,31 @@ class SlackIntegrationType(IntegrationTypeBase, CommitParsingDataHandler):
|
|||
|
||||
return schema
|
||||
|
||||
def format_pull_request_comment_event(self, event, data):
|
||||
comment_text = data['comment']['text']
|
||||
if len(comment_text) > 200:
|
||||
comment_text = '<{comment_url}|{comment_text}...>'.format(
|
||||
comment_text=comment_text[:200],
|
||||
comment_url=data['comment']['url'],
|
||||
)
|
||||
def send_event(self, event):
|
||||
log.debug('handling event %s with integration %s', event.name, self)
|
||||
|
||||
fields = None
|
||||
overrides = None
|
||||
status_text = None
|
||||
if event.__class__ not in self.valid_events:
|
||||
log.debug('event %r not present in valid event list (%s)', event, self.valid_events)
|
||||
return
|
||||
|
||||
if data['comment']['status']:
|
||||
status_color = {
|
||||
'approved': '#0ac878',
|
||||
'rejected': '#e85e4d'}.get(data['comment']['status'])
|
||||
if not self.event_enabled(event):
|
||||
return
|
||||
|
||||
if status_color:
|
||||
overrides = {"color": status_color}
|
||||
data = event.as_dict()
|
||||
|
||||
status_text = data['comment']['status']
|
||||
|
||||
if data['comment']['file']:
|
||||
fields = [
|
||||
{
|
||||
"title": "file",
|
||||
"value": data['comment']['file']
|
||||
},
|
||||
{
|
||||
"title": "line",
|
||||
"value": data['comment']['line']
|
||||
}
|
||||
]
|
||||
|
||||
template = Template(textwrap.dedent(r'''
|
||||
*${data['actor']['username']}* left ${data['comment']['type']} on pull request <${data['pullrequest']['url']}|#${data['pullrequest']['pull_request_id']}>:
|
||||
'''))
|
||||
title = render_with_traceback(
|
||||
template, data=data, comment=event.comment)
|
||||
|
||||
template = Template(textwrap.dedent(r'''
|
||||
*pull request title*: ${pr_title}
|
||||
% if status_text:
|
||||
*submitted status*: `${status_text}`
|
||||
% endif
|
||||
>>> ${comment_text}
|
||||
'''))
|
||||
text = render_with_traceback(
|
||||
template,
|
||||
comment_text=comment_text,
|
||||
pr_title=data['pullrequest']['title'],
|
||||
status_text=status_text)
|
||||
|
||||
return title, text, fields, overrides
|
||||
|
||||
def format_pull_request_review_event(self, event, data):
|
||||
template = Template(textwrap.dedent(r'''
|
||||
*${data['actor']['username']}* changed status of pull request <${data['pullrequest']['url']}|#${data['pullrequest']['pull_request_id']} to `${data['pullrequest']['status']}`>:
|
||||
'''))
|
||||
title = render_with_traceback(template, data=data)
|
||||
|
||||
template = Template(textwrap.dedent(r'''
|
||||
*pull request title*: ${pr_title}
|
||||
'''))
|
||||
text = render_with_traceback(
|
||||
template,
|
||||
pr_title=data['pullrequest']['title'])
|
||||
|
||||
return title, text
|
||||
|
||||
def format_pull_request_event(self, event, data):
|
||||
action = {
|
||||
events.PullRequestCloseEvent: 'closed',
|
||||
events.PullRequestMergeEvent: 'merged',
|
||||
events.PullRequestUpdateEvent: 'updated',
|
||||
events.PullRequestCreateEvent: 'created',
|
||||
}.get(event.__class__, str(event.__class__))
|
||||
|
||||
template = Template(textwrap.dedent(r'''
|
||||
*${data['actor']['username']}* `${action}` pull request <${data['pullrequest']['url']}|#${data['pullrequest']['pull_request_id']}>:
|
||||
'''))
|
||||
title = render_with_traceback(template, data=data, action=action)
|
||||
|
||||
template = Template(textwrap.dedent(r'''
|
||||
*pull request title*: ${pr_title}
|
||||
%if data['pullrequest']['commits']:
|
||||
*commits*: ${len(data['pullrequest']['commits'])}
|
||||
%endif
|
||||
'''))
|
||||
text = render_with_traceback(
|
||||
template,
|
||||
pr_title=data['pullrequest']['title'],
|
||||
data=data)
|
||||
|
||||
return title, text
|
||||
|
||||
def format_repo_push_event(self, data):
|
||||
branches_commits = self.aggregate_branch_data(
|
||||
data['push']['branches'], data['push']['commits'])
|
||||
|
||||
template = Template(r'''
|
||||
*${data['actor']['username']}* pushed to repo <${data['repo']['url']}|${data['repo']['repo_name']}>:
|
||||
''')
|
||||
title = render_with_traceback(template, data=data)
|
||||
|
||||
text = render_with_traceback(
|
||||
REPO_PUSH_TEMPLATE,
|
||||
data=data,
|
||||
branches_commits=branches_commits,
|
||||
html_to_slack_links=html_to_slack_links,
|
||||
)
|
||||
|
||||
return title, text
|
||||
|
||||
def format_repo_create_event(self, data):
|
||||
template = Template(r'''
|
||||
*${data['actor']['username']}* created new repository ${data['repo']['repo_name']}:
|
||||
''')
|
||||
title = render_with_traceback(template, data=data)
|
||||
|
||||
template = Template(textwrap.dedent(r'''
|
||||
repo_url: ${data['repo']['url']}
|
||||
repo_type: ${data['repo']['repo_type']}
|
||||
'''))
|
||||
text = render_with_traceback(template, data=data)
|
||||
|
||||
return title, text
|
||||
handler = SlackDataHandler()
|
||||
slack_data = handler(event, data)
|
||||
# title, text, fields, overrides
|
||||
run_task(post_text_to_slack, self.settings, slack_data)
|
||||
|
||||
|
||||
@async_task(ignore_result=True, base=RequestContextTask)
|
||||
def post_text_to_slack(settings, title, text, fields=None, overrides=None):
|
||||
def post_text_to_slack(settings, slack_data: SlackData):
|
||||
title = slack_data.title
|
||||
text = slack_data.text
|
||||
fields = slack_data.fields
|
||||
overrides = slack_data.overrides
|
||||
|
||||
log.debug('sending %s (%s) to slack %s', title, text, settings['service'])
|
||||
|
||||
fields = fields or []
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@
|
|||
# and proprietary license terms, please see https://rhodecode.com/licenses/
|
||||
|
||||
|
||||
|
||||
import deform # noqa
|
||||
import deform.widget
|
||||
import logging
|
||||
import colander
|
||||
|
|
@ -28,8 +28,14 @@ from rhodecode.lib.colander_utils import strip_whitespace
|
|||
from rhodecode.model.validation_schema.widgets import CheckboxChoiceWidgetDesc
|
||||
from rhodecode.translation import _
|
||||
from rhodecode.integrations.types.base import (
|
||||
IntegrationTypeBase, get_auth, get_web_token, get_url_vars,
|
||||
WebhookDataHandler, WEBHOOK_URL_VARS, requests_retry_call)
|
||||
IntegrationTypeBase,
|
||||
get_auth,
|
||||
get_web_token,
|
||||
get_url_vars,
|
||||
WEBHOOK_URL_VARS,
|
||||
requests_retry_call,
|
||||
)
|
||||
from rhodecode.integrations.types.handlers.webhook import WebhookDataHandler
|
||||
from rhodecode.lib.celerylib import run_task, async_task, RequestContextTask
|
||||
from rhodecode.model.validation_schema import widgets
|
||||
|
||||
|
|
@ -57,7 +63,7 @@ class WebhookSettingsSchema(colander.Schema):
|
|||
widget=widgets.CodeMirrorWidget(
|
||||
help_block_collapsable_name='Show url variables',
|
||||
help_block_collapsable=(
|
||||
'E.g http://my-serv.com/trigger_job/${{event_name}}'
|
||||
'E.g https://my-serv.com/trigger_job/${{event_name}}'
|
||||
'?PR_ID=${{pull_request_id}}'
|
||||
'\nFull list of vars:\n{}'.format(URL_VARS)),
|
||||
codemirror_mode='text',
|
||||
|
|
@ -189,11 +195,11 @@ class WebhookIntegrationType(IntegrationTypeBase):
|
|||
url_calls = handler(event, data)
|
||||
log.debug('Webhook: calling following urls: %s', [x[0] for x in url_calls])
|
||||
|
||||
run_task(post_to_webhook, url_calls, self.settings)
|
||||
run_task(post_to_webhook, self.settings, url_calls)
|
||||
|
||||
|
||||
@async_task(ignore_result=True, base=RequestContextTask)
|
||||
def post_to_webhook(url_calls, settings):
|
||||
def post_to_webhook(settings, url_calls):
|
||||
"""
|
||||
Example data::
|
||||
|
||||
|
|
|
|||
|
|
@ -32,23 +32,27 @@ def repo_push_event(backend, user_regular):
|
|||
{'message': 'this is because 5b23c3532 broke stuff'},
|
||||
{'message': 'last commit'},
|
||||
]
|
||||
commit_ids = backend.create_master_repo(commits).values()
|
||||
repo = backend.create_repo()
|
||||
r = backend.create_repo(commits)
|
||||
|
||||
commit_ids = list(backend.commit_ids.values())
|
||||
repo_name = backend.repo_name
|
||||
alias = backend.alias
|
||||
|
||||
scm_extras = AttributeDict({
|
||||
'ip': '127.0.0.1',
|
||||
'username': user_regular.username,
|
||||
'user_id': user_regular.user_id,
|
||||
'action': '',
|
||||
'repository': repo.repo_name,
|
||||
'scm': repo.scm_instance().alias,
|
||||
'repository': repo_name,
|
||||
'scm': alias,
|
||||
'config': '',
|
||||
'repo_store': '',
|
||||
'server_url': 'http://example.com',
|
||||
'server_url': 'http://httpbin:9090',
|
||||
'make_lock': None,
|
||||
'locked_by': [None],
|
||||
'commit_ids': commit_ids,
|
||||
})
|
||||
|
||||
return events.RepoPushEvent(repo_name=repo.repo_name,
|
||||
return events.RepoPushEvent(repo_name=repo_name,
|
||||
pushed_commit_ids=commit_ids,
|
||||
extras=scm_extras)
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
# Copyright (C) 2010-2023 RhodeCode GmbH
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
# Copyright (C) 2010-2023 RhodeCode GmbH
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
|
|
@ -18,11 +17,43 @@
|
|||
# and proprietary license terms, please see https://rhodecode.com/licenses/
|
||||
|
||||
import pytest
|
||||
import mock
|
||||
from mock import patch
|
||||
|
||||
from rhodecode import events
|
||||
from rhodecode.integrations.types.handlers.slack import SlackDataHandler
|
||||
from rhodecode.model.db import Session, Integration
|
||||
from rhodecode.integrations.types.slack import SlackIntegrationType
|
||||
from rhodecode.tests import GIT_REPO
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def base_slack_data():
|
||||
return {
|
||||
"pullrequest": {
|
||||
"url": "https://example.com/pr1",
|
||||
"pull_request_id": "1",
|
||||
"title": "started pr",
|
||||
"status": "new",
|
||||
"commits": ["1", "2"],
|
||||
"shadow_url": "http://shadow-url",
|
||||
},
|
||||
"actor": {"username": "foo-user"},
|
||||
"comment": {
|
||||
"comment_id": 1,
|
||||
"text": "test-comment",
|
||||
"status": "approved",
|
||||
"file": "text.py",
|
||||
"line": "1",
|
||||
"type": "note",
|
||||
},
|
||||
"push": {"branches": "", "commits": []},
|
||||
"repo": {
|
||||
"url": "https://example.com/repo1",
|
||||
"repo_name": GIT_REPO,
|
||||
"repo_type": "git",
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
|
|
@ -52,14 +83,66 @@ def slack_integration(request, app, slack_settings):
|
|||
return integration
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def slack_integration_empty(request, app, slack_settings):
|
||||
slack_settings['events'] = []
|
||||
integration = Integration()
|
||||
integration.name = 'test slack integration'
|
||||
integration.enabled = True
|
||||
integration.integration_type = SlackIntegrationType.key
|
||||
integration.settings = slack_settings
|
||||
Session().add(integration)
|
||||
Session().commit()
|
||||
request.addfinalizer(lambda: Session().delete(integration))
|
||||
return integration
|
||||
|
||||
|
||||
def test_slack_push(slack_integration, repo_push_event):
|
||||
|
||||
with patch('rhodecode.integrations.types.slack.post_text_to_slack') as call:
|
||||
events.trigger(repo_push_event)
|
||||
assert 'pushed to' in call.call_args[0][1]
|
||||
|
||||
slack_integration.settings['events'] = []
|
||||
Session().commit()
|
||||
assert 'pushed to' in call.call_args[0][1].title
|
||||
# specific commit was parsed and serialized
|
||||
assert 'change that fixes #41' in call.call_args[0][1].text
|
||||
|
||||
|
||||
def test_slack_push_no_events(slack_integration_empty, repo_push_event):
|
||||
|
||||
assert Integration.get(slack_integration_empty.integration_id).settings['events'] == []
|
||||
|
||||
with patch('rhodecode.integrations.types.slack.post_text_to_slack') as call:
|
||||
events.trigger(repo_push_event)
|
||||
assert not call.call_args
|
||||
|
||||
|
||||
def test_slack_data_handler_wrong_event():
|
||||
handler = SlackDataHandler()
|
||||
data = {"actor": {"username": "foo-user"}}
|
||||
with pytest.raises(ValueError):
|
||||
handler(events.RhodecodeEvent(), data)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("event_type, args", [
|
||||
(
|
||||
events.PullRequestCommentEvent,
|
||||
(mock.MagicMock(name="pull-request"), mock.MagicMock(name="comment")),
|
||||
),
|
||||
(
|
||||
events.PullRequestCommentEditEvent,
|
||||
(mock.MagicMock(name="pull-request"), mock.MagicMock(name="comment")),
|
||||
),
|
||||
(
|
||||
events.PullRequestReviewEvent,
|
||||
(mock.MagicMock(name="pull-request"), mock.MagicMock(name="status")),
|
||||
),
|
||||
(
|
||||
events.RepoPushEvent,
|
||||
(GIT_REPO, mock.MagicMock(name="pushed_commit_ids"), mock.MagicMock(name="extras")),
|
||||
),
|
||||
(events.PullRequestEvent, (mock.MagicMock(), )),
|
||||
(events.RepoCreateEvent, (mock.MagicMock(), )),
|
||||
])
|
||||
def test_slack_data_handler(app, event_type: events.RhodecodeEvent, args, base_slack_data):
|
||||
handler = SlackDataHandler()
|
||||
handler(event_type(*args), base_slack_data)
|
||||
|
|
|
|||
|
|
@ -18,10 +18,13 @@
|
|||
# and proprietary license terms, please see https://rhodecode.com/licenses/
|
||||
|
||||
import pytest
|
||||
import mock
|
||||
from mock import patch
|
||||
|
||||
from rhodecode import events
|
||||
from rhodecode.lib.utils2 import AttributeDict
|
||||
from rhodecode.integrations.types.webhook import WebhookDataHandler
|
||||
from rhodecode.tests import GIT_REPO
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
|
|
@ -38,7 +41,33 @@ def base_data():
|
|||
'actor': {
|
||||
'username': 'actor_name',
|
||||
'user_id': 1
|
||||
}
|
||||
},
|
||||
"pullrequest": {
|
||||
"url": "https://example.com/pr1",
|
||||
"pull_request_id": "1",
|
||||
"title": "started pr",
|
||||
"status": "new",
|
||||
"commits_uid": ["1", "2"],
|
||||
"shadow_url": "http://shadow-url",
|
||||
},
|
||||
"comment": {
|
||||
"comment_id": 1,
|
||||
"comment_text": "test-comment",
|
||||
"status": "approved",
|
||||
"comment_f_path": "text.py",
|
||||
"comment_line_no": "1",
|
||||
"comment_type": "note",
|
||||
},
|
||||
"commit": {
|
||||
"commit_id": "efefef",
|
||||
"commit_branch": "master",
|
||||
"commit_message": "changed foo"
|
||||
},
|
||||
"push": {
|
||||
"branches": "",
|
||||
"commits": []
|
||||
},
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -131,3 +160,37 @@ def test_webook_parse_url_for_push_event(
|
|||
urls = handler(repo_push_event, base_data)
|
||||
assert urls == [
|
||||
(url, headers, base_data) for url in expected_urls]
|
||||
|
||||
|
||||
@pytest.mark.parametrize("event_type, args", [
|
||||
(
|
||||
events.RepoPushEvent,
|
||||
(GIT_REPO, mock.MagicMock(name="pushed_commit_ids"), mock.MagicMock(name="extras")),
|
||||
),
|
||||
(
|
||||
events.RepoCreateEvent,
|
||||
(GIT_REPO,),
|
||||
),
|
||||
(
|
||||
events.RepoCommitCommentEvent,
|
||||
(GIT_REPO, mock.MagicMock(name="commit"), mock.MagicMock(name="comment")),
|
||||
),
|
||||
(
|
||||
events.RepoCommitCommentEditEvent,
|
||||
(GIT_REPO, mock.MagicMock(name="commit"), mock.MagicMock(name="comment")),
|
||||
),
|
||||
(
|
||||
events.PullRequestEvent,
|
||||
(mock.MagicMock(), ),
|
||||
),
|
||||
])
|
||||
def test_webhook_data_handler(app, event_type: events.RhodecodeEvent, args, base_data):
|
||||
handler = WebhookDataHandler(
|
||||
template_url='http://server.com/${branch}/${commit_id}',
|
||||
headers={'exmaple-header': 'header-values'}
|
||||
)
|
||||
handler(event_type(*args), base_data)
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -331,7 +331,7 @@ class TestCommits(BackendTestMixin):
|
|||
assert line_no == 1
|
||||
assert commit_id == file_added_commit.raw_id
|
||||
assert commit_loader() == file_added_commit
|
||||
assert 'Foobar 3' in line
|
||||
assert b'Foobar 3' in line
|
||||
|
||||
def test_get_file_annotate_does_not_exist(self):
|
||||
file_added_commit = self.repo.get_commit(commit_idx=2)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue