Skip to content

Commit 78e8805

Browse files
committed
[IMP]fastapi: enable multi-slash routes
1 parent e24654c commit 78e8805

5 files changed

Lines changed: 66 additions & 3 deletions

File tree

fastapi/demo/fastapi_endpoint_demo.xml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,4 +44,15 @@ methods. See documentation to learn more about how to create a new app.
4444
<field name="demo_auth_method">http_basic</field>
4545
<field name="user_id" ref="my_demo_app_user" />
4646
</record>
47+
48+
<record id="fastapi_endpoint_multislash_demo" model="fastapi.endpoint">
49+
<field name="name">Fastapi Multi-Slash Demo Endpoint</field>
50+
<field name="description">
51+
Like the other demo endpoint but with multi-slash
52+
</field>
53+
<field name="app">demo</field>
54+
<field name="root_path">/fastapi/demo</field>
55+
<field name="demo_auth_method">http_basic</field>
56+
<field name="user_id" ref="my_demo_app_user" />
57+
</record>
4758
</odoo>

fastapi/fastapi_dispatcher.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ def dispatch(self, endpoint, args):
2727
# don't parse the httprequest let starlette parse the stream
2828
self.request.params = {} # dict(self.request.get_http_params(), **args)
2929
environ = self._get_environ()
30-
root_path = "/" + environ["PATH_INFO"].split("/")[1]
30+
root_path = environ["PATH_INFO"]
3131
# TODO store the env into contextvar to be used by the odoo_env
3232
# depends method
3333
with fastapi_app_pool.get_app(env=request.env, root_path=root_path) as app:

fastapi/models/fastapi_endpoint.py

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -210,9 +210,24 @@ def _reset_app_cache_marker(self):
210210
to all the running instances.
211211
"""
212212

213+
@api.model
214+
def get_endpoint(self, root_path):
215+
# try to match the request url with the most similar endpoint
216+
endpoints_by_length = self.search([]).sorted(
217+
lambda fe: len(fe.root_path), reverse=True
218+
)
219+
endpoint = False
220+
while endpoints_by_length:
221+
candidate_endpoint = endpoints_by_length[0]
222+
if root_path.startswith(candidate_endpoint.root_path):
223+
endpoint = candidate_endpoint
224+
break
225+
endpoints_by_length -= candidate_endpoint
226+
return endpoint
227+
213228
@api.model
214229
def get_app(self, root_path):
215-
record = self.search([("root_path", "=", root_path)])
230+
record = self.get_endpoint(root_path)
216231
if not record:
217232
return None
218233
app = FastAPI()
@@ -238,7 +253,7 @@ def _clear_fastapi_exception_handlers(self, app: FastAPI) -> None:
238253
@api.model
239254
@tools.ormcache("root_path")
240255
def get_uid(self, root_path):
241-
record = self.search([("root_path", "=", root_path)])
256+
record = self.get_endpoint(root_path)
242257
if not record:
243258
return None
244259
return record.user_id.id

fastapi/tests/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
from . import test_fastapi
22
from . import test_fastapi_demo
3+
from . import test_fastapi_multislash_demo
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# License LGPL-3.0 or later (http://www.gnu.org/licenses/LGPL).
2+
3+
4+
from requests import Response
5+
6+
from fastapi import status
7+
8+
from ..routers import demo_router
9+
from .common import FastAPITransactionCase
10+
11+
12+
class FastAPIMultiSlashDemoCase(FastAPITransactionCase):
13+
"""The fastapi lib comes with a useful testclient that let's you
14+
easily test your endpoints. Moreover, the dependency overrides functionality
15+
allows you to provide specific implementation for part of the code to avoid
16+
to rely on some tricky http stuff for example: authentication
17+
18+
This test class is an example on how you can test your own code
19+
"""
20+
21+
@classmethod
22+
def setUpClass(cls) -> None:
23+
super().setUpClass()
24+
cls.default_fastapi_router = demo_router
25+
cls.default_fastapi_running_user = cls.env.ref(
26+
"fastapi.fastapi_endpoint_multislash_demo"
27+
)
28+
cls.default_fastapi_authenticated_partner = cls.env["res.partner"].create(
29+
{"name": "FastAPI Demo"}
30+
)
31+
32+
def test_hello_world(self) -> None:
33+
with self._create_test_client() as test_client:
34+
response: Response = test_client.get("/demo/")
35+
self.assertEqual(response.status_code, status.HTTP_200_OK)
36+
self.assertDictEqual(response.json(), {"Hello": "World"})

0 commit comments

Comments
 (0)