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