##// END OF EJS Templates
core: moved goto_switcher views into pyramid.
marcink -
r1668:6a5ff1d6 default
parent child Browse files
Show More
@@ -0,0 +1,151 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 import pytest
24
25 from . import assert_and_get_content
26 from rhodecode.tests import TestController, TEST_USER_ADMIN_LOGIN
27 from rhodecode.tests.fixture import Fixture
28
29 from rhodecode.lib.utils import map_groups
30 from rhodecode.model.repo import RepoModel
31 from rhodecode.model.repo_group import RepoGroupModel
32 from rhodecode.model.db import Session, Repository, RepoGroup
33
34 fixture = Fixture()
35
36
37 def route_path(name, params=None, **kwargs):
38 import urllib
39
40 base_url = {
41 'goto_switcher_data': '/_goto_data',
42 }[name].format(**kwargs)
43
44 if params:
45 base_url = '{}?{}'.format(base_url, urllib.urlencode(params))
46 return base_url
47
48
49 class TestGotoSwitcherData(TestController):
50
51 required_repos_with_groups = [
52 'abc',
53 'abc-fork',
54 'forks/abcd',
55 'abcd',
56 'abcde',
57 'a/abc',
58 'aa/abc',
59 'aaa/abc',
60 'aaaa/abc',
61 'repos_abc/aaa/abc',
62 'abc_repos/abc',
63 'abc_repos/abcd',
64 'xxx/xyz',
65 'forked-abc/a/abc'
66 ]
67
68 @pytest.fixture(autouse=True, scope='class')
69 def prepare(self, request, pylonsapp):
70 for repo_and_group in self.required_repos_with_groups:
71 # create structure of groups and return the last group
72
73 repo_group = map_groups(repo_and_group)
74
75 RepoModel()._create_repo(
76 repo_and_group, 'hg', 'test-ac', TEST_USER_ADMIN_LOGIN,
77 repo_group=getattr(repo_group, 'group_id', None))
78
79 Session().commit()
80
81 request.addfinalizer(self.cleanup)
82
83 def cleanup(self):
84 # first delete all repos
85 for repo_and_groups in self.required_repos_with_groups:
86 repo = Repository.get_by_repo_name(repo_and_groups)
87 if repo:
88 RepoModel().delete(repo)
89 Session().commit()
90
91 # then delete all empty groups
92 for repo_and_groups in self.required_repos_with_groups:
93 if '/' in repo_and_groups:
94 r_group = repo_and_groups.rsplit('/', 1)[0]
95 repo_group = RepoGroup.get_by_group_name(r_group)
96 if not repo_group:
97 continue
98 parents = repo_group.parents
99 RepoGroupModel().delete(repo_group, force_delete=True)
100 Session().commit()
101
102 for el in reversed(parents):
103 RepoGroupModel().delete(el, force_delete=True)
104 Session().commit()
105
106 def test_returns_list_of_repos_and_groups(self, xhr_header):
107 self.log_user()
108
109 response = self.app.get(
110 route_path('goto_switcher_data'),
111 extra_environ=xhr_header, status=200)
112 result = json.loads(response.body)['results']
113
114 repos, groups, commits = assert_and_get_content(result)
115
116 assert len(repos) == len(Repository.get_all())
117 assert len(groups) == len(RepoGroup.get_all())
118 assert len(commits) == 0
119
120 def test_returns_list_of_repos_and_groups_filtered(self, xhr_header):
121 self.log_user()
122
123 response = self.app.get(
124 route_path('goto_switcher_data'),
125 params={'query': 'abc'},
126 extra_environ=xhr_header, status=200)
127 result = json.loads(response.body)['results']
128
129 repos, groups, commits = assert_and_get_content(result)
130
131 assert len(repos) == 13
132 assert len(groups) == 5
133 assert len(commits) == 0
134
135 def test_returns_list_of_properly_sorted_and_filtered(self, xhr_header):
136 self.log_user()
137
138 response = self.app.get(
139 route_path('goto_switcher_data'),
140 params={'query': 'abc'},
141 extra_environ=xhr_header, status=200)
142 result = json.loads(response.body)['results']
143
144 repos, groups, commits = assert_and_get_content(result)
145
146 test_repos = [x['text'] for x in repos[:4]]
147 assert ['abc', 'abcd', 'a/abc', 'abcde'] == test_repos
148
149 test_groups = [x['text'] for x in groups[:4]]
150 assert ['abc_repos', 'repos_abc',
151 'forked-abc', 'forked-abc/a'] == test_groups
@@ -33,5 +33,9 b' def includeme(config):'
33 name='repo_list_data',
33 name='repo_list_data',
34 pattern='/_repos')
34 pattern='/_repos')
35
35
36 config.add_route(
37 name='goto_switcher_data',
38 pattern='/_goto_data')
39
36 # Scan module for configuration decorators.
40 # Scan module for configuration decorators.
37 config.scan()
41 config.scan()
@@ -18,6 +18,7 b''
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 re
21 import logging
22 import logging
22
23
23 from pyramid.view import view_config
24 from pyramid.view import view_config
@@ -25,8 +26,9 b' from pyramid.view import view_config'
25 from rhodecode.apps._base import BaseAppView
26 from rhodecode.apps._base import BaseAppView
26 from rhodecode.lib import helpers as h
27 from rhodecode.lib import helpers as h
27 from rhodecode.lib.auth import LoginRequired, NotAnonymous
28 from rhodecode.lib.auth import LoginRequired, NotAnonymous
29 from rhodecode.lib.index import searcher_from_config
28 from rhodecode.lib.utils2 import safe_unicode, str2bool
30 from rhodecode.lib.utils2 import safe_unicode, str2bool
29 from rhodecode.model.db import func, Repository
31 from rhodecode.model.db import func, Repository, RepoGroup
30 from rhodecode.model.repo import RepoModel
32 from rhodecode.model.repo import RepoModel
31 from rhodecode.model.scm import ScmModel
33 from rhodecode.model.scm import ScmModel
32
34
@@ -111,6 +113,57 b' class HomeView(BaseAppView):'
111 }
113 }
112 for obj in repo_iter]
114 for obj in repo_iter]
113
115
116 def _get_repo_group_list(self, name_contains=None, limit=20):
117 query = RepoGroup.query()\
118 .order_by(func.length(RepoGroup.group_name))\
119 .order_by(RepoGroup.group_name)
120
121 if name_contains:
122 ilike_expression = u'%{}%'.format(safe_unicode(name_contains))
123 query = query.filter(
124 RepoGroup.group_name.ilike(ilike_expression))
125 query = query.limit(limit)
126
127 all_groups = query.all()
128 repo_groups_iter = ScmModel().get_repo_groups(all_groups)
129 return [
130 {
131 'id': obj.group_name,
132 'text': obj.group_name,
133 'type': 'group',
134 'obj': {},
135 'url': h.url('repo_group_home', group_name=obj.group_name)
136 }
137 for obj in repo_groups_iter]
138
139 def _get_hash_commit_list(self, auth_user, hash_starts_with=None):
140 if not hash_starts_with or len(hash_starts_with) < 3:
141 return []
142
143 commit_hashes = re.compile('([0-9a-f]{2,40})').findall(hash_starts_with)
144
145 if len(commit_hashes) != 1:
146 return []
147
148 commit_hash_prefix = commit_hashes[0]
149
150 searcher = searcher_from_config(self.request.registry.settings)
151 result = searcher.search(
152 'commit_id:%s*' % commit_hash_prefix, 'commit', auth_user,
153 raise_on_exc=False)
154
155 return [
156 {
157 'id': entry['commit_id'],
158 'text': entry['commit_id'],
159 'type': 'commit',
160 'obj': {'repo': entry['repository']},
161 'url': h.url('changeset_home',
162 repo_name=entry['repository'],
163 revision=entry['commit_id'])
164 }
165 for entry in result['results']]
166
114 @LoginRequired()
167 @LoginRequired()
115 @view_config(
168 @view_config(
116 route_name='repo_list_data', request_method='GET',
169 route_name='repo_list_data', request_method='GET',
@@ -136,3 +189,49 b' class HomeView(BaseAppView):'
136 'results': res
189 'results': res
137 }
190 }
138 return data
191 return data
192
193 @LoginRequired()
194 @view_config(
195 route_name='goto_switcher_data', request_method='GET',
196 renderer='json_ext', xhr=True)
197 def goto_switcher_data(self):
198 c = self.load_default_context()
199
200 _ = self.request.translate
201
202 query = self.request.GET.get('query')
203 log.debug('generating goto switcher list, query %s', query)
204
205 res = []
206 repo_groups = self._get_repo_group_list(query)
207 if repo_groups:
208 res.append({
209 'text': _('Groups'),
210 'children': repo_groups
211 })
212
213 repos = self._get_repo_list(query)
214 if repos:
215 res.append({
216 'text': _('Repositories'),
217 'children': repos
218 })
219
220 commits = self._get_hash_commit_list(c.auth_user, query)
221 if commits:
222 unique_repos = {}
223 for commit in commits:
224 unique_repos.setdefault(commit['obj']['repo'], []
225 ).append(commit)
226
227 for repo in unique_repos:
228 res.append({
229 'text': _('Commits in %(repo)s') % {'repo': repo},
230 'children': unique_repos[repo]
231 })
232
233 data = {
234 'more': False,
235 'results': res
236 }
237 return data
@@ -188,8 +188,6 b' def make_map(config):'
188
188
189 # MAIN PAGE
189 # MAIN PAGE
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',
192 action='goto_switcher_data')
193
191
194 # TODO: johbo: Static links, to be replaced by our redirection mechanism
192 # TODO: johbo: Static links, to be replaced by our redirection mechanism
195 rmap.connect('rst_help',
193 rmap.connect('rst_help',
@@ -24,20 +24,15 b' Home controller for RhodeCode Enterprise'
24
24
25 import logging
25 import logging
26 import time
26 import time
27 import re
28
27
29 from pylons import tmpl_context as c, request, url, config
28 from pylons import tmpl_context as c
30 from pylons.i18n.translation import _
31 from sqlalchemy.sql import func
32
29
33 from rhodecode.lib.auth import (
30 from rhodecode.lib.auth import (
34 LoginRequired, HasPermissionAllDecorator, AuthUser,
31 LoginRequired, HasPermissionAllDecorator,
35 HasRepoGroupPermissionAnyDecorator, XHRRequired)
32 HasRepoGroupPermissionAnyDecorator)
36 from rhodecode.lib.base import BaseController, render
33 from rhodecode.lib.base import BaseController, render
37 from rhodecode.lib.index import searcher_from_config
34
38 from rhodecode.lib.ext_json import json
35 from rhodecode.lib.ext_json import json
39 from rhodecode.lib.utils import jsonify
40 from rhodecode.lib.utils2 import safe_unicode, str2bool
41 from rhodecode.model.db import Repository, RepoGroup
36 from rhodecode.model.db import Repository, RepoGroup
42 from rhodecode.model.repo import RepoModel
37 from rhodecode.model.repo import RepoModel
43 from rhodecode.model.repo_group import RepoGroupModel
38 from rhodecode.model.repo_group import RepoGroupModel
@@ -113,123 +108,3 b' class HomeController(BaseController):'
113 c.repo_groups_data = json.dumps(repo_group_data)
108 c.repo_groups_data = json.dumps(repo_group_data)
114
109
115 return render('index_repo_group.mako')
110 return render('index_repo_group.mako')
116
117 def _get_repo_list(self, name_contains=None, repo_type=None, limit=20):
118 query = Repository.query()\
119 .order_by(func.length(Repository.repo_name))\
120 .order_by(Repository.repo_name)
121
122 if repo_type:
123 query = query.filter(Repository.repo_type == repo_type)
124
125 if name_contains:
126 ilike_expression = u'%{}%'.format(safe_unicode(name_contains))
127 query = query.filter(
128 Repository.repo_name.ilike(ilike_expression))
129 query = query.limit(limit)
130
131 all_repos = query.all()
132 repo_iter = self.scm_model.get_repos(all_repos)
133 return [
134 {
135 'id': obj['name'],
136 'text': obj['name'],
137 'type': 'repo',
138 'obj': obj['dbrepo'],
139 'url': url('summary_home', repo_name=obj['name'])
140 }
141 for obj in repo_iter]
142
143 def _get_repo_group_list(self, name_contains=None, limit=20):
144 query = RepoGroup.query()\
145 .order_by(func.length(RepoGroup.group_name))\
146 .order_by(RepoGroup.group_name)
147
148 if name_contains:
149 ilike_expression = u'%{}%'.format(safe_unicode(name_contains))
150 query = query.filter(
151 RepoGroup.group_name.ilike(ilike_expression))
152 query = query.limit(limit)
153
154 all_groups = query.all()
155 repo_groups_iter = self.scm_model.get_repo_groups(all_groups)
156 return [
157 {
158 'id': obj.group_name,
159 'text': obj.group_name,
160 'type': 'group',
161 'obj': {},
162 'url': url('repo_group_home', group_name=obj.group_name)
163 }
164 for obj in repo_groups_iter]
165
166 def _get_hash_commit_list(self, hash_starts_with=None, limit=20):
167 if not hash_starts_with or len(hash_starts_with) < 3:
168 return []
169
170 commit_hashes = re.compile('([0-9a-f]{2,40})').findall(hash_starts_with)
171
172 if len(commit_hashes) != 1:
173 return []
174
175 commit_hash_prefix = commit_hashes[0]
176
177 auth_user = AuthUser(
178 user_id=c.rhodecode_user.user_id, ip_addr=self.ip_addr)
179 searcher = searcher_from_config(config)
180 result = searcher.search(
181 'commit_id:%s*' % commit_hash_prefix, 'commit', auth_user,
182 raise_on_exc=False)
183
184 return [
185 {
186 'id': entry['commit_id'],
187 'text': entry['commit_id'],
188 'type': 'commit',
189 'obj': {'repo': entry['repository']},
190 'url': url('changeset_home',
191 repo_name=entry['repository'],
192 revision=entry['commit_id'])
193 }
194 for entry in result['results']]
195
196 @LoginRequired()
197 @XHRRequired()
198 @jsonify
199 def goto_switcher_data(self):
200 query = request.GET.get('query')
201 log.debug('generating goto switcher list, query %s', query)
202
203 res = []
204 repo_groups = self._get_repo_group_list(query)
205 if repo_groups:
206 res.append({
207 'text': _('Groups'),
208 'children': repo_groups
209 })
210
211 repos = self._get_repo_list(query)
212 if repos:
213 res.append({
214 'text': _('Repositories'),
215 'children': repos
216 })
217
218 commits = self._get_hash_commit_list(query)
219 if commits:
220 unique_repos = {}
221 for commit in commits:
222 unique_repos.setdefault(commit['obj']['repo'], []
223 ).append(commit)
224
225 for repo in unique_repos:
226 res.append({
227 'text': _('Commits in %(repo)s') % {'repo': repo},
228 'children': unique_repos[repo]
229 })
230
231 data = {
232 'more': False,
233 'results': res
234 }
235 return data
@@ -96,6 +96,7 b' function registerRCRoutes() {'
96 pyroutes.register('user_autocomplete_data', '/_users', []);
96 pyroutes.register('user_autocomplete_data', '/_users', []);
97 pyroutes.register('user_group_autocomplete_data', '/_user_groups', []);
97 pyroutes.register('user_group_autocomplete_data', '/_user_groups', []);
98 pyroutes.register('repo_list_data', '/_repos', []);
98 pyroutes.register('repo_list_data', '/_repos', []);
99 pyroutes.register('goto_switcher_data', '/_goto_data', []);
99 pyroutes.register('repo_maintenance', '/%(repo_name)s/maintenance', ['repo_name']);
100 pyroutes.register('repo_maintenance', '/%(repo_name)s/maintenance', ['repo_name']);
100 pyroutes.register('repo_maintenance_execute', '/%(repo_name)s/maintenance/execute', ['repo_name']);
101 pyroutes.register('repo_maintenance_execute', '/%(repo_name)s/maintenance/execute', ['repo_name']);
101 pyroutes.register('strip', '/%(repo_name)s/strip', ['repo_name']);
102 pyroutes.register('strip', '/%(repo_name)s/strip', ['repo_name']);
@@ -503,7 +503,7 b''
503 query.callback({results: cachedData.results});
503 query.callback({results: cachedData.results});
504 } else {
504 } else {
505 $.ajax({
505 $.ajax({
506 url: "${h.url('goto_switcher_data')}",
506 url: pyroutes.url('goto_switcher_data'),
507 data: {'query': query.term},
507 data: {'query': query.term},
508 dataType: 'json',
508 dataType: 'json',
509 type: 'GET',
509 type: 'GET',
@@ -18,20 +18,17 b''
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 json
22
21
23 from mock import patch
24 import pytest
22 import pytest
25 from pylons import tmpl_context as c
23 from pylons import tmpl_context as c
26
24
27 import rhodecode
25 import rhodecode
28 from rhodecode.lib.utils import map_groups
26 from rhodecode.model.db import Repository, User
29 from rhodecode.model.db import Repository, User, RepoGroup
30 from rhodecode.model.meta import Session
27 from rhodecode.model.meta import Session
31 from rhodecode.model.repo import RepoModel
28 from rhodecode.model.repo import RepoModel
32 from rhodecode.model.repo_group import RepoGroupModel
29 from rhodecode.model.repo_group import RepoGroupModel
33 from rhodecode.model.settings import SettingsModel
30 from rhodecode.model.settings import SettingsModel
34 from rhodecode.tests import TestController, url, TEST_USER_ADMIN_LOGIN
31 from rhodecode.tests import TestController, url
35 from rhodecode.tests.fixture import Fixture
32 from rhodecode.tests.fixture import Fixture
36
33
37
34
@@ -130,128 +127,3 b' class TestHomeController(TestController)'
130 response.mustcontain(version_string)
127 response.mustcontain(version_string)
131 if state is False:
128 if state is False:
132 response.mustcontain(no=[version_string])
129 response.mustcontain(no=[version_string])
133
134
135 def assert_and_get_content(result):
136 repos = []
137 groups = []
138 commits = []
139 for data in result:
140 for data_item in data['children']:
141 assert data_item['id']
142 assert data_item['text']
143 assert data_item['url']
144 if data_item['type'] == 'repo':
145 repos.append(data_item)
146 elif data_item['type'] == 'group':
147 groups.append(data_item)
148 elif data_item['type'] == 'commit':
149 commits.append(data_item)
150 else:
151 raise Exception('invalid type %s' % data_item['type'])
152
153 return repos, groups, commits
154
155
156 class TestGotoSwitcherData(TestController):
157 required_repos_with_groups = [
158 'abc',
159 'abc-fork',
160 'forks/abcd',
161 'abcd',
162 'abcde',
163 'a/abc',
164 'aa/abc',
165 'aaa/abc',
166 'aaaa/abc',
167 'repos_abc/aaa/abc',
168 'abc_repos/abc',
169 'abc_repos/abcd',
170 'xxx/xyz',
171 'forked-abc/a/abc'
172 ]
173
174 @pytest.fixture(autouse=True, scope='class')
175 def prepare(self, request, pylonsapp):
176 for repo_and_group in self.required_repos_with_groups:
177 # create structure of groups and return the last group
178
179 repo_group = map_groups(repo_and_group)
180
181 RepoModel()._create_repo(
182 repo_and_group, 'hg', 'test-ac', TEST_USER_ADMIN_LOGIN,
183 repo_group=getattr(repo_group, 'group_id', None))
184
185 Session().commit()
186
187 request.addfinalizer(self.cleanup)
188
189 def cleanup(self):
190 # first delete all repos
191 for repo_and_groups in self.required_repos_with_groups:
192 repo = Repository.get_by_repo_name(repo_and_groups)
193 if repo:
194 RepoModel().delete(repo)
195 Session().commit()
196
197 # then delete all empty groups
198 for repo_and_groups in self.required_repos_with_groups:
199 if '/' in repo_and_groups:
200 r_group = repo_and_groups.rsplit('/', 1)[0]
201 repo_group = RepoGroup.get_by_group_name(r_group)
202 if not repo_group:
203 continue
204 parents = repo_group.parents
205 RepoGroupModel().delete(repo_group, force_delete=True)
206 Session().commit()
207
208 for el in reversed(parents):
209 RepoGroupModel().delete(el, force_delete=True)
210 Session().commit()
211
212 def test_returns_list_of_repos_and_groups(self):
213 self.log_user()
214
215 response = self.app.get(
216 url(controller='home', action='goto_switcher_data'),
217 headers={'X-REQUESTED-WITH': 'XMLHttpRequest', }, status=200)
218 result = json.loads(response.body)['results']
219
220 repos, groups, commits = assert_and_get_content(result)
221
222 assert len(repos) == len(Repository.get_all())
223 assert len(groups) == len(RepoGroup.get_all())
224 assert len(commits) == 0
225
226 def test_returns_list_of_repos_and_groups_filtered(self):
227 self.log_user()
228
229 response = self.app.get(
230 url(controller='home', action='goto_switcher_data'),
231 headers={'X-REQUESTED-WITH': 'XMLHttpRequest', },
232 params={'query': 'abc'}, status=200)
233 result = json.loads(response.body)['results']
234
235 repos, groups, commits = assert_and_get_content(result)
236
237 assert len(repos) == 13
238 assert len(groups) == 5
239 assert len(commits) == 0
240
241 def test_returns_list_of_properly_sorted_and_filtered(self):
242 self.log_user()
243
244 response = self.app.get(
245 url(controller='home', action='goto_switcher_data'),
246 headers={'X-REQUESTED-WITH': 'XMLHttpRequest', },
247 params={'query': 'abc'}, status=200)
248 result = json.loads(response.body)['results']
249
250 repos, groups, commits = assert_and_get_content(result)
251
252 test_repos = [x['text'] for x in repos[:4]]
253 assert ['abc', 'abcd', 'a/abc', 'abcde'] == test_repos
254
255 test_groups = [x['text'] for x in groups[:4]]
256 assert ['abc_repos', 'repos_abc',
257 'forked-abc', 'forked-abc/a'] == test_groups
General Comments 0
You need to be logged in to leave comments. Login now