131 lines
		
	
	
		
			4.2 KiB
		
	
	
	
		
			Python
		
	
	
			
		
		
	
	
			131 lines
		
	
	
		
			4.2 KiB
		
	
	
	
		
			Python
		
	
	
| # -*- coding: utf-8 -*-
 | |
| # Copyright 2017 Camptocamp SA
 | |
| # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html)
 | |
| 
 | |
| """
 | |
| 
 | |
| Base Component
 | |
| ==============
 | |
| 
 | |
| The connector proposes a 'base' Component, which can be used in
 | |
| the ``_inherit`` of your own components.  This is not a
 | |
| requirement.  It is already inherited by every component
 | |
| provided by the Connector.
 | |
| 
 | |
| Components are organized according to different usages.  The connector suggests
 | |
| 5 main kinds of Components. Each might have a few different usages.  You can be
 | |
| as creative as you want when it comes to creating new ones though.
 | |
| 
 | |
| One "usage" is responsible for a specific work, and alongside with the
 | |
| collection (the backend) and the model, the usage will be used to find the
 | |
| needed component for a task.
 | |
| 
 | |
| Some of the Components have an implementation in the ``Connector`` addon, but
 | |
| some are empty shells that need to be implemented in the different connectors.
 | |
| 
 | |
| The usual categories are:
 | |
| 
 | |
| :py:class:`~connector.components.binder.Binder`
 | |
|   The ``binders`` give the external ID or Odoo ID from respectively an
 | |
|   Odoo ID or an external ID. A default implementation is available.
 | |
| 
 | |
|   Most common usages:
 | |
| 
 | |
|   * ``binder``
 | |
| 
 | |
| :py:class:`~connector.components.mapper.Mapper`
 | |
|   The ``mappers`` transform a external record into an Odoo record or
 | |
|   conversely.
 | |
| 
 | |
|   Most common usages:
 | |
| 
 | |
|   * ``import.mapper``
 | |
|   * ``export.mapper``
 | |
| 
 | |
| :py:class:`~connector.components.backend_adapter.BackendAdapter`
 | |
|   The ``backend.adapters`` implements the discussion with the ``backend's``
 | |
|   APIs. They usually adapt their APIs to a common interface (CRUD).
 | |
| 
 | |
|   Most common usages:
 | |
| 
 | |
|   * ``backend.adapter``
 | |
| 
 | |
| :py:class:`~connector.components.synchronizer.Synchronizer`
 | |
|   A ``synchronizer`` is the main piece of a synchronization.  It
 | |
|   orchestrates the flow of a synchronization and use the other
 | |
|   Components
 | |
| 
 | |
|   Most common usages:
 | |
| 
 | |
|   * ``record.importer``
 | |
|   * ``record.exporter``
 | |
|   * ``batch.importer``
 | |
|   * ``batch.exporter``
 | |
| 
 | |
| 
 | |
| """
 | |
| 
 | |
| from odoo.addons.component.core import AbstractComponent
 | |
| from odoo.addons.queue_job.exception import RetryableJobError
 | |
| from ..database import pg_try_advisory_lock
 | |
| 
 | |
| 
 | |
| class BaseConnectorComponent(AbstractComponent):
 | |
|     """ Base component for the connector
 | |
| 
 | |
|     Is inherited by every components of the Connector (Binder, Mapper, ...)
 | |
|     and adds a few methods which are of common usage in the connectors.
 | |
| 
 | |
|     """
 | |
| 
 | |
|     _name = 'base.connector'
 | |
| 
 | |
|     @property
 | |
|     def backend_record(self):
 | |
|         """ Backend record we are working with """
 | |
|         # backward compatibility
 | |
|         return self.work.collection
 | |
| 
 | |
|     def binder_for(self, model=None):
 | |
|         """ Shortcut to get Binder for a model
 | |
| 
 | |
|         Equivalent to: ``self.component(usage='binder', model_name='xxx')``
 | |
| 
 | |
|         """
 | |
|         return self.component(usage='binder', model_name=model)
 | |
| 
 | |
|     def advisory_lock_or_retry(self, lock, retry_seconds=1):
 | |
|         """ Acquire a Postgres transactional advisory lock or retry job
 | |
| 
 | |
|         When the lock cannot be acquired, it raises a
 | |
|         :exc:`odoo.addons.queue_job.exception.RetryableJobError` so the job
 | |
|         is retried after n ``retry_seconds``.
 | |
| 
 | |
|         Usage example:
 | |
| 
 | |
|         .. code-block:: python
 | |
| 
 | |
|             lock_name = 'import_record({}, {}, {}, {})'.format(
 | |
|                 self.backend_record._name,
 | |
|                 self.backend_record.id,
 | |
|                 self.model._name,
 | |
|                 self.external_id,
 | |
|             )
 | |
|             self.advisory_lock_or_retry(lock_name, retry_seconds=2)
 | |
| 
 | |
|         See :func:`odoo.addons.connector.connector.pg_try_advisory_lock` for
 | |
|         details.
 | |
| 
 | |
|         :param lock: The lock name. Can be anything convertible to a
 | |
|            string.  It needs to represent what should not be synchronized
 | |
|            concurrently, usually the string will contain at least: the
 | |
|            action, the backend name, the backend id, the model name, the
 | |
|            external id
 | |
|         :param retry_seconds: number of seconds after which a job should
 | |
|            be retried when the lock cannot be acquired.
 | |
|         """
 | |
|         if not pg_try_advisory_lock(self.env, lock):
 | |
|             raise RetryableJobError('Could not acquire advisory lock',
 | |
|                                     seconds=retry_seconds,
 | |
|                                     ignore_retry=True)
 |