# HG changeset patch # User Marcin Kuzminski # Date 2017-08-01 21:11:49 # Node ID 32399c6a34c2362de0319f470638ac145e3e1754 # Parent f7b19fb36a76d148bef0b60fa682d220275e96d7 integrations: rewrote usage of pylons components inside integrations. - added better auth decorators as used in other parts of the app - fixed tests - refactored the code to match other parts better (repo_route/repo_group_route) diff --git a/rhodecode/integrations/routes.py b/rhodecode/integrations/routes.py --- a/rhodecode/integrations/routes.py +++ b/rhodecode/integrations/routes.py @@ -21,6 +21,7 @@ import logging from rhodecode.apps._base import ADMIN_PREFIX, add_route_requirements +from rhodecode.lib.utils2 import safe_int from rhodecode.model.db import Repository, Integration, RepoGroup from rhodecode.integrations import integration_type_registry @@ -30,7 +31,6 @@ log = logging.getLogger(__name__) def includeme(config): # global integrations - config.add_route('global_integrations_new', ADMIN_PREFIX + '/integrations/new') config.add_view('rhodecode.integrations.views.GlobalIntegrationsView', @@ -45,7 +45,7 @@ def includeme(config): ADMIN_PREFIX + '/integrations/{integration}') for route_name in ['global_integrations_home', 'global_integrations_list']: config.add_view('rhodecode.integrations.views.GlobalIntegrationsView', - attr='index', + attr='integration_list', renderer='rhodecode:templates/admin/integrations/list.mako', request_method='GET', route_name=route_name) @@ -57,7 +57,6 @@ def includeme(config): ADMIN_PREFIX + '/integrations/{integration}/{integration_id}', custom_predicates=(valid_integration,)) - for route_name in ['global_integrations_create', 'global_integrations_edit']: config.add_view('rhodecode.integrations.views.GlobalIntegrationsView', attr='settings_get', @@ -70,154 +69,159 @@ def includeme(config): request_method='POST', route_name=route_name) - # repo group integrations config.add_route('repo_group_integrations_home', - add_route_requirements( - '{repo_group_name}/settings/integrations', - ), - custom_predicates=(valid_repo_group,) - ) - config.add_route('repo_group_integrations_list', - add_route_requirements( - '{repo_group_name}/settings/integrations/{integration}', - ), - custom_predicates=(valid_repo_group, valid_integration)) - for route_name in ['repo_group_integrations_home', 'repo_group_integrations_list']: - config.add_view('rhodecode.integrations.views.RepoGroupIntegrationsView', - attr='index', - renderer='rhodecode:templates/admin/integrations/list.mako', - request_method='GET', - route_name=route_name) + add_route_requirements('/{repo_group_name}/settings/integrations'), + repo_group_route=True) + + config.add_view('rhodecode.integrations.views.RepoGroupIntegrationsView', + attr='integration_list', + renderer='rhodecode:templates/admin/integrations/list.mako', + request_method='GET', + route_name='repo_group_integrations_home') config.add_route('repo_group_integrations_new', - add_route_requirements( - '{repo_group_name}/settings/integrations/new', - ), - custom_predicates=(valid_repo_group,)) + add_route_requirements('/{repo_group_name}/settings/integrations/new'), + repo_group_route=True) config.add_view('rhodecode.integrations.views.RepoGroupIntegrationsView', attr='new_integration', renderer='rhodecode:templates/admin/integrations/new.mako', request_method='GET', route_name='repo_group_integrations_new') + config.add_route('repo_group_integrations_list', + add_route_requirements('/{repo_group_name}/settings/integrations/{integration}'), + repo_group_route=True, + custom_predicates=(valid_integration,)) + config.add_view('rhodecode.integrations.views.RepoGroupIntegrationsView', + attr='integration_list', + renderer='rhodecode:templates/admin/integrations/list.mako', + request_method='GET', + route_name='repo_group_integrations_list') + config.add_route('repo_group_integrations_create', - add_route_requirements( - '{repo_group_name}/settings/integrations/{integration}/new', - ), - custom_predicates=(valid_repo_group, valid_integration)) + add_route_requirements('/{repo_group_name}/settings/integrations/{integration}/new'), + repo_group_route=True, + custom_predicates=(valid_integration,)) + config.add_view('rhodecode.integrations.views.RepoGroupIntegrationsView', + attr='settings_get', + renderer='rhodecode:templates/admin/integrations/form.mako', + request_method='GET', + route_name='repo_group_integrations_create') + config.add_view('rhodecode.integrations.views.RepoGroupIntegrationsView', + attr='settings_post', + renderer='rhodecode:templates/admin/integrations/form.mako', + request_method='POST', + route_name='repo_group_integrations_create') + config.add_route('repo_group_integrations_edit', - add_route_requirements( - '{repo_group_name}/settings/integrations/{integration}/{integration_id}', - ), - custom_predicates=(valid_repo_group, valid_integration)) - for route_name in ['repo_group_integrations_edit', 'repo_group_integrations_create']: - config.add_view('rhodecode.integrations.views.RepoGroupIntegrationsView', - attr='settings_get', - renderer='rhodecode:templates/admin/integrations/form.mako', - request_method='GET', - route_name=route_name) - config.add_view('rhodecode.integrations.views.RepoGroupIntegrationsView', - attr='settings_post', - renderer='rhodecode:templates/admin/integrations/form.mako', - request_method='POST', - route_name=route_name) + add_route_requirements('/{repo_group_name}/settings/integrations/{integration}/{integration_id}'), + repo_group_route=True, + custom_predicates=(valid_integration,)) + config.add_view('rhodecode.integrations.views.RepoGroupIntegrationsView', + attr='settings_get', + renderer='rhodecode:templates/admin/integrations/form.mako', + request_method='GET', + route_name='repo_group_integrations_edit') + config.add_view('rhodecode.integrations.views.RepoGroupIntegrationsView', + attr='settings_post', + renderer='rhodecode:templates/admin/integrations/form.mako', + request_method='POST', + route_name='repo_group_integrations_edit') # repo integrations config.add_route('repo_integrations_home', - add_route_requirements( - '{repo_name}/settings/integrations', - ), - custom_predicates=(valid_repo,)) - config.add_route('repo_integrations_list', - add_route_requirements( - '{repo_name}/settings/integrations/{integration}', - ), - custom_predicates=(valid_repo, valid_integration)) - for route_name in ['repo_integrations_home', 'repo_integrations_list']: - config.add_view('rhodecode.integrations.views.RepoIntegrationsView', - attr='index', - request_method='GET', - renderer='rhodecode:templates/admin/integrations/list.mako', - route_name=route_name) + add_route_requirements('/{repo_name}/settings/integrations'), + repo_route=True) + config.add_view('rhodecode.integrations.views.RepoIntegrationsView', + attr='integration_list', + request_method='GET', + renderer='rhodecode:templates/admin/integrations/list.mako', + route_name='repo_integrations_home') config.add_route('repo_integrations_new', - add_route_requirements( - '{repo_name}/settings/integrations/new', - ), - custom_predicates=(valid_repo,)) + add_route_requirements('/{repo_name}/settings/integrations/new'), + repo_route=True) config.add_view('rhodecode.integrations.views.RepoIntegrationsView', attr='new_integration', renderer='rhodecode:templates/admin/integrations/new.mako', request_method='GET', route_name='repo_integrations_new') + config.add_route('repo_integrations_list', + add_route_requirements('/{repo_name}/settings/integrations/{integration}'), + repo_route=True, + custom_predicates=(valid_integration,)) + config.add_view('rhodecode.integrations.views.RepoIntegrationsView', + attr='integration_list', + request_method='GET', + renderer='rhodecode:templates/admin/integrations/list.mako', + route_name='repo_integrations_list') + config.add_route('repo_integrations_create', - add_route_requirements( - '{repo_name}/settings/integrations/{integration}/new', - ), - custom_predicates=(valid_repo, valid_integration)) + add_route_requirements('/{repo_name}/settings/integrations/{integration}/new'), + repo_route=True, + custom_predicates=(valid_integration,)) + config.add_view('rhodecode.integrations.views.RepoIntegrationsView', + attr='settings_get', + renderer='rhodecode:templates/admin/integrations/form.mako', + request_method='GET', + route_name='repo_integrations_create') + config.add_view('rhodecode.integrations.views.RepoIntegrationsView', + attr='settings_post', + renderer='rhodecode:templates/admin/integrations/form.mako', + request_method='POST', + route_name='repo_integrations_create') + config.add_route('repo_integrations_edit', - add_route_requirements( - '{repo_name}/settings/integrations/{integration}/{integration_id}', - ), - custom_predicates=(valid_repo, valid_integration)) - for route_name in ['repo_integrations_edit', 'repo_integrations_create']: - config.add_view('rhodecode.integrations.views.RepoIntegrationsView', - attr='settings_get', - renderer='rhodecode:templates/admin/integrations/form.mako', - request_method='GET', - route_name=route_name) - config.add_view('rhodecode.integrations.views.RepoIntegrationsView', - attr='settings_post', - renderer='rhodecode:templates/admin/integrations/form.mako', - request_method='POST', - route_name=route_name) + add_route_requirements('/{repo_name}/settings/integrations/{integration}/{integration_id}'), + repo_route=True, + custom_predicates=(valid_integration,)) + config.add_view('rhodecode.integrations.views.RepoIntegrationsView', + attr='settings_get', + renderer='rhodecode:templates/admin/integrations/form.mako', + request_method='GET', + route_name='repo_integrations_edit') + config.add_view('rhodecode.integrations.views.RepoIntegrationsView', + attr='settings_post', + renderer='rhodecode:templates/admin/integrations/form.mako', + request_method='POST', + route_name='repo_integrations_edit') -def valid_repo(info, request): - repo = Repository.get_by_repo_name(info['match']['repo_name']) - if repo: - return True - - -def valid_repo_group(info, request): - repo_group = RepoGroup.get_by_group_name(info['match']['repo_group_name']) - if repo_group: - return True - return False - def valid_integration(info, request): integration_type = info['match']['integration'] integration_id = info['match'].get('integration_id') - repo_name = info['match'].get('repo_name') - repo_group_name = info['match'].get('repo_group_name') if integration_type not in integration_type_registry: return False - repo, repo_group = None, None - if repo_name: - repo = Repository.get_by_repo_name(repo_name) - if not repo: + if integration_id: + if not safe_int(integration_id): return False - if repo_group_name: - repo_group = RepoGroup.get_by_group_name(repo_group_name) - if not repo_group: - return False - - if repo_name and repo_group: - raise Exception('Either repo or repo_group can be set, not both') - - if integration_id: integration = Integration.get(integration_id) if not integration: return False if integration.integration_type != integration_type: return False + + # match types to repo or repo group + repo_name = info['match'].get('repo_name') + repo_group_name = info['match'].get('repo_group_name') + repo, repo_group = None, None + if repo_name: + repo = Repository.get_by_repo_name(repo_name) + if not repo: + return False + + if repo_group_name: + repo_group = RepoGroup.get_by_group_name(repo_group_name) + if not repo_group: + return False + if repo and repo.repo_id != integration.repo_id: return False if repo_group and repo_group.group_id != integration.repo_group_id: diff --git a/rhodecode/integrations/tests/__init__.py b/rhodecode/integrations/tests/__init__.py new file mode 100644 --- /dev/null +++ b/rhodecode/integrations/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/tests/functional/test_integrations.py b/rhodecode/integrations/tests/test_integrations.py rename from rhodecode/tests/functional/test_integrations.py rename to rhodecode/integrations/tests/test_integrations.py --- a/rhodecode/tests/functional/test_integrations.py +++ b/rhodecode/integrations/tests/test_integrations.py @@ -20,10 +20,16 @@ import pytest +from rhodecode.apps._base import ADMIN_PREFIX from rhodecode.model.db import Integration from rhodecode.model.meta import Session from rhodecode.integrations import integration_type_registry -from rhodecode.config.routing import ADMIN_PREFIX + + +def route_path(name, **kwargs): + return { + 'home': '/', + }[name].format(**kwargs) @pytest.mark.usefixtures('app', 'autologin_user') @@ -32,86 +38,141 @@ class TestIntegrationsView(object): class TestGlobalIntegrationsView(TestIntegrationsView): - def test_index_no_integrations(self, app): + def test_index_no_integrations(self): url = ADMIN_PREFIX + '/integrations' - response = app.get(url) + response = self.app.get(url) assert response.status_code == 200 assert 'exist yet' in response.body - def test_index_with_integrations(self, app, global_integration_stub): + def test_index_with_integrations(self, global_integration_stub): url = ADMIN_PREFIX + '/integrations' - response = app.get(url) + response = self.app.get(url) assert response.status_code == 200 assert 'exist yet' not in response.body assert global_integration_stub.name in response.body - def test_new_integration_page(self, app): + @pytest.mark.parametrize( + 'IntegrationType', integration_type_registry.values()) + def test_new_integration_page(self, IntegrationType): url = ADMIN_PREFIX + '/integrations/new' - response = app.get(url) - - assert response.status_code == 200 + response = self.app.get(url, status=200) - for integration_key in integration_type_registry: - nurl = (ADMIN_PREFIX + '/integrations/{integration}/new').format( - integration=integration_key) - assert nurl in response.body + url = (ADMIN_PREFIX + '/integrations/{integration}/new').format( + integration=IntegrationType.key) + assert url in response.body @pytest.mark.parametrize( 'IntegrationType', integration_type_registry.values()) - def test_get_create_integration_page(self, app, IntegrationType): + def test_get_create_integration_page(self, IntegrationType): url = ADMIN_PREFIX + '/integrations/{integration_key}/new'.format( integration_key=IntegrationType.key) - response = app.get(url) + response = self.app.get(url, status=200) - assert response.status_code == 200 assert IntegrationType.display_name in response.body - def test_post_integration_page(self, app, StubIntegrationType, csrf_token, + def test_post_integration_page(self, StubIntegrationType, csrf_token, test_repo_group, backend_random): url = ADMIN_PREFIX + '/integrations/{integration_key}/new'.format( integration_key=StubIntegrationType.key) - _post_integration_test_helper(app, url, csrf_token, admin_view=True, + _post_integration_test_helper(self.app, url, csrf_token, admin_view=True, + repo=backend_random.repo, repo_group=test_repo_group) + + +class TestRepoIntegrationsView(TestIntegrationsView): + def test_index_no_integrations(self, backend_random): + url = '/{repo_name}/settings/integrations'.format( + repo_name=backend_random.repo.repo_name) + response = self.app.get(url) + + assert response.status_code == 200 + assert 'exist yet' in response.body + + def test_index_with_integrations(self, repo_integration_stub): + url = '/{repo_name}/settings/integrations'.format( + repo_name=repo_integration_stub.repo.repo_name) + stub_name = repo_integration_stub.name + + response = self.app.get(url) + + assert response.status_code == 200 + assert stub_name in response.body + assert 'exist yet' not in response.body + + @pytest.mark.parametrize( + 'IntegrationType', integration_type_registry.values()) + def test_new_integration_page(self, backend_random, IntegrationType): + repo_name = backend_random.repo.repo_name + url = '/{repo_name}/settings/integrations/new'.format( + repo_name=repo_name) + + response = self.app.get(url, status=200) + + url = '/{repo_name}/settings/integrations/{integration}/new'.format( + repo_name=repo_name, + integration=IntegrationType.key) + + assert url in response.body + + @pytest.mark.parametrize( + 'IntegrationType', integration_type_registry.values()) + def test_get_create_integration_page(self, backend_random, IntegrationType): + repo_name = backend_random.repo.repo_name + url = '/{repo_name}/settings/integrations/{integration_key}/new'.format( + repo_name=repo_name, integration_key=IntegrationType.key) + + response = self.app.get(url, status=200) + + assert IntegrationType.display_name in response.body + + def test_post_integration_page(self, backend_random, test_repo_group, + StubIntegrationType, csrf_token): + repo_name = backend_random.repo.repo_name + url = '/{repo_name}/settings/integrations/{integration_key}/new'.format( + repo_name=repo_name, integration_key=StubIntegrationType.key) + + _post_integration_test_helper( + self.app, url, csrf_token, admin_view=False, repo=backend_random.repo, repo_group=test_repo_group) class TestRepoGroupIntegrationsView(TestIntegrationsView): - def test_index_no_integrations(self, app, test_repo_group): + def test_index_no_integrations(self, test_repo_group): url = '/{repo_group_name}/settings/integrations'.format( repo_group_name=test_repo_group.group_name) - response = app.get(url) + response = self.app.get(url) assert response.status_code == 200 assert 'exist yet' in response.body - def test_index_with_integrations(self, app, test_repo_group, - repogroup_integration_stub): + def test_index_with_integrations( + self, test_repo_group, repogroup_integration_stub): + url = '/{repo_group_name}/settings/integrations'.format( repo_group_name=test_repo_group.group_name) stub_name = repogroup_integration_stub.name - response = app.get(url) + response = self.app.get(url) assert response.status_code == 200 assert 'exist yet' not in response.body assert stub_name in response.body - def test_new_integration_page(self, app, test_repo_group): + def test_new_integration_page(self, test_repo_group): repo_group_name = test_repo_group.group_name url = '/{repo_group_name}/settings/integrations/new'.format( repo_group_name=test_repo_group.group_name) - response = app.get(url) + response = self.app.get(url) assert response.status_code == 200 for integration_key in integration_type_registry: - nurl = ('/{repo_group_name}/settings/integrations' - '/{integration}/new').format( + nurl = ('/{repo_group_name}/settings/integrations/{integration}/new').format( repo_group_name=repo_group_name, integration=integration_key) @@ -119,86 +180,29 @@ class TestRepoGroupIntegrationsView(Test @pytest.mark.parametrize( 'IntegrationType', integration_type_registry.values()) - def test_get_create_integration_page(self, app, test_repo_group, - IntegrationType): + def test_get_create_integration_page( + self, test_repo_group, IntegrationType): + repo_group_name = test_repo_group.group_name url = ('/{repo_group_name}/settings/integrations/{integration_key}/new' ).format(repo_group_name=repo_group_name, integration_key=IntegrationType.key) - response = app.get(url) + response = self.app.get(url) assert response.status_code == 200 assert IntegrationType.display_name in response.body - def test_post_integration_page(self, app, test_repo_group, backend_random, + def test_post_integration_page(self, test_repo_group, backend_random, StubIntegrationType, csrf_token): + repo_group_name = test_repo_group.group_name url = ('/{repo_group_name}/settings/integrations/{integration_key}/new' ).format(repo_group_name=repo_group_name, integration_key=StubIntegrationType.key) - _post_integration_test_helper(app, url, csrf_token, admin_view=False, - repo=backend_random.repo, repo_group=test_repo_group) - - -class TestRepoIntegrationsView(TestIntegrationsView): - def test_index_no_integrations(self, app, backend_random): - url = '/{repo_name}/settings/integrations'.format( - repo_name=backend_random.repo.repo_name) - response = app.get(url) - - assert response.status_code == 200 - assert 'exist yet' in response.body - - def test_index_with_integrations(self, app, repo_integration_stub): - url = '/{repo_name}/settings/integrations'.format( - repo_name=repo_integration_stub.repo.repo_name) - stub_name = repo_integration_stub.name - - response = app.get(url) - - assert response.status_code == 200 - assert stub_name in response.body - assert 'exist yet' not in response.body - - def test_new_integration_page(self, app, backend_random): - repo_name = backend_random.repo.repo_name - url = '/{repo_name}/settings/integrations/new'.format( - repo_name=repo_name) - - response = app.get(url) - - assert response.status_code == 200 - - for integration_key in integration_type_registry: - nurl = ('/{repo_name}/settings/integrations' - '/{integration}/new').format( - repo_name=repo_name, - integration=integration_key) - - assert nurl in response.body - - @pytest.mark.parametrize( - 'IntegrationType', integration_type_registry.values()) - def test_get_create_integration_page(self, app, backend_random, - IntegrationType): - repo_name = backend_random.repo.repo_name - url = '/{repo_name}/settings/integrations/{integration_key}/new'.format( - repo_name=repo_name, integration_key=IntegrationType.key) - - response = app.get(url) - - assert response.status_code == 200 - assert IntegrationType.display_name in response.body - - def test_post_integration_page(self, app, backend_random, test_repo_group, - StubIntegrationType, csrf_token): - repo_name = backend_random.repo.repo_name - url = '/{repo_name}/settings/integrations/{integration_key}/new'.format( - repo_name=repo_name, integration_key=StubIntegrationType.key) - - _post_integration_test_helper(app, url, csrf_token, admin_view=False, + _post_integration_test_helper( + self.app, url, csrf_token, admin_view=False, repo=backend_random.repo, repo_group=test_repo_group) @@ -208,7 +212,8 @@ def _post_integration_test_helper(app, u Posts form data to create integration at the url given then deletes it and checks if the redirect url is correct. """ - + repo_name = repo.repo_name + repo_group_name = repo_group.group_name app.post(url, params={}, status=403) # missing csrf check response = app.post(url, params={'csrf_token': csrf_token}) assert response.status_code == 200 @@ -216,15 +221,15 @@ def _post_integration_test_helper(app, u scopes_destinations = [ ('global', - ADMIN_PREFIX + '/integrations'), + ADMIN_PREFIX + '/integrations'), ('root-repos', ADMIN_PREFIX + '/integrations'), - ('repo:%s' % repo.repo_name, - '/%s/settings/integrations' % repo.repo_name), - ('repogroup:%s' % repo_group.group_name, - '/%s/settings/integrations' % repo_group.group_name), - ('repogroup-recursive:%s' % repo_group.group_name, - '/%s/settings/integrations' % repo_group.group_name), + ('repo:%s' % repo_name, + '/%s/settings/integrations' % repo_name), + ('repogroup:%s' % repo_group_name, + '/%s/settings/integrations' % repo_group_name), + ('repogroup-recursive:%s' % repo_group_name, + '/%s/settings/integrations' % repo_group_name), ] for scope, destination in scopes_destinations: diff --git a/rhodecode/integrations/views.py b/rhodecode/integrations/views.py --- a/rhodecode/integrations/views.py +++ b/rhodecode/integrations/views.py @@ -18,45 +18,40 @@ # RhodeCode Enterprise Edition, including its added features, Support services, # and proprietary license terms, please see https://rhodecode.com/licenses/ -import pylons import deform import logging -import colander import peppercorn import webhelpers.paginate -from pyramid.httpexceptions import HTTPFound, HTTPForbidden, HTTPBadRequest -from pyramid.renderers import render -from pyramid.response import Response +from pyramid.httpexceptions import HTTPFound, HTTPForbidden +from rhodecode.apps._base import BaseAppView +from rhodecode.integrations import integration_type_registry from rhodecode.apps.admin.navigation import navigation_list -from rhodecode.lib import auth -from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator +from rhodecode.lib.auth import ( + LoginRequired, CSRFRequired, HasPermissionAnyDecorator, + HasRepoPermissionAnyDecorator, HasRepoGroupPermissionAnyDecorator) from rhodecode.lib.utils2 import safe_int from rhodecode.lib.helpers import Page from rhodecode.model.db import Repository, RepoGroup, Session, Integration from rhodecode.model.scm import ScmModel from rhodecode.model.integration import IntegrationModel -from rhodecode.translation import _ -from rhodecode.integrations import integration_type_registry from rhodecode.model.validation_schema.schemas.integration_schema import ( make_integration_schema, IntegrationScopeType) log = logging.getLogger(__name__) -class IntegrationSettingsViewBase(object): - """ Base Integration settings view used by both repo / global settings """ +class IntegrationSettingsViewBase(BaseAppView): + """ + Base Integration settings view used by both repo / global settings + """ def __init__(self, context, request): - self.context = context - self.request = request - self._load_general_context() + super(IntegrationSettingsViewBase, self).__init__(context, request) + self._load_view_context() - if not self.perm_check(request.user): - raise HTTPForbidden() - - def _load_general_context(self): + def _load_view_context(self): """ This avoids boilerplate for repo/global+list/edit+views/templates by doing all possible contexts at the same time however it should @@ -110,25 +105,12 @@ class IntegrationSettingsViewBase(object return False - def _template_c_context(self): - # TODO: dan: this is a stopgap in order to inherit from current pylons - # based admin/repo settings templates - this should be removed entirely - # after port to pyramid + def _get_local_tmpl_context(self, include_app_defaults=False): + _ = self.request.translate + c = super(IntegrationSettingsViewBase, self)._get_local_tmpl_context( + include_app_defaults=include_app_defaults) - c = pylons.tmpl_context c.active = 'integrations' - c.rhodecode_user = self.request.user - c.repo = self.repo - c.repo_group = self.repo_group - c.repo_name = self.repo and self.repo.repo_name or None - c.repo_group_name = self.repo_group and self.repo_group.group_name or None - - if self.repo: - c.repo_info = self.repo - c.rhodecode_db_repo = self.repo - c.repository_pull_requests = ScmModel().get_pull_requests(self.repo) - else: - c.navlist = navigation_list(self.request) return c @@ -142,6 +124,7 @@ class IntegrationSettingsViewBase(object no_scope=not self.admin_view) def _form_defaults(self): + _ = self.request.translate defaults = {} if self.integration: @@ -178,28 +161,79 @@ class IntegrationSettingsViewBase(object return defaults def _delete_integration(self, integration): - Session().delete(self.integration) + _ = self.request.translate + Session().delete(integration) Session().commit() self.request.session.flash( _('Integration {integration_name} deleted successfully.').format( - integration_name=self.integration.name), + integration_name=integration.name), queue='success') if self.repo: - redirect_to = self.request.route_url( + redirect_to = self.request.route_path( 'repo_integrations_home', repo_name=self.repo.repo_name) elif self.repo_group: - redirect_to = self.request.route_url( + redirect_to = self.request.route_path( 'repo_group_integrations_home', repo_group_name=self.repo_group.group_name) else: - redirect_to = self.request.route_url('global_integrations_home') + redirect_to = self.request.route_path('global_integrations_home') raise HTTPFound(redirect_to) - def settings_get(self, defaults=None, form=None): + def _integration_list(self): + """ List integrations """ + + c = self.load_default_context() + if self.repo: + scope = self.repo + elif self.repo_group: + scope = self.repo_group + else: + scope = 'all' + + integrations = [] + + for IntType, integration in IntegrationModel().get_integrations( + scope=scope, IntegrationType=self.IntegrationType): + + # extra permissions check *just in case* + if not self._has_perms_for_integration(integration): + continue + + integrations.append((IntType, integration)) + + sort_arg = self.request.GET.get('sort', 'name:asc') + if ':' in sort_arg: + sort_field, sort_dir = sort_arg.split(':') + else: + sort_field = sort_arg, 'asc' + + assert sort_field in ('name', 'integration_type', 'enabled', 'scope') + + integrations.sort( + key=lambda x: getattr(x[1], sort_field), + reverse=(sort_dir == 'desc')) + + page_url = webhelpers.paginate.PageURL( + self.request.path, self.request.GET) + page = safe_int(self.request.GET.get('page', 1), 1) + + integrations = Page( + integrations, page=page, items_per_page=10, url=page_url) + + c.rev_sort_dir = sort_dir != 'desc' and 'desc' or 'asc' + + c.current_IntegrationType = self.IntegrationType + c.integrations_list = integrations + c.available_integrations = integration_type_registry + + return self._get_template_context(c) + + def _settings_get(self, defaults=None, form=None): """ View that displays the integration settings as a form. """ + c = self.load_default_context() defaults = defaults or self._form_defaults() schema = self._form_schema() @@ -211,20 +245,18 @@ class IntegrationSettingsViewBase(object form = form or deform.Form(schema, appstruct=defaults, buttons=buttons) - template_context = { - 'form': form, - 'current_IntegrationType': self.IntegrationType, - 'integration': self.integration, - 'c': self._template_c_context(), - } + c.form = form + c.current_IntegrationType = self.IntegrationType + c.integration = self.integration - return template_context + return self._get_template_context(c) - @auth.CSRFRequired() - def settings_post(self): + def _settings_post(self): """ View that validates and stores the integration settings. """ + _ = self.request.translate + controls = self.request.POST.items() pstruct = peppercorn.parse(controls) @@ -264,7 +296,7 @@ class IntegrationSettingsViewBase(object _('Errors exist when saving integration settings. ' 'Please check the form inputs.'), queue='error') - return self.settings_get(form=e) + return self._settings_get(form=e) if not self.integration: self.integration = Integration() @@ -314,77 +346,110 @@ class IntegrationSettingsViewBase(object return HTTPFound(redirect_to) - def index(self): - """ List integrations """ - if self.repo: - scope = self.repo - elif self.repo_group: - scope = self.repo_group - else: - scope = 'all' - - integrations = [] - - for IntType, integration in IntegrationModel().get_integrations( - scope=scope, IntegrationType=self.IntegrationType): - - # extra permissions check *just in case* - if not self._has_perms_for_integration(integration): - continue - - integrations.append((IntType, integration)) - - sort_arg = self.request.GET.get('sort', 'name:asc') - if ':' in sort_arg: - sort_field, sort_dir = sort_arg.split(':') - else: - sort_field = sort_arg, 'asc' - - assert sort_field in ('name', 'integration_type', 'enabled', 'scope') + def _new_integration(self): + c = self.load_default_context() + c.available_integrations = integration_type_registry + return self._get_template_context(c) - integrations.sort( - key=lambda x: getattr(x[1], sort_field), - reverse=(sort_dir == 'desc')) - - page_url = webhelpers.paginate.PageURL( - self.request.path, self.request.GET) - page = safe_int(self.request.GET.get('page', 1), 1) - - integrations = Page(integrations, page=page, items_per_page=10, - url=page_url) - - template_context = { - 'sort_field': sort_field, - 'rev_sort_dir': sort_dir != 'desc' and 'desc' or 'asc', - 'current_IntegrationType': self.IntegrationType, - 'integrations_list': integrations, - 'available_integrations': integration_type_registry, - 'c': self._template_c_context(), - 'request': self.request, - } - return template_context - - def new_integration(self): - template_context = { - 'available_integrations': integration_type_registry, - 'c': self._template_c_context(), - } - return template_context + def load_default_context(self): + raise NotImplementedError() class GlobalIntegrationsView(IntegrationSettingsViewBase): - def perm_check(self, user): - return auth.HasPermissionAll('hg.admin').check_permissions(user=user) + def load_default_context(self): + c = self._get_local_tmpl_context() + c.repo = self.repo + c.repo_group = self.repo_group + c.navlist = navigation_list(self.request) + self._register_global_c(c) + return c + + @LoginRequired() + @HasPermissionAnyDecorator('hg.admin') + def integration_list(self): + return self._integration_list() + + @LoginRequired() + @HasPermissionAnyDecorator('hg.admin') + def settings_get(self): + return self._settings_get() + + @LoginRequired() + @HasPermissionAnyDecorator('hg.admin') + @CSRFRequired() + def settings_post(self): + return self._settings_post() + + @LoginRequired() + @HasPermissionAnyDecorator('hg.admin') + def new_integration(self): + return self._new_integration() class RepoIntegrationsView(IntegrationSettingsViewBase): - def perm_check(self, user): - return auth.HasRepoPermissionAll('repository.admin')( - repo_name=self.repo.repo_name, user=user) + def load_default_context(self): + c = self._get_local_tmpl_context() + + c.repo = self.repo + c.repo_group = self.repo_group + + # TODO(marcink): remove repo_info and use c.rhodecode_db_repo instead + c.repo_info = self.db_repo = self.repo + c.rhodecode_db_repo = self.repo + c.repo_name = self.db_repo.repo_name + c.repository_pull_requests = ScmModel().get_pull_requests(self.repo) + + self._register_global_c(c) + return c + + @LoginRequired() + @HasRepoPermissionAnyDecorator('repository.admin') + def integration_list(self): + return self._integration_list() + + @LoginRequired() + @HasRepoPermissionAnyDecorator('repository.admin') + def settings_get(self): + return self._settings_get() + + @LoginRequired() + @HasRepoPermissionAnyDecorator('repository.admin') + @CSRFRequired() + def settings_post(self): + return self._settings_post() + + @LoginRequired() + @HasRepoPermissionAnyDecorator('repository.admin') + def new_integration(self): + return self._new_integration() class RepoGroupIntegrationsView(IntegrationSettingsViewBase): - def perm_check(self, user): - return auth.HasRepoGroupPermissionAll('group.admin')( - group_name=self.repo_group.group_name, user=user) + def load_default_context(self): + c = self._get_local_tmpl_context() + c.repo = self.repo + c.repo_group = self.repo_group + c.navlist = navigation_list(self.request) + self._register_global_c(c) + return c + + @LoginRequired() + @HasRepoGroupPermissionAnyDecorator('group.admin') + def integration_list(self): + return self._integration_list() + @LoginRequired() + @HasRepoGroupPermissionAnyDecorator('group.admin') + def settings_get(self): + return self._settings_get() + + @LoginRequired() + @HasRepoGroupPermissionAnyDecorator('group.admin') + @CSRFRequired() + def settings_post(self): + return self._settings_post() + + @LoginRequired() + @HasRepoGroupPermissionAnyDecorator('group.admin') + def new_integration(self): + return self._new_integration() diff --git a/rhodecode/public/js/rhodecode/routes.js b/rhodecode/public/js/rhodecode/routes.js --- a/rhodecode/public/js/rhodecode/routes.js +++ b/rhodecode/public/js/rhodecode/routes.js @@ -23,13 +23,13 @@ function registerRCRoutes() { pyroutes.register('global_integrations_create', '/_admin/integrations/%(integration)s/new', ['integration']); pyroutes.register('global_integrations_edit', '/_admin/integrations/%(integration)s/%(integration_id)s', ['integration', 'integration_id']); pyroutes.register('repo_group_integrations_home', '/%(repo_group_name)s/settings/integrations', ['repo_group_name']); + pyroutes.register('repo_group_integrations_new', '/%(repo_group_name)s/settings/integrations/new', ['repo_group_name']); pyroutes.register('repo_group_integrations_list', '/%(repo_group_name)s/settings/integrations/%(integration)s', ['repo_group_name', 'integration']); - pyroutes.register('repo_group_integrations_new', '/%(repo_group_name)s/settings/integrations/new', ['repo_group_name']); pyroutes.register('repo_group_integrations_create', '/%(repo_group_name)s/settings/integrations/%(integration)s/new', ['repo_group_name', 'integration']); pyroutes.register('repo_group_integrations_edit', '/%(repo_group_name)s/settings/integrations/%(integration)s/%(integration_id)s', ['repo_group_name', 'integration', 'integration_id']); pyroutes.register('repo_integrations_home', '/%(repo_name)s/settings/integrations', ['repo_name']); + pyroutes.register('repo_integrations_new', '/%(repo_name)s/settings/integrations/new', ['repo_name']); pyroutes.register('repo_integrations_list', '/%(repo_name)s/settings/integrations/%(integration)s', ['repo_name', 'integration']); - pyroutes.register('repo_integrations_new', '/%(repo_name)s/settings/integrations/new', ['repo_name']); pyroutes.register('repo_integrations_create', '/%(repo_name)s/settings/integrations/%(integration)s/new', ['repo_name', 'integration']); pyroutes.register('repo_integrations_edit', '/%(repo_name)s/settings/integrations/%(integration)s/%(integration_id)s', ['repo_name', 'integration', 'integration_id']); pyroutes.register('ops_ping', '/_admin/ops/ping', []); diff --git a/rhodecode/templates/admin/integrations/form.mako b/rhodecode/templates/admin/integrations/form.mako --- a/rhodecode/templates/admin/integrations/form.mako +++ b/rhodecode/templates/admin/integrations/form.mako @@ -7,10 +7,10 @@ » ${h.link_to(_('Integrations'),request.route_url(route_name='repo_integrations_home', repo_name=c.repo.repo_name))} » - ${h.link_to(current_IntegrationType.display_name, + ${h.link_to(c.current_IntegrationType.display_name, request.route_url(route_name='repo_integrations_list', repo_name=c.repo.repo_name, - integration=current_IntegrationType.key))} + integration=c.current_IntegrationType.key))} %elif c.repo_group: ${h.link_to(_('Admin'),h.route_path('admin_home'))} » @@ -20,10 +20,10 @@ » ${h.link_to(_('Integrations'),request.route_url(route_name='repo_group_integrations_home', repo_group_name=c.repo_group.group_name))} » - ${h.link_to(current_IntegrationType.display_name, + ${h.link_to(c.current_IntegrationType.display_name, request.route_url(route_name='repo_group_integrations_list', repo_group_name=c.repo_group.group_name, - integration=current_IntegrationType.key))} + integration=c.current_IntegrationType.key))} %else: ${h.link_to(_('Admin'),h.route_path('admin_home'))} » @@ -31,17 +31,17 @@ » ${h.link_to(_('Integrations'),request.route_url(route_name='global_integrations_home'))} » - ${h.link_to(current_IntegrationType.display_name, + ${h.link_to(c.current_IntegrationType.display_name, request.route_url(route_name='global_integrations_list', - integration=current_IntegrationType.key))} + integration=c.current_IntegrationType.key))} %endif - %if integration: + %if c.integration: » - ${integration.name} - %elif current_IntegrationType: + ${c.integration.name} + %elif c.current_IntegrationType: » - ${current_IntegrationType.display_name} + ${c.current_IntegrationType.display_name} %endif @@ -54,16 +54,16 @@

