Show More
@@ -1,2311 +1,2314 | |||||
1 | # -*- coding: utf-8 -*- |
|
1 | # -*- coding: utf-8 -*- | |
2 |
|
2 | |||
3 | # Copyright (C) 2011-2019 RhodeCode GmbH |
|
3 | # Copyright (C) 2011-2019 RhodeCode GmbH | |
4 | # |
|
4 | # | |
5 | # This program is free software: you can redistribute it and/or modify |
|
5 | # This program is free software: you can redistribute it and/or modify | |
6 | # it under the terms of the GNU Affero General Public License, version 3 |
|
6 | # it under the terms of the GNU Affero General Public License, version 3 | |
7 | # (only), as published by the Free Software Foundation. |
|
7 | # (only), as published by the Free Software Foundation. | |
8 | # |
|
8 | # | |
9 | # This program is distributed in the hope that it will be useful, |
|
9 | # This program is distributed in the hope that it will be useful, | |
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
12 | # GNU General Public License for more details. |
|
12 | # GNU General Public License for more details. | |
13 | # |
|
13 | # | |
14 | # You should have received a copy of the GNU Affero General Public License |
|
14 | # You should have received a copy of the GNU Affero General Public License | |
15 | # along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
15 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | |
16 | # |
|
16 | # | |
17 | # This program is dual-licensed. If you wish to learn more about the |
|
17 | # This program is dual-licensed. If you wish to learn more about the | |
18 | # RhodeCode Enterprise Edition, including its added features, Support services, |
|
18 | # RhodeCode Enterprise Edition, including its added features, Support services, | |
19 | # and proprietary license terms, please see https://rhodecode.com/licenses/ |
|
19 | # and proprietary license terms, please see https://rhodecode.com/licenses/ | |
20 |
|
20 | |||
21 | import logging |
|
21 | import logging | |
22 | import time |
|
22 | import time | |
23 |
|
23 | |||
24 | import rhodecode |
|
24 | import rhodecode | |
25 | from rhodecode.api import ( |
|
25 | from rhodecode.api import ( | |
26 | jsonrpc_method, JSONRPCError, JSONRPCForbidden, JSONRPCValidationError) |
|
26 | jsonrpc_method, JSONRPCError, JSONRPCForbidden, JSONRPCValidationError) | |
27 | from rhodecode.api.utils import ( |
|
27 | from rhodecode.api.utils import ( | |
28 | has_superadmin_permission, Optional, OAttr, get_repo_or_error, |
|
28 | has_superadmin_permission, Optional, OAttr, get_repo_or_error, | |
29 | get_user_group_or_error, get_user_or_error, validate_repo_permissions, |
|
29 | get_user_group_or_error, get_user_or_error, validate_repo_permissions, | |
30 | get_perm_or_error, parse_args, get_origin, build_commit_data, |
|
30 | get_perm_or_error, parse_args, get_origin, build_commit_data, | |
31 | validate_set_owner_permissions) |
|
31 | validate_set_owner_permissions) | |
32 | from rhodecode.lib import audit_logger, rc_cache |
|
32 | from rhodecode.lib import audit_logger, rc_cache | |
33 | from rhodecode.lib import repo_maintenance |
|
33 | from rhodecode.lib import repo_maintenance | |
34 | from rhodecode.lib.auth import HasPermissionAnyApi, HasUserGroupPermissionAnyApi |
|
34 | from rhodecode.lib.auth import HasPermissionAnyApi, HasUserGroupPermissionAnyApi | |
35 | from rhodecode.lib.celerylib.utils import get_task_id |
|
35 | from rhodecode.lib.celerylib.utils import get_task_id | |
36 | from rhodecode.lib.utils2 import str2bool, time_to_datetime, safe_str, safe_int |
|
36 | from rhodecode.lib.utils2 import str2bool, time_to_datetime, safe_str, safe_int | |
37 | from rhodecode.lib.ext_json import json |
|
37 | from rhodecode.lib.ext_json import json | |
38 | from rhodecode.lib.exceptions import StatusChangeOnClosedPullRequestError |
|
38 | from rhodecode.lib.exceptions import StatusChangeOnClosedPullRequestError | |
39 | from rhodecode.lib.vcs import RepositoryError |
|
39 | from rhodecode.lib.vcs import RepositoryError | |
|
40 | from rhodecode.lib.vcs.exceptions import NodeDoesNotExistError | |||
40 | from rhodecode.model.changeset_status import ChangesetStatusModel |
|
41 | from rhodecode.model.changeset_status import ChangesetStatusModel | |
41 | from rhodecode.model.comment import CommentsModel |
|
42 | from rhodecode.model.comment import CommentsModel | |
42 | from rhodecode.model.db import ( |
|
43 | from rhodecode.model.db import ( | |
43 | Session, ChangesetStatus, RepositoryField, Repository, RepoGroup, |
|
44 | Session, ChangesetStatus, RepositoryField, Repository, RepoGroup, | |
44 | ChangesetComment) |
|
45 | ChangesetComment) | |
45 | from rhodecode.model.repo import RepoModel |
|
46 | from rhodecode.model.repo import RepoModel | |
46 | from rhodecode.model.scm import ScmModel, RepoList |
|
47 | from rhodecode.model.scm import ScmModel, RepoList | |
47 | from rhodecode.model.settings import SettingsModel, VcsSettingsModel |
|
48 | from rhodecode.model.settings import SettingsModel, VcsSettingsModel | |
48 | from rhodecode.model import validation_schema |
|
49 | from rhodecode.model import validation_schema | |
49 | from rhodecode.model.validation_schema.schemas import repo_schema |
|
50 | from rhodecode.model.validation_schema.schemas import repo_schema | |
50 |
|
51 | |||
51 | log = logging.getLogger(__name__) |
|
52 | log = logging.getLogger(__name__) | |
52 |
|
53 | |||
53 |
|
54 | |||
54 | @jsonrpc_method() |
|
55 | @jsonrpc_method() | |
55 | def get_repo(request, apiuser, repoid, cache=Optional(True)): |
|
56 | def get_repo(request, apiuser, repoid, cache=Optional(True)): | |
56 | """ |
|
57 | """ | |
57 | Gets an existing repository by its name or repository_id. |
|
58 | Gets an existing repository by its name or repository_id. | |
58 |
|
59 | |||
59 | The members section so the output returns users groups or users |
|
60 | The members section so the output returns users groups or users | |
60 | associated with that repository. |
|
61 | associated with that repository. | |
61 |
|
62 | |||
62 | This command can only be run using an |authtoken| with admin rights, |
|
63 | This command can only be run using an |authtoken| with admin rights, | |
63 | or users with at least read rights to the |repo|. |
|
64 | or users with at least read rights to the |repo|. | |
64 |
|
65 | |||
65 | :param apiuser: This is filled automatically from the |authtoken|. |
|
66 | :param apiuser: This is filled automatically from the |authtoken|. | |
66 | :type apiuser: AuthUser |
|
67 | :type apiuser: AuthUser | |
67 | :param repoid: The repository name or repository id. |
|
68 | :param repoid: The repository name or repository id. | |
68 | :type repoid: str or int |
|
69 | :type repoid: str or int | |
69 | :param cache: use the cached value for last changeset |
|
70 | :param cache: use the cached value for last changeset | |
70 | :type: cache: Optional(bool) |
|
71 | :type: cache: Optional(bool) | |
71 |
|
72 | |||
72 | Example output: |
|
73 | Example output: | |
73 |
|
74 | |||
74 | .. code-block:: bash |
|
75 | .. code-block:: bash | |
75 |
|
76 | |||
76 | { |
|
77 | { | |
77 | "error": null, |
|
78 | "error": null, | |
78 | "id": <repo_id>, |
|
79 | "id": <repo_id>, | |
79 | "result": { |
|
80 | "result": { | |
80 | "clone_uri": null, |
|
81 | "clone_uri": null, | |
81 | "created_on": "timestamp", |
|
82 | "created_on": "timestamp", | |
82 | "description": "repo description", |
|
83 | "description": "repo description", | |
83 | "enable_downloads": false, |
|
84 | "enable_downloads": false, | |
84 | "enable_locking": false, |
|
85 | "enable_locking": false, | |
85 | "enable_statistics": false, |
|
86 | "enable_statistics": false, | |
86 | "followers": [ |
|
87 | "followers": [ | |
87 | { |
|
88 | { | |
88 | "active": true, |
|
89 | "active": true, | |
89 | "admin": false, |
|
90 | "admin": false, | |
90 | "api_key": "****************************************", |
|
91 | "api_key": "****************************************", | |
91 | "api_keys": [ |
|
92 | "api_keys": [ | |
92 | "****************************************" |
|
93 | "****************************************" | |
93 | ], |
|
94 | ], | |
94 | "email": "user@example.com", |
|
95 | "email": "user@example.com", | |
95 | "emails": [ |
|
96 | "emails": [ | |
96 | "user@example.com" |
|
97 | "user@example.com" | |
97 | ], |
|
98 | ], | |
98 | "extern_name": "rhodecode", |
|
99 | "extern_name": "rhodecode", | |
99 | "extern_type": "rhodecode", |
|
100 | "extern_type": "rhodecode", | |
100 | "firstname": "username", |
|
101 | "firstname": "username", | |
101 | "ip_addresses": [], |
|
102 | "ip_addresses": [], | |
102 | "language": null, |
|
103 | "language": null, | |
103 | "last_login": "2015-09-16T17:16:35.854", |
|
104 | "last_login": "2015-09-16T17:16:35.854", | |
104 | "lastname": "surname", |
|
105 | "lastname": "surname", | |
105 | "user_id": <user_id>, |
|
106 | "user_id": <user_id>, | |
106 | "username": "name" |
|
107 | "username": "name" | |
107 | } |
|
108 | } | |
108 | ], |
|
109 | ], | |
109 | "fork_of": "parent-repo", |
|
110 | "fork_of": "parent-repo", | |
110 | "landing_rev": [ |
|
111 | "landing_rev": [ | |
111 | "rev", |
|
112 | "rev", | |
112 | "tip" |
|
113 | "tip" | |
113 | ], |
|
114 | ], | |
114 | "last_changeset": { |
|
115 | "last_changeset": { | |
115 | "author": "User <user@example.com>", |
|
116 | "author": "User <user@example.com>", | |
116 | "branch": "default", |
|
117 | "branch": "default", | |
117 | "date": "timestamp", |
|
118 | "date": "timestamp", | |
118 | "message": "last commit message", |
|
119 | "message": "last commit message", | |
119 | "parents": [ |
|
120 | "parents": [ | |
120 | { |
|
121 | { | |
121 | "raw_id": "commit-id" |
|
122 | "raw_id": "commit-id" | |
122 | } |
|
123 | } | |
123 | ], |
|
124 | ], | |
124 | "raw_id": "commit-id", |
|
125 | "raw_id": "commit-id", | |
125 | "revision": <revision number>, |
|
126 | "revision": <revision number>, | |
126 | "short_id": "short id" |
|
127 | "short_id": "short id" | |
127 | }, |
|
128 | }, | |
128 | "lock_reason": null, |
|
129 | "lock_reason": null, | |
129 | "locked_by": null, |
|
130 | "locked_by": null, | |
130 | "locked_date": null, |
|
131 | "locked_date": null, | |
131 | "owner": "owner-name", |
|
132 | "owner": "owner-name", | |
132 | "permissions": [ |
|
133 | "permissions": [ | |
133 | { |
|
134 | { | |
134 | "name": "super-admin-name", |
|
135 | "name": "super-admin-name", | |
135 | "origin": "super-admin", |
|
136 | "origin": "super-admin", | |
136 | "permission": "repository.admin", |
|
137 | "permission": "repository.admin", | |
137 | "type": "user" |
|
138 | "type": "user" | |
138 | }, |
|
139 | }, | |
139 | { |
|
140 | { | |
140 | "name": "owner-name", |
|
141 | "name": "owner-name", | |
141 | "origin": "owner", |
|
142 | "origin": "owner", | |
142 | "permission": "repository.admin", |
|
143 | "permission": "repository.admin", | |
143 | "type": "user" |
|
144 | "type": "user" | |
144 | }, |
|
145 | }, | |
145 | { |
|
146 | { | |
146 | "name": "user-group-name", |
|
147 | "name": "user-group-name", | |
147 | "origin": "permission", |
|
148 | "origin": "permission", | |
148 | "permission": "repository.write", |
|
149 | "permission": "repository.write", | |
149 | "type": "user_group" |
|
150 | "type": "user_group" | |
150 | } |
|
151 | } | |
151 | ], |
|
152 | ], | |
152 | "private": true, |
|
153 | "private": true, | |
153 | "repo_id": 676, |
|
154 | "repo_id": 676, | |
154 | "repo_name": "user-group/repo-name", |
|
155 | "repo_name": "user-group/repo-name", | |
155 | "repo_type": "hg" |
|
156 | "repo_type": "hg" | |
156 | } |
|
157 | } | |
157 | } |
|
158 | } | |
158 | """ |
|
159 | """ | |
159 |
|
160 | |||
160 | repo = get_repo_or_error(repoid) |
|
161 | repo = get_repo_or_error(repoid) | |
161 | cache = Optional.extract(cache) |
|
162 | cache = Optional.extract(cache) | |
162 |
|
163 | |||
163 | include_secrets = False |
|
164 | include_secrets = False | |
164 | if has_superadmin_permission(apiuser): |
|
165 | if has_superadmin_permission(apiuser): | |
165 | include_secrets = True |
|
166 | include_secrets = True | |
166 | else: |
|
167 | else: | |
167 | # check if we have at least read permission for this repo ! |
|
168 | # check if we have at least read permission for this repo ! | |
168 | _perms = ( |
|
169 | _perms = ( | |
169 | 'repository.admin', 'repository.write', 'repository.read',) |
|
170 | 'repository.admin', 'repository.write', 'repository.read',) | |
170 | validate_repo_permissions(apiuser, repoid, repo, _perms) |
|
171 | validate_repo_permissions(apiuser, repoid, repo, _perms) | |
171 |
|
172 | |||
172 | permissions = [] |
|
173 | permissions = [] | |
173 | for _user in repo.permissions(): |
|
174 | for _user in repo.permissions(): | |
174 | user_data = { |
|
175 | user_data = { | |
175 | 'name': _user.username, |
|
176 | 'name': _user.username, | |
176 | 'permission': _user.permission, |
|
177 | 'permission': _user.permission, | |
177 | 'origin': get_origin(_user), |
|
178 | 'origin': get_origin(_user), | |
178 | 'type': "user", |
|
179 | 'type': "user", | |
179 | } |
|
180 | } | |
180 | permissions.append(user_data) |
|
181 | permissions.append(user_data) | |
181 |
|
182 | |||
182 | for _user_group in repo.permission_user_groups(): |
|
183 | for _user_group in repo.permission_user_groups(): | |
183 | user_group_data = { |
|
184 | user_group_data = { | |
184 | 'name': _user_group.users_group_name, |
|
185 | 'name': _user_group.users_group_name, | |
185 | 'permission': _user_group.permission, |
|
186 | 'permission': _user_group.permission, | |
186 | 'origin': get_origin(_user_group), |
|
187 | 'origin': get_origin(_user_group), | |
187 | 'type': "user_group", |
|
188 | 'type': "user_group", | |
188 | } |
|
189 | } | |
189 | permissions.append(user_group_data) |
|
190 | permissions.append(user_group_data) | |
190 |
|
191 | |||
191 | following_users = [ |
|
192 | following_users = [ | |
192 | user.user.get_api_data(include_secrets=include_secrets) |
|
193 | user.user.get_api_data(include_secrets=include_secrets) | |
193 | for user in repo.followers] |
|
194 | for user in repo.followers] | |
194 |
|
195 | |||
195 | if not cache: |
|
196 | if not cache: | |
196 | repo.update_commit_cache() |
|
197 | repo.update_commit_cache() | |
197 | data = repo.get_api_data(include_secrets=include_secrets) |
|
198 | data = repo.get_api_data(include_secrets=include_secrets) | |
198 | data['permissions'] = permissions |
|
199 | data['permissions'] = permissions | |
199 | data['followers'] = following_users |
|
200 | data['followers'] = following_users | |
200 | return data |
|
201 | return data | |
201 |
|
202 | |||
202 |
|
203 | |||
203 | @jsonrpc_method() |
|
204 | @jsonrpc_method() | |
204 | def get_repos(request, apiuser, root=Optional(None), traverse=Optional(True)): |
|
205 | def get_repos(request, apiuser, root=Optional(None), traverse=Optional(True)): | |
205 | """ |
|
206 | """ | |
206 | Lists all existing repositories. |
|
207 | Lists all existing repositories. | |
207 |
|
208 | |||
208 | This command can only be run using an |authtoken| with admin rights, |
|
209 | This command can only be run using an |authtoken| with admin rights, | |
209 | or users with at least read rights to |repos|. |
|
210 | or users with at least read rights to |repos|. | |
210 |
|
211 | |||
211 | :param apiuser: This is filled automatically from the |authtoken|. |
|
212 | :param apiuser: This is filled automatically from the |authtoken|. | |
212 | :type apiuser: AuthUser |
|
213 | :type apiuser: AuthUser | |
213 | :param root: specify root repository group to fetch repositories. |
|
214 | :param root: specify root repository group to fetch repositories. | |
214 | filters the returned repositories to be members of given root group. |
|
215 | filters the returned repositories to be members of given root group. | |
215 | :type root: Optional(None) |
|
216 | :type root: Optional(None) | |
216 | :param traverse: traverse given root into subrepositories. With this flag |
|
217 | :param traverse: traverse given root into subrepositories. With this flag | |
217 | set to False, it will only return top-level repositories from `root`. |
|
218 | set to False, it will only return top-level repositories from `root`. | |
218 | if root is empty it will return just top-level repositories. |
|
219 | if root is empty it will return just top-level repositories. | |
219 | :type traverse: Optional(True) |
|
220 | :type traverse: Optional(True) | |
220 |
|
221 | |||
221 |
|
222 | |||
222 | Example output: |
|
223 | Example output: | |
223 |
|
224 | |||
224 | .. code-block:: bash |
|
225 | .. code-block:: bash | |
225 |
|
226 | |||
226 | id : <id_given_in_input> |
|
227 | id : <id_given_in_input> | |
227 | result: [ |
|
228 | result: [ | |
228 | { |
|
229 | { | |
229 | "repo_id" : "<repo_id>", |
|
230 | "repo_id" : "<repo_id>", | |
230 | "repo_name" : "<reponame>" |
|
231 | "repo_name" : "<reponame>" | |
231 | "repo_type" : "<repo_type>", |
|
232 | "repo_type" : "<repo_type>", | |
232 | "clone_uri" : "<clone_uri>", |
|
233 | "clone_uri" : "<clone_uri>", | |
233 | "private": : "<bool>", |
|
234 | "private": : "<bool>", | |
234 | "created_on" : "<datetimecreated>", |
|
235 | "created_on" : "<datetimecreated>", | |
235 | "description" : "<description>", |
|
236 | "description" : "<description>", | |
236 | "landing_rev": "<landing_rev>", |
|
237 | "landing_rev": "<landing_rev>", | |
237 | "owner": "<repo_owner>", |
|
238 | "owner": "<repo_owner>", | |
238 | "fork_of": "<name_of_fork_parent>", |
|
239 | "fork_of": "<name_of_fork_parent>", | |
239 | "enable_downloads": "<bool>", |
|
240 | "enable_downloads": "<bool>", | |
240 | "enable_locking": "<bool>", |
|
241 | "enable_locking": "<bool>", | |
241 | "enable_statistics": "<bool>", |
|
242 | "enable_statistics": "<bool>", | |
242 | }, |
|
243 | }, | |
243 | ... |
|
244 | ... | |
244 | ] |
|
245 | ] | |
245 | error: null |
|
246 | error: null | |
246 | """ |
|
247 | """ | |
247 |
|
248 | |||
248 | include_secrets = has_superadmin_permission(apiuser) |
|
249 | include_secrets = has_superadmin_permission(apiuser) | |
249 | _perms = ('repository.read', 'repository.write', 'repository.admin',) |
|
250 | _perms = ('repository.read', 'repository.write', 'repository.admin',) | |
250 | extras = {'user': apiuser} |
|
251 | extras = {'user': apiuser} | |
251 |
|
252 | |||
252 | root = Optional.extract(root) |
|
253 | root = Optional.extract(root) | |
253 | traverse = Optional.extract(traverse, binary=True) |
|
254 | traverse = Optional.extract(traverse, binary=True) | |
254 |
|
255 | |||
255 | if root: |
|
256 | if root: | |
256 | # verify parent existance, if it's empty return an error |
|
257 | # verify parent existance, if it's empty return an error | |
257 | parent = RepoGroup.get_by_group_name(root) |
|
258 | parent = RepoGroup.get_by_group_name(root) | |
258 | if not parent: |
|
259 | if not parent: | |
259 | raise JSONRPCError( |
|
260 | raise JSONRPCError( | |
260 | 'Root repository group `{}` does not exist'.format(root)) |
|
261 | 'Root repository group `{}` does not exist'.format(root)) | |
261 |
|
262 | |||
262 | if traverse: |
|
263 | if traverse: | |
263 | repos = RepoModel().get_repos_for_root(root=root, traverse=traverse) |
|
264 | repos = RepoModel().get_repos_for_root(root=root, traverse=traverse) | |
264 | else: |
|
265 | else: | |
265 | repos = RepoModel().get_repos_for_root(root=parent) |
|
266 | repos = RepoModel().get_repos_for_root(root=parent) | |
266 | else: |
|
267 | else: | |
267 | if traverse: |
|
268 | if traverse: | |
268 | repos = RepoModel().get_all() |
|
269 | repos = RepoModel().get_all() | |
269 | else: |
|
270 | else: | |
270 | # return just top-level |
|
271 | # return just top-level | |
271 | repos = RepoModel().get_repos_for_root(root=None) |
|
272 | repos = RepoModel().get_repos_for_root(root=None) | |
272 |
|
273 | |||
273 | repo_list = RepoList(repos, perm_set=_perms, extra_kwargs=extras) |
|
274 | repo_list = RepoList(repos, perm_set=_perms, extra_kwargs=extras) | |
274 | return [repo.get_api_data(include_secrets=include_secrets) |
|
275 | return [repo.get_api_data(include_secrets=include_secrets) | |
275 | for repo in repo_list] |
|
276 | for repo in repo_list] | |
276 |
|
277 | |||
277 |
|
278 | |||
278 | @jsonrpc_method() |
|
279 | @jsonrpc_method() | |
279 | def get_repo_changeset(request, apiuser, repoid, revision, |
|
280 | def get_repo_changeset(request, apiuser, repoid, revision, | |
280 | details=Optional('basic')): |
|
281 | details=Optional('basic')): | |
281 | """ |
|
282 | """ | |
282 | Returns information about a changeset. |
|
283 | Returns information about a changeset. | |
283 |
|
284 | |||
284 | Additionally parameters define the amount of details returned by |
|
285 | Additionally parameters define the amount of details returned by | |
285 | this function. |
|
286 | this function. | |
286 |
|
287 | |||
287 | This command can only be run using an |authtoken| with admin rights, |
|
288 | This command can only be run using an |authtoken| with admin rights, | |
288 | or users with at least read rights to the |repo|. |
|
289 | or users with at least read rights to the |repo|. | |
289 |
|
290 | |||
290 | :param apiuser: This is filled automatically from the |authtoken|. |
|
291 | :param apiuser: This is filled automatically from the |authtoken|. | |
291 | :type apiuser: AuthUser |
|
292 | :type apiuser: AuthUser | |
292 | :param repoid: The repository name or repository id |
|
293 | :param repoid: The repository name or repository id | |
293 | :type repoid: str or int |
|
294 | :type repoid: str or int | |
294 | :param revision: revision for which listing should be done |
|
295 | :param revision: revision for which listing should be done | |
295 | :type revision: str |
|
296 | :type revision: str | |
296 | :param details: details can be 'basic|extended|full' full gives diff |
|
297 | :param details: details can be 'basic|extended|full' full gives diff | |
297 | info details like the diff itself, and number of changed files etc. |
|
298 | info details like the diff itself, and number of changed files etc. | |
298 | :type details: Optional(str) |
|
299 | :type details: Optional(str) | |
299 |
|
300 | |||
300 | """ |
|
301 | """ | |
301 | repo = get_repo_or_error(repoid) |
|
302 | repo = get_repo_or_error(repoid) | |
302 | if not has_superadmin_permission(apiuser): |
|
303 | if not has_superadmin_permission(apiuser): | |
303 | _perms = ( |
|
304 | _perms = ( | |
304 | 'repository.admin', 'repository.write', 'repository.read',) |
|
305 | 'repository.admin', 'repository.write', 'repository.read',) | |
305 | validate_repo_permissions(apiuser, repoid, repo, _perms) |
|
306 | validate_repo_permissions(apiuser, repoid, repo, _perms) | |
306 |
|
307 | |||
307 | changes_details = Optional.extract(details) |
|
308 | changes_details = Optional.extract(details) | |
308 | _changes_details_types = ['basic', 'extended', 'full'] |
|
309 | _changes_details_types = ['basic', 'extended', 'full'] | |
309 | if changes_details not in _changes_details_types: |
|
310 | if changes_details not in _changes_details_types: | |
310 | raise JSONRPCError( |
|
311 | raise JSONRPCError( | |
311 | 'ret_type must be one of %s' % ( |
|
312 | 'ret_type must be one of %s' % ( | |
312 | ','.join(_changes_details_types))) |
|
313 | ','.join(_changes_details_types))) | |
313 |
|
314 | |||
314 | pre_load = ['author', 'branch', 'date', 'message', 'parents', |
|
315 | pre_load = ['author', 'branch', 'date', 'message', 'parents', | |
315 | 'status', '_commit', '_file_paths'] |
|
316 | 'status', '_commit', '_file_paths'] | |
316 |
|
317 | |||
317 | try: |
|
318 | try: | |
318 | cs = repo.get_commit(commit_id=revision, pre_load=pre_load) |
|
319 | cs = repo.get_commit(commit_id=revision, pre_load=pre_load) | |
319 | except TypeError as e: |
|
320 | except TypeError as e: | |
320 | raise JSONRPCError(safe_str(e)) |
|
321 | raise JSONRPCError(safe_str(e)) | |
321 | _cs_json = cs.__json__() |
|
322 | _cs_json = cs.__json__() | |
322 | _cs_json['diff'] = build_commit_data(cs, changes_details) |
|
323 | _cs_json['diff'] = build_commit_data(cs, changes_details) | |
323 | if changes_details == 'full': |
|
324 | if changes_details == 'full': | |
324 | _cs_json['refs'] = cs._get_refs() |
|
325 | _cs_json['refs'] = cs._get_refs() | |
325 | return _cs_json |
|
326 | return _cs_json | |
326 |
|
327 | |||
327 |
|
328 | |||
328 | @jsonrpc_method() |
|
329 | @jsonrpc_method() | |
329 | def get_repo_changesets(request, apiuser, repoid, start_rev, limit, |
|
330 | def get_repo_changesets(request, apiuser, repoid, start_rev, limit, | |
330 | details=Optional('basic')): |
|
331 | details=Optional('basic')): | |
331 | """ |
|
332 | """ | |
332 | Returns a set of commits limited by the number starting |
|
333 | Returns a set of commits limited by the number starting | |
333 | from the `start_rev` option. |
|
334 | from the `start_rev` option. | |
334 |
|
335 | |||
335 | Additional parameters define the amount of details returned by this |
|
336 | Additional parameters define the amount of details returned by this | |
336 | function. |
|
337 | function. | |
337 |
|
338 | |||
338 | This command can only be run using an |authtoken| with admin rights, |
|
339 | This command can only be run using an |authtoken| with admin rights, | |
339 | or users with at least read rights to |repos|. |
|
340 | or users with at least read rights to |repos|. | |
340 |
|
341 | |||
341 | :param apiuser: This is filled automatically from the |authtoken|. |
|
342 | :param apiuser: This is filled automatically from the |authtoken|. | |
342 | :type apiuser: AuthUser |
|
343 | :type apiuser: AuthUser | |
343 | :param repoid: The repository name or repository ID. |
|
344 | :param repoid: The repository name or repository ID. | |
344 | :type repoid: str or int |
|
345 | :type repoid: str or int | |
345 | :param start_rev: The starting revision from where to get changesets. |
|
346 | :param start_rev: The starting revision from where to get changesets. | |
346 | :type start_rev: str |
|
347 | :type start_rev: str | |
347 | :param limit: Limit the number of commits to this amount |
|
348 | :param limit: Limit the number of commits to this amount | |
348 | :type limit: str or int |
|
349 | :type limit: str or int | |
349 | :param details: Set the level of detail returned. Valid option are: |
|
350 | :param details: Set the level of detail returned. Valid option are: | |
350 | ``basic``, ``extended`` and ``full``. |
|
351 | ``basic``, ``extended`` and ``full``. | |
351 | :type details: Optional(str) |
|
352 | :type details: Optional(str) | |
352 |
|
353 | |||
353 | .. note:: |
|
354 | .. note:: | |
354 |
|
355 | |||
355 | Setting the parameter `details` to the value ``full`` is extensive |
|
356 | Setting the parameter `details` to the value ``full`` is extensive | |
356 | and returns details like the diff itself, and the number |
|
357 | and returns details like the diff itself, and the number | |
357 | of changed files. |
|
358 | of changed files. | |
358 |
|
359 | |||
359 | """ |
|
360 | """ | |
360 | repo = get_repo_or_error(repoid) |
|
361 | repo = get_repo_or_error(repoid) | |
361 | if not has_superadmin_permission(apiuser): |
|
362 | if not has_superadmin_permission(apiuser): | |
362 | _perms = ( |
|
363 | _perms = ( | |
363 | 'repository.admin', 'repository.write', 'repository.read',) |
|
364 | 'repository.admin', 'repository.write', 'repository.read',) | |
364 | validate_repo_permissions(apiuser, repoid, repo, _perms) |
|
365 | validate_repo_permissions(apiuser, repoid, repo, _perms) | |
365 |
|
366 | |||
366 | changes_details = Optional.extract(details) |
|
367 | changes_details = Optional.extract(details) | |
367 | _changes_details_types = ['basic', 'extended', 'full'] |
|
368 | _changes_details_types = ['basic', 'extended', 'full'] | |
368 | if changes_details not in _changes_details_types: |
|
369 | if changes_details not in _changes_details_types: | |
369 | raise JSONRPCError( |
|
370 | raise JSONRPCError( | |
370 | 'ret_type must be one of %s' % ( |
|
371 | 'ret_type must be one of %s' % ( | |
371 | ','.join(_changes_details_types))) |
|
372 | ','.join(_changes_details_types))) | |
372 |
|
373 | |||
373 | limit = int(limit) |
|
374 | limit = int(limit) | |
374 | pre_load = ['author', 'branch', 'date', 'message', 'parents', |
|
375 | pre_load = ['author', 'branch', 'date', 'message', 'parents', | |
375 | 'status', '_commit', '_file_paths'] |
|
376 | 'status', '_commit', '_file_paths'] | |
376 |
|
377 | |||
377 | vcs_repo = repo.scm_instance() |
|
378 | vcs_repo = repo.scm_instance() | |
378 | # SVN needs a special case to distinguish its index and commit id |
|
379 | # SVN needs a special case to distinguish its index and commit id | |
379 | if vcs_repo and vcs_repo.alias == 'svn' and (start_rev == '0'): |
|
380 | if vcs_repo and vcs_repo.alias == 'svn' and (start_rev == '0'): | |
380 | start_rev = vcs_repo.commit_ids[0] |
|
381 | start_rev = vcs_repo.commit_ids[0] | |
381 |
|
382 | |||
382 | try: |
|
383 | try: | |
383 | commits = vcs_repo.get_commits( |
|
384 | commits = vcs_repo.get_commits( | |
384 | start_id=start_rev, pre_load=pre_load, translate_tags=False) |
|
385 | start_id=start_rev, pre_load=pre_load, translate_tags=False) | |
385 | except TypeError as e: |
|
386 | except TypeError as e: | |
386 | raise JSONRPCError(safe_str(e)) |
|
387 | raise JSONRPCError(safe_str(e)) | |
387 | except Exception: |
|
388 | except Exception: | |
388 | log.exception('Fetching of commits failed') |
|
389 | log.exception('Fetching of commits failed') | |
389 | raise JSONRPCError('Error occurred during commit fetching') |
|
390 | raise JSONRPCError('Error occurred during commit fetching') | |
390 |
|
391 | |||
391 | ret = [] |
|
392 | ret = [] | |
392 | for cnt, commit in enumerate(commits): |
|
393 | for cnt, commit in enumerate(commits): | |
393 | if cnt >= limit != -1: |
|
394 | if cnt >= limit != -1: | |
394 | break |
|
395 | break | |
395 | _cs_json = commit.__json__() |
|
396 | _cs_json = commit.__json__() | |
396 | _cs_json['diff'] = build_commit_data(commit, changes_details) |
|
397 | _cs_json['diff'] = build_commit_data(commit, changes_details) | |
397 | if changes_details == 'full': |
|
398 | if changes_details == 'full': | |
398 | _cs_json['refs'] = { |
|
399 | _cs_json['refs'] = { | |
399 | 'branches': [commit.branch], |
|
400 | 'branches': [commit.branch], | |
400 | 'bookmarks': getattr(commit, 'bookmarks', []), |
|
401 | 'bookmarks': getattr(commit, 'bookmarks', []), | |
401 | 'tags': commit.tags |
|
402 | 'tags': commit.tags | |
402 | } |
|
403 | } | |
403 | ret.append(_cs_json) |
|
404 | ret.append(_cs_json) | |
404 | return ret |
|
405 | return ret | |
405 |
|
406 | |||
406 |
|
407 | |||
407 | @jsonrpc_method() |
|
408 | @jsonrpc_method() | |
408 | def get_repo_nodes(request, apiuser, repoid, revision, root_path, |
|
409 | def get_repo_nodes(request, apiuser, repoid, revision, root_path, | |
409 | ret_type=Optional('all'), details=Optional('basic'), |
|
410 | ret_type=Optional('all'), details=Optional('basic'), | |
410 | max_file_bytes=Optional(None)): |
|
411 | max_file_bytes=Optional(None)): | |
411 | """ |
|
412 | """ | |
412 | Returns a list of nodes and children in a flat list for a given |
|
413 | Returns a list of nodes and children in a flat list for a given | |
413 | path at given revision. |
|
414 | path at given revision. | |
414 |
|
415 | |||
415 | It's possible to specify ret_type to show only `files` or `dirs`. |
|
416 | It's possible to specify ret_type to show only `files` or `dirs`. | |
416 |
|
417 | |||
417 | This command can only be run using an |authtoken| with admin rights, |
|
418 | This command can only be run using an |authtoken| with admin rights, | |
418 | or users with at least read rights to |repos|. |
|
419 | or users with at least read rights to |repos|. | |
419 |
|
420 | |||
420 | :param apiuser: This is filled automatically from the |authtoken|. |
|
421 | :param apiuser: This is filled automatically from the |authtoken|. | |
421 | :type apiuser: AuthUser |
|
422 | :type apiuser: AuthUser | |
422 | :param repoid: The repository name or repository ID. |
|
423 | :param repoid: The repository name or repository ID. | |
423 | :type repoid: str or int |
|
424 | :type repoid: str or int | |
424 | :param revision: The revision for which listing should be done. |
|
425 | :param revision: The revision for which listing should be done. | |
425 | :type revision: str |
|
426 | :type revision: str | |
426 | :param root_path: The path from which to start displaying. |
|
427 | :param root_path: The path from which to start displaying. | |
427 | :type root_path: str |
|
428 | :type root_path: str | |
428 | :param ret_type: Set the return type. Valid options are |
|
429 | :param ret_type: Set the return type. Valid options are | |
429 | ``all`` (default), ``files`` and ``dirs``. |
|
430 | ``all`` (default), ``files`` and ``dirs``. | |
430 | :type ret_type: Optional(str) |
|
431 | :type ret_type: Optional(str) | |
431 | :param details: Returns extended information about nodes, such as |
|
432 | :param details: Returns extended information about nodes, such as | |
432 | md5, binary, and or content. |
|
433 | md5, binary, and or content. | |
433 | The valid options are ``basic`` and ``full``. |
|
434 | The valid options are ``basic`` and ``full``. | |
434 | :type details: Optional(str) |
|
435 | :type details: Optional(str) | |
435 | :param max_file_bytes: Only return file content under this file size bytes |
|
436 | :param max_file_bytes: Only return file content under this file size bytes | |
436 | :type details: Optional(int) |
|
437 | :type details: Optional(int) | |
437 |
|
438 | |||
438 | Example output: |
|
439 | Example output: | |
439 |
|
440 | |||
440 | .. code-block:: bash |
|
441 | .. code-block:: bash | |
441 |
|
442 | |||
442 | id : <id_given_in_input> |
|
443 | id : <id_given_in_input> | |
443 | result: [ |
|
444 | result: [ | |
444 | { |
|
445 | { | |
445 | "binary": false, |
|
446 | "binary": false, | |
446 | "content": "File line\nLine2\n", |
|
447 | "content": "File line\nLine2\n", | |
447 | "extension": "md", |
|
448 | "extension": "md", | |
448 | "lines": 2, |
|
449 | "lines": 2, | |
449 | "md5": "059fa5d29b19c0657e384749480f6422", |
|
450 | "md5": "059fa5d29b19c0657e384749480f6422", | |
450 | "mimetype": "text/x-minidsrc", |
|
451 | "mimetype": "text/x-minidsrc", | |
451 | "name": "file.md", |
|
452 | "name": "file.md", | |
452 | "size": 580, |
|
453 | "size": 580, | |
453 | "type": "file" |
|
454 | "type": "file" | |
454 | }, |
|
455 | }, | |
455 | ... |
|
456 | ... | |
456 | ] |
|
457 | ] | |
457 | error: null |
|
458 | error: null | |
458 | """ |
|
459 | """ | |
459 |
|
460 | |||
460 | repo = get_repo_or_error(repoid) |
|
461 | repo = get_repo_or_error(repoid) | |
461 | if not has_superadmin_permission(apiuser): |
|
462 | if not has_superadmin_permission(apiuser): | |
462 | _perms = ('repository.admin', 'repository.write', 'repository.read',) |
|
463 | _perms = ('repository.admin', 'repository.write', 'repository.read',) | |
463 | validate_repo_permissions(apiuser, repoid, repo, _perms) |
|
464 | validate_repo_permissions(apiuser, repoid, repo, _perms) | |
464 |
|
465 | |||
465 | ret_type = Optional.extract(ret_type) |
|
466 | ret_type = Optional.extract(ret_type) | |
466 | details = Optional.extract(details) |
|
467 | details = Optional.extract(details) | |
467 | _extended_types = ['basic', 'full'] |
|
468 | _extended_types = ['basic', 'full'] | |
468 | if details not in _extended_types: |
|
469 | if details not in _extended_types: | |
469 | raise JSONRPCError('ret_type must be one of %s' % (','.join(_extended_types))) |
|
470 | raise JSONRPCError('ret_type must be one of %s' % (','.join(_extended_types))) | |
470 | extended_info = False |
|
471 | extended_info = False | |
471 | content = False |
|
472 | content = False | |
472 | if details == 'basic': |
|
473 | if details == 'basic': | |
473 | extended_info = True |
|
474 | extended_info = True | |
474 |
|
475 | |||
475 | if details == 'full': |
|
476 | if details == 'full': | |
476 | extended_info = content = True |
|
477 | extended_info = content = True | |
477 |
|
478 | |||
478 | _map = {} |
|
479 | _map = {} | |
479 | try: |
|
480 | try: | |
480 | # check if repo is not empty by any chance, skip quicker if it is. |
|
481 | # check if repo is not empty by any chance, skip quicker if it is. | |
481 | _scm = repo.scm_instance() |
|
482 | _scm = repo.scm_instance() | |
482 | if _scm.is_empty(): |
|
483 | if _scm.is_empty(): | |
483 | return [] |
|
484 | return [] | |
484 |
|
485 | |||
485 | _d, _f = ScmModel().get_nodes( |
|
486 | _d, _f = ScmModel().get_nodes( | |
486 | repo, revision, root_path, flat=False, |
|
487 | repo, revision, root_path, flat=False, | |
487 | extended_info=extended_info, content=content, |
|
488 | extended_info=extended_info, content=content, | |
488 | max_file_bytes=max_file_bytes) |
|
489 | max_file_bytes=max_file_bytes) | |
489 | _map = { |
|
490 | _map = { | |
490 | 'all': _d + _f, |
|
491 | 'all': _d + _f, | |
491 | 'files': _f, |
|
492 | 'files': _f, | |
492 | 'dirs': _d, |
|
493 | 'dirs': _d, | |
493 | } |
|
494 | } | |
494 | return _map[ret_type] |
|
495 | return _map[ret_type] | |
495 | except KeyError: |
|
496 | except KeyError: | |
496 | raise JSONRPCError( |
|
497 | raise JSONRPCError( | |
497 | 'ret_type must be one of %s' % (','.join(sorted(_map.keys())))) |
|
498 | 'ret_type must be one of %s' % (','.join(sorted(_map.keys())))) | |
498 | except Exception: |
|
499 | except Exception: | |
499 | log.exception("Exception occurred while trying to get repo nodes") |
|
500 | log.exception("Exception occurred while trying to get repo nodes") | |
500 | raise JSONRPCError( |
|
501 | raise JSONRPCError( | |
501 | 'failed to get repo: `%s` nodes' % repo.repo_name |
|
502 | 'failed to get repo: `%s` nodes' % repo.repo_name | |
502 | ) |
|
503 | ) | |
503 |
|
504 | |||
504 |
|
505 | |||
505 | @jsonrpc_method() |
|
506 | @jsonrpc_method() | |
506 | def get_repo_file(request, apiuser, repoid, commit_id, file_path, |
|
507 | def get_repo_file(request, apiuser, repoid, commit_id, file_path, | |
507 | max_file_bytes=Optional(None), details=Optional('basic'), |
|
508 | max_file_bytes=Optional(None), details=Optional('basic'), | |
508 | cache=Optional(True)): |
|
509 | cache=Optional(True)): | |
509 | """ |
|
510 | """ | |
510 | Returns a single file from repository at given revision. |
|
511 | Returns a single file from repository at given revision. | |
511 |
|
512 | |||
512 | This command can only be run using an |authtoken| with admin rights, |
|
513 | This command can only be run using an |authtoken| with admin rights, | |
513 | or users with at least read rights to |repos|. |
|
514 | or users with at least read rights to |repos|. | |
514 |
|
515 | |||
515 | :param apiuser: This is filled automatically from the |authtoken|. |
|
516 | :param apiuser: This is filled automatically from the |authtoken|. | |
516 | :type apiuser: AuthUser |
|
517 | :type apiuser: AuthUser | |
517 | :param repoid: The repository name or repository ID. |
|
518 | :param repoid: The repository name or repository ID. | |
518 | :type repoid: str or int |
|
519 | :type repoid: str or int | |
519 | :param commit_id: The revision for which listing should be done. |
|
520 | :param commit_id: The revision for which listing should be done. | |
520 | :type commit_id: str |
|
521 | :type commit_id: str | |
521 | :param file_path: The path from which to start displaying. |
|
522 | :param file_path: The path from which to start displaying. | |
522 | :type file_path: str |
|
523 | :type file_path: str | |
523 | :param details: Returns different set of information about nodes. |
|
524 | :param details: Returns different set of information about nodes. | |
524 | The valid options are ``minimal`` ``basic`` and ``full``. |
|
525 | The valid options are ``minimal`` ``basic`` and ``full``. | |
525 | :type details: Optional(str) |
|
526 | :type details: Optional(str) | |
526 | :param max_file_bytes: Only return file content under this file size bytes |
|
527 | :param max_file_bytes: Only return file content under this file size bytes | |
527 | :type max_file_bytes: Optional(int) |
|
528 | :type max_file_bytes: Optional(int) | |
528 | :param cache: Use internal caches for fetching files. If disabled fetching |
|
529 | :param cache: Use internal caches for fetching files. If disabled fetching | |
529 | files is slower but more memory efficient |
|
530 | files is slower but more memory efficient | |
530 | :type cache: Optional(bool) |
|
531 | :type cache: Optional(bool) | |
531 | Example output: |
|
532 | Example output: | |
532 |
|
533 | |||
533 | .. code-block:: bash |
|
534 | .. code-block:: bash | |
534 |
|
535 | |||
535 | id : <id_given_in_input> |
|
536 | id : <id_given_in_input> | |
536 | result: { |
|
537 | result: { | |
537 | "binary": false, |
|
538 | "binary": false, | |
538 | "extension": "py", |
|
539 | "extension": "py", | |
539 | "lines": 35, |
|
540 | "lines": 35, | |
540 | "content": "....", |
|
541 | "content": "....", | |
541 | "md5": "76318336366b0f17ee249e11b0c99c41", |
|
542 | "md5": "76318336366b0f17ee249e11b0c99c41", | |
542 | "mimetype": "text/x-python", |
|
543 | "mimetype": "text/x-python", | |
543 | "name": "python.py", |
|
544 | "name": "python.py", | |
544 | "size": 817, |
|
545 | "size": 817, | |
545 | "type": "file", |
|
546 | "type": "file", | |
546 | } |
|
547 | } | |
547 | error: null |
|
548 | error: null | |
548 | """ |
|
549 | """ | |
549 |
|
550 | |||
550 | repo = get_repo_or_error(repoid) |
|
551 | repo = get_repo_or_error(repoid) | |
551 | if not has_superadmin_permission(apiuser): |
|
552 | if not has_superadmin_permission(apiuser): | |
552 | _perms = ('repository.admin', 'repository.write', 'repository.read',) |
|
553 | _perms = ('repository.admin', 'repository.write', 'repository.read',) | |
553 | validate_repo_permissions(apiuser, repoid, repo, _perms) |
|
554 | validate_repo_permissions(apiuser, repoid, repo, _perms) | |
554 |
|
555 | |||
555 | cache = Optional.extract(cache, binary=True) |
|
556 | cache = Optional.extract(cache, binary=True) | |
556 | details = Optional.extract(details) |
|
557 | details = Optional.extract(details) | |
557 | _extended_types = ['minimal', 'minimal+search', 'basic', 'full'] |
|
558 | _extended_types = ['minimal', 'minimal+search', 'basic', 'full'] | |
558 | if details not in _extended_types: |
|
559 | if details not in _extended_types: | |
559 | raise JSONRPCError( |
|
560 | raise JSONRPCError( | |
560 | 'ret_type must be one of %s, got %s' % (','.join(_extended_types)), details) |
|
561 | 'ret_type must be one of %s, got %s' % (','.join(_extended_types)), details) | |
561 | extended_info = False |
|
562 | extended_info = False | |
562 | content = False |
|
563 | content = False | |
563 |
|
564 | |||
564 | if details == 'minimal': |
|
565 | if details == 'minimal': | |
565 | extended_info = False |
|
566 | extended_info = False | |
566 |
|
567 | |||
567 | elif details == 'basic': |
|
568 | elif details == 'basic': | |
568 | extended_info = True |
|
569 | extended_info = True | |
569 |
|
570 | |||
570 | elif details == 'full': |
|
571 | elif details == 'full': | |
571 | extended_info = content = True |
|
572 | extended_info = content = True | |
572 |
|
573 | |||
573 | try: |
|
574 | try: | |
574 | # check if repo is not empty by any chance, skip quicker if it is. |
|
575 | # check if repo is not empty by any chance, skip quicker if it is. | |
575 | _scm = repo.scm_instance() |
|
576 | _scm = repo.scm_instance() | |
576 | if _scm.is_empty(): |
|
577 | if _scm.is_empty(): | |
577 | return None |
|
578 | return None | |
578 |
|
579 | |||
579 | node = ScmModel().get_node( |
|
580 | node = ScmModel().get_node( | |
580 | repo, commit_id, file_path, extended_info=extended_info, |
|
581 | repo, commit_id, file_path, extended_info=extended_info, | |
581 | content=content, max_file_bytes=max_file_bytes, cache=cache) |
|
582 | content=content, max_file_bytes=max_file_bytes, cache=cache) | |
582 |
|
583 | except NodeDoesNotExistError: | ||
|
584 | raise JSONRPCError('There is no file in repo: `{}` at path `{}` for commit: `{}`'.format( | |||
|
585 | repo.repo_name, file_path, commit_id)) | |||
583 | except Exception: |
|
586 | except Exception: | |
584 | log.exception("Exception occurred while trying to get repo %s file", |
|
587 | log.exception("Exception occurred while trying to get repo %s file", | |
585 | repo.repo_name) |
|
588 | repo.repo_name) | |
586 | raise JSONRPCError('failed to get repo: `{}` file at path {}'.format( |
|
589 | raise JSONRPCError('failed to get repo: `{}` file at path {}'.format( | |
587 | repo.repo_name, file_path)) |
|
590 | repo.repo_name, file_path)) | |
588 |
|
591 | |||
589 | return node |
|
592 | return node | |
590 |
|
593 | |||
591 |
|
594 | |||
592 | @jsonrpc_method() |
|
595 | @jsonrpc_method() | |
593 | def get_repo_fts_tree(request, apiuser, repoid, commit_id, root_path): |
|
596 | def get_repo_fts_tree(request, apiuser, repoid, commit_id, root_path): | |
594 | """ |
|
597 | """ | |
595 | Returns a list of tree nodes for path at given revision. This api is built |
|
598 | Returns a list of tree nodes for path at given revision. This api is built | |
596 | strictly for usage in full text search building, and shouldn't be consumed |
|
599 | strictly for usage in full text search building, and shouldn't be consumed | |
597 |
|
600 | |||
598 | This command can only be run using an |authtoken| with admin rights, |
|
601 | This command can only be run using an |authtoken| with admin rights, | |
599 | or users with at least read rights to |repos|. |
|
602 | or users with at least read rights to |repos|. | |
600 |
|
603 | |||
601 | """ |
|
604 | """ | |
602 |
|
605 | |||
603 | repo = get_repo_or_error(repoid) |
|
606 | repo = get_repo_or_error(repoid) | |
604 | if not has_superadmin_permission(apiuser): |
|
607 | if not has_superadmin_permission(apiuser): | |
605 | _perms = ('repository.admin', 'repository.write', 'repository.read',) |
|
608 | _perms = ('repository.admin', 'repository.write', 'repository.read',) | |
606 | validate_repo_permissions(apiuser, repoid, repo, _perms) |
|
609 | validate_repo_permissions(apiuser, repoid, repo, _perms) | |
607 |
|
610 | |||
608 | repo_id = repo.repo_id |
|
611 | repo_id = repo.repo_id | |
609 | cache_seconds = safe_int(rhodecode.CONFIG.get('rc_cache.cache_repo.expiration_time')) |
|
612 | cache_seconds = safe_int(rhodecode.CONFIG.get('rc_cache.cache_repo.expiration_time')) | |
610 | cache_on = cache_seconds > 0 |
|
613 | cache_on = cache_seconds > 0 | |
611 |
|
614 | |||
612 | cache_namespace_uid = 'cache_repo.{}'.format(repo_id) |
|
615 | cache_namespace_uid = 'cache_repo.{}'.format(repo_id) | |
613 | region = rc_cache.get_or_create_region('cache_repo', cache_namespace_uid) |
|
616 | region = rc_cache.get_or_create_region('cache_repo', cache_namespace_uid) | |
614 |
|
617 | |||
615 | @region.conditional_cache_on_arguments(namespace=cache_namespace_uid, |
|
618 | @region.conditional_cache_on_arguments(namespace=cache_namespace_uid, | |
616 | condition=cache_on) |
|
619 | condition=cache_on) | |
617 | def compute_fts_tree(repo_id, commit_id, root_path, cache_ver): |
|
620 | def compute_fts_tree(repo_id, commit_id, root_path, cache_ver): | |
618 | return ScmModel().get_fts_data(repo_id, commit_id, root_path) |
|
621 | return ScmModel().get_fts_data(repo_id, commit_id, root_path) | |
619 |
|
622 | |||
620 | try: |
|
623 | try: | |
621 | # check if repo is not empty by any chance, skip quicker if it is. |
|
624 | # check if repo is not empty by any chance, skip quicker if it is. | |
622 | _scm = repo.scm_instance() |
|
625 | _scm = repo.scm_instance() | |
623 | if _scm.is_empty(): |
|
626 | if _scm.is_empty(): | |
624 | return [] |
|
627 | return [] | |
625 | except RepositoryError: |
|
628 | except RepositoryError: | |