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