Skip to content

Commit 088509f

Browse files
committed
[ADD] reference/external_json2_api
1 parent d4a9931 commit 088509f

File tree

6 files changed

+307
-12
lines changed

6 files changed

+307
-12
lines changed

content/developer/reference.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,5 @@ Reference
1717
reference/cli
1818
reference/upgrades
1919
reference/external_api
20+
reference/external_json2_api
2021
reference/extract_api

content/developer/reference/external_api.rst

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,22 @@
1-
============
2-
External API
3-
============
1+
================
2+
External RPC API
3+
================
4+
5+
.. deprecated:: 19.0
46

57
Odoo is usually extended internally via modules, but many of its features and
68
all of its data are also available from the outside for external analysis or
79
integration with various tools. Part of the :ref:`reference/orm/model` API is
810
easily available over XML-RPC_ and accessible from a variety of languages.
911

10-
.. important::
11-
Starting with PHP8, the XML-RPC extension may not be available by default.
12-
Check out the `manual <https://www.php.net/manual/en/xmlrpc.installation.php>`_
13-
for the installation steps.
12+
Starting with PHP8, the XML-RPC extension may not be available by default.
13+
Check out the `manual <https://www.php.net/manual/en/xmlrpc.installation.php>`_
14+
for the installation steps.
1415

15-
.. note::
16-
Access to data via the external API is only available on *Custom* Odoo pricing plans. Access to
17-
the external API is not available on *One App Free* or *Standard* plans. For more information
18-
visit the `Odoo pricing page <https://www.odoo.com/pricing-plan>`_ or reach out to your Customer
19-
Success Manager.
16+
Access to data via the external API is only available on *Custom* Odoo pricing plans. Access to
17+
the external API is not available on *One App Free* or *Standard* plans. For more information
18+
visit the `Odoo pricing page <https://www.odoo.com/pricing-plan>`_ or reach out to your Customer
19+
Success Manager.
2020

