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