From ead99332c460dda7d233fb859d592346e2f3c59e Mon Sep 17 00:00:00 2001
From: Raf Ven
Date: Tue, 7 Oct 2025 10:47:33 +0200
Subject: [PATCH 1/5] [MIG] fastapi: Migration to 19.0
---
.pre-commit-config.yaml | 1 -
fastapi/README.rst | 633 ++++++++++++------------
fastapi/__manifest__.py | 4 +-
fastapi/demo/fastapi_endpoint_demo.xml | 9 +-
fastapi/dependencies.py | 31 +-
fastapi/error_handlers.py | 2 +-
fastapi/models/fastapi_endpoint.py | 4 +-
fastapi/models/fastapi_endpoint_demo.py | 8 +-
fastapi/routers/demo_router.py | 4 +-
fastapi/security/ir_rule+acl.xml | 15 +-
fastapi/security/res_groups.xml | 20 +-
fastapi/static/description/index.html | 197 ++++----
fastapi/tests/common.py | 12 +-
fastapi/tests/test_fastapi.py | 8 +-
fastapi/tests/test_fastapi_demo.py | 25 +-
fastapi/views/fastapi_endpoint.xml | 2 +-
fastapi/views/fastapi_menu.xml | 2 +-
requirements.txt | 7 -
18 files changed, 512 insertions(+), 472 deletions(-)
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index e638ae70e..367dea5f5 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -5,7 +5,6 @@ exclude: |
^base_rest_auth_api_key/|
^base_rest_pydantic/|
^extendable/|
- ^fastapi/|
^pydantic/|
^rest_log/|
# END NOT INSTALLABLE ADDONS
diff --git a/fastapi/README.rst b/fastapi/README.rst
index 3fcc9cf45..ecdb6f0dc 100644
--- a/fastapi/README.rst
+++ b/fastapi/README.rst
@@ -21,13 +21,13 @@ Odoo FastAPI
: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
- :target: https://github.com/OCA/rest-framework/tree/18.0/fastapi
+ :target: https://github.com/OCA/rest-framework/tree/19.0/fastapi
:alt: OCA/rest-framework
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
- :target: https://translation.odoo-community.org/projects/rest-framework-18-0/rest-framework-18-0-fastapi
+ :target: https://translation.odoo-community.org/projects/rest-framework-19-0/rest-framework-19-0-fastapi
:alt: Translate me on Weblate
.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png
- :target: https://runboat.odoo-community.org/builds?repo=OCA/rest-framework&target_branch=18.0
+ :target: https://runboat.odoo-community.org/builds?repo=OCA/rest-framework&target_branch=19.0
:alt: Try me on Runboat
|badge1| |badge2| |badge3| |badge4| |badge5|
@@ -91,44 +91,44 @@ let's you keep advantage of the fastapi framework and use it with Odoo.
Before you start, we must define some terms:
-- **App**: A FastAPI app is a collection of routes, dependencies, and
- other components that can be used to build a web application.
-- **Router**: A router is a collection of routes that can be mounted in
- an app.
-- **Route**: A route is a mapping between an HTTP method and a path, and
- defines what should happen when the user requests that path.
-- **Dependency**: A dependency is a callable that can be used to get
- some information from the user request, or to perform some actions
- before the request handler is called.
-- **Request**: A request is an object that contains all the information
- sent by the user's browser as part of an HTTP request.
-- **Response**: A response is an object that contains all the
- information that the user's browser needs to build the result page.
-- **Handler**: A handler is a function that takes a request and returns
- a response.
-- **Middleware**: A middleware is a function that takes a request and a
- handler, and returns a response.
+- **App**: A FastAPI app is a collection of routes, dependencies, and
+ other components that can be used to build a web application.
+- **Router**: A router is a collection of routes that can be mounted in
+ an app.
+- **Route**: A route is a mapping between an HTTP method and a path,
+ and defines what should happen when the user requests that path.
+- **Dependency**: A dependency is a callable that can be used to get
+ some information from the user request, or to perform some actions
+ before the request handler is called.
+- **Request**: A request is an object that contains all the information
+ sent by the user's browser as part of an HTTP request.
+- **Response**: A response is an object that contains all the
+ information that the user's browser needs to build the result page.
+- **Handler**: A handler is a function that takes a request and returns
+ a response.
+- **Middleware**: A middleware is a function that takes a request and a
+ handler, and returns a response.
The FastAPI framework is based on the following principles:
-- **Fast**: Very high performance, on par with NodeJS and Go (thanks to
- Starlette and Pydantic). [One of the fastest Python frameworks
- available]
-- **Fast to code**: Increase the speed to develop features by about 200%
- to 300%.
-- **Fewer bugs**: Reduce about 40% of human (developer) induced errors.
-- **Intuitive**: Great editor support. Completion everywhere. Less time
- debugging.
-- **Easy**: Designed to be easy to use and learn. Less time reading
- docs.
-- **Short**: Minimize code duplication. Multiple features from each
- parameter declaration. Fewer bugs.
-- **Robust**: Get production-ready code. With automatic interactive
- documentation.
-- **Standards-based**: Based on (and fully compatible with) the open
- standards for APIs: OpenAPI (previously known as Swagger) and JSON
- Schema.
-- **Open Source**: FastAPI is fully open-source, under the MIT license.
+- **Fast**: Very high performance, on par with NodeJS and Go (thanks to
+ Starlette and Pydantic). [One of the fastest Python frameworks
+ available]
+- **Fast to code**: Increase the speed to develop features by about
+ 200% to 300%.
+- **Fewer bugs**: Reduce about 40% of human (developer) induced errors.
+- **Intuitive**: Great editor support. Completion everywhere. Less time
+ debugging.
+- **Easy**: Designed to be easy to use and learn. Less time reading
+ docs.
+- **Short**: Minimize code duplication. Multiple features from each
+ parameter declaration. Fewer bugs.
+- **Robust**: Get production-ready code. With automatic interactive
+ documentation.
+- **Standards-based**: Based on (and fully compatible with) the open
+ standards for APIs: OpenAPI (previously known as Swagger) and JSON
+ Schema.
+- **Open Source**: FastAPI is fully open-source, under the MIT license.
The first step is to install the fastapi addon. You can do it with the
following command:
@@ -290,9 +290,9 @@ define the access rights of the user that will run your app. This group
should imply the predefined group **'FastAPI Endpoint Runner'**. This
group defines the minimum access rights that the user needs to:
-- access the endpoint instance it belongs to
-- access to its own user record
-- access to the partner record that is linked to its user record
+- access the endpoint instance it belongs to
+- access to its own user record
+- access to the partner record that is linked to its user record
.. code:: xml
@@ -350,12 +350,12 @@ function, the only way to get access to the odoo environment is to
inject it as a dependency. The fastapi addon provides a set of function
that can be used as dependencies:
-- **'odoo_env'**: Returns the current odoo environment.
-- **'fastapi_endpoint'**: Returns the current fastapi endpoint model
- instance.
-- **'authenticated_partner'**: Returns the authenticated partner.
-- **'authenticated_partner_env'**: Returns the current odoo environment
- with the authenticated_partner_id into the context.
+- **'odoo_env'**: Returns the current odoo environment.
+- **'fastapi_endpoint'**: Returns the current fastapi endpoint model
+ instance.
+- **'authenticated_partner'**: Returns the authenticated partner.
+- **'authenticated_partner_env'**: Returns the current odoo environment
+ with the authenticated_partner_id into the context.
By default, the **'odoo_env'** and **'fastapi_endpoint'** dependencies
are available without extra work.
@@ -731,11 +731,11 @@ change the contract, you must create a new route.
What can you change?
-- You can change the implementation of the route handler.
-- You can override the dependencies of the route handler.
-- You can add a new route handler.
-- You can extend the model used as parameter or as response of the route
- handler.
+- You can change the implementation of the route handler.
+- You can override the dependencies of the route handler.
+- You can add a new route handler.
+- You can extend the model used as parameter or as response of the
+ route handler.
Let's see how to do that.
@@ -994,23 +994,23 @@ As briefly introduced in a previous section, a good practice when you
develop a fastapi app and you want to protect your data in an efficient
and traceable way is to:
-- create a new user specific to the app but with any access rights.
-- create a security group specific to the app and add the user to this
- group. (This group must implies the group 'AFastAPI Endpoint Runner'
- that give the minimal access rights)
-- for each model you want to protect:
+- create a new user specific to the app but with any access rights.
+- create a security group specific to the app and add the user to this
+ group. (This group must implies the group 'AFastAPI Endpoint Runner'
+ that give the minimal access rights)
+- for each model you want to protect:
- - add a 'ir.model.access' record for the model to allow read access to
- your model and add the group to the record.
- - create a new 'ir.rule' record for the model that restricts the
- access to the records of the model to the authenticated partner by
- using the key 'authenticated_partner_id' in domain of the rule. (or
- to the user defined on the 'fastapi.endpoint' model instance if the
- method is public)
+ - add a 'ir.model.access' record for the model to allow read access
+ to your model and add the group to the record.
+ - create a new 'ir.rule' record for the model that restricts the
+ access to the records of the model to the authenticated partner by
+ using the key 'authenticated_partner_id' in domain of the rule.
+ (or to the user defined on the 'fastapi.endpoint' model instance
+ if the method is public)
-- add a dependency on the 'authenticated_partner' to your handlers when
- you need to access the authenticated partner or ensure that the
- service is called by an authenticated partner.
+- add a dependency on the 'authenticated_partner' to your handlers when
+ you need to access the authenticated partner or ensure that the
+ service is called by an authenticated partner.
.. code:: xml
@@ -1134,111 +1134,111 @@ 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:
-- A route handler must be as simple as possible. It must not contain any
- business logic. The business logic must be implemented into the
- service layer. The route handler must only call the service layer and
- return the result of the service layer. To ease extension on your
- business logic, your service layer can be implemented as an odoo
- abstract model that can be inherited by other addons.
-- A route handler should not expose the internal data structure and api
- of Odoo. It should provide the api that is needed by the client. More
- widely, an app provides a set of services that address a set of use
- cases specific to a well defined functional domain. You must always
- keep in mind that your api will remain the same for a long time even
- if you upgrade your odoo version of modify your business logic.
-- A route handler is a transactional unit of work. When you design your
- api you must ensure that the completeness of a use case is guaranteed
- by a single transaction. If you need to perform several transactions
- to complete a use case, you introduce a risk of inconsistency in your
- data or extra complexity in your client code.
-- Properly handle the errors. The route handler must return a proper
- error response when an error occurs. The error response must be
- consistent with the rest of the api. The error response must be
- documented in the api documentation. By default, the
- **'odoo-addon-fastapi'** module handles the common exception types
- defined in the **'odoo.exceptions'** module and returns a proper error
- response with the corresponding http status code. An error in the
- route handler must always return an error response with a http status
- code different from 200. The error response must contain a human
- readable message that can be displayed to the user. The error response
- can also contain a machine readable code that can be used by the
- client to handle the error in a specific way.
-- When you design your json document through the pydantic models, you
- must use the appropriate data types. For example, you must use the
- data type **'datetime.date'** to represent a date and not a string.
- You must also properly define the constraints on the fields. For
- example, if a field is optional, you must use the data type
- **'typing.Optional'**. `pydantic `__
- provides everything you need to properly define your json document.
-- Always use an appropriate pydantic model as request and/or response
- for your route handler. Constraints on the fields of the pydantic
- model must apply to the specific use case. For example, if your route
- handler is used to create a sale order, the pydantic model must not
- contain the field 'id' because the id of the sale order will be
- generated by the route handler. But if the id is required afterwords,
- the pydantic model for the response must contain the field 'id' as
- required.
-- Uses descriptive property names in your json documents. For example,
- avoid the use of documents providing a flat list of key value pairs.
-- Be consistent in the naming of your fields into your json documents.
- For example, if you use 'id' to represent the id of a sale order, you
- must use 'id' to represent the id of all the other objects.
-- Be consistent in the naming style of your fields. Always prefer
- underscore to camel case.
-- Always use plural for the name of the fields that contain a list of
- items. For example, if you have a field 'lines' that contains a list
- of sale order lines, you must use 'lines' and not 'line'.
-- You can't expect that a client will provide you the identifier of a
- specific record in odoo (for example the id of a carrier) if you don't
- provide a specific route handler to retrieve the list of available
- records. Sometimes, the client must share with odoo the identity of a
- specific record to be able to perform an appropriate action specific
- to this record (for example, the processing of a payment is different
- for each payment acquirer). In this case, you must provide a specific
- attribute that allows both the client and odoo to identify the record.
- The field 'provider' on a payment acquirer allows you to identify a
- specific record in odoo. This kind of approach allows both the client
- and odoo to identify the record without having to rely on the id of
- the record. (This will ensure that the client will not break if the id
- of the record is changed in odoo for example when tests are run on an
- other database).
-- Always use the same name for the same kind of object. For example, if
- you have a field 'lines' that contains a list of sale order lines, you
- must use the same name for the same kind of object in all the other
- json documents.
-- Manage relations between objects in your json documents the same way.
- By default, you should return the id of the related object in the json
- document. But this is not always possible or convenient, so you can
- also return the related object in the json document. The main
- advantage of returning the id of the related object is that it allows
- you to avoid the `n+1
- problem `__ . The main
- advantage of returning the related object in the json document is that
- it allows you to avoid an extra call to retrieve the related object.
- By keeping in mind the pros and cons of each approach, you can choose
- the best one for your use case. Once it's done, you must be consistent
- in the way you manage the relations of the same object.
-- It's not always a good idea to name your fields into your json
- documents with the same name as the fields of the corresponding odoo
- model. For example, in your document representing a sale order, you
- must not use the name 'order_line' for the field that contains the
- list of sale order lines. The name 'order_line' in addition to being
- confusing and not consistent with the best practices, is not
- auto-descriptive. The name 'lines' is much better.
-- Keep a defensive programming approach. If you provide a route handler
- that returns a list of records, you must ensure that the computation
- of the list is not too long or will not drain your server resources.
- For example, for search route handlers, you must ensure that the
- search is limited to a reasonable number of records by default.
-- As a corollary of the previous point, a search handler must always use
- the pagination mechanism with a reasonable default page size. The
- result list must be enclosed in a json document that contains the
- count of records into the system matching your search criteria and the
- list of records for the given page and size.
-- Use plural for the name of a service. For example, if you provide a
- service that allows you to manage the sale orders, you must use the
- name 'sale_orders' and not 'sale_order'.
-- ... and many more.
+- A route handler must be as simple as possible. It must not contain
+ any business logic. The business logic must be implemented into the
+ service layer. The route handler must only call the service layer and
+ return the result of the service layer. To ease extension on your
+ business logic, your service layer can be implemented as an odoo
+ abstract model that can be inherited by other addons.
+- A route handler should not expose the internal data structure and api
+ of Odoo. It should provide the api that is needed by the client. More
+ widely, an app provides a set of services that address a set of use
+ cases specific to a well defined functional domain. You must always
+ keep in mind that your api will remain the same for a long time even
+ if you upgrade your odoo version of modify your business logic.
+- A route handler is a transactional unit of work. When you design your
+ api you must ensure that the completeness of a use case is guaranteed
+ by a single transaction. If you need to perform several transactions
+ to complete a use case, you introduce a risk of inconsistency in your
+ data or extra complexity in your client code.
+- Properly handle the errors. The route handler must return a proper
+ error response when an error occurs. The error response must be
+ consistent with the rest of the api. The error response must be
+ documented in the api documentation. By default, the
+ **'odoo-addon-fastapi'** module handles the common exception types
+ defined in the **'odoo.exceptions'** module and returns a proper
+ error response with the corresponding http status code. An error in
+ the route handler must always return an error response with a http
+ status code different from 200. The error response must contain a
+ human readable message that can be displayed to the user. The error
+ response can also contain a machine readable code that can be used by
+ the client to handle the error in a specific way.
+- When you design your json document through the pydantic models, you
+ must use the appropriate data types. For example, you must use the
+ data type **'datetime.date'** to represent a date and not a string.
+ You must also properly define the constraints on the fields. For
+ example, if a field is optional, you must use the data type
+ **'typing.Optional'**. `pydantic `__
+ provides everything you need to properly define your json document.
+- Always use an appropriate pydantic model as request and/or response
+ for your route handler. Constraints on the fields of the pydantic
+ model must apply to the specific use case. For example, if your route
+ handler is used to create a sale order, the pydantic model must not
+ contain the field 'id' because the id of the sale order will be
+ generated by the route handler. But if the id is required afterwords,
+ the pydantic model for the response must contain the field 'id' as
+ required.
+- Uses descriptive property names in your json documents. For example,
+ avoid the use of documents providing a flat list of key value pairs.
+- Be consistent in the naming of your fields into your json documents.
+ For example, if you use 'id' to represent the id of a sale order, you
+ must use 'id' to represent the id of all the other objects.
+- Be consistent in the naming style of your fields. Always prefer
+ underscore to camel case.
+- Always use plural for the name of the fields that contain a list of
+ items. For example, if you have a field 'lines' that contains a list
+ of sale order lines, you must use 'lines' and not 'line'.
+- You can't expect that a client will provide you the identifier of a
+ specific record in odoo (for example the id of a carrier) if you
+ don't provide a specific route handler to retrieve the list of
+ available records. Sometimes, the client must share with odoo the
+ identity of a specific record to be able to perform an appropriate
+ action specific to this record (for example, the processing of a
+ payment is different for each payment acquirer). In this case, you
+ must provide a specific attribute that allows both the client and
+ odoo to identify the record. The field 'provider' on a payment
+ acquirer allows you to identify a specific record in odoo. This kind
+ of approach allows both the client and odoo to identify the record
+ without having to rely on the id of the record. (This will ensure
+ that the client will not break if the id of the record is changed in
+ odoo for example when tests are run on an other database).
+- Always use the same name for the same kind of object. For example, if
+ you have a field 'lines' that contains a list of sale order lines,
+ you must use the same name for the same kind of object in all the
+ other json documents.
+- Manage relations between objects in your json documents the same way.
+ By default, you should return the id of the related object in the
+ json document. But this is not always possible or convenient, so you
+ can also return the related object in the json document. The main
+ advantage of returning the id of the related object is that it allows
+ you to avoid the `n+1
+ problem `__ . The main
+ advantage of returning the related object in the json document is
+ that it allows you to avoid an extra call to retrieve the related
+ object. By keeping in mind the pros and cons of each approach, you
+ can choose the best one for your use case. Once it's done, you must
+ be consistent in the way you manage the relations of the same object.
+- It's not always a good idea to name your fields into your json
+ documents with the same name as the fields of the corresponding odoo
+ model. For example, in your document representing a sale order, you
+ must not use the name 'order_line' for the field that contains the
+ list of sale order lines. The name 'order_line' in addition to being
+ confusing and not consistent with the best practices, is not
+ auto-descriptive. The name 'lines' is much better.
+- Keep a defensive programming approach. If you provide a route handler
+ that returns a list of records, you must ensure that the computation
+ of the list is not too long or will not drain your server resources.
+ For example, for search route handlers, you must ensure that the
+ search is limited to a reasonable number of records by default.
+- As a corollary of the previous point, a search handler must always
+ use the pagination mechanism with a reasonable default page size. The
+ result list must be enclosed in a json document that contains the
+ count of records into the system matching your search criteria and
+ the list of records for the given page and size.
+- Use plural for the name of a service. For example, if you provide a
+ service that allows you to manage the sale orders, you must use the
+ name 'sale_orders' and not 'sale_order'.
+- ... and many more.
We could write a book about the best practices to follow when you design
your api but we will stop here. This list is the result of our
@@ -1373,78 +1373,79 @@ fastapi app.
│ ├── dependencies.py # custom dependencies
│ ├── error_handlers.py # custom error handlers
-- The **'models'** directory contains the odoo models. When you define a
- new app, as for the others addons, you will add your new model
- inheriting from the **'fastapi.endpoint'** model in this directory.
+- The **'models'** directory contains the odoo models. When you define
+ a new app, as for the others addons, you will add your new model
+ inheriting from the **'fastapi.endpoint'** model in this directory.
-- The **'routers'** directory contains the fastapi routers. You will add
- your new routers in this directory. Each route starting with the same
- prefix should be grouped in the same file. For example, all the routes
- starting with '/items' should be defined in the **'items.py'** file.
- The **'\__init\_\_.py'** file in this directory is used to import all
- the routers defined in the directory and create a global router that
- can be used in an app. For example, in your **'items.py'** file, you
- will define a router like this:
+- The **'routers'** directory contains the fastapi routers. You will
+ add your new routers in this directory. Each route starting with the
+ same prefix should be grouped in the same file. For example, all the
+ routes starting with '/items' should be defined in the **'items.py'**
+ file. The **'\__init\_\_.py'** file in this directory is used to
+ import all the routers defined in the directory and create a global
+ router that can be used in an app. For example, in your
+ **'items.py'** file, you will define a router like this:
- .. code:: python
+ .. code:: python
- router = APIRouter(tags=["items"])
+ router = APIRouter(tags=["items"])
- router.get("/items", response_model=List[Item])
- def list_items():
- pass
+ router.get("/items", response_model=List[Item])
+ def list_items():
+ pass
- In the **'\__init\_\_.py'** file, you will import the router and add
- it to the global router or your addon.
+ In the **'\__init\_\_.py'** file, you will import the router and add
+ it to the global router or your addon.
- .. code:: python
+ .. code:: python
- from fastapi import APIRouter
+ from fastapi import APIRouter
- from .items import router as items_router
+ from .items import router as items_router
- router = APIRouter()
- router.include_router(items_router)
+ router = APIRouter()
+ router.include_router(items_router)
-- The **'schemas.py'** will be used to define the pydantic models. For
- complex APIs with a lot of models, it will be better to create a
- **'schemas'** directory and split the models in different files. The
- **'\__init\_\_.py'** file in this directory will be used to import all
- the models defined in the directory. For example, in your
- **'my_model.py'** file, you will define a model like this:
+- The **'schemas.py'** will be used to define the pydantic models. For
+ complex APIs with a lot of models, it will be better to create a
+ **'schemas'** directory and split the models in different files. The
+ **'\__init\_\_.py'** file in this directory will be used to import
+ all the models defined in the directory. For example, in your
+ **'my_model.py'** file, you will define a model like this:
- .. code:: python
+ .. code:: python
- from pydantic import BaseModel
+ from pydantic import BaseModel
- class MyModel(BaseModel):
- name: str
- description: str = None
+ class MyModel(BaseModel):
+ name: str
+ description: str = None
- In the **'\__init\_\_.py'** file, you will import the model's classes
- from the files in the directory.
+ In the **'\__init\_\_.py'** file, you will import the model's classes
+ from the files in the directory.
- .. code:: python
+ .. code:: python
- from .my_model import MyModel
+ from .my_model import MyModel
- This will allow to always import the models from the schemas module
- whatever the models are spread across different files or defined in
- the **'schemas.py'** file.
+ This will allow to always import the models from the schemas module
+ whatever the models are spread across different files or defined in
+ the **'schemas.py'** file.
- .. code:: python
+ .. code:: python
- from x_api_addon.schemas import MyModel
+ from x_api_addon.schemas import MyModel
-- The **'dependencies.py'** file contains the custom dependencies that
- you will use in your routers. For example, you can define a dependency
- to check the access rights of the user.
+- The **'dependencies.py'** file contains the custom dependencies that
+ you will use in your routers. For example, you can define a
+ dependency to check the access rights of the user.
-- The **'error_handlers.py'** file contains the custom error handlers
- that you will use in your routers. The **'odoo-addon-fastapi'** module
- provides the default error handlers for the common odoo exceptions.
- Chance are that you will not need to define your own error handlers.
- But if you need to do it, you can define them in this file.
+- The **'error_handlers.py'** file contains the custom error handlers
+ that you will use in your routers. The **'odoo-addon-fastapi'**
+ module provides the default error handlers for the common odoo
+ exceptions. Chance are that you will not need to define your own
+ error handlers. But if you need to do it, you can define them in this
+ file.
What's next?
------------
@@ -1480,26 +1481,26 @@ Changelog
Features
~~~~~~~~
--
+-
- - A new parameter is now available on the endpoint model to let you
- disable the creation and the store of session files used by Odoo for
- calls to your application endpoint. This is usefull to prevent disk
- space consumption and IO operations if your application doesn't need
- to use this sessions files which are mainly used by Odoo by to store
- the session info of logged in users.
- (`#442 `__)
+ - A new parameter is now available on the endpoint model to let you
+ disable the creation and the store of session files used by Odoo
+ for calls to your application endpoint. This is usefull to prevent
+ disk space consumption and IO operations if your application
+ doesn't need to use this sessions files which are mainly used by
+ Odoo by to store the session info of logged in users.
+ (`#442 `__)
Bugfixes
~~~~~~~~
-- Fix issue with the retry of a POST request with a body content.
+- 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
- would stuck in a loop and never complete. This was due to the fact
- that the request input stream was not reset after a failed attempt to
- process the request.
- (`#440 `__)
+ Prior to this fix the retry of a POST request with a body content
+ would stuck in a loop and never complete. This was due to the fact
+ that the request input stream was not reset after a failed attempt to
+ process the request.
+ (`#440 `__)
17.0.2.0.0 (2024-10-03)
-----------------------
@@ -1507,37 +1508,37 @@ Bugfixes
Bugfixes
~~~~~~~~
-- This change is a complete rewrite of the way the transactions are
- managed when integrating a fastapi application into Odoo.
-
- In the previous implementation, specifics error handlers were put in
- place to catch exception occurring in the handling of requests made to
- a fastapi application and to rollback the transaction in case of
- error. This was done by registering specifics error handlers methods
- to the fastapi application using the 'add_exception_handler' method of
- the fastapi application. In this implementation, the transaction was
- rolled back in the error handler method.
-
- This approach was not working as expected for several reasons:
-
- - The handling of the error at the fastapi level prevented the retry
- mechanism to be triggered in case of a DB concurrency error. This is
- because the error was catch at the fastapi level and never bubbled
- up to the early stage of the processing of the request where the
- retry mechanism is implemented.
- - The cleanup of the environment and the registry was not properly
- done in case of error. In the **'odoo.service.model.retrying'**
- method, you can see that the cleanup process is different in case of
- error raised by the database and in case of error raised by the
- application.
-
- This change fix these issues by ensuring that errors are no more catch
- at the fastapi level and bubble up the fastapi processing stack
- through the event loop required to transform WSGI to ASGI. As result
- the transactional nature of the requests to the fastapi applications
- is now properly managed by the Odoo framework.
-
- (`#422 `__)
+- This change is a complete rewrite of the way the transactions are
+ managed when integrating a fastapi application into Odoo.
+
+ In the previous implementation, specifics error handlers were put in
+ place to catch exception occurring in the handling of requests made
+ to a fastapi application and to rollback the transaction in case of
+ error. This was done by registering specifics error handlers methods
+ to the fastapi application using the 'add_exception_handler' method
+ of the fastapi application. In this implementation, the transaction
+ was rolled back in the error handler method.
+
+ This approach was not working as expected for several reasons:
+
+ - The handling of the error at the fastapi level prevented the retry
+ mechanism to be triggered in case of a DB concurrency error. This
+ is because the error was catch at the fastapi level and never
+ bubbled up to the early stage of the processing of the request
+ where the retry mechanism is implemented.
+ - The cleanup of the environment and the registry was not properly
+ done in case of error. In the **'odoo.service.model.retrying'**
+ method, you can see that the cleanup process is different in case
+ of error raised by the database and in case of error raised by the
+ application.
+
+ This change fix these issues by ensuring that errors are no more
+ catch at the fastapi level and bubble up the fastapi processing stack
+ through the event loop required to transform WSGI to ASGI. As result
+ the transactional nature of the requests to the fastapi applications
+ is now properly managed by the Odoo framework.
+
+ (`#422 `__)
17.0.1.0.1 (2024-10-02)
-----------------------
@@ -1545,15 +1546,15 @@ Bugfixes
Bugfixes
~~~~~~~~
-- Fix compatibility issues with the latest Odoo version
+- Fix compatibility issues with the latest Odoo version
- From
- https://github.com/odoo/odoo/commit/cb1d057dcab28cb0b0487244ba99231ee292502e
- the original werkzeug HTTPRequest class has been wrapped in a new
- class to keep under control the attributes developers use. This
- changes take care of this new implementation but also keep
- compatibility with the old ones.
- (`#414 `__)
+ From
+ https://github.com/odoo/odoo/commit/cb1d057dcab28cb0b0487244ba99231ee292502e
+ the original werkzeug HTTPRequest class has been wrapped in a new
+ class to keep under control the attributes developers use. This
+ changes take care of this new implementation but also keep
+ compatibility with the old ones.
+ (`#414 `__)
16.0.1.2.5 (2024-01-17)
-----------------------
@@ -1561,14 +1562,14 @@ Bugfixes
Bugfixes
~~~~~~~~
-- Odoo has done an update and now, it checks domains of ir.rule on
- creation and modification.
+- Odoo has done an update and now, it checks domains of ir.rule on
+ creation and modification.
- The ir.rule 'Fastapi: Running user rule' uses a field
- (authenticate\ *partner_id) that comes from the context. This field
- wasn't always set and this caused an error when Odoo checked the
- domain. So now it is set to False by default.
- (``#410 ``*)
+ The ir.rule 'Fastapi: Running user rule' uses a field
+ (authenticate\ *partner_id) that comes from the context. This field
+ wasn't always set and this caused an error when Odoo checked the
+ domain. So now it is set to False by default.
+ (``#410 ``*)
16.0.1.2.3 (2023-12-21)
-----------------------
@@ -1576,12 +1577,12 @@ Bugfixes
Bugfixes
~~~~~~~~
-- In case of exception in endpoint execution, close the database cursor
- after rollback.
+- In case of exception in endpoint execution, close the database cursor
+ after rollback.
- This is to ensure that the *retrying* method in *service/model.py*
- does not try to flush data to the database.
- (`#405 `__)
+ This is to ensure that the *retrying* method in *service/model.py*
+ does not try to flush data to the database.
+ (`#405 `__)
16.0.1.2.2 (2023-12-12)
-----------------------
@@ -1589,17 +1590,17 @@ Bugfixes
Bugfixes
~~~~~~~~
-- When using the 'FastAPITransactionCase' class, allows to specify a
- specific override of the 'authenticated_partner_impl' method into the
- list of overrides to apply. Before this change, the
- 'authenticated_partner_impl' override given in the 'overrides'
- parameter was always overridden in the '\_create_test_client' method
- of the 'FastAPITransactionCase' class. It's now only overridden if the
- 'authenticated_partner_impl' method is not already present in the list
- of overrides to apply and no specific partner is given. If a specific
- partner is given at same time of an override for the
- 'authenticated_partner_impl' method, an error is raised.
- (`#396 `__)
+- When using the 'FastAPITransactionCase' class, allows to specify a
+ specific override of the 'authenticated_partner_impl' method into the
+ list of overrides to apply. Before this change, the
+ 'authenticated_partner_impl' override given in the 'overrides'
+ parameter was always overridden in the '\_create_test_client' method
+ of the 'FastAPITransactionCase' class. It's now only overridden if
+ the 'authenticated_partner_impl' method is not already present in the
+ list of overrides to apply and no specific partner is given. If a
+ specific partner is given at same time of an override for the
+ 'authenticated_partner_impl' method, an error is raised.
+ (`#396 `__)
16.0.1.2.1 (2023-11-03)
-----------------------
@@ -1607,12 +1608,12 @@ Bugfixes
Bugfixes
~~~~~~~~
-- Fix a typo in the Field declaration of the 'count' attribute of the
- 'PagedCollection' schema.
+- Fix a typo in the Field declaration of the 'count' attribute of the
+ 'PagedCollection' schema.
- Misspelt parameter was triggering a deprecation warning due to recent
- versions of Pydantic seeing it as an arbitrary parameter.
- (`#389 `__)
+ Misspelt parameter was triggering a deprecation warning due to recent
+ versions of Pydantic seeing it as an arbitrary parameter.
+ (`#389 `__)
16.0.1.2.0 (2023-10-13)
-----------------------
@@ -1620,15 +1621,15 @@ Bugfixes
Features
~~~~~~~~
-- The field *total* in the *PagedCollection* schema is replaced by the
- field *count*. The field *total* is now deprecated and will be removed
- in the next major version. This change is backward compatible. The
- json document returned will now contain both fields *total* and
- *count* with the same value. In your python code the field *total*, if
- used, will fill the field *count* with the same value. You are
- encouraged to use the field *count* instead of *total* and adapt your
- code accordingly.
- (`#380 `__)
+- The field *total* in the *PagedCollection* schema is replaced by the
+ field *count*. The field *total* is now deprecated and will be
+ removed in the next major version. This change is backward
+ compatible. The json document returned will now contain both fields
+ *total* and *count* with the same value. In your python code the
+ field *total*, if used, will fill the field *count* with the same
+ value. You are encouraged to use the field *count* instead of *total*
+ and adapt your code accordingly.
+ (`#380 `__)
Bug Tracker
===========
@@ -1636,7 +1637,7 @@ 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
-`feedback `_.
+`feedback `_.
Do not contact contributors directly about support or help with technical issues.
@@ -1651,7 +1652,7 @@ Authors
Contributors
------------
-- Laurent Mignon
+- Laurent Mignon
Maintainers
-----------
@@ -1674,6 +1675,6 @@ Current `maintainer `__:
|maintainer-lmignon|
-This module is part of the `OCA/rest-framework `_ project on GitHub.
+This module is part of the `OCA/rest-framework `_ project on GitHub.
You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.
diff --git a/fastapi/__manifest__.py b/fastapi/__manifest__.py
index 19b719019..72227f3c0 100644
--- a/fastapi/__manifest__.py
+++ b/fastapi/__manifest__.py
@@ -5,7 +5,7 @@
"name": "Odoo FastAPI",
"summary": """
Odoo FastAPI endpoint""",
- "version": "18.0.1.3.0",
+ "version": "19.0.1.0.0",
"license": "LGPL-3",
"author": "ACSONE SA/NV,Odoo Community Association (OCA)",
"maintainers": ["lmignon"],
@@ -30,5 +30,5 @@
]
},
"development_status": "Beta",
- "installable": False,
+ "installable": True,
}
diff --git a/fastapi/demo/fastapi_endpoint_demo.xml b/fastapi/demo/fastapi_endpoint_demo.xml
index ad94eeaf3..1b9b13732 100644
--- a/fastapi/demo/fastapi_endpoint_demo.xml
+++ b/fastapi/demo/fastapi_endpoint_demo.xml
@@ -10,7 +10,7 @@
>
My Demo Endpoint User
my_demo_app_user
-
+
My Demo Endpoint Group
-
-
+
+
@@ -40,7 +43,10 @@
['|', ('user_ids', '=', user.id), ('id', '=', authenticated_partner_id)]
-
+
@@ -59,6 +65,9 @@
Fastapi: Running user rule
[('user_id', '=', user.id)]
-
+
diff --git a/fastapi/security/res_groups.xml b/fastapi/security/res_groups.xml
index 589cbcf97..7043ea69f 100644
--- a/fastapi/security/res_groups.xml
+++ b/fastapi/security/res_groups.xml
@@ -8,19 +8,25 @@
99
+
+ FastAPI
+
+ 99
+
+
User
-
-
+
+
Administrator
-
-
+
+
@@ -28,6 +34,6 @@
the user running the endpoint handlers and performs authentication -->
FastAPI Endpoint Runner
-
+
diff --git a/fastapi/static/description/index.html b/fastapi/static/description/index.html
index 2ecae1f73..7ccacd183 100644
--- a/fastapi/static/description/index.html
+++ b/fastapi/static/description/index.html
@@ -374,7 +374,7 @@ Odoo FastAPI
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! source digest: sha256:ef181029fc269ba5b6364530192a725c5f3392bc026ef9d5fcf47f9e43ec888a
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
-

