[MIG] base_external_dbsource: Migration to v13.0

This commit is contained in:
Sergio Teruel
2020-01-09 20:57:58 +01:00
committed by bilbonet
parent d722f84f50
commit 0ed5569255
6 changed files with 152 additions and 193 deletions

View File

@@ -14,13 +14,13 @@ External Database Sources
:target: http://www.gnu.org/licenses/lgpl-3.0-standalone.html :target: http://www.gnu.org/licenses/lgpl-3.0-standalone.html
:alt: License: LGPL-3 :alt: License: LGPL-3
.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fserver--backend-lightgray.png?logo=github .. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fserver--backend-lightgray.png?logo=github
:target: https://github.com/OCA/server-backend/tree/12.0/base_external_dbsource :target: https://github.com/OCA/server-backend/tree/13.0/base_external_dbsource
:alt: OCA/server-backend :alt: OCA/server-backend
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png .. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
:target: https://translation.odoo-community.org/projects/server-backend-12-0/server-backend-12-0-base_external_dbsource :target: https://translation.odoo-community.org/projects/server-backend-13-0/server-backend-13-0-base_external_dbsource
:alt: Translate me on Weblate :alt: Translate me on Weblate
.. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png .. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png
:target: https://runbot.odoo-community.org/runbot/253/12.0 :target: https://runbot.odoo-community.org/runbot/253/13.0
:alt: Try me on Runbot :alt: Try me on Runbot
|badge1| |badge2| |badge3| |badge4| |badge5| |badge1| |badge2| |badge3| |badge4| |badge5|
@@ -75,7 +75,7 @@ Bug Tracker
Bugs are tracked on `GitHub Issues <https://github.com/OCA/server-backend/issues>`_. Bugs are tracked on `GitHub Issues <https://github.com/OCA/server-backend/issues>`_.
In case of trouble, please check there if your issue has already been reported. In case of trouble, please check there if your issue has already been reported.
If you spotted it first, help us smashing it by providing a detailed and welcomed If you spotted it first, help us smashing it by providing a detailed and welcomed
`feedback <https://github.com/OCA/server-backend/issues/new?body=module:%20base_external_dbsource%0Aversion:%2012.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_. `feedback <https://github.com/OCA/server-backend/issues/new?body=module:%20base_external_dbsource%0Aversion:%2013.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.
Do not contact contributors directly about support or help with technical issues. Do not contact contributors directly about support or help with technical issues.
@@ -111,6 +111,6 @@ OCA, or the Odoo Community Association, is a nonprofit organization whose
mission is to support the collaborative development of Odoo features and mission is to support the collaborative development of Odoo features and
promote its widespread use. promote its widespread use.
This module is part of the `OCA/server-backend <https://github.com/OCA/server-backend/tree/12.0/base_external_dbsource>`_ project on GitHub. This module is part of the `OCA/server-backend <https://github.com/OCA/server-backend/tree/13.0/base_external_dbsource>`_ project on GitHub.
You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

View File

@@ -2,26 +2,15 @@
# Copyright <2016> <Henry Zhou MAXodoo> # Copyright <2016> <Henry Zhou MAXodoo>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
{ {
'name': 'External Database Sources', "name": "External Database Sources",
'version': '12.0.1.0.1', "version": "13.0.1.0.0",
'category': 'Tools', "category": "Tools",
'author': "Daniel Reis, " "author": "Daniel Reis, " "LasLabs, " "Odoo Community Association (OCA)",
"LasLabs, " "website": "https://github.com/OCA/server-backend",
"Odoo Community Association (OCA)", "license": "LGPL-3",
'website': 'https://github.com/OCA/server-backend', "images": ["images/screenshot01.png"],
'license': 'LGPL-3', "depends": ["base"],
'images': [ "data": ["views/base_external_dbsource.xml", "security/ir.model.access.csv"],
'images/screenshot01.png', "demo": ["demo/base_external_dbsource.xml"],
], "installable": True,
'depends': [
'base',
],
'data': [
'views/base_external_dbsource.xml',
'security/ir.model.access.csv',
],
'demo': [
'demo/base_external_dbsource.xml',
],
'installable': True,
} }

View File

