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