# HG changeset patch # User Marcin Kuzminski # Date 2017-04-19 12:34:57 # Node ID 80369ba7d0929f2f65cf8cf7494b5c773cd01430 # Parent 353a4541d6bc6ca1094e6c14f49819e4b81019bf core: moved users and user groups autocomplete into pyramid. - added more tests - added some logging diff --git a/rhodecode/apps/home/__init__.py b/rhodecode/apps/home/__init__.py new file mode 100644 --- /dev/null +++ b/rhodecode/apps/home/__init__.py @@ -0,0 +1,33 @@ +# -*- coding: utf-8 -*- + +# Copyright (C) 2016-2017 RhodeCode GmbH +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License, version 3 +# (only), as published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +# This program is dual-licensed. If you wish to learn more about the +# RhodeCode Enterprise Edition, including its added features, Support services, +# and proprietary license terms, please see https://rhodecode.com/licenses/ + + +def includeme(config): + + config.add_route( + name='user_autocomplete_data', + pattern='/_users') + + config.add_route( + name='user_group_autocomplete_data', + pattern='/_user_groups') + + # Scan module for configuration decorators. + config.scan() diff --git a/rhodecode/apps/home/tests/__init__.py b/rhodecode/apps/home/tests/__init__.py new file mode 100644 --- /dev/null +++ b/rhodecode/apps/home/tests/__init__.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- + +# Copyright (C) 2016-2017 RhodeCode GmbH +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License, version 3 +# (only), as published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +# This program is dual-licensed. If you wish to learn more about the +# RhodeCode Enterprise Edition, including its added features, Support services, +# and proprietary license terms, please see https://rhodecode.com/licenses/ diff --git a/rhodecode/apps/home/tests/test_get_user_data.py b/rhodecode/apps/home/tests/test_get_user_data.py new file mode 100644 --- /dev/null +++ b/rhodecode/apps/home/tests/test_get_user_data.py @@ -0,0 +1,112 @@ +# -*- coding: utf-8 -*- + +# Copyright (C) 2016-2017 RhodeCode GmbH +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License, version 3 +# (only), as published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +# This program is dual-licensed. If you wish to learn more about the +# RhodeCode Enterprise Edition, including its added features, Support services, +# and proprietary license terms, please see https://rhodecode.com/licenses/ + +import json +import pytest + +from rhodecode.tests import TestController +from rhodecode.tests.fixture import Fixture + + +fixture = Fixture() + + +def route_path(name, params=None, **kwargs): + import urllib + + base_url = { + 'user_autocomplete_data': '/_users', + 'user_group_autocomplete_data': '/_user_groups' + }[name].format(**kwargs) + + if params: + base_url = '{}?{}'.format(base_url, urllib.urlencode(params)) + return base_url + + +class TestUserAutocompleteData(TestController): + + def test_returns_list_of_users(self, user_util, xhr_header): + self.log_user() + user = user_util.create_user(active=True) + user_name = user.username + response = self.app.get( + route_path('user_autocomplete_data'), + extra_environ=xhr_header, status=200) + result = json.loads(response.body) + values = [suggestion['value'] for suggestion in result['suggestions']] + assert user_name in values + + def test_returns_inactive_users_when_active_flag_sent( + self, user_util, xhr_header): + self.log_user() + user = user_util.create_user(active=False) + user_name = user.username + + response = self.app.get( + route_path('user_autocomplete_data', + params=dict(user_groups='true', active='0')), + extra_environ=xhr_header, status=200) + result = json.loads(response.body) + values = [suggestion['value'] for suggestion in result['suggestions']] + assert user_name in values + + response = self.app.get( + route_path('user_autocomplete_data', + params=dict(user_groups='true', active='1')), + extra_environ=xhr_header, status=200) + result = json.loads(response.body) + values = [suggestion['value'] for suggestion in result['suggestions']] + assert user_name not in values + + def test_returns_groups_when_user_groups_flag_sent( + self, user_util, xhr_header): + self.log_user() + group = user_util.create_user_group(user_groups_active=True) + group_name = group.users_group_name + response = self.app.get( + route_path('user_autocomplete_data', + params=dict(user_groups='true')), + extra_environ=xhr_header, status=200) + result = json.loads(response.body) + values = [suggestion['value'] for suggestion in result['suggestions']] + assert group_name in values + + @pytest.mark.parametrize('query, count', [ + ('hello1', 0), + ('dev', 2), + ]) + def test_result_is_limited_when_query_is_sent(self, user_util, xhr_header, + query, count): + self.log_user() + + user_util._test_name = 'dev-test' + user_util.create_user() + + user_util._test_name = 'dev-group-test' + user_util.create_user_group() + + response = self.app.get( + route_path('user_autocomplete_data', + params=dict(user_groups='true', query=query)), + extra_environ=xhr_header, status=200) + + result = json.loads(response.body) + assert len(result['suggestions']) == count diff --git a/rhodecode/apps/home/tests/test_get_user_group_data.py b/rhodecode/apps/home/tests/test_get_user_group_data.py new file mode 100644 --- /dev/null +++ b/rhodecode/apps/home/tests/test_get_user_group_data.py @@ -0,0 +1,117 @@ +# -*- coding: utf-8 -*- + +# Copyright (C) 2016-2017 RhodeCode GmbH +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License, version 3 +# (only), as published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +# This program is dual-licensed. If you wish to learn more about the +# RhodeCode Enterprise Edition, including its added features, Support services, +# and proprietary license terms, please see https://rhodecode.com/licenses/ +# -*- coding: utf-8 -*- + +# Copyright (C) 2016-2017 RhodeCode GmbH +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License, version 3 +# (only), as published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +# This program is dual-licensed. If you wish to learn more about the +# RhodeCode Enterprise Edition, including its added features, Support services, +# and proprietary license terms, please see https://rhodecode.com/licenses/ + +import json + +import pytest + +from rhodecode.tests import TestController +from rhodecode.tests.fixture import Fixture + + +fixture = Fixture() + + +def route_path(name, params=None, **kwargs): + import urllib + + base_url = { + 'user_autocomplete_data': '/_users', + 'user_group_autocomplete_data': '/_user_groups' + }[name].format(**kwargs) + + if params: + base_url = '{}?{}'.format(base_url, urllib.urlencode(params)) + return base_url + + +class TestUserGroupAutocompleteData(TestController): + + def test_returns_list_of_user_groups(self, user_util, xhr_header): + self.log_user() + user_group = user_util.create_user_group(active=True) + user_group_name = user_group.users_group_name + response = self.app.get( + route_path('user_group_autocomplete_data'), + extra_environ=xhr_header, status=200) + result = json.loads(response.body) + values = [suggestion['value'] for suggestion in result['suggestions']] + assert user_group_name in values + + def test_returns_inactive_user_groups_when_active_flag_sent( + self, user_util, xhr_header): + self.log_user() + user_group = user_util.create_user_group(active=False) + user_group_name = user_group.users_group_name + + response = self.app.get( + route_path('user_group_autocomplete_data', + params=dict(active='0')), + extra_environ=xhr_header, status=200) + result = json.loads(response.body) + values = [suggestion['value'] for suggestion in result['suggestions']] + assert user_group_name in values + + response = self.app.get( + route_path('user_group_autocomplete_data', + params=dict(active='1')), + extra_environ=xhr_header, status=200) + result = json.loads(response.body) + values = [suggestion['value'] for suggestion in result['suggestions']] + assert user_group_name not in values + + @pytest.mark.parametrize('query, count', [ + ('hello1', 0), + ('dev', 1), + ]) + def test_result_is_limited_when_query_is_sent(self, user_util, xhr_header, query, count): + self.log_user() + + user_util._test_name = 'dev-test' + user_util.create_user_group() + + response = self.app.get( + route_path('user_group_autocomplete_data', + params=dict(user_groups='true', + query=query)), + extra_environ=xhr_header, status=200) + + result = json.loads(response.body) + + assert len(result['suggestions']) == count diff --git a/rhodecode/apps/home/views.py b/rhodecode/apps/home/views.py new file mode 100644 --- /dev/null +++ b/rhodecode/apps/home/views.py @@ -0,0 +1,81 @@ +# -*- coding: utf-8 -*- + +# Copyright (C) 2016-2017 RhodeCode GmbH +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License, version 3 +# (only), as published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +# This program is dual-licensed. If you wish to learn more about the +# RhodeCode Enterprise Edition, including its added features, Support services, +# and proprietary license terms, please see https://rhodecode.com/licenses/ + +import logging + +from pyramid.view import view_config + +from rhodecode.apps._base import BaseAppView +from rhodecode.lib.auth import LoginRequired, NotAnonymous +from rhodecode.lib.utils2 import str2bool +from rhodecode.model.repo import RepoModel + +log = logging.getLogger(__name__) + + +class HomeView(BaseAppView): + + def load_default_context(self): + c = self._get_local_tmpl_context() + c.user = c.auth_user.get_instance() + self._register_global_c(c) + return c + + @LoginRequired() + @view_config( + route_name='user_autocomplete_data', request_method='GET', + renderer='json_ext', xhr=True) + def user_autocomplete_data(self): + query = self.request.GET.get('query') + active = str2bool(self.request.GET.get('active') or True) + include_groups = str2bool(self.request.GET.get('user_groups')) + + log.debug('generating user list, query:%s, active:%s, with_groups:%s', + query, active, include_groups) + + repo_model = RepoModel() + _users = repo_model.get_users( + name_contains=query, only_active=active) + + if include_groups: + # extend with user groups + _user_groups = repo_model.get_user_groups( + name_contains=query, only_active=active) + _users = _users + _user_groups + + return {'suggestions': _users} + + @LoginRequired() + @NotAnonymous() + @view_config( + route_name='user_group_autocomplete_data', request_method='GET', + renderer='json_ext', xhr=True) + def user_group_autocomplete_data(self): + query = self.request.GET.get('query') + active = str2bool(self.request.GET.get('active') or True) + log.debug('generating user group list, query:%s, active:%s', + query, active) + + repo_model = RepoModel() + _user_groups = repo_model.get_user_groups( + name_contains=query, only_active=active) + _user_groups = _user_groups + + return {'suggestions': _user_groups} diff --git a/rhodecode/config/middleware.py b/rhodecode/config/middleware.py --- a/rhodecode/config/middleware.py +++ b/rhodecode/config/middleware.py @@ -288,6 +288,7 @@ def includeme(config): config.include('rhodecode.apps.admin') config.include('rhodecode.apps.channelstream') config.include('rhodecode.apps.login') + config.include('rhodecode.apps.home') config.include('rhodecode.apps.repository') config.include('rhodecode.apps.user_profile') config.include('rhodecode.apps.my_account') diff --git a/rhodecode/config/routing.py b/rhodecode/config/routing.py --- a/rhodecode/config/routing.py +++ b/rhodecode/config/routing.py @@ -193,11 +193,6 @@ def make_map(config): rmap.connect('repo_list_data', '/_repos', controller='home', action='repo_list_data') - rmap.connect('user_autocomplete_data', '/_users', controller='home', - action='user_autocomplete_data', jsroute=True) - rmap.connect('user_group_autocomplete_data', '/_user_groups', controller='home', - action='user_group_autocomplete_data', jsroute=True) - # TODO: johbo: Static links, to be replaced by our redirection mechanism rmap.connect('rst_help', 'http://docutils.sourceforge.net/docs/user/rst/quickref.html', diff --git a/rhodecode/controllers/home.py b/rhodecode/controllers/home.py --- a/rhodecode/controllers/home.py +++ b/rhodecode/controllers/home.py @@ -255,36 +255,3 @@ class HomeController(BaseController): 'results': res } return data - - @LoginRequired() - @XHRRequired() - @jsonify - def user_autocomplete_data(self): - query = request.GET.get('query') - active = str2bool(request.GET.get('active') or True) - - repo_model = RepoModel() - _users = repo_model.get_users( - name_contains=query, only_active=active) - - if request.GET.get('user_groups'): - # extend with user groups - _user_groups = repo_model.get_user_groups( - name_contains=query, only_active=active) - _users = _users + _user_groups - - return {'suggestions': _users} - - @LoginRequired() - @XHRRequired() - @jsonify - def user_group_autocomplete_data(self): - query = request.GET.get('query') - active = str2bool(request.GET.get('active') or True) - - repo_model = RepoModel() - _user_groups = repo_model.get_user_groups( - name_contains=query, only_active=active) - _user_groups = _user_groups - - return {'suggestions': _user_groups} diff --git a/rhodecode/tests/fixture.py b/rhodecode/tests/fixture.py --- a/rhodecode/tests/fixture.py +++ b/rhodecode/tests/fixture.py @@ -286,6 +286,10 @@ class Fixture(object): gr = UserGroup.get_by_group_name(group_name=name) if gr: return gr + # map active flag to the real attribute. For API consistency of fixtures + if 'active' in kwargs: + kwargs['users_group_active'] = kwargs['active'] + del kwargs['active'] form_data = self._get_user_group_create_params(name, **kwargs) owner = kwargs.get('cur_user', TEST_USER_ADMIN_LOGIN) user_group = UserGroupModel().create( diff --git a/rhodecode/tests/functional/test_home.py b/rhodecode/tests/functional/test_home.py --- a/rhodecode/tests/functional/test_home.py +++ b/rhodecode/tests/functional/test_home.py @@ -132,77 +132,6 @@ class TestHomeController(TestController) response.mustcontain(no=[version_string]) -class TestUserAutocompleteData(TestController): - def test_returns_list_of_users(self, user_util): - self.log_user() - user = user_util.create_user(is_active=True) - user_name = user.username - response = self.app.get( - url(controller='home', action='user_autocomplete_data'), - headers={'X-REQUESTED-WITH': 'XMLHttpRequest', }, status=200) - result = json.loads(response.body) - values = [suggestion['value'] for suggestion in result['suggestions']] - assert user_name in values - - def test_returns_inactive_users_when_active_flag_sent(self, user_util): - self.log_user() - user = user_util.create_user(is_active=False) - user_name = user.username - response = self.app.get( - url(controller='home', action='user_autocomplete_data', - user_groups='true', active='0'), - headers={'X-REQUESTED-WITH': 'XMLHttpRequest', }, status=200) - result = json.loads(response.body) - values = [suggestion['value'] for suggestion in result['suggestions']] - assert user_name in values - - def test_returns_groups_when_user_groups_sent(self, user_util): - self.log_user() - group = user_util.create_user_group(user_groups_active=True) - group_name = group.users_group_name - response = self.app.get( - url(controller='home', action='user_autocomplete_data', - user_groups='true'), - headers={'X-REQUESTED-WITH': 'XMLHttpRequest', }, status=200) - result = json.loads(response.body) - values = [suggestion['value'] for suggestion in result['suggestions']] - assert group_name in values - - def test_result_is_limited_when_query_is_sent(self): - self.log_user() - fake_result = [ - { - 'first_name': 'John', - 'value_display': 'hello{} (John Smith)'.format(i), - 'icon_link': '/images/user14.png', - 'value': 'hello{}'.format(i), - 'last_name': 'Smith', - 'username': 'hello{}'.format(i), - 'id': i, - 'value_type': u'user' - } - for i in range(10) - ] - users_patcher = patch.object( - RepoModel, 'get_users', return_value=fake_result) - groups_patcher = patch.object( - RepoModel, 'get_user_groups', return_value=fake_result) - - query = 'hello' - with users_patcher as users_mock, groups_patcher as groups_mock: - response = self.app.get( - url(controller='home', action='user_autocomplete_data', - user_groups='true', query=query), - headers={'X-REQUESTED-WITH': 'XMLHttpRequest', }, status=200) - - result = json.loads(response.body) - users_mock.assert_called_once_with( - name_contains=query, only_active=True) - groups_mock.assert_called_once_with( - name_contains=query, only_active=True) - assert len(result['suggestions']) == 20 - - def assert_and_get_content(result): repos = [] groups = []