diff --git a/addons/crm_iap_lead/models/crm_iap_lead_mining_request.py b/addons/crm_iap_lead/models/crm_iap_lead_mining_request.py index f3bedd5905a..4179861e343 100644 --- a/addons/crm_iap_lead/models/crm_iap_lead_mining_request.py +++ b/addons/crm_iap_lead/models/crm_iap_lead_mining_request.py @@ -4,8 +4,7 @@ import logging from odoo import api, fields, models, _ -from odoo.exceptions import ValidationError -from odoo.addons.iap import jsonrpc, InsufficientCreditError +from odoo.addons.iap.tools import iap_tools _logger = logging.getLogger(__name__) @@ -180,9 +179,9 @@ class CRMLeadMiningRequest(models.Model): 'data': server_payload } try: - response = jsonrpc(endpoint, params=params, timeout=300) + response = iap_tools.iap_jsonrpc(endpoint, params=params, timeout=300) return response['data'] - except InsufficientCreditError as e: + except iap_tools.InsufficientCreditError as e: self.error = 'Insufficient credits. Recharge your account and retry.' self.state = 'error' self._cr.commit() diff --git a/addons/crm_iap_lead_enrich/models/crm_lead.py b/addons/crm_iap_lead_enrich/models/crm_lead.py index 0475750dc32..e03f82771e8 100644 --- a/addons/crm_iap_lead_enrich/models/crm_lead.py +++ b/addons/crm_iap_lead_enrich/models/crm_lead.py @@ -5,7 +5,7 @@ import datetime import logging from odoo import _, api, fields, models, tools -from odoo.addons.iap import InsufficientCreditError +from odoo.addons.iap.tools import iap_tools _logger = logging.getLogger(__name__) @@ -58,7 +58,7 @@ class Lead(models.Model): if lead_emails: try: iap_response = self.env['iap.enrich.api']._request_enrich(lead_emails) - except InsufficientCreditError: + except iap_tools.InsufficientCreditError: _logger.info('Sent batch %s enrich requests: failed because of credit', len(lead_emails)) if not from_cron: data = { diff --git a/addons/crm_iap_lead_enrich/models/iap_enrich_api.py b/addons/crm_iap_lead_enrich/models/iap_enrich_api.py index 9d6961ee0dc..f6a85d287ed 100644 --- a/addons/crm_iap_lead_enrich/models/iap_enrich_api.py +++ b/addons/crm_iap_lead_enrich/models/iap_enrich_api.py @@ -2,7 +2,7 @@ # Part of Odoo. See LICENSE file for full copyright and licensing details. from odoo import models, api -from odoo.addons import iap +from odoo.addons.iap.tools import iap_tools class IapEnrichAPI(models.AbstractModel): @@ -17,7 +17,7 @@ class IapEnrichAPI(models.AbstractModel): params['account_token'] = account.account_token params['dbuuid'] = dbuuid base_url = self.env['ir.config_parameter'].sudo().get_param('enrich.endpoint', self._DEFAULT_ENDPOINT) - return iap.jsonrpc(base_url + local_endpoint, params=params, timeout=300) + return iap_tools.iap_jsonrpc(base_url + local_endpoint, params=params, timeout=300) @api.model def _request_enrich(self, lead_emails): diff --git a/addons/crm_iap_lead_enrich/tests/common.py b/addons/crm_iap_lead_enrich/tests/common.py index ccdc604c12a..38ed094f93f 100644 --- a/addons/crm_iap_lead_enrich/tests/common.py +++ b/addons/crm_iap_lead_enrich/tests/common.py @@ -5,7 +5,7 @@ from functools import partial from unittest.mock import patch from odoo import exceptions -from odoo.addons import iap +from odoo.addons.iap.tools import iap_tools from odoo.addons.crm_iap_lead_enrich.models.iap_enrich_api import IapEnrichAPI from odoo.tests import common, new_test_user @@ -36,7 +36,7 @@ class MockIAPEnrich(common.BaseCase): result = {} for lead_id, email in params['domains'].items(): if sim_error and sim_error == 'credit': - raise iap.InsufficientCreditError('InsufficientCreditError') + raise iap_tools.InsufficientCreditError('InsufficientCreditError') elif sim_error and sim_error == 'jsonrpc_exception': raise exceptions.AccessError( 'The url that this service requested returned an error. Please contact the author of the app. The url it tried to contact was ' + local_endpoint diff --git a/addons/crm_iap_lead_website/models/crm_reveal_rule.py b/addons/crm_iap_lead_website/models/crm_reveal_rule.py index 58cf4093a9c..aadab777881 100644 --- a/addons/crm_iap_lead_website/models/crm_reveal_rule.py +++ b/addons/crm_iap_lead_website/models/crm_reveal_rule.py @@ -9,7 +9,7 @@ from dateutil.relativedelta import relativedelta import odoo from odoo import api, fields, models, tools, _ -from odoo.addons.iap import jsonrpc +from odoo.addons.iap.tools import iap_tools from odoo.addons.crm.models import crm_stage from odoo.exceptions import ValidationError @@ -323,7 +323,7 @@ class CRMRevealRule(models.Model): 'account_token': account_token.account_token, 'data': server_payload } - result = jsonrpc(endpoint, params=params, timeout=300) + result = iap_tools.iap_jsonrpc(endpoint, params=params, timeout=300) for res in result.get('reveal_data', []): if not res.get('not_found'): lead = self._create_lead_from_response(res) diff --git a/addons/iap/__init__.py b/addons/iap/__init__.py index 69635445a98..39a74f0331b 100644 --- a/addons/iap/__init__.py +++ b/addons/iap/__init__.py @@ -2,4 +2,12 @@ # Part of Odoo. See LICENSE file for full copyright and licensing details. from . import models -from .models.iap import jsonrpc, charge, authorize, capture, cancel, InsufficientCreditError +from . import tools + +# compatibility imports +from odoo.addons.iap.tools.iap_tools import iap_jsonrpc as jsonrpc +from odoo.addons.iap.tools.iap_tools import iap_authorize as authorize +from odoo.addons.iap.tools.iap_tools import iap_cancel as cancel +from odoo.addons.iap.tools.iap_tools import iap_capture as capture +from odoo.addons.iap.tools.iap_tools import iap_charge as charge +from odoo.addons.iap.tools.iap_tools import InsufficientCreditError diff --git a/addons/iap/__manifest__.py b/addons/iap/__manifest__.py index ac86a8718fc..5d13d6739bd 100644 --- a/addons/iap/__manifest__.py +++ b/addons/iap/__manifest__.py @@ -1,15 +1,18 @@ # -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. - { 'name': 'In-App Purchases', 'category': 'Hidden/Tools', + 'version': '1.1', 'summary': 'Basic models and helpers to support In-App purchases.', 'description': """ -This module provides standard tools (account model, context manager and helpers) to support In-App purchases inside Odoo. -""", - 'depends': ['web', 'base_setup'], +This module provides standard tools (account model, context manager and helpers) +to support In-App purchases inside Odoo. """, + 'depends': [ + 'web', + 'base_setup' + ], 'data': [ 'security/ir.model.access.csv', 'security/ir_rule.xml', @@ -17,9 +20,6 @@ This module provides standard tools (account model, context manager and helpers) 'views/iap_views.xml', 'views/res_config_settings.xml', ], - 'images': [ - 'static/src/img/iap_logo.svg', - ], 'qweb': [ 'static/src/xml/iap_templates.xml', ], diff --git a/addons/iap/models/__init__.py b/addons/iap/models/__init__.py index 22e8f027832..d148377834f 100644 --- a/addons/iap/models/__init__.py +++ b/addons/iap/models/__init__.py @@ -1,6 +1,5 @@ # -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. -from . import iap +from . import iap_account from . import res_config_settings - diff --git a/addons/iap/models/iap.py b/addons/iap/models/iap.py deleted file mode 100644 index eb235a0ab5f..00000000000 --- a/addons/iap/models/iap.py +++ /dev/null @@ -1,256 +0,0 @@ -# -*- coding: utf-8 -*- -import contextlib -import logging -import json -import uuid - -import werkzeug.urls -import requests - -from odoo import api, fields, models, exceptions, _ -from odoo.tools import pycompat - -_logger = logging.getLogger(__name__) - -DEFAULT_ENDPOINT = 'https://iap.odoo.com' - - -#---------------------------------------------------------- -# Helpers for both clients and proxy -#---------------------------------------------------------- -def get_endpoint(env): - url = env['ir.config_parameter'].sudo().get_param('iap.endpoint', DEFAULT_ENDPOINT) - return url - - -#---------------------------------------------------------- -# Helpers for clients -#---------------------------------------------------------- -class InsufficientCreditError(Exception): - pass - - -class AuthenticationError(Exception): - pass - - -def jsonrpc(url, method='call', params=None, timeout=15): - """ - Calls the provided JSON-RPC endpoint, unwraps the result and - returns JSON-RPC errors as exceptions. - """ - payload = { - 'jsonrpc': '2.0', - 'method': method, - 'params': params, - 'id': uuid.uuid4().hex, - } - - _logger.info('iap jsonrpc %s', url) - try: - req = requests.post(url, json=payload, timeout=timeout) - req.raise_for_status() - response = req.json() - if 'error' in response: - name = response['error']['data'].get('name').rpartition('.')[-1] - message = response['error']['data'].get('message') - if name == 'InsufficientCreditError': - e_class = InsufficientCreditError - elif name == 'AccessError': - e_class = exceptions.AccessError - elif name == 'UserError': - e_class = exceptions.UserError - else: - raise requests.exceptions.ConnectionError() - e = e_class(message) - e.data = response['error']['data'] - raise e - return response.get('result') - except (ValueError, requests.exceptions.ConnectionError, requests.exceptions.MissingSchema, requests.exceptions.Timeout, requests.exceptions.HTTPError) as e: - raise exceptions.AccessError( - _('The url that this service requested returned an error. Please contact the author of the app. The url it tried to contact was %s', url) - ) - -#---------------------------------------------------------- -# Helpers for proxy -#---------------------------------------------------------- -class IapTransaction(object): - - def __init__(self): - self.credit = None - -def authorize(env, key, account_token, credit, dbuuid=False, description=None, credit_template=None): - endpoint = get_endpoint(env) - params = { - 'account_token': account_token, - 'credit': credit, - 'key': key, - 'description': description, - } - if dbuuid: - params.update({'dbuuid': dbuuid}) - try: - transaction_token = jsonrpc(endpoint + '/iap/1/authorize', params=params) - except InsufficientCreditError as e: - if credit_template: - arguments = json.loads(e.args[0]) - arguments['body'] = pycompat.to_text(env['ir.qweb']._render(credit_template)) - e.args = (json.dumps(arguments),) - raise e - return transaction_token - -def cancel(env, transaction_token, key): - endpoint = get_endpoint(env) - params = { - 'token': transaction_token, - 'key': key, - } - r = jsonrpc(endpoint + '/iap/1/cancel', params=params) - return r - -def capture(env, transaction_token, key, credit): - endpoint = get_endpoint(env) - params = { - 'token': transaction_token, - 'key': key, - 'credit_to_capture': credit, - } - r = jsonrpc(endpoint + '/iap/1/capture', params=params) - return r - - -@contextlib.contextmanager -def charge(env, key, account_token, credit, dbuuid=False, description=None, credit_template=None): - """ - Account charge context manager: takes a hold for ``credit`` - amount before executing the body, then captures it if there - is no error, or cancels it if the body generates an exception. - - :param str key: service identifier - :param str account_token: user identifier - :param int credit: cost of the body's operation - :param description: a description of the purpose of the charge, - the user will be able to see it in their - dashboard - :type description: str - :param credit_template: a QWeb template to render and show to the - user if their account does not have enough - credits for the requested operation - :type credit_template: str - """ - transaction_token = authorize(env, key, account_token, credit, dbuuid, description, credit_template) - try: - transaction = IapTransaction() - transaction.credit = credit - yield transaction - except Exception as e: - r = cancel(env,transaction_token, key) - raise e - else: - r = capture(env,transaction_token, key, transaction.credit) - -#---------------------------------------------------------- -# Models for client -#---------------------------------------------------------- -class IapAccount(models.Model): - _name = 'iap.account' - _rec_name = 'service_name' - _description = 'IAP Account' - - service_name = fields.Char() - account_token = fields.Char(default=lambda s: uuid.uuid4().hex) - company_ids = fields.Many2many('res.company') - - @api.model - def get(self, service_name, force_create=True): - domain = [ - ('service_name', '=', service_name), - '|', - ('company_ids', 'in', self.env.companies.ids), - ('company_ids', '=', False) - ] - accounts = self.search(domain, order='id desc') - if not accounts: - with self.pool.cursor() as cr: - # Since the account did not exist yet, we will encounter a NoCreditError, - # which is going to rollback the database and undo the account creation, - # preventing the process to continue any further. - - # Flush the pending operations to avoid a deadlock. - self.flush() - IapAccount = self.with_env(self.env(cr=cr)) - account = IapAccount.search(domain, order='id desc', limit=1) - if not account: - if not force_create: - return account - account = IapAccount.create({'service_name': service_name}) - # fetch 'account_token' into cache with this cursor, - # as self's cursor cannot see this account - account_token = account.account_token - account = self.browse(account.id) - self.env.cache.set(account, IapAccount._fields['account_token'], account_token) - return account - accounts_with_company = accounts.filtered(lambda acc: acc.company_ids) - if accounts_with_company: - return accounts_with_company[0] - return accounts[0] - - @api.model - def get_credits_url(self, service_name, base_url='', credit=0, trial=False): - dbuuid = self.env['ir.config_parameter'].sudo().get_param('database.uuid') - if not base_url: - endpoint = get_endpoint(self.env) - route = '/iap/1/credit' - base_url = endpoint + route - account_token = self.get(service_name).account_token - d = { - 'dbuuid': dbuuid, - 'service_name': service_name, - 'account_token': account_token, - 'credit': credit, - } - if trial: - d.update({'trial': trial}) - return '%s?%s' % (base_url, werkzeug.urls.url_encode(d)) - - @api.model - def get_account_url(self): - route = '/iap/services' - endpoint = get_endpoint(self.env) - d = {'dbuuid': self.env['ir.config_parameter'].sudo().get_param('database.uuid')} - - return '%s?%s' % (endpoint + route, werkzeug.urls.url_encode(d)) - - @api.model - def get_config_account_url(self): - account = self.env['iap.account'].get('partner_autocomplete') - action = self.env.ref('iap.iap_account_action') - menu = self.env.ref('iap.iap_account_menu') - no_one = self.user_has_groups('base.group_no_one') - if account: - url = "/web#id=%s&action=%s&model=iap.account&view_type=form&menu_id=%s" % (account.id, action.id, menu.id) - else: - url = "/web#action=%s&model=iap.account&view_type=form&menu_id=%s" % (action.id, menu.id) - return no_one and url - - @api.model - def get_credits(self, service_name): - account = self.get(service_name, force_create=False) - credit = 0 - - if account: - route = '/iap/1/balance' - endpoint = get_endpoint(self.env) - url = endpoint + route - params = { - 'dbuuid': self.env['ir.config_parameter'].sudo().get_param('database.uuid'), - 'account_token': account.account_token, - 'service_name': service_name, - } - try: - credit = jsonrpc(url=url, params=params) - except Exception as e: - _logger.info('Get credit error : %s', str(e)) - credit = -1 - - return credit diff --git a/addons/iap/models/iap_account.py b/addons/iap/models/iap_account.py new file mode 100644 index 00000000000..f4be36c7dd4 --- /dev/null +++ b/addons/iap/models/iap_account.py @@ -0,0 +1,120 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +import logging +import uuid +import werkzeug.urls + +from odoo import api, fields, models +from odoo.addons.iap.tools import iap_tools + +_logger = logging.getLogger(__name__) + +DEFAULT_ENDPOINT = 'https://iap.odoo.com' + + +class IapAccount(models.Model): + _name = 'iap.account' + _rec_name = 'service_name' + _description = 'IAP Account' + + service_name = fields.Char() + account_token = fields.Char(default=lambda s: uuid.uuid4().hex) + company_ids = fields.Many2many('res.company') + + @api.model + def get(self, service_name, force_create=True): + domain = [ + ('service_name', '=', service_name), + '|', + ('company_ids', 'in', self.env.companies.ids), + ('company_ids', '=', False) + ] + accounts = self.search(domain, order='id desc') + if not accounts: + with self.pool.cursor() as cr: + # Since the account did not exist yet, we will encounter a NoCreditError, + # which is going to rollback the database and undo the account creation, + # preventing the process to continue any further. + + # Flush the pending operations to avoid a deadlock. + self.flush() + IapAccount = self.with_env(self.env(cr=cr)) + account = IapAccount.search(domain, order='id desc', limit=1) + if not account: + if not force_create: + return account + account = IapAccount.create({'service_name': service_name}) + # fetch 'account_token' into cache with this cursor, + # as self's cursor cannot see this account + account_token = account.account_token + account = self.browse(account.id) + self.env.cache.set(account, IapAccount._fields['account_token'], account_token) + return account + accounts_with_company = accounts.filtered(lambda acc: acc.company_ids) + if accounts_with_company: + return accounts_with_company[0] + return accounts[0] + + @api.model + def get_credits_url(self, service_name, base_url='', credit=0, trial=False): + """ Called notably by ajax crash manager, buy more widget, partner_autocomplete, sanilmail. """ + dbuuid = self.env['ir.config_parameter'].sudo().get_param('database.uuid') + if not base_url: + endpoint = iap_tools.iap_get_endpoint(self.env) + route = '/iap/1/credit' + base_url = endpoint + route + account_token = self.get(service_name).account_token + d = { + 'dbuuid': dbuuid, + 'service_name': service_name, + 'account_token': account_token, + 'credit': credit, + } + if trial: + d.update({'trial': trial}) + return '%s?%s' % (base_url, werkzeug.urls.url_encode(d)) + + @api.model + def get_account_url(self): + """ Called only by res settings """ + route = '/iap/services' + endpoint = iap_tools.iap_get_endpoint(self.env) + d = {'dbuuid': self.env['ir.config_parameter'].sudo().get_param('database.uuid')} + + return '%s?%s' % (endpoint + route, werkzeug.urls.url_encode(d)) + + @api.model + def get_config_account_url(self): + """ Called notably by ajax partner_autocomplete. """ + account = self.env['iap.account'].get('partner_autocomplete') + action = self.env.ref('iap.iap_account_action') + menu = self.env.ref('iap.iap_account_menu') + no_one = self.user_has_groups('base.group_no_one') + if account: + url = "/web#id=%s&action=%s&model=iap.account&view_type=form&menu_id=%s" % (account.id, action.id, menu.id) + else: + url = "/web#action=%s&model=iap.account&view_type=form&menu_id=%s" % (action.id, menu.id) + return no_one and url + + @api.model + def get_credits(self, service_name): + account = self.get(service_name, force_create=False) + credit = 0 + + if account: + route = '/iap/1/balance' + endpoint = iap_tools.iap_get_endpoint(self.env) + url = endpoint + route + params = { + 'dbuuid': self.env['ir.config_parameter'].sudo().get_param('database.uuid'), + 'account_token': account.account_token, + 'service_name': service_name, + } + try: + credit = iap_tools.iap_jsonrpc(url=url, params=params) + except Exception as e: + _logger.info('Get credit error : %s', str(e)) + credit = -1 + + return credit diff --git a/addons/iap/tools/__init__.py b/addons/iap/tools/__init__.py new file mode 100644 index 00000000000..d2ad04683aa --- /dev/null +++ b/addons/iap/tools/__init__.py @@ -0,0 +1,4 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from . import iap_tools diff --git a/addons/iap/tools/iap_tools.py b/addons/iap/tools/iap_tools.py new file mode 100644 index 00000000000..da17972f0f1 --- /dev/null +++ b/addons/iap/tools/iap_tools.py @@ -0,0 +1,150 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +import contextlib +import logging +import json +import requests +import uuid + +from odoo import exceptions, _ +from odoo.tools import pycompat + +_logger = logging.getLogger(__name__) + +DEFAULT_ENDPOINT = 'https://iap.odoo.com' + +#---------------------------------------------------------- +# Helpers for both clients and proxy +#---------------------------------------------------------- + +def iap_get_endpoint(env): + url = env['ir.config_parameter'].sudo().get_param('iap.endpoint', DEFAULT_ENDPOINT) + return url + +#---------------------------------------------------------- +# Helpers for clients +#---------------------------------------------------------- + +class InsufficientCreditError(Exception): + pass + + +def iap_jsonrpc(url, method='call', params=None, timeout=15): + """ + Calls the provided JSON-RPC endpoint, unwraps the result and + returns JSON-RPC errors as exceptions. + """ + payload = { + 'jsonrpc': '2.0', + 'method': method, + 'params': params, + 'id': uuid.uuid4().hex, + } + + _logger.info('iap jsonrpc %s', url) + try: + req = requests.post(url, json=payload, timeout=timeout) + req.raise_for_status() + response = req.json() + if 'error' in response: + name = response['error']['data'].get('name').rpartition('.')[-1] + message = response['error']['data'].get('message') + if name == 'InsufficientCreditError': + e_class = InsufficientCreditError + elif name == 'AccessError': + e_class = exceptions.AccessError + elif name == 'UserError': + e_class = exceptions.UserError + else: + raise requests.exceptions.ConnectionError() + e = e_class(message) + e.data = response['error']['data'] + raise e + return response.get('result') + except (ValueError, requests.exceptions.ConnectionError, requests.exceptions.MissingSchema, requests.exceptions.Timeout, requests.exceptions.HTTPError) as e: + raise exceptions.AccessError( + _('The url that this service requested returned an error. Please contact the author of the app. The url it tried to contact was %s', url) + ) + +#---------------------------------------------------------- +# Helpers for proxy +#---------------------------------------------------------- + +class IapTransaction(object): + + def __init__(self): + self.credit = None + + +def iap_authorize(env, key, account_token, credit, dbuuid=False, description=None, credit_template=None): + endpoint = iap_get_endpoint(env) + params = { + 'account_token': account_token, + 'credit': credit, + 'key': key, + 'description': description, + } + if dbuuid: + params.update({'dbuuid': dbuuid}) + try: + transaction_token = iap_jsonrpc(endpoint + '/iap/1/authorize', params=params) + except InsufficientCreditError as e: + if credit_template: + arguments = json.loads(e.args[0]) + arguments['body'] = pycompat.to_text(env['ir.qweb']._render(credit_template)) + e.args = (json.dumps(arguments),) + raise e + return transaction_token + + +def iap_cancel(env, transaction_token, key): + endpoint = iap_get_endpoint(env) + params = { + 'token': transaction_token, + 'key': key, + } + r = iap_jsonrpc(endpoint + '/iap/1/cancel', params=params) + return r + + +def iap_capture(env, transaction_token, key, credit): + endpoint = iap_get_endpoint(env) + params = { + 'token': transaction_token, + 'key': key, + 'credit_to_capture': credit, + } + r = iap_jsonrpc(endpoint + '/iap/1/capture', params=params) + return r + + +@contextlib.contextmanager +def iap_charge(env, key, account_token, credit, dbuuid=False, description=None, credit_template=None): + """ + Account charge context manager: takes a hold for ``credit`` + amount before executing the body, then captures it if there + is no error, or cancels it if the body generates an exception. + + :param str key: service identifier + :param str account_token: user identifier + :param int credit: cost of the body's operation + :param description: a description of the purpose of the charge, + the user will be able to see it in their + dashboard + :type description: str + :param credit_template: a QWeb template to render and show to the + user if their account does not have enough + credits for the requested operation + :type credit_template: str + """ + transaction_token = iap_authorize(env, key, account_token, credit, dbuuid, description, credit_template) + try: + transaction = IapTransaction() + transaction.credit = credit + yield transaction + except Exception as e: + r = iap_cancel(env,transaction_token, key) + raise e + else: + r = iap_capture(env,transaction_token, key, transaction.credit) diff --git a/addons/partner_autocomplete/models/res_partner.py b/addons/partner_autocomplete/models/res_partner.py index 27113317cd9..1c27a6b91ba 100644 --- a/addons/partner_autocomplete/models/res_partner.py +++ b/addons/partner_autocomplete/models/res_partner.py @@ -4,9 +4,9 @@ import logging import json from odoo import api, fields, models, exceptions, _ -from odoo.addons.iap import jsonrpc +from odoo.addons.iap.tools import iap_tools +# TDE FIXME: check those errors at iap level ? from requests.exceptions import ConnectionError, HTTPError -from odoo.addons.iap.models.iap import InsufficientCreditError _logger = logging.getLogger(__name__) @@ -96,11 +96,11 @@ class ResPartner(models.Model): 'zip': self.env.company.zip, }) try: - return jsonrpc(url=url, params=params, timeout=timeout), False + return iap_tools.iap_jsonrpc(url=url, params=params, timeout=timeout), False except (ConnectionError, HTTPError, exceptions.AccessError, exceptions.UserError) as exception: _logger.error('Autocomplete API error: %s' % str(exception)) return False, str(exception) - except InsufficientCreditError as exception: + except iap_tools.InsufficientCreditError as exception: _logger.warning('Insufficient Credits for Autocomplete Service: %s' % str(exception)) return False, 'Insufficient Credit' diff --git a/addons/sms/models/sms_api.py b/addons/sms/models/sms_api.py index 6856717a0c2..b1fa69dab6c 100644 --- a/addons/sms/models/sms_api.py +++ b/addons/sms/models/sms_api.py @@ -2,7 +2,7 @@ # Part of Odoo. See LICENSE file for full copyright and licensing details. from odoo import api, models -from odoo.addons.iap.models import iap +from odoo.addons.iap.tools import iap_tools DEFAULT_ENDPOINT = 'https://iap-sms.odoo.com' @@ -17,7 +17,7 @@ class SmsApi(models.AbstractModel): params['account_token'] = account.account_token endpoint = self.env['ir.config_parameter'].sudo().get_param('sms.endpoint', DEFAULT_ENDPOINT) # TODO PRO, the default timeout is 15, do we have to increase it ? - return iap.jsonrpc(endpoint + local_endpoint, params=params) + return iap_tools.iap_jsonrpc(endpoint + local_endpoint, params=params) @api.model def _send_sms(self, numbers, message): diff --git a/addons/snailmail/models/snailmail_letter.py b/addons/snailmail/models/snailmail_letter.py index e60286cec9e..2eb19271b3c 100644 --- a/addons/snailmail/models/snailmail_letter.py +++ b/addons/snailmail/models/snailmail_letter.py @@ -4,7 +4,7 @@ import re import base64 from odoo import fields, models, api, _ -from odoo.addons.iap import jsonrpc +from odoo.addons.iap.tools import iap_tools from odoo.tools.safe_eval import safe_eval DEFAULT_ENDPOINT = 'https://iap-snailmail.odoo.com' @@ -335,7 +335,7 @@ class SnailmailLetter(models.Model): endpoint = self.env['ir.config_parameter'].sudo().get_param('snailmail.endpoint', DEFAULT_ENDPOINT) timeout = int(self.env['ir.config_parameter'].sudo().get_param('snailmail.timeout', DEFAULT_TIMEOUT)) params = self._snailmail_create('print') - response = jsonrpc(endpoint + PRINT_ENDPOINT, params=params, timeout=timeout) + response = iap_tools.iap_jsonrpc(endpoint + PRINT_ENDPOINT, params=params, timeout=timeout) for doc in response['request']['documents']: if doc.get('sent') and response['request_code'] == 200: note = _('The document was correctly sent by post.
The tracking id is %s', doc['send_id']) diff --git a/doc/webservices/iap.rst b/doc/webservices/iap.rst index 5af45ef7ae3..ce2512c58ad 100644 --- a/doc/webservices/iap.rst +++ b/doc/webservices/iap.rst @@ -289,9 +289,9 @@ server*. There are no requirements when it comes to the server or the communication protocol between the app and our server, but ``iap`` provides a -:func:`~odoo.addons.iap.jsonrpc` helper to call a JSON-RPC2_ endpoint on an +:func:`~odoo.addons.iap.tools.iap_tools.iap_jsonrpc` helper to call a JSON-RPC2_ endpoint on an other Odoo instance and transparently re-raise relevant Odoo exceptions -(:class:`~odoo.addons.iap.models.iap.InsufficientCreditError`, +(:class:`~odoo.addons.iap.tools.iap_tools.InsufficientCreditError`, :class:`odoo.exceptions.AccessError` and :class:`odoo.exceptions.UserError`). In that call, we will need to provide: @@ -308,17 +308,17 @@ In that call, we will need to provide: .. note:: ``iap`` automatically handles - :class:`~odoo.addons.iap.models.iap.InsufficientCreditError` coming from the action + :class:`~odoo.addons.iap.tools.iap_tools.InsufficientCreditError` coming from the action and prompts the user to add credits to their account. - :func:`~odoo.addons.iap.jsonrpc` takes care of re-raising + :func:`~odoo.addons.iap.tools.iap_tools.iap_jsonrpc` takes care of re-raising :class:`~odoo.addons.iap.models.iap.InsufficientCreditError` for you. .. danger:: - If you are not using :func:`~odoo.addons.iap.jsonrpc` you *must* be + If you are not using :func:`~odoo.addons.iap.tools.iap_tools.iap_jsonrpc` you *must* be careful to re-raise - :class:`~odoo.addons.iap.models.iap.InsufficientCreditError` in your handler + :class:`~odoo.addons.iap.tools.iap_tools.InsufficientCreditError` in your handler otherwise the user will not be prompted to credit their account, and the next call will fail the same way. @@ -330,14 +330,14 @@ Service .. queue:: iap_service/series Though that is not *required*, since ``iap`` provides both a client helper -for JSON-RPC2_ calls (:func:`~odoo.addons.iap.jsonrpc`) and a service helper -for transactions (:class:`~odoo.addons.iap.models.iap.charge`) we will also be +for JSON-RPC2_ calls (:func:`~odoo.addons.iap.tools.iap_tools.iap_jsonrpc`) and a service helper +for transactions (:class:`~odoo.addons.iap.tools.iap_tools.iap_charge`) we will also be implementing the service side as an Odoo module: .. patch:: Since the query from the client comes as JSON-RPC2_ we will need the -corresponding controller which can call :class:`~odoo.addons.iap.models.iap.charge` and +corresponding controller which can call :class:`~odoo.addons.iap.tools.iap_tools.iap_charge` and perform the service within: .. patch:: @@ -347,7 +347,7 @@ perform the service within: .. todo:: "My Account" > "Your InApp Services"? -The :class:`~odoo.addons.iap.models.iap.charge` helper will: +The :class:`~odoo.addons.iap.tools.iap_tools.iap_charge` helper will: 1. authorize (create) a transaction with the specified number of credits, if the account does not have enough credits it will raise the relevant @@ -361,7 +361,7 @@ The :class:`~odoo.addons.iap.models.iap.charge` helper will: .. danger:: - By default, :class:`~odoo.addons.iap.models.iap.charge` contacts the *production* + By default, :class:`~odoo.addons.iap.tools.iap_tools.iap_charge` contacts the *production* IAP endpoint, https://iap.odoo.com. While developing and testing your service you may want to point it towards the *development* IAP endpoint https://iap-sandbox.odoo.com. @@ -371,7 +371,7 @@ The :class:`~odoo.addons.iap.models.iap.charge` helper will: Parameters --> System Parameters`, just define an entry for the key ``iap.endpoint`` if none already exists). -The :class:`~odoo.addons.iap.models.iap.charge` helper has two additional optional +The :class:`~odoo.addons.iap.tools.iap_tools.iap_charge` helper has two additional optional parameters we can use to make things clearer to the end-user. ``description`` @@ -543,7 +543,7 @@ care how they are implemented. Transaction identifier, returned by the authorization process and consumed by either capturing or cancelling the transaction. -.. exception:: odoo.addons.iap.models.iap.InsufficientCreditError +.. exception:: odoo.addons.iap.tools.iap_tools.InsufficientCreditError Raised during transaction authorization if the credits requested are not currently available on the account (either not enough credits or too many @@ -555,7 +555,7 @@ care how they are implemented. Raised by: * any operation to which a service token is required, if the service token is invalid; or - * any failure in an inter-server call. (typically, in :func:`~odoo.addons.iap.jsonrpc`). + * any failure in an inter-server call. (typically, in :func:`~odoo.addons.iap.tools.iap_tools.iap_jsonrpc`). .. exception:: odoo.exceptions.UserError :noindex: @@ -576,9 +576,9 @@ In order to test the developped app, we propose a sandbox platform that allows y The latter consists in specific tokens that will work on **IAP-Sandbox only**. * Token ``000000``: Represents a non-existing account. Returns - an :class:`~odoo.addons.iap.models.iap.InsufficientCreditError` on authorize attempt. + an :class:`~odoo.addons.iap.tools.iap_tools.InsufficientCreditError` on authorize attempt. * Token ``000111``: Represents an account without sufficient credits to perform any service. - Returns an :class:`~odoo.addons.iap.models.iap.InsufficientCreditError` on authorize attempt. + Returns an :class:`~odoo.addons.iap.tools.iap_tools.InsufficientCreditError` on authorize attempt. * Token ``111111``: Represents an account with enough credits to perform any service. An authorize attempt will return a dummy transacion token that is processed by the capture and cancel routes. @@ -600,7 +600,7 @@ module provides a few helpers to make IAP flow even simpler. Charging -------- -.. class:: odoo.addons.iap.models.iap.charge(env, key, account_token, credit[, dbuuid, description, credit_template]) +.. class:: odoo.addons.iap.tools.iap_tools.iap_charge(env, key, account_token, credit[, dbuuid, description, credit_template]) A *context manager* for authorizing and automatically capturing or cancelling transactions for use in the backend/proxy. @@ -633,7 +633,7 @@ Charging """ credits = int(MAXIMUM_POWER * factor) description = "We will demonstrate the power of this station on your home planet of Alderaan." - with charge(request.env, SERVICE_KEY, user_account, credits, description) as transaction: + with iap_charge(request.env, SERVICE_KEY, user_account, credits, description) as transaction: # TODO: allow other targets transaction.credit = max(credits, 2) # Sales ongoing one the energy price, @@ -647,7 +647,7 @@ Charging Authorize --------- -.. class:: odoo.addons.iap.models.iap.authorize(env, key, account_token, credit[, dbuuid, description, credit_template]) +.. class:: odoo.addons.iap.tools.iap_tools.iap_authorize(env, key, account_token, credit[, dbuuid, description, credit_template]) Will authorize everything. @@ -688,7 +688,7 @@ Authorize Cancel ------ -.. class:: odoo.addons.iap.models.iap.cancel(env, transaction_token, key) +.. class:: odoo.addons.iap.tools.iap_tools.iap_cancel(env, transaction_token, key) Will cancel an authorized transaction. @@ -726,7 +726,7 @@ Cancel Capture ------- -.. class:: odoo.addons.iap.models.iap.capture(env, transaction_token, key, credit) +.. class:: odoo.addons.iap.tools.iap_tools.iap_capture(env, transaction_token, key, credit) Will capture the amount ``credit`` on the given transaction.