##// END OF EJS Templates
rcextensions: added logging for basic function calls
marcink -
r3921:36433cb7 default
parent child Browse files
Show More
@@ -1,431 +1,432 b''
1 1 # Copyright (C) 2016-2019 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
5 5 # (only), as published by the Free Software Foundation.
6 6 #
7 7 # This program is distributed in the hope that it will be useful,
8 8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 10 # GNU General Public License for more details.
11 11 #
12 12 # You should have received a copy of the GNU Affero General Public License
13 13 # along with this program. If not, see <http://www.gnu.org/licenses/>.
14 14 #
15 15 # This program is dual-licensed. If you wish to learn more about the
16 16 # RhodeCode Enterprise Edition, including its added features, Support services,
17 17 # and proprietary license terms, please see https://rhodecode.com/licenses/
18
18 import logging
19 19 from .utils import DotDict, HookResponse, has_kwargs
20 log = logging.getLogger('rhodecode.' + __name__)
20 21
21 22
22 23 # Config shortcut to keep, all configuration in one place
23 24 # Example: api_key = CONFIG.my_config.api_key
24 25 CONFIG = DotDict(
25 26 my_config=DotDict(
26 27 api_key='<secret>',
27 28 ),
28 29
29 30 )
30 31
31 32
32 33 @has_kwargs({
33 34 'repo_name': '',
34 35 'repo_type': '',
35 36 'description': '',
36 37 'private': '',
37 38 'created_on': '',
38 39 'enable_downloads': '',
39 40 'repo_id': '',
40 41 'user_id': '',
41 42 'enable_statistics': '',
42 43 'clone_uri': '',
43 44 'fork_id': '',
44 45 'group_id': '',
45 46 'created_by': ''
46 47 })
47 48 def _create_repo_hook(*args, **kwargs):
48 49 """
49 50 POST CREATE REPOSITORY HOOK. This function will be executed after
50 51 each repository is created. kwargs available:
51 52
52 53 """
53 54 return HookResponse(0, '')
54 55
55 56
56 57 @has_kwargs({
57 58 'group_name': '',
58 59 'group_parent_id': '',
59 60 'group_description': '',
60 61 'group_id': '',
61 62 'user_id': '',
62 63 'created_by': '',
63 64 'created_on': '',
64 65 'enable_locking': ''
65 66 })
66 67 def _create_repo_group_hook(*args, **kwargs):
67 68 """
68 69 POST CREATE REPOSITORY GROUP HOOK, this function will be
69 70 executed after each repository group is created. kwargs available:
70 71 """
71 72 return HookResponse(0, '')
72 73
73 74
74 75 @has_kwargs({
75 76 'username': '',
76 77 'password': '',
77 78 'email': '',
78 79 'firstname': '',
79 80 'lastname': '',
80 81 'active': '',
81 82 'admin': '',
82 83 'created_by': '',
83 84 })
84 85 def _pre_create_user_hook(*args, **kwargs):
85 86 """
86 87 PRE CREATE USER HOOK, this function will be executed before each
87 88 user is created, it returns a tuple of bool, reason.
88 89 If bool is False the user creation will be stopped and reason
89 90 will be displayed to the user.
90 91
91 92 Return HookResponse(1, reason) to block user creation
92 93
93 94 """
94 95
95 96 reason = 'allowed'
96 97 return HookResponse(0, reason)
97 98
98 99
99 100 @has_kwargs({
100 101 'username': '',
101 102 'full_name_or_username': '',
102 103 'full_contact': '',
103 104 'user_id': '',
104 105 'name': '',
105 106 'firstname': '',
106 107 'short_contact': '',
107 108 'admin': '',
108 109 'lastname': '',
109 110 'ip_addresses': '',
110 111 'extern_type': '',
111 112 'extern_name': '',
112 113 'email': '',
113 114 'api_key': '',
114 115 'api_keys': '',
115 116 'last_login': '',
116 117 'full_name': '',
117 118 'active': '',
118 119 'password': '',
119 120 'emails': '',
120 121 'inherit_default_permissions': '',
121 122 'created_by': '',
122 123 'created_on': '',
123 124 })
124 125 def _create_user_hook(*args, **kwargs):
125 126 """
126 127 POST CREATE USER HOOK, this function will be executed after each user is created
127 128 """
128 129 return HookResponse(0, '')
129 130
130 131
131 132 @has_kwargs({
132 133 'repo_name': '',
133 134 'repo_type': '',
134 135 'description': '',
135 136 'private': '',
136 137 'created_on': '',
137 138 'enable_downloads': '',
138 139 'repo_id': '',
139 140 'user_id': '',
140 141 'enable_statistics': '',
141 142 'clone_uri': '',
142 143 'fork_id': '',
143 144 'group_id': '',
144 145 'deleted_by': '',
145 146 'deleted_on': '',
146 147 })
147 148 def _delete_repo_hook(*args, **kwargs):
148 149 """
149 150 POST DELETE REPOSITORY HOOK, this function will be executed after
150 151 each repository deletion
151 152 """
152 153 return HookResponse(0, '')
153 154
154 155
155 156 @has_kwargs({
156 157 'username': '',
157 158 'full_name_or_username': '',
158 159 'full_contact': '',
159 160 'user_id': '',
160 161 'name': '',
161 162 'short_contact': '',
162 163 'admin': '',
163 164 'firstname': '',
164 165 'lastname': '',
165 166 'ip_addresses': '',
166 167 'email': '',
167 168 'api_key': '',
168 169 'last_login': '',
169 170 'full_name': '',
170 171 'active': '',
171 172 'password': '',
172 173 'emails': '',
173 174 'inherit_default_permissions': '',
174 175 'deleted_by': '',
175 176 })
176 177 def _delete_user_hook(*args, **kwargs):
177 178 """
178 179 POST DELETE USER HOOK, this function will be executed after each
179 180 user is deleted kwargs available:
180 181 """
181 182 return HookResponse(0, '')
182 183
183 184
184 185 # =============================================================================
185 186 # PUSH/PULL RELATED HOOKS
186 187 # =============================================================================
187 188 @has_kwargs({
188 189 'server_url': 'url of instance that triggered this hook',
189 190 'config': 'path to .ini config used',
190 191 'scm': 'type of version control "git", "hg", "svn"',
191 192 'username': 'username of actor who triggered this event',
192 193 'ip': 'ip address of actor who triggered this hook',
193 194 'action': '',
194 195 'repository': 'repository name',
195 196 'repo_store_path': 'full path to where repositories are stored',
196 197 'commit_ids': 'pre transaction metadata for commit ids',
197 198 'hook_type': '',
198 199 'user_agent': 'Client user agent, e.g git or mercurial CLI version',
199 200 })
200 201 def _pre_push_hook(*args, **kwargs):
201 202 """
202 203 Post push hook
203 204 To stop version control from storing the transaction and send a message to user
204 205 use non-zero HookResponse with a message, e.g return HookResponse(1, 'Not allowed')
205 206
206 207 This message will be shown back to client during PUSH operation
207 208
208 209 Commit ids might look like that::
209 210
210 211 [{u'hg_env|git_env': ...,
211 212 u'multiple_heads': [],
212 213 u'name': u'default',
213 214 u'new_rev': u'd0befe0692e722e01d5677f27a104631cf798b69',
214 215 u'old_rev': u'd0befe0692e722e01d5677f27a104631cf798b69',
215 216 u'ref': u'',
216 217 u'total_commits': 2,
217 218 u'type': u'branch'}]
218 219 """
219 220 return HookResponse(0, '')
220 221
221 222
222 223 @has_kwargs({
223 224 'server_url': 'url of instance that triggered this hook',
224 225 'config': 'path to .ini config used',
225 226 'scm': 'type of version control "git", "hg", "svn"',
226 227 'username': 'username of actor who triggered this event',
227 228 'ip': 'ip address of actor who triggered this hook',
228 229 'action': '',
229 230 'repository': 'repository name',
230 231 'repo_store_path': 'full path to where repositories are stored',
231 232 'commit_ids': 'list of pushed commit_ids (sha1)',
232 233 'hook_type': '',
233 234 'user_agent': 'Client user agent, e.g git or mercurial CLI version',
234 235 })
235 236 def _push_hook(*args, **kwargs):
236 237 """
237 238 POST PUSH HOOK, this function will be executed after each push it's
238 239 executed after the build-in hook that RhodeCode uses for logging pushes
239 240 """
240 241 return HookResponse(0, '')
241 242
242 243
243 244 @has_kwargs({
244 245 'server_url': 'url of instance that triggered this hook',
245 246 'repo_store_path': 'full path to where repositories are stored',
246 247 'config': 'path to .ini config used',
247 248 'scm': 'type of version control "git", "hg", "svn"',
248 249 'username': 'username of actor who triggered this event',
249 250 'ip': 'ip address of actor who triggered this hook',
250 251 'action': '',
251 252 'repository': 'repository name',
252 253 'hook_type': '',
253 254 'user_agent': 'Client user agent, e.g git or mercurial CLI version',
254 255 })
255 256 def _pre_pull_hook(*args, **kwargs):
256 257 """
257 258 Post pull hook
258 259 """
259 260 return HookResponse(0, '')
260 261
261 262
262 263 @has_kwargs({
263 264 'server_url': 'url of instance that triggered this hook',
264 265 'repo_store_path': 'full path to where repositories are stored',
265 266 'config': 'path to .ini config used',
266 267 'scm': 'type of version control "git", "hg", "svn"',
267 268 'username': 'username of actor who triggered this event',
268 269 'ip': 'ip address of actor who triggered this hook',
269 270 'action': '',
270 271 'repository': 'repository name',
271 272 'hook_type': '',
272 273 'user_agent': 'Client user agent, e.g git or mercurial CLI version',
273 274 })
274 275 def _pull_hook(*args, **kwargs):
275 276 """
276 277 This hook will be executed after each code pull.
277 278 """
278 279 return HookResponse(0, '')
279 280
280 281
281 282 # =============================================================================
282 283 # PULL REQUEST RELATED HOOKS
283 284 # =============================================================================
284 285 @has_kwargs({
285 286 'server_url': 'url of instance that triggered this hook',
286 287 'config': 'path to .ini config used',
287 288 'scm': 'type of version control "git", "hg", "svn"',
288 289 'username': 'username of actor who triggered this event',
289 290 'ip': 'ip address of actor who triggered this hook',
290 291 'action': '',
291 292 'repository': 'repository name',
292 293 'pull_request_id': '',
293 294 'url': '',
294 295 'title': '',
295 296 'description': '',
296 297 'status': '',
297 298 'created_on': '',
298 299 'updated_on': '',
299 300 'commit_ids': '',
300 301 'review_status': '',
301 302 'mergeable': '',
302 303 'source': '',
303 304 'target': '',
304 305 'author': '',
305 306 'reviewers': '',
306 307 })
307 308 def _create_pull_request_hook(*args, **kwargs):
308 309 """
309 310 This hook will be executed after creation of a pull request.
310 311 """
311 312 return HookResponse(0, '')
312 313
313 314
314 315 @has_kwargs({
315 316 'server_url': 'url of instance that triggered this hook',
316 317 'config': 'path to .ini config used',
317 318 'scm': 'type of version control "git", "hg", "svn"',
318 319 'username': 'username of actor who triggered this event',
319 320 'ip': 'ip address of actor who triggered this hook',
320 321 'action': '',
321 322 'repository': 'repository name',
322 323 'pull_request_id': '',
323 324 'url': '',
324 325 'title': '',
325 326 'description': '',
326 327 'status': '',
327 328 'created_on': '',
328 329 'updated_on': '',
329 330 'commit_ids': '',
330 331 'review_status': '',
331 332 'mergeable': '',
332 333 'source': '',
333 334 'target': '',
334 335 'author': '',
335 336 'reviewers': '',
336 337 })
337 338 def _review_pull_request_hook(*args, **kwargs):
338 339 """
339 340 This hook will be executed after review action was made on a pull request.
340 341 """
341 342 return HookResponse(0, '')
342 343
343 344
344 345 @has_kwargs({
345 346 'server_url': 'url of instance that triggered this hook',
346 347 'config': 'path to .ini config used',
347 348 'scm': 'type of version control "git", "hg", "svn"',
348 349 'username': 'username of actor who triggered this event',
349 350 'ip': 'ip address of actor who triggered this hook',
350 351 'action': '',
351 352 'repository': 'repository name',
352 353 'pull_request_id': '',
353 354 'url': '',
354 355 'title': '',
355 356 'description': '',
356 357 'status': '',
357 358 'created_on': '',
358 359 'updated_on': '',
359 360 'commit_ids': '',
360 361 'review_status': '',
361 362 'mergeable': '',
362 363 'source': '',
363 364 'target': '',
364 365 'author': '',
365 366 'reviewers': '',
366 367 })
367 368 def _update_pull_request_hook(*args, **kwargs):
368 369 """
369 370 This hook will be executed after pull requests has been updated with new commits.
370 371 """
371 372 return HookResponse(0, '')
372 373
373 374
374 375 @has_kwargs({
375 376 'server_url': 'url of instance that triggered this hook',
376 377 'config': 'path to .ini config used',
377 378 'scm': 'type of version control "git", "hg", "svn"',
378 379 'username': 'username of actor who triggered this event',
379 380 'ip': 'ip address of actor who triggered this hook',
380 381 'action': '',
381 382 'repository': 'repository name',
382 383 'pull_request_id': '',
383 384 'url': '',
384 385 'title': '',
385 386 'description': '',
386 387 'status': '',
387 388 'created_on': '',
388 389 'updated_on': '',
389 390 'commit_ids': '',
390 391 'review_status': '',
391 392 'mergeable': '',
392 393 'source': '',
393 394 'target': '',
394 395 'author': '',
395 396 'reviewers': '',
396 397 })
397 398 def _merge_pull_request_hook(*args, **kwargs):
398 399 """
399 400 This hook will be executed after merge of a pull request.
400 401 """
401 402 return HookResponse(0, '')
402 403
403 404
404 405 @has_kwargs({
405 406 'server_url': 'url of instance that triggered this hook',
406 407 'config': 'path to .ini config used',
407 408 'scm': 'type of version control "git", "hg", "svn"',
408 409 'username': 'username of actor who triggered this event',
409 410 'ip': 'ip address of actor who triggered this hook',
410 411 'action': '',
411 412 'repository': 'repository name',
412 413 'pull_request_id': '',
413 414 'url': '',
414 415 'title': '',
415 416 'description': '',
416 417 'status': '',
417 418 'created_on': '',
418 419 'updated_on': '',
419 420 'commit_ids': '',
420 421 'review_status': '',
421 422 'mergeable': '',
422 423 'source': '',
423 424 'target': '',
424 425 'author': '',
425 426 'reviewers': '',
426 427 })
427 428 def _close_pull_request_hook(*args, **kwargs):
428 429 """
429 430 This hook will be executed after close of a pull request.
430 431 """
431 432 return HookResponse(0, '')
@@ -1,185 +1,189 b''
1 1 # Copyright (C) 2016-2019 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
5 5 # (only), as published by the Free Software Foundation.
6 6 #
7 7 # This program is distributed in the hope that it will be useful,
8 8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 10 # GNU General Public License for more details.
11 11 #
12 12 # You should have received a copy of the GNU Affero General Public License
13 13 # along with this program. If not, see <http://www.gnu.org/licenses/>.
14 14 #
15 15 # This program is dual-licensed. If you wish to learn more about the
16 16 # RhodeCode Enterprise Edition, including its added features, Support services,
17 17 # and proprietary license terms, please see https://rhodecode.com/licenses/
18 18
19 import logging
19 20 import os
20 21 import functools
21 22 import collections
22 23
24 log = logging.getLogger('rhodecode.' + __name__)
25
23 26
24 27 class HookResponse(object):
25 28 def __init__(self, status, output):
26 29 self.status = status
27 30 self.output = output
28 31
29 32 def __add__(self, other):
30 33 other_status = getattr(other, 'status', 0)
31 34 new_status = max(self.status, other_status)
32 35 other_output = getattr(other, 'output', '')
33 36 new_output = self.output + other_output
34 37
35 38 return HookResponse(new_status, new_output)
36 39
37 40 def __bool__(self):
38 41 return self.status == 0
39 42
40 43
41 44 class DotDict(dict):
42 45
43 46 def __contains__(self, k):
44 47 try:
45 48 return dict.__contains__(self, k) or hasattr(self, k)
46 49 except:
47 50 return False
48 51
49 52 # only called if k not found in normal places
50 53 def __getattr__(self, k):
51 54 try:
52 55 return object.__getattribute__(self, k)
53 56 except AttributeError:
54 57 try:
55 58 return self[k]
56 59 except KeyError:
57 60 raise AttributeError(k)
58 61
59 62 def __setattr__(self, k, v):
60 63 try:
61 64 object.__getattribute__(self, k)
62 65 except AttributeError:
63 66 try:
64 67 self[k] = v
65 68 except:
66 69 raise AttributeError(k)
67 70 else:
68 71 object.__setattr__(self, k, v)
69 72
70 73 def __delattr__(self, k):
71 74 try:
72 75 object.__getattribute__(self, k)
73 76 except AttributeError:
74 77 try:
75 78 del self[k]
76 79 except KeyError:
77 80 raise AttributeError(k)
78 81 else:
79 82 object.__delattr__(self, k)
80 83
81 84 def toDict(self):
82 85 return unserialize(self)
83 86
84 87 def __repr__(self):
85 88 keys = list(self.keys())
86 89 keys.sort()
87 90 args = ', '.join(['%s=%r' % (key, self[key]) for key in keys])
88 91 return '%s(%s)' % (self.__class__.__name__, args)
89 92
90 93 @staticmethod
91 94 def fromDict(d):
92 95 return serialize(d)
93 96
94 97
95 98 def serialize(x):
96 99 if isinstance(x, dict):
97 100 return DotDict((k, serialize(v)) for k, v in x.items())
98 101 elif isinstance(x, (list, tuple)):
99 102 return type(x)(serialize(v) for v in x)
100 103 else:
101 104 return x
102 105
103 106
104 107 def unserialize(x):
105 108 if isinstance(x, dict):
106 109 return dict((k, unserialize(v)) for k, v in x.items())
107 110 elif isinstance(x, (list, tuple)):
108 111 return type(x)(unserialize(v) for v in x)
109 112 else:
110 113 return x
111 114
112 115
113 116 def _verify_kwargs(func_name, expected_parameters, kwargs):
114 117 """
115 118 Verify that exactly `expected_parameters` are passed in as `kwargs`.
116 119 """
117 120 expected_parameters = set(expected_parameters)
118 121 kwargs_keys = set(kwargs.keys())
119 122 if kwargs_keys != expected_parameters:
120 123 missing_kwargs = expected_parameters - kwargs_keys
121 124 unexpected_kwargs = kwargs_keys - expected_parameters
122 125 raise AssertionError(
123 126 "func:%s: missing parameters: %r, unexpected parameters: %s" %
124 127 (func_name, missing_kwargs, unexpected_kwargs))
125 128
126 129
127 130 def has_kwargs(required_args):
128 131 """
129 132 decorator to verify extension calls arguments.
130 133
131 134 :param required_args:
132 135 """
133 136 def wrap(func):
134 137 def wrapper(*args, **kwargs):
135 138 _verify_kwargs(func.func_name, required_args.keys(), kwargs)
136 139 # in case there's `calls` defined on module we store the data
137 140 maybe_log_call(func.func_name, args, kwargs)
141 log.debug('Calling rcextensions function %s', func.func_name)
138 142 return func(*args, **kwargs)
139 143 return wrapper
140 144 return wrap
141 145
142 146
143 147 def maybe_log_call(name, args, kwargs):
144 148 from rhodecode.config import rcextensions
145 149 if hasattr(rcextensions, 'calls'):
146 150 calls = rcextensions.calls
147 151 calls[name].append((args, kwargs))
148 152
149 153
150 154 def str2bool(_str):
151 155 """
152 156 returns True/False value from given string, it tries to translate the
153 157 string into boolean
154 158
155 159 :param _str: string value to translate into boolean
156 160 :rtype: boolean
157 161 :returns: boolean from given string
158 162 """
159 163 if _str is None:
160 164 return False
161 165 if _str in (True, False):
162 166 return _str
163 167 _str = str(_str).strip().lower()
164 168 return _str in ('t', 'true', 'y', 'yes', 'on', '1')
165 169
166 170
167 171 def aslist(obj, sep=None, strip=True):
168 172 """
169 173 Returns given string separated by sep as list
170 174
171 175 :param obj:
172 176 :param sep:
173 177 :param strip:
174 178 """
175 179 if isinstance(obj, (basestring,)):
176 180 lst = obj.split(sep)
177 181 if strip:
178 182 lst = [v.strip() for v in lst]
179 183 return lst
180 184 elif isinstance(obj, (list, tuple)):
181 185 return obj
182 186 elif obj is None:
183 187 return []
184 188 else:
185 189 return [obj] No newline at end of file
General Comments 0
You need to be logged in to leave comments. Login now