Changeset - 555c8d26988f
[Not reviewed]
default
0 6 0
Søren Løvborg - 9 years ago 2016-09-13 18:40:49
sorenl@unity3d.com
db: always add to session in Setting.create_or_update

There's no use case for not adding the newly created Setting to the
SQLAlchemy session (and thus, once we commit, the database).

With this change, all the various "create" model methods add their
result to the session.
6 files changed with 12 insertions and 30 deletions:
0 comments (0 inline, 0 general)
kallithea/controllers/admin/auth_settings.py
Show inline comments
 
@@ -86,65 +86,64 @@ class AuthSettingsController(BaseControl
 
            c.defaults.update(defaults)
 

	
 
        log.debug(formatted_json(defaults))
 
        return formencode.htmlfill.render(
 
            render('admin/auth/auth_settings.html'),
 
            defaults=c.defaults,
 
            errors=errors,
 
            prefix_error=False,
 
            encoding="UTF-8",
 
            force_defaults=False)
 

	
 
    def index(self):
 
        self.__load_defaults()
 
        return self.__render(defaults=None, errors=None)
 

	
 
    def auth_settings(self):
 
        """POST create and store auth settings"""
 
        self.__load_defaults()
 
        log.debug("POST Result: %s", formatted_json(dict(request.POST)))
 

	
 
        # First, parse only the plugin list (not the plugin settings).
 
        _auth_plugins_validator = AuthSettingsForm([]).fields['auth_plugins']
 
        try:
 
            new_enabled_plugins = _auth_plugins_validator.to_python(request.POST.get('auth_plugins'))
 
        except formencode.Invalid:
 
            # User provided an invalid plugin list. Just fall back to
 
            # the list of currently enabled plugins. (We'll re-validate
 
            # and show an error message to the user, below.)
 
            pass
 
        else:
 
            # Hide plugins that the user has asked to be disabled, but
 
            # do not show plugins that the user has asked to be enabled
 
            # (yet), since that'll cause validation errors and/or wrong
 
            # settings being applied (e.g. checkboxes being cleared),
 
            # since the plugin settings will not be in the POST data.
 
            c.enabled_plugins = [ p for p in c.enabled_plugins if p in new_enabled_plugins ]
 

	
 
        # Next, parse everything including plugin settings.
 
        _form = AuthSettingsForm(c.enabled_plugins)()
 

	
 
        try:
 
            form_result = _form.to_python(dict(request.POST))
 
            for k, v in form_result.items():
 
                if k == 'auth_plugins':
 
                    # we want to store it comma separated inside our settings
 
                    v = ','.join(v)
 
                log.debug("%s = %s", k, str(v))
 
                setting = Setting.create_or_update(k, v)
 
                Session().add(setting)
 
            Session().commit()
 
            h.flash(_('Auth settings updated successfully'),
 
                       category='success')
 
        except formencode.Invalid as errors:
 
            log.error(traceback.format_exc())
 
            e = errors.error_dict or {}
 
            return self.__render(
 
                defaults=errors.value,
 
                errors=e,
 
            )
 
        except Exception:
 
            log.error(traceback.format_exc())
 
            h.flash(_('error occurred during update of auth settings'),
 
                    category='error')
 

	
 
        raise HTTPFound(location=url('auth_home'))
kallithea/controllers/admin/defaults.py
Show inline comments
 
@@ -26,69 +26,68 @@ Original author and date, and relevant c
 
"""
 

	
 
import logging
 
import traceback
 
import formencode
 
from formencode import htmlfill
 

	
 
from pylons import request, tmpl_context as c
 
from pylons.i18n.translation import _
 
from webob.exc import HTTPFound
 

	
 
from kallithea.config.routing import url
 
from kallithea.lib import helpers as h
 
from kallithea.lib.auth import LoginRequired, HasPermissionAnyDecorator
 
from kallithea.lib.base import BaseController, render
 
from kallithea.model.forms import DefaultsForm
 
from kallithea.model.meta import Session
 
from kallithea import BACKENDS
 
from kallithea.model.db import Setting
 

	
 
log = logging.getLogger(__name__)
 

	
 

	
 
class DefaultsController(BaseController):
 

	
 
    @LoginRequired()
 
    @HasPermissionAnyDecorator('hg.admin')
 
    def __before__(self):
 
        super(DefaultsController, self).__before__()
 

	
 
    def index(self, format='html'):
 
        c.backends = BACKENDS.keys()
 
        defaults = Setting.get_default_repo_settings()
 

	
 
        return htmlfill.render(
 
            render('admin/defaults/defaults.html'),
 
            defaults=defaults,
 
            encoding="UTF-8",
 
            force_defaults=False
 
        )
 

	
 
    def update(self, id):
 
        _form = DefaultsForm()()
 

	
 
        try:
 
            form_result = _form.to_python(dict(request.POST))
 
            for k, v in form_result.iteritems():
 
                setting = Setting.create_or_update(k, v)
 
                Session().add(setting)
 
            Session().commit()
 
            h.flash(_('Default settings updated successfully'),
 
                    category='success')
 

	
 
        except formencode.Invalid as errors:
 
            defaults = errors.value
 

	
 
            return htmlfill.render(
 
                render('admin/defaults/defaults.html'),
 
                defaults=defaults,
 
                errors=errors.error_dict or {},
 
                prefix_error=False,
 
                encoding="UTF-8",
 
                force_defaults=False)
 
        except Exception:
 
            log.error(traceback.format_exc())
 
            h.flash(_('Error occurred during update of defaults'),
 
                    category='error')
 

	
 
        raise HTTPFound(location=url('defaults'))
kallithea/controllers/admin/settings.py
Show inline comments
 
@@ -163,170 +163,157 @@ class SettingsController(BaseController)
 
            invalidate_cache = request.POST.get('invalidate', False)
 
            log.debug('rescanning repo location with destroy obsolete=%s, '
 
                      'install git hooks=%s and '
 
                      'overwrite git hooks=%s' % (rm_obsolete, install_git_hooks, overwrite_git_hooks))
 

	
 
            if invalidate_cache:
 
                log.debug('invalidating all repositories cache')
 
                for repo in Repository.query():
 
                    ScmModel().mark_for_invalidation(repo.repo_name)
 

	
 
            filesystem_repos = ScmModel().repo_scan()
 
            added, removed = repo2db_mapper(filesystem_repos, rm_obsolete,
 
                                            install_git_hooks=install_git_hooks,
 
                                            user=c.authuser.username,
 
                                            overwrite_git_hooks=overwrite_git_hooks)
 
            h.flash(h.literal(_('Repositories successfully rescanned. Added: %s. Removed: %s.') %
 
                (', '.join(h.link_to(safe_unicode(repo_name), h.url('summary_home', repo_name=repo_name))
 
                 for repo_name in added) or '-',
 
                 ', '.join(h.escape(safe_unicode(repo_name)) for repo_name in removed) or '-')),
 
                category='success')
 
            raise HTTPFound(location=url('admin_settings_mapping'))
 

	
 
        defaults = Setting.get_app_settings()
 
        defaults.update(self._get_hg_ui_settings())
 

	
 
        return htmlfill.render(
 
            render('admin/settings/settings.html'),
 
            defaults=defaults,
 
            encoding="UTF-8",
 
            force_defaults=False)
 

	
 
    @HasPermissionAnyDecorator('hg.admin')
 
    def settings_global(self):
 
        c.active = 'global'
 
        if request.POST:
 
            application_form = ApplicationSettingsForm()()
 
            try:
 
                form_result = application_form.to_python(dict(request.POST))
 
            except formencode.Invalid as errors:
 
                return htmlfill.render(
 
                    render('admin/settings/settings.html'),
 
                    defaults=errors.value,
 
                    errors=errors.error_dict or {},
 
                    prefix_error=False,
 
                    encoding="UTF-8",
 
                    force_defaults=False)
 

	
 
            try:
 
                sett1 = Setting.create_or_update('title',
 
                                            form_result['title'])
 
                Session().add(sett1)
 

	
 
                sett2 = Setting.create_or_update('realm',
 
                                            form_result['realm'])
 
                Session().add(sett2)
 

	
 
                sett3 = Setting.create_or_update('ga_code',
 
                                            form_result['ga_code'])
 
                Session().add(sett3)
 

	
 
                sett4 = Setting.create_or_update('captcha_public_key',
 
                                    form_result['captcha_public_key'])
 
                Session().add(sett4)
 

	
 
                sett5 = Setting.create_or_update('captcha_private_key',
 
                                    form_result['captcha_private_key'])
 
                Session().add(sett5)
 
                for setting in (
 
                    'title',
 
                    'realm',
 
                    'ga_code',
 
                    'captcha_public_key',
 
                    'captcha_private_key',
 
                ):
 
                    Setting.create_or_update(setting, form_result[setting])
 

	
 
                Session().commit()
 
                set_app_settings(config)
 
                h.flash(_('Updated application settings'), category='success')
 

	
 
            except Exception:
 
                log.error(traceback.format_exc())
 
                h.flash(_('Error occurred while updating '
 
                          'application settings'),
 
                          category='error')
 

	
 
            raise HTTPFound(location=url('admin_settings_global'))
 

	
 
        defaults = Setting.get_app_settings()
 
        defaults.update(self._get_hg_ui_settings())
 

	
 
        return htmlfill.render(
 
            render('admin/settings/settings.html'),
 
            defaults=defaults,
 
            encoding="UTF-8",
 
            force_defaults=False)
 

	
 
    @HasPermissionAnyDecorator('hg.admin')
 
    def settings_visual(self):
 
        c.active = 'visual'
 
        if request.POST:
 
            application_form = ApplicationVisualisationForm()()
 
            try:
 
                form_result = application_form.to_python(dict(request.POST))
 
            except formencode.Invalid as errors:
 
                return htmlfill.render(
 
                    render('admin/settings/settings.html'),
 
                    defaults=errors.value,
 
                    errors=errors.error_dict or {},
 
                    prefix_error=False,
 
                    encoding="UTF-8",
 
                    force_defaults=False)
 

	
 
            try:
 
                settings = [
 
                    ('show_public_icon', 'show_public_icon', 'bool'),
 
                    ('show_private_icon', 'show_private_icon', 'bool'),
 
                    ('stylify_metatags', 'stylify_metatags', 'bool'),
 
                    ('repository_fields', 'repository_fields', 'bool'),
 
                    ('dashboard_items', 'dashboard_items', 'int'),
 
                    ('admin_grid_items', 'admin_grid_items', 'int'),
 
                    ('show_version', 'show_version', 'bool'),
 
                    ('use_gravatar', 'use_gravatar', 'bool'),
 
                    ('gravatar_url', 'gravatar_url', 'unicode'),
 
                    ('clone_uri_tmpl', 'clone_uri_tmpl', 'unicode'),
 
                ]
 
                for setting, form_key, type_ in settings:
 
                    sett = Setting.create_or_update(setting,
 
                                        form_result[form_key], type_)
 
                    Session().add(sett)
 
                    Setting.create_or_update(setting, form_result[form_key], type_)
 

	
 
                Session().commit()
 
                set_app_settings(config)
 
                h.flash(_('Updated visualisation settings'),
 
                        category='success')
 

	
 
            except Exception:
 
                log.error(traceback.format_exc())
 
                h.flash(_('Error occurred during updating '
 
                          'visualisation settings'),
 
                        category='error')
 

	
 
            raise HTTPFound(location=url('admin_settings_visual'))
 

	
 
        defaults = Setting.get_app_settings()
 
        defaults.update(self._get_hg_ui_settings())
 

	
 
        return htmlfill.render(
 
            render('admin/settings/settings.html'),
 
            defaults=defaults,
 
            encoding="UTF-8",
 
            force_defaults=False)
 

	
 
    @HasPermissionAnyDecorator('hg.admin')
 
    def settings_email(self):
 
        c.active = 'email'
 
        if request.POST:
 
            test_email = request.POST.get('test_email')
 
            test_email_subj = 'Kallithea test email'
 
            test_body = ('Kallithea Email test, '
 
                               'Kallithea version: %s' % c.kallithea_version)
 
            if not test_email:
 
                h.flash(_('Please enter email address'), category='error')
 
                raise HTTPFound(location=url('admin_settings_email'))
 

	
 
            test_email_txt_body = EmailNotificationModel() \
 
                .get_email_tmpl(EmailNotificationModel.TYPE_DEFAULT,
 
                                'txt', body=test_body)
 
            test_email_html_body = EmailNotificationModel() \
 
                .get_email_tmpl(EmailNotificationModel.TYPE_DEFAULT,
 
                                'html', body=test_body)
 

	
 
            recipients = [test_email] if test_email else None
 

	
 
            tasks.send_email(recipients, test_email_subj,
 
                             test_email_txt_body, test_email_html_body)
 

	
 
            h.flash(_('Send email task created'), category='success')
kallithea/model/db.py
Show inline comments
 
@@ -226,96 +226,97 @@ class Setting(Base, BaseModel):
 
        """
 
        self._app_settings_value = safe_unicode(val)
 

	
 
    @hybrid_property
 
    def app_settings_type(self):
 
        return self._app_settings_type
 

	
 
    @app_settings_type.setter
 
    def app_settings_type(self, val):
 
        if val not in self.SETTINGS_TYPES:
 
            raise Exception('type must be one of %s got %s'
 
                            % (self.SETTINGS_TYPES.keys(), val))
 
        self._app_settings_type = val
 

	
 
    def __unicode__(self):
 
        return u"<%s('%s:%s[%s]')>" % (
 
            self.__class__.__name__,
 
            self.app_settings_name, self.app_settings_value, self.app_settings_type
 
        )
 

	
 
    @classmethod
 
    def get_by_name(cls, key):
 
        return cls.query() \
 
            .filter(cls.app_settings_name == key).scalar()
 

	
 
    @classmethod
 
    def get_by_name_or_create(cls, key, val='', type='unicode'):
 
        res = cls.get_by_name(key)
 
        if res is None:
 
            res = cls(key, val, type)
 
        return res
 

	
 
    @classmethod
 
    def create_or_update(cls, key, val=Optional(''), type=Optional('unicode')):
 
        """
 
        Creates or updates Kallithea setting. If updates are triggered, it will only
 
        update parameters that are explicitly set. Optional instance will be skipped.
 

	
 
        :param key:
 
        :param val:
 
        :param type:
 
        :return:
 
        """
 
        res = cls.get_by_name(key)
 
        if res is None:
 
            val = Optional.extract(val)
 
            type = Optional.extract(type)
 
            res = cls(key, val, type)
 
            Session().add(res)
 
        else:
 
            res.app_settings_name = key
 
            if not isinstance(val, Optional):
 
                # update if set
 
                res.app_settings_value = val
 
            if not isinstance(type, Optional):
 
                # update if set
 
                res.app_settings_type = type
 
        return res
 

	
 
    @classmethod
 
    def get_app_settings(cls, cache=False):
 

	
 
        ret = cls.query()
 

	
 
        if cache:
 
            ret = ret.options(FromCache("sql_cache_short", "get_hg_settings"))
 

	
 
        if ret is None:
 
            raise Exception('Could not get application settings !')
 
        settings = {}
 
        for each in ret:
 
            settings[each.app_settings_name] = \
 
                each.app_settings_value
 

	
 
        return settings
 

	
 
    @classmethod
 
    def get_auth_plugins(cls, cache=False):
 
        auth_plugins = cls.get_by_name("auth_plugins").app_settings_value
 
        return auth_plugins
 

	
 
    @classmethod
 
    def get_auth_settings(cls, cache=False):
 
        ret = cls.query() \
 
                .filter(cls.app_settings_name.startswith('auth_')).all()
 
        fd = {}
 
        for row in ret:
 
            fd[row.app_settings_name] = row.app_settings_value
 
        return fd
 

	
 
    @classmethod
 
    def get_default_repo_settings(cls, cache=False, strip_prefix=False):
 
        ret = cls.query() \
 
                .filter(cls.app_settings_name.startswith('default_')).all()
 
        fd = {}
 
        for row in ret:
 
            key = row.app_settings_name
kallithea/tests/conftest.py
Show inline comments
 
@@ -15,93 +15,91 @@ from kallithea.tests.base import invalid
 

	
 
def pytest_configure():
 
    path = os.getcwd()
 
    sys.path.insert(0, path)
 
    pkg_resources.working_set.add_entry(path)
 

	
 
    # Disable INFO logging of test database creation, restore with NOTSET
 
    logging.disable(logging.INFO)
 
    pylons.test.pylonsapp = loadapp('config:kallithea/tests/test.ini', relative_to=path)
 
    logging.disable(logging.NOTSET)
 

	
 
    # Setup the config and app_globals, only works if we can get
 
    # to the config object
 
    conf = getattr(pylons.test.pylonsapp, 'config')
 
    if conf:
 
        pylons.config._push_object(conf)
 

	
 
        if 'pylons.app_globals' in conf:
 
            pylons.app_globals._push_object(conf['pylons.app_globals'])
 

	
 
    # Initialize a translator for tests that utilize i18n
 
    translator = _get_translator(pylons.config.get('lang'))
 
    pylons.translator._push_object(translator)
 

	
 
    return pylons.test.pylonsapp
 

	
 

	
 
@pytest.yield_fixture
 
def create_test_user():
 
    """Provide users that automatically disappear after test is over."""
 
    test_user_ids = []
 
    def _create_test_user(user_form):
 
        user = UserModel().create(user_form)
 
        test_user_ids.append(user.user_id)
 
        return user
 
    yield _create_test_user
 
    for user_id in test_user_ids:
 
        UserModel().delete(user_id)
 
    Session().commit()
 

	
 

	
 
def _set_settings(*kvtseq):
 
    session = Session()
 
    for kvt in kvtseq:
 
        assert len(kvt) in (2, 3)
 
        k = kvt[0]
 
        v = kvt[1]
 
        t = kvt[2] if len(kvt) == 3 else 'unicode'
 
        setting = Setting.create_or_update(k, v, t)
 
        session.add(setting)
 
        Setting.create_or_update(k, v, t)
 
    session.commit()
 

	
 

	
 
@pytest.yield_fixture
 
def set_test_settings():
 
    """Restore settings after test is over."""
 
    # Save settings.
 
    settings_snapshot = [
 
        (s.app_settings_name, s.app_settings_value, s.app_settings_type)
 
        for s in Setting.query().all()]
 
    yield _set_settings
 
    # Restore settings.
 
    session = Session()
 
    keys = frozenset(k for (k, v, t) in settings_snapshot)
 
    for s in Setting.query().all():
 
        if s.app_settings_name not in keys:
 
            session.delete(s)
 
    for k, v, t in settings_snapshot:
 
        if t == 'list' and hasattr(v, '__iter__'):
 
            v = ','.join(v) # Quirk: must format list value manually.
 
        s = Setting.create_or_update(k, v, t)
 
        session.add(s)
 
        Setting.create_or_update(k, v, t)
 
    session.commit()
 

	
 
@pytest.yield_fixture
 
def auto_clear_ip_permissions():
 
    """Fixture that provides nothing but clearing IP permissions upon test
 
    exit. This clearing is needed to avoid other test failing to make fake http
 
    accesses."""
 
    yield
 
    # cleanup
 
    user_model = UserModel()
 

	
 
    user_ids = []
 
    user_ids.append(User.get_default_user().user_id)
 
    user_ids.append(User.get_by_username(TEST_USER_REGULAR_LOGIN).user_id)
 

	
 
    for user_id in user_ids:
 
        for ip in UserIpMap.query().filter(UserIpMap.user_id == user_id):
 
            user_model.delete_extra_ip(user_id, ip.ip_id)
 

	
 
    # IP permissions are cached, need to invalidate this cache explicitly
 
    invalidate_all_caches()
kallithea/tests/models/test_settings.py
Show inline comments
 
from kallithea.model.meta import Session
 
from kallithea.model.db import Setting
 

	
 

	
 
name = 'spam-setting-name'
 

	
 
def test_passing_list_setting_value_results_in_string_valued_setting():
 
    assert Setting.get_by_name(name) is None
 
    setting = Setting.create_or_update(name, ['spam', 'eggs'])
 
    Session().add(setting)
 
    Session().flush()
 
    try:
 
        assert Setting.get_by_name(name) is not None
 
        # Quirk: list value is stringified.
 
        assert Setting.get_by_name(name).app_settings_value \
 
               == "['spam', 'eggs']"
 
        assert Setting.get_by_name(name).app_settings_type == 'unicode'
 
    finally:
 
        Session().delete(setting)
 
        Session().flush()
 

	
 
def test_list_valued_setting_creation_requires_manual_value_formatting():
 
    assert Setting.get_by_name(name) is None
 
    # Quirk: need manual formatting of list setting value.
 
    setting = Setting.create_or_update(name, 'spam,eggs', type='list')
 
    Session().add(setting)
 
    Session().flush()
 
    try:
 
        assert setting.app_settings_value == ['spam', 'eggs']
 
    finally:
 
        Session().delete(setting)
 
        Session().flush()
 

	
 
def test_list_valued_setting_update():
 
    assert Setting.get_by_name(name) is None
 
    setting = Setting.create_or_update(name, 'spam', type='list')
 
    Session().add(setting)
 
    Session().flush()
 
    try:
 
        assert setting.app_settings_value == [u'spam']
 
        # Assign back setting value.
 
        setting.app_settings_value = setting.app_settings_value
 
        # Quirk: value is stringified on write and listified on read.
 
        assert setting.app_settings_value == ["[u'spam']"]
 
        setting.app_settings_value = setting.app_settings_value
 
        assert setting.app_settings_value == ["[u\"[u'spam']\"]"]
 
    finally:
 
        Session().delete(setting)
 
        Session().flush()
0 comments (0 inline, 0 general)