Initial commit: OCA Technical packages (595 packages)

This commit is contained in:
Ernad Husremovic 2025-08-29 15:43:03 +02:00
commit 2cc02aac6e
24950 changed files with 2318079 additions and 0 deletions

View file

@ -0,0 +1,6 @@
from . import common
from . import test_cerberus_list_validator
from . import test_cerberus_validator
from . import test_controller_builder
from . import test_openapi_generator
from . import test_service_context_provider

View file

@ -0,0 +1,263 @@
# Copyright 2017 Akretion (http://www.akretion.com).
# Copyright 2020 ACSONE SA/NV
# @author Sébastien BEAU <sebastien.beau@akretion.com>
# @author Laurent Mignon <laurent.mignon@acsone.eu>
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl).
import copy
from odoo import http
from odoo.tests.common import TransactionCase, get_db_name
from odoo.addons.component.core import (
WorkContext,
_component_databases,
_get_addon_name,
)
from odoo.addons.component.tests.common import (
ComponentRegistryCase,
TransactionComponentCase,
new_rollbacked_env,
)
from ..controllers.main import RestController, _PseudoCollection
from ..core import (
RestServicesRegistry,
_rest_controllers_per_module,
_rest_services_databases,
)
from ..tools import ROUTING_DECORATOR_ATTR, _inspect_methods
class RegistryMixin(object):
@classmethod
def setUpRegistry(cls):
with new_rollbacked_env() as env:
service_registration = env["rest.service.registration"]
# build the registry of every installed addons
services_registry = service_registration._init_global_registry()
cls._services_registry = services_registry
# ensure that we load only the services of the 'installed'
# modules, not 'to install', which means we load only the
# dependencies of the tested addons, not the siblings or
# children addons
service_registration.build_registry(
services_registry, states=("installed",)
)
# build the services of the current tested addon
current_addon = _get_addon_name(cls.__module__)
service_registration.load_services(current_addon, services_registry)
env["rest.service.registration"]._build_controllers_routes(
services_registry
)
class RestServiceRegistryCase(ComponentRegistryCase):
# pylint: disable=W8106
@staticmethod
def _setup_registry(class_or_instance):
ComponentRegistryCase._setup_registry(class_or_instance)
class_or_instance._service_registry = RestServicesRegistry()
# take a copy of registered controllers
class_or_instance._controller_children_classes = copy.deepcopy(
http.Controller.children_classes
)
class_or_instance._original_addon_rest_controllers_per_module = copy.deepcopy(
_rest_controllers_per_module[_get_addon_name(class_or_instance.__module__)]
)
db_name = get_db_name()
# makes the test component registry available for the db name
class_or_instance._original_component_databases = _component_databases.get(
db_name
)
_component_databases[db_name] = class_or_instance.comp_registry
# makes the test service registry available for the db name
class_or_instance._original_services_registry = _rest_services_databases.get(
db_name, {}
)
_rest_services_databases[db_name] = class_or_instance._service_registry
# build the services and controller of every installed addons
# but the current addon (when running with pytest/nosetest, we
# simulate the --test-enable behavior by excluding the current addon
# which is in 'to install' / 'to upgrade' with --test-enable).
current_addon = _get_addon_name(class_or_instance.__module__)
with new_rollbacked_env() as env:
RestServiceRegistration = env["rest.service.registration"]
RestServiceRegistration.build_registry(
class_or_instance._service_registry,
states=("installed",),
exclude_addons=[current_addon],
)
RestServiceRegistration._build_controllers_routes(
class_or_instance._service_registry
)
# register our components
class_or_instance.comp_registry.load_components("base_rest")
# Define a base test controller here to avoid to have this controller
# registered outside tests
class_or_instance._collection_name = "base.rest.test"
BaseTestController = class_or_instance._get_test_controller(class_or_instance)
class_or_instance._BaseTestController = BaseTestController
class_or_instance._controller_route_method_names = {
"my_controller_route_without",
"my_controller_route_with",
"my_controller_route_without_auth_2",
}
@staticmethod
def _get_test_controller(class_or_instance, root_path="/test_controller/"):
class BaseTestController(RestController):
_root_path = root_path
_collection_name = class_or_instance._collection_name
_default_auth = "public"
@http.route("/my_controller_route_without")
def my_controller_route_without(self):
return {}
@http.route(
"/my_controller_route_with",
auth="public",
cors="http://with_cors",
csrf="False",
save_session="False",
)
def my_controller_route_with(self):
return {}
@http.route("/my_controller_route_without_auth_2", auth=None)
def my_controller_route_without_auth_2(self):
return {}
return BaseTestController
@staticmethod
def _teardown_registry(class_or_instance):
ComponentRegistryCase._teardown_registry(class_or_instance)
http.Controller.children_classes = (
class_or_instance._controller_children_classes
)
db_name = get_db_name()
_component_databases[db_name] = class_or_instance._original_component_databases
_rest_services_databases[
db_name
] = class_or_instance._original_services_registry
class_or_instance._service_registry = {}
_rest_controllers_per_module[
_get_addon_name(class_or_instance.__module__)
] = class_or_instance._original_addon_rest_controllers_per_module
@staticmethod
def _build_services(class_or_instance, *classes):
class_or_instance._build_components(*classes)
with new_rollbacked_env() as env:
RestServiceRegistration = env["rest.service.registration"]
current_addon = _get_addon_name(class_or_instance.__module__)
RestServiceRegistration.load_services(
current_addon, class_or_instance._service_registry
)
RestServiceRegistration._build_controllers_routes(
class_or_instance._service_registry
)
@staticmethod
def _get_controller_for(service, addon="base_rest"):
identifier = "{}_{}_{}".format(
get_db_name(),
service._collection.replace(".", "_"),
service._usage.replace(".", "_"),
)
controllers = [
controller
for controller in http.Controller.children_classes.get(addon, [])
if getattr(controller, "_identifier", None) == identifier
]
if not controllers:
return
return controllers[-1]
@staticmethod
def _get_controller_route_methods(controller):
methods = {}
for name, method in _inspect_methods(controller):
if hasattr(method, ROUTING_DECORATOR_ATTR):
methods[name] = method
return methods
@staticmethod
def _get_service_component(class_or_instance, usage, collection=None):
collection = collection or _PseudoCollection(
class_or_instance._collection_name, class_or_instance.env
)
work = WorkContext(
model_name="rest.service.registration",
collection=collection,
components_registry=class_or_instance.comp_registry,
)
return work.component(usage=usage)
class TransactionRestServiceRegistryCase(TransactionCase, RestServiceRegistryCase):
"""Adds Odoo Transaction to inherited from ComponentRegistryCase.
This class doesn't set up the registry for you.
You're supposed to explicitly call `_setup_registry` and `_teardown_registry`
when you need it, either on setUpClass and tearDownClass or setUp and tearDown.
class MyTestCase(TransactionRestServiceRegistryCase):
def setUp(self):
super().setUp()
self._setup_registry(self)
def tearDown(self):
self._teardown_registry(self)
super().tearDown()
class MyTestCase(TransactionRestServiceRegistryCase):
@classmethod
def setUpClass(cls):
super().setUpClass()
cls._setup_registry(cls)
@classmethod
def tearDownClass(cls):
cls._teardown_registry(cls)
super().tearDownClass()
"""
# pylint: disable=W8106
@classmethod
def setUpClass(cls):
# resolve an inheritance issue (common.TransactionCase does not use
# super)
TransactionCase.setUpClass()
cls.base_url = cls.env["ir.config_parameter"].get_param("web.base.url")
@classmethod
def tearDownClass(cls):
TransactionCase.tearDownClass()
class BaseRestCase(TransactionComponentCase, RegistryMixin):
@classmethod
def setUpClass(cls):
super().setUpClass()
cls.setUpRegistry()
cls.base_url = cls.env["ir.config_parameter"].get_param("web.base.url")
cls.registry.enter_test_mode(cls.env.cr)
# pylint: disable=W8110
@classmethod
def tearDownClass(cls):
cls.registry.leave_test_mode()
super().tearDownClass()

