##// END OF EJS Templates
integrations: webhook, allow to set a custom header. Fixes #5384
marcink -
r2086:d7985951 default
parent child Browse files
Show More
@@ -29,6 +29,7 b' import colander'
29 29 from celery.task import task
30 30 from requests.packages.urllib3.util.retry import Retry
31 31
32 import rhodecode
32 33 from rhodecode import events
33 34 from rhodecode.translation import _
34 35 from rhodecode.integrations.types.base import IntegrationTypeBase
@@ -61,9 +62,10 b" URL_VARS = ', '.join('${' + x + '}' for "
61 62
62 63
63 64 class WebhookHandler(object):
64 def __init__(self, template_url, secret_token):
65 def __init__(self, template_url, secret_token, headers):
65 66 self.template_url = template_url
66 67 self.secret_token = secret_token
68 self.headers = headers
67 69
68 70 def get_base_parsed_template(self, data):
69 71 """
@@ -117,18 +119,18 b' class WebhookHandler(object):'
117 119 # register per-commit call
118 120 log.debug(
119 121 'register webhook call(%s) to url %s', event, commit_url)
120 url_cals.append((commit_url, self.secret_token, data))
122 url_cals.append((commit_url, self.secret_token, self.headers, data))
121 123
122 124 else:
123 125 # register per-branch call
124 126 log.debug(
125 127 'register webhook call(%s) to url %s', event, branch_url)
126 url_cals.append((branch_url, self.secret_token, data))
128 url_cals.append((branch_url, self.secret_token, self.headers, data))
127 129
128 130 else:
129 131 log.debug(
130 132 'register webhook call(%s) to url %s', event, url)
131 url_cals.append((url, self.secret_token, data))
133 url_cals.append((url, self.secret_token, self.headers, data))
132 134
133 135 return url_cals
134 136
@@ -136,7 +138,7 b' class WebhookHandler(object):'
136 138 url = self.get_base_parsed_template(data)
137 139 log.debug(
138 140 'register webhook call(%s) to url %s', event, url)
139 return [(url, self.secret_token, data)]
141 return [(url, self.secret_token, self.headers, data)]
140 142
141 143 def pull_request_event_handler(self, event, data):
142 144 url = self.get_base_parsed_template(data)
@@ -145,7 +147,7 b' class WebhookHandler(object):'
145 147 url = string.Template(url).safe_substitute(
146 148 pull_request_id=data['pullrequest']['pull_request_id'],
147 149 pull_request_url=data['pullrequest']['url'])
148 return [(url, self.secret_token, data)]
150 return [(url, self.secret_token, self.headers, data)]
149 151
150 152 def __call__(self, event, data):
151 153 if isinstance(event, events.RepoPushEvent):
@@ -178,11 +180,32 b' class WebhookSettingsSchema(colander.Sch'
178 180 secret_token = colander.SchemaNode(
179 181 colander.String(),
180 182 title=_('Secret Token'),
181 description=_('String used to validate received payloads.'),
183 description=_('String used to validate received payloads. It will be '
184 'sent together with event data in JSON'),
182 185 default='',
183 186 missing='',
184 187 widget=deform.widget.TextInputWidget(
185 placeholder='secret_token'
188 placeholder='e.g. secret_token'
189 ),
190 )
191 custom_header_key = colander.SchemaNode(
192 colander.String(),
193 title=_('Custom Header Key'),
194 description=_('Custom Header name to be set when calling endpoint.'),
195 default='',
196 missing='',
197 widget=deform.widget.TextInputWidget(
198 placeholder='e.g.Authorization'
199 ),
200 )
201 custom_header_val = colander.SchemaNode(
202 colander.String(),
203 title=_('Custom Header Value'),
204 description=_('Custom Header value to be set when calling endpoint.'),
205 default='',
206 missing='',
207 widget=deform.widget.TextInputWidget(
208 placeholder='e.g. RcLogin auth=xxxx'
186 209 ),
187 210 )
188 211 method_type = colander.SchemaNode(
@@ -245,7 +268,15 b' class WebhookIntegrationType(Integration'
245 268 data = event.as_dict()
246 269 template_url = self.settings['url']
247 270
248 handler = WebhookHandler(template_url, self.settings['secret_token'])
271 headers = {}
272 head_key = self.settings['custom_header_key']
273 head_val = self.settings['custom_header_val']
274 if head_key and head_val:
275 headers = {head_key: head_val}
276
277 handler = WebhookHandler(
278 template_url, self.settings['secret_token'], headers)
279
249 280 url_calls = handler(event, data)
250 281 log.debug('webhook: calling following urls: %s',
251 282 [x[0] for x in url_calls])
@@ -259,7 +290,12 b' def post_to_webhook(url_calls, settings)'
259 290 total=max_retries,
260 291 backoff_factor=0.15,
261 292 status_forcelist=[500, 502, 503, 504])
262 for url, token, data in url_calls:
293 call_headers = {
294 'User-Agent': 'RhodeCode-webhook-caller/{}'.format(
295 rhodecode.__version__)
296 } # updated below with custom ones, allows override
297
298 for url, token, headers, data in url_calls:
263 299 req_session = requests.Session()
264 300 req_session.mount( # retry max N times
265 301 'http://', requests.adapters.HTTPAdapter(max_retries=retries))
@@ -267,10 +303,14 b' def post_to_webhook(url_calls, settings)'
267 303 method = settings.get('method_type') or 'post'
268 304 call_method = getattr(req_session, method)
269 305
306 headers = headers or {}
307 call_headers.update(headers)
308
270 309 log.debug('calling WEBHOOK with method: %s', call_method)
271 310 resp = call_method(url, json={
272 311 'token': token,
273 312 'event': data
274 })
313 }, headers=call_headers)
275 314 log.debug('Got WEBHOOK response: %s', resp)
315
276 316 resp.raise_for_status() # raise exception on a failed request
@@ -44,7 +44,8 b' def base_data():'
44 44
45 45 def test_webhook_parse_url_invalid_event():
46 46 template_url = 'http://server.com/${repo_name}/build'
47 handler = WebhookHandler(template_url, 'secret_token')
47 handler = WebhookHandler(
48 template_url, 'secret_token', {'exmaple-header':'header-values'})
48 49 with pytest.raises(ValueError) as err:
49 50 handler(events.RepoDeleteEvent(''), {})
50 51 assert str(err.value).startswith('event type not supported')
@@ -57,24 +58,32 b' def test_webhook_parse_url_invalid_event'
57 58 ('http://server.com/${branch}/build', ['http://server.com/${branch}/build']),
58 59 ])
59 60 def test_webook_parse_url_for_create_event(base_data, template, expected_urls):
60 handler = WebhookHandler(template, 'secret_token')
61 headers = {'exmaple-header': 'header-values'}
62 handler = WebhookHandler(
63 template, 'secret_token', headers)
61 64 urls = handler(events.RepoCreateEvent(''), base_data)
62 assert urls == [(url, 'secret_token', base_data) for url in expected_urls]
65 assert urls == [
66 (url, 'secret_token', headers, base_data) for url in expected_urls]
63 67
64 68
65 69 @pytest.mark.parametrize('template,expected_urls', [
66 70 ('http://server.com/${repo_name}/${pull_request_id}', ['http://server.com/foo/999']),
67 71 ('http://server.com/${repo_name}/${pull_request_url}', ['http://server.com/foo/http://pr-url.com']),
68 72 ])
69 def test_webook_parse_url_for_pull_request_event(base_data, template, expected_urls):
73 def test_webook_parse_url_for_pull_request_event(
74 base_data, template, expected_urls):
75
70 76 base_data['pullrequest'] = {
71 77 'pull_request_id': 999,
72 78 'url': 'http://pr-url.com',
73 79 }
74 handler = WebhookHandler(template, 'secret_token')
80 headers = {'exmaple-header': 'header-values'}
81 handler = WebhookHandler(
82 template, 'secret_token', headers)
75 83 urls = handler(events.PullRequestCreateEvent(
76 84 AttributeDict({'target_repo': 'foo'})), base_data)
77 assert urls == [(url, 'secret_token', base_data) for url in expected_urls]
85 assert urls == [
86 (url, 'secret_token', headers, base_data) for url in expected_urls]
78 87
79 88
80 89 @pytest.mark.parametrize('template,expected_urls', [
@@ -85,7 +94,8 b' def test_webook_parse_url_for_pull_reque'
85 94 'http://server.com/dev/dev-xxx',
86 95 'http://server.com/dev/dev-yyy']),
87 96 ])
88 def test_webook_parse_url_for_push_event(pylonsapp, repo_push_event, base_data, template, expected_urls):
97 def test_webook_parse_url_for_push_event(
98 pylonsapp, repo_push_event, base_data, template, expected_urls):
89 99 base_data['push'] = {
90 100 'branches': [{'name': 'stable'}, {'name': 'dev'}],
91 101 'commits': [{'branch': 'stable', 'raw_id': 'stable-xxx'},
@@ -93,6 +103,9 b' def test_webook_parse_url_for_push_event'
93 103 {'branch': 'dev', 'raw_id': 'dev-xxx'},
94 104 {'branch': 'dev', 'raw_id': 'dev-yyy'}]
95 105 }
96 handler = WebhookHandler(template, 'secret_token')
106 headers = {'exmaple-header': 'header-values'}
107 handler = WebhookHandler(
108 template, 'secret_token', headers)
97 109 urls = handler(repo_push_event, base_data)
98 assert urls == [(url, 'secret_token', base_data) for url in expected_urls]
110 assert urls == [
111 (url, 'secret_token', headers, base_data) for url in expected_urls]
General Comments 0
You need to be logged in to leave comments. Login now