##// END OF EJS Templates
logging: Use __name__ when requesting a logger
Martin Bornhold -
r504:669c5d55 default
parent child Browse files
Show More
@@ -1,71 +1,71 b''
1 1 # Copyright (C) 2016-2016 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
5 5 # (only), as published by the Free Software Foundation.
6 6 #
7 7 # This program is distributed in the hope that it will be useful,
8 8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 10 # GNU General Public License for more details.
11 11 #
12 12 # You should have received a copy of the GNU Affero General Public License
13 13 # along with this program. If not, see <http://www.gnu.org/licenses/>.
14 14 #
15 15 # This program is dual-licensed. If you wish to learn more about the
16 16 # RhodeCode Enterprise Edition, including its added features, Support services,
17 17 # and proprietary license terms, please see https://rhodecode.com/licenses/
18 18
19 19 import logging
20 20 from pyramid.threadlocal import get_current_registry
21 21
22 log = logging.getLogger()
22 log = logging.getLogger(__name__)
23 23
24 24
25 25 def trigger(event, registry=None):
26 26 """
27 27 Helper method to send an event. This wraps the pyramid logic to send an
28 28 event.
29 29 """
30 30 # For the first step we are using pyramids thread locals here. If the
31 31 # event mechanism works out as a good solution we should think about
32 32 # passing the registry as an argument to get rid of it.
33 33 registry = registry or get_current_registry()
34 34 registry.notify(event)
35 35 log.debug('event %s triggered', event)
36 36
37 37 # Until we can work around the problem that VCS operations do not have a
38 38 # pyramid context to work with, we send the events to integrations directly
39 39
40 40 # Later it will be possible to use regular pyramid subscribers ie:
41 41 # config.add_subscriber(integrations_event_handler, RhodecodeEvent)
42 42 from rhodecode.integrations import integrations_event_handler
43 43 if isinstance(event, RhodecodeEvent):
44 44 integrations_event_handler(event)
45 45
46 46
47 47 from rhodecode.events.base import RhodecodeEvent
48 48
49 49 from rhodecode.events.user import (
50 50 UserPreCreate,
51 51 UserPreUpdate,
52 52 UserRegistered
53 53 )
54 54
55 55 from rhodecode.events.repo import (
56 56 RepoEvent,
57 57 RepoPreCreateEvent, RepoCreateEvent,
58 58 RepoPreDeleteEvent, RepoDeleteEvent,
59 59 RepoPrePushEvent, RepoPushEvent,
60 60 RepoPrePullEvent, RepoPullEvent,
61 61 )
62 62
63 63 from rhodecode.events.pullrequest import (
64 64 PullRequestEvent,
65 65 PullRequestCreateEvent,
66 66 PullRequestUpdateEvent,
67 67 PullRequestCommentEvent,
68 68 PullRequestReviewEvent,
69 69 PullRequestMergeEvent,
70 70 PullRequestCloseEvent,
71 71 )
@@ -1,220 +1,220 b''
1 1 # Copyright (C) 2016-2016 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
5 5 # (only), as published by the Free Software Foundation.
6 6 #
7 7 # This program is distributed in the hope that it will be useful,
8 8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 10 # GNU General Public License for more details.
11 11 #
12 12 # You should have received a copy of the GNU Affero General Public License
13 13 # along with this program. If not, see <http://www.gnu.org/licenses/>.
14 14 #
15 15 # This program is dual-licensed. If you wish to learn more about the
16 16 # RhodeCode Enterprise Edition, including its added features, Support services,
17 17 # and proprietary license terms, please see https://rhodecode.com/licenses/
18 18
19 19 import logging
20 20
21 21 from rhodecode.translation import lazy_ugettext
22 22 from rhodecode.model.db import User, Repository, Session
23 23 from rhodecode.events.base import RhodecodeEvent
24 24
25 log = logging.getLogger()
25 log = logging.getLogger(__name__)
26 26
27 27
28 28 class RepoEvent(RhodecodeEvent):
29 29 """
30 30 Base class for events acting on a repository.
31 31
32 32 :param repo: a :class:`Repository` instance
33 33 """
34 34
35 35 def __init__(self, repo):
36 36 super(RepoEvent, self).__init__()
37 37 self.repo = repo
38 38
39 39 def as_dict(self):
40 40 from rhodecode.model.repo import RepoModel
41 41 data = super(RepoEvent, self).as_dict()
42 42 data.update({
43 43 'repo': {
44 44 'repo_id': self.repo.repo_id,
45 45 'repo_name': self.repo.repo_name,
46 46 'repo_type': self.repo.repo_type,
47 47 'url': RepoModel().get_url(self.repo)
48 48 }
49 49 })
50 50 return data
51 51
52 52 def _commits_as_dict(self, commit_ids):
53 53 """ Helper function to serialize commit_ids """
54 54
55 55 from rhodecode.lib.utils2 import extract_mentioned_users
56 56 from rhodecode.model.db import Repository
57 57 from rhodecode.lib import helpers as h
58 58 from rhodecode.lib.helpers import process_patterns
59 59 from rhodecode.lib.helpers import urlify_commit_message
60 60 if not commit_ids:
61 61 return []
62 62 commits = []
63 63 reviewers = []
64 64 vcs_repo = self.repo.scm_instance(cache=False)
65 65 try:
66 66 for commit_id in commit_ids:
67 67 cs = vcs_repo.get_changeset(commit_id)
68 68 cs_data = cs.__json__()
69 69 cs_data['mentions'] = extract_mentioned_users(cs_data['message'])
70 70 cs_data['reviewers'] = reviewers
71 71 cs_data['url'] = h.url('changeset_home',
72 72 repo_name=self.repo.repo_name,
73 73 revision=cs_data['raw_id'],
74 74 qualified=True
75 75 )
76 76 urlified_message, issues_data = process_patterns(
77 77 cs_data['message'], self.repo.repo_name)
78 78 cs_data['issues'] = issues_data
79 79 cs_data['message_html'] = urlify_commit_message(cs_data['message'],
80 80 self.repo.repo_name)
81 81 commits.append(cs_data)
82 82 except Exception as e:
83 83 log.exception(e)
84 84 # we don't send any commits when crash happens, only full list matters
85 85 # we short circuit then.
86 86 return []
87 87 return commits
88 88
89 89 def _issues_as_dict(self, commits):
90 90 """ Helper function to serialize issues from commits """
91 91 issues = {}
92 92 for commit in commits:
93 93 for issue in commit['issues']:
94 94 issues[issue['id']] = issue
95 95 return issues
96 96
97 97
98 98 class RepoPreCreateEvent(RepoEvent):
99 99 """
100 100 An instance of this class is emitted as an :term:`event` before a repo is
101 101 created.
102 102 """
103 103 name = 'repo-pre-create'
104 104 display_name = lazy_ugettext('repository pre create')
105 105
106 106
107 107 class RepoCreateEvent(RepoEvent):
108 108 """
109 109 An instance of this class is emitted as an :term:`event` whenever a repo is
110 110 created.
111 111 """
112 112 name = 'repo-create'
113 113 display_name = lazy_ugettext('repository created')
114 114
115 115
116 116 class RepoPreDeleteEvent(RepoEvent):
117 117 """
118 118 An instance of this class is emitted as an :term:`event` whenever a repo is
119 119 created.
120 120 """
121 121 name = 'repo-pre-delete'
122 122 display_name = lazy_ugettext('repository pre delete')
123 123
124 124
125 125 class RepoDeleteEvent(RepoEvent):
126 126 """
127 127 An instance of this class is emitted as an :term:`event` whenever a repo is
128 128 created.
129 129 """
130 130 name = 'repo-delete'
131 131 display_name = lazy_ugettext('repository deleted')
132 132
133 133
134 134 class RepoVCSEvent(RepoEvent):
135 135 """
136 136 Base class for events triggered by the VCS
137 137 """
138 138 def __init__(self, repo_name, extras):
139 139 self.repo = Repository.get_by_repo_name(repo_name)
140 140 if not self.repo:
141 141 raise Exception('repo by this name %s does not exist' % repo_name)
142 142 self.extras = extras
143 143 super(RepoVCSEvent, self).__init__(self.repo)
144 144
145 145 @property
146 146 def actor(self):
147 147 if self.extras.get('username'):
148 148 return User.get_by_username(self.extras['username'])
149 149
150 150 @property
151 151 def actor_ip(self):
152 152 if self.extras.get('ip'):
153 153 return self.extras['ip']
154 154
155 155
156 156 class RepoPrePullEvent(RepoVCSEvent):
157 157 """
158 158 An instance of this class is emitted as an :term:`event` before commits
159 159 are pulled from a repo.
160 160 """
161 161 name = 'repo-pre-pull'
162 162 display_name = lazy_ugettext('repository pre pull')
163 163
164 164
165 165 class RepoPullEvent(RepoVCSEvent):
166 166 """
167 167 An instance of this class is emitted as an :term:`event` after commits
168 168 are pulled from a repo.
169 169 """
170 170 name = 'repo-pull'
171 171 display_name = lazy_ugettext('repository pull')
172 172
173 173
174 174 class RepoPrePushEvent(RepoVCSEvent):
175 175 """
176 176 An instance of this class is emitted as an :term:`event` before commits
177 177 are pushed to a repo.
178 178 """
179 179 name = 'repo-pre-push'
180 180 display_name = lazy_ugettext('repository pre push')
181 181
182 182
183 183 class RepoPushEvent(RepoVCSEvent):
184 184 """
185 185 An instance of this class is emitted as an :term:`event` after commits
186 186 are pushed to a repo.
187 187
188 188 :param extras: (optional) dict of data from proxied VCS actions
189 189 """
190 190 name = 'repo-push'
191 191 display_name = lazy_ugettext('repository push')
192 192
193 193 def __init__(self, repo_name, pushed_commit_ids, extras):
194 194 super(RepoPushEvent, self).__init__(repo_name, extras)
195 195 self.pushed_commit_ids = pushed_commit_ids
196 196
197 197 def as_dict(self):
198 198 data = super(RepoPushEvent, self).as_dict()
199 199 branch_url = repo_url = data['repo']['url']
200 200
201 201 commits = self._commits_as_dict(self.pushed_commit_ids)
202 202 issues = self._issues_as_dict(commits)
203 203
204 204 branches = set(
205 205 commit['branch'] for commit in commits if commit['branch'])
206 206 branches = [
207 207 {
208 208 'name': branch,
209 209 'url': '{}/changelog?branch={}'.format(
210 210 data['repo']['url'], branch)
211 211 }
212 212 for branch in branches
213 213 ]
214 214
215 215 data['push'] = {
216 216 'commits': commits,
217 217 'issues': issues,
218 218 'branches': branches,
219 219 }
220 return data No newline at end of file
220 return data
@@ -1,37 +1,37 b''
1 1 # -*- coding: utf-8 -*-
2 2 # Copyright (C) 2012-2016 RhodeCode GmbH
3 3 #
4 4 # This program is free software: you can redistribute it and/or modify
5 5 # it under the terms of the GNU Affero General Public License, version 3
6 6 # (only), as published by the Free Software Foundation.
7 7 #
8 8 # This program is distributed in the hope that it will be useful,
9 9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 11 # GNU General Public License for more details.
12 12 #
13 13 # You should have received a copy of the GNU Affero General Public License
14 14 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 15 #
16 16 # This program is dual-licensed. If you wish to learn more about the
17 17 # RhodeCode Enterprise Edition, including its added features, Support services,
18 18 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 19
20 20 import logging
21 21
22 log = logging.getLogger()
22 log = logging.getLogger(__name__)
23 23
24 24
25 25 class IntegrationTypeRegistry(dict):
26 26 """
27 27 Registry Class to hold IntegrationTypes
28 28 """
29 29 def register_integration_type(self, IntegrationType):
30 30 key = IntegrationType.key
31 31 if key in self:
32 32 log.warning(
33 33 'Overriding existing integration type %s (%s) with %s' % (
34 34 self[key], key, IntegrationType))
35 35
36 36 self[key] = IntegrationType
37 37
@@ -1,246 +1,246 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2012-2016 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 from __future__ import unicode_literals
22 22
23 23 import re
24 24 import logging
25 25 import requests
26 26 import colander
27 27 import textwrap
28 28 from celery.task import task
29 29 from mako.template import Template
30 30
31 31 from rhodecode import events
32 32 from rhodecode.translation import lazy_ugettext
33 33 from rhodecode.lib import helpers as h
34 34 from rhodecode.lib.celerylib import run_task
35 35 from rhodecode.lib.colander_utils import strip_whitespace
36 36 from rhodecode.integrations.types.base import IntegrationTypeBase
37 37 from rhodecode.integrations.schema import IntegrationSettingsSchemaBase
38 38
39 log = logging.getLogger()
39 log = logging.getLogger(__name__)
40 40
41 41
42 42 class SlackSettingsSchema(IntegrationSettingsSchemaBase):
43 43 service = colander.SchemaNode(
44 44 colander.String(),
45 45 title=lazy_ugettext('Slack service URL'),
46 46 description=h.literal(lazy_ugettext(
47 47 'This can be setup at the '
48 48 '<a href="https://my.slack.com/services/new/incoming-webhook/">'
49 49 'slack app manager</a>')),
50 50 default='',
51 51 placeholder='https://hooks.slack.com/services/...',
52 52 preparer=strip_whitespace,
53 53 validator=colander.url,
54 54 widget='string'
55 55 )
56 56 username = colander.SchemaNode(
57 57 colander.String(),
58 58 title=lazy_ugettext('Username'),
59 59 description=lazy_ugettext('Username to show notifications coming from.'),
60 60 missing='Rhodecode',
61 61 preparer=strip_whitespace,
62 62 widget='string',
63 63 placeholder='Rhodecode'
64 64 )
65 65 channel = colander.SchemaNode(
66 66 colander.String(),
67 67 title=lazy_ugettext('Channel'),
68 68 description=lazy_ugettext('Channel to send notifications to.'),
69 69 missing='',
70 70 preparer=strip_whitespace,
71 71 widget='string',
72 72 placeholder='#general'
73 73 )
74 74 icon_emoji = colander.SchemaNode(
75 75 colander.String(),
76 76 title=lazy_ugettext('Emoji'),
77 77 description=lazy_ugettext('Emoji to use eg. :studio_microphone:'),
78 78 missing='',
79 79 preparer=strip_whitespace,
80 80 widget='string',
81 81 placeholder=':studio_microphone:'
82 82 )
83 83
84 84
85 85 repo_push_template = Template(r'''
86 86 *${data['actor']['username']}* pushed to \
87 87 %if data['push']['branches']:
88 88 ${len(data['push']['branches']) > 1 and 'branches' or 'branch'} \
89 89 ${', '.join('<%s|%s>' % (branch['url'], branch['name']) for branch in data['push']['branches'])} \
90 90 %else:
91 91 unknown branch \
92 92 %endif
93 93 in <${data['repo']['url']}|${data['repo']['repo_name']}>
94 94 >>>
95 95 %for commit in data['push']['commits']:
96 96 <${commit['url']}|${commit['short_id']}> - ${commit['message_html']|html_to_slack_links}
97 97 %endfor
98 98 ''')
99 99
100 100
101 101 class SlackIntegrationType(IntegrationTypeBase):
102 102 key = 'slack'
103 103 display_name = lazy_ugettext('Slack')
104 104 SettingsSchema = SlackSettingsSchema
105 105 valid_events = [
106 106 events.PullRequestCloseEvent,
107 107 events.PullRequestMergeEvent,
108 108 events.PullRequestUpdateEvent,
109 109 events.PullRequestCommentEvent,
110 110 events.PullRequestReviewEvent,
111 111 events.PullRequestCreateEvent,
112 112 events.RepoPushEvent,
113 113 events.RepoCreateEvent,
114 114 ]
115 115
116 116 def send_event(self, event):
117 117 if event.__class__ not in self.valid_events:
118 118 log.debug('event not valid: %r' % event)
119 119 return
120 120
121 121 if event.name not in self.settings['events']:
122 122 log.debug('event ignored: %r' % event)
123 123 return
124 124
125 125 data = event.as_dict()
126 126
127 127 text = '*%s* caused a *%s* event' % (
128 128 data['actor']['username'], event.name)
129 129
130 130 log.debug('handling slack event for %s' % event.name)
131 131
132 132 if isinstance(event, events.PullRequestCommentEvent):
133 133 text = self.format_pull_request_comment_event(event, data)
134 134 elif isinstance(event, events.PullRequestReviewEvent):
135 135 text = self.format_pull_request_review_event(event, data)
136 136 elif isinstance(event, events.PullRequestEvent):
137 137 text = self.format_pull_request_event(event, data)
138 138 elif isinstance(event, events.RepoPushEvent):
139 139 text = self.format_repo_push_event(data)
140 140 elif isinstance(event, events.RepoCreateEvent):
141 141 text = self.format_repo_create_event(data)
142 142 else:
143 143 log.error('unhandled event type: %r' % event)
144 144
145 145 run_task(post_text_to_slack, self.settings, text)
146 146
147 147 @classmethod
148 148 def settings_schema(cls):
149 149 schema = SlackSettingsSchema()
150 150 schema.add(colander.SchemaNode(
151 151 colander.Set(),
152 152 widget='checkbox_list',
153 153 choices=sorted([e.name for e in cls.valid_events]),
154 154 description="Events activated for this integration",
155 155 name='events'
156 156 ))
157 157 return schema
158 158
159 159 def format_pull_request_comment_event(self, event, data):
160 160 comment_text = data['comment']['text']
161 161 if len(comment_text) > 200:
162 162 comment_text = '<{comment_url}|{comment_text}...>'.format(
163 163 comment_text=comment_text[:200],
164 164 comment_url=data['comment']['url'],
165 165 )
166 166
167 167 comment_status = ''
168 168 if data['comment']['status']:
169 169 comment_status = '[{}]: '.format(data['comment']['status'])
170 170
171 171 return (textwrap.dedent(
172 172 '''
173 173 {user} commented on pull request <{pr_url}|#{number}> - {pr_title}:
174 174 >>> {comment_status}{comment_text}
175 175 ''').format(
176 176 comment_status=comment_status,
177 177 user=data['actor']['username'],
178 178 number=data['pullrequest']['pull_request_id'],
179 179 pr_url=data['pullrequest']['url'],
180 180 pr_status=data['pullrequest']['status'],
181 181 pr_title=data['pullrequest']['title'],
182 182 comment_text=comment_text
183 183 )
184 184 )
185 185
186 186 def format_pull_request_review_event(self, event, data):
187 187 return (textwrap.dedent(
188 188 '''
189 189 Status changed to {pr_status} for pull request <{pr_url}|#{number}> - {pr_title}
190 190 ''').format(
191 191 user=data['actor']['username'],
192 192 number=data['pullrequest']['pull_request_id'],
193 193 pr_url=data['pullrequest']['url'],
194 194 pr_status=data['pullrequest']['status'],
195 195 pr_title=data['pullrequest']['title'],
196 196 )
197 197 )
198 198
199 199 def format_pull_request_event(self, event, data):
200 200 action = {
201 201 events.PullRequestCloseEvent: 'closed',
202 202 events.PullRequestMergeEvent: 'merged',
203 203 events.PullRequestUpdateEvent: 'updated',
204 204 events.PullRequestCreateEvent: 'created',
205 205 }.get(event.__class__, str(event.__class__))
206 206
207 207 return ('Pull request <{url}|#{number}> - {title} '
208 208 '{action} by {user}').format(
209 209 user=data['actor']['username'],
210 210 number=data['pullrequest']['pull_request_id'],
211 211 url=data['pullrequest']['url'],
212 212 title=data['pullrequest']['title'],
213 213 action=action
214 214 )
215 215
216 216 def format_repo_push_event(self, data):
217 217 result = repo_push_template.render(
218 218 data=data,
219 219 html_to_slack_links=html_to_slack_links,
220 220 )
221 221 return result
222 222
223 223 def format_repo_create_event(self, data):
224 224 return '<{}|{}> ({}) repository created by *{}*'.format(
225 225 data['repo']['url'],
226 226 data['repo']['repo_name'],
227 227 data['repo']['repo_type'],
228 228 data['actor']['username'],
229 229 )
230 230
231 231
232 232 def html_to_slack_links(message):
233 233 return re.compile(r'<a .*?href=["\'](.+?)".*?>(.+?)</a>').sub(
234 234 r'<\1|\2>', message)
235 235
236 236
237 237 @task(ignore_result=True)
238 238 def post_text_to_slack(settings, text):
239 239 log.debug('sending %s to slack %s' % (text, settings['service']))
240 240 resp = requests.post(settings['service'], json={
241 241 "channel": settings.get('channel', ''),
242 242 "username": settings.get('username', 'Rhodecode'),
243 243 "text": text,
244 244 "icon_emoji": settings.get('icon_emoji', ':studio_microphone:')
245 245 })
246 246 resp.raise_for_status() # raise exception on a failed request
@@ -1,106 +1,106 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2012-2016 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 from __future__ import unicode_literals
22 22
23 23 import logging
24 24 import requests
25 25 import colander
26 26 from celery.task import task
27 27 from mako.template import Template
28 28
29 29 from rhodecode import events
30 30 from rhodecode.translation import lazy_ugettext
31 31 from rhodecode.integrations.types.base import IntegrationTypeBase
32 32 from rhodecode.integrations.schema import IntegrationSettingsSchemaBase
33 33
34 log = logging.getLogger()
34 log = logging.getLogger(__name__)
35 35
36 36
37 37 class WebhookSettingsSchema(IntegrationSettingsSchemaBase):
38 38 url = colander.SchemaNode(
39 39 colander.String(),
40 40 title=lazy_ugettext('Webhook URL'),
41 41 description=lazy_ugettext('URL of the webhook to receive POST event.'),
42 42 default='',
43 43 validator=colander.url,
44 44 placeholder='https://www.example.com/webhook',
45 45 widget='string'
46 46 )
47 47 secret_token = colander.SchemaNode(
48 48 colander.String(),
49 49 title=lazy_ugettext('Secret Token'),
50 50 description=lazy_ugettext('String used to validate received payloads.'),
51 51 default='',
52 52 placeholder='secret_token',
53 53 widget='string'
54 54 )
55 55
56 56
57 57 class WebhookIntegrationType(IntegrationTypeBase):
58 58 key = 'webhook'
59 59 display_name = lazy_ugettext('Webhook')
60 60 valid_events = [
61 61 events.PullRequestCloseEvent,
62 62 events.PullRequestMergeEvent,
63 63 events.PullRequestUpdateEvent,
64 64 events.PullRequestCommentEvent,
65 65 events.PullRequestReviewEvent,
66 66 events.PullRequestCreateEvent,
67 67 events.RepoPushEvent,
68 68 events.RepoCreateEvent,
69 69 ]
70 70
71 71 @classmethod
72 72 def settings_schema(cls):
73 73 schema = WebhookSettingsSchema()
74 74 schema.add(colander.SchemaNode(
75 75 colander.Set(),
76 76 widget='checkbox_list',
77 77 choices=sorted([e.name for e in cls.valid_events]),
78 78 description="Events activated for this integration",
79 79 name='events'
80 80 ))
81 81 return schema
82 82
83 83 def send_event(self, event):
84 84 log.debug('handling event %s with webhook integration %s',
85 85 event.name, self)
86 86
87 87 if event.__class__ not in self.valid_events:
88 88 log.debug('event not valid: %r' % event)
89 89 return
90 90
91 91 if event.name not in self.settings['events']:
92 92 log.debug('event ignored: %r' % event)
93 93 return
94 94
95 95 data = event.as_dict()
96 96 post_to_webhook(data, self.settings)
97 97
98 98
99 99 @task(ignore_result=True)
100 100 def post_to_webhook(data, settings):
101 101 log.debug('sending event:%s to webhook %s', data['name'], settings['url'])
102 102 resp = requests.post(settings['url'], json={
103 103 'token': settings['secret_token'],
104 104 'event': data
105 105 })
106 106 resp.raise_for_status() # raise exception on a failed request
General Comments 0
You need to be logged in to leave comments. Login now