Skip to content

Commit 70b67a2

Browse files
committed
review suggestions
1 parent 73a899c commit 70b67a2

File tree

1 file changed

+113
-111
lines changed

1 file changed

+113
-111
lines changed

content/developer/reference/external_api.rst

Lines changed: 113 additions & 111 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,11 @@ Odoo is usually extended internally via modules, but many of its features and al
88
also available externally for analysis or integration with various other softwares. Part of the
99
:ref:`reference/orm/model` API is easily available over HTTP via the ``/json/2`` endpoint.
1010

11-
.. note::
12-
11+
.. tip::
1312
The actual models, fields and methods available are specific to every database and can be
1413
consulted on their ``/doc`` page.
1514

1615
.. note::
17-
1816
Access to data via the external API is only available on *Custom* Odoo pricing plans. Access to
1917
the external API is not available on *One App Free* or *Standard* plans. For more information
2018
visit the `Odoo pricing page <https://www.odoo.com/pricing-plan>`_ or reach out to your Customer
@@ -23,6 +21,8 @@ also available externally for analysis or integration with various other softwar
2321
API
2422
===
2523

24+
.. _reference/external_api/request:
25+
2626
Request
2727
-------
2828

@@ -31,9 +31,9 @@ Post a JSON object at the ``/json/2/<model>/<method>`` URL.
3131
**HTTP Headers**
3232

3333
:Host: Required, the hostname of the server.
34-
:Autorization: Required, ``bearer`` followed by an API key.
34+
:Autorization: Required, ``bearer`` followed by an :ref:`API key <reference/external_api/api_key>`.
3535
:Content-Type: Required, ``application/json``, a charset is recommended.
36-
:X-Odoo-Database: Optional, the name of the database on which to connect.
36+
:X-Odoo-Database: Optional, the name of the database to connect to.
3737
:User-Agent: Recommended, the name of your software.
3838

3939
**URL Path**
@@ -43,101 +43,106 @@ Post a JSON object at the ``/json/2/<model>/<method>`` URL.
4343

4444
**Body JSON object**
4545

46-
:ids: An array of record ids on which to execute the method.
46+
:ids: An array of record ids on which to execute the method. Empty or omitted when calling an
47+
``@api.model``-decorated method.
4748
:context: Optional, an object of additional values. e.g. ``{"lang": "en_US"}``.
48-
:*param*: As many time as needed, a value for the method's *param* parameter.
49-
50-
The ``ids`` must be absent or empty when calling a ``@api.model``-decorated method.
51-
52-
**Example**
53-
54-
.. code:: http
55-
56-
POST /json/2/res.partner/search_read HTTP/1.1
57-
Host: mycompany.example.com
58-
X-Odoo-Database: mycompany
59-
Authorization: bearer 6578616d706c65206a736f6e20617069206b6579
60-
Content-Type: application/json; charset=utf-8
61-
User-Agent: mysoftware python-requests/2.25.1
62-
63-
{
64-
"context": {
65-
"lang": "en_US"
66-
},
67-
"domain": [
68-
["name", "ilike", "%deco%"],
69-
["is_company", "=", true]
70-
],
71-
"fields": ["name"]
72-
}
49+
:*param*: As many time as needed, the method's parameters.
50+
51+
.. example::
52+
.. code:: http
53+
54+
POST /json/2/res.partner/search_read HTTP/1.1
55+
Host: mycompany.example.com
56+
X-Odoo-Database: mycompany
57+
Authorization: bearer 6578616d706c65206a736f6e20617069206b6579
58+
Content-Type: application/json; charset=utf-8
59+
User-Agent: mysoftware python-requests/2.25.1
60+
61+
{
62+
"context": {
63+
"lang": "en_US"
64+
},
65+
"domain": [
66+
["name", "ilike", "%deco%"],
67+
["is_company", "=", true]
68+
],
69+
"fields": ["name"]
70+
}
71+
72+
.. _reference/external_api/response:
7373

7474
Response
7575
--------
7676

7777
In case of **success**, a **200** status with the JSON-serialized return value of the called method
7878
in the body.
7979

