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