Show More
@@ -21,8 +21,7 b'' | |||
|
21 | 21 | import logging |
|
22 | 22 | |
|
23 | 23 | from rhodecode.integrations.registry import IntegrationTypeRegistry |
|
24 | from rhodecode.integrations.types import webhook, slack, hipchat, email | |
|
25 | ||
|
24 | from rhodecode.integrations.types import webhook, slack, hipchat, email, base | |
|
26 | 25 | log = logging.getLogger(__name__) |
|
27 | 26 | |
|
28 | 27 | |
@@ -41,6 +40,13 b' integration_type_registry.register_integ' | |||
|
41 | 40 | email.EmailIntegrationType) |
|
42 | 41 | |
|
43 | 42 | |
|
43 | # dummy EE integration to show users what we have in EE edition | |
|
44 | integration_type_registry.register_integration_type( | |
|
45 | base.EEIntegration('Jira Issues integration', 'jira')) | |
|
46 | integration_type_registry.register_integration_type( | |
|
47 | base.EEIntegration('Redmine Tracker integration', 'redmine')) | |
|
48 | ||
|
49 | ||
|
44 | 50 | def integrations_event_handler(event): |
|
45 | 51 | """ |
|
46 | 52 | Takes an event and passes it to all enabled integrations |
@@ -18,11 +18,12 b'' | |||
|
18 | 18 | # and proprietary license terms, please see https://rhodecode.com/licenses/ |
|
19 | 19 | |
|
20 | 20 | import logging |
|
21 | import collections | |
|
21 | 22 | |
|
22 | 23 | log = logging.getLogger(__name__) |
|
23 | 24 | |
|
24 | 25 | |
|
25 | class IntegrationTypeRegistry(dict): | |
|
26 | class IntegrationTypeRegistry(collections.OrderedDict): | |
|
26 | 27 | """ |
|
27 | 28 | Registry Class to hold IntegrationTypes |
|
28 | 29 | """ |
@@ -43,15 +43,15 b' class TestGlobalIntegrationsView(TestInt' | |||
|
43 | 43 | response = self.app.get(url) |
|
44 | 44 | |
|
45 | 45 | assert response.status_code == 200 |
|
46 | assert 'exist yet' in response.body | |
|
46 | response.mustcontain('exist yet') | |
|
47 | 47 | |
|
48 | 48 | def test_index_with_integrations(self, global_integration_stub): |
|
49 | 49 | url = ADMIN_PREFIX + '/integrations' |
|
50 | 50 | response = self.app.get(url) |
|
51 | 51 | |
|
52 | 52 | assert response.status_code == 200 |
|
53 | assert 'exist yet' not in response.body | |
|
54 |
|
|
|
53 | response.mustcontain(no=['exist yet']) | |
|
54 | response.mustcontain(global_integration_stub.name) | |
|
55 | 55 | |
|
56 | 56 | @pytest.mark.parametrize( |
|
57 | 57 | 'IntegrationType', integration_type_registry.values()) |
@@ -59,27 +59,29 b' class TestGlobalIntegrationsView(TestInt' | |||
|
59 | 59 | url = ADMIN_PREFIX + '/integrations/new' |
|
60 | 60 | |
|
61 | 61 | response = self.app.get(url, status=200) |
|
62 | ||
|
63 | url = (ADMIN_PREFIX + '/integrations/{integration}/new').format( | |
|
64 | integration=IntegrationType.key) | |
|
65 | assert url in response.body | |
|
62 | if not IntegrationType.is_dummy: | |
|
63 | url = (ADMIN_PREFIX + '/integrations/{integration}/new').format( | |
|
64 | integration=IntegrationType.key) | |
|
65 | response.mustcontain(url) | |
|
66 | 66 | |
|
67 | 67 | @pytest.mark.parametrize( |
|
68 | 68 | 'IntegrationType', integration_type_registry.values()) |
|
69 | 69 | def test_get_create_integration_page(self, IntegrationType): |
|
70 | 70 | url = ADMIN_PREFIX + '/integrations/{integration_key}/new'.format( |
|
71 | 71 | integration_key=IntegrationType.key) |
|
72 | ||
|
73 |
|
|
|
74 | ||
|
75 | assert IntegrationType.display_name in response.body | |
|
72 | if IntegrationType.is_dummy: | |
|
73 | self.app.get(url, status=404) | |
|
74 | else: | |
|
75 | response = self.app.get(url, status=200) | |
|
76 | response.mustcontain(IntegrationType.display_name) | |
|
76 | 77 | |
|
77 | 78 | def test_post_integration_page(self, StubIntegrationType, csrf_token, |
|
78 | 79 | test_repo_group, backend_random): |
|
79 | 80 | url = ADMIN_PREFIX + '/integrations/{integration_key}/new'.format( |
|
80 | 81 | integration_key=StubIntegrationType.key) |
|
81 | 82 | |
|
82 |
_post_integration_test_helper( |
|
|
83 | _post_integration_test_helper( | |
|
84 | self.app, url, csrf_token, admin_view=True, | |
|
83 | 85 | repo=backend_random.repo, repo_group=test_repo_group) |
|
84 | 86 | |
|
85 | 87 | |
@@ -90,7 +92,7 b' class TestRepoIntegrationsView(TestInteg' | |||
|
90 | 92 | response = self.app.get(url) |
|
91 | 93 | |
|
92 | 94 | assert response.status_code == 200 |
|
93 | assert 'exist yet' in response.body | |
|
95 | response.mustcontain('exist yet') | |
|
94 | 96 | |
|
95 | 97 | def test_index_with_integrations(self, repo_integration_stub): |
|
96 | 98 | url = '/{repo_name}/settings/integrations'.format( |
@@ -100,8 +102,8 b' class TestRepoIntegrationsView(TestInteg' | |||
|
100 | 102 | response = self.app.get(url) |
|
101 | 103 | |
|
102 | 104 | assert response.status_code == 200 |
|
103 | assert stub_name in response.body | |
|
104 | assert 'exist yet' not in response.body | |
|
105 | response.mustcontain(stub_name) | |
|
106 | response.mustcontain(no=['exist yet']) | |
|
105 | 107 | |
|
106 | 108 | @pytest.mark.parametrize( |
|
107 | 109 | 'IntegrationType', integration_type_registry.values()) |
@@ -115,8 +117,8 b' class TestRepoIntegrationsView(TestInteg' | |||
|
115 | 117 | url = '/{repo_name}/settings/integrations/{integration}/new'.format( |
|
116 | 118 | repo_name=repo_name, |
|
117 | 119 | integration=IntegrationType.key) |
|
118 | ||
|
119 | assert url in response.body | |
|
120 | if not IntegrationType.is_dummy: | |
|
121 | response.mustcontain(url) | |
|
120 | 122 | |
|
121 | 123 | @pytest.mark.parametrize( |
|
122 | 124 | 'IntegrationType', integration_type_registry.values()) |
@@ -124,10 +126,11 b' class TestRepoIntegrationsView(TestInteg' | |||
|
124 | 126 | repo_name = backend_random.repo.repo_name |
|
125 | 127 | url = '/{repo_name}/settings/integrations/{integration_key}/new'.format( |
|
126 | 128 | repo_name=repo_name, integration_key=IntegrationType.key) |
|
127 | ||
|
128 |
|
|
|
129 | ||
|
130 | assert IntegrationType.display_name in response.body | |
|
129 | if IntegrationType.is_dummy: | |
|
130 | self.app.get(url, status=404) | |
|
131 | else: | |
|
132 | response = self.app.get(url, status=200) | |
|
133 | response.mustcontain(IntegrationType.display_name) | |
|
131 | 134 | |
|
132 | 135 | def test_post_integration_page(self, backend_random, test_repo_group, |
|
133 | 136 | StubIntegrationType, csrf_token): |
@@ -147,7 +150,7 b' class TestRepoGroupIntegrationsView(Test' | |||
|
147 | 150 | response = self.app.get(url) |
|
148 | 151 | |
|
149 | 152 | assert response.status_code == 200 |
|
150 | assert 'exist yet' in response.body | |
|
153 | response.mustcontain('exist yet') | |
|
151 | 154 | |
|
152 | 155 | def test_index_with_integrations( |
|
153 | 156 | self, test_repo_group, repogroup_integration_stub): |
@@ -159,8 +162,8 b' class TestRepoGroupIntegrationsView(Test' | |||
|
159 | 162 | response = self.app.get(url) |
|
160 | 163 | |
|
161 | 164 | assert response.status_code == 200 |
|
162 | assert 'exist yet' not in response.body | |
|
163 | assert stub_name in response.body | |
|
165 | response.mustcontain(no=['exist yet']) | |
|
166 | response.mustcontain(stub_name) | |
|
164 | 167 | |
|
165 | 168 | def test_new_integration_page(self, test_repo_group): |
|
166 | 169 | repo_group_name = test_repo_group.group_name |
@@ -171,12 +174,13 b' class TestRepoGroupIntegrationsView(Test' | |||
|
171 | 174 | |
|
172 | 175 | assert response.status_code == 200 |
|
173 | 176 | |
|
174 | for integration_key in integration_type_registry: | |
|
175 | nurl = ('/{repo_group_name}/settings/integrations/{integration}/new').format( | |
|
177 | for integration_key, integration_obj in integration_type_registry.items(): | |
|
178 | if not integration_obj.is_dummy: | |
|
179 | nurl = ( | |
|
180 | '/{repo_group_name}/settings/integrations/{integration}/new').format( | |
|
176 | 181 | repo_group_name=repo_group_name, |
|
177 | 182 | integration=integration_key) |
|
178 | ||
|
179 | assert nurl in response.body | |
|
183 | response.mustcontain(nurl) | |
|
180 | 184 | |
|
181 | 185 | @pytest.mark.parametrize( |
|
182 | 186 | 'IntegrationType', integration_type_registry.values()) |
@@ -188,10 +192,11 b' class TestRepoGroupIntegrationsView(Test' | |||
|
188 | 192 | ).format(repo_group_name=repo_group_name, |
|
189 | 193 | integration_key=IntegrationType.key) |
|
190 | 194 | |
|
191 | response = self.app.get(url) | |
|
192 | ||
|
193 | assert response.status_code == 200 | |
|
194 | assert IntegrationType.display_name in response.body | |
|
195 | if not IntegrationType.is_dummy: | |
|
196 | response = self.app.get(url, status=200) | |
|
197 | response.mustcontain(IntegrationType.display_name) | |
|
198 | else: | |
|
199 | self.app.get(url, status=404) | |
|
195 | 200 | |
|
196 | 201 | def test_post_integration_page(self, test_repo_group, backend_random, |
|
197 | 202 | StubIntegrationType, csrf_token): |
@@ -217,7 +222,7 b' def _post_integration_test_helper(app, u' | |||
|
217 | 222 | app.post(url, params={}, status=403) # missing csrf check |
|
218 | 223 | response = app.post(url, params={'csrf_token': csrf_token}) |
|
219 | 224 | assert response.status_code == 200 |
|
220 | assert 'Errors exist' in response.body | |
|
225 | response.mustcontain('Errors exist') | |
|
221 | 226 | |
|
222 | 227 | scopes_destinations = [ |
|
223 | 228 | ('global', |
@@ -24,7 +24,7 b' from rhodecode.translation import _' | |||
|
24 | 24 | |
|
25 | 25 | class IntegrationTypeBase(object): |
|
26 | 26 | """ Base class for IntegrationType plugins """ |
|
27 | ||
|
27 | is_dummy = False | |
|
28 | 28 | description = '' |
|
29 | 29 | icon = ''' |
|
30 | 30 | <?xml version="1.0" encoding="UTF-8" standalone="no"?> |
@@ -99,3 +99,12 b' class IntegrationTypeBase(object):' | |||
|
99 | 99 | A colander schema of settings for the integration type |
|
100 | 100 | """ |
|
101 | 101 | return colander.Schema() |
|
102 | ||
|
103 | ||
|
104 | class EEIntegration(IntegrationTypeBase): | |
|
105 | description = 'Integration available in RhodeCode EE edition.' | |
|
106 | is_dummy = True | |
|
107 | ||
|
108 | def __init__(self, name, key, settings=None): | |
|
109 | self.display_name = name | |
|
110 | self.key = key |
@@ -23,7 +23,7 b' import logging' | |||
|
23 | 23 | import peppercorn |
|
24 | 24 | import webhelpers.paginate |
|
25 | 25 | |
|
26 | from pyramid.httpexceptions import HTTPFound, HTTPForbidden | |
|
26 | from pyramid.httpexceptions import HTTPFound, HTTPForbidden, HTTPNotFound | |
|
27 | 27 | |
|
28 | 28 | from rhodecode.apps._base import BaseAppView |
|
29 | 29 | from rhodecode.integrations import integration_type_registry |
@@ -76,7 +76,12 b' class IntegrationSettingsViewBase(BaseAp' | |||
|
76 | 76 | |
|
77 | 77 | if 'integration' in request.matchdict: # integration type context |
|
78 | 78 | integration_type = request.matchdict['integration'] |
|
79 | if integration_type not in integration_type_registry: | |
|
80 | raise HTTPNotFound() | |
|
81 | ||
|
79 | 82 | self.IntegrationType = integration_type_registry[integration_type] |
|
83 | if self.IntegrationType.is_dummy: | |
|
84 | raise HTTPNotFound() | |
|
80 | 85 | |
|
81 | 86 | if 'integration_id' in request.matchdict: # single integration context |
|
82 | 87 | integration_id = request.matchdict['integration_id'] |
@@ -1195,6 +1195,9 b' table.integrations {' | |||
|
1195 | 1195 | min-width: 140px; |
|
1196 | 1196 | } |
|
1197 | 1197 | } |
|
1198 | a.integration-box.dummy-integration { | |
|
1199 | color: @grey4 | |
|
1200 | } | |
|
1198 | 1201 | } |
|
1199 | 1202 | |
|
1200 | 1203 | //Permissions Settings |
@@ -64,23 +64,25 b'' | |||
|
64 | 64 | <a href="${home_url}" class="btn ${not c.current_IntegrationType and 'btn-primary' or ''}">${_('All')}</a> |
|
65 | 65 | |
|
66 | 66 | %for integration_key, IntegrationType in c.available_integrations.items(): |
|
67 | <% | |
|
68 | if c.repo: | |
|
69 | list_url = request.route_path('repo_integrations_list', | |
|
70 | repo_name=c.repo.repo_name, | |
|
71 |
|
|
|
72 | elif c.repo_group: | |
|
73 | list_url = request.route_path('repo_group_integrations_list', | |
|
74 | repo_group_name=c.repo_group.group_name, | |
|
75 |
|
|
|
76 | else: | |
|
77 | list_url = request.route_path('global_integrations_list', | |
|
78 | integration=integration_key) | |
|
79 | %> | |
|
80 | <a href="${list_url}" | |
|
81 | class="btn ${c.current_IntegrationType and integration_key == c.current_IntegrationType.key and 'btn-primary' or ''}"> | |
|
82 | ${IntegrationType.display_name} | |
|
83 | </a> | |
|
67 | % if not IntegrationType.is_dummy: | |
|
68 | <% | |
|
69 | if c.repo: | |
|
70 | list_url = request.route_path('repo_integrations_list', | |
|
71 | repo_name=c.repo.repo_name, | |
|
72 | integration=integration_key) | |
|
73 | elif c.repo_group: | |
|
74 | list_url = request.route_path('repo_group_integrations_list', | |
|
75 | repo_group_name=c.repo_group.group_name, | |
|
76 | integration=integration_key) | |
|
77 | else: | |
|
78 | list_url = request.route_path('global_integrations_list', | |
|
79 | integration=integration_key) | |
|
80 | %> | |
|
81 | <a href="${list_url}" | |
|
82 | class="btn ${c.current_IntegrationType and integration_key == c.current_IntegrationType.key and 'btn-primary' or ''}"> | |
|
83 | ${IntegrationType.display_name} | |
|
84 | </a> | |
|
85 | % endif | |
|
84 | 86 | %endfor |
|
85 | 87 | |
|
86 | 88 | <% |
@@ -36,7 +36,7 b'' | |||
|
36 | 36 | %endif |
|
37 | 37 | </%def> |
|
38 | 38 | |
|
39 |
%for integration, Integration |
|
|
39 | %for integration, IntegrationObject in c.available_integrations.items(): | |
|
40 | 40 | <% |
|
41 | 41 | if c.repo: |
|
42 | 42 | create_url = request.route_path('repo_integrations_create', |
@@ -49,16 +49,18 b'' | |||
|
49 | 49 | else: |
|
50 | 50 | create_url = request.route_path('global_integrations_create', |
|
51 | 51 | integration=integration) |
|
52 | if IntegrationObject.is_dummy: | |
|
53 | create_url = request.current_route_path() | |
|
52 | 54 | %> |
|
53 | <a href="${create_url}" class="integration-box"> | |
|
55 | <a href="${create_url}" class="integration-box ${'dummy-integration' if IntegrationObject.is_dummy else ''}"> | |
|
54 | 56 | <%widgets:panel> |
|
55 | 57 | <h2> |
|
56 | 58 | <div class="integration-icon"> |
|
57 |
${Integration |
|
|
59 | ${IntegrationObject.icon|n} | |
|
58 | 60 | </div> |
|
59 |
${Integration |
|
|
61 | ${IntegrationObject.display_name} | |
|
60 | 62 | </h2> |
|
61 |
${Integration |
|
|
63 | ${IntegrationObject.description or _('No description available')} | |
|
62 | 64 | </%widgets:panel> |
|
63 | 65 | </a> |
|
64 | 66 | %endfor |
General Comments 0
You need to be logged in to leave comments.
Login now