cubicweb/transaction.py
author Denis Laxalde <denis.laxalde@logilab.fr>
Thu, 09 Mar 2017 16:36:33 +0100
changeset 12053 c3c9f2e1424c
parent 11767 432f87a63057
permissions -rw-r--r--
[pyramid] Add a "pyramid" instance configuration type In a new module 'cubicweb.pyramid.config' we define a "pyramid" instance configuration type. The noticeable feature of this configuration is that it manages a 'development.ini' file that gets installed in application home (along with `.conf` file). This file is templated and includes generated values for secrets of session and authtk tokens. This means that we can just call: pserve etc/cubicweb.d/<appname>/development.ini or gunicorn --paste etc/cubicweb.d/<appname>/development.ini -b :8080 just after instance creation to get a pyramid instance running without having to hack around a 'pyramid.ini' file. This patch drops 'development.ini' from skeleton and moves it in cubicweb/pyramid so that it gets installed at instance creation which is more appropriate than in cube creation. The new configuration class sets "cubicweb.bwcompat" setting to false so it is not intended to replace the "all-in-one" configuration type (which would require a bit more work). This configuration is close to the the 'repository' configuration type with just a couple of options from WebConfiguration that are needed for Pyramid (anonymous user/password plus some miscellaneous options that I'm not so sure are really needed). Note, in particular, that we do not pull CORS settings to be injected as a WSGI middleware like in wsgi_application_from_cwconfig() since I believe this should be left as an end-user responsibility and since this can be defined in a standard way in paste configuration. This configuration inherits from ServerConfiguration but registers the same appobjects as WebConfiguration. In cubicweb.web.request._CubicWebRequestBase, we guard against access to "uiprops" and "datadir_url" of the config because this new "pyramid" config does not have these (this does not make sense without bwcompat mode). At some point, we should either avoid using `cw_request`'s pyramid request attribute or make cubicweb's web request really independant of existing implementation and drop these assumptions.

# copyright 2003-2014 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
#
# This file is part of CubicWeb.
#
# CubicWeb is free software: you can redistribute it and/or modify it under the
# terms of the GNU Lesser General Public License as published by the Free
# Software Foundation, either version 2.1 of the License, or (at your option)
# any later version.
#
# CubicWeb 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 Lesser General Public License for more
# details.
#
# You should have received a copy of the GNU Lesser General Public License along
# with CubicWeb.  If not, see <http://www.gnu.org/licenses/>.
""" undoable transaction objects. """

from cubicweb import _

from cubicweb import RepositoryError


ACTION_LABELS = {
    'C': _('entity creation'),
    'U': _('entity update'),
    'D': _('entity deletion'),
    'A': _('relation add'),
    'R': _('relation removal'),
    }


class NoSuchTransaction(RepositoryError):
    # Used by CubicWebException
    msg = _("there is no transaction #%s")

    def __init__(self, txuuid):
        super(NoSuchTransaction, self).__init__(txuuid)
        self.txuuid = txuuid

class Transaction(object):
    """an undoable transaction"""

    def __init__(self, cnx, uuid, time, ueid):
        self.cnx = cnx
        self.uuid = uuid
        self.datetime = time
        self.user_eid = ueid

    def _execute(self, *args, **kwargs):
        """execute a query using either the req or the cnx"""
        return self.cnx.execute(*args, **kwargs)


    def __repr__(self):
        return '<Transaction %s by %s on %s>' % (
            self.uuid, self.user_eid, self.datetime)

    def user(self):
        """return the user entity which has done the transaction,
        none if not found.
        """
        return self.cnx.find('CWUser', eid=self.user_eid).one()

    def actions_list(self, public=True):
        """return an ordered list of action effectued during that transaction

        if public is true, return only 'public' action, eg not ones triggered
        under the cover by hooks.
        """
        return self.cnx.transaction_actions(self.uuid, public)


class AbstractAction(object):

    def __init__(self, action, public, order):
        self.action = action
        self.public = public
        self.order = order

    @property
    def label(self):
        return ACTION_LABELS[self.action]

    @property
    def ertype(self):
        """ Return the entity or relation type this action is related to"""
        raise NotImplementedError(self)


class EntityAction(AbstractAction):

    def __init__(self, action, public, order, etype, eid, changes):
        super(EntityAction, self).__init__(action, public, order)
        self.etype = etype
        self.eid = eid
        self.changes = changes

    def __repr__(self):
        return '<%s: %s %s (%s)>' % (
            self.label, self.eid, self.changes,
            self.public and 'dbapi' or 'hook')

    @property
    def ertype(self):
        """ Return the entity or relation type this action is related to"""
        return self.etype


class RelationAction(AbstractAction):

    def __init__(self, action, public, order, rtype, eidfrom, eidto):
        super(RelationAction, self).__init__(action, public, order)
        self.rtype = rtype
        self.eid_from = eidfrom
        self.eid_to = eidto

    def __repr__(self):
        return '<%s: %s %s %s (%s)>' % (
            self.label, self.eid_from, self.rtype, self.eid_to,
            self.public and 'dbapi' or 'hook')

    @property
    def ertype(self):
        """ Return the entity or relation type this action is related to"""
        return self.rtype