@@ -3,10 +3,10 @@
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html). # License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html).
import logging import logging
import psycopg2
from contextlib import contextmanager from contextlib import contextmanager
import psycopg2
from odoo import _, api, fields, models, tools from odoo import _, api, fields, models, tools
from ..exceptions import ConnectionFailedError, ConnectionSuccessError from ..exceptions import ConnectionFailedError, ConnectionSuccessError
@@ -33,18 +33,18 @@ class BaseExternalDbsource(models.Model):
""" """
_name = "base.external.dbsource" _name = "base.external.dbsource"
_description = 'External Database Sources' _description = "External Database Sources"
CONNECTORS = [ CONNECTORS = [("postgresql", "PostgreSQL")]
('postgresql', 'PostgreSQL'),
]
# This is appended to the conn string if pass declared but not detected. # This is appended to the conn string if pass declared but not detected.
# Children should declare PWD_STRING_CONNECTOR (such as PWD_STRING_FBD) # Children should declare PWD_STRING_CONNECTOR (such as PWD_STRING_FBD)
# to allow for override. # to allow for override.
PWD_STRING = 'PWD=%s;' PWD_STRING = "PWD=%s;"
name = fields.Char('Datasource name', required=True, size=64) name = fields.Char("Datasource name", required=True, size=64)
conn_string = fields.Text('Connection string', help=""" conn_string = fields.Text(
"Connection string",
help="""
Sample connection strings: Sample connection strings:
- Microsoft SQL Server: - Microsoft SQL Server:
mssql+pymssql://username:%s@server:port/dbname?charset=utf8 mssql+pymssql://username:%s@server:port/dbname?charset=utf8
@@ -56,34 +56,31 @@ class BaseExternalDbsource(models.Model):
password=%s password=%s
- SQLite: sqlite:///test.db - SQLite: sqlite:///test.db
- Elasticsearch: https://user:%s@localhost:9200 - Elasticsearch: https://user:%s@localhost:9200
""") """,
conn_string_full = fields.Text(
readonly=True,
compute='_compute_conn_string_full',
) )
password = fields.Char('Password', size=40) conn_string_full = fields.Text(readonly=True, compute="_compute_conn_string_full")
password = fields.Char("Password", size=40)
client_cert = fields.Text() client_cert = fields.Text()
client_key = fields.Text() client_key = fields.Text()
ca_certs = fields.Char( ca_certs = fields.Char(help="Path to CA Certs file on server.")
help='Path to CA Certs file on server.',
)
connector = fields.Selection( connector = fields.Selection(
CONNECTORS, 'Connector', required=True, CONNECTORS,
"Connector",
required=True,
help="If a connector is missing from the list, check the server " help="If a connector is missing from the list, check the server "
"log to confirm that the required components were detected.", "log to confirm that the required components were detected.",
) )
current_table = None current_table = None
@api.multi @api.depends("conn_string", "password")
@api.depends('conn_string', 'password')
def _compute_conn_string_full(self): def _compute_conn_string_full(self):
for record in self: for record in self:
if record.password: if record.password:
if '%s' not in record.conn_string: if "%s" not in record.conn_string:
pwd_string = getattr( pwd_string = getattr(
record, record,
'PWD_STRING_%s' % record.connector.upper(), "PWD_STRING_%s" % record.connector.upper(),
record.PWD_STRING, record.PWD_STRING,
) )
record.conn_string += pwd_string record.conn_string += pwd_string
@@ -93,12 +90,10 @@ class BaseExternalDbsource(models.Model):
# Interface # Interface
@api.multi
def change_table(self, name): def change_table(self, name):
""" Change the table that is used for CRUD operations """ """ Change the table that is used for CRUD operations """
self.current_table = name self.current_table = name
@api.multi
def connection_close(self, connection): def connection_close(self, connection):
""" It closes the connection to the data source. """ It closes the connection to the data source.
@@ -106,10 +101,9 @@ class BaseExternalDbsource(models.Model):
the adapter type. the adapter type.
""" """
method = self._get_adapter_method('connection_close') method = self._get_adapter_method("connection_close")
return method(connection) return method(connection)
@api.multi
@contextmanager @contextmanager
def connection_open(self): def connection_open(self):
""" It provides a context manager for the data source. """ It provides a context manager for the data source.
@@ -118,20 +112,17 @@ class BaseExternalDbsource(models.Model):
the adapter type. the adapter type.
""" """
method = self._get_adapter_method('connection_open') method = self._get_adapter_method("connection_open")
try: try:
connection = method() connection = method()
yield connection yield connection
finally: finally:
try: try:
self.connection_close(connection) self.connection_close(connection)
except: except Exception:
_logger.exception('Connection close failure.') _logger.exception("Connection close failure.")
@api.multi def execute(self, query=None, execute_params=None, metadata=False, **kwargs):
def execute(
self, query=None, execute_params=None, metadata=False, **kwargs
):
""" Executes a query and returns a list of rows. """ Executes a query and returns a list of rows.
"execute_params" can be a dict of values, that can be referenced "execute_params" can be a dict of values, that can be referenced
@@ -154,24 +145,23 @@ class BaseExternalDbsource(models.Model):
# Old API compatibility # Old API compatibility
if not query: if not query:
try: try:
query = kwargs['sqlquery'] query = kwargs["sqlquery"]
except KeyError: except KeyError:
raise TypeError(_('query is a required argument')) raise TypeError(_("query is a required argument"))
if not execute_params: if not execute_params:
try: try:
execute_params = kwargs['sqlparams'] execute_params = kwargs["sqlparams"]
except KeyError: except KeyError:
pass pass
method = self._get_adapter_method('execute') method = self._get_adapter_method("execute")
rows, cols = method(query, execute_params, metadata) rows, cols = method(query, execute_params, metadata)
if metadata: if metadata:
return {'cols': cols, 'rows': rows} return {"cols": cols, "rows": rows}
else: else:
return rows return rows
@api.multi
def connection_test(self): def connection_test(self):
""" It tests the connection """ It tests the connection
@@ -179,22 +169,18 @@ class BaseExternalDbsource(models.Model):
ConnectionSuccessError: On connection success ConnectionSuccessError: On connection success
ConnectionFailedError: On connection failed ConnectionFailedError: On connection failed
""" """
for obj in self:
try: try:
with self.connection_open(): with self.connection_open():
pass pass
except Exception as e: except Exception as e:
raise ConnectionFailedError(_( raise ConnectionFailedError(
"Connection test failed:\n" _("Connection test failed:\n" "Here is what we got instead:\n%s")
"Here is what we got instead:\n%s" % tools.ustr(e)
) % tools.ustr(e)) )
raise ConnectionSuccessError(_( raise ConnectionSuccessError(
"Connection test succeeded:\n" _("Connection test succeeded:\n" "Everything seems properly set up!")
"Everything seems properly set up!", )
))
@api.multi
def remote_browse(self, record_ids, *args, **kwargs): def remote_browse(self, record_ids, *args, **kwargs):
""" It browses for and returns the records from remote by ID """ It browses for and returns the records from remote by ID
@@ -210,10 +196,9 @@ class BaseExternalDbsource(models.Model):
""" """
assert self.current_table assert self.current_table
method = self._get_adapter_method('remote_browse') method = self._get_adapter_method("remote_browse")
return method(record_ids, *args, **kwargs) return method(record_ids, *args, **kwargs)
@api.multi
def remote_create(self, vals, *args, **kwargs): def remote_create(self, vals, *args, **kwargs):
""" It creates a record on the remote data source. """ It creates a record on the remote data source.
@@ -229,10 +214,9 @@ class BaseExternalDbsource(models.Model):
""" """
assert self.current_table assert self.current_table
method = self._get_adapter_method('remote_create') method = self._get_adapter_method("remote_create")
return method(vals, *args, **kwargs) return method(vals, *args, **kwargs)
@api.multi
def remote_delete(self, record_ids, *args, **kwargs): def remote_delete(self, record_ids, *args, **kwargs):
""" It deletes records by ID on remote """ It deletes records by ID on remote
@@ -248,10 +232,9 @@ class BaseExternalDbsource(models.Model):
""" """
assert self.current_table assert self.current_table
method = self._get_adapter_method('remote_delete') method = self._get_adapter_method("remote_delete")
return method(record_ids, *args, **kwargs) return method(record_ids, *args, **kwargs)
@api.multi
def remote_search(self, query, *args, **kwargs): def remote_search(self, query, *args, **kwargs):
""" It searches the remote for the query. """ It searches the remote for the query.
@@ -267,10 +250,9 @@ class BaseExternalDbsource(models.Model):
""" """
assert self.current_table assert self.current_table
method = self._get_adapter_method('remote_search') method = self._get_adapter_method("remote_search")
return method(query, *args, **kwargs) return method(query, *args, **kwargs)
@api.multi
def remote_update(self, record_ids, vals, *args, **kwargs): def remote_update(self, record_ids, vals, *args, **kwargs):
""" It updates the remote records with the vals """ It updates the remote records with the vals
@@ -286,7 +268,7 @@ class BaseExternalDbsource(models.Model):
""" """
assert self.current_table assert self.current_table
method = self._get_adapter_method('remote_update') method = self._get_adapter_method("remote_update")
return method(record_ids, vals, *args, **kwargs) return method(record_ids, vals, *args, **kwargs)
# Adapters # Adapters
@@ -312,7 +294,6 @@ class BaseExternalDbsource(models.Model):
# Compatibility & Private # Compatibility & Private
@api.multi
def conn_open(self): def conn_open(self):
""" It opens and returns a connection to the remote data source. """ It opens and returns a connection to the remote data source.
@@ -339,14 +320,14 @@ class BaseExternalDbsource(models.Model):
""" """
self.ensure_one() self.ensure_one()
method = '%s_%s' % (method_prefix, self.connector) method = "{}_{}".format(method_prefix, self.connector)
try: try:
return getattr(self, method) return getattr(self, method)
except AttributeError: except AttributeError:
raise NotImplementedError(_( raise NotImplementedError(
_(
'"%s" method not found, check that all assets are installed ' '"%s" method not found, check that all assets are installed '
'for the %s connector type.' "for the %s connector type."
)) % (
method, self.connector,
) )
) % (method, self.connector)

View File

@@ -367,7 +367,7 @@ ul.auto-toc {
!! This file is generated by oca-gen-addon-readme !! !! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !! !! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! --> !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
<p><a class="reference external" href="https://odoo-community.org/page/development-status"><img alt="Beta" src="https://img.shields.io/badge/maturity-Beta-yellow.png" /></a> <a class="reference external" href="http://www.gnu.org/licenses/lgpl-3.0-standalone.html"><img alt="License: LGPL-3" src="https://img.shields.io/badge/licence-LGPL--3-blue.png" /></a> <a class="reference external" href="https://github.com/OCA/server-backend/tree/12.0/base_external_dbsource"><img alt="OCA/server-backend" src="https://img.shields.io/badge/github-OCA%2Fserver--backend-lightgray.png?logo=github" /></a> <a class="reference external" href="https://translation.odoo-community.org/projects/server-backend-12-0/server-backend-12-0-base_external_dbsource"><img alt="Translate me on Weblate" src="https://img.shields.io/badge/weblate-Translate%20me-F47D42.png" /></a> <a class="reference external" href="https://runbot.odoo-community.org/runbot/253/12.0"><img alt="Try me on Runbot" src="https://img.shields.io/badge/runbot-Try%20me-875A7B.png" /></a></p> <p><a class="reference external" href="https://odoo-community.org/page/development-status"><img alt="Beta" src="https://img.shields.io/badge/maturity-Beta-yellow.png" /></a> <a class="reference external" href="http://www.gnu.org/licenses/lgpl-3.0-standalone.html"><img alt="License: LGPL-3" src="https://img.shields.io/badge/licence-LGPL--3-blue.png" /></a> <a class="reference external" href="https://github.com/OCA/server-backend/tree/13.0/base_external_dbsource"><img alt="OCA/server-backend" src="https://img.shields.io/badge/github-OCA%2Fserver--backend-lightgray.png?logo=github" /></a> <a class="reference external" href="https://translation.odoo-community.org/projects/server-backend-13-0/server-backend-13-0-base_external_dbsource"><img alt="Translate me on Weblate" src="https://img.shields.io/badge/weblate-Translate%20me-F47D42.png" /></a> <a class="reference external" href="https://runbot.odoo-community.org/runbot/253/13.0"><img alt="Try me on Runbot" src="https://img.shields.io/badge/runbot-Try%20me-875A7B.png" /></a></p>
<p>This module allows you to define connections to foreign databases using ODBC, <p>This module allows you to define connections to foreign databases using ODBC,
Firebird, Oracle Client or SQLAlchemy.</p> Firebird, Oracle Client or SQLAlchemy.</p>
<p><strong>Table of contents</strong></p> <p><strong>Table of contents</strong></p>
@@ -427,7 +427,7 @@ support larger datasets in a more efficient manner.</li>
<p>Bugs are tracked on <a class="reference external" href="https://github.com/OCA/server-backend/issues">GitHub Issues</a>. <p>Bugs are tracked on <a class="reference external" href="https://github.com/OCA/server-backend/issues">GitHub Issues</a>.
In case of trouble, please check there if your issue has already been reported. In case of trouble, please check there if your issue has already been reported.
If you spotted it first, help us smashing it by providing a detailed and welcomed If you spotted it first, help us smashing it by providing a detailed and welcomed
<a class="reference external" href="https://github.com/OCA/server-backend/issues/new?body=module:%20base_external_dbsource%0Aversion:%2012.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**">feedback</a>.</p> <a class="reference external" href="https://github.com/OCA/server-backend/issues/new?body=module:%20base_external_dbsource%0Aversion:%2013.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**">feedback</a>.</p>
<p>Do not contact contributors directly about support or help with technical issues.</p> <p>Do not contact contributors directly about support or help with technical issues.</p>
</div> </div>
<div class="section" id="credits"> <div class="section" id="credits">
@@ -457,7 +457,7 @@ If you spotted it first, help us smashing it by providing a detailed and welcome
<p>OCA, or the Odoo Community Association, is a nonprofit organization whose <p>OCA, or the Odoo Community Association, is a nonprofit organization whose
mission is to support the collaborative development of Odoo features and mission is to support the collaborative development of Odoo features and
promote its widespread use.</p> promote its widespread use.</p>
<p>This module is part of the <a class="reference external" href="https://github.com/OCA/server-backend/tree/12.0/base_external_dbsource">OCA/server-backend</a> project on GitHub.</p> <p>This module is part of the <a class="reference external" href="https://github.com/OCA/server-backend/tree/13.0/base_external_dbsource">OCA/server-backend</a> project on GitHub.</p>
<p>You are welcome to contribute. To learn how please visit <a class="reference external" href="https://odoo-community.org/page/Contribute">https://odoo-community.org/page/Contribute</a>.</p> <p>You are welcome to contribute. To learn how please visit <a class="reference external" href="https://odoo-community.org/page/Contribute">https://odoo-community.org/page/Contribute</a>.</p>
</div> </div>
</div> </div>

View File

@@ -2,14 +2,13 @@
import mock import mock
from odoo.tests import common
from odoo.sql_db import connection_info_for from odoo.sql_db import connection_info_for
from odoo.tests import common
from ..exceptions import ConnectionFailedError, ConnectionSuccessError from ..exceptions import ConnectionFailedError, ConnectionSuccessError
class TestBaseExternalDbsource(common.TransactionCase): class TestBaseExternalDbsource(common.TransactionCase):
def setUp(self): def setUp(self):
super(TestBaseExternalDbsource, self).setUp() super(TestBaseExternalDbsource, self).setUp()
# Obtain current odoo instance DB connection settings # Obtain current odoo instance DB connection settings
@@ -20,26 +19,32 @@ class TestBaseExternalDbsource(common.TransactionCase):
connection_info["dbname"] = connection_info["database"] connection_info["dbname"] = connection_info["database"]
del connection_info["database"] del connection_info["database"]
# Create a proper dbsource record to test # Create a proper dbsource record to test
self.dbsource = self.env["base.external.dbsource"].create({ self.dbsource = self.env["base.external.dbsource"].create(
{
"conn_string": " ".join( "conn_string": " ".join(
"%s='%s'" % item for item in connection_info.items() "%s='%s'" % item for item in connection_info.items()
), ),
"connector": "postgresql", "connector": "postgresql",
"name": "test postgres with current odoo config", "name": "test postgres with current odoo config",
"password": password, "password": password,
}) }
)
def _test_adapter_method( def _test_adapter_method(
self, method_name, side_effect=None, return_value=None, self,
create=False, args=None, kwargs=None, method_name,
side_effect=None,
return_value=None,
create=False,
args=None,
kwargs=None,
): ):
if args is None: if args is None:
args = [] args = []
if kwargs is None: if kwargs is None:
kwargs = {} kwargs = {}
adapter = '%s_postgresql' % method_name adapter = "%s_postgresql" % method_name
with mock.patch.object(self.dbsource, with mock.patch.object(self.dbsource, adapter, create=create) as adapter:
adapter, create=create) as adapter:
if side_effect is not None: if side_effect is not None:
adapter.side_effect = side_effect adapter.side_effect = side_effect
elif return_value is not None: elif return_value is not None:
@@ -49,12 +54,10 @@ class TestBaseExternalDbsource(common.TransactionCase):
def test_conn_string_full(self): def test_conn_string_full(self):
""" It should add password if string interpolation not detected """ """ It should add password if string interpolation not detected """
self.dbsource.conn_string = 'User=Derp;' self.dbsource.conn_string = "User=Derp;"
self.dbsource.password = 'password' self.dbsource.password = "password"
expect = self.dbsource.conn_string + 'PWD=%s;' % self.dbsource.password expect = self.dbsource.conn_string + "PWD=%s;" % self.dbsource.password
self.assertEqual( self.assertEqual(self.dbsource.conn_string_full, expect)
self.dbsource.conn_string_full, expect,
)
# Interface # Interface
@@ -65,16 +68,14 @@ class TestBaseExternalDbsource(common.TransactionCase):
def test_connection_fail(self): def test_connection_fail(self):
""" It should raise for failed/invalid connection """ """ It should raise for failed/invalid connection """
with mock.patch.object(self.dbsource, 'connection_open') as conn: with mock.patch.object(self.dbsource, "connection_open") as conn:
conn.side_effect = Exception conn.side_effect = Exception
with self.assertRaises(ConnectionFailedError): with self.assertRaises(ConnectionFailedError):
self.dbsource.connection_test() self.dbsource.connection_test()
def test_connection_open_calls_close(self): def test_connection_open_calls_close(self):
""" It should close connection after context ends """ """ It should close connection after context ends """
with mock.patch.object( with mock.patch.object(self.dbsource, "connection_close") as close:
self.dbsource, 'connection_close',
) as close:
with self.dbsource.connection_open(): with self.dbsource.connection_open():
pass pass
close.assert_called_once() close.assert_called_once()
@@ -82,9 +83,7 @@ class TestBaseExternalDbsource(common.TransactionCase):
def test_connection_close(self): def test_connection_close(self):
""" It should call adapter's close method """ """ It should call adapter's close method """
args = [mock.MagicMock()] args = [mock.MagicMock()]
res, adapter = self._test_adapter_method( res, adapter = self._test_adapter_method("connection_close", args=args)
'connection_close', args=args,
)
adapter.assert_called_once_with(args[0]) adapter.assert_called_once_with(args[0])
def test_execute_asserts_query_arg(self): def test_execute_asserts_query_arg(self):
@@ -94,144 +93,140 @@ class TestBaseExternalDbsource(common.TransactionCase):
def test_execute_calls_adapter(self): def test_execute_calls_adapter(self):
""" It should call the adapter methods with proper args """ """ It should call the adapter methods with proper args """
expect = ('query', 'execute', 'metadata') expect = ("query", "execute", "metadata")
return_value = 'rows', 'cols' return_value = "rows", "cols"
res, adapter = self._test_adapter_method( res, adapter = self._test_adapter_method(
'execute', args=expect, return_value=return_value, "execute", args=expect, return_value=return_value
) )
adapter.assert_called_once_with(*expect) adapter.assert_called_once_with(*expect)
def test_execute_return(self): def test_execute_return(self):
""" It should return rows if not metadata """ """ It should return rows if not metadata """
expect = (True, True, False) expect = (True, True, False)
return_value = 'rows', 'cols' return_value = "rows", "cols"
res, adapter = self._test_adapter_method( res, adapter = self._test_adapter_method(
'execute', args=expect, return_value=return_value, "execute", args=expect, return_value=return_value
) )
self.assertEqual(res, return_value[0]) self.assertEqual(res, return_value[0])
def test_execute_return_metadata(self): def test_execute_return_metadata(self):
""" It should return rows and cols if metadata """ """ It should return rows and cols if metadata """
expect = (True, True, True) expect = (True, True, True)
return_value = 'rows', 'cols' return_value = "rows", "cols"
res, adapter = self._test_adapter_method( res, adapter = self._test_adapter_method(
'execute', args=expect, return_value=return_value, "execute", args=expect, return_value=return_value
)
self.assertEqual(
res,
{'rows': return_value[0],
'cols': return_value[1]},
) )
self.assertEqual(res, {"rows": return_value[0], "cols": return_value[1]})
def test_remote_browse(self): def test_remote_browse(self):
""" It should call the adapter method with proper args """ """ It should call the adapter method with proper args """
args = [1], 'args' args = [1], "args"
kwargs = {'kwargs': True} kwargs = {"kwargs": True}
self.dbsource.current_table = 'table' self.dbsource.current_table = "table"
res, adapter = self._test_adapter_method( res, adapter = self._test_adapter_method(
'remote_browse', create=True, args=args, kwargs=kwargs, "remote_browse", create=True, args=args, kwargs=kwargs
) )
adapter.assert_called_once_with(*args, **kwargs) adapter.assert_called_once_with(*args, **kwargs)
self.assertEqual(res, adapter()) self.assertEqual(res, adapter())
def test_remote_browse_asserts_current_table(self): def test_remote_browse_asserts_current_table(self):
""" It should raise AssertionError if a table not selected """ """ It should raise AssertionError if a table not selected """
args = [1], 'args' args = [1], "args"
kwargs = {'kwargs': True} kwargs = {"kwargs": True}
with self.assertRaises(AssertionError): with self.assertRaises(AssertionError):
res, adapter = self._test_adapter_method( res, adapter = self._test_adapter_method(
'remote_browse', create=True, args=args, kwargs=kwargs, "remote_browse", create=True, args=args, kwargs=kwargs
) )
def test_remote_create(self): def test_remote_create(self):
""" It should call the adapter method with proper args """ """ It should call the adapter method with proper args """
args = {'val': 'Value'}, 'args' args = {"val": "Value"}, "args"
kwargs = {'kwargs': True} kwargs = {"kwargs": True}
self.dbsource.current_table = 'table' self.dbsource.current_table = "table"
res, adapter = self._test_adapter_method( res, adapter = self._test_adapter_method(
'remote_create', create=True, args=args, kwargs=kwargs, "remote_create", create=True, args=args, kwargs=kwargs
) )
adapter.assert_called_once_with(*args, **kwargs) adapter.assert_called_once_with(*args, **kwargs)
self.assertEqual(res, adapter()) self.assertEqual(res, adapter())
def test_remote_create_asserts_current_table(self): def test_remote_create_asserts_current_table(self):
""" It should raise AssertionError if a table not selected """ """ It should raise AssertionError if a table not selected """
args = [1], 'args' args = [1], "args"
kwargs = {'kwargs': True} kwargs = {"kwargs": True}
with self.assertRaises(AssertionError): with self.assertRaises(AssertionError):
res, adapter = self._test_adapter_method( res, adapter = self._test_adapter_method(
'remote_create', create=True, args=args, kwargs=kwargs, "remote_create", create=True, args=args, kwargs=kwargs
) )
def test_remote_delete(self): def test_remote_delete(self):
""" It should call the adapter method with proper args """ """ It should call the adapter method with proper args """
args = [1], 'args' args = [1], "args"
kwargs = {'kwargs': True} kwargs = {"kwargs": True}
self.dbsource.current_table = 'table' self.dbsource.current_table = "table"
res, adapter = self._test_adapter_method( res, adapter = self._test_adapter_method(
'remote_delete', create=True, args=args, kwargs=kwargs, "remote_delete", create=True, args=args, kwargs=kwargs
) )
adapter.assert_called_once_with(*args, **kwargs) adapter.assert_called_once_with(*args, **kwargs)
self.assertEqual(res, adapter()) self.assertEqual(res, adapter())
def test_remote_delete_asserts_current_table(self): def test_remote_delete_asserts_current_table(self):
""" It should raise AssertionError if a table not selected """ """ It should raise AssertionError if a table not selected """
args = [1], 'args' args = [1], "args"
kwargs = {'kwargs': True} kwargs = {"kwargs": True}
with self.assertRaises(AssertionError): with self.assertRaises(AssertionError):
res, adapter = self._test_adapter_method( res, adapter = self._test_adapter_method(
'remote_delete', create=True, args=args, kwargs=kwargs, "remote_delete", create=True, args=args, kwargs=kwargs
) )
def test_remote_search(self): def test_remote_search(self):
""" It should call the adapter method with proper args """ """ It should call the adapter method with proper args """
args = {'search': 'query'}, 'args' args = {"search": "query"}, "args"
kwargs = {'kwargs': True} kwargs = {"kwargs": True}
self.dbsource.current_table = 'table' self.dbsource.current_table = "table"
res, adapter = self._test_adapter_method( res, adapter = self._test_adapter_method(
'remote_search', create=True, args=args, kwargs=kwargs, "remote_search", create=True, args=args, kwargs=kwargs
) )
adapter.assert_called_once_with(*args, **kwargs) adapter.assert_called_once_with(*args, **kwargs)
self.assertEqual(res, adapter()) self.assertEqual(res, adapter())
def test_remote_search_asserts_current_table(self): def test_remote_search_asserts_current_table(self):
""" It should raise AssertionError if a table not selected """ """ It should raise AssertionError if a table not selected """
args = [1], 'args' args = [1], "args"
kwargs = {'kwargs': True} kwargs = {"kwargs": True}
with self.assertRaises(AssertionError): with self.assertRaises(AssertionError):
res, adapter = self._test_adapter_method( res, adapter = self._test_adapter_method(
'remote_search', create=True, args=args, kwargs=kwargs, "remote_search", create=True, args=args, kwargs=kwargs
) )
def test_remote_update(self): def test_remote_update(self):
""" It should call the adapter method with proper args """ """ It should call the adapter method with proper args """
args = [1], {'vals': 'Value'}, 'args' args = [1], {"vals": "Value"}, "args"
kwargs = {'kwargs': True} kwargs = {"kwargs": True}
self.dbsource.current_table = 'table' self.dbsource.current_table = "table"
res, adapter = self._test_adapter_method( res, adapter = self._test_adapter_method(
'remote_update', create=True, args=args, kwargs=kwargs, "remote_update", create=True, args=args, kwargs=kwargs
) )
adapter.assert_called_once_with(*args, **kwargs) adapter.assert_called_once_with(*args, **kwargs)
self.assertEqual(res, adapter()) self.assertEqual(res, adapter())
def test_remote_update_asserts_current_table(self): def test_remote_update_asserts_current_table(self):
""" It should raise AssertionError if a table not selected """ """ It should raise AssertionError if a table not selected """
args = [1], 'args' args = [1], "args"
kwargs = {'kwargs': True} kwargs = {"kwargs": True}
with self.assertRaises(AssertionError): with self.assertRaises(AssertionError):
res, adapter = self._test_adapter_method( res, adapter = self._test_adapter_method(
'remote_update', create=True, args=args, kwargs=kwargs, "remote_update", create=True, args=args, kwargs=kwargs
) )
# Postgres # Postgres
def test_execute_postgresql(self): def test_execute_postgresql(self):
""" It should call generic executor with proper args """ """ It should call generic executor with proper args """
expect = ('query', 'execute', 'metadata') expect = ("query", "execute", "metadata")
with mock.patch.object( with mock.patch.object(
self.dbsource, '_execute_generic', autospec=True, self.dbsource, "_execute_generic", autospec=True
) as execute: ) as execute:
execute.return_value = 'rows', 'cols' execute.return_value = "rows", "cols"
self.dbsource.execute(*expect) self.dbsource.execute(*expect)
execute.assert_called_once_with(*expect) execute.assert_called_once_with(*expect)
@@ -239,24 +234,19 @@ class TestBaseExternalDbsource(common.TransactionCase):
def test_execute_calls_adapter_old_api(self): def test_execute_calls_adapter_old_api(self):
""" It should call the adapter correctly if old kwargs provided """ """ It should call the adapter correctly if old kwargs provided """
expect = [None, None, 'metadata'] expect = [None, None, "metadata"]
with mock.patch.object( with mock.patch.object(
self.dbsource, 'execute_postgresql', autospec=True, self.dbsource, "execute_postgresql", autospec=True
) as psql: ) as psql:
psql.return_value = 'rows', 'cols' psql.return_value = "rows", "cols"
self.dbsource.execute( self.dbsource.execute(*expect, sqlparams="params", sqlquery="query")
*expect, sqlparams='params', sqlquery='query' expect[0], expect[1] = "query", "params"
)
expect[0], expect[1] = 'query', 'params'
psql.assert_called_once_with(*expect) psql.assert_called_once_with(*expect)
def test_conn_open(self): def test_conn_open(self):
""" It should return open connection for use """ """ It should return open connection for use """
with mock.patch.object( with mock.patch.object(
self.dbsource, 'connection_open', autospec=True, self.dbsource, "connection_open", autospec=True
) as connection: ) as connection:
res = self.dbsource.conn_open() res = self.dbsource.conn_open()
self.assertEqual( self.assertEqual(res, connection().__enter__())
res,
connection().__enter__(),
)

View File

@@ -47,7 +47,6 @@
<record model="ir.actions.act_window" id="action_dbsource"> <record model="ir.actions.act_window" id="action_dbsource">
<field name="name">External Database Sources</field> <field name="name">External Database Sources</field>
<field name="res_model">base.external.dbsource</field> <field name="res_model">base.external.dbsource</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form</field> <field name="view_mode">tree,form</field>
<field name="view_id" ref="view_dbsource_tree"/> <field name="view_id" ref="view_dbsource_tree"/>
</record> </record>