##// END OF EJS Templates
webhook: quote URL variables to prevent url errors with special chars like # in pr title.
ergo -
r3477:976a0af2 default
parent child Browse files
Show More
@@ -23,17 +23,27 b' import string'
23 import collections
23 import collections
24 import logging
24 import logging
25 import requests
25 import requests
26 import urllib
26 from requests.adapters import HTTPAdapter
27 from requests.adapters import HTTPAdapter
27 from requests.packages.urllib3.util.retry import Retry
28 from requests.packages.urllib3.util.retry import Retry
28
29
29 from mako import exceptions
30 from mako import exceptions
30
31
32 from rhodecode.lib.utils2 import safe_str
31 from rhodecode.translation import _
33 from rhodecode.translation import _
32
34
33
35
34 log = logging.getLogger(__name__)
36 log = logging.getLogger(__name__)
35
37
36
38
39 class UrlTmpl(string.Template):
40
41 def safe_substitute(self, **kws):
42 # url encode the kw for usage in url
43 kws = {k: urllib.quote(safe_str(v)) for k, v in kws.items()}
44 return super(UrlTmpl, self).safe_substitute(**kws)
45
46
37 class IntegrationTypeBase(object):
47 class IntegrationTypeBase(object):
38 """ Base class for IntegrationType plugins """
48 """ Base class for IntegrationType plugins """
39 is_dummy = False
49 is_dummy = False
@@ -217,7 +227,9 b' class WebhookDataHandler(CommitParsingDa'
217 common_vars.update(extra_vars)
227 common_vars.update(extra_vars)
218
228
219 template_url = self.template_url.replace('${extra:', '${extra__')
229 template_url = self.template_url.replace('${extra:', '${extra__')
220 return string.Template(template_url).safe_substitute(**common_vars)
230 for k, v in common_vars.items():
231 template_url = UrlTmpl(template_url).safe_substitute(**{k: v})
232 return template_url
221
233
222 def repo_push_event_handler(self, event, data):
234 def repo_push_event_handler(self, event, data):
223 url = self.get_base_parsed_template(data)
235 url = self.get_base_parsed_template(data)
@@ -228,20 +240,18 b' class WebhookDataHandler(CommitParsingDa'
228 if '${branch}' in url or '${branch_head}' in url or '${commit_id}' in url:
240 if '${branch}' in url or '${branch_head}' in url or '${commit_id}' in url:
229 # call it multiple times, for each branch if used in variables
241 # call it multiple times, for each branch if used in variables
230 for branch, commit_ids in branches_commits.items():
242 for branch, commit_ids in branches_commits.items():
231 branch_url = string.Template(url).safe_substitute(branch=branch)
243 branch_url = UrlTmpl(url).safe_substitute(branch=branch)
232
244
233 if '${branch_head}' in branch_url:
245 if '${branch_head}' in branch_url:
234 # last commit in the aggregate is the head of the branch
246 # last commit in the aggregate is the head of the branch
235 branch_head = commit_ids['branch_head']
247 branch_head = commit_ids['branch_head']
236 branch_url = string.Template(branch_url).safe_substitute(
248 branch_url = UrlTmpl(branch_url).safe_substitute(branch_head=branch_head)
237 branch_head=branch_head)
238
249
239 # call further down for each commit if used
250 # call further down for each commit if used
240 if '${commit_id}' in branch_url:
251 if '${commit_id}' in branch_url:
241 for commit_data in commit_ids['commits']:
252 for commit_data in commit_ids['commits']:
242 commit_id = commit_data['raw_id']
253 commit_id = commit_data['raw_id']
243 commit_url = string.Template(branch_url).safe_substitute(
254 commit_url = UrlTmpl(branch_url).safe_substitute(commit_id=commit_id)
244 commit_id=commit_id)
245 # register per-commit call
255 # register per-commit call
246 log.debug(
256 log.debug(
247 'register %s call(%s) to url %s',
257 'register %s call(%s) to url %s',
@@ -251,36 +261,34 b' class WebhookDataHandler(CommitParsingDa'
251
261
252 else:
262 else:
253 # register per-branch call
263 # register per-branch call
254 log.debug(
264 log.debug('register %s call(%s) to url %s',
255 'register %s call(%s) to url %s',
256 self.name, event, branch_url)
265 self.name, event, branch_url)
257 url_calls.append(
266 url_calls.append((branch_url, self.headers, data))
258 (branch_url, self.headers, data))
259
267
260 else:
268 else:
261 log.debug(
269 log.debug('register %s call(%s) to url %s', self.name, event, url)
262 'register %s call(%s) to url %s', self.name, event, url)
263 url_calls.append((url, self.headers, data))
270 url_calls.append((url, self.headers, data))
264
271
265 return url_calls
272 return url_calls
266
273
267 def repo_create_event_handler(self, event, data):
274 def repo_create_event_handler(self, event, data):
268 url = self.get_base_parsed_template(data)
275 url = self.get_base_parsed_template(data)
269 log.debug(
276 log.debug('register %s call(%s) to url %s', self.name, event, url)
270 'register %s call(%s) to url %s', self.name, event, url)
271 return [(url, self.headers, data)]
277 return [(url, self.headers, data)]
272
278
273 def pull_request_event_handler(self, event, data):
279 def pull_request_event_handler(self, event, data):
274 url = self.get_base_parsed_template(data)
280 url = self.get_base_parsed_template(data)
275 log.debug(
281 log.debug('register %s call(%s) to url %s', self.name, event, url)
276 'register %s call(%s) to url %s', self.name, event, url)
282 pr_vars = [
277 url = string.Template(url).safe_substitute(
283 ('pull_request_id', data['pullrequest']['pull_request_id']),
278 pull_request_id=data['pullrequest']['pull_request_id'],
284 ('pull_request_title', data['pullrequest']['title']),
279 pull_request_title=data['pullrequest']['title'],
285 ('pull_request_url', data['pullrequest']['url']),
280 pull_request_url=data['pullrequest']['url'],
286 ('pull_request_shadow_url', data['pullrequest']['shadow_url']),
281 pull_request_shadow_url=data['pullrequest']['shadow_url'],
287 ('pull_request_commits_uid', data['pullrequest']['commits_uid']),
282 pull_request_commits_uid=data['pullrequest']['commits_uid'],
288 ]
283 )
289 for k, v in pr_vars:
290 url = UrlTmpl(url).safe_substitute(**{k: v})
291
284 return [(url, self.headers, data)]
292 return [(url, self.headers, data)]
285
293
286 def __call__(self, event, data):
294 def __call__(self, event, data):
@@ -188,7 +188,7 b' class WebhookIntegrationType(Integration'
188 handler = WebhookDataHandler(template_url, headers)
188 handler = WebhookDataHandler(template_url, headers)
189
189
190 url_calls = handler(event, data)
190 url_calls = handler(event, data)
191 log.debug('webhook: calling following urls: %s', [x[0] for x in url_calls])
191 log.debug('Webhook: calling following urls: %s', [x[0] for x in url_calls])
192
192
193 run_task(post_to_webhook, url_calls, self.settings)
193 run_task(post_to_webhook, url_calls, self.settings)
194
194
@@ -78,15 +78,18 b' def test_webook_parse_url_for_create_eve'
78 ('http://server.com/${repo_name}/${pull_request_id}',
78 ('http://server.com/${repo_name}/${pull_request_id}',
79 ['http://server.com/foo/999']),
79 ['http://server.com/foo/999']),
80 ('http://server.com/${repo_name}/${pull_request_url}',
80 ('http://server.com/${repo_name}/${pull_request_url}',
81 ['http://server.com/foo/http://pr-url.com']),
81 ['http://server.com/foo/http%3A//pr-url.com']),
82 ('http://server.com/${repo_name}/${pull_request_url}/?TITLE=${pull_request_title}',
83 ['http://server.com/foo/http%3A//pr-url.com/?TITLE=example-pr-title%20Ticket%20%23123']),
84 ('http://server.com/${repo_name}/?SHADOW_URL=${pull_request_shadow_url}',
85 ['http://server.com/foo/?SHADOW_URL=http%3A//pr-url.com/repository']),
82 ])
86 ])
83 def test_webook_parse_url_for_pull_request_event(
87 def test_webook_parse_url_for_pull_request_event(base_data, template, expected_urls):
84 base_data, template, expected_urls):
85
88
86 base_data['pullrequest'] = {
89 base_data['pullrequest'] = {
87 'pull_request_id': 999,
90 'pull_request_id': 999,
88 'url': 'http://pr-url.com',
91 'url': 'http://pr-url.com',
89 'title': 'example-pr-title',
92 'title': 'example-pr-title Ticket #123',
90 'commits_uid': 'abcdefg1234',
93 'commits_uid': 'abcdefg1234',
91 'shadow_url': 'http://pr-url.com/repository'
94 'shadow_url': 'http://pr-url.com/repository'
92 }
95 }
General Comments 0
You need to be logged in to leave comments. Login now