View file

@ -0,0 +1,247 @@
# Copyright 2020 ACSONE SA/NV
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl).
import unittest
from cerberus import Validator
from odoo.exceptions import UserError
from odoo.tests.common import BaseCase, MetaCase
from ..components.cerberus_validator import BaseRestCerberusValidator
from ..restapi import CerberusListValidator
class TestCerberusListValidator(BaseCase, MetaCase("DummyCase", (object,), {})):
"""Test all the methods that must be implemented by CerberusListValidator to
be a valid RestMethodParam"""
@classmethod
def setUpClass(cls):
super().setUpClass()
cls.simple_schema = {
"name": {"type": "string", "required": True, "nullable": True},
"title": {
"type": "string",
"nullable": False,
"required": False,
"allowed": ["mr", "mm"],
},
}
cls.nested_schema = {
"name": {"type": "string", "required": True, "empty": False},
"country": {
"type": "dict",
"schema": {
"id": {"type": "integer", "required": True, "nullable": False},
"name": {"type": "string"},
},
},
}
cls.simple_schema_list_validator = CerberusListValidator(
schema=cls.simple_schema, min_items=1, max_items=2, unique_items=True
)
cls.nested_schema_list_validator = CerberusListValidator(
schema=cls.nested_schema
)
cls.maxDiff = None
def test_to_openapi_responses(self):
res = self.simple_schema_list_validator.to_openapi_responses(None, None)
self.assertDictEqual(
res,
{
"200": {
"content": {
"application/json": {
"schema": {
"items": {
"type": "object",
"required": ["name"],
"properties": {
"name": {"nullable": True, "type": "string"},
"title": {
"enum": ["mr", "mm"],
"nullable": False,
"type": "string",
},
},
},
"maxItems": 2,
"minItems": 1,
"type": "array",
"uniqueItems": True,
}
}
}
}
},
)
res = self.nested_schema_list_validator.to_openapi_responses(None, None)
self.assertDictEqual(
res,
{
"200": {
"content": {
"application/json": {
"schema": {
"items": {
"type": "object",
"required": ["name"],
"properties": {
"name": {"type": "string"},
"country": {
"type": "object",
"required": ["id"],
"properties": {
"id": {
"nullable": False,
"type": "integer",
},
"name": {"type": "string"},
},
},
},
},
"type": "array",
}
}
}
}
},
)
def test_to_openapi_requestbody(self):
res = self.simple_schema_list_validator.to_openapi_requestbody(None, None)
self.assertEqual(
res,
{
"content": {
"application/json": {
"schema": {
"items": {
"type": "object",
"required": ["name"],
"properties": {
"name": {"nullable": True, "type": "string"},
"title": {
"enum": ["mr", "mm"],
"nullable": False,
"type": "string",
},
},
},
"maxItems": 2,
"minItems": 1,
"type": "array",
"uniqueItems": True,
}
}
}
},
)
res = self.nested_schema_list_validator.to_openapi_requestbody(None, None)
self.assertDictEqual(
res,
{
"content": {
"application/json": {
"schema": {
"items": {
"type": "object",
"required": ["name"],
"properties": {
"name": {"type": "string"},
"country": {
"type": "object",
"required": ["id"],
"properties": {
"id": {
"nullable": False,
"type": "integer",
},
"name": {"type": "string"},
},
},
},
},
"type": "array",
}
}
}
},
)
def test_to_openapi_query_parameters(self):
with self.assertRaises(NotImplementedError):
self.simple_schema_list_validator.to_openapi_query_parameters(None, None)
def test_from_params_ignore_unknown(self):
params = [{"name": "test", "unknown": True}]
res = self.simple_schema_list_validator.from_params(None, params=params)
self.assertListEqual(res, [{"name": "test"}])
def test_from_params_validation(self):
# minItems / maxItems
with self.assertRaises(UserError):
# minItems = 1
self.simple_schema_list_validator.from_params(None, params=[])
with self.assertRaises(UserError):
# maxItems = 2
self.simple_schema_list_validator.from_params(
None, params=[{"name": "test"}, {"name": "test"}, {"name": "test"}]
)
with self.assertRaises(UserError):
# name required
self.simple_schema_list_validator.from_params(None, params=[{}])
def test_to_response_ignore_unknown(self):
result = [{"name": "test", "unknown": True}]
res = self.simple_schema_list_validator.to_response(None, result=result)
self.assertListEqual(res, [{"name": "test"}])
def test_to_response_validation(self):
# If a response is not conform to the expected schema it's considered
# as a programmatic error not a user error
with self.assertRaises(SystemError):
# minItems = 1
self.simple_schema_list_validator.to_response(None, result=[])
with self.assertRaises(SystemError):
# maxItems = 2
self.simple_schema_list_validator.to_response(
None, result=[{"name": "test"}, {"name": "test"}, {"name": "test"}]
)
with self.assertRaises(SystemError):
# name required
self.simple_schema_list_validator.to_response(None, result=[{}])
def test_schema_lookup_from_string(self):
class MyService(object):
def _get_simple_schema(self):
return {"name": {"type": "string", "required": True, "nullable": True}}
def component(self, *args, **kwargs):
return BaseRestCerberusValidator(unittest.mock.Mock())
v = CerberusListValidator(schema="_get_simple_schema")
validator = v.get_cerberus_validator(MyService(), "output")
self.assertTrue(validator)
self.assertDictEqual(
validator.root_schema.schema,
{"name": {"type": "string", "required": True, "nullable": True}},
)
def test_schema_lookup_from_string_custom_validator(self):
class MyService(object):
def _get_simple_schema(self):
return Validator(
{"name": {"type": "string", "required": False}}, require_all=True
)
def component(self, *args, **kwargs):
return BaseRestCerberusValidator(unittest.mock.Mock())
v = CerberusListValidator(schema="_get_simple_schema")
validator = v.get_cerberus_validator(MyService(), "input")
self.assertTrue(validator.require_all)