80-
.. code:: http
81-
82-
HTTP/1.1 200 OK
83-
Content-Type: application/json; charset=utf-8
84-
85-
[
86-
{"id": 25, "name": "Deco Addict"}
87-
]
88-
89-
In case of **error**, a **4xx**/**5xx** status with a JSON-serialized error object in the body.
80+
.. example::
81+
.. code:: http
9082
91-
.. tabs::
92-
93-
.. code-tab:: http
94-
95-
HTTP/1.1 401 Unauthorized
83+
HTTP/1.1 200 OK
9684
Content-Type: application/json; charset=utf-8
9785
98-
{
99-
"name": "werkzeug.exceptions.Unauthorized",
100-
"message": "Invalid apikey",
101-
"arguments": ["Invalid apikey", 401],
102-
"context": {},
103-
"debug": "Traceback (most recent call last):\n File \"/opt/Odoo/community/odoo/http.py\", line 2212, in _transactioning\n return service_model.retrying(func, env=self.env)\n File \"/opt/Odoo/community/odoo/service/model.py\", line 176, in retrying\n result = func()\n File \"/opt/Odoo/community/odoo/http.py\", line 2177, in _serve_ir_http\n self.registry['ir.http']._authenticate(rule.endpoint)\n File \"/opt/Odoo/community/odoo/addons/base/models/ir_http.py\", line 274, in _authenticate\n cls._authenticate_explicit(auth)\n File \"/opt/Odoo/community/odoo/addons/base/models/ir_http.py\", line 283, in _authenticate_explicit\n getattr(cls, f'_auth_method_{auth}')()\n File \"/opt/Odoo/community/odoo/addons/base/models/ir_http.py\", line 240, in _auth_method_bearer\n raise werkzeug.exceptions.Unauthorized(\nwerkzeug.exceptions.Unauthorized: 401 Unauthorized: Invalid apikey\n"
104-
}
86+
[
87+
{"id": 25, "name": "Deco Addict"}
88+
]
10589
106-
.. tab:: Debug
107-
108-
.. code::
109-
110-
Traceback (most recent call last):
111-
File "/opt/Odoo/community/odoo/http.py", line 2212, in _transactioning
112-
return service_model.retrying(func, env=self.env)
113-
File "/opt/Odoo/community/odoo/service/model.py", line 176, in retrying
114-
result = func()
115-
File "/opt/Odoo/community/odoo/http.py", line 2177, in _serve_ir_http
116-
self.registry['ir.http']._authenticate(rule.endpoint)
117-
File "/opt/Odoo/community/odoo/addons/base/models/ir_http.py", line 274, in _authenticate
118-
cls._authenticate_explicit(auth)
119-
File "/opt/Odoo/community/odoo/addons/base/models/ir_http.py", line 283, in _authenticate_explicit
120-
getattr(cls, f'_auth_method_{auth}')()
121-
File "/opt/Odoo/community/odoo/addons/base/models/ir_http.py", line 240, in _auth_method_bearer
122-
raise werkzeug.exceptions.Unauthorized(
123-
werkzeug.exceptions.Unauthorized: 401 Unauthorized: Invalid apikey
90+
In case of **error**, a **4xx**/**5xx** status with a JSON-serialized error object in the body.
12491

12592
:name: The fully qualified name of the Python exception that occured.
12693
:message: The exception message, usually the same as `arguments[0]`.
12794
:arguments: All the exception arguments.
12895
:context: The context used by the request.
12996
:debug: The exception traceback, for debugging purpose.
13097

