repoapi.py
changeset 9052 4cba5f2cd57b
parent 9045 5378a738f333
child 9053 862040061173
equal deleted inserted replaced
9051:944d66870c6a 9052:4cba5f2cd57b
    16 # You should have received a copy of the GNU Lesser General Public License along
    16 # You should have received a copy of the GNU Lesser General Public License along
    17 # with CubicWeb.  If not, see <http://www.gnu.org/licenses/>.
    17 # with CubicWeb.  If not, see <http://www.gnu.org/licenses/>.
    18 """Official API to access the content of a repository
    18 """Official API to access the content of a repository
    19 """
    19 """
    20 from cubicweb.utils import parse_repo_uri
    20 from cubicweb.utils import parse_repo_uri
    21 from cubicweb import ConnectionError
    21 from cubicweb import ConnectionError, ProgrammingError
       
    22 from uuid import uuid4
       
    23 from contextlib import contextmanager
       
    24 from cubicweb.req import RequestSessionBase
    22 
    25 
    23 ### private function for specific method ############################
    26 ### private function for specific method ############################
    24 
    27 
    25 def _get_inmemory_repo(config, vreg=None):
    28 def _get_inmemory_repo(config, vreg=None):
    26     from cubicweb.server.repository import Repository
    29     from cubicweb.server.repository import Repository
    69         from cubicweb.zmqclient import ZMQRepositoryClient
    72         from cubicweb.zmqclient import ZMQRepositoryClient
    70         return ZMQRepositoryClient(uri)
    73         return ZMQRepositoryClient(uri)
    71     else:
    74     else:
    72         raise ConnectionError('unknown protocol: `%s`' % protocol)
    75         raise ConnectionError('unknown protocol: `%s`' % protocol)
    73 
    76 
       
    77 def _srv_cnx_func(name):
       
    78     """Decorate ClientConnection method blindly forward to Connection
       
    79     THIS TRANSITIONAL PURPOSE
       
    80 
       
    81     will be dropped when we have standalone connection"""
       
    82     def proxy(clt_cnx, *args, **kwargs):
       
    83         # the ``with`` dance is transitional. We do not have Standalone
       
    84         # Connection yet so we use this trick to unsure the session have the
       
    85         # proper cnx loaded. This can be simplified one we have Standalone
       
    86         # Connection object
       
    87         with clt_cnx._srv_cnx as cnx:
       
    88             return getattr(cnx, name)(*args, **kwargs)
       
    89     return proxy
       
    90 
       
    91 class ClientConnection(RequestSessionBase):
       
    92     """A Connection object to be used Client side.
       
    93 
       
    94     This object is aimed to be used client side (so potential communication
       
    95     with the repo through RTC) and aims to offer some compatibility with the
       
    96     cubicweb.dbapi.Connection interface.
       
    97     """
       
    98     # make exceptions available through the connection object
       
    99     ProgrammingError = ProgrammingError
       
   100     # attributes that may be overriden per connection instance
       
   101     anonymous_connection = False # XXX really needed ?
       
   102     is_repo_in_memory = True # BC, always true
       
   103 
       
   104     def __init__(self, session):
       
   105         self._session = session
       
   106         self._cnxid = None
       
   107         self._open = None
       
   108         self._web_request = False
       
   109         self.vreg = session.vreg
       
   110         self._set_user(session.user)
       
   111 
       
   112     def __enter__(self):
       
   113         assert self._open is None
       
   114         self._open = True
       
   115         self._cnxid = '%s-%s' % (self._session.id, uuid4().hex)
       
   116         self._session.set_cnx(self._cnxid)
       
   117         self._session._cnx.ctx_count += 1
       
   118 
       
   119     def __exit__(self, exc_type, exc_val, exc_tb):
       
   120         self._open = False
       
   121         cnxid = self._cnxid
       
   122         self._cnxid = None
       
   123         self._session._cnx.ctx_count -= 1
       
   124         self._session.close_cnx(cnxid)
       
   125 
       
   126 
       
   127     # begin silly BC
       
   128     @property
       
   129     def _closed(self):
       
   130         return not self._open
       
   131 
       
   132     def close(self):
       
   133         if self._open:
       
   134             self.__exit__(None, None, None)
       
   135 
       
   136     def __repr__(self):
       
   137         if self.anonymous_connection:
       
   138             return '<Connection %s (anonymous)>' % self._cnxid
       
   139         return '<Connection %s>' % self._cnxid
       
   140     # end silly BC
       
   141 
       
   142     @property
       
   143     @contextmanager
       
   144     def _srv_cnx(self):
       
   145         """ensure that the session is locked to the right transaction
       
   146 
       
   147         TRANSITIONAL PURPOSE, This will be dropped once we use standalone
       
   148         session object"""
       
   149         if not self._open:
       
   150             raise ProgrammingError('Closed connection %s' % self._cnxid)
       
   151         session = self._session
       
   152         old_cnx = session._current_cnx_id
       
   153         try:
       
   154             session.set_cnx(self._cnxid)
       
   155             session.set_cnxset()
       
   156             try:
       
   157                 yield session
       
   158             finally:
       
   159                 session.free_cnxset()
       
   160         finally:
       
   161             if old_cnx is not None:
       
   162                 session.set_cnx(old_cnx)
       
   163 
       
   164     # Main Connection purpose in life #########################################
       
   165 
       
   166     call_service = _srv_cnx_func('call_service')
       
   167 
       
   168     def execute(self, *args, **kwargs):
       
   169         # the ``with`` dance is transitional. We do not have Standalone
       
   170         # Connection yet so we use this trick to unsure the session have the
       
   171         # proper cnx loaded. This can be simplified one we have Standalone
       
   172         # Connection object
       
   173         with self._srv_cnx as cnx:
       
   174             rset = cnx.execute(*args, **kwargs)
       
   175         rset.req = self
       
   176         return rset
       
   177 
       
   178     commit = _srv_cnx_func('commit')
       
   179     rollback = _srv_cnx_func('rollback')
       
   180 
       
   181     # session data methods #####################################################
       
   182 
       
   183     get_shared_data = _srv_cnx_func('get_shared_data')
       
   184     set_shared_data = _srv_cnx_func('set_shared_data')
       
   185 
       
   186     # meta-data accessors ######################################################
       
   187 
       
   188     def source_defs(self):
       
   189         """Return the definition of sources used by the repository."""
       
   190         return self._session.repo.source_defs()
       
   191 
       
   192     def get_schema(self):
       
   193         """Return the schema currently used by the repository."""
       
   194         return self._session.repo.source_defs()
       
   195 
       
   196     def get_option_value(self, option, foreid=None):
       
   197         """Return the value for `option` in the configuration. If `foreid` is
       
   198         specified, the actual repository to which this entity belongs is
       
   199         dereferenced and the option value retrieved from it.
       
   200         """
       
   201         return self._session.repo.get_option_value(option, foreid)
       
   202 
       
   203     describe = _srv_cnx_func('describe')
       
   204 
       
   205     # undo support ############################################################
       
   206 
       
   207     def undoable_transactions(self, ueid=None, req=None, **actionfilters):
       
   208         """Return a list of undoable transaction objects by the connection's
       
   209         user, ordered by descendant transaction time.
       
   210 
       
   211         Managers may filter according to user (eid) who has done the transaction
       
   212         using the `ueid` argument. Others will only see their own transactions.
       
   213 
       
   214         Additional filtering capabilities is provided by using the following
       
   215         named arguments:
       
   216 
       
   217         * `etype` to get only transactions creating/updating/deleting entities
       
   218           of the given type
       
   219 
       
   220         * `eid` to get only transactions applied to entity of the given eid
       
   221 
       
   222         * `action` to get only transactions doing the given action (action in
       
   223           'C', 'U', 'D', 'A', 'R'). If `etype`, action can only be 'C', 'U' or
       
   224           'D'.
       
   225 
       
   226         * `public`: when additional filtering is provided, their are by default
       
   227           only searched in 'public' actions, unless a `public` argument is given
       
   228           and set to false.
       
   229         """
       
   230         # the ``with`` dance is transitional. We do not have Standalone
       
   231         # Connection yet so we use this trick to unsure the session have the
       
   232         # proper cnx loaded. This can be simplified one we have Standalone
       
   233         # Connection object
       
   234         with self._srv_cnx as cnx:
       
   235             source = cnx.repo.system_source
       
   236             txinfos = source.undoable_transactions(cnx, ueid, **actionfilters)
       
   237         for txinfo in txinfos:
       
   238             txinfo.req = req or self  # XXX mostly wrong
       
   239         return txinfos
       
   240 
       
   241     def transaction_info(self, txuuid, req=None):
       
   242         """Return transaction object for the given uid.
       
   243 
       
   244         raise `NoSuchTransaction` if not found or if session's user is not
       
   245         allowed (eg not in managers group and the transaction doesn't belong to
       
   246         him).
       
   247         """
       
   248         # the ``with`` dance is transitional. We do not have Standalone
       
   249         # Connection yet so we use this trick to unsure the session have the
       
   250         # proper cnx loaded. This can be simplified one we have Standalone
       
   251         # Connection object
       
   252         with self._srv_cnx as cnx:
       
   253             txinfo = cnx.repo.system_source.tx_info(cnx, txuuid)
       
   254         if req:
       
   255             txinfo.req = req
       
   256         else:
       
   257             txinfo.cnx = self
       
   258         return txinfo
       
   259 
       
   260     def transaction_actions(self, txuuid, public=True):
       
   261         """Return an ordered list of action effectued during that transaction.
       
   262 
       
   263         If public is true, return only 'public' actions, eg not ones triggered
       
   264         under the cover by hooks, else return all actions.
       
   265 
       
   266         raise `NoSuchTransaction` if the transaction is not found or if
       
   267         session's user is not allowed (eg not in managers group and the
       
   268         transaction doesn't belong to him).
       
   269         """
       
   270         # the ``with`` dance is transitional. We do not have Standalone
       
   271         # Connection yet so we use this trick to unsure the session have the
       
   272         # proper cnx loaded. This can be simplified one we have Standalone
       
   273         # Connection object
       
   274         with self._srv_cnx as cnx:
       
   275             return cnx.repo.system_source.tx_actions(cnx, txuuid, public)
       
   276 
       
   277     def undo_transaction(self, txuuid):
       
   278         """Undo the given transaction. Return potential restoration errors.
       
   279 
       
   280         raise `NoSuchTransaction` if not found or if session's user is not
       
   281         allowed (eg not in managers group and the transaction doesn't belong to
       
   282         him).
       
   283         """
       
   284         # the ``with`` dance is transitional. We do not have Standalone
       
   285         # Connection yet so we use this trick to unsure the session have the
       
   286         # proper cnx loaded. This can be simplified one we have Standalone
       
   287         # Connection object
       
   288         with self._srv_cnx as cnx:
       
   289             return cnx.repo.system_source.undo_transaction(cnx, txuuid)