+

This addon provides the basis to smoothly integrate the
FastAPI framework into Odoo.
This integration allows you to use all the goodies from
@@ -499,8 +499,8 @@
This approach was not working as expected for several reasons:
- The handling of the error at the fastapi level prevented the retry
-mechanism to be triggered in case of a DB concurrency error. This is
-because the error was catch at the fastapi level and never bubbled
-up to the early stage of the processing of the request where the
-retry mechanism is implemented.
+mechanism to be triggered in case of a DB concurrency error. This
+is because the error was catch at the fastapi level and never
+bubbled up to the early stage of the processing of the request
+where the retry mechanism is implemented.
- The cleanup of the environment and the registry was not properly
done in case of error. In the ‘odoo.service.model.retrying’
-method, you can see that the cleanup process is different in case of
-error raised by the database and in case of error raised by the
+method, you can see that the cleanup process is different in case
+of error raised by the database and in case of error raised by the
application.
-This change fix these issues by ensuring that errors are no more catch
-at the fastapi level and bubble up the fastapi processing stack
+
This change fix these issues by ensuring that errors are no more
+catch at the fastapi level and bubble up the fastapi processing stack
through the event loop required to transform WSGI to ASGI. As result
the transactional nature of the requests to the fastapi applications
is now properly managed by the Odoo framework.
@@ -1891,10 +1892,10 @@ Bugfixes
list of overrides to apply. Before this change, the
‘authenticated_partner_impl’ override given in the ‘overrides’
parameter was always overridden in the ‘_create_test_client’ method
-of the ‘FastAPITransactionCase’ class. It’s now only overridden if the
-‘authenticated_partner_impl’ method is not already present in the list
-of overrides to apply and no specific partner is given. If a specific
-partner is given at same time of an override for the
+of the ‘FastAPITransactionCase’ class. It’s now only overridden if
+the ‘authenticated_partner_impl’ method is not already present in the
+list of overrides to apply and no specific partner is given. If a
+specific partner is given at same time of an override for the
‘authenticated_partner_impl’ method, an error is raised.
(#396)
@@ -1920,13 +1921,13 @@ 16.0.1.2.0 (2023-10-13)
Features
- The field total in the PagedCollection schema is replaced by the
-field count. The field total is now deprecated and will be removed
-in the next major version. This change is backward compatible. The
-json document returned will now contain both fields total and
-count with the same value. In your python code the field total, if
-used, will fill the field count with the same value. You are
-encouraged to use the field count instead of total and adapt your
-code accordingly.
+field count. The field total is now deprecated and will be
+removed in the next major version. This change is backward
+compatible. The json document returned will now contain both fields
+total and count with the same value. In your python code the
+field total, if used, will fill the field count with the same
+value. You are encouraged to use the field count instead of total
+and adapt your code accordingly.
(#380)
@@ -1937,7 +1938,7 @@ 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
-feedback.
+feedback.
Do not contact contributors directly about support or help with technical issues.
diff --git a/fastapi/tests/common.py b/fastapi/tests/common.py
index f9c05b67e..eb6c9d364 100644
--- a/fastapi/tests/common.py
+++ b/fastapi/tests/common.py
@@ -14,8 +14,8 @@
from odoo.tests import tagged
from odoo.tests.common import TransactionCase
-from odoo.addons.base.models.res_partner import Partner
-from odoo.addons.base.models.res_users import Users
+from odoo.addons.base.models.res_partner import ResPartner
+from odoo.addons.base.models.res_users import ResUsers
from fastapi import APIRouter, FastAPI
from fastapi.testclient import TestClient
@@ -80,8 +80,8 @@ def setUpClass(cls):
cls.default_fastapi_app: FastAPI | None = None
cls.default_fastapi_router: APIRouter | None = None
cls.default_fastapi_odoo_env: Environment = cls.env
- cls.default_fastapi_running_user: Users | None = None
- cls.default_fastapi_authenticated_partner: Partner | None = None
+ cls.default_fastapi_running_user: ResUsers | None = None
+ cls.default_fastapi_authenticated_partner: ResPartner | None = None
cls.default_fastapi_dependency_overrides: dict[
Callable[..., Any], Callable[..., Any]
] = {}
@@ -91,8 +91,8 @@ def _create_test_client(
self,
app: FastAPI | None = None,
router: APIRouter | None = None,
- user: Users | None = None,
- partner: Partner | None = None,
+ user: ResUsers | None = None,
+ partner: ResPartner | None = None,
env: Environment = None,
dependency_overrides: dict[Callable[..., Any], Callable[..., Any]] = None,
raise_server_exceptions: bool = True,
diff --git a/fastapi/tests/test_fastapi.py b/fastapi/tests/test_fastapi.py
index fbda932fb..0d13f77a7 100644
--- a/fastapi/tests/test_fastapi.py
+++ b/fastapi/tests/test_fastapi.py
@@ -5,8 +5,8 @@
import unittest
from contextlib import contextmanager
-from odoo import sql_db
from odoo.tests.common import HttpCase
+from odoo.tests.test_cursor import TestCursor
from odoo.tools import mute_logger
from fastapi import status
@@ -35,7 +35,7 @@ def setUpClass(cls):
@contextmanager
def _mocked_commit(self):
with unittest.mock.patch.object(
- sql_db.TestCursor, "commit", return_value=None
+ TestCursor, "commit", return_value=None
) as mocked_commit:
yield mocked_commit
@@ -156,7 +156,9 @@ def test_request_validation_error(self) -> None:
route = "/fastapi_demo/demo/exception?exception_type=BAD&error_message="
response = self.url_open(route, timeout=200)
mocked_commit.assert_not_called()
- self.assertEqual(response.status_code, status.HTTP_422_UNPROCESSABLE_ENTITY)
+ self.assertEqual(
+ response.status_code, status.HTTP_422_UNPROCESSABLE_CONTENT
+ )
def test_no_commit_on_exception(self) -> None:
# this test check that the way we mock the cursor is working as expected
diff --git a/fastapi/tests/test_fastapi_demo.py b/fastapi/tests/test_fastapi_demo.py
index 152f560af..f8585c610 100644
--- a/fastapi/tests/test_fastapi_demo.py
+++ b/fastapi/tests/test_fastapi_demo.py
@@ -10,7 +10,11 @@
from fastapi import status
-from ..dependencies import fastapi_endpoint
+from ..dependencies import (
+ authenticated_partner_from_basic_auth_user,
+ authenticated_partner_impl,
+ fastapi_endpoint,
+)
from ..routers import demo_router
from ..schemas import DemoEndpointAppInfo, DemoExceptionType
from .common import FastAPITransactionCase
@@ -53,6 +57,25 @@ def test_who_ami(self) -> None:
},
)
+ def test_who_ami_http_basic(self) -> None:
+ self.default_fastapi_authenticated_partner = self.env["res.partner"]
+ dependency_overrides = {
+ authenticated_partner_impl: authenticated_partner_from_basic_auth_user
+ }
+ with self._create_test_client(
+ dependency_overrides=dependency_overrides
+ ) as test_client:
+ response: Response = test_client.get("/demo/who_ami", auth=("demo", "demo"))
+ self.assertEqual(response.status_code, status.HTTP_200_OK)
+ partner = self.env.ref("base.partner_demo")
+ self.assertDictEqual(
+ response.json(),
+ {
+ "name": partner.name,
+ "display_name": partner.display_name,
+ },
+ )
+
def test_endpoint_info(self) -> None:
demo_app = self.env.ref("fastapi.fastapi_endpoint_demo")
with self._create_test_client(
diff --git a/fastapi/views/fastapi_endpoint.xml b/fastapi/views/fastapi_endpoint.xml
index 77b04fd9b..cd7ce166c 100644
--- a/fastapi/views/fastapi_endpoint.xml
+++ b/fastapi/views/fastapi_endpoint.xml
@@ -72,7 +72,7 @@
name="inactive"
domain="[('active','=',False)]"
/>
-
+
FastAPI
fastapi,static/description/icon.png
-
+
diff --git a/requirements.txt b/requirements.txt
index 69f99aad8..bcba9b734 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,13 +1,6 @@
# generated from manifests external_dependencies
a2wsgi>=1.10.6
-apispec
-cerberus
-contextvars
-extendable>=0.0.4
fastapi>=0.110.0
parse-accept-language
-pydantic>=2.0.0
-pyquerystring
python-multipart
-typing-extensions
ujson
From 38ce6792bfcd2830f760fea727f648e727fc8d0e Mon Sep 17 00:00:00 2001
From: Raf Ven
Date: Tue, 7 Oct 2025 10:58:31 +0200
Subject: [PATCH 2/5] [FIX] fastapi: detail error response for UserError
exceptions
---
fastapi/error_handlers.py | 4 ++--
fastapi/tests/test_fastapi.py | 4 ++--
2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/fastapi/error_handlers.py b/fastapi/error_handlers.py
index 192ef4064..0c67a52e9 100644
--- a/fastapi/error_handlers.py
+++ b/fastapi/error_handlers.py
@@ -36,10 +36,10 @@ def convert_exception_to_status_body(exc: Exception) -> tuple[int, dict]:
details = jsonable_encoder(exc.errors())
elif isinstance(exc, AccessDenied | AccessError):
status_code = status.HTTP_403_FORBIDDEN
- details = "AccessError"
+ details = exc.args[0]
elif isinstance(exc, MissingError):
status_code = status.HTTP_404_NOT_FOUND
- details = "MissingError"
+ details = exc.args[0]
elif isinstance(exc, UserError):
status_code = status.HTTP_400_BAD_REQUEST
details = exc.args[0]
diff --git a/fastapi/tests/test_fastapi.py b/fastapi/tests/test_fastapi.py
index 0d13f77a7..59d0a89e0 100644
--- a/fastapi/tests/test_fastapi.py
+++ b/fastapi/tests/test_fastapi.py
@@ -130,7 +130,7 @@ def test_access_error(self) -> None:
self.assert_exception_processed(
exception_type=DemoExceptionType.access_error,
error_message="test",
- expected_message="AccessError",
+ expected_message="test",
expected_status_code=status.HTTP_403_FORBIDDEN,
)
@@ -138,7 +138,7 @@ def test_missing_error(self) -> None:
self.assert_exception_processed(
exception_type=DemoExceptionType.missing_error,
error_message="test",
- expected_message="MissingError",
+ expected_message="test",
expected_status_code=status.HTTP_404_NOT_FOUND,
)
From 1bea4e6769448a0f276d56f73acabe2c582bf37a Mon Sep 17 00:00:00 2001
From: "Laurent Mignon (ACSONE)"
Date: Tue, 7 Oct 2025 17:49:53 +0200
Subject: [PATCH 3/5] [FIX] fastapi: explicit load of demo data for tests
---
fastapi/models/fastapi_endpoint.py | 17 +++++++++++++++++
fastapi/tests/common.py | 4 ++--
fastapi/tests/test_fastapi.py | 1 +
fastapi/tests/test_fastapi_demo.py | 3 ++-
4 files changed, 22 insertions(+), 3 deletions(-)
diff --git a/fastapi/models/fastapi_endpoint.py b/fastapi/models/fastapi_endpoint.py
index bf046fc09..39b3ee6f1 100644
--- a/fastapi/models/fastapi_endpoint.py
+++ b/fastapi/models/fastapi_endpoint.py
@@ -11,6 +11,7 @@
from starlette.routing import Mount
from odoo import api, exceptions, fields, models, tools
+from odoo.tools import convert
from fastapi import APIRouter, Depends, FastAPI
@@ -338,3 +339,19 @@ def _get_fastapi_app_middlewares(self) -> list[Middleware]:
def _get_fastapi_app_dependencies(self) -> list[Depends]:
"""Return the dependencies to use for the fastapi app."""
return [Depends(dependencies.accept_language)]
+
+ # test utility
+ @api.model
+ def has_demo_data(self):
+ return (
+ self.env.ref("fastapi.fastapi_endpoint_demo", raise_if_not_found=False)
+ is not None
+ )
+
+ def _load_demo_data(self):
+ if self.has_demo_data():
+ return
+ # Load demo data
+ convert.convert_file(
+ self.env, "fastapi", "demo/fastapi_endpoint_demo.xml", None, mode="init"
+ )
diff --git a/fastapi/tests/common.py b/fastapi/tests/common.py
index eb6c9d364..8738c77f8 100644
--- a/fastapi/tests/common.py
+++ b/fastapi/tests/common.py
@@ -12,10 +12,10 @@
from odoo.api import Environment
from odoo.tests import tagged
-from odoo.tests.common import TransactionCase
from odoo.addons.base.models.res_partner import ResPartner
from odoo.addons.base.models.res_users import ResUsers
+from odoo.addons.base.tests.common import TransactionCaseWithUserDemo
from fastapi import APIRouter, FastAPI
from fastapi.testclient import TestClient
@@ -47,7 +47,7 @@ def default_exception_handler(request: Request, exc: Exception) -> Response:
@tagged("post_install", "-at_install")
-class FastAPITransactionCase(TransactionCase):
+class FastAPITransactionCase(TransactionCaseWithUserDemo):
"""
This class is a base class for FastAPI tests.
diff --git a/fastapi/tests/test_fastapi.py b/fastapi/tests/test_fastapi.py
index 59d0a89e0..9008f09bd 100644
--- a/fastapi/tests/test_fastapi.py
+++ b/fastapi/tests/test_fastapi.py
@@ -19,6 +19,7 @@ class FastAPIHttpCase(HttpCase):
@classmethod
def setUpClass(cls):
super().setUpClass()
+ cls.env["fastapi.endpoint"]._load_demo_data()
cls.fastapi_demo_app = cls.env.ref("fastapi.fastapi_endpoint_demo")
cls.fastapi_multi_demo_app = cls.env.ref(
"fastapi.fastapi_endpoint_multislash_demo"
diff --git a/fastapi/tests/test_fastapi_demo.py b/fastapi/tests/test_fastapi_demo.py
index f8585c610..854b9d5eb 100644
--- a/fastapi/tests/test_fastapi_demo.py
+++ b/fastapi/tests/test_fastapi_demo.py
@@ -32,6 +32,7 @@ class FastAPIDemoCase(FastAPITransactionCase):
@classmethod
def setUpClass(cls) -> None:
super().setUpClass()
+ cls.env["fastapi.endpoint"]._load_demo_data()
cls.default_fastapi_router = demo_router
cls.default_fastapi_running_user = cls.env.ref("fastapi.my_demo_app_user")
cls.default_fastapi_authenticated_partner = cls.env["res.partner"].create(
@@ -67,7 +68,7 @@ def test_who_ami_http_basic(self) -> None:
) as test_client:
response: Response = test_client.get("/demo/who_ami", auth=("demo", "demo"))
self.assertEqual(response.status_code, status.HTTP_200_OK)
- partner = self.env.ref("base.partner_demo")
+ partner = self.partner_demo
self.assertDictEqual(
response.json(),
{
From 556fa8920e1fd5e3bc8f33579bddd4e7ba80b3a5 Mon Sep 17 00:00:00 2001
From: "Laurent Mignon (ACSONE)"
Date: Tue, 7 Oct 2025 17:50:58 +0200
Subject: [PATCH 4/5] [FIX] fastapi: authenticate in a non-interactive mode
---
fastapi/dependencies.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/fastapi/dependencies.py b/fastapi/dependencies.py
index c5c9291f3..5c24d40e6 100644
--- a/fastapi/dependencies.py
+++ b/fastapi/dependencies.py
@@ -127,7 +127,7 @@ def basic_auth_user(
"login": username,
"password": password,
},
- user_agent_env=None,
+ user_agent_env={"interactive": False},
)
)
return env["res.users"].browse(response.get("uid"))
From 28554c81ba3db32b5f3f3f7d2772bca5b4cc7977 Mon Sep 17 00:00:00 2001
From: Raf Ven
Date: Tue, 7 Oct 2025 11:01:26 +0200
Subject: [PATCH 5/5] [DON'T MERGE] test-requirements.txt
---
test-requirements.txt | 1 +
1 file changed, 1 insertion(+)
diff --git a/test-requirements.txt b/test-requirements.txt
index cf108f84e..ae4432b6b 100644
--- a/test-requirements.txt
+++ b/test-requirements.txt
@@ -1,2 +1,3 @@
+odoo-addon-endpoint_route_handler @ git+https://github.com/OCA/web-api.git@refs/pull/117/head#subdirectory=endpoint_route_handler
odoo_test_helper
httpx