View file

@ -0,0 +1,449 @@
# Copyright 2020 ACSONE SA/NV
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl).
import unittest
from cerberus import Validator
from odoo.exceptions import UserError
from odoo.tests.common import BaseCase, MetaCase
from ..components.cerberus_validator import BaseRestCerberusValidator
from ..restapi import CerberusValidator
from ..tools import cerberus_to_json
class TestCerberusValidator(BaseCase, MetaCase("DummyCase", (object,), {})):
"""Test all the methods that must be implemented by CerberusValidator to
be a valid RestMethodParam"""
@classmethod
def setUpClass(cls):
super().setUpClass()
cls.simple_schema = {
"name": {"type": "string", "required": True, "nullable": True},
"title": {
"type": "string",
"nullable": False,
"required": False,
"allowed": ["mr", "mm"],
},
"age": {"type": "integer", "default": 18},
"interests": {"type": "list", "schema": {"type": "string"}},
}
cls.nested_schema = {
"name": {"type": "string", "required": True, "empty": False},
"country": {
"type": "dict",
"schema": {
"id": {"type": "integer", "required": True, "nullable": False},
"name": {"type": "string"},
},
},
"is_company": {"type": "boolean"},
}
cls.simple_schema_cerberus_validator = CerberusValidator(
schema=cls.simple_schema
)
cls.nested_schema_cerberus_validator = CerberusValidator(
schema=cls.nested_schema
)
def test_to_openapi_responses(self):
res = self.simple_schema_cerberus_validator.to_openapi_responses(None, None)
self.assertDictEqual(
res,
{
"200": {
"content": {
"application/json": {
"schema": {
"type": "object",
"required": ["name"],
"properties": {
"name": {"nullable": True, "type": "string"},
"title": {
"enum": ["mr", "mm"],
"nullable": False,
"type": "string",
},
"age": {"default": 18, "type": "integer"},
"interests": {
"type": "array",
"items": {"type": "string"},
},
},
}
}
}
}
},
)
res = self.nested_schema_cerberus_validator.to_openapi_responses(None, None)
self.assertDictEqual(
res,
{
"200": {
"content": {
"application/json": {
"schema": {
"type": "object",
"required": ["name"],
"properties": {
"name": {"type": "string"},
"country": {
"type": "object",
"required": ["id"],
"properties": {
"id": {
"nullable": False,
"type": "integer",
},
"name": {"type": "string"},
},
},
"is_company": {"type": "boolean"},
},
}
}
}
}
},
)
def test_to_openapi_requestbody(self):
res = self.simple_schema_cerberus_validator.to_openapi_requestbody(None, None)
self.assertEqual(
res,
{
"content": {
"application/json": {
"schema": {
"type": "object",
"required": ["name"],
"properties": {
"name": {"nullable": True, "type": "string"},
"title": {
"enum": ["mr", "mm"],
"nullable": False,
"type": "string",
},
"age": {"default": 18, "type": "integer"},
"interests": {
"type": "array",
"items": {"type": "string"},
},
},
}
}
}
},
)
res = self.nested_schema_cerberus_validator.to_openapi_requestbody(None, None)
self.assertDictEqual(
res,
{
"content": {
"application/json": {
"schema": {
"type": "object",
"required": ["name"],
"properties": {
"name": {"type": "string"},
"country": {
"type": "object",
"required": ["id"],
"properties": {
"id": {"nullable": False, "type": "integer"},
"name": {"type": "string"},
},
},
"is_company": {"type": "boolean"},
},
}
}
}
},
)
def test_to_openapi_query_parameters(self):
res = self.simple_schema_cerberus_validator.to_openapi_query_parameters(
None, None
)
self.assertListEqual(
res,
[
{
"name": "name",
"in": "query",
"required": True,
"allowEmptyValue": True,
"default": None,
"schema": {"type": "string"},
},
{
"name": "title",
"in": "query",
"required": False,
"allowEmptyValue": False,
"default": None,
"schema": {"type": "string", "enum": ["mr", "mm"]},
},
{
"name": "age",
"in": "query",
"required": False,
"allowEmptyValue": False,
"default": 18,
"schema": {"type": "integer"},
},
{
"name": "interests[]",
"in": "query",
"required": False,
"allowEmptyValue": False,
"default": None,
"schema": {"type": "array", "items": {"type": "string"}},
},
],
)
res = self.nested_schema_cerberus_validator.to_openapi_query_parameters(
None, None
)
self.assertListEqual(
res,
[
{
"name": "name",
"in": "query",
"required": True,
"allowEmptyValue": False,
"default": None,
"schema": {"type": "string"},
},
{
"name": "country",
"in": "query",
"required": False,
"allowEmptyValue": False,
"default": None,
"schema": {"type": "object"},
},
{
"name": "is_company",
"in": "query",
"required": False,
"allowEmptyValue": False,
"default": None,
"schema": {"type": "boolean"},
},
],
)
def test_from_params_add_default(self):
params = {"name": "test"}
res = self.simple_schema_cerberus_validator.from_params(None, params=params)
self.assertDictEqual(res, {"name": "test", "age": 18})
def test_from_params_ignore_unknown(self):
params = {"name": "test", "unknown": True}
res = self.simple_schema_cerberus_validator.from_params(None, params=params)
self.assertDictEqual(res, {"name": "test", "age": 18})
def test_from_params_validation(self):
# name is required
with self.assertRaises(UserError):
self.simple_schema_cerberus_validator.from_params(None, params={})
def test_to_response_add_default(self):
result = {"name": "test"}
res = self.simple_schema_cerberus_validator.to_response(None, result=result)
self.assertDictEqual(res, {"name": "test", "age": 18})
def test_to_response_ignore_unknown(self):
result = {"name": "test", "unknown": True}
res = self.simple_schema_cerberus_validator.to_response(None, result=result)
self.assertDictEqual(res, {"name": "test", "age": 18})
def test_to_response_validation(self):
# name is required
# If a response is not conform to the expected schema it's considered
# as a programmatic error not a user error
with self.assertRaises(SystemError):
self.simple_schema_cerberus_validator.to_response(None, result={})
def test_schema_lookup_from_string(self):
class MyService(object):
def _get_simple_schema(self):
return {"name": {"type": "string", "required": True, "nullable": True}}
def component(self, *args, **kwargs):
return BaseRestCerberusValidator(unittest.mock.Mock())
v = CerberusValidator(schema="_get_simple_schema")
validator = v.get_cerberus_validator(MyService(), "output")
self.assertTrue(validator)
self.assertDictEqual(
validator.root_schema.schema,
{"name": {"type": "string", "required": True, "nullable": True}},
)
def test_schema_lookup_from_string_custom_validator(self):
class MyService(object):
def _get_simple_schema(self):
return Validator(
{"name": {"type": "string", "required": False}}, require_all=True
)
def component(self, *args, **kwargs):
return BaseRestCerberusValidator(unittest.mock.Mock())
v = CerberusValidator(schema="_get_simple_schema")
validator = v.get_cerberus_validator(MyService(), "input")
self.assertTrue(validator.require_all)
def test_custom_validator_handler(self):
assertEq = self.assertEqual
class CustomBaseRestCerberusValidator(BaseRestCerberusValidator):
def get_validator_handler(self, service, method_name, direction):
# In your implementation, this is where you can handle how the
# validator is retrieved / computed (dispatch to dedicated
# components...).
assertEq(service, my_service)
assertEq(method_name, "my_endpoint")
assertEq(direction, "input")
# A callable with no parameter is expected.
return lambda: Validator(
{"name": {"type": "string", "required": False}}, require_all=True
)
def has_validator_handler(self, service, method_name, direction):
return True
class MyService(object):
def component(self, *args, **kwargs):
return CustomBaseRestCerberusValidator(unittest.mock.Mock())
my_service = MyService()
v = CerberusValidator(schema="my_endpoint")
validator = v.get_cerberus_validator(my_service, "input")
self.assertTrue(validator.require_all)
def test_cerberus_key_value_mapping_to_openapi(self):
schema = {
"indexes": {
"type": "dict",
"required": True,
"nullable": True,
"keysrules": {"type": "string"},
"valuesrules": {"type": "string"},
}
}
openapi = cerberus_to_json(schema)
self.assertDictEqual(
openapi,
{
"type": "object",
"required": ["indexes"],
"properties": {
"indexes": {
"nullable": True,
"type": "object",
"additionalProperties": {"type": "string"},
}
},
},
)
schema = {
"indexes": {
"type": "dict",
"required": True,
"nullable": True,
"keysrules": {"type": "string"},
"valuesrules": {
"type": "dict",
"schema": {
"id": {
"type": "integer",
"required": True,
"nullable": False,
},
"name": {"type": "string"},
},
},
}
}
openapi = cerberus_to_json(schema)
self.assertDictEqual(
openapi,
{
"type": "object",
"required": ["indexes"],
"properties": {
"indexes": {
"nullable": True,
"type": "object",
"additionalProperties": {
"type": "object",
"required": ["id"],
"properties": {
"id": {
"nullable": False,
"type": "integer",
},
"name": {"type": "string"},
},
},
}
},
},
)
def test_cerberus_meta_to_openapi(self):
schema = {
"indexes": {
"type": "dict",
"meta": {
"description": "A key/value mapping where Key is the model "
"name used to fill the index and value is the "
"index name",
"example": {
"shopinvader.category": "demo_elasticsearch_backend_"
"shopinvader_category_en_US",
"shopinvader.variant": "demo_elasticsearch_backend_"
"shopinvader_variant_en_US",
},
},
"required": True,
"nullable": True,
"keysrules": {"type": "string"},
"valuesrules": {"type": "string"},
}
}
openapi = cerberus_to_json(schema)
self.assertDictEqual(
openapi,
{
"type": "object",
"required": ["indexes"],
"properties": {
"indexes": {
"description": "A key/value mapping where Key is the model "
"name used to fill the index and value is "
"the index name",
"example": {
"shopinvader.category": "demo_elasticsearch_backend_"
"shopinvader_category_en_US",
"shopinvader.variant": "demo_elasticsearch_backend_"
"shopinvader_variant_en_US",
},
"nullable": True,
"type": "object",
"additionalProperties": {"type": "string"},
}
},
},
)

