feature: Go To switcher now searches commit hashes as well
This commit is contained in:
parent
6c265d1ff9
commit
c683b5b179
7 changed files with 106 additions and 30 deletions
|
|
@ -125,8 +125,8 @@ def make_map(config):
|
|||
|
||||
# MAIN PAGE
|
||||
rmap.connect('home', '/', controller='home', action='index')
|
||||
rmap.connect('repo_switcher_data', '/_repos_and_groups', controller='home',
|
||||
action='repo_switcher_data')
|
||||
rmap.connect('goto_switcher_data', '/_goto_data', controller='home',
|
||||
action='goto_switcher_data')
|
||||
rmap.connect('repo_list_data', '/_repos', controller='home',
|
||||
action='repo_list_data')
|
||||
|
||||
|
|
|
|||
|
|
@ -24,16 +24,17 @@ Home controller for RhodeCode Enterprise
|
|||
|
||||
import logging
|
||||
import time
|
||||
import re
|
||||
|
||||
|
||||
from pylons import tmpl_context as c, request
|
||||
from pylons import tmpl_context as c, request, url, config
|
||||
from pylons.i18n.translation import _
|
||||
from sqlalchemy.sql import func
|
||||
|
||||
from rhodecode.lib.auth import (
|
||||
LoginRequired, HasPermissionAllDecorator,
|
||||
LoginRequired, HasPermissionAllDecorator, AuthUser,
|
||||
HasRepoGroupPermissionAnyDecorator, XHRRequired)
|
||||
from rhodecode.lib.base import BaseController, render
|
||||
from rhodecode.lib.index import searcher_from_config
|
||||
from rhodecode.lib.ext_json import json
|
||||
from rhodecode.lib.utils import jsonify
|
||||
from rhodecode.lib.utils2 import safe_unicode
|
||||
|
|
@ -134,7 +135,8 @@ class HomeController(BaseController):
|
|||
'id': obj['name'],
|
||||
'text': obj['name'],
|
||||
'type': 'repo',
|
||||
'obj': obj['dbrepo']
|
||||
'obj': obj['dbrepo'],
|
||||
'url': url('summary_home', repo_name=obj['name'])
|
||||
}
|
||||
for obj in repo_iter]
|
||||
|
||||
|
|
@ -156,16 +158,45 @@ class HomeController(BaseController):
|
|||
'id': obj.group_name,
|
||||
'text': obj.group_name,
|
||||
'type': 'group',
|
||||
'obj': {}
|
||||
'obj': {},
|
||||
'url': url('repo_group_home', group_name=obj.group_name)
|
||||
}
|
||||
for obj in repo_groups_iter]
|
||||
|
||||
def _get_hash_commit_list(self, hash_starts_with=None, limit=20):
|
||||
if not hash_starts_with or len(hash_starts_with) < 3:
|
||||
return []
|
||||
|
||||
commit_hashes = re.compile('([0-9a-f]{2,40})').findall(hash_starts_with)
|
||||
|
||||
if len(commit_hashes) != 1:
|
||||
return []
|
||||
|
||||
commit_hash_prefix = commit_hashes[0]
|
||||
|
||||
auth_user = AuthUser(
|
||||
user_id=c.rhodecode_user.user_id, ip_addr=self.ip_addr)
|
||||
searcher = searcher_from_config(config)
|
||||
result = searcher.search(
|
||||
'commit_id:%s*' % commit_hash_prefix, 'commit', auth_user)
|
||||
|
||||
return [
|
||||
{
|
||||
'id': entry['commit_id'],
|
||||
'text': entry['commit_id'],
|
||||
'type': 'commit',
|
||||
'obj': {'repo': entry['repository']},
|
||||
'url': url('changeset_home',
|
||||
repo_name=entry['repository'], revision=entry['commit_id'])
|
||||
}
|
||||
for entry in result['results']]
|
||||
|
||||
@LoginRequired()
|
||||
@XHRRequired()
|
||||
@jsonify
|
||||
def repo_switcher_data(self):
|
||||
def goto_switcher_data(self):
|
||||
query = request.GET.get('query')
|
||||
log.debug('generating switcher repo/groups list, query %s', query)
|
||||
log.debug('generating goto switcher list, query %s', query)
|
||||
|
||||
res = []
|
||||
repo_groups = self._get_repo_group_list(query)
|
||||
|
|
@ -182,6 +213,19 @@ class HomeController(BaseController):
|
|||
'children': repos
|
||||
})
|
||||
|
||||
commits = self._get_hash_commit_list(query)
|
||||
if commits:
|
||||
unique_repos = {}
|
||||
for commit in commits:
|
||||
unique_repos.setdefault(commit['obj']['repo'], []
|
||||
).append(commit)
|
||||
|
||||
for repo in unique_repos:
|
||||
res.append({
|
||||
'text': _('Commits in %(repo)s') % {'repo': repo},
|
||||
'children': unique_repos[repo]
|
||||
})
|
||||
|
||||
data = {
|
||||
'more': False,
|
||||
'results': res
|
||||
|
|
@ -203,6 +247,7 @@ class HomeController(BaseController):
|
|||
'text': _('Repositories'),
|
||||
'children': repos
|
||||
})
|
||||
|
||||
data = {
|
||||
'more': False,
|
||||
'results': res
|
||||
|
|
|
|||
|
|
@ -42,7 +42,6 @@ class BaseSearch(object):
|
|||
def search(self, query, document_type, search_user, repo_name=None):
|
||||
raise Exception('NotImplemented')
|
||||
|
||||
|
||||
def searcher_from_config(config, prefix='search.'):
|
||||
_config = {}
|
||||
for key in config.keys():
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ Index schema for RhodeCode
|
|||
from __future__ import absolute_import
|
||||
import logging
|
||||
import os
|
||||
import re
|
||||
|
||||
from pylons.i18n.translation import _
|
||||
|
||||
|
|
@ -59,6 +60,7 @@ FRAGMENTER = ContextFragmenter(200)
|
|||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
|
||||
class Search(BaseSearch):
|
||||
|
||||
name = 'whoosh'
|
||||
|
|
@ -90,8 +92,19 @@ class Search(BaseSearch):
|
|||
if self.searcher:
|
||||
self.searcher.close()
|
||||
|
||||
def _extend_query(self, query):
|
||||
hashes = re.compile('([0-9a-f]{5,40})').findall(query)
|
||||
if hashes:
|
||||
hashes_or_query = ' OR '.join('commit_id:%s*' % h for h in hashes)
|
||||
query = u'(%s) OR %s' % (query, hashes_or_query)
|
||||
return query
|
||||
|
||||
def search(self, query, document_type, search_user, repo_name=None,
|
||||
requested_page=1, page_limit=10):
|
||||
|
||||
original_query = query
|
||||
query = self._extend_query(query)
|
||||
|
||||
log.debug(u'QUERY: %s on %s', query, document_type)
|
||||
result = {
|
||||
'results': [],
|
||||
|
|
|
|||
|
|
@ -455,8 +455,11 @@
|
|||
tmpl += '<i class="icon-unlock-alt"></i> ';
|
||||
}
|
||||
}
|
||||
if(obj_dict && state.type == 'commit') {
|
||||
tmpl += '<i class="icon-tag"></i>';
|
||||
}
|
||||
if(obj_dict && state.type == 'group'){
|
||||
tmpl += '<i class="icon-folder-close"></i> ';
|
||||
tmpl += '<i class="icon-folder-close"></i> ';
|
||||
}
|
||||
tmpl += escapeMarkup(state.text);
|
||||
return tmpl;
|
||||
|
|
@ -496,7 +499,7 @@
|
|||
query.callback({results: cachedData.results});
|
||||
} else {
|
||||
$.ajax({
|
||||
url: "${h.url('repo_switcher_data')}",
|
||||
url: "${h.url('goto_switcher_data')}",
|
||||
data: {'query': query.term},
|
||||
dataType: 'json',
|
||||
type: 'GET',
|
||||
|
|
@ -514,7 +517,7 @@
|
|||
|
||||
$("#repo_switcher").on('select2-selecting', function(e){
|
||||
e.preventDefault();
|
||||
window.location = pyroutes.url('summary_home', {'repo_name': e.val});
|
||||
window.location = e.choice.url;
|
||||
});
|
||||
|
||||
## Global mouse bindings ##
|
||||
|
|
|
|||
|
|
@ -181,19 +181,25 @@ class TestUserAutocompleteData(TestController):
|
|||
def assert_and_get_content(result):
|
||||
repos = []
|
||||
groups = []
|
||||
commits = []
|
||||
for data in result:
|
||||
for data_item in data['children']:
|
||||
assert data_item['id']
|
||||
assert data_item['text']
|
||||
assert data_item['url']
|
||||
if data_item['type'] == 'repo':
|
||||
repos.append(data_item)
|
||||
else:
|
||||
elif data_item['type'] == 'group':
|
||||
groups.append(data_item)
|
||||
elif data_item['type'] == 'commit':
|
||||
commits.append(data_item)
|
||||
else:
|
||||
raise Exception('invalid type %s' % data_item['type'])
|
||||
|
||||
return repos, groups
|
||||
return repos, groups, commits
|
||||
|
||||
|
||||
class TestRepoSwitcherData(TestController):
|
||||
class TestGotoSwitcherData(TestController):
|
||||
required_repos_with_groups = [
|
||||
'abc',
|
||||
'abc-fork',
|
||||
|
|
@ -253,39 +259,41 @@ class TestRepoSwitcherData(TestController):
|
|||
self.log_user()
|
||||
|
||||
response = self.app.get(
|
||||
url(controller='home', action='repo_switcher_data'),
|
||||
url(controller='home', action='goto_switcher_data'),
|
||||
headers={'X-REQUESTED-WITH': 'XMLHttpRequest', }, status=200)
|
||||
result = json.loads(response.body)['results']
|
||||
|
||||
repos, groups = assert_and_get_content(result)
|
||||
repos, groups, commits = assert_and_get_content(result)
|
||||
|
||||
assert len(repos) == len(Repository.get_all())
|
||||
assert len(groups) == len(RepoGroup.get_all())
|
||||
assert len(commits) == 0
|
||||
|
||||
def test_returns_list_of_repos_and_groups_filtered(self):
|
||||
self.log_user()
|
||||
|
||||
response = self.app.get(
|
||||
url(controller='home', action='repo_switcher_data'),
|
||||
url(controller='home', action='goto_switcher_data'),
|
||||
headers={'X-REQUESTED-WITH': 'XMLHttpRequest', },
|
||||
params={'query': 'abc'}, status=200)
|
||||
result = json.loads(response.body)['results']
|
||||
|
||||
repos, groups = assert_and_get_content(result)
|
||||
repos, groups, commits = assert_and_get_content(result)
|
||||
|
||||
assert len(repos) == 13
|
||||
assert len(groups) == 5
|
||||
assert len(commits) == 0
|
||||
|
||||
def test_returns_list_of_properly_sorted_and_filtered(self):
|
||||
self.log_user()
|
||||
|
||||
response = self.app.get(
|
||||
url(controller='home', action='repo_switcher_data'),
|
||||
url(controller='home', action='goto_switcher_data'),
|
||||
headers={'X-REQUESTED-WITH': 'XMLHttpRequest', },
|
||||
params={'query': 'abc'}, status=200)
|
||||
result = json.loads(response.body)['results']
|
||||
|
||||
repos, groups = assert_and_get_content(result)
|
||||
repos, groups, commits = assert_and_get_content(result)
|
||||
|
||||
test_repos = [x['text'] for x in repos[:4]]
|
||||
assert ['abc', 'abcd', 'a/abc', 'abcde'] == test_repos
|
||||
|
|
@ -300,54 +308,58 @@ class TestRepoListData(TestController):
|
|||
self.log_user()
|
||||
|
||||
response = self.app.get(
|
||||
url(controller='home', action='repo_switcher_data'),
|
||||
url(controller='home', action='repo_list_data'),
|
||||
headers={'X-REQUESTED-WITH': 'XMLHttpRequest', }, status=200)
|
||||
result = json.loads(response.body)['results']
|
||||
|
||||
repos, groups = assert_and_get_content(result)
|
||||
repos, groups, commits = assert_and_get_content(result)
|
||||
|
||||
assert len(repos) == len(Repository.get_all())
|
||||
assert len(groups) == 0
|
||||
assert len(commits) == 0
|
||||
|
||||
def test_returns_list_of_repos_and_groups_filtered(self):
|
||||
self.log_user()
|
||||
|
||||
response = self.app.get(
|
||||
url(controller='home', action='repo_switcher_data'),
|
||||
url(controller='home', action='repo_list_data'),
|
||||
headers={'X-REQUESTED-WITH': 'XMLHttpRequest', },
|
||||
params={'query': 'vcs_test_git'}, status=200)
|
||||
result = json.loads(response.body)['results']
|
||||
|
||||
repos, groups = assert_and_get_content(result)
|
||||
repos, groups, commits = assert_and_get_content(result)
|
||||
|
||||
assert len(repos) == len(Repository.query().filter(
|
||||
Repository.repo_name.ilike('%vcs_test_git%')).all())
|
||||
assert len(groups) == 0
|
||||
assert len(commits) == 0
|
||||
|
||||
def test_returns_list_of_repos_and_groups_filtered_with_type(self):
|
||||
self.log_user()
|
||||
|
||||
response = self.app.get(
|
||||
url(controller='home', action='repo_switcher_data'),
|
||||
url(controller='home', action='repo_list_data'),
|
||||
headers={'X-REQUESTED-WITH': 'XMLHttpRequest', },
|
||||
params={'query': 'vcs_test_git', 'repo_type': 'git'}, status=200)
|
||||
result = json.loads(response.body)['results']
|
||||
|
||||
repos, groups = assert_and_get_content(result)
|
||||
repos, groups, commits = assert_and_get_content(result)
|
||||
|
||||
assert len(repos) == len(Repository.query().filter(
|
||||
Repository.repo_name.ilike('%vcs_test_git%')).all())
|
||||
assert len(groups) == 0
|
||||
assert len(commits) == 0
|
||||
|
||||
def test_returns_list_of_repos_non_ascii_query(self):
|
||||
self.log_user()
|
||||
response = self.app.get(
|
||||
url(controller='home', action='repo_switcher_data'),
|
||||
url(controller='home', action='repo_list_data'),
|
||||
headers={'X-REQUESTED-WITH': 'XMLHttpRequest', },
|
||||
params={'query': 'ć_vcs_test_ą', 'repo_type': 'git'}, status=200)
|
||||
result = json.loads(response.body)['results']
|
||||
|
||||
repos, groups = assert_and_get_content(result)
|
||||
repos, groups, commits = assert_and_get_content(result)
|
||||
|
||||
assert len(repos) == 0
|
||||
assert len(groups) == 0
|
||||
assert len(commits) == 0
|
||||
|
|
|
|||
|
|
@ -129,6 +129,10 @@ class TestSearchController(TestController):
|
|||
('author:marcin@python-blog.com '
|
||||
'commit_id:b986218ba1c9b0d6a259fac9b050b1724ed8e545', 1, [
|
||||
('hg', 'b986218ba1c9b0d6a259fac9b050b1724ed8e545')]),
|
||||
('b986218ba1c9b0d6a259fac9b050b1724ed8e545', 1, [
|
||||
('hg', 'b986218ba1c9b0d6a259fac9b050b1724ed8e545')]),
|
||||
('b986218b', 1, [
|
||||
('hg', 'b986218ba1c9b0d6a259fac9b050b1724ed8e545')]),
|
||||
])
|
||||
def test_search_commit_messages(
|
||||
self, query, expected_hits, expected_commits, enabled_backends):
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue