Skip to content

Commit

Permalink
[ADD] fastapi_backport
Browse files Browse the repository at this point in the history
  • Loading branch information
paradoxxxzero committed Jun 18, 2024
1 parent fb6b727 commit 17d8822
Show file tree
Hide file tree
Showing 7 changed files with 203 additions and 0 deletions.
32 changes: 32 additions & 0 deletions fastapi_backport/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Copyright 2024 Akretion (http://www.akretion.com).
# @author Florian Mounier <[email protected]>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).

import logging
from . import models
from . import http
from odoo import SUPERUSER_ID, api
from odoo.addons.extendable.models.ir_http import IrHttp
from odoo.addons.fastapi.tests.common import FastAPITransactionCase
from odoo.tests.common import SavepointCase

_logger = logging.getLogger(__name__)

FastAPITransactionCase.__bases__ = (SavepointCase,)


@classmethod
def _dispatch(cls):
with cls._extendable_context_registry():
return super(IrHttp, cls)._dispatch()


IrHttp._dispatch = _dispatch


def post_init_hook(cr, registry):
env = api.Environment(cr, SUPERUSER_ID, {})
# this is the trigger that sends notifications when jobs change
_logger.info("Resyncing registries")
endpoints_ids = env["fastapi.endpoint"].search([]).ids
env["fastapi.endpoint"]._handle_registry_sync(endpoints_ids)
20 changes: 20 additions & 0 deletions fastapi_backport/__manifest__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Copyright 2024 Akretion (http://www.akretion.com).
# @author Florian Mounier <[email protected]>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).

{
"name": "Fastapi Backport",
"summary": "Backport of FastAPI to Odoo 14.0",
"version": "14.0.1.0.0",
"author": " Akretion",
"license": "AGPL-3",
"depends": [
"sixteen_in_fourteen",
"base_future_response",
"fastapi",
"pydantic",
"extendable_fastapi",
"extendable",
],
"post_init_hook": "post_init_hook",
}
110 changes: 110 additions & 0 deletions fastapi_backport/http.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
# Copyright 2023 ACSONE SA/NV
# Copyright 2024 Akretion (http://www.akretion.com).
# @author Florian Mounier <[email protected]>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).

import json
import logging
from functools import lru_cache

import werkzeug.datastructures

import odoo
from odoo import http
from odoo.tools import date_utils

from odoo.addons.fastapi.fastapi_dispatcher import FastApiDispatcher

_logger = logging.getLogger(__name__)


class FastapiRootPaths:
_root_paths_by_db = {}

@classmethod
def set_root_paths(cls, db, root_paths):
cls._root_paths_by_db[db] = root_paths
cls.is_fastapi_path.cache_clear()

@classmethod
@lru_cache(maxsize=1024)
def is_fastapi_path(cls, db, path):
return any(
path.startswith(root_path)
for root_path in cls._root_paths_by_db.get(db, [])
)


class FastapiRequest(http.WebRequest):
_request_type = "fastapi"

def __init__(self, *args):
super().__init__(*args)
self.params = {}
self._dispatcher = FastApiDispatcher(self)

def make_response(self, data, headers=None, cookies=None, status=200):
"""Helper for non-HTML responses, or HTML responses with custom
response headers or cookies.
While handlers can just return the HTML markup of a page they want to
send as a string if non-HTML data is returned they need to create a
complete response object, or the returned data will not be correctly
interpreted by the clients.
:param basestring data: response body
:param headers: HTTP headers to set on the response
:type headers: ``[(name, value)]``
:param collections.abc.Mapping cookies: cookies to set on the client
"""
response = http.Response(data, status=status, headers=headers)
if cookies:
for k, v in cookies.items():
response.set_cookie(k, v)
return response

def make_json_response(self, data, headers=None, cookies=None, status=200):
"""Helper for JSON responses, it json-serializes ``data`` and
sets the Content-Type header accordingly if none is provided.
:param data: the data that will be json-serialized into the response body
:param int status: http status code
:param List[(str, str)] headers: HTTP headers to set on the response
:param collections.abc.Mapping cookies: cookies to set on the client
:rtype: :class:`~odoo.http.Response`
"""
data = json.dumps(data, ensure_ascii=False, default=date_utils.json_default)

headers = werkzeug.datastructures.Headers(headers)
headers["Content-Length"] = len(data)
if "Content-Type" not in headers:
headers["Content-Type"] = "application/json; charset=utf-8"

return self.make_response(data, headers.to_wsgi_list(), cookies, status)

def dispatch(self):
return self._dispatcher.dispatch(None, None)

def _handle_exception(self, exception):
_logger.exception(
"Exception during fastapi request handling", exc_info=exception
)
return self._dispatcher.handle_error(exception)


ori_get_request = http.root.__class__.get_request


def get_request(self, httprequest):
db = httprequest.session.db
if db and odoo.service.db.exp_db_exist(db):
# on the very first request processed by a worker,
# registry is not loaded yet
# so we enforce its loading here.
odoo.registry(db)
if FastapiRootPaths.is_fastapi_path(db, httprequest.path):
return FastapiRequest(httprequest)
return ori_get_request(self, httprequest)


http.root.__class__.get_request = get_request
1 change: 1 addition & 0 deletions fastapi_backport/models/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from . import fastapi_endpoint
33 changes: 33 additions & 0 deletions fastapi_backport/models/fastapi_endpoint.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Copyright 2024 Akretion (http://www.akretion.com).
# @author Florian Mounier <[email protected]>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).


from odoo import api, models

from ..http import FastapiRootPaths


class FastapiEndpoint(models.Model):
_inherit = "fastapi.endpoint"

@api.model
def _update_root_paths_registry(self):
root_paths = self.env["fastapi.endpoint"].search([]).mapped("root_path")
FastapiRootPaths.set_root_paths(self.env.cr.dbname, root_paths)

def _register_hook(self):
super()._register_hook()
self._update_root_paths_registry()

def _inverse_root_path(self):
super()._inverse_root_path()
self._update_root_paths_registry()

@api.depends("root_path")
def _compute_urls(self):
base_url = self.env["ir.config_parameter"].sudo().get_param("web.base.url")
for rec in self:
rec.docs_url = f"{base_url}{rec.root_path}/docs"
rec.redoc_url = f"{base_url}{rec.root_path}/redoc"
rec.openapi_url = f"{base_url}{rec.root_path}/openapi.json"
1 change: 1 addition & 0 deletions setup/fastapi_backport/odoo/addons/fastapi_backport
6 changes: 6 additions & 0 deletions setup/fastapi_backport/setup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import setuptools

setuptools.setup(
setup_requires=['setuptools-odoo'],
odoo_addon=True,
)

0 comments on commit 17d8822

Please sign in to comment.