View file

@ -0,0 +1,671 @@
# Copyright 2020 ACSONE SA/NV
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl).
from contextlib import contextmanager
from odoo.addons.component.core import Component
from .. import restapi
from ..tools import ROUTING_DECORATOR_ATTR
from .common import TransactionRestServiceRegistryCase
class TestControllerBuilder(TransactionRestServiceRegistryCase):
"""Test Odoo controller builder
In this class we test the generation of odoo controllers from the services
component
"""
def setUp(self):
super().setUp()
self._setup_registry(self)
def tearDown(self):
self._teardown_registry(self)
super().tearDown()
def test_01(self):
"""Test controller generated for old API services
In this test we check that the controller generated for services with
methods not decorated with the restapi.method decorator contains
the required method to route requests to services. In the original
implementation, these routes where hardcoded into the base controller.
"""
# pylint: disable=R7980
class TestServiceOldApi(Component):
_inherit = "base.rest.service"
_name = "test.ping.service"
_usage = "ping"
_collection = self._collection_name
_description = "test"
def get(self, _id, message):
pass
def search(self, message):
return {"response": "Search called search with message " + message}
def update(self, _id, message):
return {"response": "PUT called with message " + message}
# pylint:disable=method-required-super
def create(self, **params):
return {"response": "POST called with message " + params["message"]}
def delete(self, _id):
return {"response": "DELETE called with id %s " % _id}
def my_method(self, **params):
pass
def my_instance_method(self, _id, **params):
pass
# Validator
def _validator_search(self):
return {"message": {"type": "string"}}
# Validator
def _validator_get(self):
# no parameters by default
return {}
def _validator_update(self):
return {"message": {"type": "string"}}
def _validator_create(self):
return {"message": {"type": "string"}}
def _validator_my_method(self):
return {"message": {"type": "string"}}
def _validator_my_instance_method(self):
return {"message": {"type": "string"}}
self.assertFalse(self._get_controller_for(TestServiceOldApi))
self._build_services(self, TestServiceOldApi)
controller = self._get_controller_for(TestServiceOldApi)
routes = self._get_controller_route_methods(controller)
self.assertSetEqual(
set(routes.keys()),
{
"get_get",
"get_search",
"post_update",
"put_update",
"post_create",
"post_delete",
"delete_delete",
"post_my_method",
"post_my_instance_method",
}
| self._controller_route_method_names,
)
self.assertTrue(controller)
# the generated method_name is always the {http_method}_{method_name}
method = routes["get_get"]
self.assertDictEqual(
getattr(method, ROUTING_DECORATOR_ATTR),
{
"methods": ["GET"],
"auth": "public",
"cors": None,
"csrf": False,
"routes": [
"/test_controller/ping/<int:id>/get",
"/test_controller/ping/<int:id>",
],
"save_session": True,
"type": "restapi",
},
)
method = routes["get_search"]
self.assertDictEqual(
getattr(method, ROUTING_DECORATOR_ATTR),
{
"methods": ["GET"],
"auth": "public",
"cors": None,
"csrf": False,
"routes": ["/test_controller/ping/search", "/test_controller/ping/"],
"save_session": True,
"type": "restapi",
},
)
method = routes["post_update"]
self.assertDictEqual(
getattr(method, ROUTING_DECORATOR_ATTR),
{
"methods": ["POST"],
"auth": "public",
"cors": None,
"csrf": False,
"routes": [
"/test_controller/ping/<int:id>/update",
"/test_controller/ping/<int:id>",
],
"save_session": True,
"type": "restapi",
},
)
method = routes["put_update"]
self.assertDictEqual(
getattr(method, ROUTING_DECORATOR_ATTR),
{
"methods": ["PUT"],
"auth": "public",
"cors": None,
"csrf": False,
"routes": ["/test_controller/ping/<int:id>"],
"save_session": True,
"type": "restapi",
},
)
method = routes["post_create"]
self.assertDictEqual(
getattr(method, ROUTING_DECORATOR_ATTR),
{
"methods": ["POST"],
"auth": "public",
"cors": None,
"csrf": False,
"routes": ["/test_controller/ping/create", "/test_controller/ping/"],
"save_session": True,
"type": "restapi",
},
)
method = routes["post_delete"]
self.assertDictEqual(
getattr(method, ROUTING_DECORATOR_ATTR),
{
"methods": ["POST"],
"auth": "public",
"cors": None,
"csrf": False,
"routes": ["/test_controller/ping/<int:id>/delete"],
"save_session": True,
"type": "restapi",
},
)
method = routes["delete_delete"]
self.assertDictEqual(
getattr(method, ROUTING_DECORATOR_ATTR),
{
"methods": ["DELETE"],
"auth": "public",
"cors": None,
"csrf": False,
"routes": ["/test_controller/ping/<int:id>"],
"save_session": True,
"type": "restapi",
},
)
method = routes["post_my_method"]
self.assertDictEqual(
getattr(method, ROUTING_DECORATOR_ATTR),
{
"methods": ["POST"],
"auth": "public",
"cors": None,
"csrf": False,
"routes": ["/test_controller/ping/my_method"],
"save_session": True,
"type": "restapi",
},
)
method = routes["post_my_instance_method"]
self.assertDictEqual(
getattr(method, ROUTING_DECORATOR_ATTR),
{
"methods": ["POST"],
"auth": "public",
"cors": None,
"csrf": False,
"routes": ["/test_controller/ping/<int:id>/my_instance_method"],
"save_session": True,
"type": "restapi",
},
)
def test_02(self):
"""Test controller generated from services with new API methods
In this case we check that the generated controller for a service
where the methods are decorated with restapi.method contains the
required method to route the requests to the methods
"""
# pylint: disable=R7980
class TestServiceNewApi(Component):
_inherit = "base.rest.service"
_name = "test.partner.service"
_usage = "partner"
_collection = self._collection_name
_description = "test"
@restapi.method(
[(["/<int:id>/get", "/<int:id>"], "GET")],
output_param=restapi.CerberusValidator("_get_partner_schema"),
auth="public",
)
def get(self, _id):
return {"name": self.env["res.partner"].browse(_id).name}
@restapi.method(
[(["/<int:id>/get_name"], "GET")],
output_param=restapi.CerberusValidator("_get_partner_schema"),
auth="public",
)
def get_name(self, _id):
return {"name": self.env["res.partner"].browse(_id).name}
@restapi.method(
[(["/<int:id>/change_name"], "POST")],
input_param=restapi.CerberusValidator("_get_partner_schema"),
auth="user",
)
def update_name(self, _id, **params):
pass
def _get_partner_schema(self):
return {"name": {"type": "string", "required": True}}
self.assertFalse(self._get_controller_for(TestServiceNewApi))
self._build_services(self, TestServiceNewApi)
controller = self._get_controller_for(TestServiceNewApi)
routes = self._get_controller_route_methods(controller)
self.assertSetEqual(
set(routes.keys()),
{"get_get", "get_get_name", "post_update_name"}
| self._controller_route_method_names,
)
method = routes["get_get"]
self.assertDictEqual(
getattr(method, ROUTING_DECORATOR_ATTR),
{
"methods": ["GET"],
"auth": "public",
"cors": None,
"csrf": False,
"routes": [
"/test_controller/partner/<int:id>/get",
"/test_controller/partner/<int:id>",
],
"save_session": True,
"type": "restapi",
},
)
method = routes["get_get_name"]
self.assertDictEqual(
getattr(method, ROUTING_DECORATOR_ATTR),
{
"methods": ["GET"],
"auth": "public",
"cors": None,
"csrf": False,
"routes": ["/test_controller/partner/<int:id>/get_name"],
"save_session": True,
"type": "restapi",
},
)
method = routes["post_update_name"]
self.assertDictEqual(
getattr(method, ROUTING_DECORATOR_ATTR),
{
"methods": ["POST"],
"auth": "user",
"cors": None,
"csrf": False,
"routes": ["/test_controller/partner/<int:id>/change_name"],
"save_session": True,
"type": "restapi",
},
)
def test_03(self):
"""Check that the controller builder takes care of services inheritance"""
# pylint: disable=R7980
class TestPartnerService(Component):
_inherit = "base.rest.service"
_name = "test.partner.service"
_usage = "partner"
_collection = self._collection_name
_description = "test"
@restapi.method(
[(["/<int:id>/get", "/<int:id>"], "GET")],
output_param=restapi.CerberusValidator("_get_partner_schema"),
auth="public",
)
def get(self, _id):
return {"name": self.env["res.partner"].browse(_id).name}
def _get_partner_schema(self):
return {"name": {"type": "string", "required": True}}
class TestInheritPartnerService(Component):
_inherit = "test.partner.service"
@restapi.method(
[(["/<int:id>/get_name"], "GET")],
output_param=restapi.CerberusValidator("_get_partner_schema"),
auth="public",
)
def get_name(self, _id):
return {"name": self.env["res.partner"].browse(_id).name}
@restapi.method(
[(["/<int:id>/change_name"], "POST")],
input_param=restapi.CerberusValidator("_get_partner_schema"),
auth="user",
)
def update_name(self, _id, **params):
pass
self.assertFalse(self._get_controller_for(TestPartnerService))
self._build_services(self, TestPartnerService, TestInheritPartnerService)
controller = self._get_controller_for(TestPartnerService)
routes = self._get_controller_route_methods(controller)
self.assertSetEqual(
set(routes.keys()),
{"get_get", "get_get_name", "post_update_name"}
| self._controller_route_method_names,
)
method = routes["get_get"]
self.assertDictEqual(
getattr(method, ROUTING_DECORATOR_ATTR),
{
"methods": ["GET"],
"auth": "public",
"cors": None,
"csrf": False,
"routes": [
"/test_controller/partner/<int:id>/get",
"/test_controller/partner/<int:id>",
],
"save_session": True,
"type": "restapi",
},
)
method = routes["get_get_name"]
self.assertDictEqual(
getattr(method, ROUTING_DECORATOR_ATTR),
{
"methods": ["GET"],
"auth": "public",
"cors": None,
"csrf": False,
"routes": ["/test_controller/partner/<int:id>/get_name"],
"save_session": True,
"type": "restapi",
},
)
method = routes["post_update_name"]
self.assertDictEqual(
getattr(method, ROUTING_DECORATOR_ATTR),
{
"methods": ["POST"],
"auth": "user",
"cors": None,
"csrf": False,
"routes": ["/test_controller/partner/<int:id>/change_name"],
"save_session": True,
"type": "restapi",
},
)
class TestControllerBuilder2(TransactionRestServiceRegistryCase):
"""Test Odoo controller builder
In this class we test the generation of odoo controllers from the services
component
The test requires a fresh base controller
"""
def setUp(self):
super().setUp()
self._setup_registry(self)
def tearDown(self):
self._teardown_registry(self)
super().tearDown()
def test_04(self):
"""Test controller generated from services with new API methods and
old api takes into account the _default_auth
Routes directly defined on the RestConroller without auth should also
use the _default_auth
"""
default_auth = "my_default_auth"
default_cors = "*"
default_csrf = True
default_save_session = True
self._BaseTestController._default_auth = default_auth
self._BaseTestController._default_cors = default_cors
self._BaseTestController._default_csrf = default_csrf
self._BaseTestController._default_save_session = default_save_session
# pylint: disable=R7980
class TestService(Component):
_inherit = "base.rest.service"
_name = "test.partner.service"
_usage = "partner"
_collection = self._collection_name
_description = "test"
@restapi.method(
[(["/new_api_method_with"], "GET")],
auth="public",
cors="http://my_site",
csrf=not default_csrf,
save_session=not default_save_session,
)
def new_api_method_with(self, _id):
return {"name": self.env["res.partner"].browse(_id).name}
@restapi.method([(["/new_api_method_without"], "GET")])
def new_api_method_without(self, _id):
return {"name": self.env["res.partner"].browse(_id).name}
# OLD API method
def get(self, _id, message):
pass
# Validator
def _validator_get(self):
# no parameters by default
return {}
self._build_services(self, TestService)
controller = self._get_controller_for(TestService)
routes = self._get_controller_route_methods(controller)
for attr, default in [
("auth", default_auth),
("cors", default_cors),
("csrf", default_csrf),
("save_session", default_save_session),
]:
self.assertEqual(
getattr(routes["get_new_api_method_without"], ROUTING_DECORATOR_ATTR)[
attr
],
default,
"wrong %s" % attr,
)
self.assertEqual(
getattr(routes["get_new_api_method_with"], ROUTING_DECORATOR_ATTR)["auth"],
"public",
)
self.assertEqual(
getattr(routes["get_new_api_method_with"], ROUTING_DECORATOR_ATTR)["cors"],
"http://my_site",
)
self.assertEqual(
getattr(routes["get_new_api_method_with"], ROUTING_DECORATOR_ATTR)["csrf"],
not default_csrf,
)
self.assertEqual(
getattr(routes["get_new_api_method_with"], ROUTING_DECORATOR_ATTR)[
"save_session"
],
not default_save_session,
)
self.assertEqual(
getattr(routes["get_get"], ROUTING_DECORATOR_ATTR)["auth"],
default_auth,
"wrong auth for get_get",
)
for attr, default in [
("auth", default_auth),
("cors", default_cors),
("csrf", default_csrf),
("save_session", default_save_session),
]:
self.assertEqual(
getattr(routes["my_controller_route_without"], ROUTING_DECORATOR_ATTR)[
attr
],
default,
"wrong %s" % attr,
)
routing = getattr(routes["my_controller_route_with"], ROUTING_DECORATOR_ATTR)
for attr, value in [
("auth", "public"),
("cors", "http://with_cors"),
("csrf", "False"),
("save_session", "False"),
]:
self.assertEqual(
routing[attr],
value,
"wrong %s" % attr,
)
self.assertEqual(
getattr(
routes["my_controller_route_without_auth_2"], ROUTING_DECORATOR_ATTR
)["auth"],
None,
"wrong auth for my_controller_route_without_auth_2",
)
def test_05(self):
"""Test auth="public_or_default" on restapi.method
The auth method on the route should be public_or_my_default_auth
since the ir.http model provides the _auth_method_public_or_my_default_auth methods
"""
default_auth = "my_default_auth"
self._BaseTestController._default_auth = default_auth
# pylint: disable=R7980
class TestService(Component):
_inherit = "base.rest.service"
_name = "test.partner.service"
_usage = "partner"
_collection = self._collection_name
_description = "test"
@restapi.method(
[(["/new_api_method_with_public_or"], "GET")], auth="public_or_default"
)
def new_api_method_with_public_or(self, _id):
return {"name": self.env["res.partner"].browse(_id).name}
# Validator
def _validator_get(self):
# no parameters by default
return {}
# delare the auth méthod on ir.http
with _add_method(
self.env["ir.http"],
"_auth_method_public_or_my_default_auth",
lambda a: True,
):
self._build_services(self, TestService)
controller = self._get_controller_for(TestService)
routes = self._get_controller_route_methods(controller)
self.assertEqual(
getattr(
routes["get_new_api_method_with_public_or"], ROUTING_DECORATOR_ATTR
)["auth"],
"public_or_my_default_auth",
)
def test_06(self):
"""Test auth="public_or_default" on restapi.method
The auth method on the route should be the default_auth configurerd on the controller
since the ir.http model doesn't provides the _auth_method_public_or_my_default_auth
methods
"""
default_auth = "my_default_auth"
self._BaseTestController._default_auth = default_auth
# pylint: disable=R7980
class TestService(Component):
_inherit = "base.rest.service"
_name = "test.partner.service"
_usage = "partner"
_collection = self._collection_name
_description = "test"
@restapi.method(
[(["/new_api_method_with_public_or"], "GET")], auth="public_or_default"
)
def new_api_method_with_public_or(self, _id):
return {"name": self.env["res.partner"].browse(_id).name}
# Validator
def _validator_get(self):
# no parameters by default
return {}
self._build_services(self, TestService)
controller = self._get_controller_for(TestService)
routes = self._get_controller_route_methods(controller)
self.assertEqual(
getattr(
routes["get_new_api_method_with_public_or"], ROUTING_DECORATOR_ATTR
)["auth"],
"my_default_auth",
)
@contextmanager
def _add_method(obj, name, method):
try:
setattr(obj.__class__, name, method)
yield
finally:
delattr(obj.__class__, name)

