From b6ca6de739846338721326d28bd4a90d328af14c Mon Sep 17 00:00:00 2001 From: PicchiSeba Date: Mon, 26 Oct 2026 12:31:27 +0100 Subject: [PATCH] [IMP]fastapi: expose FastAPI docs only if field is set --- fastapi/README.rst | 12 +-- fastapi/models/fastapi_endpoint.py | 8 +- fastapi/readme/USAGE.md | 6 ++ fastapi/static/description/index.html | 105 +++++++++++++------------- fastapi/tests/test_fastapi.py | 42 +++++++++++ fastapi/views/fastapi_endpoint.xml | 1 + 6 files changed, 114 insertions(+), 60 deletions(-) diff --git a/fastapi/README.rst b/fastapi/README.rst index 7c164592c..f7c133f41 100644 --- a/fastapi/README.rst +++ b/fastapi/README.rst @@ -1,7 +1,3 @@ -.. image:: https://odoo-community.org/readme-banner-image - :target: https://odoo-community.org/get-involved?utm_source=readme - :alt: Odoo Community Association - ============ Odoo FastAPI ============ @@ -17,7 +13,7 @@ Odoo FastAPI .. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png :target: https://odoo-community.org/page/development-status :alt: Beta -.. |badge2| image:: https://img.shields.io/badge/license-LGPL--3-blue.png +.. |badge2| image:: https://img.shields.io/badge/licence-LGPL--3-blue.png :target: http://www.gnu.org/licenses/lgpl-3.0-standalone.html :alt: License: LGPL-3 .. |badge3| image:: https://img.shields.io/badge/github-OCA%2Frest--framework-lightgray.png?logo=github @@ -263,6 +259,12 @@ Now, you can start your Odoo server, install your addon and create a new endpoint instance for your app. Once it's done click on the docs url to access the interactive documentation of your app. +**Note**: FastAPI automatically exposes three services to easily +interact with the APIs and their structure (Swagger, Redoc, OpenAPI). +When exposing your Odoo instance to the open internet you might want to +disable these services. To do so toggle the "Expose FastAPI docs" +checkbox in the FastAPI Endpoint form view. + Before trying to test your app, you need to define on the endpoint instance the user that will be used to run the app. You can do it by setting the **'user_id'** field. This information is the most important diff --git a/fastapi/models/fastapi_endpoint.py b/fastapi/models/fastapi_endpoint.py index 660bb2c5d..a907ceadc 100644 --- a/fastapi/models/fastapi_endpoint.py +++ b/fastapi/models/fastapi_endpoint.py @@ -65,6 +65,7 @@ class FastapiEndpoint(models.Model): "unexpecteed disk space consumption.", default=True, ) + expose_doc_urls = fields.Boolean("Expose FastAPI docs", default=True) @api.depends("root_path") def _compute_root_path(self): @@ -150,7 +151,7 @@ def _handle_route_updates(self, vals): @api.model def _fastapi_app_fields(self) -> list[str]: """The list of fields requiring to refresh the fastapi app if modified""" - return [] + return ["expose_doc_urls"] def _make_routing_rule(self, options=None): """Generator of rule""" @@ -321,12 +322,15 @@ def _get_app_dependencies_overrides(self) -> dict[Callable, Callable]: def _prepare_fastapi_app_params(self) -> dict[str, Any]: """Return the params to pass to the Fast API app constructor""" - return { + to_return = { "title": self.name, "description": self.description, "middleware": self._get_fastapi_app_middlewares(), "dependencies": self._get_fastapi_app_dependencies(), } + if not self.expose_doc_urls: + to_return |= {"docs_url": None, "redoc_url": None, "openapi_url": None} + return to_return def _get_fastapi_routers(self) -> list[APIRouter]: """Return the api routers to use for the instance. diff --git a/fastapi/readme/USAGE.md b/fastapi/readme/USAGE.md index 131d7f7b5..c53513a9d 100644 --- a/fastapi/readme/USAGE.md +++ b/fastapi/readme/USAGE.md @@ -178,6 +178,12 @@ Now, you can start your Odoo server, install your addon and create a new endpoint instance for your app. Once it's done click on the docs url to access the interactive documentation of your app. +**Note**: FastAPI automatically exposes three services to easily +interact with the APIs and their structure (Swagger, Redoc, OpenAPI). +When exposing your Odoo instance to the open internet you might want to +disable these services. To do so toggle the "Expose FastAPI docs" +checkbox in the FastAPI Endpoint form view. + Before trying to test your app, you need to define on the endpoint instance the user that will be used to run the app. You can do it by setting the **'user_id'** field. This information is the most important diff --git a/fastapi/static/description/index.html b/fastapi/static/description/index.html index 9a7aefbae..446842b45 100644 --- a/fastapi/static/description/index.html +++ b/fastapi/static/description/index.html @@ -360,21 +360,16 @@ -
+
+