98+
.. example::
99+
100+
.. tabs::
101+
102+
.. tab:: HTTP
103+
.. code:: http
104+
105+
HTTP/1.1 401 Unauthorized
106+
Content-Type: application/json; charset=utf-8
107+
108+
{
109+
"name": "werkzeug.exceptions.Unauthorized",
110+
"message": "Invalid apikey",
111+
"arguments": ["Invalid apikey", 401],
112+
"context": {},
113+
"debug": "Traceback (most recent call last):\n File \"/opt/Odoo/community/odoo/http.py\", line 2212, in _transactioning\n return service_model.retrying(func, env=self.env)\n File \"/opt/Odoo/community/odoo/service/model.py\", line 176, in retrying\n result = func()\n File \"/opt/Odoo/community/odoo/http.py\", line 2177, in _serve_ir_http\n self.registry['ir.http']._authenticate(rule.endpoint)\n File \"/opt/Odoo/community/odoo/addons/base/models/ir_http.py\", line 274, in _authenticate\n cls._authenticate_explicit(auth)\n File \"/opt/Odoo/community/odoo/addons/base/models/ir_http.py\", line 283, in _authenticate_explicit\n getattr(cls, f'_auth_method_{auth}')()\n File \"/opt/Odoo/community/odoo/addons/base/models/ir_http.py\", line 240, in _auth_method_bearer\n raise werkzeug.exceptions.Unauthorized(\nwerkzeug.exceptions.Unauthorized: 401 Unauthorized: Invalid apikey\n"
114+
}
115+
116+
.. tab:: Debug
117+
.. code::
118+
119+
Traceback (most recent call last):
120+
File "/opt/Odoo/community/odoo/http.py", line 2212, in _transactioning
121+
return service_model.retrying(func, env=self.env)
122+
File "/opt/Odoo/community/odoo/service/model.py", line 176, in retrying
123+
result = func()
124+
File "/opt/Odoo/community/odoo/http.py", line 2177, in _serve_ir_http
125+
self.registry['ir.http']._authenticate(rule.endpoint)
126+
File "/opt/Odoo/community/odoo/addons/base/models/ir_http.py", line 274, in _authenticate
127+
cls._authenticate_explicit(auth)
128+
File "/opt/Odoo/community/odoo/addons/base/models/ir_http.py", line 283, in _authenticate_explicit
129+
getattr(cls, f'_auth_method_{auth}')()
130+
File "/opt/Odoo/community/odoo/addons/base/models/ir_http.py", line 240, in _auth_method_bearer
131+
raise werkzeug.exceptions.Unauthorized(
132+
werkzeug.exceptions.Unauthorized: 401 Unauthorized: Invalid apikey
133+
131134
Configuration
132135
=============
133136

137+
.. _reference/external_api/api_key:
138+
134139
API Key
135140
-------
136141

137142
An API key must be set in the ``Authorization`` request header, as a bearer token.
138143

139-
Create a new API key for a user via :guilabel:`Preferences`, :guilabel:`Account Security`, and
140-
:guilabel:`New API Key`.
144+
Create a new API key for a user via :menuselection:`Preferences --> Account Security -->
145+
New API Key`.
141146

142147
.. have the three images appear next to each other
143148
.. list-table::
@@ -154,86 +159,85 @@ Create a new API key for a user via :guilabel:`Preferences`, :guilabel:`Account
154159
Both a description and a duration are needed to create a new API key. The description makes it
155160
possible to identify the key, and to determine later whether the key is still in use or should be
156161
removed. The duration determines the lifetime of the key, after which the key becomes invalid. It is
157-
recommended to set a short duration (typically 1 day) for interactive usage. For security reasons,
158-
It is not possible to create keys that last for more than 3 months. This means that long lasting
159-
keys must be rotated at least once every 3 months.
162+
recommended to set a short duration (typically one day) for interactive usage. For security reasons,
163+
it is not possible to create keys that last for more than three months. This means that long lasting
164+
keys must be rotated at least once every three months.
160165

161-
The :guilabel:`Generate Key` button creates a strong 160-bits random key. Its value appears on
162-
screen, this is the only time and place the key is visible on screen. It must be copied, kept secret
163-
and stored somewhere secure. If it ever gets compromised or lost, then it must be removed.
166+
The :guilabel:`Generate Key` button creates a strong 160-bits random key. The key value is displayed
167+
only once during creation and cannot be retrieved later. Copy the key immediately and store it
168+
securely. If the key is compromised or lost, delete it immediately and generate a new one.
164169

165-
Please refer to OWASP's `Secrets Management Cheat Sheet`_ for further guidance on the management of
166-
API keys.
167-
168-
.. _Secrets Management Cheat Sheet: https://cheatsheetseries.owasp.org/cheatsheets/Secrets_Management_Cheat_Sheet.html#secrets-management-cheat-sheet
170+
Please refer to `OWASP's Secrets Management Cheat Sheet
171+
<https://cheatsheetseries.owasp.org/cheatsheets/Secrets_Management_Cheat_Sheet.html#secrets-management-cheat-sheet>`_
172+
for further guidance on the management of API keys.
169173

174+
.. _reference/external_api/access_rights:
170175

171176
Access Rights
172177
-------------
173178

174-
The JSON-2 API uses the standard :ref:`security <reference/security>` model of Odoo. All operations
179+
The JSON-2 API uses the standard :ref:`security models of Odoo <reference/security>`. All operations
175180
are validated against the access rights, record rules and field accesses of the user.
176181

177182
For **interactive usage**, such as discovering the API or running one-time scripts, it is fine to
178183
use a **personal account**.
179184

180185
For **extended automated usage**, such as an integration with another software, it is recommended to
181-
create and use **dedicated bot users**.
182-
183-
Using dedicated bot users has several benefits:
186+
create and use **dedicated bot users**. Using dedicated bot users has several benefits:
184187

185188
* The minimum required permissions can be granted to the bot, limiting the impact if the API key
186189
gets compromised.
187190
* The password can be set empty to disable login/password authentication, limiting the likelihood
188191
of the account getting compromised.
189192
* The :ref:`reference/fields/automatic/log_access` use the bot account. No user is impersonalized.
190193

194+
.. _reference/external_api/database:
191195

192196
Database
193197
--------
194198

195199
Depending on the deployment, the ``Host`` and/or ``X-Odoo-Database`` request headers might be
196200
required. The ``Host`` header is required by HTTP/1.1 and is needed on servers where Odoo is
197-
installed next to other web applications, so a web-server/reverse-proxy is able to route the request
198-
to the Odoo server. The ``X-Odoo-Database`` header is required when a single Odoo server hosts
199-
multiple databases, and that :ref:`dbfilter` wasn't configured to use the ``Host`` header.
201+
installed next to other web applications, so that a web-server/reverse-proxy is able to route the
202+
request to the Odoo server. The ``X-Odoo-Database`` header is required when a single Odoo server
203+
hosts multiple databases and the :ref:`dbfilter` wasn't configured to use the ``Host`` header.
200204

201205
Most HTTP client libraries automatically set the ``Host`` header using the connection URL.
202206

207+
.. _reference/external_api/transaction:
203208

204209
Transaction
205210
===========
206211

207212
All calls to the JSON-2 endpoint run in their own SQL transaction. The transaction is committed in
208-
case of success, and is discarded in case of error. Using the JSON-2 API, it is not possible to
209-
chain multiple calls inside a single transaction. It means that one must be cautious when doing
210-
multiple consecutive calls, as the database might be modified by other concurrent transactions. This
211-
is especially dangerous when doing operations related to reservations, payments, and the such.
213+
case of success and is discarded in case of error. Using the JSON-2 API, it is not possible to chain
214+
multiple calls inside a single transaction. It means that one must be cautious when making multiple
215+
consecutive calls, as the database might be modified by other concurrent transactions. This is
216+
especially dangerous when performing operations related to reservations, payments, and such.
212217

213-
The solution is to always call a single method that does all the related operations in a single
218+
The solution is to always call a single method that performs all the related operations in a single
214219
transaction. This way, the data is guaranteed to stay consistent: either everything is done
215220
(success, commit), or nothing is done (error, rollback).
216221

217-
In the ORM, the ``search_read`` method is an example of a single method that does multiple
222+
In the ORM, the ``search_read`` method is an example of a single method that performs multiple
218223
operations (``search`` then ``read``) in a single transaction. If a concurrent request removes one
219224
of the records ``search`` retrieves, then there is a risk that subsequent calls to ``read`` fail for
220-
a missing record error. Such problem cannot occurs in ``search_read``, as the system guarantees
225+
a missing record error. Such a problem cannot occur in ``search_read``, as the system guarantees
221226
proper isolation between transactions.
222227

223-
In business models those methods are often prefixed by ``action_``, such as
224-
``sale.order/action_confirm``, which verifies that a sale order is valid before confirming it.
228+
In business models, those methods are often prefixed by ``action_``, such as
229+
``sale.order``'s ``action_confirm`` method, which verifies that a sales order is valid before
230+
confirming it.
225231

226-
When no method exists for a set of related operations, it is possible to create a new one in a
227-
dedicated module.
232+
When no method exists for a set of related operations, a new one can be created in a dedicated
233+
module.
228234

229235
.. seealso::
236+
- :doc:`Tutorial to create a module <../tutorials/server_framework_101>`
237+
- PostgreSQL - Transaction Isolation - `Repeatable Read
238+
<https://www.postgresql.org/docs/current/transaction-iso.html#XACT-REPEATABLE-READ>`_
230239

231-
:doc:`Tutorial to create a module <../tutorials/server_framework_101>`
232-
233-
PostgreSQL - Transaction Isolation - `Repeatable Read`_
234-
235-
.. _repeatable read: https://www.postgresql.org/docs/current/transaction-iso.html#XACT-REPEATABLE-READ
236-
240+
.. _reference/external_api/code_example:
237241

238242
Code Example
239243
============
@@ -283,7 +287,6 @@ documentation is be available at https://mycompany.example.com/doc
283287
names = res_read.json()
284288
print(names)
285289

286-
287290
.. code-tab:: javascript
288291

289292
(async () => {
@@ -351,7 +354,6 @@ documentation is be available at https://mycompany.example.com/doc
351354
--silent \
352355
--fail-with-body
353356

354-
355357
The above example is equivalent to running::
356358

357359
Model = self.env["res.partner"].with_context({"lang": "en_US"})
@@ -364,12 +366,14 @@ then in a new transaction::
364366
names = records.read(["name"])
365367
return json.dumps(names)
366368

369+
.. _reference/external_api/dynamic_doc:
367370

368371
Dynamic Documentation
369372
=====================
370373

371374
Under construction
372375

376+
.. _reference/external_api/migration:
373377

374378
Migrating from XML-RPC / JSON-RPC
375379
=================================
@@ -383,7 +387,6 @@ db (database) and object. All three services are deprecated.
383387
The other controllers ``@route(type='jsonrpc')`` (known until Odoo 18 as ``type='json'``) are not
384388
subject to this deprecation notice.
385389

386-
387390
Common service
388391
--------------
389392

@@ -542,4 +545,3 @@ Using JSON-2:
542545
"load": None,
543546
},
544547
).json()
545-

0 commit comments

Comments
 (0)