##// END OF EJS Templates
integrations-db: don't use default contructor to be consisten with other modules...
marcink -
r448:b686b009 default
parent child Browse files
Show More

The requested changes are too big and content was truncated. Show full diff

@@ -1,258 +1,258 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 import colander
22 22 import logging
23 23 import pylons
24 24
25 25 from pyramid.httpexceptions import HTTPFound, HTTPForbidden
26 26 from pyramid.renderers import render
27 27 from pyramid.response import Response
28 28
29 29 from rhodecode.lib import auth
30 30 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator
31 31 from rhodecode.model.db import Repository, Session, Integration
32 32 from rhodecode.model.scm import ScmModel
33 33 from rhodecode.model.integration import IntegrationModel
34 34 from rhodecode.admin.navigation import navigation_list
35 35 from rhodecode.translation import _
36 36 from rhodecode.integrations import integration_type_registry
37 37
38 38 log = logging.getLogger(__name__)
39 39
40 40
41 41 class IntegrationSettingsViewBase(object):
42 42 """ Base Integration settings view used by both repo / global settings """
43 43
44 44 def __init__(self, context, request):
45 45 self.context = context
46 46 self.request = request
47 47 self._load_general_context()
48 48
49 49 if not self.perm_check(request.user):
50 50 raise HTTPForbidden()
51 51
52 52 def _load_general_context(self):
53 53 """
54 54 This avoids boilerplate for repo/global+list/edit+views/templates
55 55 by doing all possible contexts at the same time however it should
56 56 be split up into separate functions once more "contexts" exist
57 57 """
58 58
59 59 self.IntegrationType = None
60 60 self.repo = None
61 61 self.integration = None
62 62 self.integrations = {}
63 63
64 64 request = self.request
65 65
66 66 if 'repo_name' in request.matchdict: # we're in a repo context
67 67 repo_name = request.matchdict['repo_name']
68 68 self.repo = Repository.get_by_repo_name(repo_name)
69 69
70 70 if 'integration' in request.matchdict: # we're in integration context
71 71 integration_type = request.matchdict['integration']
72 72 self.IntegrationType = integration_type_registry[integration_type]
73 73
74 74 if 'integration_id' in request.matchdict: # single integration context
75 75 integration_id = request.matchdict['integration_id']
76 76 self.integration = Integration.get(integration_id)
77 77 else: # list integrations context
78 78 for integration in IntegrationModel().get_integrations(self.repo):
79 79 self.integrations.setdefault(integration.integration_type, []
80 80 ).append(integration)
81 81
82 82 self.settings = self.integration and self.integration.settings or {}
83 83
84 84 def _template_c_context(self):
85 85 # TODO: dan: this is a stopgap in order to inherit from current pylons
86 86 # based admin/repo settings templates - this should be removed entirely
87 87 # after port to pyramid
88 88
89 89 c = pylons.tmpl_context
90 90 c.active = 'integrations'
91 91 c.rhodecode_user = self.request.user
92 92 c.repo = self.repo
93 93 c.repo_name = self.repo and self.repo.repo_name or None
94 94 if self.repo:
95 95 c.repo_info = self.repo
96 96 c.rhodecode_db_repo = self.repo
97 97 c.repository_pull_requests = ScmModel().get_pull_requests(self.repo)
98 98 else:
99 99 c.navlist = navigation_list(self.request)
100 100
101 101 return c
102 102
103 103 def _form_schema(self):
104 104 return self.IntegrationType.settings_schema()
105 105
106 106 def settings_get(self, defaults=None, errors=None):
107 107 """
108 108 View that displays the plugin settings as a form.
109 109 """
110 110 defaults = defaults or {}
111 111 errors = errors or {}
112 112
113 113 schema = self._form_schema()
114 114
115 115 if not defaults:
116 116 if self.integration:
117 117 defaults['enabled'] = self.integration.enabled
118 118 defaults['name'] = self.integration.name
119 119 else:
120 120 if self.repo:
121 121 scope = self.repo.repo_name
122 122 else:
123 123 scope = _('Global')
124 124
125 125 defaults['name'] = '{} {} integration'.format(scope,
126 126 self.IntegrationType.display_name)
127 127 defaults['enabled'] = True
128 128
129 129 for node in schema:
130 130 setting = self.settings.get(node.name)
131 131 if setting is not None:
132 132 defaults.setdefault(node.name, setting)
133 133 else:
134 134 if node.default:
135 135 defaults.setdefault(node.name, node.default)
136 136
137 137 template_context = {
138 138 'defaults': defaults,
139 139 'errors': errors,
140 140 'schema': schema,
141 141 'current_IntegrationType': self.IntegrationType,
142 142 'integration': self.integration,
143 143 'settings': self.settings,
144 144 'resource': self.context,
145 145 'c': self._template_c_context(),
146 146 }
147 147
148 148 return template_context
149 149
150 150 @auth.CSRFRequired()
151 151 def settings_post(self):
152 152 """
153 153 View that validates and stores the plugin settings.
154 154 """
155 155 if self.request.params.get('delete'):
156 156 Session().delete(self.integration)
157 157 Session().commit()
158 158 self.request.session.flash(
159 159 _('Integration {integration_name} deleted successfully.').format(
160 160 integration_name=self.integration.name),
161 161 queue='success')
162 162 if self.repo:
163 163 redirect_to = self.request.route_url(
164 164 'repo_integrations_home', repo_name=self.repo.repo_name)
165 165 else:
166 166 redirect_to = self.request.route_url('global_integrations_home')
167 167 raise HTTPFound(redirect_to)
168 168
169 169 schema = self._form_schema()
170 170
171 171 params = {}
172 172 for node in schema.children:
173 173 if type(node.typ) in (colander.Set, colander.List):
174 174 val = self.request.params.getall(node.name)
175 175 else:
176 176 val = self.request.params.get(node.name)
177 177 if val:
178 178 params[node.name] = val
179 179
180 180 try:
181 181 valid_data = schema.deserialize(params)
182 except colander.Invalid, e:
182 except colander.Invalid as e:
183 183 # Display error message and display form again.
184 184 self.request.session.flash(
185 185 _('Errors exist when saving plugin settings. '
186 186 'Please check the form inputs.'),
187 187 queue='error')
188 188 return self.settings_get(errors=e.asdict(), defaults=params)
189 189
190 190 if not self.integration:
191 self.integration = Integration(
192 integration_type=self.IntegrationType.key)
191 self.integration = Integration()
192 self.integration.integration_type = self.IntegrationType.key
193 193 if self.repo:
194 194 self.integration.repo = self.repo
195 Session.add(self.integration)
195 Session().add(self.integration)
196 196
197 197 self.integration.enabled = valid_data.pop('enabled', False)
198 198 self.integration.name = valid_data.pop('name')
199 199 self.integration.settings = valid_data
200 200
201 Session.commit()
201 Session().commit()
202 202
203 203 # Display success message and redirect.
204 204 self.request.session.flash(
205 205 _('Integration {integration_name} updated successfully.').format(
206 206 integration_name=self.IntegrationType.display_name),
207 207 queue='success')
208 208
209 209 if self.repo:
210 210 redirect_to = self.request.route_url(
211 211 'repo_integrations_edit', repo_name=self.repo.repo_name,
212 212 integration=self.integration.integration_type,
213 213 integration_id=self.integration.integration_id)
214 214 else:
215 215 redirect_to = self.request.route_url(
216 216 'global_integrations_edit',
217 217 integration=self.integration.integration_type,
218 218 integration_id=self.integration.integration_id)
219 219
220 220 return HTTPFound(redirect_to)
221 221
222 222 def index(self):
223 223 current_integrations = self.integrations
224 224 if self.IntegrationType:
225 225 current_integrations = {
226 226 self.IntegrationType.key: self.integrations.get(
227 227 self.IntegrationType.key, [])
228 228 }
229 229
230 230 template_context = {
231 231 'current_IntegrationType': self.IntegrationType,
232 232 'current_integrations': current_integrations,
233 233 'current_integration': 'none',
234 234 'available_integrations': integration_type_registry,
235 235 'c': self._template_c_context()
236 236 }
237 237
238 238 if self.repo:
239 239 html = render('rhodecode:templates/admin/integrations/list.html',
240 240 template_context,
241 241 request=self.request)
242 242 else:
243 243 html = render('rhodecode:templates/admin/integrations/list.html',
244 244 template_context,
245 245 request=self.request)
246 246
247 247 return Response(html)
248 248
249 249
250 250 class GlobalIntegrationsView(IntegrationSettingsViewBase):
251 251 def perm_check(self, user):
252 252 return auth.HasPermissionAll('hg.admin').check_permissions(user=user)
253 253
254 254
255 255 class RepoIntegrationsView(IntegrationSettingsViewBase):
256 256 def perm_check(self, user):
257 257 return auth.HasRepoPermissionAll('repository.admin'
258 258 )(repo_name=self.repo.repo_name, user=user)
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
@@ -1,132 +1,132 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2011-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
22 22 """
23 23 Model for integrations
24 24 """
25 25
26 26
27 27 import logging
28 28 import traceback
29 29
30 30 from pylons import tmpl_context as c
31 31 from pylons.i18n.translation import _, ungettext
32 32 from sqlalchemy import or_
33 33 from sqlalchemy.sql.expression import false, true
34 34 from mako import exceptions
35 35
36 36 import rhodecode
37 37 from rhodecode import events
38 38 from rhodecode.lib import helpers as h
39 39 from rhodecode.lib.caching_query import FromCache
40 40 from rhodecode.lib.utils import PartialRenderer
41 41 from rhodecode.model import BaseModel
42 42 from rhodecode.model.db import Integration, User
43 43 from rhodecode.model.meta import Session
44 44 from rhodecode.integrations import integration_type_registry
45 45 from rhodecode.integrations.types.base import IntegrationTypeBase
46 46
47 47 log = logging.getLogger(__name__)
48 48
49 49
50 50 class IntegrationModel(BaseModel):
51 51
52 52 cls = Integration
53 53
54 54 def __get_integration(self, integration):
55 55 if isinstance(integration, Integration):
56 56 return integration
57 57 elif isinstance(integration, (int, long)):
58 58 return self.sa.query(Integration).get(integration)
59 59 else:
60 60 if integration:
61 61 raise Exception('integration must be int, long or Instance'
62 62 ' of Integration got %s' % type(integration))
63 63
64 64 def create(self, IntegrationType, enabled, name, settings, repo=None):
65 65 """ Create an IntegrationType integration """
66 integration = Integration(
67 integration_type=IntegrationType.key,
68 settings={},
69 repo=repo,
70 enabled=enabled,
71 name=name
72 )
66 integration = Integration()
67 integration.integration_type = IntegrationType.key
68 integration.settings = {}
69 integration.repo = repo
70 integration.enabled = enabled
71 integration.name = name
72
73 73 self.sa.add(integration)
74 74 self.sa.commit()
75 75 return integration
76 76
77 77 def delete(self, integration):
78 78 try:
79 79 integration = self.__get_integration(integration)
80 80 if integration:
81 81 self.sa.delete(integration)
82 82 return True
83 83 except Exception:
84 84 log.error(traceback.format_exc())
85 85 raise
86 86 return False
87 87
88 88 def get_integration_handler(self, integration):
89 89 TypeClass = integration_type_registry.get(integration.integration_type)
90 90 if not TypeClass:
91 91 log.error('No class could be found for integration type: {}'.format(
92 92 integration.integration_type))
93 93 return None
94 94
95 95 return TypeClass(integration.settings)
96 96
97 97 def send_event(self, integration, event):
98 98 """ Send an event to an integration """
99 99 handler = self.get_integration_handler(integration)
100 100 if handler:
101 101 handler.send_event(event)
102 102
103 103 def get_integrations(self, repo=None):
104 104 if repo:
105 105 return self.sa.query(Integration).filter(
106 106 Integration.repo_id==repo.repo_id).all()
107 107
108 108 # global integrations
109 109 return self.sa.query(Integration).filter(
110 110 Integration.repo_id==None).all()
111 111
112 112 def get_for_event(self, event, cache=False):
113 113 """
114 114 Get integrations that match an event
115 115 """
116 116 query = self.sa.query(Integration).filter(Integration.enabled==True)
117 117
118 118 if isinstance(event, events.RepoEvent): # global + repo integrations
119 119 query = query.filter(
120 120 or_(Integration.repo_id==None,
121 121 Integration.repo_id==event.repo.repo_id))
122 122 if cache:
123 123 query = query.options(FromCache(
124 124 "sql_cache_short",
125 125 "get_enabled_repo_integrations_%i" % event.repo.repo_id))
126 126 else: # only global integrations
127 127 query = query.filter(Integration.repo_id==None)
128 128 if cache:
129 129 query = query.options(FromCache(
130 130 "sql_cache_short", "get_enabled_global_integrations"))
131 131
132 132 return query.all()
@@ -1,96 +1,95 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2010-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 import pytest
22 22 import requests
23 23 from mock import Mock, patch
24 24
25 25 from rhodecode import events
26 26 from rhodecode.model.db import Session, Integration
27 27 from rhodecode.integrations.types.slack import SlackIntegrationType
28 28
29 29 @pytest.fixture
30 30 def repo_push_event(backend, user_regular):
31 31 commits = [
32 32 {'message': 'ancestor commit fixes #15'},
33 33 {'message': 'quick fixes'},
34 34 {'message': 'change that fixes #41, #2'},
35 35 {'message': 'this is because 5b23c3532 broke stuff'},
36 36 {'message': 'last commit'},
37 37 ]
38 38 commit_ids = backend.create_master_repo(commits).values()
39 39 repo = backend.create_repo()
40 40 scm_extras = {
41 41 'ip': '127.0.0.1',
42 42 'username': user_regular.username,
43 43 'action': '',
44 44 'repository': repo.repo_name,
45 45 'scm': repo.scm_instance().alias,
46 46 'config': '',
47 47 'server_url': 'http://example.com',
48 48 'make_lock': None,
49 49 'locked_by': [None],
50 50 'commit_ids': commit_ids,
51 51 }
52 52
53 53 return events.RepoPushEvent(repo_name=repo.repo_name,
54 54 pushed_commit_ids=commit_ids,
55 55 extras=scm_extras)
56 56
57 57
58 58 @pytest.fixture
59 59 def slack_settings():
60 60 return {
61 61 "service": "mock://slackintegration",
62 62 "events": [
63 63 "pullrequest-create",
64 64 "repo-push",
65 65 ],
66 66 "channel": "#testing",
67 67 "icon_emoji": ":recycle:",
68 68 "username": "rhodecode-test"
69 69 }
70 70
71 71
72 72 @pytest.fixture
73 73 def slack_integration(request, app, slack_settings):
74 integration = Integration(
75 name='test slack integration',
76 enabled=True,
77 integration_type=SlackIntegrationType.key
78 )
74 integration = Integration()
75 integration.name = 'test slack integration'
76 integration.enabled = True
77 integration.integration_type = SlackIntegrationType.key
79 78 integration.settings = slack_settings
80 79 Session().add(integration)
81 80 Session().commit()
82 81 request.addfinalizer(lambda: Session().delete(integration))
83 82 return integration
84 83
85 84
86 85 def test_slack_push(slack_integration, repo_push_event):
87 86 with patch('rhodecode.integrations.types.slack.post_text_to_slack') as call:
88 87 events.trigger(repo_push_event)
89 88 assert 'pushed to' in call.call_args[0][1]
90 89
91 90 slack_integration.settings['events'] = []
92 91 Session().commit()
93 92
94 93 with patch('rhodecode.integrations.types.slack.post_text_to_slack') as call:
95 94 events.trigger(repo_push_event)
96 95 assert not call.call_args
General Comments 0
You need to be logged in to leave comments. Login now