Odoo FastAPI

- - -Odoo Community Association - -
-

Odoo FastAPI

-

Beta License: LGPL-3 OCA/rest-framework Translate me on Weblate Try me on Runboat

+

Beta License: LGPL-3 OCA/rest-framework Translate me on Weblate Try me on Runboat

This addon provides the basis to smoothly integrate the FastAPI framework into Odoo.

This integration allows you to use all the goodies from @@ -487,9 +482,9 @@

Odoo FastAPI

-

Usage

+

Usage

-

Dealing with the odoo environment

+

Dealing with the odoo environment

The ‘odoo.addons.fastapi.dependencies’ module provides a set of functions that you can use to inject reusable dependencies into your routes. For example, the ‘odoo_env’ function returns the current @@ -756,7 +756,7 @@

Dealing with the odoo environment dependency for all the methods that are not public.

-

The dependency injection mechanism

+

The dependency injection mechanism

The ‘odoo_env’ dependency relies on a simple implementation that retrieves the current odoo environment from ContextVar variable initialized at the start of the request processing by the specific @@ -809,7 +809,7 @@

The dependency injection mechanis implementation could depend on the endpoint configuration.

-

The authentication mechanism

+

The authentication mechanism

To make our app not tightly coupled with a specific authentication mechanism, we will use the ‘authenticated_partner’ dependency. As for the ‘fastapi_endpoint’ this dependency depends on an abstract @@ -974,7 +974,7 @@

The authentication mechanism< github

-

Managing configuration parameters for your app

+

Managing configuration parameters for your app

As we have seen in the previous section, you can add configuration fields on the fastapi endpoint model to allow the user to configure your app (as for any odoo model you extend). When you need to access these @@ -1040,7 +1040,7 @@

Managing configuration parameters

-

Dealing with languages

+

Dealing with languages

The fastapi addon parses the Accept-Language header of the request to determine the language to use. This parsing is done by respecting the RFC 7231 @@ -1055,7 +1055,7 @@

Dealing with languages

app to instruct the api consumers how to request a specific language.

-

How to extend an existing app

+

How to extend an existing app

When you develop a fastapi app, in a native python app it’s not possible to extend an existing one. This limitation doesn’t apply to the fastapi addon because the fastapi endpoint model is designed to be extended. @@ -1084,7 +1084,7 @@

How to extend an existing app

Let’s see how to do that.

-

Changing the implementation of the route handler

+

Changing the implementation of the route handler

Let’s say that you want to change the implementation of the route handler ‘/demo/echo’. Since a route handler is just a python method, it could seems a tedious task since we are not into a model method and @@ -1152,7 +1152,7 @@

Changing the implementation of th implementation to this method.

-

Overriding the dependencies of the route handler

+

Overriding the dependencies of the route handler

As you’ve previously seen, the dependency injection mechanism of fastapi is very powerful. By designing your route handler to rely on dependencies with a specific functional scope, you can easily change the @@ -1165,7 +1165,7 @@

Overriding the dependencies of t ‘odoo/addons/fastapi/models/fastapi_endpoint_demo.py’)

-

Adding a new route handler

+

Adding a new route handler

Let’s say that you want to add a new route handler ‘/demo/echo2’. You could be tempted to add this new route handler in your new addons by importing the router of the existing app and adding the new route @@ -1223,7 +1223,7 @@