2121
.. seealso::
2222
- :doc:`Tutorial on web services <../howtos/web_services>`
Loading
Loading
Loading
Lines changed: 294 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,294 @@
1+
===================
2+
External JSON-2 API
3+
===================
4+
5+
.. versionadded:: 19.0
6+
7+
Odoo is usually extended internally via modules, but many of its features and all of its data are
8+
also available from the outside for external analysis or integration with various other softwares.
9+
Part of the :ref:`reference/orm/model` API is easily available over HTTP via the ``/json/2``
10+
endpoint. The actual models, fields and methods available are specific to every database and can be
11+
consulted on their ``/doc`` page.
12+
13+
API
14+
===
15+
16+
Request
17+
-------
18+
19+
Post a JSON object at the ``/json/2/<model>/<method>`` URL.
20+
21+
HTTP Headers
22+
~~~~~~~~~~~~
23+
24+
:Host: Required, the hostname of the server.
25+
:Autorization: Required, ``bearer`` followed by an API Key.
26+
:Content-Type: Required, ``application/json``, a charset is recommended.
27+
:X-Odoo-Dataase: Optional, the name of the database on which to connect.
28+
:User-Agent: Recommended when integrating with another software.
29+
30+
URL Path
31+
~~~~~~~~
32+
33+
:model: Required, the technical model name.
34+
:method: Required, the method to execute.
35+
36+
JSON Body
37+
~~~~~~~~~
38+
39+
A JSON object with the keys:
40+
41+
:ids: An array of record ids on which to execute the method.
42+
:context: Optional, an object of additional values. e.g. ``{"lang": "en_US"}``.
43+
:*param*: As many time as needed, a value for the method's *param* parameter.
44+
45+
The ``ids`` entry must be missing or an empty array when calling a ``@api.model``-decorated method.
46+
47+
Example
48+
~~~~~~~
49+
50+
.. code:: http
51+
52+
POST /json/2/res.partner/search_read HTTP/1.1
53+
Host: mycompany.example.com
54+
X-Odoo-Database: mycompany
55+
Authorization: bearer 6578616d706c65206a736f6e20617069206b6579
56+
Content-Type: application/json; charset=utf-8
57+
User-Agent: mysoftware python-requests/2.25.1
58+
59+
{
60+
"context": {
61+
"lang": "en_US"
62+
},
63+
"domain": [
64+
["name", "ilike", "%deco%"],
65+
["is_company", "=", true]
66+
],
67+
"fields": ["name"]
68+
}
69+
70+
Response
71+
--------
72+
73+
Success
74+
~~~~~~~
75+
76+
A **200** status with the JSON-serialized return value of the called method in the body.
77+
78+
.. code:: http
79+
80+
HTTP/1.1 200 OK
81+
Content-Type: application/json; charset=utf-8
82+
83+
[
84+
{"id": 25, "name": "Deco Addict"}
85+
]
86+
87+
Error
88+
~~~~~
89+
90+
A **4xx**/**5xx** status with the JSON-serialized error string in the body.
91+
92+
.. code:: http
93+
94+
HTTP/1.1 401 Unauthorized
95+
Content-Type: application/json; charset=utf-8
96+
97+
"Invalid apikey"
98+
99+
The complete traceback is available in the server log, at the same date and time as the response.
100+
101+
102+
Configuration
103+
=============
104+
105+
API Key
106+
-------
107+
108+
An API key must be set in the ``Authorization`` request header, as a bearer token.
109+
110+
Create a new API key for a user via :guilabel:`Preferences`, :guilabel:`Account Security`, and
111+
:guilabel:`New API Key`.
112+
113+
.. have the three images appear next to each other
114+
.. list-table::
115+
116+
* - .. image:: external_api/preferences2.png
117+
:align: center
118+
119+
- .. image:: external_api/account-security2.png
120+
:align: center
121+
122+
- .. image:: external_api/new-api-key.png
123+
:align: center
124+
125+
A description and a duration are needed to create a new api key. The description makes it possible
126+
to identify the key, and to determine later whether the key is still in use or should be removed.
127+
The duration determines the lifetime of the key after which the the key becomes invalid. It is
128+
recommended to set a short duration (typically 1 day) for interactive usage. It is not possible to
129+
create keys that last for more than 3 months, it means that long lasting keys must be rotated at
130+
least once every 3 months.
131+
132+
The :guilabel:`Generate Key` button creates a 160 bits strong random key. Its value appears on
133+
screen, this is the only time and place the key is visible on screen. It must be copied, kept secret
134+
and stored somewhere secure. If it ever gets compromized or lost, then it must be removed.
135+
136+
Please refer to OWASP's `Secrets Management Cheat Sheet`_ for further guidance on the management of
137+
API keys.
138+
139+
.. _Secrets Management Cheat Sheet: https://cheatsheetseries.owasp.org/cheatsheets/Secrets_Management_Cheat_Sheet.html#secrets-management-cheat-sheet
140+
141+
142+
Access Rights
143+
-------------
144+
145+
The JSON-2 API uses the standard :ref:`security <reference/security>` model of Odoo. All operations
146+
are validated against the access rights, record rules and field accesses of the user.
147+
148+
For **interfactive usage**, such as discovering the API or running one-time scripts, it is fine to
149+
use a **personal account**.
150+
151+
For **extended automated usage**, such as an integration with another software, it is recommended to
152+
create and use **dedicated bot users**.
153+
154+
Using dedicated bot users has several benefits:
155+
156+
* The minimum required permissions can be granted to the bot, limiting the impact may the API key
157+
gets compromised;
158+
* The password can be set empty to disable login/password authentication, limiting the likelihood
159+
the account gets compromized;
160+
* The :ref:`reference/fields/automatic/log_access` use the bot account. No user gets impersonalized.
161+
162+
163+
Database
164+
--------
165+
166+
Depending on the deploiement, the ``Host`` and/or ``X-Odoo-Database`` request headers might be
167+
required. The ``Host`` header is required by HTTP/1.1 and is needed on servers where Odoo is
168+
installed next to other web applications, so a web-server/reverse-proxy is able to route the request
169+
to the Odoo server. The ``X-Odoo-Database`` header is required when a single Odoo server hosts
170+
multiple databases, and that :ref:`dbfilter` wasn't configured to use the ``Host`` header.
171+
172+
Most HTTP client libraries automatically set the ``Host`` header using the connection url.
173+
174+
175+
Transaction
176+
===========
177+
178+
.. note::
179+
180+
Under construction
181+
182+
183+
Code Example
184+
============
185+
186+
The following example showcases how to execute two of the :ref:`reference/orm/models/crud` on a fake
187+
database ``mycompany`` hosted on a fake website ``https://mycompany.example.com``. Its comprehensive
188+
documentation would be available at https://mycompany.example.com/doc
189+
190+
.. tabs::
191+
192+
.. code-tab:: python
193+
194+
import requests
195+
196+
BASE_URL = "https://mycompany.example.com/json/2"
197+
API_KEY = ... # get it from a secure location
198+
headers = {
199+
"Authorization": f"bearer {API_KEY}",
200+
"X-Odoo-Database": "mycompany",
201+
"User-Agent": "mysoftware " + requests.utils.default_user_agent(),
202+
}
203+
204+
res_search = requests.post(
205+
f"{BASE_URL}/res.partner/search",
206+
headers=headers,
207+
json={
208+
"context": {"lang": "en_US"},
209+
"domain": [
210+
("name", "ilike", "%deco%"),
211+
("is_company", "=", True),
212+
],
213+
},
214+
)
215+
res_search.raise_for_status()
216+
ids = res_search.json()
217+
218+
res_read = requests.post(
219+
f"{BASE_URL}/res.partner/read",
220+
headers=headers,
221+
json={
222+
"context": {"lang": "en_US"},
223+
"fields": ["name"],
224+
}
225+
)
226+
res_read.raise_for_status()
227+
names = res_read.json()
228+
print(names)
229+
230+
231+
.. code-tab:: javascript
232+
233+
(async () => {
234+
const BASE_URL = "https://mycompany.example.com/json/2";
235+
const API_KEY = ; // get it from a secure location
236+
const headers = {
237+
"Content-Type": "application/json",
238+
"Authorization": "bearer " + API_KEY,
239+
"X-Odoo-Database": DATABASE,
240+
}
241+
242+
const reqSearch = {
243+
method: "POST",
244+
headers: headers,
245+
body: {
246+
context: {lang: "en_US"},
247+
domain: [
248+
["name", "ilike", "%deco%"],
249+
["is_company", "=", true],
250+
],
251+
},
252+
};
253+
const resSearch = await fetch(BASE_URL + "/res.partner/search_read", reqSearch);
254+
if (!response.ok) throw new Error(resSearch.json());
255+
const ids = await resSearch.json();
256+
257+
const reqRead = {
258+
method: "POST",
259+
headers: headers,
260+
body: {
261+
ids: ids,
262+
context: {lang: "en_US"},
263+
fields: ["name"],
264+
},
265+
};
266+
const resRead = await fetch(BASE_URL + "/res.partner/search_read", reqRead);
267+
if (!response.ok) throw new Error(resRead.json());
268+
const names = await resRead.json();
269+
console.log(names);
270+
})();
271+
272+
273+
The above example would be equivalent to running::
274+
275+
with odoo.sql_db.db_connect("mycompany") as cr:
276+
env = odoo.api.Environment(cr, uid=..., context={"lang": "en_US"})
277+
records = env['res.partner'].search([
278+
("name", "ilike", "%deco%"),
279+
("is_company", "=", True),
280+
])
281+
# return json.dumps(records.ids)
282+
283+
with odoo.sql_db.db_connect("mycompany") as cr:
284+
env = odoo.api.Environment(cr, uid=..., context={"lang": "en_US"})
285+
names = env['res.partner'].browse(ids).read(['name'])
286+
# return json.dumps(names)
287+
288+
289+
Migrating from XML-RPC / JSON-RPC
290+
=================================
291+
292+
.. note::
293+
294+
Under construction

0 commit comments

Comments
 (0)