mirror of
https://github.com/bringout/oca-technical.git
synced 2026-04-18 12:52:05 +02:00
422 lines
16 KiB
ReStructuredText
422 lines
16 KiB
ReStructuredText
=========
|
||
Base Rest
|
||
=========
|
||
|
||
..
|
||
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||
!! This file is generated by oca-gen-addon-readme !!
|
||
!! changes will be overwritten. !!
|
||
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||
!! source digest: sha256:517d5b1d74542047b404d2130e5d9239fe591f43b1a89ca02339766c8c8a6584
|
||
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||
|
||
.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png
|
||
:target: https://odoo-community.org/page/development-status
|
||
:alt: Beta
|
||
.. |badge2| image:: https://img.shields.io/badge/licence-LGPL--3-blue.png
|
||
:target: http://www.gnu.org/licenses/lgpl-3.0-standalone.html
|
||
:alt: License: LGPL-3
|
||
.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Frest--framework-lightgray.png?logo=github
|
||
:target: https://github.com/OCA/rest-framework/tree/16.0/base_rest
|
||
:alt: OCA/rest-framework
|
||
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
|
||
:target: https://translation.odoo-community.org/projects/rest-framework-16-0/rest-framework-16-0-base_rest
|
||
:alt: Translate me on Weblate
|
||
.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png
|
||
:target: https://runboat.odoo-community.org/builds?repo=OCA/rest-framework&target_branch=16.0
|
||
:alt: Try me on Runboat
|
||
|
||
|badge1| |badge2| |badge3| |badge4| |badge5|
|
||
|
||
This addon is deprecated and not fully supported anymore on Odoo 16.
|
||
Please migrate to the FastAPI migration module.
|
||
See https://github.com/OCA/rest-framework/pull/291.
|
||
|
||
This addon provides the basis to develop high level REST APIs for Odoo.
|
||
|
||
As Odoo becomes one of the central pieces of enterprise IT systems, it often
|
||
becomes necessary to set up specialized service interfaces, so existing
|
||
systems can interact with Odoo.
|
||
|
||
While the XML-RPC interface of Odoo comes handy in such situations, it
|
||
requires a deep understanding of Odoo’s internal data model. When used
|
||
extensively, it creates a strong coupling between Odoo internals and client
|
||
systems, therefore increasing maintenance costs.
|
||
|
||
Once developed, an `OpenApi <https://spec.openapis.org/oas/v3.0.3>`_ documentation
|
||
is generated from the source code and available via a
|
||
`Swagger UI <https://swagger.io/tools/swagger-ui/>`_ served by your odoo server
|
||
at `https://my_odoo_server/api-docs`.
|
||
|
||
**Table of contents**
|
||
|
||
.. contents::
|
||
:local:
|
||
|
||
Configuration
|
||
=============
|
||
|
||
If an error occurs when calling a method of a service (ie missing parameter,
|
||
..) the system returns only a general description of the problem without
|
||
details. This is done on purpose to ensure maximum opacity on implementation
|
||
details and therefore lower security issue.
|
||
|
||
This restriction can be problematic when the services are accessed by an
|
||
external system in development. To know the details of an error it is indeed
|
||
necessary to have access to the log of the server. It is not always possible
|
||
to provide this kind of access. That's why you can configure the server to run
|
||
these services in development mode.
|
||
|
||
To run the REST API in development mode you must add a new section
|
||
'**[base_rest]**' with the option '**dev_mode=True**' in the server config
|
||
file.
|
||
|
||
.. code-block:: cfg
|
||
|
||
[base_rest]
|
||
dev_mode=True
|
||
|
||
When the REST API runs in development mode, the original description and a
|
||
stack trace is returned in case of error. **Be careful to not use this mode
|
||
in production**.
|
||
|
||
Usage
|
||
=====
|
||
|
||
To add your own REST service you must provides at least 2 classes.
|
||
|
||
* A Component providing the business logic of your service,
|
||
* A Controller to register your service.
|
||
|
||
The business logic of your service must be implemented into a component
|
||
(``odoo.addons.component.core.Component``) that inherit from
|
||
'base.rest.service'
|
||
|
||
Initially, base_rest expose by default all public methods defined in a service.
|
||
The conventions for accessing methods via HTTP were as follows:
|
||
|
||
* The method ``def get(self, _id)`` if defined, is accessible via HTTP GET routes ``<string:_service_name>/<int:_id>`` and ``<string:_service_name>/<int:_id>/get``.
|
||
* The method ``def search(self, **params)`` if defined, is accessible via the HTTP GET routes ``<string:_service_name>/`` and ``<string:_service_name>/search``.
|
||
* The method ``def delete(self, _id)`` if defined, is accessible via the HTTP DELETE route ``<string:_service_name>/<int:_id>``.
|
||
* The ``def update(self, _id, **params)`` method, if defined, is accessible via the HTTP PUT route ``<string:_service_name>/<int:_id>``.
|
||
* Other methods are only accessible via HTTP POST routes ``<string:_service_name>`` or ``<string:_service_name>/<string:method_name>`` or ``<string:_service_name>/<int:_id>`` or ``<string:_service_name>/<int:_id>/<string:method_name>``
|
||
|
||
.. code-block:: python
|
||
|
||
from odoo.addons.component.core import Component
|
||
|
||
|
||
class PingService(Component):
|
||
_inherit = 'base.rest.service'
|
||
_name = 'ping.service'
|
||
_usage = 'ping'
|
||
_collection = 'my_module.services'
|
||
|
||
|
||
# The following method are 'public' and can be called from the controller.
|
||
def get(self, _id, message):
|
||
return {
|
||
'response': 'Get called with message ' + message}
|
||
|
||
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}
|
||
|
||
# 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'}}
|
||
|
||
Once you have implemented your services (ping, ...), you must tell to Odoo
|
||
how to access to these services. This process is done by implementing a
|
||
controller that inherits from ``odoo.addons.base_rest.controllers.main.RestController``
|
||
|
||
.. code-block:: python
|
||
|
||
from odoo.addons.base_rest.controllers import main
|
||
|
||
class MyRestController(main.RestController):
|
||
_root_path = '/my_services_api/'
|
||
_collection_name = my_module.services
|
||
|
||
In your controller, _'root_path' is used to specify the root of the path to
|
||
access to your services and '_collection_name' is the name of the collection
|
||
providing the business logic for the requested service/
|
||
|
||
|
||
By inheriting from ``RestController`` the following routes will be registered
|
||
to access to your services
|
||
|
||
.. code-block:: python
|
||
|
||
@route([
|
||
ROOT_PATH + '<string:_service_name>',
|
||
ROOT_PATH + '<string:_service_name>/search',
|
||
ROOT_PATH + '<string:_service_name>/<int:_id>',
|
||
ROOT_PATH + '<string:_service_name>/<int:_id>/get'
|
||
], methods=['GET'], auth="user", csrf=False)
|
||
def get(self, _service_name, _id=None, **params):
|
||
method_name = 'get' if _id else 'search'
|
||
return self._process_method(_service_name, method_name, _id, params)
|
||
|
||
@route([
|
||
ROOT_PATH + '<string:_service_name>',
|
||
ROOT_PATH + '<string:_service_name>/<string:method_name>',
|
||
ROOT_PATH + '<string:_service_name>/<int:_id>',
|
||
ROOT_PATH + '<string:_service_name>/<int:_id>/<string:method_name>'
|
||
], methods=['POST'], auth="user", csrf=False)
|
||
def modify(self, _service_name, _id=None, method_name=None, **params):
|
||
if not method_name:
|
||
method_name = 'update' if _id else 'create'
|
||
if method_name == 'get':
|
||
_logger.error("HTTP POST with method name 'get' is not allowed. "
|
||
"(service name: %s)", _service_name)
|
||
raise BadRequest()
|
||
return self._process_method(_service_name, method_name, _id, params)
|
||
|
||
@route([
|
||
ROOT_PATH + '<string:_service_name>/<int:_id>',
|
||
], methods=['PUT'], auth="user", csrf=False)
|
||
def update(self, _service_name, _id, **params):
|
||
return self._process_method(_service_name, 'update', _id, params)
|
||
|
||
@route([
|
||
ROOT_PATH + '<string:_service_name>/<int:_id>',
|
||
], methods=['DELETE'], auth="user", csrf=False)
|
||
def delete(self, _service_name, _id):
|
||
return self._process_method(_service_name, 'delete', _id)
|
||
|
||
|
||
As result an HTTP GET call to 'http://my_odoo/my_services_api/ping' will be
|
||
dispatched to the method ``PingService.search``
|
||
|
||
In addition to easily exposing your methods, the module allows you to define
|
||
data schemas to which the exchanged data must conform. These schemas are defined
|
||
on the basis of `Cerberus schemas <https://docs.python-cerberus.org/en/stable/>`_
|
||
and associated to the methods using the
|
||
following naming convention. For a method `my_method`:
|
||
|
||
* ``def _validator_my_method(self):`` will be called to get the schema required to
|
||
validate the input parameters.
|
||
* ``def _validator_return_my_method(self):`` if defined, will be called to get
|
||
the schema used to validate the response.
|
||
|
||
In order to offer even more flexibility, a new API has been developed.
|
||
|
||
This new API replaces the implicit approach used to expose a service by the use
|
||
of a python decorator to explicitly mark a method as being available via the
|
||
REST API: ``odoo.addons.base_rest.restapi.method``.
|
||
|
||
|
||
.. code-block:: python
|
||
|
||
class PartnerNewApiService(Component):
|
||
_inherit = "base.rest.service"
|
||
_name = "partner.new_api.service"
|
||
_usage = "partner"
|
||
_collection = "base.rest.demo.new_api.services"
|
||
_description = """
|
||
Partner New API Services
|
||
Services developed with the new api provided by base_rest
|
||
"""
|
||
|
||
@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}
|
||
}
|
||
|
||
@restapi.method(
|
||
[(["/list", "/"], "GET")],
|
||
output_param=restapi.CerberusListValidator("_get_partner_schema"),
|
||
auth="public",
|
||
)
|
||
def list(self):
|
||
partners = self.env["res.partner"].search([])
|
||
return [{"name": p.name} for p in partners]
|
||
|
||
Thanks to this new api, you are now free to specify your own routes but also
|
||
to use other object types as parameter or response to your methods.
|
||
For example, `base_rest_datamodel` allows you to use Datamodel object instance
|
||
into your services.
|
||
|
||
.. code-block:: python
|
||
|
||
from marshmallow import fields
|
||
|
||
from odoo.addons.base_rest import restapi
|
||
from odoo.addons.component.core import Component
|
||
from odoo.addons.datamodel.core import Datamodel
|
||
|
||
|
||
class PartnerSearchParam(Datamodel):
|
||
_name = "partner.search.param"
|
||
|
||
id = fields.Integer(required=False, allow_none=False)
|
||
name = fields.String(required=False, allow_none=False)
|
||
|
||
|
||
class PartnerShortInfo(Datamodel):
|
||
_name = "partner.short.info"
|
||
|
||
id = fields.Integer(required=True, allow_none=False)
|
||
name = fields.String(required=True, allow_none=False)
|
||
|
||
|
||
class PartnerNewApiService(Component):
|
||
_inherit = "base.rest.service"
|
||
_name = "partner.new_api.service"
|
||
_usage = "partner"
|
||
_collection = "base.rest.demo.new_api.services"
|
||
_description = """
|
||
Partner New API Services
|
||
Services developed with the new api provided by base_rest
|
||
"""
|
||
|
||
@restapi.method(
|
||
[(["/", "/search"], "GET")],
|
||
input_param=restapi.Datamodel("partner.search.param"),
|
||
output_param=restapi.Datamodel("partner.short.info", is_list=True),
|
||
auth="public",
|
||
)
|
||
def search(self, partner_search_param):
|
||
"""
|
||
Search for partners
|
||
:param partner_search_param: An instance of partner.search.param
|
||
:return: List of partner.short.info
|
||
"""
|
||
domain = []
|
||
if partner_search_param.name:
|
||
domain.append(("name", "like", partner_search_param.name))
|
||
if partner_search_param.id:
|
||
domain.append(("id", "=", partner_search_param.id))
|
||
res = []
|
||
PartnerShortInfo = self.env.datamodels["partner.short.info"]
|
||
for p in self.env["res.partner"].search(domain):
|
||
res.append(PartnerShortInfo(id=p.id, name=p.name))
|
||
return res
|
||
|
||
The BaseRestServiceContextProvider provides context for your services,
|
||
including authenticated_partner_id.
|
||
You are free to redefine the method _get_authenticated_partner_id() to pass the
|
||
authenticated_partner_id based on the authentication mechanism of your choice.
|
||
See base_rest_auth_jwt for an example.
|
||
|
||
In addition, authenticated_partner_id is available in record rule evaluation context.
|
||
|
||
Known issues / Roadmap
|
||
======================
|
||
|
||
The `roadmap <https://github.com/OCA/rest-framework/issues?q=is%3Aopen+is%3Aissue+label%3Aenhancement+label%3Abase_rest>`_
|
||
and `known issues <https://github.com/OCA/rest-framework/issues?q=is%3Aopen+is%3Aissue+label%3Abug+label%3Abase_rest>`_ can
|
||
be found on GitHub.
|
||
|
||
Changelog
|
||
=========
|
||
|
||
16.0.1.0.2 (2023-10-07)
|
||
~~~~~~~~~~~~~~~~~~~~~~~
|
||
|
||
**Features**
|
||
|
||
- Add support for oauth2 security scheme in the Swagger UI. If your openapi
|
||
specification contains a security scheme of type oauth2, the Swagger UI will
|
||
display a login button in the top right corner. In order to finalize the
|
||
login process, a redirect URL must be provided when initializing the Swagger
|
||
UI. The Swagger UI is now initialized with a `oauth2RedirectUrl` option that
|
||
references a oauth2-redirect.html file provided by the swagger-ui lib and served
|
||
by the current addon. (`#379 <https://github.com/OCA/rest-framework/issues/379>`_)
|
||
|
||
|
||
12.0.2.0.1
|
||
~~~~~~~~~~
|
||
|
||
* _validator_...() methods can now return a cerberus ``Validator`` object
|
||
instead of a schema dictionnary, for additional flexibility (e.g. allowing
|
||
validator options such as ``allow_unknown``).
|
||
|
||
12.0.2.0.0
|
||
~~~~~~~~~~
|
||
|
||
* Licence changed from AGPL-3 to LGPL-3
|
||
|
||
12.0.1.0.1
|
||
~~~~~~~~~~
|
||
|
||
* Fix issue when rendering the jsonapi documentation if no documentation is
|
||
provided on a method part of the REST api.
|
||
|
||
12.0.1.0.0
|
||
~~~~~~~~~~
|
||
|
||
First official version. The addon has been incubated into the
|
||
`Shopinvader repository <https://github.com/akretion/odoo-shopinvader>`_ from
|
||
Akretion. For more information you need to look at the git log.
|
||
|
||
Bug Tracker
|
||
===========
|
||
|
||
Bugs are tracked on `GitHub Issues <https://github.com/OCA/rest-framework/issues>`_.
|
||
In case of trouble, please check there if your issue has already been reported.
|
||
If you spotted it first, help us to smash it by providing a detailed and welcomed
|
||
`feedback <https://github.com/OCA/rest-framework/issues/new?body=module:%20base_rest%0Aversion:%2016.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.
|
||
|
||
Do not contact contributors directly about support or help with technical issues.
|
||
|
||
Credits
|
||
=======
|
||
|
||
Authors
|
||
~~~~~~~
|
||
|
||
* ACSONE SA/NV
|
||
|
||
Contributors
|
||
~~~~~~~~~~~~
|
||
|
||
* Laurent Mignon <laurent.mignon@acsone.eu>
|
||
* Sébastien Beau <sebastien.beau@akretion.com>
|
||
|
||
Maintainers
|
||
~~~~~~~~~~~
|
||
|
||
This module is maintained by the OCA.
|
||
|
||
.. image:: https://odoo-community.org/logo.png
|
||
:alt: Odoo Community Association
|
||
:target: https://odoo-community.org
|
||
|
||
OCA, or the Odoo Community Association, is a nonprofit organization whose
|
||
mission is to support the collaborative development of Odoo features and
|
||
promote its widespread use.
|
||
|
||
This module is part of the `OCA/rest-framework <https://github.com/OCA/rest-framework/tree/16.0/base_rest>`_ project on GitHub.
|
||
|
||
You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.
|