##// 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 PullRequestReviewEvent,
75 PullRequestReviewEvent,
76 PullRequestMergeEvent,
76 PullRequestMergeEvent,
77 PullRequestCloseEvent,
77 PullRequestCloseEvent,
78 PullRequestCommentEvent,
78 )
79 )
@@ -44,6 +44,9 b' class RhodecodeEvent(object):'
44 self._request = request
44 self._request = request
45 self.utc_timestamp = datetime.datetime.utcnow()
45 self.utc_timestamp = datetime.datetime.utcnow()
46
46
47 def __repr__(self):
48 return '<%s:(%s)>' % (self.__class__.__name__, self.name)
49
47 def get_request(self):
50 def get_request(self):
48 if self._request:
51 if self._request:
49 return self._request
52 return self._request
@@ -116,3 +119,4 b' class RhodeCodeIntegrationEvent(Rhodecod'
116 """
119 """
117 Special subclass for Integration events
120 Special subclass for Integration events
118 """
121 """
122 description = ''
@@ -77,6 +77,7 b' class PullRequestCreateEvent(PullRequest'
77 """
77 """
78 name = 'pullrequest-create'
78 name = 'pullrequest-create'
79 display_name = lazy_ugettext('pullrequest created')
79 display_name = lazy_ugettext('pullrequest created')
80 description = lazy_ugettext('Event triggered after pull request was created')
80
81
81
82
82 class PullRequestCloseEvent(PullRequestEvent):
83 class PullRequestCloseEvent(PullRequestEvent):
@@ -86,6 +87,7 b' class PullRequestCloseEvent(PullRequestE'
86 """
87 """
87 name = 'pullrequest-close'
88 name = 'pullrequest-close'
88 display_name = lazy_ugettext('pullrequest closed')
89 display_name = lazy_ugettext('pullrequest closed')
90 description = lazy_ugettext('Event triggered after pull request was closed')
89
91
90
92
91 class PullRequestUpdateEvent(PullRequestEvent):
93 class PullRequestUpdateEvent(PullRequestEvent):
@@ -95,6 +97,7 b' class PullRequestUpdateEvent(PullRequest'
95 """
97 """
96 name = 'pullrequest-update'
98 name = 'pullrequest-update'
97 display_name = lazy_ugettext('pullrequest commits updated')
99 display_name = lazy_ugettext('pullrequest commits updated')
100 description = lazy_ugettext('Event triggered after pull requests was updated')
98
101
99
102
100 class PullRequestReviewEvent(PullRequestEvent):
103 class PullRequestReviewEvent(PullRequestEvent):
@@ -104,6 +107,8 b' class PullRequestReviewEvent(PullRequest'
104 """
107 """
105 name = 'pullrequest-review'
108 name = 'pullrequest-review'
106 display_name = lazy_ugettext('pullrequest review changed')
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 def __init__(self, pullrequest, status):
113 def __init__(self, pullrequest, status):
109 super(PullRequestReviewEvent, self).__init__(pullrequest)
114 super(PullRequestReviewEvent, self).__init__(pullrequest)
@@ -117,6 +122,8 b' class PullRequestMergeEvent(PullRequestE'
117 """
122 """
118 name = 'pullrequest-merge'
123 name = 'pullrequest-merge'
119 display_name = lazy_ugettext('pullrequest merged')
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 class PullRequestCommentEvent(PullRequestEvent):
129 class PullRequestCommentEvent(PullRequestEvent):
@@ -126,6 +133,8 b' class PullRequestCommentEvent(PullReques'
126 """
133 """
127 name = 'pullrequest-comment'
134 name = 'pullrequest-comment'
128 display_name = lazy_ugettext('pullrequest commented')
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 def __init__(self, pullrequest, comment):
139 def __init__(self, pullrequest, comment):
131 super(PullRequestCommentEvent, self).__init__(pullrequest)
140 super(PullRequestCommentEvent, self).__init__(pullrequest)
@@ -186,13 +186,33 b' class RepoCommitCommentEvent(RepoEvent):'
186 An instance of this class is emitted as an :term:`event` after a comment is made
186 An instance of this class is emitted as an :term:`event` after a comment is made
187 on repository commit.
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 def __init__(self, repo, commit, comment):
195 def __init__(self, repo, commit, comment):
190 super(RepoCommitCommentEvent, self).__init__(repo)
196 super(RepoCommitCommentEvent, self).__init__(repo)
191 self.commit = commit
197 self.commit = commit
192 self.comment = comment
198 self.comment = comment
193
199
194 name = 'repo-commit-comment'
200 def as_dict(self):
195 display_name = lazy_ugettext('repository commit comment')
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 class RepoPreCreateEvent(RepoEvent):
218 class RepoPreCreateEvent(RepoEvent):
@@ -202,6 +222,7 b' class RepoPreCreateEvent(RepoEvent):'
202 """
222 """
203 name = 'repo-pre-create'
223 name = 'repo-pre-create'
204 display_name = lazy_ugettext('repository pre create')
224 display_name = lazy_ugettext('repository pre create')
225 description = lazy_ugettext('Event triggered before repository is created')
205
226
206
227
207 class RepoCreateEvent(RepoEvent):
228 class RepoCreateEvent(RepoEvent):
@@ -211,6 +232,7 b' class RepoCreateEvent(RepoEvent):'
211 """
232 """
212 name = 'repo-create'
233 name = 'repo-create'
213 display_name = lazy_ugettext('repository created')
234 display_name = lazy_ugettext('repository created')
235 description = lazy_ugettext('Event triggered after repository was created')
214
236
215
237
216 class RepoPreDeleteEvent(RepoEvent):
238 class RepoPreDeleteEvent(RepoEvent):
@@ -220,6 +242,7 b' class RepoPreDeleteEvent(RepoEvent):'
220 """
242 """
221 name = 'repo-pre-delete'
243 name = 'repo-pre-delete'
222 display_name = lazy_ugettext('repository pre delete')
244 display_name = lazy_ugettext('repository pre delete')
245 description = lazy_ugettext('Event triggered before a repository is deleted')
223
246
224
247
225 class RepoDeleteEvent(RepoEvent):
248 class RepoDeleteEvent(RepoEvent):
@@ -229,6 +252,7 b' class RepoDeleteEvent(RepoEvent):'
229 """
252 """
230 name = 'repo-delete'
253 name = 'repo-delete'
231 display_name = lazy_ugettext('repository deleted')
254 display_name = lazy_ugettext('repository deleted')
255 description = lazy_ugettext('Event triggered after repository was deleted')
232
256
233
257
234 class RepoVCSEvent(RepoEvent):
258 class RepoVCSEvent(RepoEvent):
@@ -269,6 +293,7 b' class RepoPrePullEvent(RepoVCSEvent):'
269 """
293 """
270 name = 'repo-pre-pull'
294 name = 'repo-pre-pull'
271 display_name = lazy_ugettext('repository pre pull')
295 display_name = lazy_ugettext('repository pre pull')
296 description = lazy_ugettext('Event triggered before repository code is pulled')
272
297
273
298
274 class RepoPullEvent(RepoVCSEvent):
299 class RepoPullEvent(RepoVCSEvent):
@@ -278,6 +303,7 b' class RepoPullEvent(RepoVCSEvent):'
278 """
303 """
279 name = 'repo-pull'
304 name = 'repo-pull'
280 display_name = lazy_ugettext('repository pull')
305 display_name = lazy_ugettext('repository pull')
306 description = lazy_ugettext('Event triggered after repository code was pulled')
281
307
282
308
283 class RepoPrePushEvent(RepoVCSEvent):
309 class RepoPrePushEvent(RepoVCSEvent):
@@ -287,6 +313,8 b' class RepoPrePushEvent(RepoVCSEvent):'
287 """
313 """
288 name = 'repo-pre-push'
314 name = 'repo-pre-push'
289 display_name = lazy_ugettext('repository pre push')
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 class RepoPushEvent(RepoVCSEvent):
320 class RepoPushEvent(RepoVCSEvent):
@@ -298,6 +326,8 b' class RepoPushEvent(RepoVCSEvent):'
298 """
326 """
299 name = 'repo-push'
327 name = 'repo-push'
300 display_name = lazy_ugettext('repository push')
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 def __init__(self, repo_name, pushed_commit_ids, extras):
332 def __init__(self, repo_name, pushed_commit_ids, extras):
303 super(RepoPushEvent, self).__init__(repo_name, extras)
333 super(RepoPushEvent, self).__init__(repo_name, extras)
@@ -60,6 +60,7 b' class RepoGroupCreateEvent(RepoGroupEven'
60 """
60 """
61 name = 'repo-group-create'
61 name = 'repo-group-create'
62 display_name = lazy_ugettext('repository group created')
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 class RepoGroupDeleteEvent(RepoGroupEvent):
66 class RepoGroupDeleteEvent(RepoGroupEvent):
@@ -69,6 +70,7 b' class RepoGroupDeleteEvent(RepoGroupEven'
69 """
70 """
70 name = 'repo-group-delete'
71 name = 'repo-group-delete'
71 display_name = lazy_ugettext('repository group deleted')
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 class RepoGroupUpdateEvent(RepoGroupEvent):
76 class RepoGroupUpdateEvent(RepoGroupEvent):
@@ -78,3 +80,4 b' class RepoGroupUpdateEvent(RepoGroupEven'
78 """
80 """
79 name = 'repo-group-update'
81 name = 'repo-group-update'
80 display_name = lazy_ugettext('repository group update')
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 return colander.Schema()
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 class EEIntegration(IntegrationTypeBase):
142 class EEIntegration(IntegrationTypeBase):
130 description = 'Integration available in RhodeCode EE edition.'
143 description = 'Integration available in RhodeCode EE edition.'
@@ -139,31 +152,58 b' class EEIntegration(IntegrationTypeBase)'
139 # Helpers #
152 # Helpers #
140 # updating this required to update the `common_vars` as well.
153 # updating this required to update the `common_vars` as well.
141 WEBHOOK_URL_VARS = [
154 WEBHOOK_URL_VARS = [
155 # GENERAL
156 ('General', [
142 ('event_name', 'Unique name of the event type, e.g pullrequest-update'),
157 ('event_name', 'Unique name of the event type, e.g pullrequest-update'),
143 ('repo_name', 'Full name of the repository'),
158 ('repo_name', 'Full name of the repository'),
144 ('repo_type', 'VCS type of repository'),
159 ('repo_type', 'VCS type of repository'),
145 ('repo_id', 'Unique id of repository'),
160 ('repo_id', 'Unique id of repository'),
146 ('repo_url', 'Repository url'),
161 ('repo_url', 'Repository url'),
162 ]
163 ),
147 # extra repo fields
164 # extra repo fields
165 ('Repository', [
148 ('extra:<extra_key_name>', 'Extra repo variables, read from its settings.'),
166 ('extra:<extra_key_name>', 'Extra repo variables, read from its settings.'),
149
167 ]
168 ),
150 # special attrs below that we handle, using multi-call
169 # special attrs below that we handle, using multi-call
170 ('Commit push - Multicalls', [
151 ('branch', 'Name of each branch submitted, if any.'),
171 ('branch', 'Name of each branch submitted, if any.'),
152 ('branch_head', 'Head ID of pushed branch (full sha of last commit), if any.'),
172 ('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.'),
173 ('commit_id', 'ID (full sha) of each commit submitted, if any.'),
154
174 ]
175 ),
155 # pr events vars
176 # pr events vars
177 ('Pull request', [
156 ('pull_request_id', 'Unique ID of the pull request.'),
178 ('pull_request_id', 'Unique ID of the pull request.'),
157 ('pull_request_title', 'Title of the pull request.'),
179 ('pull_request_title', 'Title of the pull request.'),
158 ('pull_request_url', 'Pull request url.'),
180 ('pull_request_url', 'Pull request url.'),
159 ('pull_request_shadow_url', 'Pull request shadow repo clone url.'),
181 ('pull_request_shadow_url', 'Pull request shadow repo clone url.'),
160 ('pull_request_commits_uid', 'Calculated UID of all commits inside the PR. '
182 ('pull_request_commits_uid', 'Calculated UID of all commits inside the PR. '
161 'Changes after PR update'),
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 # user who triggers the call
200 # user who triggers the call
201 ('Caller', [
164 ('username', 'User who triggered the call.'),
202 ('username', 'User who triggered the call.'),
165 ('user_id', 'User id who triggered the call.'),
203 ('user_id', 'User id who triggered the call.'),
166 ]
204 ]
205 ),
206 ]
167
207
168 # common vars for url template used for CI plugins. Shared with webhook
208 # common vars for url template used for CI plugins. Shared with webhook
169 CI_URL_VARS = WEBHOOK_URL_VARS
209 CI_URL_VARS = WEBHOOK_URL_VARS
@@ -271,6 +311,26 b' class WebhookDataHandler(CommitParsingDa'
271
311
272 return url_calls
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 def repo_create_event_handler(self, event, data):
334 def repo_create_event_handler(self, event, data):
275 url = self.get_base_parsed_template(data)
335 url = self.get_base_parsed_template(data)
276 log.debug('register %s call(%s) to url %s', self.name, event, url)
336 log.debug('register %s call(%s) to url %s', self.name, event, url)
@@ -298,12 +358,13 b' class WebhookDataHandler(CommitParsingDa'
298 return self.repo_push_event_handler(event, data)
358 return self.repo_push_event_handler(event, data)
299 elif isinstance(event, events.RepoCreateEvent):
359 elif isinstance(event, events.RepoCreateEvent):
300 return self.repo_create_event_handler(event, data)
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 elif isinstance(event, events.PullRequestEvent):
363 elif isinstance(event, events.PullRequestEvent):
302 return self.pull_request_event_handler(event, data)
364 return self.pull_request_event_handler(event, data)
303 else:
365 else:
304 raise ValueError(
366 raise ValueError(
305 'event type `%s` not in supported list: %s' % (
367 'event type `{}` has no handler defined'.format(event.__class__))
306 event.__class__, events))
307
368
308
369
309 def get_auth(settings):
370 def get_auth(settings):
@@ -320,9 +381,13 b' def get_web_token(settings):'
320
381
321
382
322 def get_url_vars(url_vars):
383 def get_url_vars(url_vars):
323 return '\n'.join(
384 items = []
324 '{} - {}'.format('${' + key + '}', explanation)
385
325 for key, explanation in url_vars)
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 def render_with_traceback(template, *args, **kwargs):
393 def render_with_traceback(template, *args, **kwargs):
@@ -19,13 +19,14 b''
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 from __future__ import unicode_literals
21 from __future__ import unicode_literals
22 import deform
23 import logging
22 import logging
23
24 import colander
24 import colander
25
25 import deform.widget
26 from mako.template import Template
26 from mako.template import Template
27
27
28 from rhodecode import events
28 from rhodecode import events
29 from rhodecode.model.validation_schema.widgets import CheckboxChoiceWidgetDesc
29 from rhodecode.translation import _
30 from rhodecode.translation import _
30 from rhodecode.lib.celerylib import run_task
31 from rhodecode.lib.celerylib import run_task
31 from rhodecode.lib.celerylib import tasks
32 from rhodecode.lib.celerylib import tasks
@@ -174,6 +175,10 b' class EmailIntegrationType(IntegrationTy'
174 display_name = _('Email')
175 display_name = _('Email')
175 description = _('Send repo push summaries to a list of recipients via email')
176 description = _('Send repo push summaries to a list of recipients via email')
176
177
178 valid_events = [
179 events.RepoPushEvent
180 ]
181
177 @classmethod
182 @classmethod
178 def icon(cls):
183 def icon(cls):
179 return '''
184 return '''
@@ -240,19 +245,45 b' class EmailIntegrationType(IntegrationTy'
240
245
241 def settings_schema(self):
246 def settings_schema(self):
242 schema = EmailSettingsSchema()
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 return schema
258 return schema
244
259
245 def send_event(self, event):
260 def send_event(self, event):
246 data = event.as_dict()
261 log.debug('handling event %s with integration %s', event.name, self)
247 log.debug('got event: %r', event)
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 if isinstance(event, events.RepoPushEvent):
281 if isinstance(event, events.RepoPushEvent):
250 repo_push_handler(data, self.settings)
282 self.repo_push_handler(event, event_data)
251 else:
283 else:
252 log.debug('ignoring event: %r', event)
284 log.debug('ignoring event: %r', event)
253
285
254
286 def repo_push_handler(self, event, data):
255 def repo_push_handler(data, settings):
256 commit_num = len(data['push']['commits'])
287 commit_num = len(data['push']['commits'])
257 server_url = data['server_url']
288 server_url = data['server_url']
258
289
@@ -292,7 +323,8 b' def repo_push_handler(data, settings):'
292 subject=subject,
323 subject=subject,
293 instance_url=server_url)
324 instance_url=server_url)
294
325
295 for email_address in settings['recipients']:
326 recipients = self.integration_settings['recipients']
327 for email_address in recipients:
296 run_task(
328 run_task(
297 tasks.send_email, email_address, subject,
329 tasks.send_email, email_address, subject,
298 email_body_plaintext, email_body_html)
330 email_body_plaintext, email_body_html)
@@ -26,6 +26,7 b' import colander'
26 import textwrap
26 import textwrap
27 from mako.template import Template
27 from mako.template import Template
28 from rhodecode import events
28 from rhodecode import events
29 from rhodecode.model.validation_schema.widgets import CheckboxChoiceWidgetDesc
29 from rhodecode.translation import _
30 from rhodecode.translation import _
30 from rhodecode.lib import helpers as h
31 from rhodecode.lib import helpers as h
31 from rhodecode.lib.celerylib import run_task, async_task, RequestContextTask
32 from rhodecode.lib.celerylib import run_task, async_task, RequestContextTask
@@ -119,13 +120,10 b' class HipchatIntegrationType(Integration'
119
120
120 def send_event(self, event):
121 def send_event(self, event):
121 if event.__class__ not in self.valid_events:
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 return
124 return
124
125
125 allowed_events = self.settings['events']
126 if not self.event_enabled(event):
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)
129 return
127 return
130
128
131 data = event.as_dict()
129 data = event.as_dict()
@@ -133,8 +131,6 b' class HipchatIntegrationType(Integration'
133 text = '<b>%s<b> caused a <b>%s</b> event' % (
131 text = '<b>%s<b> caused a <b>%s</b> event' % (
134 data['actor']['username'], event.name)
132 data['actor']['username'], event.name)
135
133
136 log.debug('handling hipchat event for %s', event.name)
137
138 if isinstance(event, events.PullRequestCommentEvent):
134 if isinstance(event, events.PullRequestCommentEvent):
139 text = self.format_pull_request_comment_event(event, data)
135 text = self.format_pull_request_comment_event(event, data)
140 elif isinstance(event, events.PullRequestReviewEvent):
136 elif isinstance(event, events.PullRequestReviewEvent):
@@ -154,12 +150,12 b' class HipchatIntegrationType(Integration'
154 schema = HipchatSettingsSchema()
150 schema = HipchatSettingsSchema()
155 schema.add(colander.SchemaNode(
151 schema.add(colander.SchemaNode(
156 colander.Set(),
152 colander.Set(),
157 widget=deform.widget.CheckboxChoiceWidget(
153 widget=CheckboxChoiceWidgetDesc(
158 values=sorted(
154 values=sorted(
159 [(e.name, e.display_name) for e in self.valid_events]
155 [(e.name, e.display_name, e.description) for e in self.valid_events]
160 )
161 ),
156 ),
162 description="Events activated for this integration",
157 ),
158 description="List of events activated for this integration",
163 name='events'
159 name='events'
164 ))
160 ))
165
161
@@ -30,6 +30,7 b' import colander'
30 from mako.template import Template
30 from mako.template import Template
31
31
32 from rhodecode import events
32 from rhodecode import events
33 from rhodecode.model.validation_schema.widgets import CheckboxChoiceWidgetDesc
33 from rhodecode.translation import _
34 from rhodecode.translation import _
34 from rhodecode.lib import helpers as h
35 from rhodecode.lib import helpers as h
35 from rhodecode.lib.celerylib import run_task, async_task, RequestContextTask
36 from rhodecode.lib.celerylib import run_task, async_task, RequestContextTask
@@ -134,14 +135,13 b' class SlackIntegrationType(IntegrationTy'
134 ]
135 ]
135
136
136 def send_event(self, event):
137 def send_event(self, event):
138 log.debug('handling event %s with integration %s', event.name, self)
139
137 if event.__class__ not in self.valid_events:
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 return
142 return
140
143
141 allowed_events = self.settings['events']
144 if not self.event_enabled(event):
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)
145 return
145 return
146
146
147 data = event.as_dict()
147 data = event.as_dict()
@@ -154,8 +154,6 b' class SlackIntegrationType(IntegrationTy'
154 fields = None
154 fields = None
155 overrides = None
155 overrides = None
156
156
157 log.debug('handling slack event for %s', event.name)
158
159 if isinstance(event, events.PullRequestCommentEvent):
157 if isinstance(event, events.PullRequestCommentEvent):
160 (title, text, fields, overrides) \
158 (title, text, fields, overrides) \
161 = self.format_pull_request_comment_event(event, data)
159 = self.format_pull_request_comment_event(event, data)
@@ -176,12 +174,12 b' class SlackIntegrationType(IntegrationTy'
176 schema = SlackSettingsSchema()
174 schema = SlackSettingsSchema()
177 schema.add(colander.SchemaNode(
175 schema.add(colander.SchemaNode(
178 colander.Set(),
176 colander.Set(),
179 widget=deform.widget.CheckboxChoiceWidget(
177 widget=CheckboxChoiceWidgetDesc(
180 values=sorted(
178 values=sorted(
181 [(e.name, e.display_name) for e in self.valid_events]
179 [(e.name, e.display_name, e.description) for e in self.valid_events]
182 )
183 ),
180 ),
184 description="Events activated for this integration",
181 ),
182 description="List of events activated for this integration",
185 name='events'
183 name='events'
186 ))
184 ))
187
185
@@ -20,13 +20,14 b''
20
20
21 from __future__ import unicode_literals
21 from __future__ import unicode_literals
22
22
23 import deform
24 import deform.widget
23 import deform.widget
25 import logging
24 import logging
26 import colander
25 import colander
27
26
28 import rhodecode
27 import rhodecode
29 from rhodecode import events
28 from rhodecode import events
29 from rhodecode.lib.colander_utils import strip_whitespace
30 from rhodecode.model.validation_schema.widgets import CheckboxChoiceWidgetDesc
30 from rhodecode.translation import _
31 from rhodecode.translation import _
31 from rhodecode.integrations.types.base import (
32 from rhodecode.integrations.types.base import (
32 IntegrationTypeBase, get_auth, get_web_token, get_url_vars,
33 IntegrationTypeBase, get_auth, get_web_token, get_url_vars,
@@ -53,11 +54,12 b' class WebhookSettingsSchema(colander.Sch'
53 'objects in data in such cases.'),
54 'objects in data in such cases.'),
54 missing=colander.required,
55 missing=colander.required,
55 required=True,
56 required=True,
57 preparer=strip_whitespace,
56 validator=colander.url,
58 validator=colander.url,
57 widget=widgets.CodeMirrorWidget(
59 widget=widgets.CodeMirrorWidget(
58 help_block_collapsable_name='Show url variables',
60 help_block_collapsable_name='Show url variables',
59 help_block_collapsable=(
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 '?PR_ID=${{pull_request_id}}'
63 '?PR_ID=${{pull_request_id}}'
62 '\nFull list of vars:\n{}'.format(URL_VARS)),
64 '\nFull list of vars:\n{}'.format(URL_VARS)),
63 codemirror_mode='text',
65 codemirror_mode='text',
@@ -146,34 +148,31 b' class WebhookIntegrationType(Integration'
146 events.PullRequestCreateEvent,
148 events.PullRequestCreateEvent,
147 events.RepoPushEvent,
149 events.RepoPushEvent,
148 events.RepoCreateEvent,
150 events.RepoCreateEvent,
151 events.RepoCommitCommentEvent,
149 ]
152 ]
150
153
151 def settings_schema(self):
154 def settings_schema(self):
152 schema = WebhookSettingsSchema()
155 schema = WebhookSettingsSchema()
153 schema.add(colander.SchemaNode(
156 schema.add(colander.SchemaNode(
154 colander.Set(),
157 colander.Set(),
155 widget=deform.widget.CheckboxChoiceWidget(
158 widget=CheckboxChoiceWidgetDesc(
156 values=sorted(
159 values=sorted(
157 [(e.name, e.display_name) for e in self.valid_events]
160 [(e.name, e.display_name, e.description) for e in self.valid_events]
158 )
159 ),
161 ),
160 description="Events activated for this integration",
162 ),
163 description="List of events activated for this integration",
161 name='events'
164 name='events'
162 ))
165 ))
163 return schema
166 return schema
164
167
165 def send_event(self, event):
168 def send_event(self, event):
166 log.debug(
169 log.debug('handling event %s with integration %s', event.name, self)
167 'handling event %s with Webhook integration %s', event.name, self)
168
170
169 if event.__class__ not in self.valid_events:
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 return
173 return
172
174
173 allowed_events = self.settings['events']
175 if not self.event_enabled(event):
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)
177 return
176 return
178
177
179 data = event.as_dict()
178 data = event.as_dict()
@@ -20,13 +20,40 b''
20
20
21 import logging
21 import logging
22
22
23 import deform
24 import deform.widget
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 class CodeMirrorWidget(deform.widget.TextAreaWidget):
43 class CodeMirrorWidget(deform.widget.TextAreaWidget):
31 template = 'codemirror'
44 template = 'codemirror'
32 requirements = (('deform', None), ('codemirror', None))
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 </div>
22 </div>
23 </div>
23 </div>
24 ${field.end_sequence()}
24 ${field.end_sequence()}
25 </div> No newline at end of file
25 </div>
@@ -40,7 +40,7 b' from rhodecode.events import ('
40 PullRequestUpdateEvent,
40 PullRequestUpdateEvent,
41 PullRequestReviewEvent,
41 PullRequestReviewEvent,
42 PullRequestMergeEvent,
42 PullRequestMergeEvent,
43 PullRequestCloseEvent,
43 PullRequestCloseEvent
44 ])
44 ])
45 def test_pullrequest_events_serialized(EventClass, pr_util, config_stub):
45 def test_pullrequest_events_serialized(EventClass, pr_util, config_stub):
46 pr = pr_util.create_pull_request()
46 pr = pr_util.create_pull_request()
@@ -20,6 +20,7 b''
20
20
21 import pytest
21 import pytest
22
22
23 from rhodecode.lib.utils2 import StrictAttributeDict
23 from rhodecode.tests.events.conftest import EventCatcher
24 from rhodecode.tests.events.conftest import EventCatcher
24
25
25 from rhodecode.lib import hooks_base, utils2
26 from rhodecode.lib import hooks_base, utils2
@@ -28,7 +29,7 b' from rhodecode.events.repo import ('
28 RepoPrePullEvent, RepoPullEvent,
29 RepoPrePullEvent, RepoPullEvent,
29 RepoPrePushEvent, RepoPushEvent,
30 RepoPrePushEvent, RepoPushEvent,
30 RepoPreCreateEvent, RepoCreateEvent,
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 hooks_base.post_pull(scm_extras)
122 hooks_base.post_pull(scm_extras)
122 assert event_catcher.events_types == [RepoPullEvent]
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 count = 1
50 count = 1
51
51
52
52 def counter():
53 def counter():
53 global count
54 global count
54 val = count
55 val = count
@@ -52,8 +52,7 b' def test_webhook_parse_url_invalid_event'
52 handler(event, {})
52 handler(event, {})
53
53
54 err = str(err.value)
54 err = str(err.value)
55 assert err.startswith(
55 assert err == "event type `<class 'rhodecode.events.repo.RepoDeleteEvent'>` has no handler defined"
56 'event type `%s` not in supported list' % event.__class__)
57
56
58
57
59 @pytest.mark.parametrize('template,expected_urls', [
58 @pytest.mark.parametrize('template,expected_urls', [
General Comments 0
You need to be logged in to leave comments. Login now