Adding a new route handler

-

Extending the model used as parameter or as response of the route handler

+

Extending the model used as parameter or as response of the route handler

The fastapi python library uses the pydantic library to define the models. By default, once a model is defined, it’s not possible to extend it. However, a companion python library called @@ -1294,7 +1294,7 @@

Extending the model used as para

-

Managing security into the route handlers

+

Managing security into the route handlers

By default the route handlers are processed using the user configured on the ‘fastapi.endpoint’ model instance. (default is the Public user). You have seen previously how to define a dependency that will be used to @@ -1367,7 +1367,7 @@

Managing security into the route

-

How to test your fastapi app

+

How to test your fastapi app

Thanks to the starlette test client, it’s possible to test your fastapi app in a very simple way. With the test client, you can call your route handlers as if they were real http endpoints. The test client is @@ -1438,7 +1438,7 @@

How to test your fastapi app

-

Overall considerations when you develop an fastapi app

+

Overall considerations when you develop an fastapi app

Developing a fastapi app requires to follow some good practices to ensure that the app is robust and easy to maintain. Here are some of them:

@@ -1563,9 +1563,9 @@

Overall considerations when you useful.

-

Miscellaneous

+

Miscellaneous

-

Development of a search route handler

+

Development of a search route handler

The ‘odoo-addon-fastapi’ module provides 2 useful piece of code to help you be consistent when writing a route handler for a search route.

    @@ -1614,7 +1614,7 @@

    Development of a search route ha ‘odoo-addon-fastapi’ module and the ‘odoo-addon-extendable’

-

Error handling

+

Error handling

The error handling is a very important topic in the design of the fastapi integration with odoo. By default, when instantiating the fastapi app, the fastapi library declare a default exception handler @@ -1634,7 +1634,7 @@

Error handling

handler in your app, it will be ignored.

-

FastAPI addons directory structure

+

FastAPI addons directory structure

When you develop a new addon to expose an api with fastapi, it’s a good practice to follow the same directory structure and naming convention for the files related to the api. It will help you to easily find the @@ -1743,7 +1743,7 @@

FastAPI addons directory structu

-

What’s next?

+

What’s next?

The ‘odoo-addon-fastapi’ module is still in its early stage of development. It will evolve over time to integrate your feedback and to provide the missing features. It’s now up to you to try it and to @@ -1751,7 +1751,7 @@

What’s next?

-

Changelog

+

Changelog

-

17.0.3.0.0 (2024-10-03)

+

17.0.3.0.0 (2024-10-03)

-

Features

+

Features

    • A new parameter is now available on the endpoint model to let you @@ -1785,7 +1785,7 @@

      Features

-

Bugfixes

+

Bugfixes

  • Fix issue with the retry of a POST request with a body content.

    Prior to this fix the retry of a POST request with a body content @@ -1798,9 +1798,9 @@

    Bugfixes

-

17.0.2.0.0 (2024-10-03)

+

17.0.2.0.0 (2024-10-03)

-

Bugfixes

+

Bugfixes

  • This change is a complete rewrite of the way the transactions are managed when integrating a fastapi application into Odoo.

    @@ -1835,9 +1835,9 @@

    Bugfixes

-

17.0.1.0.1 (2024-10-02)

+

17.0.1.0.1 (2024-10-02)

-

Bugfixes

+

Bugfixes

  • Fix compatibility issues with the latest Odoo version

    From @@ -1852,9 +1852,9 @@

    Bugfixes

-

16.0.1.2.5 (2024-01-17)

+

16.0.1.2.5 (2024-01-17)

-

Bugfixes

+

Bugfixes

  • Odoo has done an update and now, it checks domains of ir.rule on creation and modification.

    @@ -1868,9 +1868,9 @@

    Bugfixes

-

16.0.1.2.3 (2023-12-21)

+

16.0.1.2.3 (2023-12-21)

-

Bugfixes

+

Bugfixes

  • In case of exception in endpoint execution, close the database cursor after rollback.

    @@ -1882,9 +1882,9 @@

    Bugfixes