View file

@ -0,0 +1,338 @@
# Copyright 2020 ACSONE SA/NV
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl).
from odoo.addons.component.core import Component
from .. import restapi
from .common import TransactionRestServiceRegistryCase
class TestOpenAPIGenerator(TransactionRestServiceRegistryCase):
"""Test openapi document generation from REST services"""
def setUp(self):
super().setUp()
self._setup_registry(self)
def tearDown(self):
self._teardown_registry(self)
super().tearDown()
def test_01(self):
"""Simple test case"""
# pylint: disable=R7980
class PartnerService(Component):
_inherit = "base.rest.service"
_name = "test.partner.service"
_usage = "partner"
_collection = self._collection_name
_description = "Sercice description"
@restapi.method(
[(["/<int:id>/get", "/<int:id>"], "GET")],
output_param=restapi.CerberusValidator("_get_partner_schema"),
auth="public",
)
def get(self, _id):
"""Get the partner information"""
def _get_partner_schema(self):
return {"name": {"type": "string", "required": True}}
self._build_services(self, PartnerService)
service = self._get_service_component(self, "partner")
openapi = service.to_openapi()
self.assertTrue(openapi)
# The service url is available at base_url/controller._root_path/_usage
url = openapi["servers"][0]["url"]
self.assertEqual(self.base_url + "/test_controller/partner", url)
# The title is generated from the service usage
# The service info must contains a title and a description
info = openapi["info"]
self.assertEqual(info["title"], "%s REST services" % PartnerService._usage)
self.assertEqual(info["description"], PartnerService._description)
paths = openapi["paths"]
# The paths must contains 2 entries (1 by routes)
self.assertSetEqual({"/{id}/get", "/{id}"}, set(openapi["paths"]))
for p in ["/{id}/get", "/{id}"]:
path = paths[p]
# The method for the paths is get
self.assertIn("get", path)
# The summary is the method docstring
get = path["get"]
self.assertEqual(get["summary"], "Get the partner information")
# The reponse for status 200 is the openapi schema generated from
# the cerberus schema returned by the _get_partner_schema method
resp = None
for item in get["responses"].items():
if item[0] == "200":
resp = item[1]
self.assertTrue(resp)
self.assertDictEqual(
resp,
{
"content": {
"application/json": {
"schema": {
"properties": {"name": {"type": "string"}},
"required": ["name"],
"type": "object",
}
}
}
},
)
# The path contains parameters
self.assertDictEqual(
path.get("parameters", [{}])[0],
{
"in": "path",
"name": "id",
"required": True,
"schema": {"type": "integer", "format": "int32"},
},
)
def test_02(self):
"""Test path parameters
The new api allows you to define paths parameters. In this test
we check that these parameters are into the openapi specification
"""
# pylint: disable=R7980
class PartnerService(Component):
_inherit = "base.rest.service"
_name = "test.partner.service"
_usage = "partner"
_collection = self._collection_name
_description = "Sercice description"
@restapi.method(
[(["/<int:id>/update_name/<string:name>"], "POST")], auth="public"
)
def update_name(self, _id, _name):
"""update_name"""
self._build_services(self, PartnerService)
service = self._get_service_component(self, "partner")
openapi = service.to_openapi()
self.assertTrue(openapi)
paths = openapi["paths"]
self.assertIn("/{id}/update_name/{name}", paths)
path = paths["/{id}/update_name/{name}"]
self.assertIn("post", path)
parameters = path["parameters"]
self.assertEqual(2, len(parameters))
name_param = {}
id_param = {}
for p in parameters:
if p["name"] == "id":
id_param = p
else:
name_param = p
self.assertDictEqual(
name_param,
{
"in": "path",
"name": "name",
"required": True,
"schema": {"type": "string"},
},
)
self.assertDictEqual(
id_param,
{
"in": "path",
"name": "id",
"required": True,
"schema": {"type": "integer", "format": "int32"},
},
)
# pylint: disable=W8110
def test_03(self):
"""Test default parameters and default responses
You can define default parameters and responses at service level.
In this test we check that these parameters and responses are into the
openapi specification
"""
default_params = [
{
"name": "API-KEY",
"in": "header",
"description": "API key for Authorization",
"required": True,
"schema": {"type": "string"},
"style": "simple",
}
]
# pylint: disable=R7980
class PartnerService(Component):
_inherit = "base.rest.service"
_name = "test.partner.service"
_usage = "partner"
_collection = self._collection_name
_description = "Sercice description"
@restapi.method(
[(["/<int:id>/update_name/<string:name>"], "POST")], auth="public"
)
def update_name(self, _id, _name):
"""update_name"""
def _get_openapi_default_parameters(self):
defaults = super()._get_openapi_default_parameters()
defaults.extend(default_params)
return defaults
def _get_openapi_default_responses(self):
responses = super()._get_openapi_default_responses().copy()
responses["999"] = "TEST"
return responses
self._build_services(self, PartnerService)
service = self._get_service_component(self, "partner")
openapi = service.to_openapi()
paths = openapi["paths"]
self.assertIn("/{id}/update_name/{name}", paths)
path = paths["/{id}/update_name/{name}"]
self.assertIn("post", path)
parameters = path["post"].get("parameters", [])
self.assertListEqual(parameters, default_params)
responses = path["post"].get("responses", [])
self.assertIn("999", responses)
def test_04(self):
"""Binary and Multipart form-data test case"""
# pylint: disable=R7980
class AttachmentService(Component):
_inherit = "base.rest.service"
_name = "test.attachment.service"
_usage = "attachment"
_collection = self._collection_name
_description = "Sercice description"
@restapi.method(
routes=[(["/<int:id>/download"], "GET")],
output_param=restapi.BinaryData(required=True),
)
def download(self, _id):
"""download the attachment"""
@restapi.method(
routes=[(["/create"], "POST")],
input_param=restapi.MultipartFormData(
{
"file": restapi.BinaryData(
mediatypes=["image/png", "image/jpeg"]
),
"params": restapi.CerberusValidator("_get_attachment_schema"),
}
),
output_param=restapi.CerberusValidator("_get_attachment_schema"),
)
# pylint: disable=W8106
def create(self, file, params):
"""create the attachment"""
def _get_attachment_schema(self):
return {"name": {"type": "string", "required": True}}
self._build_services(self, AttachmentService)
service = self._get_service_component(self, "attachment")
openapi = service.to_openapi()
paths = openapi["paths"]
# The paths must contains 2 entries (1 by routes)
self.assertSetEqual({"/{id}/download", "/create"}, set(openapi["paths"]))
path_download = paths["/{id}/download"]
resp_download = None
for item in path_download["get"]["responses"].items():
if item[0] == "200":
resp_download = item[1]
self.assertTrue(resp_download)
self.assertDictEqual(
resp_download,
{
"content": {
"*/*": {
"schema": {
"type": "string",
"format": "binary",
"required": True,
}
}
}
},
)
# The path contains parameters
self.assertDictEqual(
path_download.get("parameters", [{}])[0],
{
"in": "path",
"name": "id",
"required": True,
"schema": {"type": "integer", "format": "int32"},
},
)
path_create = paths["/create"]
resp_create = None
for item in path_create["post"]["responses"].items():
if item[0] == "200":
resp_create = item[1]
self.assertTrue(resp_create)
self.assertDictEqual(
resp_create,
{
"content": {
"application/json": {
"schema": {
"properties": {"name": {"type": "string"}},
"required": ["name"],
"type": "object",
}
}
}
},
)
request_create = path_create["post"]["requestBody"]
self.assertDictEqual(
request_create,
{
"content": {
"multipart/form-data": {
"schema": {
"type": "object",
"properties": {
"file": {
"type": "string",
"format": "binary",
"required": False,
},
"params": {
"type": "object",
"properties": {
"name": {
"type": "string",
},
},
"required": ["name"],
},
},
"encoding": {
"file": {"contentType": "image/png, image/jpeg"}
},
}
}
}
},
)

