Skip to content

Commit f87ee68

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

File tree

6 files changed

+292
-12
lines changed

6 files changed

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

0 commit comments

Comments
 (0)