- %if integration: - ${current_IntegrationType.display_name} - ${integration.name} + %if c.integration: + ${c.current_IntegrationType.display_name} - ${c.integration.name} %else: ${_('Create New %(integration_type)s Integration') % { - 'integration_type': current_IntegrationType.display_name + 'integration_type': c.current_IntegrationType.display_name }} %endif

- ${form.render() | n} + ${c.form.render() | n}
diff --git a/rhodecode/templates/admin/integrations/list.mako b/rhodecode/templates/admin/integrations/list.mako --- a/rhodecode/templates/admin/integrations/list.mako +++ b/rhodecode/templates/admin/integrations/list.mako @@ -15,22 +15,22 @@ » ${h.link_to(_('Settings'),h.url('admin_settings'))} %endif - %if current_IntegrationType: + %if c.current_IntegrationType: » %if c.repo: ${h.link_to(_('Integrations'), - request.route_url(route_name='repo_integrations_home', - repo_name=c.repo.repo_name))} + request.route_path(route_name='repo_integrations_home', + repo_name=c.repo.repo_name))} %elif c.repo_group: ${h.link_to(_('Integrations'), - request.route_url(route_name='repo_group_integrations_home', - repo_group_name=c.repo_group.group_name))} + request.route_path(route_name='repo_group_integrations_home', + repo_group_name=c.repo_group.group_name))} %else: ${h.link_to(_('Integrations'), - request.route_url(route_name='global_integrations_home'))} + request.route_path(route_name='global_integrations_home'))} %endif » - ${current_IntegrationType.display_name} + ${c.current_IntegrationType.display_name} %else: » ${_('Integrations')} @@ -61,9 +61,9 @@ home_url = request.route_path('global_integrations_home') %> - ${_('All')} + ${_('All')} - %for integration_key, IntegrationType in available_integrations.items(): + %for integration_key, IntegrationType in c.available_integrations.items(): <% if c.repo: list_url = request.route_path('repo_integrations_list', @@ -78,12 +78,14 @@ integration=integration_key) %> + class="btn ${c.current_IntegrationType and integration_key == c.current_IntegrationType.key and 'btn-primary' or ''}"> ${IntegrationType.display_name} %endfor <% + integration_type = c.current_IntegrationType and c.current_IntegrationType.display_name or '' + if c.repo: create_url = h.route_path('repo_integrations_new', repo_name=c.repo.repo_name) elif c.repo_group: @@ -98,19 +100,19 @@ - - - - + + + + - %if not integrations_list: + %if not c.integrations_list: %endif - %for IntegrationType, integration in integrations_list: + %for IntegrationType, integration in c.integrations_list:
${_('Enabled')}${_('Name')}${_('Type')}${_('Scope')}${_('Enabled')}${_('Name')}${_('Type')}${_('Scope')} ${_('Actions')}
- <% integration_type = current_IntegrationType and current_IntegrationType.display_name or '' %> + %if c.repo: ${_('No {type} integrations for repo {repo} exist yet.').format(type=integration_type, repo=c.repo.repo_name)} %elif c.repo_group: @@ -119,14 +121,14 @@ ${_('No {type} integrations exist yet.').format(type=integration_type)} %endif - %if current_IntegrationType: + %if c.current_IntegrationType: <% if c.repo: - create_url = h.route_path('repo_integrations_create', repo_name=c.repo.repo_name, integration=current_IntegrationType.key) + create_url = h.route_path('repo_integrations_create', repo_name=c.repo.repo_name, integration=c.current_IntegrationType.key) elif c.repo_group: - create_url = h.route_path('repo_group_integrations_create', repo_group_name=c.repo_group.group_name, integration=current_IntegrationType.key) + create_url = h.route_path('repo_group_integrations_create', repo_group_name=c.repo_group.group_name, integration=c.current_IntegrationType.key) else: - create_url = h.route_path('global_integrations_create', integration=current_IntegrationType.key) + create_url = h.route_path('global_integrations_create', integration=c.current_IntegrationType.key) %> %endif @@ -134,7 +136,7 @@
%if integration.enabled: @@ -147,9 +149,9 @@ ${integration.name} - %if integration.integration_type in available_integrations: + %if integration.integration_type in c.available_integrations:
- ${available_integrations[integration.integration_type].icon|n} + ${c.available_integrations[integration.integration_type].icon|n}
%else: ? @@ -220,7 +222,7 @@
- ${integrations_list.pager('$link_previous ~2~ $link_next')} + ${c.integrations_list.pager('$link_previous ~2~ $link_next')}
diff --git a/rhodecode/templates/admin/integrations/new.mako b/rhodecode/templates/admin/integrations/new.mako --- a/rhodecode/templates/admin/integrations/new.mako +++ b/rhodecode/templates/admin/integrations/new.mako @@ -36,7 +36,7 @@ %endif - %for integration, IntegrationType in available_integrations.items(): + %for integration, IntegrationType in c.available_integrations.items(): <% if c.repo: create_url = request.route_path('repo_integrations_create', diff --git a/rhodecode/tests/integrations/test_integration.py b/rhodecode/tests/integrations/test_integration.py --- a/rhodecode/tests/integrations/test_integration.py +++ b/rhodecode/tests/integrations/test_integration.py @@ -25,21 +25,21 @@ from rhodecode import events from rhodecode.tests.fixture import Fixture from rhodecode.model.db import Session, Integration from rhodecode.model.integration import IntegrationModel -from rhodecode.integrations.types.base import IntegrationTypeBase class TestDeleteScopesDeletesIntegrations(object): - def test_delete_repo_with_integration_deletes_integration(self, - repo_integration_stub): + def test_delete_repo_with_integration_deletes_integration( + self, repo_integration_stub): + Session().delete(repo_integration_stub.repo) Session().commit() Session().expire_all() integration = Integration.get(repo_integration_stub.integration_id) assert integration is None + def test_delete_repo_group_with_integration_deletes_integration( + self, repogroup_integration_stub): - def test_delete_repo_group_with_integration_deletes_integration(self, - repogroup_integration_stub): Session().delete(repogroup_integration_stub.repo_group) Session().commit() Session().expire_all() @@ -146,7 +146,6 @@ def test_enabled_integration_repo_scopes integrations['root_repo'], ] - triggered_integrations = IntegrationModel().get_for_event( events.RepoEvent(repos['other_repo'])) @@ -157,7 +156,6 @@ def test_enabled_integration_repo_scopes integrations['other_group_recursive'], ] - triggered_integrations = IntegrationModel().get_for_event( events.RepoEvent(repos['parent_repo'])) @@ -193,26 +191,22 @@ def test_disabled_integration_repo_scope assert triggered_integrations == [] - triggered_integrations = IntegrationModel().get_for_event( events.RepoEvent(repos['parent_repo'])) assert triggered_integrations == [] - triggered_integrations = IntegrationModel().get_for_event( events.RepoEvent(repos['child_repo'])) assert triggered_integrations == [] - triggered_integrations = IntegrationModel().get_for_event( events.RepoEvent(repos['other_repo'])) assert triggered_integrations == [] - def test_enabled_non_repo_integrations(integration_repos): integrations = integration_repos['integrations'] diff --git a/rhodecode/tests/plugin.py b/rhodecode/tests/plugin.py --- a/rhodecode/tests/plugin.py +++ b/rhodecode/tests/plugin.py @@ -336,7 +336,7 @@ def test_repo_group(request): usage automatically """ fixture = Fixture() - repogroupid = 'test_repo_group_%s' % int(time.time()) + repogroupid = 'test_repo_group_%s' % str(time.time()).replace('.', '') repo_group = fixture.create_repo_group(repogroupid) def _cleanup(): @@ -353,7 +353,7 @@ def test_user_group(request): usage automatically """ fixture = Fixture() - usergroupid = 'test_user_group_%s' % int(time.time()) + usergroupid = 'test_user_group_%s' % str(time.time()).replace('.', '') user_group = fixture.create_user_group(usergroupid) def _cleanup():