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