##// END OF EJS Templates
events: added support for pull-request-comment and commit-comment events....
marcink -
r4314:9d74b996 default
parent child Browse files
Show More
@@ -0,0 +1,26 b''
1 <div tal:define="css_class css_class|field.widget.css_class;
2 style style|field.widget.style;
3 oid oid|field.oid;
4 inline getattr(field.widget, 'inline', False)"
5 tal:omit-tag="not inline">
6 ${field.start_sequence()}
7 <div tal:repeat="choice values | field.widget.values"
8 tal:omit-tag="inline"
9 class="checkbox">
10 <div tal:define="(value, title, help_block) choice">
11 <input tal:attributes="checked value in cstruct;
12 class css_class;
13 style style"
14 type="checkbox"
15 name="checkbox"
16 value="${value}"
17 id="${oid}-${repeat.choice.index}"/>
18 <label for="${oid}-${repeat.choice.index}"
19 tal:attributes="class inline and 'checkbox-inline'">
20 ${title}
21 </label>
22 <p tal:condition="help_block" class="help-block">${help_block}</p>
23 </div>
24 </div>
25 ${field.end_sequence()}
26 </div>
@@ -75,4 +75,5 b' from rhodecode.events.pullrequest import'
75 75 PullRequestReviewEvent,
76 76 PullRequestMergeEvent,
77 77 PullRequestCloseEvent,
78 PullRequestCommentEvent,
78 79 )
@@ -44,6 +44,9 b' class RhodecodeEvent(object):'
44 44 self._request = request
45 45 self.utc_timestamp = datetime.datetime.utcnow()
46 46
47 def __repr__(self):
48 return '<%s:(%s)>' % (self.__class__.__name__, self.name)
49
47 50 def get_request(self):
48 51 if self._request:
49 52 return self._request
@@ -116,3 +119,4 b' class RhodeCodeIntegrationEvent(Rhodecod'
116 119 """
117 120 Special subclass for Integration events
118 121 """
122 description = ''
@@ -77,6 +77,7 b' class PullRequestCreateEvent(PullRequest'
77 77 """
78 78 name = 'pullrequest-create'
79 79 display_name = lazy_ugettext('pullrequest created')
80 description = lazy_ugettext('Event triggered after pull request was created')
80 81
81 82
82 83 class PullRequestCloseEvent(PullRequestEvent):
@@ -86,6 +87,7 b' class PullRequestCloseEvent(PullRequestE'
86 87 """
87 88 name = 'pullrequest-close'
88 89 display_name = lazy_ugettext('pullrequest closed')
90 description = lazy_ugettext('Event triggered after pull request was closed')
89 91
90 92
91 93 class PullRequestUpdateEvent(PullRequestEvent):
@@ -95,6 +97,7 b' class PullRequestUpdateEvent(PullRequest'
95 97 """
96 98 name = 'pullrequest-update'
97 99 display_name = lazy_ugettext('pullrequest commits updated')
100 description = lazy_ugettext('Event triggered after pull requests was updated')
98 101
99 102
100 103 class PullRequestReviewEvent(PullRequestEvent):
@@ -104,6 +107,8 b' class PullRequestReviewEvent(PullRequest'
104 107 """
105 108 name = 'pullrequest-review'
106 109 display_name = lazy_ugettext('pullrequest review changed')
110 description = lazy_ugettext('Event triggered after a review status of a '
111 'pull requests has changed to other.')
107 112
108 113 def __init__(self, pullrequest, status):
109 114 super(PullRequestReviewEvent, self).__init__(pullrequest)
@@ -117,6 +122,8 b' class PullRequestMergeEvent(PullRequestE'
117 122 """
118 123 name = 'pullrequest-merge'
119 124 display_name = lazy_ugettext('pullrequest merged')
125 description = lazy_ugettext('Event triggered after a successful merge operation '
126 'was executed on a pull request')
120 127
121 128
122 129 class PullRequestCommentEvent(PullRequestEvent):
@@ -126,6 +133,8 b' class PullRequestCommentEvent(PullReques'
126 133 """
127 134 name = 'pullrequest-comment'
128 135 display_name = lazy_ugettext('pullrequest commented')
136 description = lazy_ugettext('Event triggered after a comment was made on a code '
137 'in the pull request')
129 138
130 139 def __init__(self, pullrequest, comment):
131 140 super(PullRequestCommentEvent, self).__init__(pullrequest)
@@ -186,13 +186,33 b' class RepoCommitCommentEvent(RepoEvent):'
186 186 An instance of this class is emitted as an :term:`event` after a comment is made
187 187 on repository commit.
188 188 """
189
190 name = 'repo-commit-comment'
191 display_name = lazy_ugettext('repository commit comment')
192 description = lazy_ugettext('Event triggered after a comment was made '
193 'on commit inside a repository')
194
189 195 def __init__(self, repo, commit, comment):
190 196 super(RepoCommitCommentEvent, self).__init__(repo)
191 197 self.commit = commit
192 198 self.comment = comment
193 199
194 name = 'repo-commit-comment'
195 display_name = lazy_ugettext('repository commit comment')
200 def as_dict(self):
201 data = super(RepoCommitCommentEvent, self).as_dict()
202 data['commit'] = {
203 'commit_id': self.commit.raw_id,
204 'commit_message': self.commit.message,
205 'commit_branch': self.commit.branch,
206 }
207
208 data['comment'] = {
209 'comment_id': self.comment.comment_id,
210 'comment_text': self.comment.text,
211 'comment_type': self.comment.comment_type,
212 'comment_f_path': self.comment.f_path,
213 'comment_line_no': self.comment.line_no,
214 }
215 return data
196 216
197 217
198 218 class RepoPreCreateEvent(RepoEvent):
@@ -202,6 +222,7 b' class RepoPreCreateEvent(RepoEvent):'
202 222 """
203 223 name = 'repo-pre-create'
204 224 display_name = lazy_ugettext('repository pre create')
225 description = lazy_ugettext('Event triggered before repository is created')
205 226
206 227
207 228 class RepoCreateEvent(RepoEvent):
@@ -211,6 +232,7 b' class RepoCreateEvent(RepoEvent):'
211 232 """
212 233 name = 'repo-create'
213 234 display_name = lazy_ugettext('repository created')
235 description = lazy_ugettext('Event triggered after repository was created')
214 236
215 237
216 238 class RepoPreDeleteEvent(RepoEvent):
@@ -220,6 +242,7 b' class RepoPreDeleteEvent(RepoEvent):'
220 242 """
221 243 name = 'repo-pre-delete'
222 244 display_name = lazy_ugettext('repository pre delete')
245 description = lazy_ugettext('Event triggered before a repository is deleted')
223 246
224 247
225 248 class RepoDeleteEvent(RepoEvent):
@@ -229,6 +252,7 b' class RepoDeleteEvent(RepoEvent):'
229 252 """
230 253 name = 'repo-delete'
231 254 display_name = lazy_ugettext('repository deleted')
255 description = lazy_ugettext('Event triggered after repository was deleted')
232 256
233 257
234 258 class RepoVCSEvent(RepoEvent):
@@ -269,6 +293,7 b' class RepoPrePullEvent(RepoVCSEvent):'
269 293 """
270 294 name = 'repo-pre-pull'
271 295 display_name = lazy_ugettext('repository pre pull')
296 description = lazy_ugettext('Event triggered before repository code is pulled')
272 297
273 298
274 299 class RepoPullEvent(RepoVCSEvent):
@@ -278,6 +303,7 b' class RepoPullEvent(RepoVCSEvent):'
278 303 """
279 304 name = 'repo-pull'
280 305 display_name = lazy_ugettext('repository pull')
306 description = lazy_ugettext('Event triggered after repository code was pulled')
281 307
282 308
283 309 class RepoPrePushEvent(RepoVCSEvent):
@@ -287,6 +313,8 b' class RepoPrePushEvent(RepoVCSEvent):'
287 313 """
288 314 name = 'repo-pre-push'
289 315 display_name = lazy_ugettext('repository pre push')
316 description = lazy_ugettext('Event triggered before the code is '
317 'pushed to a repository')
290 318
291 319
292 320 class RepoPushEvent(RepoVCSEvent):
@@ -298,6 +326,8 b' class RepoPushEvent(RepoVCSEvent):'
298 326 """
299 327 name = 'repo-push'
300 328 display_name = lazy_ugettext('repository push')
329 description = lazy_ugettext('Event triggered after the code was '
330 'pushed to a repository')
301 331
302 332 def __init__(self, repo_name, pushed_commit_ids, extras):
303 333 super(RepoPushEvent, self).__init__(repo_name, extras)
@@ -60,6 +60,7 b' class RepoGroupCreateEvent(RepoGroupEven'
60 60 """
61 61 name = 'repo-group-create'
62 62 display_name = lazy_ugettext('repository group created')
63 description = lazy_ugettext('Event triggered after a repository group was created')
63 64
64 65
65 66 class RepoGroupDeleteEvent(RepoGroupEvent):
@@ -69,6 +70,7 b' class RepoGroupDeleteEvent(RepoGroupEven'
69 70 """
70 71 name = 'repo-group-delete'
71 72 display_name = lazy_ugettext('repository group deleted')
73 description = lazy_ugettext('Event triggered after a repository group was deleted')
72 74
73 75
74 76 class RepoGroupUpdateEvent(RepoGroupEvent):
@@ -78,3 +80,4 b' class RepoGroupUpdateEvent(RepoGroupEven'
78 80 """
79 81 name = 'repo-group-update'
80 82 display_name = lazy_ugettext('repository group update')
83 description = lazy_ugettext('Event triggered after a repository group was updated')
@@ -125,6 +125,19 b' class IntegrationTypeBase(object):'
125 125 """
126 126 return colander.Schema()
127 127
128 def event_enabled(self, event):
129 """
130 Checks if submitted event is enabled based on the plugin settings
131 :param event:
132 :return: bool
133 """
134 allowed_events = self.settings['events']
135 if event.name not in allowed_events:
136 log.debug('event ignored: %r event %s not in allowed set of events %s',
137 event, event.name, allowed_events)
138 return False
139 return True
140
128 141
129 142 class EEIntegration(IntegrationTypeBase):
130 143 description = 'Integration available in RhodeCode EE edition.'
@@ -139,30 +152,57 b' class EEIntegration(IntegrationTypeBase)'
139 152 # Helpers #
140 153 # updating this required to update the `common_vars` as well.
141 154 WEBHOOK_URL_VARS = [
142 ('event_name', 'Unique name of the event type, e.g pullrequest-update'),
143 ('repo_name', 'Full name of the repository'),
144 ('repo_type', 'VCS type of repository'),
145 ('repo_id', 'Unique id of repository'),
146 ('repo_url', 'Repository url'),
155 # GENERAL
156 ('General', [
157 ('event_name', 'Unique name of the event type, e.g pullrequest-update'),
158 ('repo_name', 'Full name of the repository'),
159 ('repo_type', 'VCS type of repository'),
160 ('repo_id', 'Unique id of repository'),
161 ('repo_url', 'Repository url'),
162 ]
163 ),
147 164 # extra repo fields
148 ('extra:<extra_key_name>', 'Extra repo variables, read from its settings.'),
149
165 ('Repository', [
166 ('extra:<extra_key_name>', 'Extra repo variables, read from its settings.'),
167 ]
168 ),
150 169 # special attrs below that we handle, using multi-call
151 ('branch', 'Name of each branch submitted, if any.'),
152 ('branch_head', 'Head ID of pushed branch (full sha of last commit), if any.'),
153 ('commit_id', 'ID (full sha) of each commit submitted, if any.'),
154
170 ('Commit push - Multicalls', [
171 ('branch', 'Name of each branch submitted, if any.'),
172 ('branch_head', 'Head ID of pushed branch (full sha of last commit), if any.'),
173 ('commit_id', 'ID (full sha) of each commit submitted, if any.'),
174 ]
175 ),
155 176 # pr events vars
156 ('pull_request_id', 'Unique ID of the pull request.'),
157 ('pull_request_title', 'Title of the pull request.'),
158 ('pull_request_url', 'Pull request url.'),
159 ('pull_request_shadow_url', 'Pull request shadow repo clone url.'),
160 ('pull_request_commits_uid', 'Calculated UID of all commits inside the PR. '
161 'Changes after PR update'),
177 ('Pull request', [
178 ('pull_request_id', 'Unique ID of the pull request.'),
179 ('pull_request_title', 'Title of the pull request.'),
180 ('pull_request_url', 'Pull request url.'),
181 ('pull_request_shadow_url', 'Pull request shadow repo clone url.'),
182 ('pull_request_commits_uid', 'Calculated UID of all commits inside the PR. '
183 'Changes after PR update'),
184 ]
185 ),
186 # commit comment event vars
187 ('Commit comment', [
188 ('commit_comment_id', 'Unique ID of the comment made on a commit.'),
189 ('commit_comment_text', 'Text of commit comment.'),
190 ('commit_comment_type', 'Type of comment, e.g note/todo.'),
162 191
192 ('commit_comment_f_path', 'Optionally path of file for inline comments.'),
193 ('commit_comment_line_no', 'Line number of the file: eg o10, or n200'),
194
195 ('commit_comment_commit_id', 'Commit id that comment was left at.'),
196 ('commit_comment_commit_branch', 'Commit branch that comment was left at'),
197 ('commit_comment_commit_message', 'Commit message that comment was left at'),
198 ]
199 ),
163 200 # user who triggers the call
164 ('username', 'User who triggered the call.'),
165 ('user_id', 'User id who triggered the call.'),
201 ('Caller', [
202 ('username', 'User who triggered the call.'),
203 ('user_id', 'User id who triggered the call.'),
204 ]
205 ),
166 206 ]
167 207
168 208 # common vars for url template used for CI plugins. Shared with webhook
@@ -271,6 +311,26 b' class WebhookDataHandler(CommitParsingDa'
271 311
272 312 return url_calls
273 313
314 def repo_commit_comment_handler(self, event, data):
315 url = self.get_base_parsed_template(data)
316 log.debug('register %s call(%s) to url %s', self.name, event, url)
317 comment_vars = [
318 ('commit_comment_id', data['comment']['comment_id']),
319 ('commit_comment_text', data['comment']['comment_text']),
320 ('commit_comment_type', data['comment']['comment_type']),
321
322 ('commit_comment_f_path', data['comment']['comment_f_path']),
323 ('commit_comment_line_no', data['comment']['comment_line_no']),
324
325 ('commit_comment_commit_id', data['commit']['commit_id']),
326 ('commit_comment_commit_branch', data['commit']['commit_branch']),
327 ('commit_comment_commit_message', data['commit']['commit_message']),
328 ]
329 for k, v in comment_vars:
330 url = UrlTmpl(url).safe_substitute(**{k: v})
331
332 return [(url, self.headers, data)]
333
274 334 def repo_create_event_handler(self, event, data):
275 335 url = self.get_base_parsed_template(data)
276 336 log.debug('register %s call(%s) to url %s', self.name, event, url)
@@ -298,12 +358,13 b' class WebhookDataHandler(CommitParsingDa'
298 358 return self.repo_push_event_handler(event, data)
299 359 elif isinstance(event, events.RepoCreateEvent):
300 360 return self.repo_create_event_handler(event, data)
361 elif isinstance(event, events.RepoCommitCommentEvent):
362 return self.repo_commit_comment_handler(event, data)
301 363 elif isinstance(event, events.PullRequestEvent):
302 364 return self.pull_request_event_handler(event, data)
303 365 else:
304 366 raise ValueError(
305 'event type `%s` not in supported list: %s' % (
306 event.__class__, events))
367 'event type `{}` has no handler defined'.format(event.__class__))
307 368
308 369
309 370 def get_auth(settings):
@@ -320,9 +381,13 b' def get_web_token(settings):'
320 381
321 382
322 383 def get_url_vars(url_vars):
323 return '\n'.join(
324 '{} - {}'.format('${' + key + '}', explanation)
325 for key, explanation in url_vars)
384 items = []
385
386 for section, section_items in url_vars:
387 items.append('\n*{}*'.format(section))
388 for key, explanation in section_items:
389 items.append(' {} - {}'.format('${' + key + '}', explanation))
390 return '\n'.join(items)
326 391
327 392
328 393 def render_with_traceback(template, *args, **kwargs):
@@ -19,13 +19,14 b''
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 from __future__ import unicode_literals
22 import deform
23 22 import logging
23
24 24 import colander
25
25 import deform.widget
26 26 from mako.template import Template
27 27
28 28 from rhodecode import events
29 from rhodecode.model.validation_schema.widgets import CheckboxChoiceWidgetDesc
29 30 from rhodecode.translation import _
30 31 from rhodecode.lib.celerylib import run_task
31 32 from rhodecode.lib.celerylib import tasks
@@ -174,6 +175,10 b' class EmailIntegrationType(IntegrationTy'
174 175 display_name = _('Email')
175 176 description = _('Send repo push summaries to a list of recipients via email')
176 177
178 valid_events = [
179 events.RepoPushEvent
180 ]
181
177 182 @classmethod
178 183 def icon(cls):
179 184 return '''
@@ -240,59 +245,86 b' class EmailIntegrationType(IntegrationTy'
240 245
241 246 def settings_schema(self):
242 247 schema = EmailSettingsSchema()
248 schema.add(colander.SchemaNode(
249 colander.Set(),
250 widget=CheckboxChoiceWidgetDesc(
251 values=sorted(
252 [(e.name, e.display_name, e.description) for e in self.valid_events]
253 ),
254 ),
255 description="List of events activated for this integration",
256 name='events'
257 ))
243 258 return schema
244 259
245 260 def send_event(self, event):
246 data = event.as_dict()
247 log.debug('got event: %r', event)
261 log.debug('handling event %s with integration %s', event.name, self)
262
263 if event.__class__ not in self.valid_events:
264 log.debug('event %r not present in valid event list (%s)', event, self.valid_events)
265 return
266
267 if not self.event_enabled(event):
268 # NOTE(marcink): for legacy reasons we're skipping this check...
269 # since the email event haven't had any settings...
270 pass
248 271
272 handler = EmailEventHandler(self.settings)
273 handler(event, event_data=event.as_dict())
274
275
276 class EmailEventHandler(object):
277 def __init__(self, integration_settings):
278 self.integration_settings = integration_settings
279
280 def __call__(self, event, event_data):
249 281 if isinstance(event, events.RepoPushEvent):
250 repo_push_handler(data, self.settings)
282 self.repo_push_handler(event, event_data)
251 283 else:
252 284 log.debug('ignoring event: %r', event)
253 285
254
255 def repo_push_handler(data, settings):
256 commit_num = len(data['push']['commits'])
257 server_url = data['server_url']
286 def repo_push_handler(self, event, data):
287 commit_num = len(data['push']['commits'])
288 server_url = data['server_url']
258 289
259 if commit_num == 1:
260 if data['push']['branches']:
261 _subject = '[{repo_name}] {author} pushed {commit_num} commit on branches: {branches}'
262 else:
263 _subject = '[{repo_name}] {author} pushed {commit_num} commit'
264 subject = _subject.format(
265 author=data['actor']['username'],
266 repo_name=data['repo']['repo_name'],
267 commit_num=commit_num,
268 branches=', '.join(
269 branch['name'] for branch in data['push']['branches'])
270 )
271 else:
272 if data['push']['branches']:
273 _subject = '[{repo_name}] {author} pushed {commit_num} commits on branches: {branches}'
290 if commit_num == 1:
291 if data['push']['branches']:
292 _subject = '[{repo_name}] {author} pushed {commit_num} commit on branches: {branches}'
293 else:
294 _subject = '[{repo_name}] {author} pushed {commit_num} commit'
295 subject = _subject.format(
296 author=data['actor']['username'],
297 repo_name=data['repo']['repo_name'],
298 commit_num=commit_num,
299 branches=', '.join(
300 branch['name'] for branch in data['push']['branches'])
301 )
274 302 else:
275 _subject = '[{repo_name}] {author} pushed {commit_num} commits'
276 subject = _subject.format(
277 author=data['actor']['username'],
278 repo_name=data['repo']['repo_name'],
279 commit_num=commit_num,
280 branches=', '.join(
281 branch['name'] for branch in data['push']['branches']))
303 if data['push']['branches']:
304 _subject = '[{repo_name}] {author} pushed {commit_num} commits on branches: {branches}'
305 else:
306 _subject = '[{repo_name}] {author} pushed {commit_num} commits'
307 subject = _subject.format(
308 author=data['actor']['username'],
309 repo_name=data['repo']['repo_name'],
310 commit_num=commit_num,
311 branches=', '.join(
312 branch['name'] for branch in data['push']['branches']))
282 313
283 email_body_plaintext = render_with_traceback(
284 REPO_PUSH_TEMPLATE_PLAINTEXT,
285 data=data,
286 subject=subject,
287 instance_url=server_url)
314 email_body_plaintext = render_with_traceback(
315 REPO_PUSH_TEMPLATE_PLAINTEXT,
316 data=data,
317 subject=subject,
318 instance_url=server_url)
288 319
289 email_body_html = render_with_traceback(
290 REPO_PUSH_TEMPLATE_HTML,
291 data=data,
292 subject=subject,
293 instance_url=server_url)
320 email_body_html = render_with_traceback(
321 REPO_PUSH_TEMPLATE_HTML,
322 data=data,
323 subject=subject,
324 instance_url=server_url)
294 325
295 for email_address in settings['recipients']:
296 run_task(
297 tasks.send_email, email_address, subject,
298 email_body_plaintext, email_body_html)
326 recipients = self.integration_settings['recipients']
327 for email_address in recipients:
328 run_task(
329 tasks.send_email, email_address, subject,
330 email_body_plaintext, email_body_html)
@@ -26,6 +26,7 b' import colander'
26 26 import textwrap
27 27 from mako.template import Template
28 28 from rhodecode import events
29 from rhodecode.model.validation_schema.widgets import CheckboxChoiceWidgetDesc
29 30 from rhodecode.translation import _
30 31 from rhodecode.lib import helpers as h
31 32 from rhodecode.lib.celerylib import run_task, async_task, RequestContextTask
@@ -119,13 +120,10 b' class HipchatIntegrationType(Integration'
119 120
120 121 def send_event(self, event):
121 122 if event.__class__ not in self.valid_events:
122 log.debug('event not valid: %r', event)
123 log.debug('event %r not present in valid event list (%s)', event, self.valid_events)
123 124 return
124 125
125 allowed_events = self.settings['events']
126 if event.name not in allowed_events:
127 log.debug('event ignored: %r event %s not in allowed events %s',
128 event, event.name, allowed_events)
126 if not self.event_enabled(event):
129 127 return
130 128
131 129 data = event.as_dict()
@@ -133,8 +131,6 b' class HipchatIntegrationType(Integration'
133 131 text = '<b>%s<b> caused a <b>%s</b> event' % (
134 132 data['actor']['username'], event.name)
135 133
136 log.debug('handling hipchat event for %s', event.name)
137
138 134 if isinstance(event, events.PullRequestCommentEvent):
139 135 text = self.format_pull_request_comment_event(event, data)
140 136 elif isinstance(event, events.PullRequestReviewEvent):
@@ -154,12 +150,12 b' class HipchatIntegrationType(Integration'
154 150 schema = HipchatSettingsSchema()
155 151 schema.add(colander.SchemaNode(
156 152 colander.Set(),
157 widget=deform.widget.CheckboxChoiceWidget(
153 widget=CheckboxChoiceWidgetDesc(
158 154 values=sorted(
159 [(e.name, e.display_name) for e in self.valid_events]
160 )
155 [(e.name, e.display_name, e.description) for e in self.valid_events]
156 ),
161 157 ),
162 description="Events activated for this integration",
158 description="List of events activated for this integration",
163 159 name='events'
164 160 ))
165 161
@@ -30,6 +30,7 b' import colander'
30 30 from mako.template import Template
31 31
32 32 from rhodecode import events
33 from rhodecode.model.validation_schema.widgets import CheckboxChoiceWidgetDesc
33 34 from rhodecode.translation import _
34 35 from rhodecode.lib import helpers as h
35 36 from rhodecode.lib.celerylib import run_task, async_task, RequestContextTask
@@ -134,14 +135,13 b' class SlackIntegrationType(IntegrationTy'
134 135 ]
135 136
136 137 def send_event(self, event):
138 log.debug('handling event %s with integration %s', event.name, self)
139
137 140 if event.__class__ not in self.valid_events:
138 log.debug('event not valid: %r', event)
141 log.debug('event %r not present in valid event list (%s)', event, self.valid_events)
139 142 return
140 143
141 allowed_events = self.settings['events']
142 if event.name not in allowed_events:
143 log.debug('event ignored: %r event %s not in allowed events %s',
144 event, event.name, allowed_events)
144 if not self.event_enabled(event):
145 145 return
146 146
147 147 data = event.as_dict()
@@ -154,8 +154,6 b' class SlackIntegrationType(IntegrationTy'
154 154 fields = None
155 155 overrides = None
156 156
157 log.debug('handling slack event for %s', event.name)
158
159 157 if isinstance(event, events.PullRequestCommentEvent):
160 158 (title, text, fields, overrides) \
161 159 = self.format_pull_request_comment_event(event, data)
@@ -176,12 +174,12 b' class SlackIntegrationType(IntegrationTy'
176 174 schema = SlackSettingsSchema()
177 175 schema.add(colander.SchemaNode(
178 176 colander.Set(),
179 widget=deform.widget.CheckboxChoiceWidget(
177 widget=CheckboxChoiceWidgetDesc(
180 178 values=sorted(
181 [(e.name, e.display_name) for e in self.valid_events]
182 )
179 [(e.name, e.display_name, e.description) for e in self.valid_events]
180 ),
183 181 ),
184 description="Events activated for this integration",
182 description="List of events activated for this integration",
185 183 name='events'
186 184 ))
187 185
@@ -20,13 +20,14 b''
20 20
21 21 from __future__ import unicode_literals
22 22
23 import deform
24 23 import deform.widget
25 24 import logging
26 25 import colander
27 26
28 27 import rhodecode
29 28 from rhodecode import events
29 from rhodecode.lib.colander_utils import strip_whitespace
30 from rhodecode.model.validation_schema.widgets import CheckboxChoiceWidgetDesc
30 31 from rhodecode.translation import _
31 32 from rhodecode.integrations.types.base import (
32 33 IntegrationTypeBase, get_auth, get_web_token, get_url_vars,
@@ -53,11 +54,12 b' class WebhookSettingsSchema(colander.Sch'
53 54 'objects in data in such cases.'),
54 55 missing=colander.required,
55 56 required=True,
57 preparer=strip_whitespace,
56 58 validator=colander.url,
57 59 widget=widgets.CodeMirrorWidget(
58 60 help_block_collapsable_name='Show url variables',
59 61 help_block_collapsable=(
60 'E.g http://my-serv/trigger_job/${{event_name}}'
62 'E.g http://my-serv.com/trigger_job/${{event_name}}'
61 63 '?PR_ID=${{pull_request_id}}'
62 64 '\nFull list of vars:\n{}'.format(URL_VARS)),
63 65 codemirror_mode='text',
@@ -146,34 +148,31 b' class WebhookIntegrationType(Integration'
146 148 events.PullRequestCreateEvent,
147 149 events.RepoPushEvent,
148 150 events.RepoCreateEvent,
151 events.RepoCommitCommentEvent,
149 152 ]
150 153
151 154 def settings_schema(self):
152 155 schema = WebhookSettingsSchema()
153 156 schema.add(colander.SchemaNode(
154 157 colander.Set(),
155 widget=deform.widget.CheckboxChoiceWidget(
158 widget=CheckboxChoiceWidgetDesc(
156 159 values=sorted(
157 [(e.name, e.display_name) for e in self.valid_events]
158 )
160 [(e.name, e.display_name, e.description) for e in self.valid_events]
161 ),
159 162 ),
160 description="Events activated for this integration",
163 description="List of events activated for this integration",
161 164 name='events'
162 165 ))
163 166 return schema
164 167
165 168 def send_event(self, event):
166 log.debug(
167 'handling event %s with Webhook integration %s', event.name, self)
169 log.debug('handling event %s with integration %s', event.name, self)
168 170
169 171 if event.__class__ not in self.valid_events:
170 log.debug('event not valid: %r', event)
172 log.debug('event %r not present in valid event list (%s)', event, self.valid_events)
171 173 return
172 174
173 allowed_events = self.settings['events']
174 if event.name not in allowed_events:
175 log.debug('event ignored: %r event %s not in allowed events %s',
176 event, event.name, allowed_events)
175 if not self.event_enabled(event):
177 176 return
178 177
179 178 data = event.as_dict()
@@ -20,13 +20,40 b''
20 20
21 21 import logging
22 22
23 import deform
24 23 import deform.widget
24 from deform.widget import null, OptGroup, string_types
25
26 log = logging.getLogger(__name__)
25 27
26 28
27 log = logging.getLogger(__name__)
29 def _normalize_choices(values):
30 result = []
31 for item in values:
32 if isinstance(item, OptGroup):
33 normalized_options = _normalize_choices(item.options)
34 result.append(OptGroup(item.label, *normalized_options))
35 else:
36 value, description, help_block = item
37 if not isinstance(value, string_types):
38 value = str(value)
39 result.append((value, description, help_block))
40 return result
28 41
29 42
30 43 class CodeMirrorWidget(deform.widget.TextAreaWidget):
31 44 template = 'codemirror'
32 45 requirements = (('deform', None), ('codemirror', None))
46
47
48 class CheckboxChoiceWidgetDesc(deform.widget.CheckboxChoiceWidget):
49 template = "checkbox_choice_desc"
50
51 def serialize(self, field, cstruct, **kw):
52 if cstruct in (null, None):
53 cstruct = ()
54 readonly = kw.get("readonly", self.readonly)
55 values = kw.get("values", self.values)
56 kw["values"] = _normalize_choices(values)
57 template = readonly and self.readonly_template or self.template
58 tmpl_values = self.get_template_values(field, cstruct, kw)
59 return field.renderer(template, **tmpl_values)
@@ -22,4 +22,4 b''
22 22 </div>
23 23 </div>
24 24 ${field.end_sequence()}
25 </div> No newline at end of file
25 </div>
@@ -40,7 +40,7 b' from rhodecode.events import ('
40 40 PullRequestUpdateEvent,
41 41 PullRequestReviewEvent,
42 42 PullRequestMergeEvent,
43 PullRequestCloseEvent,
43 PullRequestCloseEvent
44 44 ])
45 45 def test_pullrequest_events_serialized(EventClass, pr_util, config_stub):
46 46 pr = pr_util.create_pull_request()
@@ -20,6 +20,7 b''
20 20
21 21 import pytest
22 22
23 from rhodecode.lib.utils2 import StrictAttributeDict
23 24 from rhodecode.tests.events.conftest import EventCatcher
24 25
25 26 from rhodecode.lib import hooks_base, utils2
@@ -28,7 +29,7 b' from rhodecode.events.repo import ('
28 29 RepoPrePullEvent, RepoPullEvent,
29 30 RepoPrePushEvent, RepoPushEvent,
30 31 RepoPreCreateEvent, RepoCreateEvent,
31 RepoPreDeleteEvent, RepoDeleteEvent,
32 RepoPreDeleteEvent, RepoDeleteEvent, RepoCommitCommentEvent,
32 33 )
33 34
34 35
@@ -121,3 +122,24 b' def test_push_fires_events(scm_extras):'
121 122 hooks_base.post_pull(scm_extras)
122 123 assert event_catcher.events_types == [RepoPullEvent]
123 124
125
126 @pytest.mark.parametrize('EventClass', [RepoCommitCommentEvent])
127 def test_repo_commit_event(config_stub, repo_stub, EventClass):
128
129 commit = StrictAttributeDict({
130 'raw_id': 'raw_id',
131 'message': 'message',
132 'branch': 'branch',
133 })
134
135 comment = StrictAttributeDict({
136 'comment_id': 'comment_id',
137 'text': 'text',
138 'comment_type': 'comment_type',
139 'f_path': 'f_path',
140 'line_no': 'line_no',
141 })
142 event = EventClass(repo=repo_stub, commit=commit, comment=comment)
143 data = event.as_dict()
144 assert data['commit']['commit_id']
145 assert data['comment']['comment_id']
@@ -49,6 +49,7 b' class TestDeleteScopesDeletesIntegration'
49 49
50 50 count = 1
51 51
52
52 53 def counter():
53 54 global count
54 55 val = count
@@ -52,8 +52,7 b' def test_webhook_parse_url_invalid_event'
52 52 handler(event, {})
53 53
54 54 err = str(err.value)
55 assert err.startswith(
56 'event type `%s` not in supported list' % event.__class__)
55 assert err == "event type `<class 'rhodecode.events.repo.RepoDeleteEvent'>` has no handler defined"
57 56
58 57
59 58 @pytest.mark.parametrize('template,expected_urls', [
General Comments 0
You need to be logged in to leave comments. Login now