##// END OF EJS Templates
webhook: handle ${commit_id} variable independent from ${branch}....
marcink -
r3041:d813300a stable
parent child Browse files
Show More
@@ -1,269 +1,270 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2018 RhodeCode GmbH
3 # Copyright (C) 2010-2018 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
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 import pytest
21 import pytest
22
22
23 from rhodecode.apps._base import ADMIN_PREFIX
23 from rhodecode.apps._base import ADMIN_PREFIX
24 from rhodecode.model.db import Integration
24 from rhodecode.model.db import Integration
25 from rhodecode.model.meta import Session
25 from rhodecode.model.meta import Session
26 from rhodecode.integrations import integration_type_registry
26 from rhodecode.integrations import integration_type_registry
27
27
28
28
29 def route_path(name, **kwargs):
29 def route_path(name, **kwargs):
30 return {
30 return {
31 'home': '/',
31 'home': '/',
32 }[name].format(**kwargs)
32 }[name].format(**kwargs)
33
33
34
34
35 def _post_integration_test_helper(app, url, csrf_token, repo, repo_group,
36 admin_view):
37 """
38 Posts form data to create integration at the url given then deletes it and
39 checks if the redirect url is correct.
40 """
41 repo_name = repo.repo_name
42 repo_group_name = repo_group.group_name
43 app.post(url, params={}, status=403) # missing csrf check
44 response = app.post(url, params={'csrf_token': csrf_token})
45 assert response.status_code == 200
46 response.mustcontain('Errors exist')
47
48 scopes_destinations = [
49 ('global',
50 ADMIN_PREFIX + '/integrations'),
51 ('root-repos',
52 ADMIN_PREFIX + '/integrations'),
53 ('repo:%s' % repo_name,
54 '/%s/settings/integrations' % repo_name),
55 ('repogroup:%s' % repo_group_name,
56 '/%s/_settings/integrations' % repo_group_name),
57 ('repogroup-recursive:%s' % repo_group_name,
58 '/%s/_settings/integrations' % repo_group_name),
59 ]
60
61 for scope, destination in scopes_destinations:
62 if admin_view:
63 destination = ADMIN_PREFIX + '/integrations'
64
65 form_data = [
66 ('csrf_token', csrf_token),
67 ('__start__', 'options:mapping'),
68 ('name', 'test integration'),
69 ('scope', scope),
70 ('enabled', 'true'),
71 ('__end__', 'options:mapping'),
72 ('__start__', 'settings:mapping'),
73 ('test_int_field', '34'),
74 ('test_string_field', ''), # empty value on purpose as it's required
75 ('__end__', 'settings:mapping'),
76 ]
77 errors_response = app.post(url, form_data)
78 assert 'Errors exist' in errors_response.body
79
80 form_data[-2] = ('test_string_field', 'data!')
81 assert Session().query(Integration).count() == 0
82 created_response = app.post(url, form_data)
83 assert Session().query(Integration).count() == 1
84
85 delete_response = app.post(
86 created_response.location,
87 params={'csrf_token': csrf_token, 'delete': 'delete'})
88
89 assert Session().query(Integration).count() == 0
90 assert delete_response.location.endswith(destination)
91
92
93
35 @pytest.mark.usefixtures('app', 'autologin_user')
94 @pytest.mark.usefixtures('app', 'autologin_user')
36 class TestIntegrationsView(object):
95 class TestIntegrationsView(object):
37 pass
96 pass
38
97
39
98
40 class TestGlobalIntegrationsView(TestIntegrationsView):
99 class TestGlobalIntegrationsView(TestIntegrationsView):
41 def test_index_no_integrations(self):
100 def test_index_no_integrations(self):
42 url = ADMIN_PREFIX + '/integrations'
101 url = ADMIN_PREFIX + '/integrations'
43 response = self.app.get(url)
102 response = self.app.get(url)
44
103
45 assert response.status_code == 200
104 assert response.status_code == 200
46 response.mustcontain('exist yet')
105 response.mustcontain('exist yet')
47
106
48 def test_index_with_integrations(self, global_integration_stub):
107 def test_index_with_integrations(self, global_integration_stub):
49 url = ADMIN_PREFIX + '/integrations'
108 url = ADMIN_PREFIX + '/integrations'
50 response = self.app.get(url)
109 response = self.app.get(url)
51
110
52 assert response.status_code == 200
111 assert response.status_code == 200
53 response.mustcontain(no=['exist yet'])
112 response.mustcontain(no=['exist yet'])
54 response.mustcontain(global_integration_stub.name)
113 response.mustcontain(global_integration_stub.name)
55
114
56 @pytest.mark.parametrize(
115 @pytest.mark.parametrize(
57 'IntegrationType', integration_type_registry.values())
116 'IntegrationType', integration_type_registry.values())
58 def test_new_integration_page(self, IntegrationType):
117 def test_new_integration_page(self, IntegrationType):
59 url = ADMIN_PREFIX + '/integrations/new'
118 url = ADMIN_PREFIX + '/integrations/new'
60
119
61 response = self.app.get(url, status=200)
120 response = self.app.get(url, status=200)
62 if not IntegrationType.is_dummy:
121 if not IntegrationType.is_dummy:
63 url = (ADMIN_PREFIX + '/integrations/{integration}/new').format(
122 url = (ADMIN_PREFIX + '/integrations/{integration}/new').format(
64 integration=IntegrationType.key)
123 integration=IntegrationType.key)
65 response.mustcontain(url)
124 response.mustcontain(url)
66
125
67 @pytest.mark.parametrize(
126 @pytest.mark.parametrize(
68 'IntegrationType', integration_type_registry.values())
127 'IntegrationType', integration_type_registry.values())
69 def test_get_create_integration_page(self, IntegrationType):
128 def test_get_create_integration_page(self, IntegrationType):
70 url = ADMIN_PREFIX + '/integrations/{integration_key}/new'.format(
129 url = ADMIN_PREFIX + '/integrations/{integration_key}/new'.format(
71 integration_key=IntegrationType.key)
130 integration_key=IntegrationType.key)
72 if IntegrationType.is_dummy:
131 if IntegrationType.is_dummy:
73 self.app.get(url, status=404)
132 self.app.get(url, status=404)
74 else:
133 else:
75 response = self.app.get(url, status=200)
134 response = self.app.get(url, status=200)
76 response.mustcontain(IntegrationType.display_name)
135 response.mustcontain(IntegrationType.display_name)
77
136
78 def test_post_integration_page(self, StubIntegrationType, csrf_token,
137 def test_post_integration_page(self, StubIntegrationType, csrf_token,
79 test_repo_group, backend_random):
138 test_repo_group, backend_random):
80 url = ADMIN_PREFIX + '/integrations/{integration_key}/new'.format(
139 url = ADMIN_PREFIX + '/integrations/{integration_key}/new'.format(
81 integration_key=StubIntegrationType.key)
140 integration_key=StubIntegrationType.key)
82
141
83 _post_integration_test_helper(
142 _post_integration_test_helper(
84 self.app, url, csrf_token, admin_view=True,
143 self.app, url, csrf_token, admin_view=True,
85 repo=backend_random.repo, repo_group=test_repo_group)
144 repo=backend_random.repo, repo_group=test_repo_group)
86
145
87
146
88 class TestRepoIntegrationsView(TestIntegrationsView):
147 class TestRepoIntegrationsView(TestIntegrationsView):
89 def test_index_no_integrations(self, backend_random):
148 def test_index_no_integrations(self, backend_random):
90 url = '/{repo_name}/settings/integrations'.format(
149 url = '/{repo_name}/settings/integrations'.format(
91 repo_name=backend_random.repo.repo_name)
150 repo_name=backend_random.repo.repo_name)
92 response = self.app.get(url)
151 response = self.app.get(url)
93
152
94 assert response.status_code == 200
153 assert response.status_code == 200
95 response.mustcontain('exist yet')
154 response.mustcontain('exist yet')
96
155
97 def test_index_with_integrations(self, repo_integration_stub):
156 def test_index_with_integrations(self, repo_integration_stub):
98 url = '/{repo_name}/settings/integrations'.format(
157 url = '/{repo_name}/settings/integrations'.format(
99 repo_name=repo_integration_stub.repo.repo_name)
158 repo_name=repo_integration_stub.repo.repo_name)
100 stub_name = repo_integration_stub.name
159 stub_name = repo_integration_stub.name
101
160
102 response = self.app.get(url)
161 response = self.app.get(url)
103
162
104 assert response.status_code == 200
163 assert response.status_code == 200
105 response.mustcontain(stub_name)
164 response.mustcontain(stub_name)
106 response.mustcontain(no=['exist yet'])
165 response.mustcontain(no=['exist yet'])
107
166
108 @pytest.mark.parametrize(
167 @pytest.mark.parametrize(
109 'IntegrationType', integration_type_registry.values())
168 'IntegrationType', integration_type_registry.values())
110 def test_new_integration_page(self, backend_random, IntegrationType):
169 def test_new_integration_page(self, backend_random, IntegrationType):
111 repo_name = backend_random.repo.repo_name
170 repo_name = backend_random.repo.repo_name
112 url = '/{repo_name}/settings/integrations/new'.format(
171 url = '/{repo_name}/settings/integrations/new'.format(
113 repo_name=repo_name)
172 repo_name=repo_name)
114
173
115 response = self.app.get(url, status=200)
174 response = self.app.get(url, status=200)
116
175
117 url = '/{repo_name}/settings/integrations/{integration}/new'.format(
176 url = '/{repo_name}/settings/integrations/{integration}/new'.format(
118 repo_name=repo_name,
177 repo_name=repo_name,
119 integration=IntegrationType.key)
178 integration=IntegrationType.key)
120 if not IntegrationType.is_dummy:
179 if not IntegrationType.is_dummy:
121 response.mustcontain(url)
180 response.mustcontain(url)
122
181
123 @pytest.mark.parametrize(
182 @pytest.mark.parametrize(
124 'IntegrationType', integration_type_registry.values())
183 'IntegrationType', integration_type_registry.values())
125 def test_get_create_integration_page(self, backend_random, IntegrationType):
184 def test_get_create_integration_page(self, backend_random, IntegrationType):
126 repo_name = backend_random.repo.repo_name
185 repo_name = backend_random.repo.repo_name
127 url = '/{repo_name}/settings/integrations/{integration_key}/new'.format(
186 url = '/{repo_name}/settings/integrations/{integration_key}/new'.format(
128 repo_name=repo_name, integration_key=IntegrationType.key)
187 repo_name=repo_name, integration_key=IntegrationType.key)
129 if IntegrationType.is_dummy:
188 if IntegrationType.is_dummy:
130 self.app.get(url, status=404)
189 self.app.get(url, status=404)
131 else:
190 else:
132 response = self.app.get(url, status=200)
191 response = self.app.get(url, status=200)
133 response.mustcontain(IntegrationType.display_name)
192 response.mustcontain(IntegrationType.display_name)
134
193
135 def test_post_integration_page(self, backend_random, test_repo_group,
194 def test_post_integration_page(self, backend_random, test_repo_group,
136 StubIntegrationType, csrf_token):
195 StubIntegrationType, csrf_token):
137 repo_name = backend_random.repo.repo_name
196 repo_name = backend_random.repo.repo_name
138 url = '/{repo_name}/settings/integrations/{integration_key}/new'.format(
197 url = '/{repo_name}/settings/integrations/{integration_key}/new'.format(
139 repo_name=repo_name, integration_key=StubIntegrationType.key)
198 repo_name=repo_name, integration_key=StubIntegrationType.key)
140
199
141 _post_integration_test_helper(
200 _post_integration_test_helper(
142 self.app, url, csrf_token, admin_view=False,
201 self.app, url, csrf_token, admin_view=False,
143 repo=backend_random.repo, repo_group=test_repo_group)
202 repo=backend_random.repo, repo_group=test_repo_group)
144
203
145
204
146 class TestRepoGroupIntegrationsView(TestIntegrationsView):
205 class TestRepoGroupIntegrationsView(TestIntegrationsView):
147 def test_index_no_integrations(self, test_repo_group):
206 def test_index_no_integrations(self, test_repo_group):
148 url = '/{repo_group_name}/_settings/integrations'.format(
207 url = '/{repo_group_name}/_settings/integrations'.format(
149 repo_group_name=test_repo_group.group_name)
208 repo_group_name=test_repo_group.group_name)
150 response = self.app.get(url)
209 response = self.app.get(url)
151
210
152 assert response.status_code == 200
211 assert response.status_code == 200
153 response.mustcontain('exist yet')
212 response.mustcontain('exist yet')
154
213
155 def test_index_with_integrations(
214 def test_index_with_integrations(
156 self, test_repo_group, repogroup_integration_stub):
215 self, test_repo_group, repogroup_integration_stub):
157
216
158 url = '/{repo_group_name}/_settings/integrations'.format(
217 url = '/{repo_group_name}/_settings/integrations'.format(
159 repo_group_name=test_repo_group.group_name)
218 repo_group_name=test_repo_group.group_name)
160
219
161 stub_name = repogroup_integration_stub.name
220 stub_name = repogroup_integration_stub.name
162 response = self.app.get(url)
221 response = self.app.get(url)
163
222
164 assert response.status_code == 200
223 assert response.status_code == 200
165 response.mustcontain(no=['exist yet'])
224 response.mustcontain(no=['exist yet'])
166 response.mustcontain(stub_name)
225 response.mustcontain(stub_name)
167
226
168 def test_new_integration_page(self, test_repo_group):
227 def test_new_integration_page(self, test_repo_group):
169 repo_group_name = test_repo_group.group_name
228 repo_group_name = test_repo_group.group_name
170 url = '/{repo_group_name}/_settings/integrations/new'.format(
229 url = '/{repo_group_name}/_settings/integrations/new'.format(
171 repo_group_name=test_repo_group.group_name)
230 repo_group_name=test_repo_group.group_name)
172
231
173 response = self.app.get(url)
232 response = self.app.get(url)
174
233
175 assert response.status_code == 200
234 assert response.status_code == 200
176
235
177 for integration_key, integration_obj in integration_type_registry.items():
236 for integration_key, integration_obj in integration_type_registry.items():
178 if not integration_obj.is_dummy:
237 if not integration_obj.is_dummy:
179 nurl = (
238 nurl = (
180 '/{repo_group_name}/_settings/integrations/{integration}/new').format(
239 '/{repo_group_name}/_settings/integrations/{integration}/new').format(
181 repo_group_name=repo_group_name,
240 repo_group_name=repo_group_name,
182 integration=integration_key)
241 integration=integration_key)
183 response.mustcontain(nurl)
242 response.mustcontain(nurl)
184
243
185 @pytest.mark.parametrize(
244 @pytest.mark.parametrize(
186 'IntegrationType', integration_type_registry.values())
245 'IntegrationType', integration_type_registry.values())
187 def test_get_create_integration_page(
246 def test_get_create_integration_page(
188 self, test_repo_group, IntegrationType):
247 self, test_repo_group, IntegrationType):
189
248
190 repo_group_name = test_repo_group.group_name
249 repo_group_name = test_repo_group.group_name
191 url = ('/{repo_group_name}/_settings/integrations/{integration_key}/new'
250 url = ('/{repo_group_name}/_settings/integrations/{integration_key}/new'
192 ).format(repo_group_name=repo_group_name,
251 ).format(repo_group_name=repo_group_name,
193 integration_key=IntegrationType.key)
252 integration_key=IntegrationType.key)
194
253
195 if not IntegrationType.is_dummy:
254 if not IntegrationType.is_dummy:
196 response = self.app.get(url, status=200)
255 response = self.app.get(url, status=200)
197 response.mustcontain(IntegrationType.display_name)
256 response.mustcontain(IntegrationType.display_name)
198 else:
257 else:
199 self.app.get(url, status=404)
258 self.app.get(url, status=404)
200
259
201 def test_post_integration_page(self, test_repo_group, backend_random,
260 def test_post_integration_page(self, test_repo_group, backend_random,
202 StubIntegrationType, csrf_token):
261 StubIntegrationType, csrf_token):
203
262
204 repo_group_name = test_repo_group.group_name
263 repo_group_name = test_repo_group.group_name
205 url = ('/{repo_group_name}/_settings/integrations/{integration_key}/new'
264 url = ('/{repo_group_name}/_settings/integrations/{integration_key}/new'
206 ).format(repo_group_name=repo_group_name,
265 ).format(repo_group_name=repo_group_name,
207 integration_key=StubIntegrationType.key)
266 integration_key=StubIntegrationType.key)
208
267
209 _post_integration_test_helper(
268 _post_integration_test_helper(
210 self.app, url, csrf_token, admin_view=False,
269 self.app, url, csrf_token, admin_view=False,
211 repo=backend_random.repo, repo_group=test_repo_group)
270 repo=backend_random.repo, repo_group=test_repo_group)
212
213
214 def _post_integration_test_helper(app, url, csrf_token, repo, repo_group,
215 admin_view):
216 """
217 Posts form data to create integration at the url given then deletes it and
218 checks if the redirect url is correct.
219 """
220 repo_name = repo.repo_name
221 repo_group_name = repo_group.group_name
222 app.post(url, params={}, status=403) # missing csrf check
223 response = app.post(url, params={'csrf_token': csrf_token})
224 assert response.status_code == 200
225 response.mustcontain('Errors exist')
226
227 scopes_destinations = [
228 ('global',
229 ADMIN_PREFIX + '/integrations'),
230 ('root-repos',
231 ADMIN_PREFIX + '/integrations'),
232 ('repo:%s' % repo_name,
233 '/%s/settings/integrations' % repo_name),
234 ('repogroup:%s' % repo_group_name,
235 '/%s/_settings/integrations' % repo_group_name),
236 ('repogroup-recursive:%s' % repo_group_name,
237 '/%s/_settings/integrations' % repo_group_name),
238 ]
239
240 for scope, destination in scopes_destinations:
241 if admin_view:
242 destination = ADMIN_PREFIX + '/integrations'
243
244 form_data = [
245 ('csrf_token', csrf_token),
246 ('__start__', 'options:mapping'),
247 ('name', 'test integration'),
248 ('scope', scope),
249 ('enabled', 'true'),
250 ('__end__', 'options:mapping'),
251 ('__start__', 'settings:mapping'),
252 ('test_int_field', '34'),
253 ('test_string_field', ''), # empty value on purpose as it's required
254 ('__end__', 'settings:mapping'),
255 ]
256 errors_response = app.post(url, form_data)
257 assert 'Errors exist' in errors_response.body
258
259 form_data[-2] = ('test_string_field', 'data!')
260 assert Session().query(Integration).count() == 0
261 created_response = app.post(url, form_data)
262 assert Session().query(Integration).count() == 1
263
264 delete_response = app.post(
265 created_response.location,
266 params={'csrf_token': csrf_token, 'delete': 'delete'})
267
268 assert Session().query(Integration).count() == 0
269 assert delete_response.location.endswith(destination)
@@ -1,322 +1,322 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2012-2018 RhodeCode GmbH
3 # Copyright (C) 2012-2018 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
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 import colander
21 import colander
22 import string
22 import string
23 import collections
23 import collections
24 import logging
24 import logging
25
25
26 from mako import exceptions
26 from mako import exceptions
27
27
28 from rhodecode.translation import _
28 from rhodecode.translation import _
29
29
30
30
31 log = logging.getLogger(__name__)
31 log = logging.getLogger(__name__)
32
32
33
33
34 class IntegrationTypeBase(object):
34 class IntegrationTypeBase(object):
35 """ Base class for IntegrationType plugins """
35 """ Base class for IntegrationType plugins """
36 is_dummy = False
36 is_dummy = False
37 description = ''
37 description = ''
38
38
39 @classmethod
39 @classmethod
40 def icon(cls):
40 def icon(cls):
41 return '''
41 return '''
42 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
42 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
43 <svg
43 <svg
44 xmlns:dc="http://purl.org/dc/elements/1.1/"
44 xmlns:dc="http://purl.org/dc/elements/1.1/"
45 xmlns:cc="http://creativecommons.org/ns#"
45 xmlns:cc="http://creativecommons.org/ns#"
46 xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
46 xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
47 xmlns:svg="http://www.w3.org/2000/svg"
47 xmlns:svg="http://www.w3.org/2000/svg"
48 xmlns="http://www.w3.org/2000/svg"
48 xmlns="http://www.w3.org/2000/svg"
49 xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
49 xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
50 xmlns:inkscape="http://setwww.inkscape.org/namespaces/inkscape"
50 xmlns:inkscape="http://setwww.inkscape.org/namespaces/inkscape"
51 viewBox="0 -256 1792 1792"
51 viewBox="0 -256 1792 1792"
52 id="svg3025"
52 id="svg3025"
53 version="1.1"
53 version="1.1"
54 inkscape:version="0.48.3.1 r9886"
54 inkscape:version="0.48.3.1 r9886"
55 width="100%"
55 width="100%"
56 height="100%"
56 height="100%"
57 sodipodi:docname="cog_font_awesome.svg">
57 sodipodi:docname="cog_font_awesome.svg">
58 <metadata
58 <metadata
59 id="metadata3035">
59 id="metadata3035">
60 <rdf:RDF>
60 <rdf:RDF>
61 <cc:Work
61 <cc:Work
62 rdf:about="">
62 rdf:about="">
63 <dc:format>image/svg+xml</dc:format>
63 <dc:format>image/svg+xml</dc:format>
64 <dc:type
64 <dc:type
65 rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
65 rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
66 </cc:Work>
66 </cc:Work>
67 </rdf:RDF>
67 </rdf:RDF>
68 </metadata>
68 </metadata>
69 <defs
69 <defs
70 id="defs3033" />
70 id="defs3033" />
71 <sodipodi:namedview
71 <sodipodi:namedview
72 pagecolor="#ffffff"
72 pagecolor="#ffffff"
73 bordercolor="#666666"
73 bordercolor="#666666"
74 borderopacity="1"
74 borderopacity="1"
75 objecttolerance="10"
75 objecttolerance="10"
76 gridtolerance="10"
76 gridtolerance="10"
77 guidetolerance="10"
77 guidetolerance="10"
78 inkscape:pageopacity="0"
78 inkscape:pageopacity="0"
79 inkscape:pageshadow="2"
79 inkscape:pageshadow="2"
80 inkscape:window-width="640"
80 inkscape:window-width="640"
81 inkscape:window-height="480"
81 inkscape:window-height="480"
82 id="namedview3031"
82 id="namedview3031"
83 showgrid="false"
83 showgrid="false"
84 inkscape:zoom="0.13169643"
84 inkscape:zoom="0.13169643"
85 inkscape:cx="896"
85 inkscape:cx="896"
86 inkscape:cy="896"
86 inkscape:cy="896"
87 inkscape:window-x="0"
87 inkscape:window-x="0"
88 inkscape:window-y="25"
88 inkscape:window-y="25"
89 inkscape:window-maximized="0"
89 inkscape:window-maximized="0"
90 inkscape:current-layer="svg3025" />
90 inkscape:current-layer="svg3025" />
91 <g
91 <g
92 transform="matrix(1,0,0,-1,121.49153,1285.4237)"
92 transform="matrix(1,0,0,-1,121.49153,1285.4237)"
93 id="g3027">
93 id="g3027">
94 <path
94 <path
95 d="m 1024,640 q 0,106 -75,181 -75,75 -181,75 -106,0 -181,-75 -75,-75 -75,-181 0,-106 75,-181 75,-75 181,-75 106,0 181,75 75,75 75,181 z m 512,109 V 527 q 0,-12 -8,-23 -8,-11 -20,-13 l -185,-28 q -19,-54 -39,-91 35,-50 107,-138 10,-12 10,-25 0,-13 -9,-23 -27,-37 -99,-108 -72,-71 -94,-71 -12,0 -26,9 l -138,108 q -44,-23 -91,-38 -16,-136 -29,-186 -7,-28 -36,-28 H 657 q -14,0 -24.5,8.5 Q 622,-111 621,-98 L 593,86 q -49,16 -90,37 L 362,16 Q 352,7 337,7 323,7 312,18 186,132 147,186 q -7,10 -7,23 0,12 8,23 15,21 51,66.5 36,45.5 54,70.5 -27,50 -41,99 L 29,495 Q 16,497 8,507.5 0,518 0,531 v 222 q 0,12 8,23 8,11 19,13 l 186,28 q 14,46 39,92 -40,57 -107,138 -10,12 -10,24 0,10 9,23 26,36 98.5,107.5 72.5,71.5 94.5,71.5 13,0 26,-10 l 138,-107 q 44,23 91,38 16,136 29,186 7,28 36,28 h 222 q 14,0 24.5,-8.5 Q 914,1391 915,1378 l 28,-184 q 49,-16 90,-37 l 142,107 q 9,9 24,9 13,0 25,-10 129,-119 165,-170 7,-8 7,-22 0,-12 -8,-23 -15,-21 -51,-66.5 -36,-45.5 -54,-70.5 26,-50 41,-98 l 183,-28 q 13,-2 21,-12.5 8,-10.5 8,-23.5 z"
95 d="m 1024,640 q 0,106 -75,181 -75,75 -181,75 -106,0 -181,-75 -75,-75 -75,-181 0,-106 75,-181 75,-75 181,-75 106,0 181,75 75,75 75,181 z m 512,109 V 527 q 0,-12 -8,-23 -8,-11 -20,-13 l -185,-28 q -19,-54 -39,-91 35,-50 107,-138 10,-12 10,-25 0,-13 -9,-23 -27,-37 -99,-108 -72,-71 -94,-71 -12,0 -26,9 l -138,108 q -44,-23 -91,-38 -16,-136 -29,-186 -7,-28 -36,-28 H 657 q -14,0 -24.5,8.5 Q 622,-111 621,-98 L 593,86 q -49,16 -90,37 L 362,16 Q 352,7 337,7 323,7 312,18 186,132 147,186 q -7,10 -7,23 0,12 8,23 15,21 51,66.5 36,45.5 54,70.5 -27,50 -41,99 L 29,495 Q 16,497 8,507.5 0,518 0,531 v 222 q 0,12 8,23 8,11 19,13 l 186,28 q 14,46 39,92 -40,57 -107,138 -10,12 -10,24 0,10 9,23 26,36 98.5,107.5 72.5,71.5 94.5,71.5 13,0 26,-10 l 138,-107 q 44,23 91,38 16,136 29,186 7,28 36,28 h 222 q 14,0 24.5,-8.5 Q 914,1391 915,1378 l 28,-184 q 49,-16 90,-37 l 142,107 q 9,9 24,9 13,0 25,-10 129,-119 165,-170 7,-8 7,-22 0,-12 -8,-23 -15,-21 -51,-66.5 -36,-45.5 -54,-70.5 26,-50 41,-98 l 183,-28 q 13,-2 21,-12.5 8,-10.5 8,-23.5 z"
96 id="path3029"
96 id="path3029"
97 inkscape:connector-curvature="0"
97 inkscape:connector-curvature="0"
98 style="fill:currentColor" />
98 style="fill:currentColor" />
99 </g>
99 </g>
100 </svg>
100 </svg>
101 '''
101 '''
102
102
103 def __init__(self, settings):
103 def __init__(self, settings):
104 """
104 """
105 :param settings: dict of settings to be used for the integration
105 :param settings: dict of settings to be used for the integration
106 """
106 """
107 self.settings = settings
107 self.settings = settings
108
108
109 def settings_schema(self):
109 def settings_schema(self):
110 """
110 """
111 A colander schema of settings for the integration type
111 A colander schema of settings for the integration type
112 """
112 """
113 return colander.Schema()
113 return colander.Schema()
114
114
115
115
116 class EEIntegration(IntegrationTypeBase):
116 class EEIntegration(IntegrationTypeBase):
117 description = 'Integration available in RhodeCode EE edition.'
117 description = 'Integration available in RhodeCode EE edition.'
118 is_dummy = True
118 is_dummy = True
119
119
120 def __init__(self, name, key, settings=None):
120 def __init__(self, name, key, settings=None):
121 self.display_name = name
121 self.display_name = name
122 self.key = key
122 self.key = key
123 super(EEIntegration, self).__init__(settings)
123 super(EEIntegration, self).__init__(settings)
124
124
125
125
126 # Helpers #
126 # Helpers #
127 # updating this required to update the `common_vars` as well.
127 # updating this required to update the `common_vars` as well.
128 WEBHOOK_URL_VARS = [
128 WEBHOOK_URL_VARS = [
129 ('event_name', 'Unique name of the event type, e.g pullrequest-update'),
129 ('event_name', 'Unique name of the event type, e.g pullrequest-update'),
130 ('repo_name', 'Full name of the repository'),
130 ('repo_name', 'Full name of the repository'),
131 ('repo_type', 'VCS type of repository'),
131 ('repo_type', 'VCS type of repository'),
132 ('repo_id', 'Unique id of repository'),
132 ('repo_id', 'Unique id of repository'),
133 ('repo_url', 'Repository url'),
133 ('repo_url', 'Repository url'),
134 # extra repo fields
134 # extra repo fields
135 ('extra:<extra_key_name>', 'Extra repo variables, read from its settings.'),
135 ('extra:<extra_key_name>', 'Extra repo variables, read from its settings.'),
136
136
137 # special attrs below that we handle, using multi-call
137 # special attrs below that we handle, using multi-call
138 ('branch', 'Name of each branch submitted, if any.'),
138 ('branch', 'Name of each branch submitted, if any.'),
139 ('branch_head', 'Head ID of pushed branch (full sha of last commit), if any.'),
139 ('branch_head', 'Head ID of pushed branch (full sha of last commit), if any.'),
140 ('commit_id', 'ID (full sha) of each commit submitted, if any.'),
140 ('commit_id', 'ID (full sha) of each commit submitted, if any.'),
141
141
142 # pr events vars
142 # pr events vars
143 ('pull_request_id', 'Unique ID of the pull request.'),
143 ('pull_request_id', 'Unique ID of the pull request.'),
144 ('pull_request_title', 'Title of the pull request.'),
144 ('pull_request_title', 'Title of the pull request.'),
145 ('pull_request_url', 'Pull request url.'),
145 ('pull_request_url', 'Pull request url.'),
146 ('pull_request_shadow_url', 'Pull request shadow repo clone url.'),
146 ('pull_request_shadow_url', 'Pull request shadow repo clone url.'),
147 ('pull_request_commits_uid', 'Calculated UID of all commits inside the PR. '
147 ('pull_request_commits_uid', 'Calculated UID of all commits inside the PR. '
148 'Changes after PR update'),
148 'Changes after PR update'),
149
149
150 # user who triggers the call
150 # user who triggers the call
151 ('username', 'User who triggered the call.'),
151 ('username', 'User who triggered the call.'),
152 ('user_id', 'User id who triggered the call.'),
152 ('user_id', 'User id who triggered the call.'),
153 ]
153 ]
154
154
155 # common vars for url template used for CI plugins. Shared with webhook
155 # common vars for url template used for CI plugins. Shared with webhook
156 CI_URL_VARS = WEBHOOK_URL_VARS
156 CI_URL_VARS = WEBHOOK_URL_VARS
157
157
158
158
159 class CommitParsingDataHandler(object):
159 class CommitParsingDataHandler(object):
160
160
161 def aggregate_branch_data(self, branches, commits):
161 def aggregate_branch_data(self, branches, commits):
162 branch_data = collections.OrderedDict()
162 branch_data = collections.OrderedDict()
163 for obj in branches:
163 for obj in branches:
164 branch_data[obj['name']] = obj
164 branch_data[obj['name']] = obj
165
165
166 branches_commits = collections.OrderedDict()
166 branches_commits = collections.OrderedDict()
167 for commit in commits:
167 for commit in commits:
168 if commit.get('git_ref_change'):
168 if commit.get('git_ref_change'):
169 # special case for GIT that allows creating tags,
169 # special case for GIT that allows creating tags,
170 # deleting branches without associated commit
170 # deleting branches without associated commit
171 continue
171 continue
172 commit_branch = commit['branch']
172 commit_branch = commit['branch']
173
173
174 if commit_branch not in branches_commits:
174 if commit_branch not in branches_commits:
175 _branch = branch_data[commit_branch] \
175 _branch = branch_data[commit_branch] \
176 if commit_branch else commit_branch
176 if commit_branch else commit_branch
177 branch_commits = {'branch': _branch,
177 branch_commits = {'branch': _branch,
178 'branch_head': '',
178 'branch_head': '',
179 'commits': []}
179 'commits': []}
180 branches_commits[commit_branch] = branch_commits
180 branches_commits[commit_branch] = branch_commits
181
181
182 branch_commits = branches_commits[commit_branch]
182 branch_commits = branches_commits[commit_branch]
183 branch_commits['commits'].append(commit)
183 branch_commits['commits'].append(commit)
184 branch_commits['branch_head'] = commit['raw_id']
184 branch_commits['branch_head'] = commit['raw_id']
185 return branches_commits
185 return branches_commits
186
186
187
187
188 class WebhookDataHandler(CommitParsingDataHandler):
188 class WebhookDataHandler(CommitParsingDataHandler):
189 name = 'webhook'
189 name = 'webhook'
190
190
191 def __init__(self, template_url, headers):
191 def __init__(self, template_url, headers):
192 self.template_url = template_url
192 self.template_url = template_url
193 self.headers = headers
193 self.headers = headers
194
194
195 def get_base_parsed_template(self, data):
195 def get_base_parsed_template(self, data):
196 """
196 """
197 initially parses the passed in template with some common variables
197 initially parses the passed in template with some common variables
198 available on ALL calls
198 available on ALL calls
199 """
199 """
200 # note: make sure to update the `WEBHOOK_URL_VARS` if this changes
200 # note: make sure to update the `WEBHOOK_URL_VARS` if this changes
201 common_vars = {
201 common_vars = {
202 'repo_name': data['repo']['repo_name'],
202 'repo_name': data['repo']['repo_name'],
203 'repo_type': data['repo']['repo_type'],
203 'repo_type': data['repo']['repo_type'],
204 'repo_id': data['repo']['repo_id'],
204 'repo_id': data['repo']['repo_id'],
205 'repo_url': data['repo']['url'],
205 'repo_url': data['repo']['url'],
206 'username': data['actor']['username'],
206 'username': data['actor']['username'],
207 'user_id': data['actor']['user_id'],
207 'user_id': data['actor']['user_id'],
208 'event_name': data['name']
208 'event_name': data['name']
209 }
209 }
210
210
211 extra_vars = {}
211 extra_vars = {}
212 for extra_key, extra_val in data['repo']['extra_fields'].items():
212 for extra_key, extra_val in data['repo']['extra_fields'].items():
213 extra_vars['extra__{}'.format(extra_key)] = extra_val
213 extra_vars['extra__{}'.format(extra_key)] = extra_val
214 common_vars.update(extra_vars)
214 common_vars.update(extra_vars)
215
215
216 template_url = self.template_url.replace('${extra:', '${extra__')
216 template_url = self.template_url.replace('${extra:', '${extra__')
217 return string.Template(template_url).safe_substitute(**common_vars)
217 return string.Template(template_url).safe_substitute(**common_vars)
218
218
219 def repo_push_event_handler(self, event, data):
219 def repo_push_event_handler(self, event, data):
220 url = self.get_base_parsed_template(data)
220 url = self.get_base_parsed_template(data)
221 url_cals = []
221 url_calls = []
222
222
223 branches_commits = self.aggregate_branch_data(
223 branches_commits = self.aggregate_branch_data(
224 data['push']['branches'], data['push']['commits'])
224 data['push']['branches'], data['push']['commits'])
225 if '${branch}' in url or '${branch_head}' in url:
225 if '${branch}' in url or '${branch_head}' in url or '${commit_id}' in url:
226 # call it multiple times, for each branch if used in variables
226 # call it multiple times, for each branch if used in variables
227 for branch, commit_ids in branches_commits.items():
227 for branch, commit_ids in branches_commits.items():
228 branch_url = string.Template(url).safe_substitute(branch=branch)
228 branch_url = string.Template(url).safe_substitute(branch=branch)
229
229
230 if '${branch_head}' in branch_url:
230 if '${branch_head}' in branch_url:
231 # last commit in the aggregate is the head of the branch
231 # last commit in the aggregate is the head of the branch
232 branch_head = commit_ids['branch_head']
232 branch_head = commit_ids['branch_head']
233 branch_url = string.Template(branch_url).safe_substitute(
233 branch_url = string.Template(branch_url).safe_substitute(
234 branch_head=branch_head)
234 branch_head=branch_head)
235
235
236 # call further down for each commit if used
236 # call further down for each commit if used
237 if '${commit_id}' in branch_url:
237 if '${commit_id}' in branch_url:
238 for commit_data in commit_ids['commits']:
238 for commit_data in commit_ids['commits']:
239 commit_id = commit_data['raw_id']
239 commit_id = commit_data['raw_id']
240 commit_url = string.Template(branch_url).safe_substitute(
240 commit_url = string.Template(branch_url).safe_substitute(
241 commit_id=commit_id)
241 commit_id=commit_id)
242 # register per-commit call
242 # register per-commit call
243 log.debug(
243 log.debug(
244 'register %s call(%s) to url %s',
244 'register %s call(%s) to url %s',
245 self.name, event, commit_url)
245 self.name, event, commit_url)
246 url_cals.append(
246 url_calls.append(
247 (commit_url, self.headers, data))
247 (commit_url, self.headers, data))
248
248
249 else:
249 else:
250 # register per-branch call
250 # register per-branch call
251 log.debug(
251 log.debug(
252 'register %s call(%s) to url %s',
252 'register %s call(%s) to url %s',
253 self.name, event, branch_url)
253 self.name, event, branch_url)
254 url_cals.append(
254 url_calls.append(
255 (branch_url, self.headers, data))
255 (branch_url, self.headers, data))
256
256
257 else:
257 else:
258 log.debug(
258 log.debug(
259 'register %s call(%s) to url %s', self.name, event, url)
259 'register %s call(%s) to url %s', self.name, event, url)
260 url_cals.append((url, self.headers, data))
260 url_calls.append((url, self.headers, data))
261
261
262 return url_cals
262 return url_calls
263
263
264 def repo_create_event_handler(self, event, data):
264 def repo_create_event_handler(self, event, data):
265 url = self.get_base_parsed_template(data)
265 url = self.get_base_parsed_template(data)
266 log.debug(
266 log.debug(
267 'register %s call(%s) to url %s', self.name, event, url)
267 'register %s call(%s) to url %s', self.name, event, url)
268 return [(url, self.headers, data)]
268 return [(url, self.headers, data)]
269
269
270 def pull_request_event_handler(self, event, data):
270 def pull_request_event_handler(self, event, data):
271 url = self.get_base_parsed_template(data)
271 url = self.get_base_parsed_template(data)
272 log.debug(
272 log.debug(
273 'register %s call(%s) to url %s', self.name, event, url)
273 'register %s call(%s) to url %s', self.name, event, url)
274 url = string.Template(url).safe_substitute(
274 url = string.Template(url).safe_substitute(
275 pull_request_id=data['pullrequest']['pull_request_id'],
275 pull_request_id=data['pullrequest']['pull_request_id'],
276 pull_request_title=data['pullrequest']['title'],
276 pull_request_title=data['pullrequest']['title'],
277 pull_request_url=data['pullrequest']['url'],
277 pull_request_url=data['pullrequest']['url'],
278 pull_request_shadow_url=data['pullrequest']['shadow_url'],
278 pull_request_shadow_url=data['pullrequest']['shadow_url'],
279 pull_request_commits_uid=data['pullrequest']['commits_uid'],
279 pull_request_commits_uid=data['pullrequest']['commits_uid'],
280 )
280 )
281 return [(url, self.headers, data)]
281 return [(url, self.headers, data)]
282
282
283 def __call__(self, event, data):
283 def __call__(self, event, data):
284 from rhodecode import events
284 from rhodecode import events
285
285
286 if isinstance(event, events.RepoPushEvent):
286 if isinstance(event, events.RepoPushEvent):
287 return self.repo_push_event_handler(event, data)
287 return self.repo_push_event_handler(event, data)
288 elif isinstance(event, events.RepoCreateEvent):
288 elif isinstance(event, events.RepoCreateEvent):
289 return self.repo_create_event_handler(event, data)
289 return self.repo_create_event_handler(event, data)
290 elif isinstance(event, events.PullRequestEvent):
290 elif isinstance(event, events.PullRequestEvent):
291 return self.pull_request_event_handler(event, data)
291 return self.pull_request_event_handler(event, data)
292 else:
292 else:
293 raise ValueError(
293 raise ValueError(
294 'event type `%s` not in supported list: %s' % (
294 'event type `%s` not in supported list: %s' % (
295 event.__class__, events))
295 event.__class__, events))
296
296
297
297
298 def get_auth(settings):
298 def get_auth(settings):
299 from requests.auth import HTTPBasicAuth
299 from requests.auth import HTTPBasicAuth
300 username = settings.get('username')
300 username = settings.get('username')
301 password = settings.get('password')
301 password = settings.get('password')
302 if username and password:
302 if username and password:
303 return HTTPBasicAuth(username, password)
303 return HTTPBasicAuth(username, password)
304 return None
304 return None
305
305
306
306
307 def get_web_token(settings):
307 def get_web_token(settings):
308 return settings['secret_token']
308 return settings['secret_token']
309
309
310
310
311 def get_url_vars(url_vars):
311 def get_url_vars(url_vars):
312 return '\n'.join(
312 return '\n'.join(
313 '{} - {}'.format('${' + key + '}', explanation)
313 '{} - {}'.format('${' + key + '}', explanation)
314 for key, explanation in url_vars)
314 for key, explanation in url_vars)
315
315
316
316
317 def render_with_traceback(template, *args, **kwargs):
317 def render_with_traceback(template, *args, **kwargs):
318 try:
318 try:
319 return template.render(*args, **kwargs)
319 return template.render(*args, **kwargs)
320 except Exception:
320 except Exception:
321 log.error(exceptions.text_error_template().render())
321 log.error(exceptions.text_error_template().render())
322 raise
322 raise
@@ -1,124 +1,132 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2018 RhodeCode GmbH
3 # Copyright (C) 2010-2018 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
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 import pytest
21 import pytest
22
22
23 from rhodecode import events
23 from rhodecode import events
24 from rhodecode.lib.utils2 import AttributeDict
24 from rhodecode.lib.utils2 import AttributeDict
25 from rhodecode.integrations.types.webhook import WebhookDataHandler
25 from rhodecode.integrations.types.webhook import WebhookDataHandler
26
26
27
27
28 @pytest.fixture
28 @pytest.fixture
29 def base_data():
29 def base_data():
30 return {
30 return {
31 'name': 'event',
31 'name': 'event',
32 'repo': {
32 'repo': {
33 'repo_name': 'foo',
33 'repo_name': 'foo',
34 'repo_type': 'hg',
34 'repo_type': 'hg',
35 'repo_id': '12',
35 'repo_id': '12',
36 'url': 'http://repo.url/foo',
36 'url': 'http://repo.url/foo',
37 'extra_fields': {},
37 'extra_fields': {},
38 },
38 },
39 'actor': {
39 'actor': {
40 'username': 'actor_name',
40 'username': 'actor_name',
41 'user_id': 1
41 'user_id': 1
42 }
42 }
43 }
43 }
44
44
45
45
46 def test_webhook_parse_url_invalid_event():
46 def test_webhook_parse_url_invalid_event():
47 template_url = 'http://server.com/${repo_name}/build'
47 template_url = 'http://server.com/${repo_name}/build'
48 handler = WebhookDataHandler(
48 handler = WebhookDataHandler(
49 template_url, {'exmaple-header': 'header-values'})
49 template_url, {'exmaple-header': 'header-values'})
50 event = events.RepoDeleteEvent('')
50 event = events.RepoDeleteEvent('')
51 with pytest.raises(ValueError) as err:
51 with pytest.raises(ValueError) as err:
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.startswith(
56 'event type `%s` not in supported list' % event.__class__)
56 'event type `%s` not in supported list' % event.__class__)
57
57
58
58
59 @pytest.mark.parametrize('template,expected_urls', [
59 @pytest.mark.parametrize('template,expected_urls', [
60 ('http://server.com/${repo_name}/build',
60 ('http://server.com/${repo_name}/build',
61 ['http://server.com/foo/build']),
61 ['http://server.com/foo/build']),
62 ('http://server.com/${repo_name}/${repo_type}',
62 ('http://server.com/${repo_name}/${repo_type}',
63 ['http://server.com/foo/hg']),
63 ['http://server.com/foo/hg']),
64 ('http://${server}.com/${repo_name}/${repo_id}',
64 ('http://${server}.com/${repo_name}/${repo_id}',
65 ['http://${server}.com/foo/12']),
65 ['http://${server}.com/foo/12']),
66 ('http://server.com/${branch}/build',
66 ('http://server.com/${branch}/build',
67 ['http://server.com/${branch}/build']),
67 ['http://server.com/${branch}/build']),
68 ])
68 ])
69 def test_webook_parse_url_for_create_event(base_data, template, expected_urls):
69 def test_webook_parse_url_for_create_event(base_data, template, expected_urls):
70 headers = {'exmaple-header': 'header-values'}
70 headers = {'exmaple-header': 'header-values'}
71 handler = WebhookDataHandler(template, headers)
71 handler = WebhookDataHandler(template, headers)
72 urls = handler(events.RepoCreateEvent(''), base_data)
72 urls = handler(events.RepoCreateEvent(''), base_data)
73 assert urls == [
73 assert urls == [
74 (url, headers, base_data) for url in expected_urls]
74 (url, headers, base_data) for url in expected_urls]
75
75
76
76
77 @pytest.mark.parametrize('template,expected_urls', [
77 @pytest.mark.parametrize('template,expected_urls', [
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://pr-url.com']),
82 ])
82 ])
83 def test_webook_parse_url_for_pull_request_event(
83 def test_webook_parse_url_for_pull_request_event(
84 base_data, template, expected_urls):
84 base_data, template, expected_urls):
85
85
86 base_data['pullrequest'] = {
86 base_data['pullrequest'] = {
87 'pull_request_id': 999,
87 'pull_request_id': 999,
88 'url': 'http://pr-url.com',
88 'url': 'http://pr-url.com',
89 'title': 'example-pr-title',
89 'title': 'example-pr-title',
90 'commits_uid': 'abcdefg1234',
90 'commits_uid': 'abcdefg1234',
91 'shadow_url': 'http://pr-url.com/repository'
91 'shadow_url': 'http://pr-url.com/repository'
92 }
92 }
93 headers = {'exmaple-header': 'header-values'}
93 headers = {'exmaple-header': 'header-values'}
94 handler = WebhookDataHandler(template, headers)
94 handler = WebhookDataHandler(template, headers)
95 urls = handler(events.PullRequestCreateEvent(
95 urls = handler(events.PullRequestCreateEvent(
96 AttributeDict({'target_repo': 'foo'})), base_data)
96 AttributeDict({'target_repo': 'foo'})), base_data)
97 assert urls == [
97 assert urls == [
98 (url, headers, base_data) for url in expected_urls]
98 (url, headers, base_data) for url in expected_urls]
99
99
100
100
101 @pytest.mark.parametrize('template,expected_urls', [
101 @pytest.mark.parametrize('template,expected_urls', [
102 ('http://server.com/${branch}/build',
102 ('http://server.com/${branch}/build',
103 ['http://server.com/stable/build',
103 ['http://server.com/stable/build',
104 'http://server.com/dev/build']),
104 'http://server.com/dev/build']),
105 ('http://server.com/${branch}/${commit_id}',
105 ('http://server.com/${branch}/${commit_id}',
106 ['http://server.com/stable/stable-xxx',
106 ['http://server.com/stable/stable-xxx',
107 'http://server.com/stable/stable-yyy',
107 'http://server.com/stable/stable-yyy',
108 'http://server.com/dev/dev-xxx',
108 'http://server.com/dev/dev-xxx',
109 'http://server.com/dev/dev-yyy']),
109 'http://server.com/dev/dev-yyy']),
110 ('http://server.com/${branch_head}',
111 ['http://server.com/stable-yyy',
112 'http://server.com/dev-yyy']),
113 ('http://server.com/${commit_id}',
114 ['http://server.com/stable-xxx',
115 'http://server.com/stable-yyy',
116 'http://server.com/dev-xxx',
117 'http://server.com/dev-yyy']),
110 ])
118 ])
111 def test_webook_parse_url_for_push_event(
119 def test_webook_parse_url_for_push_event(
112 baseapp, repo_push_event, base_data, template, expected_urls):
120 baseapp, repo_push_event, base_data, template, expected_urls):
113 base_data['push'] = {
121 base_data['push'] = {
114 'branches': [{'name': 'stable'}, {'name': 'dev'}],
122 'branches': [{'name': 'stable'}, {'name': 'dev'}],
115 'commits': [{'branch': 'stable', 'raw_id': 'stable-xxx'},
123 'commits': [{'branch': 'stable', 'raw_id': 'stable-xxx'},
116 {'branch': 'stable', 'raw_id': 'stable-yyy'},
124 {'branch': 'stable', 'raw_id': 'stable-yyy'},
117 {'branch': 'dev', 'raw_id': 'dev-xxx'},
125 {'branch': 'dev', 'raw_id': 'dev-xxx'},
118 {'branch': 'dev', 'raw_id': 'dev-yyy'}]
126 {'branch': 'dev', 'raw_id': 'dev-yyy'}]
119 }
127 }
120 headers = {'exmaple-header': 'header-values'}
128 headers = {'exmaple-header': 'header-values'}
121 handler = WebhookDataHandler(template, headers)
129 handler = WebhookDataHandler(template, headers)
122 urls = handler(repo_push_event, base_data)
130 urls = handler(repo_push_event, base_data)
123 assert urls == [
131 assert urls == [
124 (url, headers, base_data) for url in expected_urls]
132 (url, headers, base_data) for url in expected_urls]
General Comments 0
You need to be logged in to leave comments. Login now