View file

@ -0,0 +1,150 @@
# Copyright 2021 ACSONE SA/NV
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl).
from odoo.addons.component.core import Component
from odoo.addons.website.tools import MockRequest
from .. import restapi
from .common import BaseRestCase, TransactionRestServiceRegistryCase
class TestServiceContextProvider(TransactionRestServiceRegistryCase):
"""Test Odoo service context provider
In this class we test the context provided by the service context provider
"""
def setUp(self):
super().setUp()
self._setup_registry(self)
def tearDown(self):
self._teardown_registry(self)
super().tearDown()
def test_01(self):
"""Test authenticated_partner_id
In this case we check that the default service context provider provides
no authenticated_partner_id
"""
# pylint: disable=R7980
class TestServiceNewApi(Component):
_inherit = "base.rest.service"
_name = "test.partner.service"
_usage = "partner"
_collection = self._collection_name
_description = "test"
@restapi.method(
[(["/<int:id>/get", "/<int:id>"], "GET")],
output_param=restapi.CerberusValidator("_get_partner_schema"),
auth="public",
)
def get(self, _id):
return {"name": self.env["res.partner"].browse(_id).name}
self._build_services(self, TestServiceNewApi)
controller = self._get_controller_for(TestServiceNewApi)
with MockRequest(self.env), controller().service_component(
"partner"
) as service:
self.assertFalse(service.work.authenticated_partner_id)
def test_02(self):
"""Test authenticated_partner_id
In this case we check that the 'abstract.user.authenticated.partner.provider'
service context provider provides the current user's partner as
authenticated_partner_id
"""
# pylint: disable=R7880
class TestComponentContextprovider(Component):
_name = "test.component.context.provider"
_inherit = [
"abstract.user.authenticated.partner.provider",
"base.rest.service.context.provider",
]
_usage = "test_component_context_provider"
self._BaseTestController._component_context_provider = (
"test_component_context_provider"
)
# pylint: disable=R7980
class TestServiceNewApi(Component):
_inherit = "base.rest.service"
_name = "test.partner.service"
_usage = "partner"
_collection = self._collection_name
_description = "test"
@restapi.method(
[(["/<int:id>/get", "/<int:id>"], "GET")],
output_param=restapi.CerberusValidator("_get_partner_schema"),
auth="public",
)
def get(self, _id):
return {"name": self.env["res.partner"].browse(_id).name}
self._build_components(TestComponentContextprovider)
self._build_services(self, TestServiceNewApi)
controller = self._get_controller_for(TestServiceNewApi)
with MockRequest(self.env), controller().service_component(
"partner"
) as service:
self.assertEqual(
service.work.authenticated_partner_id, self.env.user.partner_id.id
)
def test_03(self):
"""Test authenticated_partner_id
In this case we check that redefining the method _get_authenticated_partner_id
changes the authenticated_partner_id provided by the service context provider
"""
# pylint: disable=R7880
class TestComponentContextprovider(Component):
_name = "test.component.context.provider"
_inherit = "base.rest.service.context.provider"
_usage = "test_component_context_provider"
def _get_authenticated_partner_id(self):
return 9999
self._BaseTestController._component_context_provider = (
"test_component_context_provider"
)
# pylint: disable=R7980
class TestServiceNewApi(Component):
_inherit = "base.rest.service"
_name = "test.partner.service"
_usage = "partner"
_collection = self._collection_name
_description = "test"
@restapi.method(
[(["/<int:id>/get", "/<int:id>"], "GET")],
output_param=restapi.CerberusValidator("_get_partner_schema"),
auth="public",
)
def get(self, _id):
return {"name": self.env["res.partner"].browse(_id).name}
self._build_components(TestComponentContextprovider)
self._build_services(self, TestServiceNewApi)
controller = self._get_controller_for(TestServiceNewApi)
with MockRequest(self.env), controller().service_component(
"partner"
) as service:
self.assertEqual(service.work.authenticated_partner_id, 9999)
class CommonCase(BaseRestCase):
# dummy test method to pass codecov
def test_04(self):
self.assertEqual(self.registry.test_cr, self.cr)