events: added support for pull-request-comment and commit-comment events.
- allows chopts like actions on commits - re organized events and code cleanups
This commit is contained in:
parent
875bda9f88
commit
324544678c
17 changed files with 325 additions and 113 deletions
|
|
@ -75,4 +75,5 @@ from rhodecode.events.pullrequest import ( # pragma: no cover
|
|||
PullRequestReviewEvent,
|
||||
PullRequestMergeEvent,
|
||||
PullRequestCloseEvent,
|
||||
PullRequestCommentEvent,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -44,6 +44,9 @@ class RhodecodeEvent(object):
|
|||
self._request = request
|
||||
self.utc_timestamp = datetime.datetime.utcnow()
|
||||
|
||||
def __repr__(self):
|
||||
return '<%s:(%s)>' % (self.__class__.__name__, self.name)
|
||||
|
||||
def get_request(self):
|
||||
if self._request:
|
||||
return self._request
|
||||
|
|
@ -116,3 +119,4 @@ class RhodeCodeIntegrationEvent(RhodecodeEvent):
|
|||
"""
|
||||
Special subclass for Integration events
|
||||
"""
|
||||
description = ''
|
||||
|
|
|
|||
|
|
@ -77,6 +77,7 @@ class PullRequestCreateEvent(PullRequestEvent):
|
|||
"""
|
||||
name = 'pullrequest-create'
|
||||
display_name = lazy_ugettext('pullrequest created')
|
||||
description = lazy_ugettext('Event triggered after pull request was created')
|
||||
|
||||
|
||||
class PullRequestCloseEvent(PullRequestEvent):
|
||||
|
|
@ -86,6 +87,7 @@ class PullRequestCloseEvent(PullRequestEvent):
|
|||
"""
|
||||
name = 'pullrequest-close'
|
||||
display_name = lazy_ugettext('pullrequest closed')
|
||||
description = lazy_ugettext('Event triggered after pull request was closed')
|
||||
|
||||
|
||||
class PullRequestUpdateEvent(PullRequestEvent):
|
||||
|
|
@ -95,6 +97,7 @@ class PullRequestUpdateEvent(PullRequestEvent):
|
|||
"""
|
||||
name = 'pullrequest-update'
|
||||
display_name = lazy_ugettext('pullrequest commits updated')
|
||||
description = lazy_ugettext('Event triggered after pull requests was updated')
|
||||
|
||||
|
||||
class PullRequestReviewEvent(PullRequestEvent):
|
||||
|
|
@ -104,6 +107,8 @@ class PullRequestReviewEvent(PullRequestEvent):
|
|||
"""
|
||||
name = 'pullrequest-review'
|
||||
display_name = lazy_ugettext('pullrequest review changed')
|
||||
description = lazy_ugettext('Event triggered after a review status of a '
|
||||
'pull requests has changed to other.')
|
||||
|
||||
def __init__(self, pullrequest, status):
|
||||
super(PullRequestReviewEvent, self).__init__(pullrequest)
|
||||
|
|
@ -117,6 +122,8 @@ class PullRequestMergeEvent(PullRequestEvent):
|
|||
"""
|
||||
name = 'pullrequest-merge'
|
||||
display_name = lazy_ugettext('pullrequest merged')
|
||||
description = lazy_ugettext('Event triggered after a successful merge operation '
|
||||
'was executed on a pull request')
|
||||
|
||||
|
||||
class PullRequestCommentEvent(PullRequestEvent):
|
||||
|
|
@ -126,6 +133,8 @@ class PullRequestCommentEvent(PullRequestEvent):
|
|||
"""
|
||||
name = 'pullrequest-comment'
|
||||
display_name = lazy_ugettext('pullrequest commented')
|
||||
description = lazy_ugettext('Event triggered after a comment was made on a code '
|
||||
'in the pull request')
|
||||
|
||||
def __init__(self, pullrequest, comment):
|
||||
super(PullRequestCommentEvent, self).__init__(pullrequest)
|
||||
|
|
|
|||
|
|
@ -186,13 +186,33 @@ class RepoCommitCommentEvent(RepoEvent):
|
|||
An instance of this class is emitted as an :term:`event` after a comment is made
|
||||
on repository commit.
|
||||
"""
|
||||
|
||||
name = 'repo-commit-comment'
|
||||
display_name = lazy_ugettext('repository commit comment')
|
||||
description = lazy_ugettext('Event triggered after a comment was made '
|
||||
'on commit inside a repository')
|
||||
|
||||
def __init__(self, repo, commit, comment):
|
||||
super(RepoCommitCommentEvent, self).__init__(repo)
|
||||
self.commit = commit
|
||||
self.comment = comment
|
||||
|
||||
name = 'repo-commit-comment'
|
||||
display_name = lazy_ugettext('repository commit comment')
|
||||
def as_dict(self):
|
||||
data = super(RepoCommitCommentEvent, self).as_dict()
|
||||
data['commit'] = {
|
||||
'commit_id': self.commit.raw_id,
|
||||
'commit_message': self.commit.message,
|
||||
'commit_branch': self.commit.branch,
|
||||
}
|
||||
|
||||
data['comment'] = {
|
||||
'comment_id': self.comment.comment_id,
|
||||
'comment_text': self.comment.text,
|
||||
'comment_type': self.comment.comment_type,
|
||||
'comment_f_path': self.comment.f_path,
|
||||
'comment_line_no': self.comment.line_no,
|
||||
}
|
||||
return data
|
||||
|
||||
|
||||
class RepoPreCreateEvent(RepoEvent):
|
||||
|
|
@ -202,6 +222,7 @@ class RepoPreCreateEvent(RepoEvent):
|
|||
"""
|
||||
name = 'repo-pre-create'
|
||||
display_name = lazy_ugettext('repository pre create')
|
||||
description = lazy_ugettext('Event triggered before repository is created')
|
||||
|
||||
|
||||
class RepoCreateEvent(RepoEvent):
|
||||
|
|
@ -211,6 +232,7 @@ class RepoCreateEvent(RepoEvent):
|
|||
"""
|
||||
name = 'repo-create'
|
||||
display_name = lazy_ugettext('repository created')
|
||||
description = lazy_ugettext('Event triggered after repository was created')
|
||||
|
||||
|
||||
class RepoPreDeleteEvent(RepoEvent):
|
||||
|
|
@ -220,6 +242,7 @@ class RepoPreDeleteEvent(RepoEvent):
|
|||
"""
|
||||
name = 'repo-pre-delete'
|
||||
display_name = lazy_ugettext('repository pre delete')
|
||||
description = lazy_ugettext('Event triggered before a repository is deleted')
|
||||
|
||||
|
||||
class RepoDeleteEvent(RepoEvent):
|
||||
|
|
@ -229,6 +252,7 @@ class RepoDeleteEvent(RepoEvent):
|
|||
"""
|
||||
name = 'repo-delete'
|
||||
display_name = lazy_ugettext('repository deleted')
|
||||
description = lazy_ugettext('Event triggered after repository was deleted')
|
||||
|
||||
|
||||
class RepoVCSEvent(RepoEvent):
|
||||
|
|
@ -269,6 +293,7 @@ class RepoPrePullEvent(RepoVCSEvent):
|
|||
"""
|
||||
name = 'repo-pre-pull'
|
||||
display_name = lazy_ugettext('repository pre pull')
|
||||
description = lazy_ugettext('Event triggered before repository code is pulled')
|
||||
|
||||
|
||||
class RepoPullEvent(RepoVCSEvent):
|
||||
|
|
@ -278,6 +303,7 @@ class RepoPullEvent(RepoVCSEvent):
|
|||
"""
|
||||
name = 'repo-pull'
|
||||
display_name = lazy_ugettext('repository pull')
|
||||
description = lazy_ugettext('Event triggered after repository code was pulled')
|
||||
|
||||
|
||||
class RepoPrePushEvent(RepoVCSEvent):
|
||||
|
|
@ -287,6 +313,8 @@ class RepoPrePushEvent(RepoVCSEvent):
|
|||
"""
|
||||
name = 'repo-pre-push'
|
||||
display_name = lazy_ugettext('repository pre push')
|
||||
description = lazy_ugettext('Event triggered before the code is '
|
||||
'pushed to a repository')
|
||||
|
||||
|
||||
class RepoPushEvent(RepoVCSEvent):
|
||||
|
|
@ -298,6 +326,8 @@ class RepoPushEvent(RepoVCSEvent):
|
|||
"""
|
||||
name = 'repo-push'
|
||||
display_name = lazy_ugettext('repository push')
|
||||
description = lazy_ugettext('Event triggered after the code was '
|
||||
'pushed to a repository')
|
||||
|
||||
def __init__(self, repo_name, pushed_commit_ids, extras):
|
||||
super(RepoPushEvent, self).__init__(repo_name, extras)
|
||||
|
|
|
|||
|
|
@ -60,6 +60,7 @@ class RepoGroupCreateEvent(RepoGroupEvent):
|
|||
"""
|
||||
name = 'repo-group-create'
|
||||
display_name = lazy_ugettext('repository group created')
|
||||
description = lazy_ugettext('Event triggered after a repository group was created')
|
||||
|
||||
|
||||
class RepoGroupDeleteEvent(RepoGroupEvent):
|
||||
|
|
@ -69,6 +70,7 @@ class RepoGroupDeleteEvent(RepoGroupEvent):
|
|||
"""
|
||||
name = 'repo-group-delete'
|
||||
display_name = lazy_ugettext('repository group deleted')
|
||||
description = lazy_ugettext('Event triggered after a repository group was deleted')
|
||||
|
||||
|
||||
class RepoGroupUpdateEvent(RepoGroupEvent):
|
||||
|
|
@ -78,3 +80,4 @@ class RepoGroupUpdateEvent(RepoGroupEvent):
|
|||
"""
|
||||
name = 'repo-group-update'
|
||||
display_name = lazy_ugettext('repository group update')
|
||||
description = lazy_ugettext('Event triggered after a repository group was updated')
|
||||
|
|
|
|||
|
|
@ -125,6 +125,19 @@ class IntegrationTypeBase(object):
|
|||
"""
|
||||
return colander.Schema()
|
||||
|
||||
def event_enabled(self, event):
|
||||
"""
|
||||
Checks if submitted event is enabled based on the plugin settings
|
||||
:param event:
|
||||
:return: bool
|
||||
"""
|
||||
allowed_events = self.settings['events']
|
||||
if event.name not in allowed_events:
|
||||
log.debug('event ignored: %r event %s not in allowed set of events %s',
|
||||
event, event.name, allowed_events)
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
class EEIntegration(IntegrationTypeBase):
|
||||
description = 'Integration available in RhodeCode EE edition.'
|
||||
|
|
@ -139,30 +152,57 @@ class EEIntegration(IntegrationTypeBase):
|
|||
# Helpers #
|
||||
# updating this required to update the `common_vars` as well.
|
||||
WEBHOOK_URL_VARS = [
|
||||
('event_name', 'Unique name of the event type, e.g pullrequest-update'),
|
||||
('repo_name', 'Full name of the repository'),
|
||||
('repo_type', 'VCS type of repository'),
|
||||
('repo_id', 'Unique id of repository'),
|
||||
('repo_url', 'Repository url'),
|
||||
# GENERAL
|
||||
('General', [
|
||||
('event_name', 'Unique name of the event type, e.g pullrequest-update'),
|
||||
('repo_name', 'Full name of the repository'),
|
||||
('repo_type', 'VCS type of repository'),
|
||||
('repo_id', 'Unique id of repository'),
|
||||
('repo_url', 'Repository url'),
|
||||
]
|
||||
),
|
||||
# extra repo fields
|
||||
('extra:<extra_key_name>', 'Extra repo variables, read from its settings.'),
|
||||
|
||||
('Repository', [
|
||||
('extra:<extra_key_name>', 'Extra repo variables, read from its settings.'),
|
||||
]
|
||||
),
|
||||
# special attrs below that we handle, using multi-call
|
||||
('branch', 'Name of each branch submitted, if any.'),
|
||||
('branch_head', 'Head ID of pushed branch (full sha of last commit), if any.'),
|
||||
('commit_id', 'ID (full sha) of each commit submitted, if any.'),
|
||||
|
||||
('Commit push - Multicalls', [
|
||||
('branch', 'Name of each branch submitted, if any.'),
|
||||
('branch_head', 'Head ID of pushed branch (full sha of last commit), if any.'),
|
||||
('commit_id', 'ID (full sha) of each commit submitted, if any.'),
|
||||
]
|
||||
),
|
||||
# pr events vars
|
||||
('pull_request_id', 'Unique ID of the pull request.'),
|
||||
('pull_request_title', 'Title of the pull request.'),
|
||||
('pull_request_url', 'Pull request url.'),
|
||||
('pull_request_shadow_url', 'Pull request shadow repo clone url.'),
|
||||
('pull_request_commits_uid', 'Calculated UID of all commits inside the PR. '
|
||||
'Changes after PR update'),
|
||||
('Pull request', [
|
||||
('pull_request_id', 'Unique ID of the pull request.'),
|
||||
('pull_request_title', 'Title of the pull request.'),
|
||||
('pull_request_url', 'Pull request url.'),
|
||||
('pull_request_shadow_url', 'Pull request shadow repo clone url.'),
|
||||
('pull_request_commits_uid', 'Calculated UID of all commits inside the PR. '
|
||||
'Changes after PR update'),
|
||||
]
|
||||
),
|
||||
# commit comment event vars
|
||||
('Commit comment', [
|
||||
('commit_comment_id', 'Unique ID of the comment made on a commit.'),
|
||||
('commit_comment_text', 'Text of commit comment.'),
|
||||
('commit_comment_type', 'Type of comment, e.g note/todo.'),
|
||||
|
||||
('commit_comment_f_path', 'Optionally path of file for inline comments.'),
|
||||
('commit_comment_line_no', 'Line number of the file: eg o10, or n200'),
|
||||
|
||||
('commit_comment_commit_id', 'Commit id that comment was left at.'),
|
||||
('commit_comment_commit_branch', 'Commit branch that comment was left at'),
|
||||
('commit_comment_commit_message', 'Commit message that comment was left at'),
|
||||
]
|
||||
),
|
||||
# user who triggers the call
|
||||
('username', 'User who triggered the call.'),
|
||||
('user_id', 'User id who triggered the call.'),
|
||||
('Caller', [
|
||||
('username', 'User who triggered the call.'),
|
||||
('user_id', 'User id who triggered the call.'),
|
||||
]
|
||||
),
|
||||
]
|
||||
|
||||
# common vars for url template used for CI plugins. Shared with webhook
|
||||
|
|
@ -271,6 +311,26 @@ class WebhookDataHandler(CommitParsingDataHandler):
|
|||
|
||||
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_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)
|
||||
|
|
@ -298,12 +358,13 @@ class WebhookDataHandler(CommitParsingDataHandler):
|
|||
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.PullRequestEvent):
|
||||
return self.pull_request_event_handler(event, data)
|
||||
else:
|
||||
raise ValueError(
|
||||
'event type `%s` not in supported list: %s' % (
|
||||
event.__class__, events))
|
||||
'event type `{}` has no handler defined'.format(event.__class__))
|
||||
|
||||
|
||||
def get_auth(settings):
|
||||
|
|
@ -320,9 +381,13 @@ def get_web_token(settings):
|
|||
|
||||
|
||||
def get_url_vars(url_vars):
|
||||
return '\n'.join(
|
||||
'{} - {}'.format('${' + key + '}', explanation)
|
||||
for key, explanation in url_vars)
|
||||
items = []
|
||||
|
||||
for section, section_items in url_vars:
|
||||
items.append('\n*{}*'.format(section))
|
||||
for key, explanation in section_items:
|
||||
items.append(' {} - {}'.format('${' + key + '}', explanation))
|
||||
return '\n'.join(items)
|
||||
|
||||
|
||||
def render_with_traceback(template, *args, **kwargs):
|
||||
|
|
|
|||
|
|
@ -19,13 +19,14 @@
|
|||
# and proprietary license terms, please see https://rhodecode.com/licenses/
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import deform
|
||||
import logging
|
||||
import colander
|
||||
|
||||
import colander
|
||||
import deform.widget
|
||||
from mako.template import Template
|
||||
|
||||
from rhodecode import events
|
||||
from rhodecode.model.validation_schema.widgets import CheckboxChoiceWidgetDesc
|
||||
from rhodecode.translation import _
|
||||
from rhodecode.lib.celerylib import run_task
|
||||
from rhodecode.lib.celerylib import tasks
|
||||
|
|
@ -174,6 +175,10 @@ class EmailIntegrationType(IntegrationTypeBase):
|
|||
display_name = _('Email')
|
||||
description = _('Send repo push summaries to a list of recipients via email')
|
||||
|
||||
valid_events = [
|
||||
events.RepoPushEvent
|
||||
]
|
||||
|
||||
@classmethod
|
||||
def icon(cls):
|
||||
return '''
|
||||
|
|
@ -240,59 +245,86 @@ class EmailIntegrationType(IntegrationTypeBase):
|
|||
|
||||
def settings_schema(self):
|
||||
schema = EmailSettingsSchema()
|
||||
schema.add(colander.SchemaNode(
|
||||
colander.Set(),
|
||||
widget=CheckboxChoiceWidgetDesc(
|
||||
values=sorted(
|
||||
[(e.name, e.display_name, e.description) for e in self.valid_events]
|
||||
),
|
||||
),
|
||||
description="List of events activated for this integration",
|
||||
name='events'
|
||||
))
|
||||
return schema
|
||||
|
||||
def send_event(self, event):
|
||||
data = event.as_dict()
|
||||
log.debug('got event: %r', 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):
|
||||
# NOTE(marcink): for legacy reasons we're skipping this check...
|
||||
# since the email event haven't had any settings...
|
||||
pass
|
||||
|
||||
handler = EmailEventHandler(self.settings)
|
||||
handler(event, event_data=event.as_dict())
|
||||
|
||||
|
||||
class EmailEventHandler(object):
|
||||
def __init__(self, integration_settings):
|
||||
self.integration_settings = integration_settings
|
||||
|
||||
def __call__(self, event, event_data):
|
||||
if isinstance(event, events.RepoPushEvent):
|
||||
repo_push_handler(data, self.settings)
|
||||
self.repo_push_handler(event, event_data)
|
||||
else:
|
||||
log.debug('ignoring event: %r', event)
|
||||
|
||||
def repo_push_handler(self, event, data):
|
||||
commit_num = len(data['push']['commits'])
|
||||
server_url = data['server_url']
|
||||
|
||||
def repo_push_handler(data, settings):
|
||||
commit_num = len(data['push']['commits'])
|
||||
server_url = data['server_url']
|
||||
|
||||
if commit_num == 1:
|
||||
if data['push']['branches']:
|
||||
_subject = '[{repo_name}] {author} pushed {commit_num} commit on branches: {branches}'
|
||||
if commit_num == 1:
|
||||
if data['push']['branches']:
|
||||
_subject = '[{repo_name}] {author} pushed {commit_num} commit on branches: {branches}'
|
||||
else:
|
||||
_subject = '[{repo_name}] {author} pushed {commit_num} commit'
|
||||
subject = _subject.format(
|
||||
author=data['actor']['username'],
|
||||
repo_name=data['repo']['repo_name'],
|
||||
commit_num=commit_num,
|
||||
branches=', '.join(
|
||||
branch['name'] for branch in data['push']['branches'])
|
||||
)
|
||||
else:
|
||||
_subject = '[{repo_name}] {author} pushed {commit_num} commit'
|
||||
subject = _subject.format(
|
||||
author=data['actor']['username'],
|
||||
repo_name=data['repo']['repo_name'],
|
||||
commit_num=commit_num,
|
||||
branches=', '.join(
|
||||
branch['name'] for branch in data['push']['branches'])
|
||||
)
|
||||
else:
|
||||
if data['push']['branches']:
|
||||
_subject = '[{repo_name}] {author} pushed {commit_num} commits on branches: {branches}'
|
||||
else:
|
||||
_subject = '[{repo_name}] {author} pushed {commit_num} commits'
|
||||
subject = _subject.format(
|
||||
author=data['actor']['username'],
|
||||
repo_name=data['repo']['repo_name'],
|
||||
commit_num=commit_num,
|
||||
branches=', '.join(
|
||||
branch['name'] for branch in data['push']['branches']))
|
||||
if data['push']['branches']:
|
||||
_subject = '[{repo_name}] {author} pushed {commit_num} commits on branches: {branches}'
|
||||
else:
|
||||
_subject = '[{repo_name}] {author} pushed {commit_num} commits'
|
||||
subject = _subject.format(
|
||||
author=data['actor']['username'],
|
||||
repo_name=data['repo']['repo_name'],
|
||||
commit_num=commit_num,
|
||||
branches=', '.join(
|
||||
branch['name'] for branch in data['push']['branches']))
|
||||
|
||||
email_body_plaintext = render_with_traceback(
|
||||
REPO_PUSH_TEMPLATE_PLAINTEXT,
|
||||
data=data,
|
||||
subject=subject,
|
||||
instance_url=server_url)
|
||||
email_body_plaintext = render_with_traceback(
|
||||
REPO_PUSH_TEMPLATE_PLAINTEXT,
|
||||
data=data,
|
||||
subject=subject,
|
||||
instance_url=server_url)
|
||||
|
||||
email_body_html = render_with_traceback(
|
||||
REPO_PUSH_TEMPLATE_HTML,
|
||||
data=data,
|
||||
subject=subject,
|
||||
instance_url=server_url)
|
||||
email_body_html = render_with_traceback(
|
||||
REPO_PUSH_TEMPLATE_HTML,
|
||||
data=data,
|
||||
subject=subject,
|
||||
instance_url=server_url)
|
||||
|
||||
for email_address in settings['recipients']:
|
||||
run_task(
|
||||
tasks.send_email, email_address, subject,
|
||||
email_body_plaintext, email_body_html)
|
||||
recipients = self.integration_settings['recipients']
|
||||
for email_address in recipients:
|
||||
run_task(
|
||||
tasks.send_email, email_address, subject,
|
||||
email_body_plaintext, email_body_html)
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ import colander
|
|||
import textwrap
|
||||
from mako.template import Template
|
||||
from rhodecode import events
|
||||
from rhodecode.model.validation_schema.widgets import CheckboxChoiceWidgetDesc
|
||||
from rhodecode.translation import _
|
||||
from rhodecode.lib import helpers as h
|
||||
from rhodecode.lib.celerylib import run_task, async_task, RequestContextTask
|
||||
|
|
@ -119,13 +120,10 @@ class HipchatIntegrationType(IntegrationTypeBase, CommitParsingDataHandler):
|
|||
|
||||
def send_event(self, event):
|
||||
if event.__class__ not in self.valid_events:
|
||||
log.debug('event not valid: %r', event)
|
||||
log.debug('event %r not present in valid event list (%s)', event, self.valid_events)
|
||||
return
|
||||
|
||||
allowed_events = self.settings['events']
|
||||
if event.name not in allowed_events:
|
||||
log.debug('event ignored: %r event %s not in allowed events %s',
|
||||
event, event.name, allowed_events)
|
||||
if not self.event_enabled(event):
|
||||
return
|
||||
|
||||
data = event.as_dict()
|
||||
|
|
@ -133,8 +131,6 @@ class HipchatIntegrationType(IntegrationTypeBase, CommitParsingDataHandler):
|
|||
text = '<b>%s<b> caused a <b>%s</b> event' % (
|
||||
data['actor']['username'], event.name)
|
||||
|
||||
log.debug('handling hipchat event for %s', event.name)
|
||||
|
||||
if isinstance(event, events.PullRequestCommentEvent):
|
||||
text = self.format_pull_request_comment_event(event, data)
|
||||
elif isinstance(event, events.PullRequestReviewEvent):
|
||||
|
|
@ -154,12 +150,12 @@ class HipchatIntegrationType(IntegrationTypeBase, CommitParsingDataHandler):
|
|||
schema = HipchatSettingsSchema()
|
||||
schema.add(colander.SchemaNode(
|
||||
colander.Set(),
|
||||
widget=deform.widget.CheckboxChoiceWidget(
|
||||
widget=CheckboxChoiceWidgetDesc(
|
||||
values=sorted(
|
||||
[(e.name, e.display_name) for e in self.valid_events]
|
||||
)
|
||||
[(e.name, e.display_name, e.description) for e in self.valid_events]
|
||||
),
|
||||
),
|
||||
description="Events activated for this integration",
|
||||
description="List of events activated for this integration",
|
||||
name='events'
|
||||
))
|
||||
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ import colander
|
|||
from mako.template import Template
|
||||
|
||||
from rhodecode import events
|
||||
from rhodecode.model.validation_schema.widgets import CheckboxChoiceWidgetDesc
|
||||
from rhodecode.translation import _
|
||||
from rhodecode.lib import helpers as h
|
||||
from rhodecode.lib.celerylib import run_task, async_task, RequestContextTask
|
||||
|
|
@ -134,14 +135,13 @@ class SlackIntegrationType(IntegrationTypeBase, CommitParsingDataHandler):
|
|||
]
|
||||
|
||||
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 not valid: %r', event)
|
||||
log.debug('event %r not present in valid event list (%s)', event, self.valid_events)
|
||||
return
|
||||
|
||||
allowed_events = self.settings['events']
|
||||
if event.name not in allowed_events:
|
||||
log.debug('event ignored: %r event %s not in allowed events %s',
|
||||
event, event.name, allowed_events)
|
||||
if not self.event_enabled(event):
|
||||
return
|
||||
|
||||
data = event.as_dict()
|
||||
|
|
@ -154,8 +154,6 @@ class SlackIntegrationType(IntegrationTypeBase, CommitParsingDataHandler):
|
|||
fields = None
|
||||
overrides = None
|
||||
|
||||
log.debug('handling slack event for %s', event.name)
|
||||
|
||||
if isinstance(event, events.PullRequestCommentEvent):
|
||||
(title, text, fields, overrides) \
|
||||
= self.format_pull_request_comment_event(event, data)
|
||||
|
|
@ -176,12 +174,12 @@ class SlackIntegrationType(IntegrationTypeBase, CommitParsingDataHandler):
|
|||
schema = SlackSettingsSchema()
|
||||
schema.add(colander.SchemaNode(
|
||||
colander.Set(),
|
||||
widget=deform.widget.CheckboxChoiceWidget(
|
||||
widget=CheckboxChoiceWidgetDesc(
|
||||
values=sorted(
|
||||
[(e.name, e.display_name) for e in self.valid_events]
|
||||
)
|
||||
[(e.name, e.display_name, e.description) for e in self.valid_events]
|
||||
),
|
||||
),
|
||||
description="Events activated for this integration",
|
||||
description="List of events activated for this integration",
|
||||
name='events'
|
||||
))
|
||||
|
||||
|
|
|
|||
|
|
@ -20,13 +20,14 @@
|
|||
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import deform
|
||||
import deform.widget
|
||||
import logging
|
||||
import colander
|
||||
|
||||
import rhodecode
|
||||
from rhodecode import events
|
||||
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,
|
||||
|
|
@ -53,11 +54,12 @@ class WebhookSettingsSchema(colander.Schema):
|
|||
'objects in data in such cases.'),
|
||||
missing=colander.required,
|
||||
required=True,
|
||||
preparer=strip_whitespace,
|
||||
validator=colander.url,
|
||||
widget=widgets.CodeMirrorWidget(
|
||||
help_block_collapsable_name='Show url variables',
|
||||
help_block_collapsable=(
|
||||
'E.g http://my-serv/trigger_job/${{event_name}}'
|
||||
'E.g http://my-serv.com/trigger_job/${{event_name}}'
|
||||
'?PR_ID=${{pull_request_id}}'
|
||||
'\nFull list of vars:\n{}'.format(URL_VARS)),
|
||||
codemirror_mode='text',
|
||||
|
|
@ -146,34 +148,31 @@ class WebhookIntegrationType(IntegrationTypeBase):
|
|||
events.PullRequestCreateEvent,
|
||||
events.RepoPushEvent,
|
||||
events.RepoCreateEvent,
|
||||
events.RepoCommitCommentEvent,
|
||||
]
|
||||
|
||||
def settings_schema(self):
|
||||
schema = WebhookSettingsSchema()
|
||||
schema.add(colander.SchemaNode(
|
||||
colander.Set(),
|
||||
widget=deform.widget.CheckboxChoiceWidget(
|
||||
widget=CheckboxChoiceWidgetDesc(
|
||||
values=sorted(
|
||||
[(e.name, e.display_name) for e in self.valid_events]
|
||||
)
|
||||
[(e.name, e.display_name, e.description) for e in self.valid_events]
|
||||
),
|
||||
),
|
||||
description="Events activated for this integration",
|
||||
description="List of events activated for this integration",
|
||||
name='events'
|
||||
))
|
||||
return schema
|
||||
|
||||
def send_event(self, event):
|
||||
log.debug(
|
||||
'handling event %s with Webhook integration %s', event.name, self)
|
||||
log.debug('handling event %s with integration %s', event.name, self)
|
||||
|
||||
if event.__class__ not in self.valid_events:
|
||||
log.debug('event not valid: %r', event)
|
||||
log.debug('event %r not present in valid event list (%s)', event, self.valid_events)
|
||||
return
|
||||
|
||||
allowed_events = self.settings['events']
|
||||
if event.name not in allowed_events:
|
||||
log.debug('event ignored: %r event %s not in allowed events %s',
|
||||
event, event.name, allowed_events)
|
||||
if not self.event_enabled(event):
|
||||
return
|
||||
|
||||
data = event.as_dict()
|
||||
|
|
|
|||
|
|
@ -20,13 +20,40 @@
|
|||
|
||||
import logging
|
||||
|
||||
import deform
|
||||
import deform.widget
|
||||
|
||||
from deform.widget import null, OptGroup, string_types
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def _normalize_choices(values):
|
||||
result = []
|
||||
for item in values:
|
||||
if isinstance(item, OptGroup):
|
||||
normalized_options = _normalize_choices(item.options)
|
||||
result.append(OptGroup(item.label, *normalized_options))
|
||||
else:
|
||||
value, description, help_block = item
|
||||
if not isinstance(value, string_types):
|
||||
value = str(value)
|
||||
result.append((value, description, help_block))
|
||||
return result
|
||||
|
||||
|
||||
class CodeMirrorWidget(deform.widget.TextAreaWidget):
|
||||
template = 'codemirror'
|
||||
requirements = (('deform', None), ('codemirror', None))
|
||||
|
||||
|
||||
class CheckboxChoiceWidgetDesc(deform.widget.CheckboxChoiceWidget):
|
||||
template = "checkbox_choice_desc"
|
||||
|
||||
def serialize(self, field, cstruct, **kw):
|
||||
if cstruct in (null, None):
|
||||
cstruct = ()
|
||||
readonly = kw.get("readonly", self.readonly)
|
||||
values = kw.get("values", self.values)
|
||||
kw["values"] = _normalize_choices(values)
|
||||
template = readonly and self.readonly_template or self.template
|
||||
tmpl_values = self.get_template_values(field, cstruct, kw)
|
||||
return field.renderer(template, **tmpl_values)
|
||||
|
|
|
|||
|
|
@ -22,4 +22,4 @@
|
|||
</div>
|
||||
</div>
|
||||
${field.end_sequence()}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
26
rhodecode/templates/forms/checkbox_choice_desc.pt
Normal file
26
rhodecode/templates/forms/checkbox_choice_desc.pt
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
<div tal:define="css_class css_class|field.widget.css_class;
|
||||
style style|field.widget.style;
|
||||
oid oid|field.oid;
|
||||
inline getattr(field.widget, 'inline', False)"
|
||||
tal:omit-tag="not inline">
|
||||
${field.start_sequence()}
|
||||
<div tal:repeat="choice values | field.widget.values"
|
||||
tal:omit-tag="inline"
|
||||
class="checkbox">
|
||||
<div tal:define="(value, title, help_block) choice">
|
||||
<input tal:attributes="checked value in cstruct;
|
||||
class css_class;
|
||||
style style"
|
||||
type="checkbox"
|
||||
name="checkbox"
|
||||
value="${value}"
|
||||
id="${oid}-${repeat.choice.index}"/>
|
||||
<label for="${oid}-${repeat.choice.index}"
|
||||
tal:attributes="class inline and 'checkbox-inline'">
|
||||
${title}
|
||||
</label>
|
||||
<p tal:condition="help_block" class="help-block">${help_block}</p>
|
||||
</div>
|
||||
</div>
|
||||
${field.end_sequence()}
|
||||
</div>
|
||||
|
|
@ -40,7 +40,7 @@ from rhodecode.events import (
|
|||
PullRequestUpdateEvent,
|
||||
PullRequestReviewEvent,
|
||||
PullRequestMergeEvent,
|
||||
PullRequestCloseEvent,
|
||||
PullRequestCloseEvent
|
||||
])
|
||||
def test_pullrequest_events_serialized(EventClass, pr_util, config_stub):
|
||||
pr = pr_util.create_pull_request()
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@
|
|||
|
||||
import pytest
|
||||
|
||||
from rhodecode.lib.utils2 import StrictAttributeDict
|
||||
from rhodecode.tests.events.conftest import EventCatcher
|
||||
|
||||
from rhodecode.lib import hooks_base, utils2
|
||||
|
|
@ -28,7 +29,7 @@ from rhodecode.events.repo import (
|
|||
RepoPrePullEvent, RepoPullEvent,
|
||||
RepoPrePushEvent, RepoPushEvent,
|
||||
RepoPreCreateEvent, RepoCreateEvent,
|
||||
RepoPreDeleteEvent, RepoDeleteEvent,
|
||||
RepoPreDeleteEvent, RepoDeleteEvent, RepoCommitCommentEvent,
|
||||
)
|
||||
|
||||
|
||||
|
|
@ -121,3 +122,24 @@ def test_push_fires_events(scm_extras):
|
|||
hooks_base.post_pull(scm_extras)
|
||||
assert event_catcher.events_types == [RepoPullEvent]
|
||||
|
||||
|
||||
@pytest.mark.parametrize('EventClass', [RepoCommitCommentEvent])
|
||||
def test_repo_commit_event(config_stub, repo_stub, EventClass):
|
||||
|
||||
commit = StrictAttributeDict({
|
||||
'raw_id': 'raw_id',
|
||||
'message': 'message',
|
||||
'branch': 'branch',
|
||||
})
|
||||
|
||||
comment = StrictAttributeDict({
|
||||
'comment_id': 'comment_id',
|
||||
'text': 'text',
|
||||
'comment_type': 'comment_type',
|
||||
'f_path': 'f_path',
|
||||
'line_no': 'line_no',
|
||||
})
|
||||
event = EventClass(repo=repo_stub, commit=commit, comment=comment)
|
||||
data = event.as_dict()
|
||||
assert data['commit']['commit_id']
|
||||
assert data['comment']['comment_id']
|
||||
|
|
|
|||
|
|
@ -49,6 +49,7 @@ class TestDeleteScopesDeletesIntegrations(object):
|
|||
|
||||
count = 1
|
||||
|
||||
|
||||
def counter():
|
||||
global count
|
||||
val = count
|
||||
|
|
|
|||
|
|
@ -52,8 +52,7 @@ def test_webhook_parse_url_invalid_event():
|
|||
handler(event, {})
|
||||
|
||||
err = str(err.value)
|
||||
assert err.startswith(
|
||||
'event type `%s` not in supported list' % event.__class__)
|
||||
assert err == "event type `<class 'rhodecode.events.repo.RepoDeleteEvent'>` has no handler defined"
|
||||
|
||||
|
||||
@pytest.mark.parametrize('template,expected_urls', [
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue