mirror of
https://github.com/bringout/oca-technical.git
synced 2026-04-18 16:52:05 +02:00
Initial commit: OCA Technical packages (595 packages)
This commit is contained in:
commit
2cc02aac6e
24950 changed files with 2318079 additions and 0 deletions
|
|
@ -0,0 +1,6 @@
|
|||
# Copyright 2016 Akretion (http://www.akretion.com)
|
||||
# Sébastien BEAU <sebastien.beau@akretion.com>
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
||||
|
||||
from . import main
|
||||
from . import api_docs
|
||||
|
|
@ -0,0 +1,113 @@
|
|||
# Copyright 2018 ACSONE SA/NV
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
||||
|
||||
import json
|
||||
from contextlib import contextmanager
|
||||
|
||||
from odoo.http import Controller, request, route
|
||||
|
||||
from odoo.addons.component.core import WorkContext
|
||||
|
||||
from ..core import _rest_services_databases
|
||||
from .main import _PseudoCollection
|
||||
|
||||
|
||||
class ApiDocsController(Controller):
|
||||
def make_json_response(self, data, headers=None, cookies=None):
|
||||
data = json.dumps(data)
|
||||
if headers is None:
|
||||
headers = {}
|
||||
headers["Content-Type"] = "application/json"
|
||||
return request.make_response(data, headers=headers, cookies=cookies)
|
||||
|
||||
@route(
|
||||
["/api-docs", "/api-docs/index.html"],
|
||||
methods=["GET"],
|
||||
type="http",
|
||||
auth="public",
|
||||
)
|
||||
def index(self, **params):
|
||||
self._get_api_urls()
|
||||
primary_name = params.get("urls.primaryName")
|
||||
swagger_settings = {
|
||||
"urls": self._get_api_urls(),
|
||||
"urls.primaryName": primary_name,
|
||||
}
|
||||
values = {"swagger_settings": swagger_settings}
|
||||
return request.render("base_rest.openapi", values)
|
||||
|
||||
@route("/api-docs/<path:collection>/<string:service_name>.json", auth="public")
|
||||
def api(self, collection, service_name):
|
||||
with self.service_and_controller_class(collection, service_name) as (
|
||||
service,
|
||||
controller_class,
|
||||
):
|
||||
openapi_doc = service.to_openapi(
|
||||
default_auth=controller_class._default_auth
|
||||
)
|
||||
return self.make_json_response(openapi_doc)
|
||||
|
||||
def _get_api_urls(self):
|
||||
"""
|
||||
This method lookup into the dictionary of registered REST service
|
||||
for the current database to built the list of available REST API
|
||||
:return:
|
||||
"""
|
||||
services_registry = _rest_services_databases.get(request.env.cr.dbname, {})
|
||||
api_urls = []
|
||||
for rest_root_path, spec in list(services_registry.items()):
|
||||
collection_path = rest_root_path[1:-1] # remove '/'
|
||||
collection_name = spec["collection_name"]
|
||||
for service in self._get_service_in_collection(collection_name):
|
||||
api_urls.append(
|
||||
{
|
||||
"name": "{}: {}".format(collection_path, service._usage),
|
||||
"url": "/api-docs/%s/%s.json"
|
||||
% (collection_path, service._usage),
|
||||
}
|
||||
)
|
||||
api_urls = sorted(api_urls, key=lambda k: k["name"])
|
||||
return api_urls
|
||||
|
||||
def _filter_service_components(self, components):
|
||||
reg_model = request.env["rest.service.registration"]
|
||||
return [c for c in components if reg_model._filter_service_component(c)]
|
||||
|
||||
def _get_service_in_collection(self, collection_name):
|
||||
with self.work_on_component(collection_name) as work:
|
||||
components = work.components_registry.lookup(collection_name)
|
||||
services = self._filter_service_components(components)
|
||||
services = [work.component(usage=s._usage) for s in services]
|
||||
return services
|
||||
|
||||
@contextmanager
|
||||
def service_and_controller_class(self, collection_path, service_name):
|
||||
"""
|
||||
Return the component that implements the methods of the requested
|
||||
service.
|
||||
:param collection_path:
|
||||
:param service_name:
|
||||
:return: an instance of invader.service component,
|
||||
the base controller class serving the service
|
||||
"""
|
||||
services_spec = self._get_services_specs(collection_path)
|
||||
collection_name = services_spec["collection_name"]
|
||||
controller_class = services_spec["controller_class"]
|
||||
with self.work_on_component(collection_name) as work:
|
||||
service = work.component(usage=service_name)
|
||||
yield service, controller_class
|
||||
|
||||
@contextmanager
|
||||
def work_on_component(self, collection_name):
|
||||
"""
|
||||
Return the all the components implementing REST services
|
||||
:param collection_name:
|
||||
:return: a WorkContext instance
|
||||
"""
|
||||
|
||||
collection = _PseudoCollection(collection_name, request.env)
|
||||
yield WorkContext(model_name="rest.service.registration", collection=collection)
|
||||
|
||||
def _get_services_specs(self, path):
|
||||
services_registry = _rest_services_databases.get(request.env.cr.dbname, {})
|
||||
return services_registry["/" + path + "/"]
|
||||
|
|
@ -0,0 +1,201 @@
|
|||
# Copyright 2018 ACSONE SA/NV
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
||||
|
||||
import logging
|
||||
from contextlib import contextmanager
|
||||
|
||||
from werkzeug.exceptions import BadRequest
|
||||
|
||||
from odoo import models
|
||||
from odoo.http import Controller, Response, request
|
||||
|
||||
from odoo.addons.component.core import WorkContext, _get_addon_name
|
||||
|
||||
from ..core import _rest_controllers_per_module
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class _PseudoCollection(object):
|
||||
__slots__ = "_name", "env", "id"
|
||||
|
||||
def __init__(self, name, env):
|
||||
self._name = name
|
||||
self.env = env
|
||||
self.id = None
|
||||
|
||||
|
||||
class RestController(Controller):
|
||||
"""Generic REST Controller
|
||||
|
||||
This controller is the base controller used by as base controller for all the REST
|
||||
controller generated from the service components.
|
||||
|
||||
You must inherit of this controller into your code to register the root path
|
||||
used to serve all the services defined for the given collection name.
|
||||
This registration requires 2 parameters:
|
||||
|
||||
_root_path:
|
||||
_collection_name:
|
||||
|
||||
Only one controller by _collection_name, _root_path should exists into an
|
||||
odoo database. If more than one controller exists, a warning is issued into
|
||||
the log at startup and the concrete controller used as base class
|
||||
for the services registered into the collection name and served at the
|
||||
root path is not predictable.
|
||||
|
||||
Module A:
|
||||
class ControllerA(RestController):
|
||||
_root_path='/my_path/'
|
||||
_collection_name='my_services_collection'
|
||||
|
||||
Module B depends A: A
|
||||
class ControllerB(ControllerA): / \
|
||||
pass B C
|
||||
/
|
||||
Module C depends A: D
|
||||
class ControllerC(ControllerA):
|
||||
pass
|
||||
|
||||
Module D depends B:
|
||||
class ControllerB(ControllerB):
|
||||
pass
|
||||
|
||||
In the preceding illustration, services in module C will never be served
|
||||
by controller D or B. Therefore if the generic dispatch method is overridden
|
||||
in B or D, this override wil never apply to services in C since in Odoo
|
||||
controllers are not designed to be inherited. That's why it's an error
|
||||
to have more than one controller registered for the same root path and
|
||||
collection name.
|
||||
|
||||
The following properties can be specified to define common properties to
|
||||
apply to generated REST routes.
|
||||
|
||||
_default_auth: The default authentication to apply to all pre defined routes.
|
||||
default: 'user'
|
||||
_default_cors: The default Access-Control-Allow-Origin cors directive value.
|
||||
default: None
|
||||
_default_csrf: Whether CSRF protection should be enabled for the route.
|
||||
default: False
|
||||
_default_save_session: Whether session should be saved into the session store
|
||||
default: True
|
||||
"""
|
||||
|
||||
_root_path = None
|
||||
_collection_name = None
|
||||
# The default authentication to apply to all pre defined routes.
|
||||
_default_auth = "user"
|
||||
# The default Access-Control-Allow-Origin cors directive value.
|
||||
_default_cors = None
|
||||
# Whether CSRF protection should be enabled for the route.
|
||||
_default_csrf = False
|
||||
# Whether session should be saved into the session store
|
||||
_default_save_session = True
|
||||
|
||||
_component_context_provider = "component_context_provider"
|
||||
|
||||
@classmethod
|
||||
def __init_subclass__(cls):
|
||||
super().__init_subclass__()
|
||||
if "RestController" not in globals() or not any(
|
||||
issubclass(b, RestController) for b in cls.__bases__
|
||||
):
|
||||
return
|
||||
# register the rest controller into the rest controllers registry
|
||||
root_path = getattr(cls, "_root_path", None)
|
||||
collection_name = getattr(cls, "_collection_name", None)
|
||||
if root_path and collection_name:
|
||||
cls._module = _get_addon_name(cls.__module__)
|
||||
_rest_controllers_per_module[cls._module].append(
|
||||
{
|
||||
"root_path": root_path,
|
||||
"collection_name": collection_name,
|
||||
"controller_class": cls,
|
||||
}
|
||||
)
|
||||
_logger.debug(
|
||||
"Added rest controller %s for module %s",
|
||||
_rest_controllers_per_module[cls._module][-1],
|
||||
cls._module,
|
||||
)
|
||||
|
||||
def _get_component_context(self, collection=None):
|
||||
"""
|
||||
This method can be inherited to add parameter into the component
|
||||
context
|
||||
:return: dict of key value.
|
||||
"""
|
||||
work = WorkContext(
|
||||
model_name="rest.service.registration",
|
||||
collection=collection or self.default_collection,
|
||||
request=request,
|
||||
controller=self,
|
||||
)
|
||||
provider = work.component(usage=self._component_context_provider)
|
||||
return provider._get_component_context()
|
||||
|
||||
def make_response(self, data):
|
||||
if isinstance(data, Response):
|
||||
# The response has been build by the called method...
|
||||
return data
|
||||
# By default return result as json
|
||||
return request.make_json_response(data)
|
||||
|
||||
@property
|
||||
def collection_name(self):
|
||||
return self._collection_name
|
||||
|
||||
@property
|
||||
def default_collection(self):
|
||||
return _PseudoCollection(self.collection_name, request.env)
|
||||
|
||||
@contextmanager
|
||||
def work_on_component(self, collection=None):
|
||||
"""
|
||||
Return the component that implements the methods of the requested
|
||||
service.
|
||||
:param service_name:
|
||||
:return: an instance of base.rest.service component
|
||||
"""
|
||||
collection = collection or self.default_collection
|
||||
component_ctx = self._get_component_context(collection=collection)
|
||||
env = collection.env
|
||||
collection.env = env(
|
||||
context=dict(
|
||||
env.context,
|
||||
authenticated_partner_id=component_ctx.get("authenticated_partner_id"),
|
||||
)
|
||||
)
|
||||
yield WorkContext(model_name="rest.service.registration", **component_ctx)
|
||||
|
||||
@contextmanager
|
||||
def service_component(self, service_name, collection=None):
|
||||
"""
|
||||
Return the component that implements the methods of the requested
|
||||
service.
|
||||
:param service_name:
|
||||
:return: an instance of base.rest.service component
|
||||
"""
|
||||
with self.work_on_component(collection=collection) as work:
|
||||
service = work.component(usage=service_name)
|
||||
yield service
|
||||
|
||||
def _validate_method_name(self, method_name):
|
||||
if method_name.startswith("_"):
|
||||
_logger.error(
|
||||
"REST API called with an unallowed method "
|
||||
"name: %s.\n Method can't start with '_'",
|
||||
method_name,
|
||||
)
|
||||
raise BadRequest()
|
||||
return True
|
||||
|
||||
def _process_method(
|
||||
self, service_name, method_name, *args, collection=None, params=None
|
||||
):
|
||||
self._validate_method_name(method_name)
|
||||
if isinstance(collection, models.Model) and not collection:
|
||||
raise request.not_found()
|
||||
with self.service_component(service_name, collection=collection) as service:
|
||||
result = service.dispatch(method_name, *args, params=params)
|
||||
return self.make_response(result)
|
||||
Loading…
Add table
Add a link
Reference in a new issue