Show More
@@ -0,0 +1,103 b'' | |||||
|
1 | # -*- coding: utf-8 -*- | |||
|
2 | ||||
|
3 | # Copyright (C) 2016-2017 RhodeCode GmbH | |||
|
4 | # | |||
|
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 | |||
|
7 | # (only), as published by the Free Software Foundation. | |||
|
8 | # | |||
|
9 | # This program is distributed in the hope that it will be useful, | |||
|
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
|
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
|
12 | # GNU General Public License for more details. | |||
|
13 | # | |||
|
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/>. | |||
|
16 | # | |||
|
17 | # This program is dual-licensed. If you wish to learn more about the | |||
|
18 | # RhodeCode Enterprise Edition, including its added features, Support services, | |||
|
19 | # and proprietary license terms, please see https://rhodecode.com/licenses/ | |||
|
20 | ||||
|
21 | import json | |||
|
22 | ||||
|
23 | from . import assert_and_get_content | |||
|
24 | from rhodecode.tests import TestController | |||
|
25 | from rhodecode.tests.fixture import Fixture | |||
|
26 | from rhodecode.model.db import Repository | |||
|
27 | ||||
|
28 | fixture = Fixture() | |||
|
29 | ||||
|
30 | ||||
|
31 | def route_path(name, params=None, **kwargs): | |||
|
32 | import urllib | |||
|
33 | ||||
|
34 | base_url = { | |||
|
35 | 'repo_list_data': '/_repos', | |||
|
36 | }[name].format(**kwargs) | |||
|
37 | ||||
|
38 | if params: | |||
|
39 | base_url = '{}?{}'.format(base_url, urllib.urlencode(params)) | |||
|
40 | return base_url | |||
|
41 | ||||
|
42 | ||||
|
43 | class TestRepoListData(TestController): | |||
|
44 | ||||
|
45 | def test_returns_list_of_repos_and_groups(self, xhr_header): | |||
|
46 | self.log_user() | |||
|
47 | ||||
|
48 | response = self.app.get( | |||
|
49 | route_path('repo_list_data'), | |||
|
50 | extra_environ=xhr_header, status=200) | |||
|
51 | result = json.loads(response.body)['results'] | |||
|
52 | ||||
|
53 | repos, groups, commits = assert_and_get_content(result) | |||
|
54 | ||||
|
55 | assert len(repos) == len(Repository.get_all()) | |||
|
56 | assert len(groups) == 0 | |||
|
57 | assert len(commits) == 0 | |||
|
58 | ||||
|
59 | def test_returns_list_of_repos_and_groups_filtered(self, xhr_header): | |||
|
60 | self.log_user() | |||
|
61 | ||||
|
62 | response = self.app.get( | |||
|
63 | route_path('repo_list_data'), | |||
|
64 | params={'query': 'vcs_test_git'}, | |||
|
65 | extra_environ=xhr_header, status=200) | |||
|
66 | result = json.loads(response.body)['results'] | |||
|
67 | ||||
|
68 | repos, groups, commits = assert_and_get_content(result) | |||
|
69 | ||||
|
70 | assert len(repos) == len(Repository.query().filter( | |||
|
71 | Repository.repo_name.ilike('%vcs_test_git%')).all()) | |||
|
72 | assert len(groups) == 0 | |||
|
73 | assert len(commits) == 0 | |||
|
74 | ||||
|
75 | def test_returns_list_of_repos_and_groups_filtered_with_type(self, xhr_header): | |||
|
76 | self.log_user() | |||
|
77 | ||||
|
78 | response = self.app.get( | |||
|
79 | route_path('repo_list_data'), | |||
|
80 | params={'query': 'vcs_test_git', 'repo_type': 'git'}, | |||
|
81 | extra_environ=xhr_header, status=200) | |||
|
82 | result = json.loads(response.body)['results'] | |||
|
83 | ||||
|
84 | repos, groups, commits = assert_and_get_content(result) | |||
|
85 | ||||
|
86 | assert len(repos) == len(Repository.query().filter( | |||
|
87 | Repository.repo_name.ilike('%vcs_test_git%')).all()) | |||
|
88 | assert len(groups) == 0 | |||
|
89 | assert len(commits) == 0 | |||
|
90 | ||||
|
91 | def test_returns_list_of_repos_non_ascii_query(self, xhr_header): | |||
|
92 | self.log_user() | |||
|
93 | response = self.app.get( | |||
|
94 | route_path('repo_list_data'), | |||
|
95 | params={'query': 'ć_vcs_test_ą', 'repo_type': 'git'}, | |||
|
96 | extra_environ=xhr_header, status=200) | |||
|
97 | result = json.loads(response.body)['results'] | |||
|
98 | ||||
|
99 | repos, groups, commits = assert_and_get_content(result) | |||
|
100 | ||||
|
101 | assert len(repos) == 0 | |||
|
102 | assert len(groups) == 0 | |||
|
103 | assert len(commits) == 0 |
@@ -29,5 +29,9 b' def includeme(config):' | |||||
29 | name='user_group_autocomplete_data', |
|
29 | name='user_group_autocomplete_data', | |
30 | pattern='/_user_groups') |
|
30 | pattern='/_user_groups') | |
31 |
|
31 | |||
|
32 | config.add_route( | |||
|
33 | name='repo_list_data', | |||
|
34 | pattern='/_repos') | |||
|
35 | ||||
32 | # Scan module for configuration decorators. |
|
36 | # Scan module for configuration decorators. | |
33 | config.scan() |
|
37 | config.scan() |
@@ -17,3 +17,24 b'' | |||||
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 | ||||
|
21 | ||||
|
22 | def assert_and_get_content(result): | |||
|
23 | repos = [] | |||
|
24 | groups = [] | |||
|
25 | commits = [] | |||
|
26 | for data in result: | |||
|
27 | for data_item in data['children']: | |||
|
28 | assert data_item['id'] | |||
|
29 | assert data_item['text'] | |||
|
30 | assert data_item['url'] | |||
|
31 | if data_item['type'] == 'repo': | |||
|
32 | repos.append(data_item) | |||
|
33 | elif data_item['type'] == 'group': | |||
|
34 | groups.append(data_item) | |||
|
35 | elif data_item['type'] == 'commit': | |||
|
36 | commits.append(data_item) | |||
|
37 | else: | |||
|
38 | raise Exception('invalid type %s' % data_item['type']) | |||
|
39 | ||||
|
40 | return repos, groups, commits No newline at end of file |
@@ -23,9 +23,13 b' import logging' | |||||
23 | from pyramid.view import view_config |
|
23 | from pyramid.view import view_config | |
24 |
|
24 | |||
25 | from rhodecode.apps._base import BaseAppView |
|
25 | from rhodecode.apps._base import BaseAppView | |
|
26 | from rhodecode.lib import helpers as h | |||
26 | from rhodecode.lib.auth import LoginRequired, NotAnonymous |
|
27 | from rhodecode.lib.auth import LoginRequired, NotAnonymous | |
27 | from rhodecode.lib.utils2 import str2bool |
|
28 | from rhodecode.lib.utils2 import safe_unicode, str2bool | |
|
29 | from rhodecode.model.db import func, Repository | |||
28 | from rhodecode.model.repo import RepoModel |
|
30 | from rhodecode.model.repo import RepoModel | |
|
31 | from rhodecode.model.scm import ScmModel | |||
|
32 | ||||
29 |
|
33 | |||
30 | log = logging.getLogger(__name__) |
|
34 | log = logging.getLogger(__name__) | |
31 |
|
35 | |||
@@ -79,3 +83,56 b' class HomeView(BaseAppView):' | |||||
79 | _user_groups = _user_groups |
|
83 | _user_groups = _user_groups | |
80 |
|
84 | |||
81 | return {'suggestions': _user_groups} |
|
85 | return {'suggestions': _user_groups} | |
|
86 | ||||
|
87 | def _get_repo_list(self, name_contains=None, repo_type=None, limit=20): | |||
|
88 | query = Repository.query()\ | |||
|
89 | .order_by(func.length(Repository.repo_name))\ | |||
|
90 | .order_by(Repository.repo_name) | |||
|
91 | ||||
|
92 | if repo_type: | |||
|
93 | query = query.filter(Repository.repo_type == repo_type) | |||
|
94 | ||||
|
95 | if name_contains: | |||
|
96 | ilike_expression = u'%{}%'.format(safe_unicode(name_contains)) | |||
|
97 | query = query.filter( | |||
|
98 | Repository.repo_name.ilike(ilike_expression)) | |||
|
99 | query = query.limit(limit) | |||
|
100 | ||||
|
101 | all_repos = query.all() | |||
|
102 | # permission checks are inside this function | |||
|
103 | repo_iter = ScmModel().get_repos(all_repos) | |||
|
104 | return [ | |||
|
105 | { | |||
|
106 | 'id': obj['name'], | |||
|
107 | 'text': obj['name'], | |||
|
108 | 'type': 'repo', | |||
|
109 | 'obj': obj['dbrepo'], | |||
|
110 | 'url': h.url('summary_home', repo_name=obj['name']) | |||
|
111 | } | |||
|
112 | for obj in repo_iter] | |||
|
113 | ||||
|
114 | @LoginRequired() | |||
|
115 | @view_config( | |||
|
116 | route_name='repo_list_data', request_method='GET', | |||
|
117 | renderer='json_ext', xhr=True) | |||
|
118 | def repo_list_data(self): | |||
|
119 | _ = self.request.translate | |||
|
120 | ||||
|
121 | query = self.request.GET.get('query') | |||
|
122 | repo_type = self.request.GET.get('repo_type') | |||
|
123 | log.debug('generating repo list, query:%s, repo_type:%s', | |||
|
124 | query, repo_type) | |||
|
125 | ||||
|
126 | res = [] | |||
|
127 | repos = self._get_repo_list(query, repo_type=repo_type) | |||
|
128 | if repos: | |||
|
129 | res.append({ | |||
|
130 | 'text': _('Repositories'), | |||
|
131 | 'children': repos | |||
|
132 | }) | |||
|
133 | ||||
|
134 | data = { | |||
|
135 | 'more': False, | |||
|
136 | 'results': res | |||
|
137 | } | |||
|
138 | return data |
@@ -190,8 +190,6 b' def make_map(config):' | |||||
190 | rmap.connect('home', '/', controller='home', action='index', jsroute=True) |
|
190 | rmap.connect('home', '/', controller='home', action='index', jsroute=True) | |
191 | rmap.connect('goto_switcher_data', '/_goto_data', controller='home', |
|
191 | rmap.connect('goto_switcher_data', '/_goto_data', controller='home', | |
192 | action='goto_switcher_data') |
|
192 | action='goto_switcher_data') | |
193 | rmap.connect('repo_list_data', '/_repos', controller='home', |
|
|||
194 | action='repo_list_data') |
|
|||
195 |
|
193 | |||
196 | # TODO: johbo: Static links, to be replaced by our redirection mechanism |
|
194 | # TODO: johbo: Static links, to be replaced by our redirection mechanism | |
197 | rmap.connect('rst_help', |
|
195 | rmap.connect('rst_help', |
@@ -233,25 +233,3 b' class HomeController(BaseController):' | |||||
233 | 'results': res |
|
233 | 'results': res | |
234 | } |
|
234 | } | |
235 | return data |
|
235 | return data | |
236 |
|
||||
237 | @LoginRequired() |
|
|||
238 | @XHRRequired() |
|
|||
239 | @jsonify |
|
|||
240 | def repo_list_data(self): |
|
|||
241 | query = request.GET.get('query') |
|
|||
242 | repo_type = request.GET.get('repo_type') |
|
|||
243 | log.debug('generating repo list, query:%s', query) |
|
|||
244 |
|
||||
245 | res = [] |
|
|||
246 | repos = self._get_repo_list(query, repo_type=repo_type) |
|
|||
247 | if repos: |
|
|||
248 | res.append({ |
|
|||
249 | 'text': _('Repositories'), |
|
|||
250 | 'children': repos |
|
|||
251 | }) |
|
|||
252 |
|
||||
253 | data = { |
|
|||
254 | 'more': False, |
|
|||
255 | 'results': res |
|
|||
256 | } |
|
|||
257 | return data |
|
@@ -13,8 +13,6 b'' | |||||
13 | function registerRCRoutes() { |
|
13 | function registerRCRoutes() { | |
14 | // routes registration |
|
14 | // routes registration | |
15 | pyroutes.register('home', '/', []); |
|
15 | pyroutes.register('home', '/', []); | |
16 | pyroutes.register('user_autocomplete_data', '/_users', []); |
|
|||
17 | pyroutes.register('user_group_autocomplete_data', '/_user_groups', []); |
|
|||
18 | pyroutes.register('new_repo', '/_admin/create_repository', []); |
|
16 | pyroutes.register('new_repo', '/_admin/create_repository', []); | |
19 | pyroutes.register('edit_user', '/_admin/users/%(user_id)s/edit', ['user_id']); |
|
17 | pyroutes.register('edit_user', '/_admin/users/%(user_id)s/edit', ['user_id']); | |
20 | pyroutes.register('edit_user_group_members', '/_admin/user_groups/%(user_group_id)s/edit/members', ['user_group_id']); |
|
18 | pyroutes.register('edit_user_group_members', '/_admin/user_groups/%(user_group_id)s/edit/members', ['user_group_id']); | |
@@ -95,6 +93,9 b' function registerRCRoutes() {' | |||||
95 | pyroutes.register('register', '/_admin/register', []); |
|
93 | pyroutes.register('register', '/_admin/register', []); | |
96 | pyroutes.register('reset_password', '/_admin/password_reset', []); |
|
94 | pyroutes.register('reset_password', '/_admin/password_reset', []); | |
97 | pyroutes.register('reset_password_confirmation', '/_admin/password_reset_confirmation', []); |
|
95 | pyroutes.register('reset_password_confirmation', '/_admin/password_reset_confirmation', []); | |
|
96 | pyroutes.register('user_autocomplete_data', '/_users', []); | |||
|
97 | pyroutes.register('user_group_autocomplete_data', '/_user_groups', []); | |||
|
98 | pyroutes.register('repo_list_data', '/_repos', []); | |||
98 | pyroutes.register('repo_maintenance', '/%(repo_name)s/maintenance', ['repo_name']); |
|
99 | pyroutes.register('repo_maintenance', '/%(repo_name)s/maintenance', ['repo_name']); | |
99 | pyroutes.register('repo_maintenance_execute', '/%(repo_name)s/maintenance/execute', ['repo_name']); |
|
100 | pyroutes.register('repo_maintenance_execute', '/%(repo_name)s/maintenance/execute', ['repo_name']); | |
100 | pyroutes.register('strip', '/%(repo_name)s/strip', ['repo_name']); |
|
101 | pyroutes.register('strip', '/%(repo_name)s/strip', ['repo_name']); |
@@ -139,7 +139,7 b' var repoFilter = function(data) {' | |||||
139 | query.callback({results: cachedData.results}); |
|
139 | query.callback({results: cachedData.results}); | |
140 | } else { |
|
140 | } else { | |
141 | $.ajax({ |
|
141 | $.ajax({ | |
142 |
url: |
|
142 | url: pyroutes.url('repo_list_data'), | |
143 | data: {'query': query.term}, |
|
143 | data: {'query': query.term}, | |
144 | dataType: 'json', |
|
144 | dataType: 'json', | |
145 | type: 'GET', |
|
145 | type: 'GET', |
@@ -191,7 +191,7 b' var repoTypeFilter = function(data) {' | |||||
191 | query.callback({results: cachedData.results}); |
|
191 | query.callback({results: cachedData.results}); | |
192 | } else { |
|
192 | } else { | |
193 | $.ajax({ |
|
193 | $.ajax({ | |
194 |
url: |
|
194 | url: pyroutes.url('repo_list_data'), | |
195 | data: {'query': query.term, repo_type: '${c.repo_info.repo_type}'}, |
|
195 | data: {'query': query.term, repo_type: '${c.repo_info.repo_type}'}, | |
196 | dataType: 'json', |
|
196 | dataType: 'json', | |
197 | type: 'GET', |
|
197 | type: 'GET', |
@@ -136,7 +136,7 b' var repoFilter = function(data) {' | |||||
136 | query.callback({results: cachedData.results}); |
|
136 | query.callback({results: cachedData.results}); | |
137 | } else { |
|
137 | } else { | |
138 | $.ajax({ |
|
138 | $.ajax({ | |
139 |
url: |
|
139 | url: pyroutes.url('repo_list_data'), | |
140 | data: {'query': query.term}, |
|
140 | data: {'query': query.term}, | |
141 | dataType: 'json', |
|
141 | dataType: 'json', | |
142 | type: 'GET', |
|
142 | type: 'GET', |
@@ -255,65 +255,3 b' class TestGotoSwitcherData(TestControlle' | |||||
255 | test_groups = [x['text'] for x in groups[:4]] |
|
255 | test_groups = [x['text'] for x in groups[:4]] | |
256 | assert ['abc_repos', 'repos_abc', |
|
256 | assert ['abc_repos', 'repos_abc', | |
257 | 'forked-abc', 'forked-abc/a'] == test_groups |
|
257 | 'forked-abc', 'forked-abc/a'] == test_groups | |
258 |
|
||||
259 |
|
||||
260 | class TestRepoListData(TestController): |
|
|||
261 | def test_returns_list_of_repos_and_groups(self, user_util): |
|
|||
262 | self.log_user() |
|
|||
263 |
|
||||
264 | response = self.app.get( |
|
|||
265 | url(controller='home', action='repo_list_data'), |
|
|||
266 | headers={'X-REQUESTED-WITH': 'XMLHttpRequest', }, status=200) |
|
|||
267 | result = json.loads(response.body)['results'] |
|
|||
268 |
|
||||
269 | repos, groups, commits = assert_and_get_content(result) |
|
|||
270 |
|
||||
271 | assert len(repos) == len(Repository.get_all()) |
|
|||
272 | assert len(groups) == 0 |
|
|||
273 | assert len(commits) == 0 |
|
|||
274 |
|
||||
275 | def test_returns_list_of_repos_and_groups_filtered(self): |
|
|||
276 | self.log_user() |
|
|||
277 |
|
||||
278 | response = self.app.get( |
|
|||
279 | url(controller='home', action='repo_list_data'), |
|
|||
280 | headers={'X-REQUESTED-WITH': 'XMLHttpRequest', }, |
|
|||
281 | params={'query': 'vcs_test_git'}, status=200) |
|
|||
282 | result = json.loads(response.body)['results'] |
|
|||
283 |
|
||||
284 | repos, groups, commits = assert_and_get_content(result) |
|
|||
285 |
|
||||
286 | assert len(repos) == len(Repository.query().filter( |
|
|||
287 | Repository.repo_name.ilike('%vcs_test_git%')).all()) |
|
|||
288 | assert len(groups) == 0 |
|
|||
289 | assert len(commits) == 0 |
|
|||
290 |
|
||||
291 | def test_returns_list_of_repos_and_groups_filtered_with_type(self): |
|
|||
292 | self.log_user() |
|
|||
293 |
|
||||
294 | response = self.app.get( |
|
|||
295 | url(controller='home', action='repo_list_data'), |
|
|||
296 | headers={'X-REQUESTED-WITH': 'XMLHttpRequest', }, |
|
|||
297 | params={'query': 'vcs_test_git', 'repo_type': 'git'}, status=200) |
|
|||
298 | result = json.loads(response.body)['results'] |
|
|||
299 |
|
||||
300 | repos, groups, commits = assert_and_get_content(result) |
|
|||
301 |
|
||||
302 | assert len(repos) == len(Repository.query().filter( |
|
|||
303 | Repository.repo_name.ilike('%vcs_test_git%')).all()) |
|
|||
304 | assert len(groups) == 0 |
|
|||
305 | assert len(commits) == 0 |
|
|||
306 |
|
||||
307 | def test_returns_list_of_repos_non_ascii_query(self): |
|
|||
308 | self.log_user() |
|
|||
309 | response = self.app.get( |
|
|||
310 | url(controller='home', action='repo_list_data'), |
|
|||
311 | headers={'X-REQUESTED-WITH': 'XMLHttpRequest', }, |
|
|||
312 | params={'query': 'ć_vcs_test_ą', 'repo_type': 'git'}, status=200) |
|
|||
313 | result = json.loads(response.body)['results'] |
|
|||
314 |
|
||||
315 | repos, groups, commits = assert_and_get_content(result) |
|
|||
316 |
|
||||
317 | assert len(repos) == 0 |
|
|||
318 | assert len(groups) == 0 |
|
|||
319 | assert len(commits) == 0 |
|
General Comments 0
You need to be logged in to leave comments.
Login now