From 3f774874d1cab7b294169b79164d4834a50280b3 Mon Sep 17 00:00:00 2001 From: mle Date: Wed, 21 Jun 2023 09:13:20 +0200 Subject: [PATCH] [ADD] Address router to app + cart sub-app --- requirements.txt | 2 +- shopinvader_v2_app_demo/__manifest__.py | 11 ++- .../demo/fastapi_endpoint.xml | 1 - shopinvader_v2_app_demo/demo/res_groups.xml | 10 +- .../models/fastapi_endpoint.py | 94 ++++++++++++------- .../views/fastapi_endpoint.xml | 25 +++++ test-requirements.txt | 9 ++ 7 files changed, 112 insertions(+), 40 deletions(-) create mode 100644 shopinvader_v2_app_demo/views/fastapi_endpoint.xml diff --git a/requirements.txt b/requirements.txt index 695d790868..a13aa9e5ce 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,3 @@ # generated from manifests external_dependencies -openupgradelib fastapi +openupgradelib diff --git a/shopinvader_v2_app_demo/__manifest__.py b/shopinvader_v2_app_demo/__manifest__.py index 71f53bcd39..27cd0f5906 100644 --- a/shopinvader_v2_app_demo/__manifest__.py +++ b/shopinvader_v2_app_demo/__manifest__.py @@ -11,8 +11,17 @@ "license": "AGPL-3", "author": "ACSONE SA/NV", "website": "https://github.com/shopinvader/odoo-shopinvader", - "depends": ["fastapi"], + "depends": [ + "fastapi", + "shopinvader_api_address", + "shopinvader_api_sale", + "shopinvader_anonymous_partner", + "shopinvader_fastapi_auth_jwt", + ], "external_dependencies": {"python": ["fastapi"]}, + "data": [ + "views/fastapi_endpoint.xml", + ], "demo": [ "demo/res_users.xml", "demo/res_groups.xml", diff --git a/shopinvader_v2_app_demo/demo/fastapi_endpoint.xml b/shopinvader_v2_app_demo/demo/fastapi_endpoint.xml index 1dbe76e556..9a2392cd3d 100644 --- a/shopinvader_v2_app_demo/demo/fastapi_endpoint.xml +++ b/shopinvader_v2_app_demo/demo/fastapi_endpoint.xml @@ -9,7 +9,6 @@ ]]> shopinvader_demo /shopinvader_demo - http_basic diff --git a/shopinvader_v2_app_demo/demo/res_groups.xml b/shopinvader_v2_app_demo/demo/res_groups.xml index 2811b0157f..210b734639 100644 --- a/shopinvader_v2_app_demo/demo/res_groups.xml +++ b/shopinvader_v2_app_demo/demo/res_groups.xml @@ -5,6 +5,14 @@ Shopinvader App Demo Group - + + diff --git a/shopinvader_v2_app_demo/models/fastapi_endpoint.py b/shopinvader_v2_app_demo/models/fastapi_endpoint.py index e0e29e04d4..a1b33bf861 100644 --- a/shopinvader_v2_app_demo/models/fastapi_endpoint.py +++ b/shopinvader_v2_app_demo/models/fastapi_endpoint.py @@ -1,16 +1,22 @@ # Copyright 2023 ACSONE SA/NV # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). -from typing import List +from functools import partial +from typing import Any, List -from fastapi import APIRouter +from fastapi import APIRouter, FastAPI -from odoo import _, api, fields, models -from odoo.exceptions import ValidationError +from odoo import api, fields, models -from odoo.addons.fastapi.depends import ( - authenticated_partner_from_basic_auth_user, - authenticated_partner_impl, +from odoo.addons.fastapi.dependencies import authenticated_partner_impl +from odoo.addons.fastapi_auth_jwt.dependencies import ( + auth_jwt_authenticated_partner, + auth_jwt_default_validator_name, +) +from odoo.addons.shopinvader_api_address.routers.address_service import address_router +from odoo.addons.shopinvader_api_sale.routers import cart_router +from odoo.addons.shopinvader_fastapi_auth_jwt.dependencies import ( + auth_jwt_authenticated_or_anonymous_partner_autocreate, ) @@ -22,11 +28,7 @@ class FastapiEndpoint(models.Model): selection_add=[("shopinvader_demo", "Shopinvader Demo Endpoint")], ondelete={"shopinvader_demo": "cascade"}, ) - - shopinvader_demo_auth_method = fields.Selection( - selection=[("http_basic", "HTTP Basic")], - string="Authentication method", - ) + auth_jwt_validator_id = fields.Many2one("auth.jwt.validator") @api.model def _get_fastapi_routers(self): @@ -36,35 +38,55 @@ def _get_fastapi_routers(self): @api.model def _get_shopinvader_demo_fastapi_routers(self) -> List[APIRouter]: - return [] + if "address" not in address_router.tags: + address_router.tags.append("address") + return [address_router] - @api.constrains("app", "shopinvader_demo_auth_method") - def _validate_demo_auth_method(self): - for rec in self: - if rec.app == "shopinvader_demo" and not rec.shopinvader_demo_auth_method: - raise ValidationError( - _( - "The authentication method is required for app %(app)s", - app=rec.app, - ) - ) + def _get_shopinvader_demo_tags(self, params) -> list: + tags_metadata = params.get("openapi_tags", []) or [] + tags_metadata.append( + { + "name": "address", + "description": "Set of services to manage addresses", + } + ) + base_url = self.env["ir.config_parameter"].sudo().get_param("web.base.url") + tags_metadata.append( + { + "name": "cart", + "description": "Set of services to manage carts", + "externalDocs": { + "description": "Cart services are available under " + "a specific authentication mechanism", + "url": f"{base_url}{self.root_path}/cart/docs", + }, + } + ) + return tags_metadata - @api.model - def _fastapi_app_fields(self) -> List[str]: - fields = super()._fastapi_app_fields() - fields.append("shopinvader_demo_auth_method") - return fields + def _prepare_fastapi_app_params(self) -> dict[str, Any]: + params = super()._prepare_fastapi_app_params() + if self.app == "shopinvader_demo": + params["openapi_tags"] = self._get_shopinvader_demo_tags(params) + return params def _get_app(self): app = super()._get_app() - if self.app == "shopinvader_demo_auth_method": - # Here we add the overrides to the authenticated_partner_impl method - # according to the authentication method configured on the demo app - if self.demo_auth_method == "http_basic": - authenticated_partner_impl_override = ( - authenticated_partner_from_basic_auth_user - ) + if self.app == "shopinvader_demo": app.dependency_overrides[ authenticated_partner_impl - ] = authenticated_partner_impl_override + ] = auth_jwt_authenticated_partner + app.dependency_overrides[auth_jwt_default_validator_name] = partial( + lambda a: a, self.auth_jwt_validator_id.name or None + ) + cart_app = FastAPI() + cart_app.include_router(cart_router) + cart_app.dependency_overrides[ + authenticated_partner_impl + ] = auth_jwt_authenticated_or_anonymous_partner_autocreate + cart_app.dependency_overrides[auth_jwt_default_validator_name] = partial( + lambda a: a, self.auth_jwt_validator_id.name or None + ) + app.mount(self.root_path + "/cart", cart_app) + return app diff --git a/shopinvader_v2_app_demo/views/fastapi_endpoint.xml b/shopinvader_v2_app_demo/views/fastapi_endpoint.xml new file mode 100644 index 0000000000..6c443612a9 --- /dev/null +++ b/shopinvader_v2_app_demo/views/fastapi_endpoint.xml @@ -0,0 +1,25 @@ + + + + + + fastapi.endpoint + + + + + + + + + + + diff --git a/test-requirements.txt b/test-requirements.txt index 045b91c0ec..7c70947bd2 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -7,3 +7,12 @@ vcrpy vcrpy-unittest unittest2 # For shopinvader test_controller, which inherits component odoo-test-helper +fastapi @ git+https://github.com/tiangolo/fastapi@refs/pull/5452/head +odoo-addon-shopinvader-schema-address @ git+https://github.com/shopinvader/odoo-shopinvader@refs/pull/1361/head#subdirectory=setup/shopinvader_schema_address +odoo-addon-shopinvader-api-address @ git+https://github.com/shopinvader/odoo-shopinvader@refs/pull/1361/head#subdirectory=setup/shopinvader_api_address +odoo-addon-shopinvader-api-sale @ git+https://github.com/shopinvader/odoo-shopinvader@refs/pull/1369/head#subdirectory=setup/shopinvader_api_sale +odoo-addon-shopinvader-anonymous-partner @ git+https://github.com/shopinvader/odoo-shopinvader@refs/pull/1366/head#subdirectory=setup/shopinvader_anonymous_partner +odoo-addon-shopinvader-fastapi-auth-jwt @ git+https://github.com/shopinvader/odoo-shopinvader@refs/pull/1366/head#subdirectory=setup/shopinvader_fastapi_auth_jwt +odoo-addon-auth_jwt @ git+https://github.com/oca/server-auth@refs/pull/520/head#subdirectory=setup/auth_jwt +odoo-addon-fastapi_auth_jwt @ git+https://github.com/oca/rest-framework@refs/pull/347/head#subdirectory=setup/fastapi_auth_jwt +httpx # For FastAPI tests