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