diff --git a/fastapi/fastapi_dispatcher.py b/fastapi/fastapi_dispatcher.py index 79b80b753..27faee852 100644 --- a/fastapi/fastapi_dispatcher.py +++ b/fastapi/fastapi_dispatcher.py @@ -28,10 +28,13 @@ def dispatch(self, endpoint, args): self.request.params = {} # dict(self.request.get_http_params(), **args) environ = self._get_environ() path = environ["PATH_INFO"] + endpoint_model = request.env["fastapi.endpoint"].sudo() + # Ensure that only one app exists by endpoint's root path + root_path = endpoint_model._get_root_path(path) or path # TODO store the env into contextvar to be used by the odoo_env # depends method - with fastapi_app_pool.get_app(env=request.env, root_path=path) as app: - uid = request.env["fastapi.endpoint"].sudo().get_uid(path) + with fastapi_app_pool.get_app(env=request.env, root_path=root_path) as app: + uid = endpoint_model.get_uid(root_path) data = BytesIO() with self._manage_odoo_env(uid): for r in app(environ, self._make_response): diff --git a/fastapi/models/fastapi_endpoint.py b/fastapi/models/fastapi_endpoint.py index 388e56a6d..ee3c94424 100644 --- a/fastapi/models/fastapi_endpoint.py +++ b/fastapi/models/fastapi_endpoint.py @@ -266,6 +266,17 @@ def _get_id_for_path(self, path): root_path = self._find_first_matching_url_path(id_by_path.keys(), path) return id_by_path.get(root_path) + @api.model + def _get_root_path(self, path): + """Return the root path of the endpoint that serves the given path. + + Not cached on the path: a request path can contain anything, so caching + on it would keep one entry per distinct path. + """ + return self._find_first_matching_url_path( + self._get_id_by_root_path_map().keys(), path + ) + @api.model def _get_endpoint(self, path): id_ = self._get_id_for_path(path) diff --git a/fastapi/tests/test_fastapi.py b/fastapi/tests/test_fastapi.py index e074325e0..7525372ec 100644 --- a/fastapi/tests/test_fastapi.py +++ b/fastapi/tests/test_fastapi.py @@ -11,6 +11,7 @@ from fastapi import status +from ..pools import fastapi_app_pool from ..schemas import DemoExceptionType @@ -195,6 +196,17 @@ def test_url_matching(self): "/fastapi/v1", ) + def test_app_pool_keyed_on_root_path(self): + # Calls to endpoints under the same root path are processed by the same + # app: only one app is initialized by root path + for route in ("/fastapi_demo/demo", "/fastapi_demo/demo/lang"): + response = self.url_open(route, timeout=20) + self.assertEqual(response.status_code, 200) + pools = fastapi_app_pool._queue_by_db_by_root_path[self.env.cr.dbname] + self.assertIn(self.fastapi_demo_app.root_path, pools) + self.assertNotIn("/fastapi_demo/demo", pools) + self.assertNotIn("/fastapi_demo/demo/lang", pools) + def test_multi_slash(self): route = "/fastapi/demo-multi/demo/" response = self.url_open(route, timeout=20)