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