##// END OF EJS Templates
api: validate commit_id when using commit_comment API
marcink -
r1416:4543c26e default
parent child Browse files
Show More
@@ -1,58 +1,81 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2010-2017 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 import pytest
22 22
23 23 from rhodecode.model.db import ChangesetStatus
24 24 from rhodecode.api.tests.utils import (
25 25 build_data, api_call, assert_error, assert_ok)
26 26
27 27
28 28 @pytest.mark.usefixtures("testuser_api", "app")
29 29 class TestCommentCommit(object):
30 30 def test_api_comment_commit_on_empty_repo(self, backend):
31 31 repo = backend.create_repo()
32 32 id_, params = build_data(
33 33 self.apikey, 'comment_commit', repoid=repo.repo_name,
34 34 commit_id='tip', message='message', status_change=None)
35 35 response = api_call(self.app, params)
36 expected = 'failed to set comment on repository `%s`' % repo.repo_name
36 expected = 'There are no commits yet'
37 37 assert_error(id_, expected, given=response.body)
38 38
39 @pytest.mark.parametrize("commit_id, expected_err", [
40 ('abcabca', {'hg': 'Commit {commit} does not exist for {repo}',
41 'git': 'Commit {commit} does not exist for {repo}',
42 'svn': 'Commit id {commit} not understood.'}),
43 ('idontexist', {'hg': 'Commit {commit} does not exist for {repo}',
44 'git': 'Commit {commit} does not exist for {repo}',
45 'svn': 'Commit id {commit} not understood.'}),
46 ])
47 def test_api_comment_commit_wrong_hash(self, backend, commit_id, expected_err):
48 repo_name = backend.repo.repo_name
49 id_, params = build_data(
50 self.apikey, 'comment_commit', repoid=repo_name,
51 commit_id=commit_id, message='message', status_change=None)
52 response = api_call(self.app, params)
53
54 expected_err = expected_err[backend.alias]
55 expected_err = expected_err.format(
56 repo=backend.repo.scm_instance(), commit=commit_id)
57 assert_error(id_, expected_err, given=response.body)
58
39 59 @pytest.mark.parametrize("status_change, message, commit_id", [
40 60 (None, 'Hallo', 'tip'),
41 61 (ChangesetStatus.STATUS_APPROVED, 'Approved', 'tip'),
42 62 (ChangesetStatus.STATUS_REJECTED, 'Rejected', 'tip'),
43 63 ])
44 64 def test_api_comment_commit(
45 65 self, backend, status_change, message, commit_id,
46 66 no_notifications):
67
68 commit_id = backend.repo.scm_instance().get_changeset(commit_id).raw_id
69
47 70 id_, params = build_data(
48 71 self.apikey, 'comment_commit', repoid=backend.repo_name,
49 72 commit_id=commit_id, message=message, status=status_change)
50 73 response = api_call(self.app, params)
51 74 repo = backend.repo.scm_instance()
52 75 expected = {
53 76 'msg': 'Commented on commit `%s` for repository `%s`' % (
54 77 repo.get_changeset().raw_id, backend.repo_name),
55 78 'status_change': status_change,
56 79 'success': True
57 80 }
58 81 assert_ok(id_, expected, given=response.body)
@@ -1,1981 +1,1987 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2011-2017 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 import logging
22 22 import time
23 23
24 24 import rhodecode
25 25 from rhodecode.api import (
26 26 jsonrpc_method, JSONRPCError, JSONRPCForbidden, JSONRPCValidationError)
27 27 from rhodecode.api.utils import (
28 28 has_superadmin_permission, Optional, OAttr, get_repo_or_error,
29 29 get_user_group_or_error, get_user_or_error, validate_repo_permissions,
30 30 get_perm_or_error, parse_args, get_origin, build_commit_data,
31 31 validate_set_owner_permissions)
32 32 from rhodecode.lib.auth import HasPermissionAnyApi, HasUserGroupPermissionAnyApi
33 from rhodecode.lib.exceptions import StatusChangeOnClosedPullRequestError
34 33 from rhodecode.lib.utils2 import str2bool, time_to_datetime
35 34 from rhodecode.lib.ext_json import json
35 from rhodecode.lib.exceptions import StatusChangeOnClosedPullRequestError
36 36 from rhodecode.model.changeset_status import ChangesetStatusModel
37 37 from rhodecode.model.comment import CommentsModel
38 38 from rhodecode.model.db import (
39 39 Session, ChangesetStatus, RepositoryField, Repository, RepoGroup,
40 40 ChangesetComment)
41 41 from rhodecode.model.repo import RepoModel
42 42 from rhodecode.model.scm import ScmModel, RepoList
43 43 from rhodecode.model.settings import SettingsModel, VcsSettingsModel
44 44 from rhodecode.model import validation_schema
45 45 from rhodecode.model.validation_schema.schemas import repo_schema
46 46
47 47 log = logging.getLogger(__name__)
48 48
49 49
50 50 @jsonrpc_method()
51 51 def get_repo(request, apiuser, repoid, cache=Optional(True)):
52 52 """
53 53 Gets an existing repository by its name or repository_id.
54 54
55 55 The members section so the output returns users groups or users
56 56 associated with that repository.
57 57
58 58 This command can only be run using an |authtoken| with admin rights,
59 59 or users with at least read rights to the |repo|.
60 60
61 61 :param apiuser: This is filled automatically from the |authtoken|.
62 62 :type apiuser: AuthUser
63 63 :param repoid: The repository name or repository id.
64 64 :type repoid: str or int
65 65 :param cache: use the cached value for last changeset
66 66 :type: cache: Optional(bool)
67 67
68 68 Example output:
69 69
70 70 .. code-block:: bash
71 71
72 72 {
73 73 "error": null,
74 74 "id": <repo_id>,
75 75 "result": {
76 76 "clone_uri": null,
77 77 "created_on": "timestamp",
78 78 "description": "repo description",
79 79 "enable_downloads": false,
80 80 "enable_locking": false,
81 81 "enable_statistics": false,
82 82 "followers": [
83 83 {
84 84 "active": true,
85 85 "admin": false,
86 86 "api_key": "****************************************",
87 87 "api_keys": [
88 88 "****************************************"
89 89 ],
90 90 "email": "user@example.com",
91 91 "emails": [
92 92 "user@example.com"
93 93 ],
94 94 "extern_name": "rhodecode",
95 95 "extern_type": "rhodecode",
96 96 "firstname": "username",
97 97 "ip_addresses": [],
98 98 "language": null,
99 99 "last_login": "2015-09-16T17:16:35.854",
100 100 "lastname": "surname",
101 101 "user_id": <user_id>,
102 102 "username": "name"
103 103 }
104 104 ],
105 105 "fork_of": "parent-repo",
106 106 "landing_rev": [
107 107 "rev",
108 108 "tip"
109 109 ],
110 110 "last_changeset": {
111 111 "author": "User <user@example.com>",
112 112 "branch": "default",
113 113 "date": "timestamp",
114 114 "message": "last commit message",
115 115 "parents": [
116 116 {
117 117 "raw_id": "commit-id"
118 118 }
119 119 ],
120 120 "raw_id": "commit-id",
121 121 "revision": <revision number>,
122 122 "short_id": "short id"
123 123 },
124 124 "lock_reason": null,
125 125 "locked_by": null,
126 126 "locked_date": null,
127 127 "members": [
128 128 {
129 129 "name": "super-admin-name",
130 130 "origin": "super-admin",
131 131 "permission": "repository.admin",
132 132 "type": "user"
133 133 },
134 134 {
135 135 "name": "owner-name",
136 136 "origin": "owner",
137 137 "permission": "repository.admin",
138 138 "type": "user"
139 139 },
140 140 {
141 141 "name": "user-group-name",
142 142 "origin": "permission",
143 143 "permission": "repository.write",
144 144 "type": "user_group"
145 145 }
146 146 ],
147 147 "owner": "owner-name",
148 148 "permissions": [
149 149 {
150 150 "name": "super-admin-name",
151 151 "origin": "super-admin",
152 152 "permission": "repository.admin",
153 153 "type": "user"
154 154 },
155 155 {
156 156 "name": "owner-name",
157 157 "origin": "owner",
158 158 "permission": "repository.admin",
159 159 "type": "user"
160 160 },
161 161 {
162 162 "name": "user-group-name",
163 163 "origin": "permission",
164 164 "permission": "repository.write",
165 165 "type": "user_group"
166 166 }
167 167 ],
168 168 "private": true,
169 169 "repo_id": 676,
170 170 "repo_name": "user-group/repo-name",
171 171 "repo_type": "hg"
172 172 }
173 173 }
174 174 """
175 175
176 176 repo = get_repo_or_error(repoid)
177 177 cache = Optional.extract(cache)
178 178
179 179 include_secrets = False
180 180 if has_superadmin_permission(apiuser):
181 181 include_secrets = True
182 182 else:
183 183 # check if we have at least read permission for this repo !
184 184 _perms = (
185 185 'repository.admin', 'repository.write', 'repository.read',)
186 186 validate_repo_permissions(apiuser, repoid, repo, _perms)
187 187
188 188 permissions = []
189 189 for _user in repo.permissions():
190 190 user_data = {
191 191 'name': _user.username,
192 192 'permission': _user.permission,
193 193 'origin': get_origin(_user),
194 194 'type': "user",
195 195 }
196 196 permissions.append(user_data)
197 197
198 198 for _user_group in repo.permission_user_groups():
199 199 user_group_data = {
200 200 'name': _user_group.users_group_name,
201 201 'permission': _user_group.permission,
202 202 'origin': get_origin(_user_group),
203 203 'type': "user_group",
204 204 }
205 205 permissions.append(user_group_data)
206 206
207 207 following_users = [
208 208 user.user.get_api_data(include_secrets=include_secrets)
209 209 for user in repo.followers]
210 210
211 211 if not cache:
212 212 repo.update_commit_cache()
213 213 data = repo.get_api_data(include_secrets=include_secrets)
214 214 data['members'] = permissions # TODO: this should be deprecated soon
215 215 data['permissions'] = permissions
216 216 data['followers'] = following_users
217 217 return data
218 218
219 219
220 220 @jsonrpc_method()
221 221 def get_repos(request, apiuser, root=Optional(None), traverse=Optional(True)):
222 222 """
223 223 Lists all existing repositories.
224 224
225 225 This command can only be run using an |authtoken| with admin rights,
226 226 or users with at least read rights to |repos|.
227 227
228 228 :param apiuser: This is filled automatically from the |authtoken|.
229 229 :type apiuser: AuthUser
230 230 :param root: specify root repository group to fetch repositories.
231 231 filters the returned repositories to be members of given root group.
232 232 :type root: Optional(None)
233 233 :param traverse: traverse given root into subrepositories. With this flag
234 234 set to False, it will only return top-level repositories from `root`.
235 235 if root is empty it will return just top-level repositories.
236 236 :type traverse: Optional(True)
237 237
238 238
239 239 Example output:
240 240
241 241 .. code-block:: bash
242 242
243 243 id : <id_given_in_input>
244 244 result: [
245 245 {
246 246 "repo_id" : "<repo_id>",
247 247 "repo_name" : "<reponame>"
248 248 "repo_type" : "<repo_type>",
249 249 "clone_uri" : "<clone_uri>",
250 250 "private": : "<bool>",
251 251 "created_on" : "<datetimecreated>",
252 252 "description" : "<description>",
253 253 "landing_rev": "<landing_rev>",
254 254 "owner": "<repo_owner>",
255 255 "fork_of": "<name_of_fork_parent>",
256 256 "enable_downloads": "<bool>",
257 257 "enable_locking": "<bool>",
258 258 "enable_statistics": "<bool>",
259 259 },
260 260 ...
261 261 ]
262 262 error: null
263 263 """
264 264
265 265 include_secrets = has_superadmin_permission(apiuser)
266 266 _perms = ('repository.read', 'repository.write', 'repository.admin',)
267 267 extras = {'user': apiuser}
268 268
269 269 root = Optional.extract(root)
270 270 traverse = Optional.extract(traverse, binary=True)
271 271
272 272 if root:
273 273 # verify parent existance, if it's empty return an error
274 274 parent = RepoGroup.get_by_group_name(root)
275 275 if not parent:
276 276 raise JSONRPCError(
277 277 'Root repository group `{}` does not exist'.format(root))
278 278
279 279 if traverse:
280 280 repos = RepoModel().get_repos_for_root(root=root, traverse=traverse)
281 281 else:
282 282 repos = RepoModel().get_repos_for_root(root=parent)
283 283 else:
284 284 if traverse:
285 285 repos = RepoModel().get_all()
286 286 else:
287 287 # return just top-level
288 288 repos = RepoModel().get_repos_for_root(root=None)
289 289
290 290 repo_list = RepoList(repos, perm_set=_perms, extra_kwargs=extras)
291 291 return [repo.get_api_data(include_secrets=include_secrets)
292 292 for repo in repo_list]
293 293
294 294
295 295 @jsonrpc_method()
296 296 def get_repo_changeset(request, apiuser, repoid, revision,
297 297 details=Optional('basic')):
298 298 """
299 299 Returns information about a changeset.
300 300
301 301 Additionally parameters define the amount of details returned by
302 302 this function.
303 303
304 304 This command can only be run using an |authtoken| with admin rights,
305 305 or users with at least read rights to the |repo|.
306 306
307 307 :param apiuser: This is filled automatically from the |authtoken|.
308 308 :type apiuser: AuthUser
309 309 :param repoid: The repository name or repository id
310 310 :type repoid: str or int
311 311 :param revision: revision for which listing should be done
312 312 :type revision: str
313 313 :param details: details can be 'basic|extended|full' full gives diff
314 314 info details like the diff itself, and number of changed files etc.
315 315 :type details: Optional(str)
316 316
317 317 """
318 318 repo = get_repo_or_error(repoid)
319 319 if not has_superadmin_permission(apiuser):
320 320 _perms = (
321 321 'repository.admin', 'repository.write', 'repository.read',)
322 322 validate_repo_permissions(apiuser, repoid, repo, _perms)
323 323
324 324 changes_details = Optional.extract(details)
325 325 _changes_details_types = ['basic', 'extended', 'full']
326 326 if changes_details not in _changes_details_types:
327 327 raise JSONRPCError(
328 328 'ret_type must be one of %s' % (
329 329 ','.join(_changes_details_types)))
330 330
331 331 pre_load = ['author', 'branch', 'date', 'message', 'parents',
332 332 'status', '_commit', '_file_paths']
333 333
334 334 try:
335 335 cs = repo.get_commit(commit_id=revision, pre_load=pre_load)
336 336 except TypeError as e:
337 337 raise JSONRPCError(e.message)
338 338 _cs_json = cs.__json__()
339 339 _cs_json['diff'] = build_commit_data(cs, changes_details)
340 340 if changes_details == 'full':
341 341 _cs_json['refs'] = {
342 342 'branches': [cs.branch],
343 343 'bookmarks': getattr(cs, 'bookmarks', []),
344 344 'tags': cs.tags
345 345 }
346 346 return _cs_json
347 347
348 348
349 349 @jsonrpc_method()
350 350 def get_repo_changesets(request, apiuser, repoid, start_rev, limit,
351 351 details=Optional('basic')):
352 352 """
353 353 Returns a set of commits limited by the number starting
354 354 from the `start_rev` option.
355 355
356 356 Additional parameters define the amount of details returned by this
357 357 function.
358 358
359 359 This command can only be run using an |authtoken| with admin rights,
360 360 or users with at least read rights to |repos|.
361 361
362 362 :param apiuser: This is filled automatically from the |authtoken|.
363 363 :type apiuser: AuthUser
364 364 :param repoid: The repository name or repository ID.
365 365 :type repoid: str or int
366 366 :param start_rev: The starting revision from where to get changesets.
367 367 :type start_rev: str
368 368 :param limit: Limit the number of commits to this amount
369 369 :type limit: str or int
370 370 :param details: Set the level of detail returned. Valid option are:
371 371 ``basic``, ``extended`` and ``full``.
372 372 :type details: Optional(str)
373 373
374 374 .. note::
375 375
376 376 Setting the parameter `details` to the value ``full`` is extensive
377 377 and returns details like the diff itself, and the number
378 378 of changed files.
379 379
380 380 """
381 381 repo = get_repo_or_error(repoid)
382 382 if not has_superadmin_permission(apiuser):
383 383 _perms = (
384 384 'repository.admin', 'repository.write', 'repository.read',)
385 385 validate_repo_permissions(apiuser, repoid, repo, _perms)
386 386
387 387 changes_details = Optional.extract(details)
388 388 _changes_details_types = ['basic', 'extended', 'full']
389 389 if changes_details not in _changes_details_types:
390 390 raise JSONRPCError(
391 391 'ret_type must be one of %s' % (
392 392 ','.join(_changes_details_types)))
393 393
394 394 limit = int(limit)
395 395 pre_load = ['author', 'branch', 'date', 'message', 'parents',
396 396 'status', '_commit', '_file_paths']
397 397
398 398 vcs_repo = repo.scm_instance()
399 399 # SVN needs a special case to distinguish its index and commit id
400 400 if vcs_repo and vcs_repo.alias == 'svn' and (start_rev == '0'):
401 401 start_rev = vcs_repo.commit_ids[0]
402 402
403 403 try:
404 404 commits = vcs_repo.get_commits(
405 405 start_id=start_rev, pre_load=pre_load)
406 406 except TypeError as e:
407 407 raise JSONRPCError(e.message)
408 408 except Exception:
409 409 log.exception('Fetching of commits failed')
410 410 raise JSONRPCError('Error occurred during commit fetching')
411 411
412 412 ret = []
413 413 for cnt, commit in enumerate(commits):
414 414 if cnt >= limit != -1:
415 415 break
416 416 _cs_json = commit.__json__()
417 417 _cs_json['diff'] = build_commit_data(commit, changes_details)
418 418 if changes_details == 'full':
419 419 _cs_json['refs'] = {
420 420 'branches': [commit.branch],
421 421 'bookmarks': getattr(commit, 'bookmarks', []),
422 422 'tags': commit.tags
423 423 }
424 424 ret.append(_cs_json)
425 425 return ret
426 426
427 427
428 428 @jsonrpc_method()
429 429 def get_repo_nodes(request, apiuser, repoid, revision, root_path,
430 430 ret_type=Optional('all'), details=Optional('basic'),
431 431 max_file_bytes=Optional(None)):
432 432 """
433 433 Returns a list of nodes and children in a flat list for a given
434 434 path at given revision.
435 435
436 436 It's possible to specify ret_type to show only `files` or `dirs`.
437 437
438 438 This command can only be run using an |authtoken| with admin rights,
439 439 or users with at least read rights to |repos|.
440 440
441 441 :param apiuser: This is filled automatically from the |authtoken|.
442 442 :type apiuser: AuthUser
443 443 :param repoid: The repository name or repository ID.
444 444 :type repoid: str or int
445 445 :param revision: The revision for which listing should be done.
446 446 :type revision: str
447 447 :param root_path: The path from which to start displaying.
448 448 :type root_path: str
449 449 :param ret_type: Set the return type. Valid options are
450 450 ``all`` (default), ``files`` and ``dirs``.
451 451 :type ret_type: Optional(str)
452 452 :param details: Returns extended information about nodes, such as
453 453 md5, binary, and or content. The valid options are ``basic`` and
454 454 ``full``.
455 455 :type details: Optional(str)
456 456 :param max_file_bytes: Only return file content under this file size bytes
457 457 :type details: Optional(int)
458 458
459 459 Example output:
460 460
461 461 .. code-block:: bash
462 462
463 463 id : <id_given_in_input>
464 464 result: [
465 465 {
466 466 "name" : "<name>"
467 467 "type" : "<type>",
468 468 "binary": "<true|false>" (only in extended mode)
469 469 "md5" : "<md5 of file content>" (only in extended mode)
470 470 },
471 471 ...
472 472 ]
473 473 error: null
474 474 """
475 475
476 476 repo = get_repo_or_error(repoid)
477 477 if not has_superadmin_permission(apiuser):
478 478 _perms = (
479 479 'repository.admin', 'repository.write', 'repository.read',)
480 480 validate_repo_permissions(apiuser, repoid, repo, _perms)
481 481
482 482 ret_type = Optional.extract(ret_type)
483 483 details = Optional.extract(details)
484 484 _extended_types = ['basic', 'full']
485 485 if details not in _extended_types:
486 486 raise JSONRPCError(
487 487 'ret_type must be one of %s' % (','.join(_extended_types)))
488 488 extended_info = False
489 489 content = False
490 490 if details == 'basic':
491 491 extended_info = True
492 492
493 493 if details == 'full':
494 494 extended_info = content = True
495 495
496 496 _map = {}
497 497 try:
498 498 # check if repo is not empty by any chance, skip quicker if it is.
499 499 _scm = repo.scm_instance()
500 500 if _scm.is_empty():
501 501 return []
502 502
503 503 _d, _f = ScmModel().get_nodes(
504 504 repo, revision, root_path, flat=False,
505 505 extended_info=extended_info, content=content,
506 506 max_file_bytes=max_file_bytes)
507 507 _map = {
508 508 'all': _d + _f,
509 509 'files': _f,
510 510 'dirs': _d,
511 511 }
512 512 return _map[ret_type]
513 513 except KeyError:
514 514 raise JSONRPCError(
515 515 'ret_type must be one of %s' % (','.join(sorted(_map.keys()))))
516 516 except Exception:
517 517 log.exception("Exception occurred while trying to get repo nodes")
518 518 raise JSONRPCError(
519 519 'failed to get repo: `%s` nodes' % repo.repo_name
520 520 )
521 521
522 522
523 523 @jsonrpc_method()
524 524 def get_repo_refs(request, apiuser, repoid):
525 525 """
526 526 Returns a dictionary of current references. It returns
527 527 bookmarks, branches, closed_branches, and tags for given repository
528 528
529 529 It's possible to specify ret_type to show only `files` or `dirs`.
530 530
531 531 This command can only be run using an |authtoken| with admin rights,
532 532 or users with at least read rights to |repos|.
533 533
534 534 :param apiuser: This is filled automatically from the |authtoken|.
535 535 :type apiuser: AuthUser
536 536 :param repoid: The repository name or repository ID.
537 537 :type repoid: str or int
538 538
539 539 Example output:
540 540
541 541 .. code-block:: bash
542 542
543 543 id : <id_given_in_input>
544 544 "result": {
545 545 "bookmarks": {
546 546 "dev": "5611d30200f4040ba2ab4f3d64e5b06408a02188",
547 547 "master": "367f590445081d8ec8c2ea0456e73ae1f1c3d6cf"
548 548 },
549 549 "branches": {
550 550 "default": "5611d30200f4040ba2ab4f3d64e5b06408a02188",
551 551 "stable": "367f590445081d8ec8c2ea0456e73ae1f1c3d6cf"
552 552 },
553 553 "branches_closed": {},
554 554 "tags": {
555 555 "tip": "5611d30200f4040ba2ab4f3d64e5b06408a02188",
556 556 "v4.4.0": "1232313f9e6adac5ce5399c2a891dc1e72b79022",
557 557 "v4.4.1": "cbb9f1d329ae5768379cdec55a62ebdd546c4e27",
558 558 "v4.4.2": "24ffe44a27fcd1c5b6936144e176b9f6dd2f3a17",
559 559 }
560 560 }
561 561 error: null
562 562 """
563 563
564 564 repo = get_repo_or_error(repoid)
565 565 if not has_superadmin_permission(apiuser):
566 566 _perms = ('repository.admin', 'repository.write', 'repository.read',)
567 567 validate_repo_permissions(apiuser, repoid, repo, _perms)
568 568
569 569 try:
570 570 # check if repo is not empty by any chance, skip quicker if it is.
571 571 vcs_instance = repo.scm_instance()
572 572 refs = vcs_instance.refs()
573 573 return refs
574 574 except Exception:
575 575 log.exception("Exception occurred while trying to get repo refs")
576 576 raise JSONRPCError(
577 577 'failed to get repo: `%s` references' % repo.repo_name
578 578 )
579 579
580 580
581 581 @jsonrpc_method()
582 582 def create_repo(
583 583 request, apiuser, repo_name, repo_type,
584 584 owner=Optional(OAttr('apiuser')),
585 585 description=Optional(''),
586 586 private=Optional(False),
587 587 clone_uri=Optional(None),
588 588 landing_rev=Optional('rev:tip'),
589 589 enable_statistics=Optional(False),
590 590 enable_locking=Optional(False),
591 591 enable_downloads=Optional(False),
592 592 copy_permissions=Optional(False)):
593 593 """
594 594 Creates a repository.
595 595
596 596 * If the repository name contains "/", repository will be created inside
597 597 a repository group or nested repository groups
598 598
599 599 For example "foo/bar/repo1" will create |repo| called "repo1" inside
600 600 group "foo/bar". You have to have permissions to access and write to
601 601 the last repository group ("bar" in this example)
602 602
603 603 This command can only be run using an |authtoken| with at least
604 604 permissions to create repositories, or write permissions to
605 605 parent repository groups.
606 606
607 607 :param apiuser: This is filled automatically from the |authtoken|.
608 608 :type apiuser: AuthUser
609 609 :param repo_name: Set the repository name.
610 610 :type repo_name: str
611 611 :param repo_type: Set the repository type; 'hg','git', or 'svn'.
612 612 :type repo_type: str
613 613 :param owner: user_id or username
614 614 :type owner: Optional(str)
615 615 :param description: Set the repository description.
616 616 :type description: Optional(str)
617 617 :param private: set repository as private
618 618 :type private: bool
619 619 :param clone_uri: set clone_uri
620 620 :type clone_uri: str
621 621 :param landing_rev: <rev_type>:<rev>
622 622 :type landing_rev: str
623 623 :param enable_locking:
624 624 :type enable_locking: bool
625 625 :param enable_downloads:
626 626 :type enable_downloads: bool
627 627 :param enable_statistics:
628 628 :type enable_statistics: bool
629 629 :param copy_permissions: Copy permission from group in which the
630 630 repository is being created.
631 631 :type copy_permissions: bool
632 632
633 633
634 634 Example output:
635 635
636 636 .. code-block:: bash
637 637
638 638 id : <id_given_in_input>
639 639 result: {
640 640 "msg": "Created new repository `<reponame>`",
641 641 "success": true,
642 642 "task": "<celery task id or None if done sync>"
643 643 }
644 644 error: null
645 645
646 646
647 647 Example error output:
648 648
649 649 .. code-block:: bash
650 650
651 651 id : <id_given_in_input>
652 652 result : null
653 653 error : {
654 654 'failed to create repository `<repo_name>`'
655 655 }
656 656
657 657 """
658 658
659 659 owner = validate_set_owner_permissions(apiuser, owner)
660 660
661 661 description = Optional.extract(description)
662 662 copy_permissions = Optional.extract(copy_permissions)
663 663 clone_uri = Optional.extract(clone_uri)
664 664 landing_commit_ref = Optional.extract(landing_rev)
665 665
666 666 defs = SettingsModel().get_default_repo_settings(strip_prefix=True)
667 667 if isinstance(private, Optional):
668 668 private = defs.get('repo_private') or Optional.extract(private)
669 669 if isinstance(repo_type, Optional):
670 670 repo_type = defs.get('repo_type')
671 671 if isinstance(enable_statistics, Optional):
672 672 enable_statistics = defs.get('repo_enable_statistics')
673 673 if isinstance(enable_locking, Optional):
674 674 enable_locking = defs.get('repo_enable_locking')
675 675 if isinstance(enable_downloads, Optional):
676 676 enable_downloads = defs.get('repo_enable_downloads')
677 677
678 678 schema = repo_schema.RepoSchema().bind(
679 679 repo_type_options=rhodecode.BACKENDS.keys(),
680 680 # user caller
681 681 user=apiuser)
682 682
683 683 try:
684 684 schema_data = schema.deserialize(dict(
685 685 repo_name=repo_name,
686 686 repo_type=repo_type,
687 687 repo_owner=owner.username,
688 688 repo_description=description,
689 689 repo_landing_commit_ref=landing_commit_ref,
690 690 repo_clone_uri=clone_uri,
691 691 repo_private=private,
692 692 repo_copy_permissions=copy_permissions,
693 693 repo_enable_statistics=enable_statistics,
694 694 repo_enable_downloads=enable_downloads,
695 695 repo_enable_locking=enable_locking))
696 696 except validation_schema.Invalid as err:
697 697 raise JSONRPCValidationError(colander_exc=err)
698 698
699 699 try:
700 700 data = {
701 701 'owner': owner,
702 702 'repo_name': schema_data['repo_group']['repo_name_without_group'],
703 703 'repo_name_full': schema_data['repo_name'],
704 704 'repo_group': schema_data['repo_group']['repo_group_id'],
705 705 'repo_type': schema_data['repo_type'],
706 706 'repo_description': schema_data['repo_description'],
707 707 'repo_private': schema_data['repo_private'],
708 708 'clone_uri': schema_data['repo_clone_uri'],
709 709 'repo_landing_rev': schema_data['repo_landing_commit_ref'],
710 710 'enable_statistics': schema_data['repo_enable_statistics'],
711 711 'enable_locking': schema_data['repo_enable_locking'],
712 712 'enable_downloads': schema_data['repo_enable_downloads'],
713 713 'repo_copy_permissions': schema_data['repo_copy_permissions'],
714 714 }
715 715
716 716 task = RepoModel().create(form_data=data, cur_user=owner)
717 717 from celery.result import BaseAsyncResult
718 718 task_id = None
719 719 if isinstance(task, BaseAsyncResult):
720 720 task_id = task.task_id
721 721 # no commit, it's done in RepoModel, or async via celery
722 722 return {
723 723 'msg': "Created new repository `%s`" % (schema_data['repo_name'],),
724 724 'success': True, # cannot return the repo data here since fork
725 725 # can be done async
726 726 'task': task_id
727 727 }
728 728 except Exception:
729 729 log.exception(
730 730 u"Exception while trying to create the repository %s",
731 731 schema_data['repo_name'])
732 732 raise JSONRPCError(
733 733 'failed to create repository `%s`' % (schema_data['repo_name'],))
734 734
735 735
736 736 @jsonrpc_method()
737 737 def add_field_to_repo(request, apiuser, repoid, key, label=Optional(''),
738 738 description=Optional('')):
739 739 """
740 740 Adds an extra field to a repository.
741 741
742 742 This command can only be run using an |authtoken| with at least
743 743 write permissions to the |repo|.
744 744
745 745 :param apiuser: This is filled automatically from the |authtoken|.
746 746 :type apiuser: AuthUser
747 747 :param repoid: Set the repository name or repository id.
748 748 :type repoid: str or int
749 749 :param key: Create a unique field key for this repository.
750 750 :type key: str
751 751 :param label:
752 752 :type label: Optional(str)
753 753 :param description:
754 754 :type description: Optional(str)
755 755 """
756 756 repo = get_repo_or_error(repoid)
757 757 if not has_superadmin_permission(apiuser):
758 758 _perms = ('repository.admin',)
759 759 validate_repo_permissions(apiuser, repoid, repo, _perms)
760 760
761 761 label = Optional.extract(label) or key
762 762 description = Optional.extract(description)
763 763
764 764 field = RepositoryField.get_by_key_name(key, repo)
765 765 if field:
766 766 raise JSONRPCError('Field with key '
767 767 '`%s` exists for repo `%s`' % (key, repoid))
768 768
769 769 try:
770 770 RepoModel().add_repo_field(repo, key, field_label=label,
771 771 field_desc=description)
772 772 Session().commit()
773 773 return {
774 774 'msg': "Added new repository field `%s`" % (key,),
775 775 'success': True,
776 776 }
777 777 except Exception:
778 778 log.exception("Exception occurred while trying to add field to repo")
779 779 raise JSONRPCError(
780 780 'failed to create new field for repository `%s`' % (repoid,))
781 781
782 782
783 783 @jsonrpc_method()
784 784 def remove_field_from_repo(request, apiuser, repoid, key):
785 785 """
786 786 Removes an extra field from a repository.
787 787
788 788 This command can only be run using an |authtoken| with at least
789 789 write permissions to the |repo|.
790 790
791 791 :param apiuser: This is filled automatically from the |authtoken|.
792 792 :type apiuser: AuthUser
793 793 :param repoid: Set the repository name or repository ID.
794 794 :type repoid: str or int
795 795 :param key: Set the unique field key for this repository.
796 796 :type key: str
797 797 """
798 798
799 799 repo = get_repo_or_error(repoid)
800 800 if not has_superadmin_permission(apiuser):
801 801 _perms = ('repository.admin',)
802 802 validate_repo_permissions(apiuser, repoid, repo, _perms)
803 803
804 804 field = RepositoryField.get_by_key_name(key, repo)
805 805 if not field:
806 806 raise JSONRPCError('Field with key `%s` does not '
807 807 'exists for repo `%s`' % (key, repoid))
808 808
809 809 try:
810 810 RepoModel().delete_repo_field(repo, field_key=key)
811 811 Session().commit()
812 812 return {
813 813 'msg': "Deleted repository field `%s`" % (key,),
814 814 'success': True,
815 815 }
816 816 except Exception:
817 817 log.exception(
818 818 "Exception occurred while trying to delete field from repo")
819 819 raise JSONRPCError(
820 820 'failed to delete field for repository `%s`' % (repoid,))
821 821
822 822
823 823 @jsonrpc_method()
824 824 def update_repo(
825 825 request, apiuser, repoid, repo_name=Optional(None),
826 826 owner=Optional(OAttr('apiuser')), description=Optional(''),
827 827 private=Optional(False), clone_uri=Optional(None),
828 828 landing_rev=Optional('rev:tip'), fork_of=Optional(None),
829 829 enable_statistics=Optional(False),
830 830 enable_locking=Optional(False),
831 831 enable_downloads=Optional(False), fields=Optional('')):
832 832 """
833 833 Updates a repository with the given information.
834 834
835 835 This command can only be run using an |authtoken| with at least
836 836 admin permissions to the |repo|.
837 837
838 838 * If the repository name contains "/", repository will be updated
839 839 accordingly with a repository group or nested repository groups
840 840
841 841 For example repoid=repo-test name="foo/bar/repo-test" will update |repo|
842 842 called "repo-test" and place it inside group "foo/bar".
843 843 You have to have permissions to access and write to the last repository
844 844 group ("bar" in this example)
845 845
846 846 :param apiuser: This is filled automatically from the |authtoken|.
847 847 :type apiuser: AuthUser
848 848 :param repoid: repository name or repository ID.
849 849 :type repoid: str or int
850 850 :param repo_name: Update the |repo| name, including the
851 851 repository group it's in.
852 852 :type repo_name: str
853 853 :param owner: Set the |repo| owner.
854 854 :type owner: str
855 855 :param fork_of: Set the |repo| as fork of another |repo|.
856 856 :type fork_of: str
857 857 :param description: Update the |repo| description.
858 858 :type description: str
859 859 :param private: Set the |repo| as private. (True | False)
860 860 :type private: bool
861 861 :param clone_uri: Update the |repo| clone URI.
862 862 :type clone_uri: str
863 863 :param landing_rev: Set the |repo| landing revision. Default is ``rev:tip``.
864 864 :type landing_rev: str
865 865 :param enable_statistics: Enable statistics on the |repo|, (True | False).
866 866 :type enable_statistics: bool
867 867 :param enable_locking: Enable |repo| locking.
868 868 :type enable_locking: bool
869 869 :param enable_downloads: Enable downloads from the |repo|, (True | False).
870 870 :type enable_downloads: bool
871 871 :param fields: Add extra fields to the |repo|. Use the following
872 872 example format: ``field_key=field_val,field_key2=fieldval2``.
873 873 Escape ', ' with \,
874 874 :type fields: str
875 875 """
876 876
877 877 repo = get_repo_or_error(repoid)
878 878
879 879 include_secrets = False
880 880 if not has_superadmin_permission(apiuser):
881 881 validate_repo_permissions(apiuser, repoid, repo, ('repository.admin',))
882 882 else:
883 883 include_secrets = True
884 884
885 885 updates = dict(
886 886 repo_name=repo_name
887 887 if not isinstance(repo_name, Optional) else repo.repo_name,
888 888
889 889 fork_id=fork_of
890 890 if not isinstance(fork_of, Optional) else repo.fork.repo_name if repo.fork else None,
891 891
892 892 user=owner
893 893 if not isinstance(owner, Optional) else repo.user.username,
894 894
895 895 repo_description=description
896 896 if not isinstance(description, Optional) else repo.description,
897 897
898 898 repo_private=private
899 899 if not isinstance(private, Optional) else repo.private,
900 900
901 901 clone_uri=clone_uri
902 902 if not isinstance(clone_uri, Optional) else repo.clone_uri,
903 903
904 904 repo_landing_rev=landing_rev
905 905 if not isinstance(landing_rev, Optional) else repo._landing_revision,
906 906
907 907 repo_enable_statistics=enable_statistics
908 908 if not isinstance(enable_statistics, Optional) else repo.enable_statistics,
909 909
910 910 repo_enable_locking=enable_locking
911 911 if not isinstance(enable_locking, Optional) else repo.enable_locking,
912 912
913 913 repo_enable_downloads=enable_downloads
914 914 if not isinstance(enable_downloads, Optional) else repo.enable_downloads)
915 915
916 916 ref_choices, _labels = ScmModel().get_repo_landing_revs(repo=repo)
917 917
918 918 schema = repo_schema.RepoSchema().bind(
919 919 repo_type_options=rhodecode.BACKENDS.keys(),
920 920 repo_ref_options=ref_choices,
921 921 # user caller
922 922 user=apiuser,
923 923 old_values=repo.get_api_data())
924 924 try:
925 925 schema_data = schema.deserialize(dict(
926 926 # we save old value, users cannot change type
927 927 repo_type=repo.repo_type,
928 928
929 929 repo_name=updates['repo_name'],
930 930 repo_owner=updates['user'],
931 931 repo_description=updates['repo_description'],
932 932 repo_clone_uri=updates['clone_uri'],
933 933 repo_fork_of=updates['fork_id'],
934 934 repo_private=updates['repo_private'],
935 935 repo_landing_commit_ref=updates['repo_landing_rev'],
936 936 repo_enable_statistics=updates['repo_enable_statistics'],
937 937 repo_enable_downloads=updates['repo_enable_downloads'],
938 938 repo_enable_locking=updates['repo_enable_locking']))
939 939 except validation_schema.Invalid as err:
940 940 raise JSONRPCValidationError(colander_exc=err)
941 941
942 942 # save validated data back into the updates dict
943 943 validated_updates = dict(
944 944 repo_name=schema_data['repo_group']['repo_name_without_group'],
945 945 repo_group=schema_data['repo_group']['repo_group_id'],
946 946
947 947 user=schema_data['repo_owner'],
948 948 repo_description=schema_data['repo_description'],
949 949 repo_private=schema_data['repo_private'],
950 950 clone_uri=schema_data['repo_clone_uri'],
951 951 repo_landing_rev=schema_data['repo_landing_commit_ref'],
952 952 repo_enable_statistics=schema_data['repo_enable_statistics'],
953 953 repo_enable_locking=schema_data['repo_enable_locking'],
954 954 repo_enable_downloads=schema_data['repo_enable_downloads'],
955 955 )
956 956
957 957 if schema_data['repo_fork_of']:
958 958 fork_repo = get_repo_or_error(schema_data['repo_fork_of'])
959 959 validated_updates['fork_id'] = fork_repo.repo_id
960 960
961 961 # extra fields
962 962 fields = parse_args(Optional.extract(fields), key_prefix='ex_')
963 963 if fields:
964 964 validated_updates.update(fields)
965 965
966 966 try:
967 967 RepoModel().update(repo, **validated_updates)
968 968 Session().commit()
969 969 return {
970 970 'msg': 'updated repo ID:%s %s' % (repo.repo_id, repo.repo_name),
971 971 'repository': repo.get_api_data(include_secrets=include_secrets)
972 972 }
973 973 except Exception:
974 974 log.exception(
975 975 u"Exception while trying to update the repository %s",
976 976 repoid)
977 977 raise JSONRPCError('failed to update repo `%s`' % repoid)
978 978
979 979
980 980 @jsonrpc_method()
981 981 def fork_repo(request, apiuser, repoid, fork_name,
982 982 owner=Optional(OAttr('apiuser')),
983 983 description=Optional(''),
984 984 private=Optional(False),
985 985 clone_uri=Optional(None),
986 986 landing_rev=Optional('rev:tip'),
987 987 copy_permissions=Optional(False)):
988 988 """
989 989 Creates a fork of the specified |repo|.
990 990
991 991 * If the fork_name contains "/", fork will be created inside
992 992 a repository group or nested repository groups
993 993
994 994 For example "foo/bar/fork-repo" will create fork called "fork-repo"
995 995 inside group "foo/bar". You have to have permissions to access and
996 996 write to the last repository group ("bar" in this example)
997 997
998 998 This command can only be run using an |authtoken| with minimum
999 999 read permissions of the forked repo, create fork permissions for an user.
1000 1000
1001 1001 :param apiuser: This is filled automatically from the |authtoken|.
1002 1002 :type apiuser: AuthUser
1003 1003 :param repoid: Set repository name or repository ID.
1004 1004 :type repoid: str or int
1005 1005 :param fork_name: Set the fork name, including it's repository group membership.
1006 1006 :type fork_name: str
1007 1007 :param owner: Set the fork owner.
1008 1008 :type owner: str
1009 1009 :param description: Set the fork description.
1010 1010 :type description: str
1011 1011 :param copy_permissions: Copy permissions from parent |repo|. The
1012 1012 default is False.
1013 1013 :type copy_permissions: bool
1014 1014 :param private: Make the fork private. The default is False.
1015 1015 :type private: bool
1016 1016 :param landing_rev: Set the landing revision. The default is tip.
1017 1017
1018 1018 Example output:
1019 1019
1020 1020 .. code-block:: bash
1021 1021
1022 1022 id : <id_for_response>
1023 1023 api_key : "<api_key>"
1024 1024 args: {
1025 1025 "repoid" : "<reponame or repo_id>",
1026 1026 "fork_name": "<forkname>",
1027 1027 "owner": "<username or user_id = Optional(=apiuser)>",
1028 1028 "description": "<description>",
1029 1029 "copy_permissions": "<bool>",
1030 1030 "private": "<bool>",
1031 1031 "landing_rev": "<landing_rev>"
1032 1032 }
1033 1033
1034 1034 Example error output:
1035 1035
1036 1036 .. code-block:: bash
1037 1037
1038 1038 id : <id_given_in_input>
1039 1039 result: {
1040 1040 "msg": "Created fork of `<reponame>` as `<forkname>`",
1041 1041 "success": true,
1042 1042 "task": "<celery task id or None if done sync>"
1043 1043 }
1044 1044 error: null
1045 1045
1046 1046 """
1047 1047
1048 1048 repo = get_repo_or_error(repoid)
1049 1049 repo_name = repo.repo_name
1050 1050
1051 1051 if not has_superadmin_permission(apiuser):
1052 1052 # check if we have at least read permission for
1053 1053 # this repo that we fork !
1054 1054 _perms = (
1055 1055 'repository.admin', 'repository.write', 'repository.read')
1056 1056 validate_repo_permissions(apiuser, repoid, repo, _perms)
1057 1057
1058 1058 # check if the regular user has at least fork permissions as well
1059 1059 if not HasPermissionAnyApi('hg.fork.repository')(user=apiuser):
1060 1060 raise JSONRPCForbidden()
1061 1061
1062 1062 # check if user can set owner parameter
1063 1063 owner = validate_set_owner_permissions(apiuser, owner)
1064 1064
1065 1065 description = Optional.extract(description)
1066 1066 copy_permissions = Optional.extract(copy_permissions)
1067 1067 clone_uri = Optional.extract(clone_uri)
1068 1068 landing_commit_ref = Optional.extract(landing_rev)
1069 1069 private = Optional.extract(private)
1070 1070
1071 1071 schema = repo_schema.RepoSchema().bind(
1072 1072 repo_type_options=rhodecode.BACKENDS.keys(),
1073 1073 # user caller
1074 1074 user=apiuser)
1075 1075
1076 1076 try:
1077 1077 schema_data = schema.deserialize(dict(
1078 1078 repo_name=fork_name,
1079 1079 repo_type=repo.repo_type,
1080 1080 repo_owner=owner.username,
1081 1081 repo_description=description,
1082 1082 repo_landing_commit_ref=landing_commit_ref,
1083 1083 repo_clone_uri=clone_uri,
1084 1084 repo_private=private,
1085 1085 repo_copy_permissions=copy_permissions))
1086 1086 except validation_schema.Invalid as err:
1087 1087 raise JSONRPCValidationError(colander_exc=err)
1088 1088
1089 1089 try:
1090 1090 data = {
1091 1091 'fork_parent_id': repo.repo_id,
1092 1092
1093 1093 'repo_name': schema_data['repo_group']['repo_name_without_group'],
1094 1094 'repo_name_full': schema_data['repo_name'],
1095 1095 'repo_group': schema_data['repo_group']['repo_group_id'],
1096 1096 'repo_type': schema_data['repo_type'],
1097 1097 'description': schema_data['repo_description'],
1098 1098 'private': schema_data['repo_private'],
1099 1099 'copy_permissions': schema_data['repo_copy_permissions'],
1100 1100 'landing_rev': schema_data['repo_landing_commit_ref'],
1101 1101 }
1102 1102
1103 1103 task = RepoModel().create_fork(data, cur_user=owner)
1104 1104 # no commit, it's done in RepoModel, or async via celery
1105 1105 from celery.result import BaseAsyncResult
1106 1106 task_id = None
1107 1107 if isinstance(task, BaseAsyncResult):
1108 1108 task_id = task.task_id
1109 1109 return {
1110 1110 'msg': 'Created fork of `%s` as `%s`' % (
1111 1111 repo.repo_name, schema_data['repo_name']),
1112 1112 'success': True, # cannot return the repo data here since fork
1113 1113 # can be done async
1114 1114 'task': task_id
1115 1115 }
1116 1116 except Exception:
1117 1117 log.exception(
1118 1118 u"Exception while trying to create fork %s",
1119 1119 schema_data['repo_name'])
1120 1120 raise JSONRPCError(
1121 1121 'failed to fork repository `%s` as `%s`' % (
1122 1122 repo_name, schema_data['repo_name']))
1123 1123
1124 1124
1125 1125 @jsonrpc_method()
1126 1126 def delete_repo(request, apiuser, repoid, forks=Optional('')):
1127 1127 """
1128 1128 Deletes a repository.
1129 1129
1130 1130 * When the `forks` parameter is set it's possible to detach or delete
1131 1131 forks of deleted repository.
1132 1132
1133 1133 This command can only be run using an |authtoken| with admin
1134 1134 permissions on the |repo|.
1135 1135
1136 1136 :param apiuser: This is filled automatically from the |authtoken|.
1137 1137 :type apiuser: AuthUser
1138 1138 :param repoid: Set the repository name or repository ID.
1139 1139 :type repoid: str or int
1140 1140 :param forks: Set to `detach` or `delete` forks from the |repo|.
1141 1141 :type forks: Optional(str)
1142 1142
1143 1143 Example error output:
1144 1144
1145 1145 .. code-block:: bash
1146 1146
1147 1147 id : <id_given_in_input>
1148 1148 result: {
1149 1149 "msg": "Deleted repository `<reponame>`",
1150 1150 "success": true
1151 1151 }
1152 1152 error: null
1153 1153 """
1154 1154
1155 1155 repo = get_repo_or_error(repoid)
1156 1156 if not has_superadmin_permission(apiuser):
1157 1157 _perms = ('repository.admin',)
1158 1158 validate_repo_permissions(apiuser, repoid, repo, _perms)
1159 1159
1160 1160 try:
1161 1161 handle_forks = Optional.extract(forks)
1162 1162 _forks_msg = ''
1163 1163 _forks = [f for f in repo.forks]
1164 1164 if handle_forks == 'detach':
1165 1165 _forks_msg = ' ' + 'Detached %s forks' % len(_forks)
1166 1166 elif handle_forks == 'delete':
1167 1167 _forks_msg = ' ' + 'Deleted %s forks' % len(_forks)
1168 1168 elif _forks:
1169 1169 raise JSONRPCError(
1170 1170 'Cannot delete `%s` it still contains attached forks' %
1171 1171 (repo.repo_name,)
1172 1172 )
1173 1173
1174 1174 RepoModel().delete(repo, forks=forks)
1175 1175 Session().commit()
1176 1176 return {
1177 1177 'msg': 'Deleted repository `%s`%s' % (
1178 1178 repo.repo_name, _forks_msg),
1179 1179 'success': True
1180 1180 }
1181 1181 except Exception:
1182 1182 log.exception("Exception occurred while trying to delete repo")
1183 1183 raise JSONRPCError(
1184 1184 'failed to delete repository `%s`' % (repo.repo_name,)
1185 1185 )
1186 1186
1187 1187
1188 1188 #TODO: marcink, change name ?
1189 1189 @jsonrpc_method()
1190 1190 def invalidate_cache(request, apiuser, repoid, delete_keys=Optional(False)):
1191 1191 """
1192 1192 Invalidates the cache for the specified repository.
1193 1193
1194 1194 This command can only be run using an |authtoken| with admin rights to
1195 1195 the specified repository.
1196 1196
1197 1197 This command takes the following options:
1198 1198
1199 1199 :param apiuser: This is filled automatically from |authtoken|.
1200 1200 :type apiuser: AuthUser
1201 1201 :param repoid: Sets the repository name or repository ID.
1202 1202 :type repoid: str or int
1203 1203 :param delete_keys: This deletes the invalidated keys instead of
1204 1204 just flagging them.
1205 1205 :type delete_keys: Optional(``True`` | ``False``)
1206 1206
1207 1207 Example output:
1208 1208
1209 1209 .. code-block:: bash
1210 1210
1211 1211 id : <id_given_in_input>
1212 1212 result : {
1213 1213 'msg': Cache for repository `<repository name>` was invalidated,
1214 1214 'repository': <repository name>
1215 1215 }
1216 1216 error : null
1217 1217
1218 1218 Example error output:
1219 1219
1220 1220 .. code-block:: bash
1221 1221
1222 1222 id : <id_given_in_input>
1223 1223 result : null
1224 1224 error : {
1225 1225 'Error occurred during cache invalidation action'
1226 1226 }
1227 1227
1228 1228 """
1229 1229
1230 1230 repo = get_repo_or_error(repoid)
1231 1231 if not has_superadmin_permission(apiuser):
1232 1232 _perms = ('repository.admin', 'repository.write',)
1233 1233 validate_repo_permissions(apiuser, repoid, repo, _perms)
1234 1234
1235 1235 delete = Optional.extract(delete_keys)
1236 1236 try:
1237 1237 ScmModel().mark_for_invalidation(repo.repo_name, delete=delete)
1238 1238 return {
1239 1239 'msg': 'Cache for repository `%s` was invalidated' % (repoid,),
1240 1240 'repository': repo.repo_name
1241 1241 }
1242 1242 except Exception:
1243 1243 log.exception(
1244 1244 "Exception occurred while trying to invalidate repo cache")
1245 1245 raise JSONRPCError(
1246 1246 'Error occurred during cache invalidation action'
1247 1247 )
1248 1248
1249 1249
1250 1250 #TODO: marcink, change name ?
1251 1251 @jsonrpc_method()
1252 1252 def lock(request, apiuser, repoid, locked=Optional(None),
1253 1253 userid=Optional(OAttr('apiuser'))):
1254 1254 """
1255 1255 Sets the lock state of the specified |repo| by the given user.
1256 1256 From more information, see :ref:`repo-locking`.
1257 1257
1258 1258 * If the ``userid`` option is not set, the repository is locked to the
1259 1259 user who called the method.
1260 1260 * If the ``locked`` parameter is not set, the current lock state of the
1261 1261 repository is displayed.
1262 1262
1263 1263 This command can only be run using an |authtoken| with admin rights to
1264 1264 the specified repository.
1265 1265
1266 1266 This command takes the following options:
1267 1267
1268 1268 :param apiuser: This is filled automatically from the |authtoken|.
1269 1269 :type apiuser: AuthUser
1270 1270 :param repoid: Sets the repository name or repository ID.
1271 1271 :type repoid: str or int
1272 1272 :param locked: Sets the lock state.
1273 1273 :type locked: Optional(``True`` | ``False``)
1274 1274 :param userid: Set the repository lock to this user.
1275 1275 :type userid: Optional(str or int)
1276 1276
1277 1277 Example error output:
1278 1278
1279 1279 .. code-block:: bash
1280 1280
1281 1281 id : <id_given_in_input>
1282 1282 result : {
1283 1283 'repo': '<reponame>',
1284 1284 'locked': <bool: lock state>,
1285 1285 'locked_since': <int: lock timestamp>,
1286 1286 'locked_by': <username of person who made the lock>,
1287 1287 'lock_reason': <str: reason for locking>,
1288 1288 'lock_state_changed': <bool: True if lock state has been changed in this request>,
1289 1289 'msg': 'Repo `<reponame>` locked by `<username>` on <timestamp>.'
1290 1290 or
1291 1291 'msg': 'Repo `<repository name>` not locked.'
1292 1292 or
1293 1293 'msg': 'User `<user name>` set lock state for repo `<repository name>` to `<new lock state>`'
1294 1294 }
1295 1295 error : null
1296 1296
1297 1297 Example error output:
1298 1298
1299 1299 .. code-block:: bash
1300 1300
1301 1301 id : <id_given_in_input>
1302 1302 result : null
1303 1303 error : {
1304 1304 'Error occurred locking repository `<reponame>`'
1305 1305 }
1306 1306 """
1307 1307
1308 1308 repo = get_repo_or_error(repoid)
1309 1309 if not has_superadmin_permission(apiuser):
1310 1310 # check if we have at least write permission for this repo !
1311 1311 _perms = ('repository.admin', 'repository.write',)
1312 1312 validate_repo_permissions(apiuser, repoid, repo, _perms)
1313 1313
1314 1314 # make sure normal user does not pass someone else userid,
1315 1315 # he is not allowed to do that
1316 1316 if not isinstance(userid, Optional) and userid != apiuser.user_id:
1317 1317 raise JSONRPCError('userid is not the same as your user')
1318 1318
1319 1319 if isinstance(userid, Optional):
1320 1320 userid = apiuser.user_id
1321 1321
1322 1322 user = get_user_or_error(userid)
1323 1323
1324 1324 if isinstance(locked, Optional):
1325 1325 lockobj = repo.locked
1326 1326
1327 1327 if lockobj[0] is None:
1328 1328 _d = {
1329 1329 'repo': repo.repo_name,
1330 1330 'locked': False,
1331 1331 'locked_since': None,
1332 1332 'locked_by': None,
1333 1333 'lock_reason': None,
1334 1334 'lock_state_changed': False,
1335 1335 'msg': 'Repo `%s` not locked.' % repo.repo_name
1336 1336 }
1337 1337 return _d
1338 1338 else:
1339 1339 _user_id, _time, _reason = lockobj
1340 1340 lock_user = get_user_or_error(userid)
1341 1341 _d = {
1342 1342 'repo': repo.repo_name,
1343 1343 'locked': True,
1344 1344 'locked_since': _time,
1345 1345 'locked_by': lock_user.username,
1346 1346 'lock_reason': _reason,
1347 1347 'lock_state_changed': False,
1348 1348 'msg': ('Repo `%s` locked by `%s` on `%s`.'
1349 1349 % (repo.repo_name, lock_user.username,
1350 1350 json.dumps(time_to_datetime(_time))))
1351 1351 }
1352 1352 return _d
1353 1353
1354 1354 # force locked state through a flag
1355 1355 else:
1356 1356 locked = str2bool(locked)
1357 1357 lock_reason = Repository.LOCK_API
1358 1358 try:
1359 1359 if locked:
1360 1360 lock_time = time.time()
1361 1361 Repository.lock(repo, user.user_id, lock_time, lock_reason)
1362 1362 else:
1363 1363 lock_time = None
1364 1364 Repository.unlock(repo)
1365 1365 _d = {
1366 1366 'repo': repo.repo_name,
1367 1367 'locked': locked,
1368 1368 'locked_since': lock_time,
1369 1369 'locked_by': user.username,
1370 1370 'lock_reason': lock_reason,
1371 1371 'lock_state_changed': True,
1372 1372 'msg': ('User `%s` set lock state for repo `%s` to `%s`'
1373 1373 % (user.username, repo.repo_name, locked))
1374 1374 }
1375 1375 return _d
1376 1376 except Exception:
1377 1377 log.exception(
1378 1378 "Exception occurred while trying to lock repository")
1379 1379 raise JSONRPCError(
1380 1380 'Error occurred locking repository `%s`' % repo.repo_name
1381 1381 )
1382 1382
1383 1383
1384 1384 @jsonrpc_method()
1385 1385 def comment_commit(
1386 1386 request, apiuser, repoid, commit_id, message, status=Optional(None),
1387 1387 comment_type=Optional(ChangesetComment.COMMENT_TYPE_NOTE),
1388 1388 resolves_comment_id=Optional(None),
1389 1389 userid=Optional(OAttr('apiuser'))):
1390 1390 """
1391 1391 Set a commit comment, and optionally change the status of the commit.
1392 1392
1393 1393 :param apiuser: This is filled automatically from the |authtoken|.
1394 1394 :type apiuser: AuthUser
1395 1395 :param repoid: Set the repository name or repository ID.
1396 1396 :type repoid: str or int
1397 1397 :param commit_id: Specify the commit_id for which to set a comment.
1398 1398 :type commit_id: str
1399 1399 :param message: The comment text.
1400 1400 :type message: str
1401 1401 :param status: (**Optional**) status of commit, one of: 'not_reviewed',
1402 1402 'approved', 'rejected', 'under_review'
1403 1403 :type status: str
1404 1404 :param comment_type: Comment type, one of: 'note', 'todo'
1405 1405 :type comment_type: Optional(str), default: 'note'
1406 1406 :param userid: Set the user name of the comment creator.
1407 1407 :type userid: Optional(str or int)
1408 1408
1409 1409 Example error output:
1410 1410
1411 1411 .. code-block:: bash
1412 1412
1413 1413 {
1414 1414 "id" : <id_given_in_input>,
1415 1415 "result" : {
1416 1416 "msg": "Commented on commit `<commit_id>` for repository `<repoid>`",
1417 1417 "status_change": null or <status>,
1418 1418 "success": true
1419 1419 },
1420 1420 "error" : null
1421 1421 }
1422 1422
1423 1423 """
1424 1424 repo = get_repo_or_error(repoid)
1425 1425 if not has_superadmin_permission(apiuser):
1426 1426 _perms = ('repository.read', 'repository.write', 'repository.admin')
1427 1427 validate_repo_permissions(apiuser, repoid, repo, _perms)
1428 1428
1429 try:
1430 commit_id = repo.scm_instance().get_commit(commit_id=commit_id).raw_id
1431 except Exception as e:
1432 log.exception('Failed to fetch commit')
1433 raise JSONRPCError(e.message)
1434
1429 1435 if isinstance(userid, Optional):
1430 1436 userid = apiuser.user_id
1431 1437
1432 1438 user = get_user_or_error(userid)
1433 1439 status = Optional.extract(status)
1434 1440 comment_type = Optional.extract(comment_type)
1435 1441 resolves_comment_id = Optional.extract(resolves_comment_id)
1436 1442
1437 1443 allowed_statuses = [x[0] for x in ChangesetStatus.STATUSES]
1438 1444 if status and status not in allowed_statuses:
1439 1445 raise JSONRPCError('Bad status, must be on '
1440 1446 'of %s got %s' % (allowed_statuses, status,))
1441 1447
1442 1448 if resolves_comment_id:
1443 1449 comment = ChangesetComment.get(resolves_comment_id)
1444 1450 if not comment:
1445 1451 raise JSONRPCError(
1446 1452 'Invalid resolves_comment_id `%s` for this commit.'
1447 1453 % resolves_comment_id)
1448 1454 if comment.comment_type != ChangesetComment.COMMENT_TYPE_TODO:
1449 1455 raise JSONRPCError(
1450 1456 'Comment `%s` is wrong type for setting status to resolved.'
1451 1457 % resolves_comment_id)
1452 1458
1453 1459 try:
1454 1460 rc_config = SettingsModel().get_all_settings()
1455 1461 renderer = rc_config.get('rhodecode_markup_renderer', 'rst')
1456 1462 status_change_label = ChangesetStatus.get_status_lbl(status)
1457 1463 comm = CommentsModel().create(
1458 1464 message, repo, user, commit_id=commit_id,
1459 1465 status_change=status_change_label,
1460 1466 status_change_type=status,
1461 1467 renderer=renderer,
1462 1468 comment_type=comment_type,
1463 1469 resolves_comment_id=resolves_comment_id
1464 1470 )
1465 1471 if status:
1466 1472 # also do a status change
1467 1473 try:
1468 1474 ChangesetStatusModel().set_status(
1469 1475 repo, status, user, comm, revision=commit_id,
1470 1476 dont_allow_on_closed_pull_request=True
1471 1477 )
1472 1478 except StatusChangeOnClosedPullRequestError:
1473 1479 log.exception(
1474 1480 "Exception occurred while trying to change repo commit status")
1475 1481 msg = ('Changing status on a changeset associated with '
1476 1482 'a closed pull request is not allowed')
1477 1483 raise JSONRPCError(msg)
1478 1484
1479 1485 Session().commit()
1480 1486 return {
1481 1487 'msg': (
1482 1488 'Commented on commit `%s` for repository `%s`' % (
1483 1489 comm.revision, repo.repo_name)),
1484 1490 'status_change': status,
1485 1491 'success': True,
1486 1492 }
1487 1493 except JSONRPCError:
1488 1494 # catch any inside errors, and re-raise them to prevent from
1489 1495 # below global catch to silence them
1490 1496 raise
1491 1497 except Exception:
1492 1498 log.exception("Exception occurred while trying to comment on commit")
1493 1499 raise JSONRPCError(
1494 1500 'failed to set comment on repository `%s`' % (repo.repo_name,)
1495 1501 )
1496 1502
1497 1503
1498 1504 @jsonrpc_method()
1499 1505 def grant_user_permission(request, apiuser, repoid, userid, perm):
1500 1506 """
1501 1507 Grant permissions for the specified user on the given repository,
1502 1508 or update existing permissions if found.
1503 1509
1504 1510 This command can only be run using an |authtoken| with admin
1505 1511 permissions on the |repo|.
1506 1512
1507 1513 :param apiuser: This is filled automatically from the |authtoken|.
1508 1514 :type apiuser: AuthUser
1509 1515 :param repoid: Set the repository name or repository ID.
1510 1516 :type repoid: str or int
1511 1517 :param userid: Set the user name.
1512 1518 :type userid: str
1513 1519 :param perm: Set the user permissions, using the following format
1514 1520 ``(repository.(none|read|write|admin))``
1515 1521 :type perm: str
1516 1522
1517 1523 Example output:
1518 1524
1519 1525 .. code-block:: bash
1520 1526
1521 1527 id : <id_given_in_input>
1522 1528 result: {
1523 1529 "msg" : "Granted perm: `<perm>` for user: `<username>` in repo: `<reponame>`",
1524 1530 "success": true
1525 1531 }
1526 1532 error: null
1527 1533 """
1528 1534
1529 1535 repo = get_repo_or_error(repoid)
1530 1536 user = get_user_or_error(userid)
1531 1537 perm = get_perm_or_error(perm)
1532 1538 if not has_superadmin_permission(apiuser):
1533 1539 _perms = ('repository.admin',)
1534 1540 validate_repo_permissions(apiuser, repoid, repo, _perms)
1535 1541
1536 1542 try:
1537 1543
1538 1544 RepoModel().grant_user_permission(repo=repo, user=user, perm=perm)
1539 1545
1540 1546 Session().commit()
1541 1547 return {
1542 1548 'msg': 'Granted perm: `%s` for user: `%s` in repo: `%s`' % (
1543 1549 perm.permission_name, user.username, repo.repo_name
1544 1550 ),
1545 1551 'success': True
1546 1552 }
1547 1553 except Exception:
1548 1554 log.exception(
1549 1555 "Exception occurred while trying edit permissions for repo")
1550 1556 raise JSONRPCError(
1551 1557 'failed to edit permission for user: `%s` in repo: `%s`' % (
1552 1558 userid, repoid
1553 1559 )
1554 1560 )
1555 1561
1556 1562
1557 1563 @jsonrpc_method()
1558 1564 def revoke_user_permission(request, apiuser, repoid, userid):
1559 1565 """
1560 1566 Revoke permission for a user on the specified repository.
1561 1567
1562 1568 This command can only be run using an |authtoken| with admin
1563 1569 permissions on the |repo|.
1564 1570
1565 1571 :param apiuser: This is filled automatically from the |authtoken|.
1566 1572 :type apiuser: AuthUser
1567 1573 :param repoid: Set the repository name or repository ID.
1568 1574 :type repoid: str or int
1569 1575 :param userid: Set the user name of revoked user.
1570 1576 :type userid: str or int
1571 1577
1572 1578 Example error output:
1573 1579
1574 1580 .. code-block:: bash
1575 1581
1576 1582 id : <id_given_in_input>
1577 1583 result: {
1578 1584 "msg" : "Revoked perm for user: `<username>` in repo: `<reponame>`",
1579 1585 "success": true
1580 1586 }
1581 1587 error: null
1582 1588 """
1583 1589
1584 1590 repo = get_repo_or_error(repoid)
1585 1591 user = get_user_or_error(userid)
1586 1592 if not has_superadmin_permission(apiuser):
1587 1593 _perms = ('repository.admin',)
1588 1594 validate_repo_permissions(apiuser, repoid, repo, _perms)
1589 1595
1590 1596 try:
1591 1597 RepoModel().revoke_user_permission(repo=repo, user=user)
1592 1598 Session().commit()
1593 1599 return {
1594 1600 'msg': 'Revoked perm for user: `%s` in repo: `%s`' % (
1595 1601 user.username, repo.repo_name
1596 1602 ),
1597 1603 'success': True
1598 1604 }
1599 1605 except Exception:
1600 1606 log.exception(
1601 1607 "Exception occurred while trying revoke permissions to repo")
1602 1608 raise JSONRPCError(
1603 1609 'failed to edit permission for user: `%s` in repo: `%s`' % (
1604 1610 userid, repoid
1605 1611 )
1606 1612 )
1607 1613
1608 1614
1609 1615 @jsonrpc_method()
1610 1616 def grant_user_group_permission(request, apiuser, repoid, usergroupid, perm):
1611 1617 """
1612 1618 Grant permission for a user group on the specified repository,
1613 1619 or update existing permissions.
1614 1620
1615 1621 This command can only be run using an |authtoken| with admin
1616 1622 permissions on the |repo|.
1617 1623
1618 1624 :param apiuser: This is filled automatically from the |authtoken|.
1619 1625 :type apiuser: AuthUser
1620 1626 :param repoid: Set the repository name or repository ID.
1621 1627 :type repoid: str or int
1622 1628 :param usergroupid: Specify the ID of the user group.
1623 1629 :type usergroupid: str or int
1624 1630 :param perm: Set the user group permissions using the following
1625 1631 format: (repository.(none|read|write|admin))
1626 1632 :type perm: str
1627 1633
1628 1634 Example output:
1629 1635
1630 1636 .. code-block:: bash
1631 1637
1632 1638 id : <id_given_in_input>
1633 1639 result : {
1634 1640 "msg" : "Granted perm: `<perm>` for group: `<usersgroupname>` in repo: `<reponame>`",
1635 1641 "success": true
1636 1642
1637 1643 }
1638 1644 error : null
1639 1645
1640 1646 Example error output:
1641 1647
1642 1648 .. code-block:: bash
1643 1649
1644 1650 id : <id_given_in_input>
1645 1651 result : null
1646 1652 error : {
1647 1653 "failed to edit permission for user group: `<usergroup>` in repo `<repo>`'
1648 1654 }
1649 1655
1650 1656 """
1651 1657
1652 1658 repo = get_repo_or_error(repoid)
1653 1659 perm = get_perm_or_error(perm)
1654 1660 if not has_superadmin_permission(apiuser):
1655 1661 _perms = ('repository.admin',)
1656 1662 validate_repo_permissions(apiuser, repoid, repo, _perms)
1657 1663
1658 1664 user_group = get_user_group_or_error(usergroupid)
1659 1665 if not has_superadmin_permission(apiuser):
1660 1666 # check if we have at least read permission for this user group !
1661 1667 _perms = ('usergroup.read', 'usergroup.write', 'usergroup.admin',)
1662 1668 if not HasUserGroupPermissionAnyApi(*_perms)(
1663 1669 user=apiuser, user_group_name=user_group.users_group_name):
1664 1670 raise JSONRPCError(
1665 1671 'user group `%s` does not exist' % (usergroupid,))
1666 1672
1667 1673 try:
1668 1674 RepoModel().grant_user_group_permission(
1669 1675 repo=repo, group_name=user_group, perm=perm)
1670 1676
1671 1677 Session().commit()
1672 1678 return {
1673 1679 'msg': 'Granted perm: `%s` for user group: `%s` in '
1674 1680 'repo: `%s`' % (
1675 1681 perm.permission_name, user_group.users_group_name,
1676 1682 repo.repo_name
1677 1683 ),
1678 1684 'success': True
1679 1685 }
1680 1686 except Exception:
1681 1687 log.exception(
1682 1688 "Exception occurred while trying change permission on repo")
1683 1689 raise JSONRPCError(
1684 1690 'failed to edit permission for user group: `%s` in '
1685 1691 'repo: `%s`' % (
1686 1692 usergroupid, repo.repo_name
1687 1693 )
1688 1694 )
1689 1695
1690 1696
1691 1697 @jsonrpc_method()
1692 1698 def revoke_user_group_permission(request, apiuser, repoid, usergroupid):
1693 1699 """
1694 1700 Revoke the permissions of a user group on a given repository.
1695 1701
1696 1702 This command can only be run using an |authtoken| with admin
1697 1703 permissions on the |repo|.
1698 1704
1699 1705 :param apiuser: This is filled automatically from the |authtoken|.
1700 1706 :type apiuser: AuthUser
1701 1707 :param repoid: Set the repository name or repository ID.
1702 1708 :type repoid: str or int
1703 1709 :param usergroupid: Specify the user group ID.
1704 1710 :type usergroupid: str or int
1705 1711
1706 1712 Example output:
1707 1713
1708 1714 .. code-block:: bash
1709 1715
1710 1716 id : <id_given_in_input>
1711 1717 result: {
1712 1718 "msg" : "Revoked perm for group: `<usersgroupname>` in repo: `<reponame>`",
1713 1719 "success": true
1714 1720 }
1715 1721 error: null
1716 1722 """
1717 1723
1718 1724 repo = get_repo_or_error(repoid)
1719 1725 if not has_superadmin_permission(apiuser):
1720 1726 _perms = ('repository.admin',)
1721 1727 validate_repo_permissions(apiuser, repoid, repo, _perms)
1722 1728
1723 1729 user_group = get_user_group_or_error(usergroupid)
1724 1730 if not has_superadmin_permission(apiuser):
1725 1731 # check if we have at least read permission for this user group !
1726 1732 _perms = ('usergroup.read', 'usergroup.write', 'usergroup.admin',)
1727 1733 if not HasUserGroupPermissionAnyApi(*_perms)(
1728 1734 user=apiuser, user_group_name=user_group.users_group_name):
1729 1735 raise JSONRPCError(
1730 1736 'user group `%s` does not exist' % (usergroupid,))
1731 1737
1732 1738 try:
1733 1739 RepoModel().revoke_user_group_permission(
1734 1740 repo=repo, group_name=user_group)
1735 1741
1736 1742 Session().commit()
1737 1743 return {
1738 1744 'msg': 'Revoked perm for user group: `%s` in repo: `%s`' % (
1739 1745 user_group.users_group_name, repo.repo_name
1740 1746 ),
1741 1747 'success': True
1742 1748 }
1743 1749 except Exception:
1744 1750 log.exception("Exception occurred while trying revoke "
1745 1751 "user group permission on repo")
1746 1752 raise JSONRPCError(
1747 1753 'failed to edit permission for user group: `%s` in '
1748 1754 'repo: `%s`' % (
1749 1755 user_group.users_group_name, repo.repo_name
1750 1756 )
1751 1757 )
1752 1758
1753 1759
1754 1760 @jsonrpc_method()
1755 1761 def pull(request, apiuser, repoid):
1756 1762 """
1757 1763 Triggers a pull on the given repository from a remote location. You
1758 1764 can use this to keep remote repositories up-to-date.
1759 1765
1760 1766 This command can only be run using an |authtoken| with admin
1761 1767 rights to the specified repository. For more information,
1762 1768 see :ref:`config-token-ref`.
1763 1769
1764 1770 This command takes the following options:
1765 1771
1766 1772 :param apiuser: This is filled automatically from the |authtoken|.
1767 1773 :type apiuser: AuthUser
1768 1774 :param repoid: The repository name or repository ID.
1769 1775 :type repoid: str or int
1770 1776
1771 1777 Example output:
1772 1778
1773 1779 .. code-block:: bash
1774 1780
1775 1781 id : <id_given_in_input>
1776 1782 result : {
1777 1783 "msg": "Pulled from `<repository name>`"
1778 1784 "repository": "<repository name>"
1779 1785 }
1780 1786 error : null
1781 1787
1782 1788 Example error output:
1783 1789
1784 1790 .. code-block:: bash
1785 1791
1786 1792 id : <id_given_in_input>
1787 1793 result : null
1788 1794 error : {
1789 1795 "Unable to pull changes from `<reponame>`"
1790 1796 }
1791 1797
1792 1798 """
1793 1799
1794 1800 repo = get_repo_or_error(repoid)
1795 1801 if not has_superadmin_permission(apiuser):
1796 1802 _perms = ('repository.admin',)
1797 1803 validate_repo_permissions(apiuser, repoid, repo, _perms)
1798 1804
1799 1805 try:
1800 1806 ScmModel().pull_changes(repo.repo_name, apiuser.username)
1801 1807 return {
1802 1808 'msg': 'Pulled from `%s`' % repo.repo_name,
1803 1809 'repository': repo.repo_name
1804 1810 }
1805 1811 except Exception:
1806 1812 log.exception("Exception occurred while trying to "
1807 1813 "pull changes from remote location")
1808 1814 raise JSONRPCError(
1809 1815 'Unable to pull changes from `%s`' % repo.repo_name
1810 1816 )
1811 1817
1812 1818
1813 1819 @jsonrpc_method()
1814 1820 def strip(request, apiuser, repoid, revision, branch):
1815 1821 """
1816 1822 Strips the given revision from the specified repository.
1817 1823
1818 1824 * This will remove the revision and all of its decendants.
1819 1825
1820 1826 This command can only be run using an |authtoken| with admin rights to
1821 1827 the specified repository.
1822 1828
1823 1829 This command takes the following options:
1824 1830
1825 1831 :param apiuser: This is filled automatically from the |authtoken|.
1826 1832 :type apiuser: AuthUser
1827 1833 :param repoid: The repository name or repository ID.
1828 1834 :type repoid: str or int
1829 1835 :param revision: The revision you wish to strip.
1830 1836 :type revision: str
1831 1837 :param branch: The branch from which to strip the revision.
1832 1838 :type branch: str
1833 1839
1834 1840 Example output:
1835 1841
1836 1842 .. code-block:: bash
1837 1843
1838 1844 id : <id_given_in_input>
1839 1845 result : {
1840 1846 "msg": "'Stripped commit <commit_hash> from repo `<repository name>`'"
1841 1847 "repository": "<repository name>"
1842 1848 }
1843 1849 error : null
1844 1850
1845 1851 Example error output:
1846 1852
1847 1853 .. code-block:: bash
1848 1854
1849 1855 id : <id_given_in_input>
1850 1856 result : null
1851 1857 error : {
1852 1858 "Unable to strip commit <commit_hash> from repo `<repository name>`"
1853 1859 }
1854 1860
1855 1861 """
1856 1862
1857 1863 repo = get_repo_or_error(repoid)
1858 1864 if not has_superadmin_permission(apiuser):
1859 1865 _perms = ('repository.admin',)
1860 1866 validate_repo_permissions(apiuser, repoid, repo, _perms)
1861 1867
1862 1868 try:
1863 1869 ScmModel().strip(repo, revision, branch)
1864 1870 return {
1865 1871 'msg': 'Stripped commit %s from repo `%s`' % (
1866 1872 revision, repo.repo_name),
1867 1873 'repository': repo.repo_name
1868 1874 }
1869 1875 except Exception:
1870 1876 log.exception("Exception while trying to strip")
1871 1877 raise JSONRPCError(
1872 1878 'Unable to strip commit %s from repo `%s`' % (
1873 1879 revision, repo.repo_name)
1874 1880 )
1875 1881
1876 1882
1877 1883 @jsonrpc_method()
1878 1884 def get_repo_settings(request, apiuser, repoid, key=Optional(None)):
1879 1885 """
1880 1886 Returns all settings for a repository. If key is given it only returns the
1881 1887 setting identified by the key or null.
1882 1888
1883 1889 :param apiuser: This is filled automatically from the |authtoken|.
1884 1890 :type apiuser: AuthUser
1885 1891 :param repoid: The repository name or repository id.
1886 1892 :type repoid: str or int
1887 1893 :param key: Key of the setting to return.
1888 1894 :type: key: Optional(str)
1889 1895
1890 1896 Example output:
1891 1897
1892 1898 .. code-block:: bash
1893 1899
1894 1900 {
1895 1901 "error": null,
1896 1902 "id": 237,
1897 1903 "result": {
1898 1904 "extensions_largefiles": true,
1899 1905 "hooks_changegroup_push_logger": true,
1900 1906 "hooks_changegroup_repo_size": false,
1901 1907 "hooks_outgoing_pull_logger": true,
1902 1908 "phases_publish": "True",
1903 1909 "rhodecode_hg_use_rebase_for_merging": true,
1904 1910 "rhodecode_pr_merge_enabled": true,
1905 1911 "rhodecode_use_outdated_comments": true
1906 1912 }
1907 1913 }
1908 1914 """
1909 1915
1910 1916 # Restrict access to this api method to admins only.
1911 1917 if not has_superadmin_permission(apiuser):
1912 1918 raise JSONRPCForbidden()
1913 1919
1914 1920 try:
1915 1921 repo = get_repo_or_error(repoid)
1916 1922 settings_model = VcsSettingsModel(repo=repo)
1917 1923 settings = settings_model.get_global_settings()
1918 1924 settings.update(settings_model.get_repo_settings())
1919 1925
1920 1926 # If only a single setting is requested fetch it from all settings.
1921 1927 key = Optional.extract(key)
1922 1928 if key is not None:
1923 1929 settings = settings.get(key, None)
1924 1930 except Exception:
1925 1931 msg = 'Failed to fetch settings for repository `{}`'.format(repoid)
1926 1932 log.exception(msg)
1927 1933 raise JSONRPCError(msg)
1928 1934
1929 1935 return settings
1930 1936
1931 1937
1932 1938 @jsonrpc_method()
1933 1939 def set_repo_settings(request, apiuser, repoid, settings):
1934 1940 """
1935 1941 Update repository settings. Returns true on success.
1936 1942
1937 1943 :param apiuser: This is filled automatically from the |authtoken|.
1938 1944 :type apiuser: AuthUser
1939 1945 :param repoid: The repository name or repository id.
1940 1946 :type repoid: str or int
1941 1947 :param settings: The new settings for the repository.
1942 1948 :type: settings: dict
1943 1949
1944 1950 Example output:
1945 1951
1946 1952 .. code-block:: bash
1947 1953
1948 1954 {
1949 1955 "error": null,
1950 1956 "id": 237,
1951 1957 "result": true
1952 1958 }
1953 1959 """
1954 1960 # Restrict access to this api method to admins only.
1955 1961 if not has_superadmin_permission(apiuser):
1956 1962 raise JSONRPCForbidden()
1957 1963
1958 1964 if type(settings) is not dict:
1959 1965 raise JSONRPCError('Settings have to be a JSON Object.')
1960 1966
1961 1967 try:
1962 1968 settings_model = VcsSettingsModel(repo=repoid)
1963 1969
1964 1970 # Merge global, repo and incoming settings.
1965 1971 new_settings = settings_model.get_global_settings()
1966 1972 new_settings.update(settings_model.get_repo_settings())
1967 1973 new_settings.update(settings)
1968 1974
1969 1975 # Update the settings.
1970 1976 inherit_global_settings = new_settings.get(
1971 1977 'inherit_global_settings', False)
1972 1978 settings_model.create_or_update_repo_settings(
1973 1979 new_settings, inherit_global_settings=inherit_global_settings)
1974 1980 Session().commit()
1975 1981 except Exception:
1976 1982 msg = 'Failed to update settings for repository `{}`'.format(repoid)
1977 1983 log.exception(msg)
1978 1984 raise JSONRPCError(msg)
1979 1985
1980 1986 # Indicate success.
1981 1987 return True
General Comments 0
You need to be logged in to leave comments. Login now