##// END OF EJS Templates
invalidation: don't create CacheInvalidation records on startup...
Mads Kiilerich -
r3774:60335b70 beta
parent child Browse files
Show More
@@ -1,242 +1,242 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.controllers.summary
4 4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5 5
6 6 Summary controller for Rhodecode
7 7
8 8 :created_on: Apr 18, 2010
9 9 :author: marcink
10 10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
11 11 :license: GPLv3, see COPYING for more details.
12 12 """
13 13 # This program is free software: you can redistribute it and/or modify
14 14 # it under the terms of the GNU General Public License as published by
15 15 # the Free Software Foundation, either version 3 of the License, or
16 16 # (at your option) any later version.
17 17 #
18 18 # This program is distributed in the hope that it will be useful,
19 19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 21 # GNU General Public License for more details.
22 22 #
23 23 # You should have received a copy of the GNU General Public License
24 24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 25
26 26 import traceback
27 27 import calendar
28 28 import logging
29 29 import urllib
30 30 from time import mktime
31 31 from datetime import timedelta, date
32 32 from urlparse import urlparse
33 33
34 34 from pylons import tmpl_context as c, request, url, config
35 35 from pylons.i18n.translation import _
36 36 from webob.exc import HTTPBadRequest
37 37
38 38 from beaker.cache import cache_region, region_invalidate
39 39
40 40 from rhodecode.lib import helpers as h
41 41 from rhodecode.lib.compat import product
42 42 from rhodecode.lib.vcs.exceptions import ChangesetError, EmptyRepositoryError, \
43 43 NodeDoesNotExistError
44 44 from rhodecode.config.conf import ALL_READMES, ALL_EXTS, LANGUAGES_EXTENSIONS_MAP
45 45 from rhodecode.model.db import Statistics, CacheInvalidation
46 46 from rhodecode.lib.utils import jsonify
47 47 from rhodecode.lib.utils2 import safe_unicode, safe_str
48 48 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator,\
49 49 NotAnonymous
50 50 from rhodecode.lib.base import BaseRepoController, render
51 51 from rhodecode.lib.vcs.backends.base import EmptyChangeset
52 52 from rhodecode.lib.markup_renderer import MarkupRenderer
53 53 from rhodecode.lib.celerylib import run_task
54 54 from rhodecode.lib.celerylib.tasks import get_commits_stats
55 55 from rhodecode.lib.helpers import RepoPage
56 56 from rhodecode.lib.compat import json, OrderedDict
57 57 from rhodecode.lib.vcs.nodes import FileNode
58 58 from rhodecode.controllers.changelog import _load_changelog_summary
59 59
60 60 log = logging.getLogger(__name__)
61 61
62 62 README_FILES = [''.join([x[0][0], x[1][0]]) for x in
63 63 sorted(list(product(ALL_READMES, ALL_EXTS)),
64 64 key=lambda y:y[0][1] + y[1][1])]
65 65
66 66
67 67 class SummaryController(BaseRepoController):
68 68
69 69 def __before__(self):
70 70 super(SummaryController, self).__before__()
71 71
72 72 def _get_download_links(self, repo):
73 73
74 74 download_l = []
75 75
76 76 branches_group = ([], _("Branches"))
77 77 tags_group = ([], _("Tags"))
78 78
79 79 for name, chs in c.rhodecode_repo.branches.items():
80 80 #chs = chs.split(':')[-1]
81 81 branches_group[0].append((chs, name),)
82 82 download_l.append(branches_group)
83 83
84 84 for name, chs in c.rhodecode_repo.tags.items():
85 85 #chs = chs.split(':')[-1]
86 86 tags_group[0].append((chs, name),)
87 87 download_l.append(tags_group)
88 88
89 89 return download_l
90 90
91 91 def __get_readme_data(self, db_repo):
92 92 repo_name = db_repo.repo_name
93 93
94 94 @cache_region('long_term')
95 95 def _get_readme_from_cache(key, kind):
96 96 readme_data = None
97 97 readme_file = None
98 98 log.debug('Looking for README file')
99 99 try:
100 100 # get's the landing revision! or tip if fails
101 101 cs = db_repo.get_landing_changeset()
102 102 if isinstance(cs, EmptyChangeset):
103 103 raise EmptyRepositoryError()
104 104 renderer = MarkupRenderer()
105 105 for f in README_FILES:
106 106 try:
107 107 readme = cs.get_node(f)
108 108 if not isinstance(readme, FileNode):
109 109 continue
110 110 readme_file = f
111 111 log.debug('Found README file `%s` rendering...' %
112 112 readme_file)
113 113 readme_data = renderer.render(readme.content, f)
114 114 break
115 115 except NodeDoesNotExistError:
116 116 continue
117 117 except ChangesetError:
118 118 log.error(traceback.format_exc())
119 119 pass
120 120 except EmptyRepositoryError:
121 121 pass
122 122 except Exception:
123 123 log.error(traceback.format_exc())
124 124
125 125 return readme_data, readme_file
126 126
127 127 kind = 'README'
128 128 valid = CacheInvalidation.test_and_set_valid(repo_name, kind)
129 if not valid:
129 if not valid:
130 130 region_invalidate(_get_readme_from_cache, None, repo_name, kind)
131 131 return _get_readme_from_cache(repo_name, kind)
132 132
133 133 @LoginRequired()
134 134 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
135 135 'repository.admin')
136 136 def index(self, repo_name):
137 137 c.dbrepo = dbrepo = c.rhodecode_db_repo
138 138 _load_changelog_summary()
139 139 if self.rhodecode_user.username == 'default':
140 140 # for default(anonymous) user we don't need to pass credentials
141 141 username = ''
142 142 password = ''
143 143 else:
144 144 username = str(self.rhodecode_user.username)
145 145 password = '@'
146 146
147 147 parsed_url = urlparse(url.current(qualified=True))
148 148
149 149 default_clone_uri = '{scheme}://{user}{pass}{netloc}{path}'
150 150
151 151 uri_tmpl = config.get('clone_uri', default_clone_uri)
152 152 uri_tmpl = uri_tmpl.replace('{', '%(').replace('}', ')s')
153 153 decoded_path = safe_unicode(urllib.unquote(parsed_url.path))
154 154 uri_dict = {
155 155 'user': urllib.quote(username),
156 156 'pass': password,
157 157 'scheme': parsed_url.scheme,
158 158 'netloc': parsed_url.netloc,
159 159 'path': urllib.quote(safe_str(decoded_path))
160 160 }
161 161
162 162 uri = (uri_tmpl % uri_dict)
163 163 # generate another clone url by id
164 164 uri_dict.update(
165 165 {'path': decoded_path.replace(repo_name, '_%s' % c.dbrepo.repo_id)}
166 166 )
167 167 uri_id = uri_tmpl % uri_dict
168 168
169 169 c.clone_repo_url = uri
170 170 c.clone_repo_url_id = uri_id
171 171
172 172 td = date.today() + timedelta(days=1)
173 173 td_1m = td - timedelta(days=calendar.mdays[td.month])
174 174 td_1y = td - timedelta(days=365)
175 175
176 176 ts_min_m = mktime(td_1m.timetuple())
177 177 ts_min_y = mktime(td_1y.timetuple())
178 178 ts_max_y = mktime(td.timetuple())
179 179
180 180 if dbrepo.enable_statistics:
181 181 c.show_stats = True
182 182 c.no_data_msg = _('No data loaded yet')
183 183 recurse_limit = 500 # don't recurse more than 500 times when parsing
184 184 run_task(get_commits_stats, c.dbrepo.repo_name, ts_min_y,
185 185 ts_max_y, recurse_limit)
186 186 else:
187 187 c.show_stats = False
188 188 c.no_data_msg = _('Statistics are disabled for this repository')
189 189 c.ts_min = ts_min_m
190 190 c.ts_max = ts_max_y
191 191
192 192 stats = self.sa.query(Statistics)\
193 193 .filter(Statistics.repository == dbrepo)\
194 194 .scalar()
195 195
196 196 c.stats_percentage = 0
197 197
198 198 if stats and stats.languages:
199 199 c.no_data = False is dbrepo.enable_statistics
200 200 lang_stats_d = json.loads(stats.languages)
201 201 c.commit_data = stats.commit_activity
202 202 c.overview_data = stats.commit_activity_combined
203 203
204 204 lang_stats = ((x, {"count": y,
205 205 "desc": LANGUAGES_EXTENSIONS_MAP.get(x)})
206 206 for x, y in lang_stats_d.items())
207 207
208 208 c.trending_languages = json.dumps(
209 209 sorted(lang_stats, reverse=True, key=lambda k: k[1])[:10]
210 210 )
211 211 last_rev = stats.stat_on_revision + 1
212 212 c.repo_last_rev = c.rhodecode_repo.count()\
213 213 if c.rhodecode_repo.revisions else 0
214 214 if last_rev == 0 or c.repo_last_rev == 0:
215 215 pass
216 216 else:
217 217 c.stats_percentage = '%.2f' % ((float((last_rev)) /
218 218 c.repo_last_rev) * 100)
219 219 else:
220 220 c.commit_data = json.dumps({})
221 221 c.overview_data = json.dumps([[ts_min_y, 0], [ts_max_y, 10]])
222 222 c.trending_languages = json.dumps({})
223 223 c.no_data = True
224 224
225 225 c.enable_downloads = dbrepo.enable_downloads
226 226 if c.enable_downloads:
227 227 c.download_options = self._get_download_links(c.rhodecode_repo)
228 228
229 229 c.readme_data, c.readme_file = \
230 230 self.__get_readme_data(c.rhodecode_db_repo)
231 231 return render('summary/summary.html')
232 232
233 233 @LoginRequired()
234 234 @NotAnonymous()
235 235 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
236 236 'repository.admin')
237 237 @jsonify
238 238 def repo_size(self, repo_name):
239 239 if request.is_xhr:
240 240 return c.rhodecode_db_repo._repo_size()
241 241 else:
242 242 raise HTTPBadRequest()
@@ -1,799 +1,794 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.lib.utils
4 4 ~~~~~~~~~~~~~~~~~~~
5 5
6 6 Utilities library for RhodeCode
7 7
8 8 :created_on: Apr 18, 2010
9 9 :author: marcink
10 10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
11 11 :license: GPLv3, see COPYING for more details.
12 12 """
13 13 # This program is free software: you can redistribute it and/or modify
14 14 # it under the terms of the GNU General Public License as published by
15 15 # the Free Software Foundation, either version 3 of the License, or
16 16 # (at your option) any later version.
17 17 #
18 18 # This program is distributed in the hope that it will be useful,
19 19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 21 # GNU General Public License for more details.
22 22 #
23 23 # You should have received a copy of the GNU General Public License
24 24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 25
26 26 import os
27 27 import re
28 28 import logging
29 29 import datetime
30 30 import traceback
31 31 import paste
32 32 import beaker
33 33 import tarfile
34 34 import shutil
35 35 import decorator
36 36 import warnings
37 37 from os.path import abspath
38 38 from os.path import dirname as dn, join as jn
39 39
40 40 from paste.script.command import Command, BadCommand
41 41
42 42 from mercurial import ui, config
43 43
44 44 from webhelpers.text import collapse, remove_formatting, strip_tags
45 45
46 46 from rhodecode.lib.vcs import get_backend
47 47 from rhodecode.lib.vcs.backends.base import BaseChangeset
48 48 from rhodecode.lib.vcs.utils.lazy import LazyProperty
49 49 from rhodecode.lib.vcs.utils.helpers import get_scm
50 50 from rhodecode.lib.vcs.exceptions import VCSError
51 51
52 52 from rhodecode.lib.caching_query import FromCache
53 53
54 54 from rhodecode.model import meta
55 55 from rhodecode.model.db import Repository, User, RhodeCodeUi, \
56 56 UserLog, RepoGroup, RhodeCodeSetting, CacheInvalidation, UserGroup
57 57 from rhodecode.model.meta import Session
58 58 from rhodecode.model.repos_group import ReposGroupModel
59 59 from rhodecode.lib.utils2 import safe_str, safe_unicode
60 60 from rhodecode.lib.vcs.utils.fakemod import create_module
61 61 from rhodecode.model.users_group import UserGroupModel
62 62
63 63 log = logging.getLogger(__name__)
64 64
65 65 REMOVED_REPO_PAT = re.compile(r'rm__\d{8}_\d{6}_\d{6}__.*')
66 66
67 67
68 68 def recursive_replace(str_, replace=' '):
69 69 """
70 70 Recursive replace of given sign to just one instance
71 71
72 72 :param str_: given string
73 73 :param replace: char to find and replace multiple instances
74 74
75 75 Examples::
76 76 >>> recursive_replace("Mighty---Mighty-Bo--sstones",'-')
77 77 'Mighty-Mighty-Bo-sstones'
78 78 """
79 79
80 80 if str_.find(replace * 2) == -1:
81 81 return str_
82 82 else:
83 83 str_ = str_.replace(replace * 2, replace)
84 84 return recursive_replace(str_, replace)
85 85
86 86
87 87 def repo_name_slug(value):
88 88 """
89 89 Return slug of name of repository
90 90 This function is called on each creation/modification
91 91 of repository to prevent bad names in repo
92 92 """
93 93
94 94 slug = remove_formatting(value)
95 95 slug = strip_tags(slug)
96 96
97 97 for c in """`?=[]\;'"<>,/~!@#$%^&*()+{}|: """:
98 98 slug = slug.replace(c, '-')
99 99 slug = recursive_replace(slug, '-')
100 100 slug = collapse(slug, '-')
101 101 return slug
102 102
103 103
104 104 #==============================================================================
105 105 # PERM DECORATOR HELPERS FOR EXTRACTING NAMES FOR PERM CHECKS
106 106 #==============================================================================
107 107 def get_repo_slug(request):
108 108 _repo = request.environ['pylons.routes_dict'].get('repo_name')
109 109 if _repo:
110 110 _repo = _repo.rstrip('/')
111 111 return _repo
112 112
113 113
114 114 def get_repos_group_slug(request):
115 115 _group = request.environ['pylons.routes_dict'].get('group_name')
116 116 if _group:
117 117 _group = _group.rstrip('/')
118 118 return _group
119 119
120 120
121 121 def get_user_group_slug(request):
122 122 _group = request.environ['pylons.routes_dict'].get('id')
123 123 try:
124 124 _group = UserGroup.get(_group)
125 125 if _group:
126 126 _group = _group.users_group_name
127 127 except Exception:
128 128 log.debug(traceback.format_exc())
129 129 #catch all failures here
130 130 pass
131 131
132 132 return _group
133 133
134 134
135 135 def action_logger(user, action, repo, ipaddr='', sa=None, commit=False):
136 136 """
137 137 Action logger for various actions made by users
138 138
139 139 :param user: user that made this action, can be a unique username string or
140 140 object containing user_id attribute
141 141 :param action: action to log, should be on of predefined unique actions for
142 142 easy translations
143 143 :param repo: string name of repository or object containing repo_id,
144 144 that action was made on
145 145 :param ipaddr: optional ip address from what the action was made
146 146 :param sa: optional sqlalchemy session
147 147
148 148 """
149 149
150 150 if not sa:
151 151 sa = meta.Session()
152 152
153 153 try:
154 154 if hasattr(user, 'user_id'):
155 155 user_obj = User.get(user.user_id)
156 156 elif isinstance(user, basestring):
157 157 user_obj = User.get_by_username(user)
158 158 else:
159 159 raise Exception('You have to provide a user object or a username')
160 160
161 161 if hasattr(repo, 'repo_id'):
162 162 repo_obj = Repository.get(repo.repo_id)
163 163 repo_name = repo_obj.repo_name
164 164 elif isinstance(repo, basestring):
165 165 repo_name = repo.lstrip('/')
166 166 repo_obj = Repository.get_by_repo_name(repo_name)
167 167 else:
168 168 repo_obj = None
169 169 repo_name = ''
170 170
171 171 user_log = UserLog()
172 172 user_log.user_id = user_obj.user_id
173 173 user_log.username = user_obj.username
174 174 user_log.action = safe_unicode(action)
175 175
176 176 user_log.repository = repo_obj
177 177 user_log.repository_name = repo_name
178 178
179 179 user_log.action_date = datetime.datetime.now()
180 180 user_log.user_ip = ipaddr
181 181 sa.add(user_log)
182 182
183 183 log.info('Logging action:%s on %s by user:%s ip:%s' %
184 184 (action, safe_unicode(repo), user_obj, ipaddr))
185 185 if commit:
186 186 sa.commit()
187 187 except Exception:
188 188 log.error(traceback.format_exc())
189 189 raise
190 190
191 191
192 192 def get_filesystem_repos(path, recursive=False, skip_removed_repos=True):
193 193 """
194 194 Scans given path for repos and return (name,(type,path)) tuple
195 195
196 196 :param path: path to scan for repositories
197 197 :param recursive: recursive search and return names with subdirs in front
198 198 """
199 199
200 200 # remove ending slash for better results
201 201 path = path.rstrip(os.sep)
202 202 log.debug('now scanning in %s location recursive:%s...' % (path, recursive))
203 203
204 204 def _get_repos(p):
205 205 if not os.access(p, os.W_OK):
206 206 log.warn('ignoring repo path without write access: %s', p)
207 207 return
208 208 for dirpath in os.listdir(p):
209 209 if os.path.isfile(os.path.join(p, dirpath)):
210 210 continue
211 211 cur_path = os.path.join(p, dirpath)
212 212
213 213 # skip removed repos
214 214 if skip_removed_repos and REMOVED_REPO_PAT.match(dirpath):
215 215 continue
216 216
217 217 #skip .<somethin> dirs
218 218 if dirpath.startswith('.'):
219 219 continue
220 220
221 221 try:
222 222 scm_info = get_scm(cur_path)
223 223 yield scm_info[1].split(path, 1)[-1].lstrip(os.sep), scm_info
224 224 except VCSError:
225 225 if not recursive:
226 226 continue
227 227 #check if this dir containts other repos for recursive scan
228 228 rec_path = os.path.join(p, dirpath)
229 229 if os.path.isdir(rec_path):
230 230 for inner_scm in _get_repos(rec_path):
231 231 yield inner_scm
232 232
233 233 return _get_repos(path)
234 234
235 235
236 236 def is_valid_repo(repo_name, base_path, scm=None):
237 237 """
238 238 Returns True if given path is a valid repository False otherwise.
239 239 If scm param is given also compare if given scm is the same as expected
240 240 from scm parameter
241 241
242 242 :param repo_name:
243 243 :param base_path:
244 244 :param scm:
245 245
246 246 :return True: if given path is a valid repository
247 247 """
248 248 full_path = os.path.join(safe_str(base_path), safe_str(repo_name))
249 249
250 250 try:
251 251 scm_ = get_scm(full_path)
252 252 if scm:
253 253 return scm_[0] == scm
254 254 return True
255 255 except VCSError:
256 256 return False
257 257
258 258
259 259 def is_valid_repos_group(repos_group_name, base_path, skip_path_check=False):
260 260 """
261 261 Returns True if given path is a repository group False otherwise
262 262
263 263 :param repo_name:
264 264 :param base_path:
265 265 """
266 266 full_path = os.path.join(safe_str(base_path), safe_str(repos_group_name))
267 267
268 268 # check if it's not a repo
269 269 if is_valid_repo(repos_group_name, base_path):
270 270 return False
271 271
272 272 try:
273 273 # we need to check bare git repos at higher level
274 274 # since we might match branches/hooks/info/objects or possible
275 275 # other things inside bare git repo
276 276 get_scm(os.path.dirname(full_path))
277 277 return False
278 278 except VCSError:
279 279 pass
280 280
281 281 # check if it's a valid path
282 282 if skip_path_check or os.path.isdir(full_path):
283 283 return True
284 284
285 285 return False
286 286
287 287
288 288 def ask_ok(prompt, retries=4, complaint='Yes or no please!'):
289 289 while True:
290 290 ok = raw_input(prompt)
291 291 if ok in ('y', 'ye', 'yes'):
292 292 return True
293 293 if ok in ('n', 'no', 'nop', 'nope'):
294 294 return False
295 295 retries = retries - 1
296 296 if retries < 0:
297 297 raise IOError
298 298 print complaint
299 299
300 300 #propagated from mercurial documentation
301 301 ui_sections = ['alias', 'auth',
302 302 'decode/encode', 'defaults',
303 303 'diff', 'email',
304 304 'extensions', 'format',
305 305 'merge-patterns', 'merge-tools',
306 306 'hooks', 'http_proxy',
307 307 'smtp', 'patch',
308 308 'paths', 'profiling',
309 309 'server', 'trusted',
310 310 'ui', 'web', ]
311 311
312 312
313 313 def make_ui(read_from='file', path=None, checkpaths=True, clear_session=True):
314 314 """
315 315 A function that will read python rc files or database
316 316 and make an mercurial ui object from read options
317 317
318 318 :param path: path to mercurial config file
319 319 :param checkpaths: check the path
320 320 :param read_from: read from 'file' or 'db'
321 321 """
322 322
323 323 baseui = ui.ui()
324 324
325 325 # clean the baseui object
326 326 baseui._ocfg = config.config()
327 327 baseui._ucfg = config.config()
328 328 baseui._tcfg = config.config()
329 329
330 330 if read_from == 'file':
331 331 if not os.path.isfile(path):
332 332 log.debug('hgrc file is not present at %s, skipping...' % path)
333 333 return False
334 334 log.debug('reading hgrc from %s' % path)
335 335 cfg = config.config()
336 336 cfg.read(path)
337 337 for section in ui_sections:
338 338 for k, v in cfg.items(section):
339 339 log.debug('settings ui from file: [%s] %s=%s' % (section, k, v))
340 340 baseui.setconfig(safe_str(section), safe_str(k), safe_str(v))
341 341
342 342 elif read_from == 'db':
343 343 sa = meta.Session()
344 344 ret = sa.query(RhodeCodeUi)\
345 345 .options(FromCache("sql_cache_short", "get_hg_ui_settings"))\
346 346 .all()
347 347
348 348 hg_ui = ret
349 349 for ui_ in hg_ui:
350 350 if ui_.ui_active:
351 351 log.debug('settings ui from db: [%s] %s=%s', ui_.ui_section,
352 352 ui_.ui_key, ui_.ui_value)
353 353 baseui.setconfig(safe_str(ui_.ui_section), safe_str(ui_.ui_key),
354 354 safe_str(ui_.ui_value))
355 355 if ui_.ui_key == 'push_ssl':
356 356 # force set push_ssl requirement to False, rhodecode
357 357 # handles that
358 358 baseui.setconfig(safe_str(ui_.ui_section), safe_str(ui_.ui_key),
359 359 False)
360 360 if clear_session:
361 361 meta.Session.remove()
362 362 return baseui
363 363
364 364
365 365 def set_rhodecode_config(config):
366 366 """
367 367 Updates pylons config with new settings from database
368 368
369 369 :param config:
370 370 """
371 371 hgsettings = RhodeCodeSetting.get_app_settings()
372 372
373 373 for k, v in hgsettings.items():
374 374 config[k] = v
375 375
376 376
377 377 def map_groups(path):
378 378 """
379 379 Given a full path to a repository, create all nested groups that this
380 380 repo is inside. This function creates parent-child relationships between
381 381 groups and creates default perms for all new groups.
382 382
383 383 :param paths: full path to repository
384 384 """
385 385 sa = meta.Session()
386 386 groups = path.split(Repository.url_sep())
387 387 parent = None
388 388 group = None
389 389
390 390 # last element is repo in nested groups structure
391 391 groups = groups[:-1]
392 392 rgm = ReposGroupModel(sa)
393 393 owner = User.get_first_admin()
394 394 for lvl, group_name in enumerate(groups):
395 395 group_name = '/'.join(groups[:lvl] + [group_name])
396 396 group = RepoGroup.get_by_group_name(group_name)
397 397 desc = '%s group' % group_name
398 398
399 399 # skip folders that are now removed repos
400 400 if REMOVED_REPO_PAT.match(group_name):
401 401 break
402 402
403 403 if group is None:
404 404 log.debug('creating group level: %s group_name: %s'
405 405 % (lvl, group_name))
406 406 group = RepoGroup(group_name, parent)
407 407 group.group_description = desc
408 408 group.user = owner
409 409 sa.add(group)
410 410 perm_obj = rgm._create_default_perms(group)
411 411 sa.add(perm_obj)
412 412 sa.flush()
413 413
414 414 parent = group
415 415 return group
416 416
417 417
418 418 def repo2db_mapper(initial_repo_list, remove_obsolete=False,
419 419 install_git_hook=False):
420 420 """
421 421 maps all repos given in initial_repo_list, non existing repositories
422 422 are created, if remove_obsolete is True it also check for db entries
423 423 that are not in initial_repo_list and removes them.
424 424
425 425 :param initial_repo_list: list of repositories found by scanning methods
426 426 :param remove_obsolete: check for obsolete entries in database
427 427 :param install_git_hook: if this is True, also check and install githook
428 428 for a repo if missing
429 429 """
430 430 from rhodecode.model.repo import RepoModel
431 431 from rhodecode.model.scm import ScmModel
432 432 sa = meta.Session()
433 433 rm = RepoModel()
434 434 user = User.get_first_admin()
435 435 added = []
436 436
437 437 ##creation defaults
438 438 defs = RhodeCodeSetting.get_default_repo_settings(strip_prefix=True)
439 439 enable_statistics = defs.get('repo_enable_statistics')
440 440 enable_locking = defs.get('repo_enable_locking')
441 441 enable_downloads = defs.get('repo_enable_downloads')
442 442 private = defs.get('repo_private')
443 443
444 444 for name, repo in initial_repo_list.items():
445 445 group = map_groups(name)
446 446 db_repo = rm.get_by_repo_name(name)
447 447 # found repo that is on filesystem not in RhodeCode database
448 448 if not db_repo:
449 449 log.info('repository %s not found, creating now' % name)
450 450 added.append(name)
451 451 desc = (repo.description
452 452 if repo.description != 'unknown'
453 453 else '%s repository' % name)
454 454
455 455 new_repo = rm.create_repo(
456 456 repo_name=name,
457 457 repo_type=repo.alias,
458 458 description=desc,
459 459 repos_group=getattr(group, 'group_id', None),
460 460 owner=user,
461 461 just_db=True,
462 462 enable_locking=enable_locking,
463 463 enable_downloads=enable_downloads,
464 464 enable_statistics=enable_statistics,
465 465 private=private
466 466 )
467 467 # we added that repo just now, and make sure it has githook
468 468 # installed
469 469 if new_repo.repo_type == 'git':
470 470 ScmModel().install_git_hook(new_repo.scm_instance)
471 471 new_repo.update_changeset_cache()
472 472 elif install_git_hook:
473 473 if db_repo.repo_type == 'git':
474 474 ScmModel().install_git_hook(db_repo.scm_instance)
475 # during starting install all cache keys for all repositories in the
476 # system, this will register all repos and multiple instances
477 cache_key = CacheInvalidation._get_cache_key(name)
478 log.debug("Creating invalidation cache key for %s: %s", name, cache_key)
479 CacheInvalidation.test_and_set_valid(name, None)
480 475
481 476 sa.commit()
482 477 removed = []
483 478 if remove_obsolete:
484 479 # remove from database those repositories that are not in the filesystem
485 480 for repo in sa.query(Repository).all():
486 481 if repo.repo_name not in initial_repo_list.keys():
487 482 log.debug("Removing non-existing repository found in db `%s`" %
488 483 repo.repo_name)
489 484 try:
490 485 removed.append(repo.repo_name)
491 486 RepoModel(sa).delete(repo, forks='detach', fs_remove=False)
492 487 sa.commit()
493 488 except Exception:
494 489 #don't hold further removals on error
495 490 log.error(traceback.format_exc())
496 491 sa.rollback()
497 492 return added, removed
498 493
499 494
500 495 # set cache regions for beaker so celery can utilise it
501 496 def add_cache(settings):
502 497 cache_settings = {'regions': None}
503 498 for key in settings.keys():
504 499 for prefix in ['beaker.cache.', 'cache.']:
505 500 if key.startswith(prefix):
506 501 name = key.split(prefix)[1].strip()
507 502 cache_settings[name] = settings[key].strip()
508 503 if cache_settings['regions']:
509 504 for region in cache_settings['regions'].split(','):
510 505 region = region.strip()
511 506 region_settings = {}
512 507 for key, value in cache_settings.items():
513 508 if key.startswith(region):
514 509 region_settings[key.split('.')[1]] = value
515 510 region_settings['expire'] = int(region_settings.get('expire',
516 511 60))
517 512 region_settings.setdefault('lock_dir',
518 513 cache_settings.get('lock_dir'))
519 514 region_settings.setdefault('data_dir',
520 515 cache_settings.get('data_dir'))
521 516
522 517 if 'type' not in region_settings:
523 518 region_settings['type'] = cache_settings.get('type',
524 519 'memory')
525 520 beaker.cache.cache_regions[region] = region_settings
526 521
527 522
528 523 def load_rcextensions(root_path):
529 524 import rhodecode
530 525 from rhodecode.config import conf
531 526
532 527 path = os.path.join(root_path, 'rcextensions', '__init__.py')
533 528 if os.path.isfile(path):
534 529 rcext = create_module('rc', path)
535 530 EXT = rhodecode.EXTENSIONS = rcext
536 531 log.debug('Found rcextensions now loading %s...' % rcext)
537 532
538 533 # Additional mappings that are not present in the pygments lexers
539 534 conf.LANGUAGES_EXTENSIONS_MAP.update(getattr(EXT, 'EXTRA_MAPPINGS', {}))
540 535
541 536 #OVERRIDE OUR EXTENSIONS FROM RC-EXTENSIONS (if present)
542 537
543 538 if getattr(EXT, 'INDEX_EXTENSIONS', []) != []:
544 539 log.debug('settings custom INDEX_EXTENSIONS')
545 540 conf.INDEX_EXTENSIONS = getattr(EXT, 'INDEX_EXTENSIONS', [])
546 541
547 542 #ADDITIONAL MAPPINGS
548 543 log.debug('adding extra into INDEX_EXTENSIONS')
549 544 conf.INDEX_EXTENSIONS.extend(getattr(EXT, 'EXTRA_INDEX_EXTENSIONS', []))
550 545
551 546 # auto check if the module is not missing any data, set to default if is
552 547 # this will help autoupdate new feature of rcext module
553 548 from rhodecode.config import rcextensions
554 549 for k in dir(rcextensions):
555 550 if not k.startswith('_') and not hasattr(EXT, k):
556 551 setattr(EXT, k, getattr(rcextensions, k))
557 552
558 553
559 554 def get_custom_lexer(extension):
560 555 """
561 556 returns a custom lexer if it's defined in rcextensions module, or None
562 557 if there's no custom lexer defined
563 558 """
564 559 import rhodecode
565 560 from pygments import lexers
566 561 #check if we didn't define this extension as other lexer
567 562 if rhodecode.EXTENSIONS and extension in rhodecode.EXTENSIONS.EXTRA_LEXERS:
568 563 _lexer_name = rhodecode.EXTENSIONS.EXTRA_LEXERS[extension]
569 564 return lexers.get_lexer_by_name(_lexer_name)
570 565
571 566
572 567 #==============================================================================
573 568 # TEST FUNCTIONS AND CREATORS
574 569 #==============================================================================
575 570 def create_test_index(repo_location, config, full_index):
576 571 """
577 572 Makes default test index
578 573
579 574 :param config: test config
580 575 :param full_index:
581 576 """
582 577
583 578 from rhodecode.lib.indexers.daemon import WhooshIndexingDaemon
584 579 from rhodecode.lib.pidlock import DaemonLock, LockHeld
585 580
586 581 repo_location = repo_location
587 582
588 583 index_location = os.path.join(config['app_conf']['index_dir'])
589 584 if not os.path.exists(index_location):
590 585 os.makedirs(index_location)
591 586
592 587 try:
593 588 l = DaemonLock(file_=jn(dn(index_location), 'make_index.lock'))
594 589 WhooshIndexingDaemon(index_location=index_location,
595 590 repo_location=repo_location)\
596 591 .run(full_index=full_index)
597 592 l.release()
598 593 except LockHeld:
599 594 pass
600 595
601 596
602 597 def create_test_env(repos_test_path, config):
603 598 """
604 599 Makes a fresh database and
605 600 install test repository into tmp dir
606 601 """
607 602 from rhodecode.lib.db_manage import DbManage
608 603 from rhodecode.tests import HG_REPO, GIT_REPO, TESTS_TMP_PATH
609 604
610 605 # PART ONE create db
611 606 dbconf = config['sqlalchemy.db1.url']
612 607 log.debug('making test db %s' % dbconf)
613 608
614 609 # create test dir if it doesn't exist
615 610 if not os.path.isdir(repos_test_path):
616 611 log.debug('Creating testdir %s' % repos_test_path)
617 612 os.makedirs(repos_test_path)
618 613
619 614 dbmanage = DbManage(log_sql=True, dbconf=dbconf, root=config['here'],
620 615 tests=True)
621 616 dbmanage.create_tables(override=True)
622 617 dbmanage.create_settings(dbmanage.config_prompt(repos_test_path))
623 618 dbmanage.create_default_user()
624 619 dbmanage.admin_prompt()
625 620 dbmanage.create_permissions()
626 621 dbmanage.populate_default_permissions()
627 622 Session().commit()
628 623 # PART TWO make test repo
629 624 log.debug('making test vcs repositories')
630 625
631 626 idx_path = config['app_conf']['index_dir']
632 627 data_path = config['app_conf']['cache_dir']
633 628
634 629 #clean index and data
635 630 if idx_path and os.path.exists(idx_path):
636 631 log.debug('remove %s' % idx_path)
637 632 shutil.rmtree(idx_path)
638 633
639 634 if data_path and os.path.exists(data_path):
640 635 log.debug('remove %s' % data_path)
641 636 shutil.rmtree(data_path)
642 637
643 638 #CREATE DEFAULT TEST REPOS
644 639 cur_dir = dn(dn(abspath(__file__)))
645 640 tar = tarfile.open(jn(cur_dir, 'tests', "vcs_test_hg.tar.gz"))
646 641 tar.extractall(jn(TESTS_TMP_PATH, HG_REPO))
647 642 tar.close()
648 643
649 644 cur_dir = dn(dn(abspath(__file__)))
650 645 tar = tarfile.open(jn(cur_dir, 'tests', "vcs_test_git.tar.gz"))
651 646 tar.extractall(jn(TESTS_TMP_PATH, GIT_REPO))
652 647 tar.close()
653 648
654 649 #LOAD VCS test stuff
655 650 from rhodecode.tests.vcs import setup_package
656 651 setup_package()
657 652
658 653
659 654 #==============================================================================
660 655 # PASTER COMMANDS
661 656 #==============================================================================
662 657 class BasePasterCommand(Command):
663 658 """
664 659 Abstract Base Class for paster commands.
665 660
666 661 The celery commands are somewhat aggressive about loading
667 662 celery.conf, and since our module sets the `CELERY_LOADER`
668 663 environment variable to our loader, we have to bootstrap a bit and
669 664 make sure we've had a chance to load the pylons config off of the
670 665 command line, otherwise everything fails.
671 666 """
672 667 min_args = 1
673 668 min_args_error = "Please provide a paster config file as an argument."
674 669 takes_config_file = 1
675 670 requires_config_file = True
676 671
677 672 def notify_msg(self, msg, log=False):
678 673 """Make a notification to user, additionally if logger is passed
679 674 it logs this action using given logger
680 675
681 676 :param msg: message that will be printed to user
682 677 :param log: logging instance, to use to additionally log this message
683 678
684 679 """
685 680 if log and isinstance(log, logging):
686 681 log(msg)
687 682
688 683 def run(self, args):
689 684 """
690 685 Overrides Command.run
691 686
692 687 Checks for a config file argument and loads it.
693 688 """
694 689 if len(args) < self.min_args:
695 690 raise BadCommand(
696 691 self.min_args_error % {'min_args': self.min_args,
697 692 'actual_args': len(args)})
698 693
699 694 # Decrement because we're going to lob off the first argument.
700 695 # @@ This is hacky
701 696 self.min_args -= 1
702 697 self.bootstrap_config(args[0])
703 698 self.update_parser()
704 699 return super(BasePasterCommand, self).run(args[1:])
705 700
706 701 def update_parser(self):
707 702 """
708 703 Abstract method. Allows for the class's parser to be updated
709 704 before the superclass's `run` method is called. Necessary to
710 705 allow options/arguments to be passed through to the underlying
711 706 celery command.
712 707 """
713 708 raise NotImplementedError("Abstract Method.")
714 709
715 710 def bootstrap_config(self, conf):
716 711 """
717 712 Loads the pylons configuration.
718 713 """
719 714 from pylons import config as pylonsconfig
720 715
721 716 self.path_to_ini_file = os.path.realpath(conf)
722 717 conf = paste.deploy.appconfig('config:' + self.path_to_ini_file)
723 718 pylonsconfig.init_app(conf.global_conf, conf.local_conf)
724 719
725 720 def _init_session(self):
726 721 """
727 722 Inits SqlAlchemy Session
728 723 """
729 724 logging.config.fileConfig(self.path_to_ini_file)
730 725 from pylons import config
731 726 from rhodecode.model import init_model
732 727 from rhodecode.lib.utils2 import engine_from_config
733 728
734 729 #get to remove repos !!
735 730 add_cache(config)
736 731 engine = engine_from_config(config, 'sqlalchemy.db1.')
737 732 init_model(engine)
738 733
739 734
740 735 def check_git_version():
741 736 """
742 737 Checks what version of git is installed in system, and issues a warning
743 738 if it's too old for RhodeCode to properly work.
744 739 """
745 740 from rhodecode import BACKENDS
746 741 from rhodecode.lib.vcs.backends.git.repository import GitRepository
747 742 from distutils.version import StrictVersion
748 743
749 744 stdout, stderr = GitRepository._run_git_command('--version', _bare=True,
750 745 _safe=True)
751 746
752 747 ver = (stdout.split(' ')[-1] or '').strip() or '0.0.0'
753 748 if len(ver.split('.')) > 3:
754 749 #StrictVersion needs to be only 3 element type
755 750 ver = '.'.join(ver.split('.')[:3])
756 751 try:
757 752 _ver = StrictVersion(ver)
758 753 except Exception:
759 754 _ver = StrictVersion('0.0.0')
760 755 stderr = traceback.format_exc()
761 756
762 757 req_ver = '1.7.4'
763 758 to_old_git = False
764 759 if _ver < StrictVersion(req_ver):
765 760 to_old_git = True
766 761
767 762 if 'git' in BACKENDS:
768 763 log.debug('GIT version detected: %s' % stdout)
769 764 if stderr:
770 765 log.warning('Unable to detect git version, org error was: %r' % stderr)
771 766 elif to_old_git:
772 767 log.warning('RhodeCode detected git version %s, which is too old '
773 768 'for the system to function properly. Make sure '
774 769 'its version is at least %s' % (ver, req_ver))
775 770 return _ver
776 771
777 772
778 773 @decorator.decorator
779 774 def jsonify(func, *args, **kwargs):
780 775 """Action decorator that formats output for JSON
781 776
782 777 Given a function that will return content, this decorator will turn
783 778 the result into JSON, with a content-type of 'application/json' and
784 779 output it.
785 780
786 781 """
787 782 from pylons.decorators.util import get_pylons
788 783 from rhodecode.lib.compat import json
789 784 pylons = get_pylons(args)
790 785 pylons.response.headers['Content-Type'] = 'application/json; charset=utf-8'
791 786 data = func(*args, **kwargs)
792 787 if isinstance(data, (list, tuple)):
793 788 msg = "JSON responses with Array envelopes are susceptible to " \
794 789 "cross-site data leak attacks, see " \
795 790 "http://wiki.pylonshq.com/display/pylonsfaq/Warnings"
796 791 warnings.warn(msg, Warning, 2)
797 792 log.warning(msg)
798 793 log.debug("Returning JSON wrapped action output")
799 794 return json.dumps(data, encoding='utf-8')
@@ -1,1307 +1,1310 b''
1 1 from __future__ import with_statement
2 2 import random
3 3 import mock
4 4
5 5 from rhodecode.tests import *
6 6 from rhodecode.tests.fixture import Fixture
7 7 from rhodecode.lib.compat import json
8 8 from rhodecode.lib.auth import AuthUser
9 9 from rhodecode.model.user import UserModel
10 10 from rhodecode.model.users_group import UserGroupModel
11 11 from rhodecode.model.repo import RepoModel
12 12 from rhodecode.model.meta import Session
13 13 from rhodecode.model.scm import ScmModel
14 14 from rhodecode.model.db import Repository
15 15
16 16
17 17 API_URL = '/_admin/api'
18 18 TEST_USER_GROUP = 'test_users_group'
19 19
20 20 fixture = Fixture()
21 21
22 22
23 23 def _build_data(apikey, method, **kw):
24 24 """
25 25 Builds API data with given random ID
26 26
27 27 :param random_id:
28 28 :type random_id:
29 29 """
30 30 random_id = random.randrange(1, 9999)
31 31 return random_id, json.dumps({
32 32 "id": random_id,
33 33 "api_key": apikey,
34 34 "method": method,
35 35 "args": kw
36 36 })
37 37
38 38 jsonify = lambda obj: json.loads(json.dumps(obj))
39 39
40 40
41 41 def crash(*args, **kwargs):
42 42 raise Exception('Total Crash !')
43 43
44 44
45 45 def api_call(test_obj, params):
46 46 response = test_obj.app.post(API_URL, content_type='application/json',
47 47 params=params)
48 48 return response
49 49
50 50
51 51 ## helpers
52 52 def make_users_group(name=TEST_USER_GROUP):
53 53 gr = fixture.create_user_group(name, cur_user=TEST_USER_ADMIN_LOGIN)
54 54 UserGroupModel().add_user_to_group(users_group=gr,
55 55 user=TEST_USER_ADMIN_LOGIN)
56 56 Session().commit()
57 57 return gr
58 58
59 59
60 60 def destroy_users_group(name=TEST_USER_GROUP):
61 61 UserGroupModel().delete(users_group=name, force=True)
62 62 Session().commit()
63 63
64 64
65 65 class BaseTestApi(object):
66 66 REPO = None
67 67 REPO_TYPE = None
68 68
69 69 @classmethod
70 70 def setUpClass(self):
71 71 self.usr = UserModel().get_by_username(TEST_USER_ADMIN_LOGIN)
72 72 self.apikey = self.usr.api_key
73 73 self.test_user = UserModel().create_or_update(
74 74 username='test-api',
75 75 password='test',
76 76 email='test@api.rhodecode.org',
77 77 firstname='first',
78 78 lastname='last'
79 79 )
80 80 Session().commit()
81 81 self.TEST_USER_LOGIN = self.test_user.username
82 82 self.apikey_regular = self.test_user.api_key
83 83
84 84 @classmethod
85 85 def teardownClass(self):
86 86 pass
87 87
88 88 def setUp(self):
89 89 self.maxDiff = None
90 90 make_users_group()
91 91
92 92 def tearDown(self):
93 93 destroy_users_group()
94 94
95 95 def _compare_ok(self, id_, expected, given):
96 96 expected = jsonify({
97 97 'id': id_,
98 98 'error': None,
99 99 'result': expected
100 100 })
101 101 given = json.loads(given)
102 102 self.assertEqual(expected, given)
103 103
104 104 def _compare_error(self, id_, expected, given):
105 105 expected = jsonify({
106 106 'id': id_,
107 107 'error': expected,
108 108 'result': None
109 109 })
110 110 given = json.loads(given)
111 111 self.assertEqual(expected, given)
112 112
113 113 # def test_Optional(self):
114 114 # from rhodecode.controllers.api.api import Optional
115 115 # option1 = Optional(None)
116 116 # self.assertEqual('<Optional:%s>' % None, repr(option1))
117 117 #
118 118 # self.assertEqual(1, Optional.extract(Optional(1)))
119 119 # self.assertEqual('trololo', Optional.extract('trololo'))
120 120
121 121 def test_api_wrong_key(self):
122 122 id_, params = _build_data('trololo', 'get_user')
123 123 response = api_call(self, params)
124 124
125 125 expected = 'Invalid API KEY'
126 126 self._compare_error(id_, expected, given=response.body)
127 127
128 128 def test_api_missing_non_optional_param(self):
129 129 id_, params = _build_data(self.apikey, 'get_repo')
130 130 response = api_call(self, params)
131 131
132 132 expected = 'Missing non optional `repoid` arg in JSON DATA'
133 133 self._compare_error(id_, expected, given=response.body)
134 134
135 135 def test_api_missing_non_optional_param_args_null(self):
136 136 id_, params = _build_data(self.apikey, 'get_repo')
137 137 params = params.replace('"args": {}', '"args": null')
138 138 response = api_call(self, params)
139 139
140 140 expected = 'Missing non optional `repoid` arg in JSON DATA'
141 141 self._compare_error(id_, expected, given=response.body)
142 142
143 143 def test_api_missing_non_optional_param_args_bad(self):
144 144 id_, params = _build_data(self.apikey, 'get_repo')
145 145 params = params.replace('"args": {}', '"args": 1')
146 146 response = api_call(self, params)
147 147
148 148 expected = 'Missing non optional `repoid` arg in JSON DATA'
149 149 self._compare_error(id_, expected, given=response.body)
150 150
151 151 def test_api_args_is_null(self):
152 152 id_, params = _build_data(self.apikey, 'get_users',)
153 153 params = params.replace('"args": {}', '"args": null')
154 154 response = api_call(self, params)
155 155 self.assertEqual(response.status, '200 OK')
156 156
157 157 def test_api_args_is_bad(self):
158 158 id_, params = _build_data(self.apikey, 'get_users',)
159 159 params = params.replace('"args": {}', '"args": 1')
160 160 response = api_call(self, params)
161 161 self.assertEqual(response.status, '200 OK')
162 162
163 163 def test_api_get_users(self):
164 164 id_, params = _build_data(self.apikey, 'get_users',)
165 165 response = api_call(self, params)
166 166 ret_all = []
167 167 for usr in UserModel().get_all():
168 168 ret = usr.get_api_data()
169 169 ret_all.append(jsonify(ret))
170 170 expected = ret_all
171 171 self._compare_ok(id_, expected, given=response.body)
172 172
173 173 def test_api_get_user(self):
174 174 id_, params = _build_data(self.apikey, 'get_user',
175 175 userid=TEST_USER_ADMIN_LOGIN)
176 176 response = api_call(self, params)
177 177
178 178 usr = UserModel().get_by_username(TEST_USER_ADMIN_LOGIN)
179 179 ret = usr.get_api_data()
180 180 ret['permissions'] = AuthUser(usr.user_id).permissions
181 181
182 182 expected = ret
183 183 self._compare_ok(id_, expected, given=response.body)
184 184
185 185 def test_api_get_user_that_does_not_exist(self):
186 186 id_, params = _build_data(self.apikey, 'get_user',
187 187 userid='trololo')
188 188 response = api_call(self, params)
189 189
190 190 expected = "user `%s` does not exist" % 'trololo'
191 191 self._compare_error(id_, expected, given=response.body)
192 192
193 193 def test_api_get_user_without_giving_userid(self):
194 194 id_, params = _build_data(self.apikey, 'get_user')
195 195 response = api_call(self, params)
196 196
197 197 usr = UserModel().get_by_username(TEST_USER_ADMIN_LOGIN)
198 198 ret = usr.get_api_data()
199 199 ret['permissions'] = AuthUser(usr.user_id).permissions
200 200
201 201 expected = ret
202 202 self._compare_ok(id_, expected, given=response.body)
203 203
204 204 def test_api_get_user_without_giving_userid_non_admin(self):
205 205 id_, params = _build_data(self.apikey_regular, 'get_user')
206 206 response = api_call(self, params)
207 207
208 208 usr = UserModel().get_by_username(self.TEST_USER_LOGIN)
209 209 ret = usr.get_api_data()
210 210 ret['permissions'] = AuthUser(usr.user_id).permissions
211 211
212 212 expected = ret
213 213 self._compare_ok(id_, expected, given=response.body)
214 214
215 215 def test_api_get_user_with_giving_userid_non_admin(self):
216 216 id_, params = _build_data(self.apikey_regular, 'get_user',
217 217 userid=self.TEST_USER_LOGIN)
218 218 response = api_call(self, params)
219 219
220 220 expected = 'userid is not the same as your user'
221 221 self._compare_error(id_, expected, given=response.body)
222 222
223 223 def test_api_pull(self):
224 224 #TODO: issues with rhodecode_extras here.. not sure why !
225 225 pass
226 226
227 227 # repo_name = 'test_pull'
228 228 # r = fixture.create_repo(repo_name, repo_type=self.REPO_TYPE)
229 229 # r.clone_uri = TEST_self.REPO
230 230 # Session.add(r)
231 231 # Session.commit()
232 232 #
233 233 # id_, params = _build_data(self.apikey, 'pull',
234 234 # repoid=repo_name,)
235 235 # response = self.app.post(API_URL, content_type='application/json',
236 236 # params=params)
237 237 #
238 238 # expected = 'Pulled from `%s`' % repo_name
239 239 # self._compare_ok(id_, expected, given=response.body)
240 240 #
241 241 # fixture.destroy_repo(repo_name)
242 242
243 243 def test_api_pull_error(self):
244 244 id_, params = _build_data(self.apikey, 'pull',
245 245 repoid=self.REPO,)
246 246 response = api_call(self, params)
247 247
248 248 expected = 'Unable to pull changes from `%s`' % self.REPO
249 249 self._compare_error(id_, expected, given=response.body)
250 250
251 251 def test_api_rescan_repos(self):
252 252 id_, params = _build_data(self.apikey, 'rescan_repos')
253 253 response = api_call(self, params)
254 254
255 255 expected = {'added': [], 'removed': []}
256 256 self._compare_ok(id_, expected, given=response.body)
257 257
258 258 @mock.patch.object(ScmModel, 'repo_scan', crash)
259 259 def test_api_rescann_error(self):
260 260 id_, params = _build_data(self.apikey, 'rescan_repos',)
261 261 response = api_call(self, params)
262 262
263 263 expected = 'Error occurred during rescan repositories action'
264 264 self._compare_error(id_, expected, given=response.body)
265 265
266 266 def test_api_invalidate_cache(self):
267 repo = RepoModel().get_by_repo_name(self.REPO)
268 repo.scm_instance_cached() # seed cache
269
267 270 id_, params = _build_data(self.apikey, 'invalidate_cache',
268 271 repoid=self.REPO)
269 272 response = api_call(self, params)
270 273
271 274 expected = ("Caches of repository `%s` was invalidated" % (self.REPO))
272 275 self._compare_ok(id_, expected, given=response.body)
273 276
274 277 @mock.patch.object(ScmModel, 'mark_for_invalidation', crash)
275 278 def test_api_invalidate_cache_error(self):
276 279 id_, params = _build_data(self.apikey, 'invalidate_cache',
277 280 repoid=self.REPO)
278 281 response = api_call(self, params)
279 282
280 283 expected = 'Error occurred during cache invalidation action'
281 284 self._compare_error(id_, expected, given=response.body)
282 285
283 286 def test_api_lock_repo_lock_aquire(self):
284 287 id_, params = _build_data(self.apikey, 'lock',
285 288 userid=TEST_USER_ADMIN_LOGIN,
286 289 repoid=self.REPO,
287 290 locked=True)
288 291 response = api_call(self, params)
289 292 expected = ('User `%s` set lock state for repo `%s` to `%s`'
290 293 % (TEST_USER_ADMIN_LOGIN, self.REPO, True))
291 294 self._compare_ok(id_, expected, given=response.body)
292 295
293 296 def test_api_lock_repo_lock_aquire_by_non_admin(self):
294 297 repo_name = 'api_delete_me'
295 298 fixture.create_repo(repo_name, repo_type=self.REPO_TYPE,
296 299 cur_user=self.TEST_USER_LOGIN)
297 300 try:
298 301 id_, params = _build_data(self.apikey_regular, 'lock',
299 302 repoid=repo_name,
300 303 locked=True)
301 304 response = api_call(self, params)
302 305 expected = ('User `%s` set lock state for repo `%s` to `%s`'
303 306 % (self.TEST_USER_LOGIN, repo_name, True))
304 307 self._compare_ok(id_, expected, given=response.body)
305 308 finally:
306 309 fixture.destroy_repo(repo_name)
307 310
308 311 def test_api_lock_repo_lock_aquire_non_admin_with_userid(self):
309 312 repo_name = 'api_delete_me'
310 313 fixture.create_repo(repo_name, repo_type=self.REPO_TYPE,
311 314 cur_user=self.TEST_USER_LOGIN)
312 315 try:
313 316 id_, params = _build_data(self.apikey_regular, 'lock',
314 317 userid=TEST_USER_ADMIN_LOGIN,
315 318 repoid=repo_name,
316 319 locked=True)
317 320 response = api_call(self, params)
318 321 expected = 'userid is not the same as your user'
319 322 self._compare_error(id_, expected, given=response.body)
320 323 finally:
321 324 fixture.destroy_repo(repo_name)
322 325
323 326 def test_api_lock_repo_lock_aquire_non_admin_not_his_repo(self):
324 327 id_, params = _build_data(self.apikey_regular, 'lock',
325 328 repoid=self.REPO,
326 329 locked=True)
327 330 response = api_call(self, params)
328 331 expected = 'repository `%s` does not exist' % (self.REPO)
329 332 self._compare_error(id_, expected, given=response.body)
330 333
331 334 def test_api_lock_repo_lock_release(self):
332 335 id_, params = _build_data(self.apikey, 'lock',
333 336 userid=TEST_USER_ADMIN_LOGIN,
334 337 repoid=self.REPO,
335 338 locked=False)
336 339 response = api_call(self, params)
337 340 expected = ('User `%s` set lock state for repo `%s` to `%s`'
338 341 % (TEST_USER_ADMIN_LOGIN, self.REPO, False))
339 342 self._compare_ok(id_, expected, given=response.body)
340 343
341 344 def test_api_lock_repo_lock_aquire_optional_userid(self):
342 345 id_, params = _build_data(self.apikey, 'lock',
343 346 repoid=self.REPO,
344 347 locked=True)
345 348 response = api_call(self, params)
346 349 expected = ('User `%s` set lock state for repo `%s` to `%s`'
347 350 % (TEST_USER_ADMIN_LOGIN, self.REPO, True))
348 351 self._compare_ok(id_, expected, given=response.body)
349 352
350 353 def test_api_lock_repo_lock_optional_locked(self):
351 354 from rhodecode.lib.utils2 import time_to_datetime
352 355 _locked_since = json.dumps(time_to_datetime(Repository\
353 356 .get_by_repo_name(self.REPO).locked[1]))
354 357 id_, params = _build_data(self.apikey, 'lock',
355 358 repoid=self.REPO)
356 359 response = api_call(self, params)
357 360 expected = ('Repo `%s` locked by `%s`. Locked=`True`. Locked since: `%s`'
358 361 % (self.REPO, TEST_USER_ADMIN_LOGIN, _locked_since))
359 362 self._compare_ok(id_, expected, given=response.body)
360 363
361 364 @mock.patch.object(Repository, 'lock', crash)
362 365 def test_api_lock_error(self):
363 366 id_, params = _build_data(self.apikey, 'lock',
364 367 userid=TEST_USER_ADMIN_LOGIN,
365 368 repoid=self.REPO,
366 369 locked=True)
367 370 response = api_call(self, params)
368 371
369 372 expected = 'Error occurred locking repository `%s`' % self.REPO
370 373 self._compare_error(id_, expected, given=response.body)
371 374
372 375 def test_api_get_locks_regular_user(self):
373 376 id_, params = _build_data(self.apikey_regular, 'get_locks')
374 377 response = api_call(self, params)
375 378 expected = []
376 379 self._compare_ok(id_, expected, given=response.body)
377 380
378 381 def test_api_get_locks_with_userid_regular_user(self):
379 382 id_, params = _build_data(self.apikey_regular, 'get_locks',
380 383 userid=TEST_USER_ADMIN_LOGIN)
381 384 response = api_call(self, params)
382 385 expected = 'userid is not the same as your user'
383 386 self._compare_error(id_, expected, given=response.body)
384 387
385 388 def test_api_get_locks(self):
386 389 id_, params = _build_data(self.apikey, 'get_locks')
387 390 response = api_call(self, params)
388 391 expected = []
389 392 self._compare_ok(id_, expected, given=response.body)
390 393
391 394 def test_api_get_locks_with_userid(self):
392 395 id_, params = _build_data(self.apikey, 'get_locks',
393 396 userid=TEST_USER_REGULAR_LOGIN)
394 397 response = api_call(self, params)
395 398 expected = []
396 399 self._compare_ok(id_, expected, given=response.body)
397 400
398 401 def test_api_create_existing_user(self):
399 402 id_, params = _build_data(self.apikey, 'create_user',
400 403 username=TEST_USER_ADMIN_LOGIN,
401 404 email='test@foo.com',
402 405 password='trololo')
403 406 response = api_call(self, params)
404 407
405 408 expected = "user `%s` already exist" % TEST_USER_ADMIN_LOGIN
406 409 self._compare_error(id_, expected, given=response.body)
407 410
408 411 def test_api_create_user_with_existing_email(self):
409 412 id_, params = _build_data(self.apikey, 'create_user',
410 413 username=TEST_USER_ADMIN_LOGIN + 'new',
411 414 email=TEST_USER_REGULAR_EMAIL,
412 415 password='trololo')
413 416 response = api_call(self, params)
414 417
415 418 expected = "email `%s` already exist" % TEST_USER_REGULAR_EMAIL
416 419 self._compare_error(id_, expected, given=response.body)
417 420
418 421 def test_api_create_user(self):
419 422 username = 'test_new_api_user'
420 423 email = username + "@foo.com"
421 424
422 425 id_, params = _build_data(self.apikey, 'create_user',
423 426 username=username,
424 427 email=email,
425 428 password='trololo')
426 429 response = api_call(self, params)
427 430
428 431 usr = UserModel().get_by_username(username)
429 432 ret = dict(
430 433 msg='created new user `%s`' % username,
431 434 user=jsonify(usr.get_api_data())
432 435 )
433 436
434 437 expected = ret
435 438 self._compare_ok(id_, expected, given=response.body)
436 439
437 440 UserModel().delete(usr.user_id)
438 441 Session().commit()
439 442
440 443 @mock.patch.object(UserModel, 'create_or_update', crash)
441 444 def test_api_create_user_when_exception_happened(self):
442 445
443 446 username = 'test_new_api_user'
444 447 email = username + "@foo.com"
445 448
446 449 id_, params = _build_data(self.apikey, 'create_user',
447 450 username=username,
448 451 email=email,
449 452 password='trololo')
450 453 response = api_call(self, params)
451 454 expected = 'failed to create user `%s`' % username
452 455 self._compare_error(id_, expected, given=response.body)
453 456
454 457 def test_api_delete_user(self):
455 458 usr = UserModel().create_or_update(username=u'test_user',
456 459 password=u'qweqwe',
457 460 email=u'u232@rhodecode.org',
458 461 firstname=u'u1', lastname=u'u1')
459 462 Session().commit()
460 463 username = usr.username
461 464 email = usr.email
462 465 usr_id = usr.user_id
463 466 ## DELETE THIS USER NOW
464 467
465 468 id_, params = _build_data(self.apikey, 'delete_user',
466 469 userid=username,)
467 470 response = api_call(self, params)
468 471
469 472 ret = {'msg': 'deleted user ID:%s %s' % (usr_id, username),
470 473 'user': None}
471 474 expected = ret
472 475 self._compare_ok(id_, expected, given=response.body)
473 476
474 477 @mock.patch.object(UserModel, 'delete', crash)
475 478 def test_api_delete_user_when_exception_happened(self):
476 479 usr = UserModel().create_or_update(username=u'test_user',
477 480 password=u'qweqwe',
478 481 email=u'u232@rhodecode.org',
479 482 firstname=u'u1', lastname=u'u1')
480 483 Session().commit()
481 484 username = usr.username
482 485
483 486 id_, params = _build_data(self.apikey, 'delete_user',
484 487 userid=username,)
485 488 response = api_call(self, params)
486 489 ret = 'failed to delete ID:%s %s' % (usr.user_id,
487 490 usr.username)
488 491 expected = ret
489 492 self._compare_error(id_, expected, given=response.body)
490 493
491 494 @parameterized.expand([('firstname', 'new_username'),
492 495 ('lastname', 'new_username'),
493 496 ('email', 'new_username'),
494 497 ('admin', True),
495 498 ('admin', False),
496 499 ('ldap_dn', 'test'),
497 500 ('ldap_dn', None),
498 501 ('active', False),
499 502 ('active', True),
500 503 ('password', 'newpass')
501 504 ])
502 505 def test_api_update_user(self, name, expected):
503 506 usr = UserModel().get_by_username(self.TEST_USER_LOGIN)
504 507 kw = {name: expected,
505 508 'userid': usr.user_id}
506 509 id_, params = _build_data(self.apikey, 'update_user', **kw)
507 510 response = api_call(self, params)
508 511
509 512 ret = {
510 513 'msg': 'updated user ID:%s %s' % (usr.user_id, self.TEST_USER_LOGIN),
511 514 'user': jsonify(UserModel()\
512 515 .get_by_username(self.TEST_USER_LOGIN)\
513 516 .get_api_data())
514 517 }
515 518
516 519 expected = ret
517 520 self._compare_ok(id_, expected, given=response.body)
518 521
519 522 def test_api_update_user_no_changed_params(self):
520 523 usr = UserModel().get_by_username(TEST_USER_ADMIN_LOGIN)
521 524 ret = jsonify(usr.get_api_data())
522 525 id_, params = _build_data(self.apikey, 'update_user',
523 526 userid=TEST_USER_ADMIN_LOGIN)
524 527
525 528 response = api_call(self, params)
526 529 ret = {
527 530 'msg': 'updated user ID:%s %s' % (usr.user_id, TEST_USER_ADMIN_LOGIN),
528 531 'user': ret
529 532 }
530 533 expected = ret
531 534 self._compare_ok(id_, expected, given=response.body)
532 535
533 536 def test_api_update_user_by_user_id(self):
534 537 usr = UserModel().get_by_username(TEST_USER_ADMIN_LOGIN)
535 538 ret = jsonify(usr.get_api_data())
536 539 id_, params = _build_data(self.apikey, 'update_user',
537 540 userid=usr.user_id)
538 541
539 542 response = api_call(self, params)
540 543 ret = {
541 544 'msg': 'updated user ID:%s %s' % (usr.user_id, TEST_USER_ADMIN_LOGIN),
542 545 'user': ret
543 546 }
544 547 expected = ret
545 548 self._compare_ok(id_, expected, given=response.body)
546 549
547 550 @mock.patch.object(UserModel, 'update_user', crash)
548 551 def test_api_update_user_when_exception_happens(self):
549 552 usr = UserModel().get_by_username(TEST_USER_ADMIN_LOGIN)
550 553 ret = jsonify(usr.get_api_data())
551 554 id_, params = _build_data(self.apikey, 'update_user',
552 555 userid=usr.user_id)
553 556
554 557 response = api_call(self, params)
555 558 ret = 'failed to update user `%s`' % usr.user_id
556 559
557 560 expected = ret
558 561 self._compare_error(id_, expected, given=response.body)
559 562
560 563 def test_api_get_repo(self):
561 564 new_group = 'some_new_group'
562 565 make_users_group(new_group)
563 566 RepoModel().grant_users_group_permission(repo=self.REPO,
564 567 group_name=new_group,
565 568 perm='repository.read')
566 569 Session().commit()
567 570 id_, params = _build_data(self.apikey, 'get_repo',
568 571 repoid=self.REPO)
569 572 response = api_call(self, params)
570 573
571 574 repo = RepoModel().get_by_repo_name(self.REPO)
572 575 ret = repo.get_api_data()
573 576
574 577 members = []
575 578 followers = []
576 579 for user in repo.repo_to_perm:
577 580 perm = user.permission.permission_name
578 581 user = user.user
579 582 user_data = user.get_api_data()
580 583 user_data['type'] = "user"
581 584 user_data['permission'] = perm
582 585 members.append(user_data)
583 586
584 587 for users_group in repo.users_group_to_perm:
585 588 perm = users_group.permission.permission_name
586 589 users_group = users_group.users_group
587 590 users_group_data = users_group.get_api_data()
588 591 users_group_data['type'] = "users_group"
589 592 users_group_data['permission'] = perm
590 593 members.append(users_group_data)
591 594
592 595 for user in repo.followers:
593 596 followers.append(user.user.get_api_data())
594 597
595 598 ret['members'] = members
596 599 ret['followers'] = followers
597 600
598 601 expected = ret
599 602 self._compare_ok(id_, expected, given=response.body)
600 603 destroy_users_group(new_group)
601 604
602 605 def test_api_get_repo_by_non_admin(self):
603 606 id_, params = _build_data(self.apikey, 'get_repo',
604 607 repoid=self.REPO)
605 608 response = api_call(self, params)
606 609
607 610 repo = RepoModel().get_by_repo_name(self.REPO)
608 611 ret = repo.get_api_data()
609 612
610 613 members = []
611 614 followers = []
612 615 for user in repo.repo_to_perm:
613 616 perm = user.permission.permission_name
614 617 user = user.user
615 618 user_data = user.get_api_data()
616 619 user_data['type'] = "user"
617 620 user_data['permission'] = perm
618 621 members.append(user_data)
619 622
620 623 for users_group in repo.users_group_to_perm:
621 624 perm = users_group.permission.permission_name
622 625 users_group = users_group.users_group
623 626 users_group_data = users_group.get_api_data()
624 627 users_group_data['type'] = "users_group"
625 628 users_group_data['permission'] = perm
626 629 members.append(users_group_data)
627 630
628 631 for user in repo.followers:
629 632 followers.append(user.user.get_api_data())
630 633
631 634 ret['members'] = members
632 635 ret['followers'] = followers
633 636
634 637 expected = ret
635 638 self._compare_ok(id_, expected, given=response.body)
636 639
637 640 def test_api_get_repo_by_non_admin_no_permission_to_repo(self):
638 641 RepoModel().grant_user_permission(repo=self.REPO,
639 642 user=self.TEST_USER_LOGIN,
640 643 perm='repository.none')
641 644
642 645 id_, params = _build_data(self.apikey_regular, 'get_repo',
643 646 repoid=self.REPO)
644 647 response = api_call(self, params)
645 648
646 649 expected = 'repository `%s` does not exist' % (self.REPO)
647 650 self._compare_error(id_, expected, given=response.body)
648 651
649 652 def test_api_get_repo_that_doesn_not_exist(self):
650 653 id_, params = _build_data(self.apikey, 'get_repo',
651 654 repoid='no-such-repo')
652 655 response = api_call(self, params)
653 656
654 657 ret = 'repository `%s` does not exist' % 'no-such-repo'
655 658 expected = ret
656 659 self._compare_error(id_, expected, given=response.body)
657 660
658 661 def test_api_get_repos(self):
659 662 id_, params = _build_data(self.apikey, 'get_repos')
660 663 response = api_call(self, params)
661 664
662 665 result = []
663 666 for repo in RepoModel().get_all():
664 667 result.append(repo.get_api_data())
665 668 ret = jsonify(result)
666 669
667 670 expected = ret
668 671 self._compare_ok(id_, expected, given=response.body)
669 672
670 673 def test_api_get_repos_non_admin(self):
671 674 id_, params = _build_data(self.apikey_regular, 'get_repos')
672 675 response = api_call(self, params)
673 676
674 677 result = []
675 678 for repo in RepoModel().get_all_user_repos(self.TEST_USER_LOGIN):
676 679 result.append(repo.get_api_data())
677 680 ret = jsonify(result)
678 681
679 682 expected = ret
680 683 self._compare_ok(id_, expected, given=response.body)
681 684
682 685 @parameterized.expand([('all', 'all'),
683 686 ('dirs', 'dirs'),
684 687 ('files', 'files'), ])
685 688 def test_api_get_repo_nodes(self, name, ret_type):
686 689 rev = 'tip'
687 690 path = '/'
688 691 id_, params = _build_data(self.apikey, 'get_repo_nodes',
689 692 repoid=self.REPO, revision=rev,
690 693 root_path=path,
691 694 ret_type=ret_type)
692 695 response = api_call(self, params)
693 696
694 697 # we don't the actual return types here since it's tested somewhere
695 698 # else
696 699 expected = json.loads(response.body)['result']
697 700 self._compare_ok(id_, expected, given=response.body)
698 701
699 702 def test_api_get_repo_nodes_bad_revisions(self):
700 703 rev = 'i-dont-exist'
701 704 path = '/'
702 705 id_, params = _build_data(self.apikey, 'get_repo_nodes',
703 706 repoid=self.REPO, revision=rev,
704 707 root_path=path,)
705 708 response = api_call(self, params)
706 709
707 710 expected = 'failed to get repo: `%s` nodes' % self.REPO
708 711 self._compare_error(id_, expected, given=response.body)
709 712
710 713 def test_api_get_repo_nodes_bad_path(self):
711 714 rev = 'tip'
712 715 path = '/idontexits'
713 716 id_, params = _build_data(self.apikey, 'get_repo_nodes',
714 717 repoid=self.REPO, revision=rev,
715 718 root_path=path,)
716 719 response = api_call(self, params)
717 720
718 721 expected = 'failed to get repo: `%s` nodes' % self.REPO
719 722 self._compare_error(id_, expected, given=response.body)
720 723
721 724 def test_api_get_repo_nodes_bad_ret_type(self):
722 725 rev = 'tip'
723 726 path = '/'
724 727 ret_type = 'error'
725 728 id_, params = _build_data(self.apikey, 'get_repo_nodes',
726 729 repoid=self.REPO, revision=rev,
727 730 root_path=path,
728 731 ret_type=ret_type)
729 732 response = api_call(self, params)
730 733
731 734 expected = 'ret_type must be one of %s' % (['files', 'dirs', 'all'])
732 735 self._compare_error(id_, expected, given=response.body)
733 736
734 737 def test_api_create_repo(self):
735 738 repo_name = 'api-repo'
736 739 id_, params = _build_data(self.apikey, 'create_repo',
737 740 repo_name=repo_name,
738 741 owner=TEST_USER_ADMIN_LOGIN,
739 742 repo_type='hg',
740 743 )
741 744 response = api_call(self, params)
742 745
743 746 repo = RepoModel().get_by_repo_name(repo_name)
744 747 ret = {
745 748 'msg': 'Created new repository `%s`' % repo_name,
746 749 'repo': jsonify(repo.get_api_data())
747 750 }
748 751 expected = ret
749 752 self._compare_ok(id_, expected, given=response.body)
750 753 fixture.destroy_repo(repo_name)
751 754
752 755 def test_api_create_repo_unknown_owner(self):
753 756 repo_name = 'api-repo'
754 757 owner = 'i-dont-exist'
755 758 id_, params = _build_data(self.apikey, 'create_repo',
756 759 repo_name=repo_name,
757 760 owner=owner,
758 761 repo_type='hg',
759 762 )
760 763 response = api_call(self, params)
761 764 expected = 'user `%s` does not exist' % owner
762 765 self._compare_error(id_, expected, given=response.body)
763 766
764 767 def test_api_create_repo_dont_specify_owner(self):
765 768 repo_name = 'api-repo'
766 769 owner = 'i-dont-exist'
767 770 id_, params = _build_data(self.apikey, 'create_repo',
768 771 repo_name=repo_name,
769 772 repo_type='hg',
770 773 )
771 774 response = api_call(self, params)
772 775
773 776 repo = RepoModel().get_by_repo_name(repo_name)
774 777 ret = {
775 778 'msg': 'Created new repository `%s`' % repo_name,
776 779 'repo': jsonify(repo.get_api_data())
777 780 }
778 781 expected = ret
779 782 self._compare_ok(id_, expected, given=response.body)
780 783 fixture.destroy_repo(repo_name)
781 784
782 785 def test_api_create_repo_by_non_admin(self):
783 786 repo_name = 'api-repo'
784 787 owner = 'i-dont-exist'
785 788 id_, params = _build_data(self.apikey_regular, 'create_repo',
786 789 repo_name=repo_name,
787 790 repo_type='hg',
788 791 )
789 792 response = api_call(self, params)
790 793
791 794 repo = RepoModel().get_by_repo_name(repo_name)
792 795 ret = {
793 796 'msg': 'Created new repository `%s`' % repo_name,
794 797 'repo': jsonify(repo.get_api_data())
795 798 }
796 799 expected = ret
797 800 self._compare_ok(id_, expected, given=response.body)
798 801 fixture.destroy_repo(repo_name)
799 802
800 803 def test_api_create_repo_by_non_admin_specify_owner(self):
801 804 repo_name = 'api-repo'
802 805 owner = 'i-dont-exist'
803 806 id_, params = _build_data(self.apikey_regular, 'create_repo',
804 807 repo_name=repo_name,
805 808 repo_type='hg',
806 809 owner=owner
807 810 )
808 811 response = api_call(self, params)
809 812
810 813 expected = 'Only RhodeCode admin can specify `owner` param'
811 814 self._compare_error(id_, expected, given=response.body)
812 815 fixture.destroy_repo(repo_name)
813 816
814 817 def test_api_create_repo_exists(self):
815 818 repo_name = self.REPO
816 819 id_, params = _build_data(self.apikey, 'create_repo',
817 820 repo_name=repo_name,
818 821 owner=TEST_USER_ADMIN_LOGIN,
819 822 repo_type='hg',
820 823 )
821 824 response = api_call(self, params)
822 825 expected = "repo `%s` already exist" % repo_name
823 826 self._compare_error(id_, expected, given=response.body)
824 827
825 828 @mock.patch.object(RepoModel, 'create_repo', crash)
826 829 def test_api_create_repo_exception_occurred(self):
827 830 repo_name = 'api-repo'
828 831 id_, params = _build_data(self.apikey, 'create_repo',
829 832 repo_name=repo_name,
830 833 owner=TEST_USER_ADMIN_LOGIN,
831 834 repo_type='hg',
832 835 )
833 836 response = api_call(self, params)
834 837 expected = 'failed to create repository `%s`' % repo_name
835 838 self._compare_error(id_, expected, given=response.body)
836 839
837 840 def test_api_delete_repo(self):
838 841 repo_name = 'api_delete_me'
839 842 fixture.create_repo(repo_name, repo_type=self.REPO_TYPE)
840 843
841 844 id_, params = _build_data(self.apikey, 'delete_repo',
842 845 repoid=repo_name,)
843 846 response = api_call(self, params)
844 847
845 848 ret = {
846 849 'msg': 'Deleted repository `%s`' % repo_name,
847 850 'success': True
848 851 }
849 852 expected = ret
850 853 self._compare_ok(id_, expected, given=response.body)
851 854
852 855 def test_api_delete_repo_by_non_admin(self):
853 856 repo_name = 'api_delete_me'
854 857 fixture.create_repo(repo_name, repo_type=self.REPO_TYPE,
855 858 cur_user=self.TEST_USER_LOGIN)
856 859 try:
857 860 id_, params = _build_data(self.apikey_regular, 'delete_repo',
858 861 repoid=repo_name,)
859 862 response = api_call(self, params)
860 863
861 864 ret = {
862 865 'msg': 'Deleted repository `%s`' % repo_name,
863 866 'success': True
864 867 }
865 868 expected = ret
866 869 self._compare_ok(id_, expected, given=response.body)
867 870 finally:
868 871 fixture.destroy_repo(repo_name)
869 872
870 873 def test_api_delete_repo_by_non_admin_no_permission(self):
871 874 repo_name = 'api_delete_me'
872 875 fixture.create_repo(repo_name, repo_type=self.REPO_TYPE)
873 876 try:
874 877 id_, params = _build_data(self.apikey_regular, 'delete_repo',
875 878 repoid=repo_name,)
876 879 response = api_call(self, params)
877 880 expected = 'repository `%s` does not exist' % (repo_name)
878 881 self._compare_error(id_, expected, given=response.body)
879 882 finally:
880 883 fixture.destroy_repo(repo_name)
881 884
882 885 def test_api_delete_repo_exception_occurred(self):
883 886 repo_name = 'api_delete_me'
884 887 fixture.create_repo(repo_name, repo_type=self.REPO_TYPE)
885 888 try:
886 889 with mock.patch.object(RepoModel, 'delete', crash):
887 890 id_, params = _build_data(self.apikey, 'delete_repo',
888 891 repoid=repo_name,)
889 892 response = api_call(self, params)
890 893
891 894 expected = 'failed to delete repository `%s`' % repo_name
892 895 self._compare_error(id_, expected, given=response.body)
893 896 finally:
894 897 fixture.destroy_repo(repo_name)
895 898
896 899 def test_api_fork_repo(self):
897 900 fork_name = 'api-repo-fork'
898 901 id_, params = _build_data(self.apikey, 'fork_repo',
899 902 repoid=self.REPO,
900 903 fork_name=fork_name,
901 904 owner=TEST_USER_ADMIN_LOGIN,
902 905 )
903 906 response = api_call(self, params)
904 907
905 908 ret = {
906 909 'msg': 'Created fork of `%s` as `%s`' % (self.REPO,
907 910 fork_name),
908 911 'success': True
909 912 }
910 913 expected = ret
911 914 self._compare_ok(id_, expected, given=response.body)
912 915 fixture.destroy_repo(fork_name)
913 916
914 917 def test_api_fork_repo_non_admin(self):
915 918 fork_name = 'api-repo-fork'
916 919 id_, params = _build_data(self.apikey_regular, 'fork_repo',
917 920 repoid=self.REPO,
918 921 fork_name=fork_name,
919 922 )
920 923 response = api_call(self, params)
921 924
922 925 ret = {
923 926 'msg': 'Created fork of `%s` as `%s`' % (self.REPO,
924 927 fork_name),
925 928 'success': True
926 929 }
927 930 expected = ret
928 931 self._compare_ok(id_, expected, given=response.body)
929 932 fixture.destroy_repo(fork_name)
930 933
931 934 def test_api_fork_repo_non_admin_specify_owner(self):
932 935 fork_name = 'api-repo-fork'
933 936 id_, params = _build_data(self.apikey_regular, 'fork_repo',
934 937 repoid=self.REPO,
935 938 fork_name=fork_name,
936 939 owner=TEST_USER_ADMIN_LOGIN,
937 940 )
938 941 response = api_call(self, params)
939 942 expected = 'Only RhodeCode admin can specify `owner` param'
940 943 self._compare_error(id_, expected, given=response.body)
941 944 fixture.destroy_repo(fork_name)
942 945
943 946 def test_api_fork_repo_non_admin_no_permission_to_fork(self):
944 947 RepoModel().grant_user_permission(repo=self.REPO,
945 948 user=self.TEST_USER_LOGIN,
946 949 perm='repository.none')
947 950 fork_name = 'api-repo-fork'
948 951 id_, params = _build_data(self.apikey_regular, 'fork_repo',
949 952 repoid=self.REPO,
950 953 fork_name=fork_name,
951 954 )
952 955 response = api_call(self, params)
953 956 expected = 'repository `%s` does not exist' % (self.REPO)
954 957 self._compare_error(id_, expected, given=response.body)
955 958 fixture.destroy_repo(fork_name)
956 959
957 960 def test_api_fork_repo_unknown_owner(self):
958 961 fork_name = 'api-repo-fork'
959 962 owner = 'i-dont-exist'
960 963 id_, params = _build_data(self.apikey, 'fork_repo',
961 964 repoid=self.REPO,
962 965 fork_name=fork_name,
963 966 owner=owner,
964 967 )
965 968 response = api_call(self, params)
966 969 expected = 'user `%s` does not exist' % owner
967 970 self._compare_error(id_, expected, given=response.body)
968 971
969 972 def test_api_fork_repo_fork_exists(self):
970 973 fork_name = 'api-repo-fork'
971 974 fixture.create_fork(self.REPO, fork_name)
972 975
973 976 try:
974 977 fork_name = 'api-repo-fork'
975 978
976 979 id_, params = _build_data(self.apikey, 'fork_repo',
977 980 repoid=self.REPO,
978 981 fork_name=fork_name,
979 982 owner=TEST_USER_ADMIN_LOGIN,
980 983 )
981 984 response = api_call(self, params)
982 985
983 986 expected = "fork `%s` already exist" % fork_name
984 987 self._compare_error(id_, expected, given=response.body)
985 988 finally:
986 989 fixture.destroy_repo(fork_name)
987 990
988 991 def test_api_fork_repo_repo_exists(self):
989 992 fork_name = self.REPO
990 993
991 994 id_, params = _build_data(self.apikey, 'fork_repo',
992 995 repoid=self.REPO,
993 996 fork_name=fork_name,
994 997 owner=TEST_USER_ADMIN_LOGIN,
995 998 )
996 999 response = api_call(self, params)
997 1000
998 1001 expected = "repo `%s` already exist" % fork_name
999 1002 self._compare_error(id_, expected, given=response.body)
1000 1003
1001 1004 @mock.patch.object(RepoModel, 'create_fork', crash)
1002 1005 def test_api_fork_repo_exception_occurred(self):
1003 1006 fork_name = 'api-repo-fork'
1004 1007 id_, params = _build_data(self.apikey, 'fork_repo',
1005 1008 repoid=self.REPO,
1006 1009 fork_name=fork_name,
1007 1010 owner=TEST_USER_ADMIN_LOGIN,
1008 1011 )
1009 1012 response = api_call(self, params)
1010 1013
1011 1014 expected = 'failed to fork repository `%s` as `%s`' % (self.REPO,
1012 1015 fork_name)
1013 1016 self._compare_error(id_, expected, given=response.body)
1014 1017
1015 1018 def test_api_get_users_group(self):
1016 1019 id_, params = _build_data(self.apikey, 'get_users_group',
1017 1020 usersgroupid=TEST_USER_GROUP)
1018 1021 response = api_call(self, params)
1019 1022
1020 1023 users_group = UserGroupModel().get_group(TEST_USER_GROUP)
1021 1024 members = []
1022 1025 for user in users_group.members:
1023 1026 user = user.user
1024 1027 members.append(user.get_api_data())
1025 1028
1026 1029 ret = users_group.get_api_data()
1027 1030 ret['members'] = members
1028 1031 expected = ret
1029 1032 self._compare_ok(id_, expected, given=response.body)
1030 1033
1031 1034 def test_api_get_users_groups(self):
1032 1035
1033 1036 make_users_group('test_users_group2')
1034 1037
1035 1038 id_, params = _build_data(self.apikey, 'get_users_groups',)
1036 1039 response = api_call(self, params)
1037 1040
1038 1041 expected = []
1039 1042 for gr_name in [TEST_USER_GROUP, 'test_users_group2']:
1040 1043 users_group = UserGroupModel().get_group(gr_name)
1041 1044 ret = users_group.get_api_data()
1042 1045 expected.append(ret)
1043 1046 self._compare_ok(id_, expected, given=response.body)
1044 1047
1045 1048 UserGroupModel().delete(users_group='test_users_group2')
1046 1049 Session().commit()
1047 1050
1048 1051 def test_api_create_users_group(self):
1049 1052 group_name = 'some_new_group'
1050 1053 id_, params = _build_data(self.apikey, 'create_users_group',
1051 1054 group_name=group_name)
1052 1055 response = api_call(self, params)
1053 1056
1054 1057 ret = {
1055 1058 'msg': 'created new user group `%s`' % group_name,
1056 1059 'users_group': jsonify(UserGroupModel()\
1057 1060 .get_by_name(group_name)\
1058 1061 .get_api_data())
1059 1062 }
1060 1063 expected = ret
1061 1064 self._compare_ok(id_, expected, given=response.body)
1062 1065
1063 1066 destroy_users_group(group_name)
1064 1067
1065 1068 def test_api_get_users_group_that_exist(self):
1066 1069 id_, params = _build_data(self.apikey, 'create_users_group',
1067 1070 group_name=TEST_USER_GROUP)
1068 1071 response = api_call(self, params)
1069 1072
1070 1073 expected = "user group `%s` already exist" % TEST_USER_GROUP
1071 1074 self._compare_error(id_, expected, given=response.body)
1072 1075
1073 1076 @mock.patch.object(UserGroupModel, 'create', crash)
1074 1077 def test_api_get_users_group_exception_occurred(self):
1075 1078 group_name = 'exception_happens'
1076 1079 id_, params = _build_data(self.apikey, 'create_users_group',
1077 1080 group_name=group_name)
1078 1081 response = api_call(self, params)
1079 1082
1080 1083 expected = 'failed to create group `%s`' % group_name
1081 1084 self._compare_error(id_, expected, given=response.body)
1082 1085
1083 1086 def test_api_add_user_to_users_group(self):
1084 1087 gr_name = 'test_group'
1085 1088 fixture.create_user_group(gr_name)
1086 1089 id_, params = _build_data(self.apikey, 'add_user_to_users_group',
1087 1090 usersgroupid=gr_name,
1088 1091 userid=TEST_USER_ADMIN_LOGIN)
1089 1092 response = api_call(self, params)
1090 1093
1091 1094 expected = {
1092 1095 'msg': 'added member `%s` to user group `%s`' % (
1093 1096 TEST_USER_ADMIN_LOGIN, gr_name
1094 1097 ),
1095 1098 'success': True}
1096 1099 self._compare_ok(id_, expected, given=response.body)
1097 1100
1098 1101 UserGroupModel().delete(users_group=gr_name)
1099 1102 Session().commit()
1100 1103
1101 1104 def test_api_add_user_to_users_group_that_doesnt_exist(self):
1102 1105 id_, params = _build_data(self.apikey, 'add_user_to_users_group',
1103 1106 usersgroupid='false-group',
1104 1107 userid=TEST_USER_ADMIN_LOGIN)
1105 1108 response = api_call(self, params)
1106 1109
1107 1110 expected = 'user group `%s` does not exist' % 'false-group'
1108 1111 self._compare_error(id_, expected, given=response.body)
1109 1112
1110 1113 @mock.patch.object(UserGroupModel, 'add_user_to_group', crash)
1111 1114 def test_api_add_user_to_users_group_exception_occurred(self):
1112 1115 gr_name = 'test_group'
1113 1116 fixture.create_user_group(gr_name)
1114 1117 id_, params = _build_data(self.apikey, 'add_user_to_users_group',
1115 1118 usersgroupid=gr_name,
1116 1119 userid=TEST_USER_ADMIN_LOGIN)
1117 1120 response = api_call(self, params)
1118 1121
1119 1122 expected = 'failed to add member to user group `%s`' % gr_name
1120 1123 self._compare_error(id_, expected, given=response.body)
1121 1124
1122 1125 UserGroupModel().delete(users_group=gr_name)
1123 1126 Session().commit()
1124 1127
1125 1128 def test_api_remove_user_from_users_group(self):
1126 1129 gr_name = 'test_group_3'
1127 1130 gr = fixture.create_user_group(gr_name)
1128 1131 UserGroupModel().add_user_to_group(gr, user=TEST_USER_ADMIN_LOGIN)
1129 1132 Session().commit()
1130 1133 id_, params = _build_data(self.apikey, 'remove_user_from_users_group',
1131 1134 usersgroupid=gr_name,
1132 1135 userid=TEST_USER_ADMIN_LOGIN)
1133 1136 response = api_call(self, params)
1134 1137
1135 1138 expected = {
1136 1139 'msg': 'removed member `%s` from user group `%s`' % (
1137 1140 TEST_USER_ADMIN_LOGIN, gr_name
1138 1141 ),
1139 1142 'success': True}
1140 1143 self._compare_ok(id_, expected, given=response.body)
1141 1144
1142 1145 UserGroupModel().delete(users_group=gr_name)
1143 1146 Session().commit()
1144 1147
1145 1148 @mock.patch.object(UserGroupModel, 'remove_user_from_group', crash)
1146 1149 def test_api_remove_user_from_users_group_exception_occurred(self):
1147 1150 gr_name = 'test_group_3'
1148 1151 gr = fixture.create_user_group(gr_name)
1149 1152 UserGroupModel().add_user_to_group(gr, user=TEST_USER_ADMIN_LOGIN)
1150 1153 Session().commit()
1151 1154 id_, params = _build_data(self.apikey, 'remove_user_from_users_group',
1152 1155 usersgroupid=gr_name,
1153 1156 userid=TEST_USER_ADMIN_LOGIN)
1154 1157 response = api_call(self, params)
1155 1158
1156 1159 expected = 'failed to remove member from user group `%s`' % gr_name
1157 1160 self._compare_error(id_, expected, given=response.body)
1158 1161
1159 1162 UserGroupModel().delete(users_group=gr_name)
1160 1163 Session().commit()
1161 1164
1162 1165 @parameterized.expand([('none', 'repository.none'),
1163 1166 ('read', 'repository.read'),
1164 1167 ('write', 'repository.write'),
1165 1168 ('admin', 'repository.admin')])
1166 1169 def test_api_grant_user_permission(self, name, perm):
1167 1170 id_, params = _build_data(self.apikey, 'grant_user_permission',
1168 1171 repoid=self.REPO,
1169 1172 userid=TEST_USER_ADMIN_LOGIN,
1170 1173 perm=perm)
1171 1174 response = api_call(self, params)
1172 1175
1173 1176 ret = {
1174 1177 'msg': 'Granted perm: `%s` for user: `%s` in repo: `%s`' % (
1175 1178 perm, TEST_USER_ADMIN_LOGIN, self.REPO
1176 1179 ),
1177 1180 'success': True
1178 1181 }
1179 1182 expected = ret
1180 1183 self._compare_ok(id_, expected, given=response.body)
1181 1184
1182 1185 def test_api_grant_user_permission_wrong_permission(self):
1183 1186 perm = 'haha.no.permission'
1184 1187 id_, params = _build_data(self.apikey, 'grant_user_permission',
1185 1188 repoid=self.REPO,
1186 1189 userid=TEST_USER_ADMIN_LOGIN,
1187 1190 perm=perm)
1188 1191 response = api_call(self, params)
1189 1192
1190 1193 expected = 'permission `%s` does not exist' % perm
1191 1194 self._compare_error(id_, expected, given=response.body)
1192 1195
1193 1196 @mock.patch.object(RepoModel, 'grant_user_permission', crash)
1194 1197 def test_api_grant_user_permission_exception_when_adding(self):
1195 1198 perm = 'repository.read'
1196 1199 id_, params = _build_data(self.apikey, 'grant_user_permission',
1197 1200 repoid=self.REPO,
1198 1201 userid=TEST_USER_ADMIN_LOGIN,
1199 1202 perm=perm)
1200 1203 response = api_call(self, params)
1201 1204
1202 1205 expected = 'failed to edit permission for user: `%s` in repo: `%s`' % (
1203 1206 TEST_USER_ADMIN_LOGIN, self.REPO
1204 1207 )
1205 1208 self._compare_error(id_, expected, given=response.body)
1206 1209
1207 1210 def test_api_revoke_user_permission(self):
1208 1211 id_, params = _build_data(self.apikey, 'revoke_user_permission',
1209 1212 repoid=self.REPO,
1210 1213 userid=TEST_USER_ADMIN_LOGIN,)
1211 1214 response = api_call(self, params)
1212 1215
1213 1216 expected = {
1214 1217 'msg': 'Revoked perm for user: `%s` in repo: `%s`' % (
1215 1218 TEST_USER_ADMIN_LOGIN, self.REPO
1216 1219 ),
1217 1220 'success': True
1218 1221 }
1219 1222 self._compare_ok(id_, expected, given=response.body)
1220 1223
1221 1224 @mock.patch.object(RepoModel, 'revoke_user_permission', crash)
1222 1225 def test_api_revoke_user_permission_exception_when_adding(self):
1223 1226 id_, params = _build_data(self.apikey, 'revoke_user_permission',
1224 1227 repoid=self.REPO,
1225 1228 userid=TEST_USER_ADMIN_LOGIN,)
1226 1229 response = api_call(self, params)
1227 1230
1228 1231 expected = 'failed to edit permission for user: `%s` in repo: `%s`' % (
1229 1232 TEST_USER_ADMIN_LOGIN, self.REPO
1230 1233 )
1231 1234 self._compare_error(id_, expected, given=response.body)
1232 1235
1233 1236 @parameterized.expand([('none', 'repository.none'),
1234 1237 ('read', 'repository.read'),
1235 1238 ('write', 'repository.write'),
1236 1239 ('admin', 'repository.admin')])
1237 1240 def test_api_grant_users_group_permission(self, name, perm):
1238 1241 id_, params = _build_data(self.apikey, 'grant_users_group_permission',
1239 1242 repoid=self.REPO,
1240 1243 usersgroupid=TEST_USER_GROUP,
1241 1244 perm=perm)
1242 1245 response = api_call(self, params)
1243 1246
1244 1247 ret = {
1245 1248 'msg': 'Granted perm: `%s` for user group: `%s` in repo: `%s`' % (
1246 1249 perm, TEST_USER_GROUP, self.REPO
1247 1250 ),
1248 1251 'success': True
1249 1252 }
1250 1253 expected = ret
1251 1254 self._compare_ok(id_, expected, given=response.body)
1252 1255
1253 1256 def test_api_grant_users_group_permission_wrong_permission(self):
1254 1257 perm = 'haha.no.permission'
1255 1258 id_, params = _build_data(self.apikey, 'grant_users_group_permission',
1256 1259 repoid=self.REPO,
1257 1260 usersgroupid=TEST_USER_GROUP,
1258 1261 perm=perm)
1259 1262 response = api_call(self, params)
1260 1263
1261 1264 expected = 'permission `%s` does not exist' % perm
1262 1265 self._compare_error(id_, expected, given=response.body)
1263 1266
1264 1267 @mock.patch.object(RepoModel, 'grant_users_group_permission', crash)
1265 1268 def test_api_grant_users_group_permission_exception_when_adding(self):
1266 1269 perm = 'repository.read'
1267 1270 id_, params = _build_data(self.apikey, 'grant_users_group_permission',
1268 1271 repoid=self.REPO,
1269 1272 usersgroupid=TEST_USER_GROUP,
1270 1273 perm=perm)
1271 1274 response = api_call(self, params)
1272 1275
1273 1276 expected = 'failed to edit permission for user group: `%s` in repo: `%s`' % (
1274 1277 TEST_USER_GROUP, self.REPO
1275 1278 )
1276 1279 self._compare_error(id_, expected, given=response.body)
1277 1280
1278 1281 def test_api_revoke_users_group_permission(self):
1279 1282 RepoModel().grant_users_group_permission(repo=self.REPO,
1280 1283 group_name=TEST_USER_GROUP,
1281 1284 perm='repository.read')
1282 1285 Session().commit()
1283 1286 id_, params = _build_data(self.apikey, 'revoke_users_group_permission',
1284 1287 repoid=self.REPO,
1285 1288 usersgroupid=TEST_USER_GROUP,)
1286 1289 response = api_call(self, params)
1287 1290
1288 1291 expected = {
1289 1292 'msg': 'Revoked perm for user group: `%s` in repo: `%s`' % (
1290 1293 TEST_USER_GROUP, self.REPO
1291 1294 ),
1292 1295 'success': True
1293 1296 }
1294 1297 self._compare_ok(id_, expected, given=response.body)
1295 1298
1296 1299 @mock.patch.object(RepoModel, 'revoke_users_group_permission', crash)
1297 1300 def test_api_revoke_users_group_permission_exception_when_adding(self):
1298 1301
1299 1302 id_, params = _build_data(self.apikey, 'revoke_users_group_permission',
1300 1303 repoid=self.REPO,
1301 1304 usersgroupid=TEST_USER_GROUP,)
1302 1305 response = api_call(self, params)
1303 1306
1304 1307 expected = 'failed to edit permission for user group: `%s` in repo: `%s`' % (
1305 1308 TEST_USER_GROUP, self.REPO
1306 1309 )
1307 1310 self._compare_error(id_, expected, given=response.body)
General Comments 0
You need to be logged in to leave comments. Login now