Skip to content

Commit 0dbdeb7

Browse files
committed
[ADD] fastapi_backport
1 parent ac9297f commit 0dbdeb7

File tree

5 files changed

+171
-0
lines changed

5 files changed

+171
-0
lines changed

fastapi_backport/__init__.py

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# Copyright 2024 Akretion (http://www.akretion.com).
2+
# @author Florian Mounier <florian.mounier@akretion.com>
3+
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
4+
5+
from . import models
6+
from . import http
7+
from odoo.addons.extendable.models.ir_http import IrHttp
8+
from odoo.addons.fastapi.tests.common import FastAPITransactionCase
9+
from odoo.addons.fastapi.fastapi_dispatcher import FastApiDispatcher
10+
from odoo.tests.common import SavepointCase
11+
12+
FastAPITransactionCase.__bases__ = (SavepointCase,)
13+
14+
15+
@classmethod
16+
def _dispatch(cls):
17+
with cls._extendable_context_registry():
18+
return super(IrHttp, cls)._dispatch()
19+
20+
21+
IrHttp._dispatch = _dispatch

fastapi_backport/__manifest__.py

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# Copyright 2024 Akretion (http://www.akretion.com).
2+
# @author Florian Mounier <florian.mounier@akretion.com>
3+
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
4+
5+
{
6+
"name": "Fastapi Backport",
7+
"summary": "Backport of FastAPI to Odoo 14.0",
8+
"version": "14.0.1.0.0",
9+
"author": " Akretion",
10+
"license": "AGPL-3",
11+
"depends": [
12+
"sixteen_in_fourteen",
13+
"base_future_response",
14+
"fastapi",
15+
"pydantic",
16+
"extendable_fastapi",
17+
"extendable",
18+
],
19+
}

fastapi_backport/http.py

+106
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
# Copyright 2023 ACSONE SA/NV
2+
# Copyright 2024 Akretion (http://www.akretion.com).
3+
# @author Florian Mounier <florian.mounier@akretion.com>
4+
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
5+
6+
from functools import lru_cache
7+
8+
import odoo
9+
import json
10+
import werkzeug.datastructures
11+
12+
from odoo import http
13+
from odoo.addons.fastapi.fastapi_dispatcher import FastApiDispatcher
14+
from odoo.tools import date_utils
15+
16+
17+
class FastapiRootPaths:
18+
_root_paths_by_db = {}
19+
20+
@classmethod
21+
def set_root_paths(cls, db, root_paths):
22+
cls._root_paths_by_db[db] = root_paths
23+
cls.is_fastapi_path.cache_clear()
24+
25+
@classmethod
26+
@lru_cache(maxsize=1024)
27+
def is_fastapi_path(cls, db, path):
28+
return any(
29+
path.startswith(root_path)
30+
for root_path in cls._root_paths_by_db.get(db, [])
31+
)
32+
33+
34+
class FastapiRequest(http.WebRequest):
35+
_request_type = "fastapi"
36+
37+
def __init__(self, *args):
38+
super().__init__(*args)
39+
self.params = {}
40+
self._dispatcher = FastApiDispatcher(self)
41+
42+
def make_response(self, data, headers=None, cookies=None, status=200):
43+
"""Helper for non-HTML responses, or HTML responses with custom
44+
response headers or cookies.
45+
46+
While handlers can just return the HTML markup of a page they want to
47+
send as a string if non-HTML data is returned they need to create a
48+
complete response object, or the returned data will not be correctly
49+
interpreted by the clients.
50+
51+
:param basestring data: response body
52+
:param headers: HTTP headers to set on the response
53+
:type headers: ``[(name, value)]``
54+
:param collections.abc.Mapping cookies: cookies to set on the client
55+
"""
56+
response = http.Response(data, status=status, headers=headers)
57+
if cookies:
58+
for k, v in cookies.items():
59+
response.set_cookie(k, v)
60+
return response
61+
62+
def make_json_response(self, data, headers=None, cookies=None, status=200):
63+
"""Helper for JSON responses, it json-serializes ``data`` and
64+
sets the Content-Type header accordingly if none is provided.
65+
66+
:param data: the data that will be json-serialized into the response body
67+
:param int status: http status code
68+
:param List[(str, str)] headers: HTTP headers to set on the response
69+
:param collections.abc.Mapping cookies: cookies to set on the client
70+
:rtype: :class:`~odoo.http.Response`
71+
"""
72+
data = json.dumps(data, ensure_ascii=False, default=date_utils.json_default)
73+
74+
headers = werkzeug.datastructures.Headers(headers)
75+
headers["Content-Length"] = len(data)
76+
if "Content-Type" not in headers:
77+
headers["Content-Type"] = "application/json; charset=utf-8"
78+
79+
return self.make_response(data, headers.to_wsgi_list(), cookies, status)
80+
81+
def dispatch(self):
82+
try:
83+
return self._dispatcher.dispatch(None, None)
84+
except Exception as e:
85+
return self._handle_exception(e)
86+
87+
def _handle_exception(self, exception):
88+
return self._dispatcher.handle_error(exception)
89+
90+
91+
ori_get_request = http.root.__class__.get_request
92+
93+
94+
def get_request(self, httprequest):
95+
db = httprequest.session.db
96+
if db and odoo.service.db.exp_db_exist(db):
97+
# on the very first request processed by a worker,
98+
# registry is not loaded yet
99+
# so we enforce its loading here.
100+
odoo.registry(db)
101+
if FastapiRootPaths.is_fastapi_path(db, httprequest.path):
102+
return FastapiRequest(httprequest)
103+
return ori_get_request(self, httprequest)
104+
105+
106+
http.root.__class__.get_request = get_request

fastapi_backport/models/__init__.py

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
from . import fastapi_endpoint
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# Copyright 2024 Akretion (http://www.akretion.com).
2+
# @author Florian Mounier <florian.mounier@akretion.com>
3+
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
4+
5+
6+
from odoo import api, models
7+
from ..http import FastapiRootPaths
8+
9+
10+
class FastapiEndpoint(models.Model):
11+
_inherit = "fastapi.endpoint"
12+
13+
@api.model
14+
def _update_root_paths_registry(self):
15+
root_paths = self.env["fastapi.endpoint"].search([]).mapped("root_path")
16+
FastapiRootPaths.set_root_paths(self.env.cr.dbname, root_paths)
17+
18+
def _register_hook(self):
19+
super()._register_hook()
20+
self._update_root_paths_registry()
21+
22+
def _inverse_root_path(self):
23+
super()._inverse_root_path()
24+
self._update_root_paths_registry()

0 commit comments

Comments
 (0)