-

16.0.1.2.2 (2023-12-12)

+

16.0.1.2.2 (2023-12-12)

-

Bugfixes

+

Bugfixes

  • When using the ‘FastAPITransactionCase’ class, allows to specify a specific override of the ‘authenticated_partner_impl’ method into the @@ -1901,9 +1901,9 @@

    Bugfixes

-

16.0.1.2.1 (2023-11-03)

+

16.0.1.2.1 (2023-11-03)

-

Bugfixes

+

Bugfixes

  • Fix a typo in the Field declaration of the ‘count’ attribute of the ‘PagedCollection’ schema.

    @@ -1915,9 +1915,9 @@

    Bugfixes

-

16.0.1.2.0 (2023-10-13)

+

16.0.1.2.0 (2023-10-13)

-

Features

+

Features

  • The field total in the PagedCollection schema is replaced by the field count. The field total is now deprecated and will be removed @@ -1933,7 +1933,7 @@

    Features

-

Bug Tracker

+

Bug Tracker

Bugs are tracked on GitHub Issues. In case of trouble, please check there if your issue has already been reported. If you spotted it first, help us to smash it by providing a detailed and welcomed @@ -1941,22 +1941,22 @@

Bug Tracker

Do not contact contributors directly about support or help with technical issues.

-
diff --git a/fastapi/tests/test_fastapi.py b/fastapi/tests/test_fastapi.py index 35fa7ac99..44c252aa6 100644 --- a/fastapi/tests/test_fastapi.py +++ b/fastapi/tests/test_fastapi.py @@ -4,6 +4,7 @@ import os import unittest from contextlib import contextmanager +from pathlib import Path from odoo import sql_db from odoo.tests.common import HttpCase @@ -45,6 +46,17 @@ def _assert_expected_lang(self, accept_language, expected_lang): self.assertEqual(response.status_code, 200) self.assertEqual(response.content, expected_lang) + def endpoint_path(self, endpoint, path): + """Properly contatenate and endpoint's root_path and another path""" + if path[0] == "/": + # concatenating two paths where the second one starts with a '/' makes the + # resulting path exactly the same as this second path + # eg. Path("/root") / Path("/endpoint") -> PosixPath("/endpoint") + # We want to avoid that, we should get a result like + # -> PosixPath("/root/endpoint") + path = path[1:] + return str(Path(endpoint.root_path) / path) + def test_call(self): route = "/fastapi_demo/demo/" response = self.url_open(route) @@ -79,6 +91,36 @@ def test_retrying_post(self): self.assertEqual(response.status_code, 200) self.assertDictEqual(response.json(), {"retries": nbr_retries, "file": "test"}) + def test_expose_docs(self): + self.assertNotIn( + "docs_url", self.fastapi_demo_app._prepare_fastapi_app_params().keys() + ) + response = self.url_open( + self.endpoint_path(self.fastapi_demo_app, "/docs"), timeout=20 + ) + self.assertEqual(response.status_code, 200) + + unexposed_endpoint = self.env["fastapi.endpoint"].create( + { + "name": "Test Endpoint - non exposed", + "root_path": "/test-endpoint/", + "app": "demo", + "demo_auth_method": "api_key", + "expose_doc_urls": False, + } + ) + self.assertIn( + "docs_url", unexposed_endpoint._prepare_fastapi_app_params().keys() + ) + self.assertEqual( + unexposed_endpoint._prepare_fastapi_app_params().get("docs_url"), None + ) + unexposed_endpoint._handle_registry_sync() + response = self.url_open( + self.endpoint_path(unexposed_endpoint, "/docs"), timeout=20 + ) + self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) + @mute_logger("odoo.http") def assert_exception_processed( self, diff --git a/fastapi/views/fastapi_endpoint.xml b/fastapi/views/fastapi_endpoint.xml index 77b04fd9b..af516a0b0 100644 --- a/fastapi/views/fastapi_endpoint.xml +++ b/fastapi/views/fastapi_endpoint.xml @@ -49,6 +49,7 @@ +