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