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