From 361da9ee268c51c74f44b7f03823e0e76af3ad28 Mon Sep 17 00:00:00 2001 From: Edwin Gonzales Date: Fri, 24 Jul 2026 19:52:07 +0800 Subject: [PATCH 1/4] test(spp_api_v2_simulation): reproduce API V2 app build failure on stale router import Call _get_fastapi_routers() on the api_v2 fastapi.endpoint and assert the /aggregation router is included. Fails before the fix with ModuleNotFoundError (routers.aggregation was renamed to routers.analytics). No existing test built the app, so the stale import went uncaught. --- spp_api_v2_simulation/tests/__init__.py | 1 + .../tests/test_fastapi_router_inclusion.py | 33 +++++++++++++++++++ 2 files changed, 34 insertions(+) create mode 100644 spp_api_v2_simulation/tests/test_fastapi_router_inclusion.py diff --git a/spp_api_v2_simulation/tests/__init__.py b/spp_api_v2_simulation/tests/__init__.py index 61fea0393..bb9de8d43 100644 --- a/spp_api_v2_simulation/tests/__init__.py +++ b/spp_api_v2_simulation/tests/__init__.py @@ -8,3 +8,4 @@ from . import test_router_coverage from . import test_scenario_update from . import test_simulation_service +from . import test_fastapi_router_inclusion diff --git a/spp_api_v2_simulation/tests/test_fastapi_router_inclusion.py b/spp_api_v2_simulation/tests/test_fastapi_router_inclusion.py new file mode 100644 index 000000000..79900bfb3 --- /dev/null +++ b/spp_api_v2_simulation/tests/test_fastapi_router_inclusion.py @@ -0,0 +1,33 @@ +# Part of OpenSPP. See LICENSE file for full copyright and licensing details. +"""Building the API V2 FastAPI app must not abort on a stale router import. + +``fastapi.endpoint._get_fastapi_routers()`` runs during app construction, which +FastAPI dispatch performs for a request to the API root *before* endpoint-level +OAuth/JWT checks. A broken import here (e.g. a router module renamed without +updating the importer) therefore turns into an unauthenticated failure that +takes the whole API V2 endpoint down. This test calls the router aggregation +for the api_v2 app and asserts the simulation ``/aggregation`` router is +included without raising. +""" + +from odoo.tests import TransactionCase, tagged + + +@tagged("post_install", "-at_install") +class TestSimulationFastapiRouterInclusion(TransactionCase): + def test_api_v2_routers_build_and_include_aggregation(self): + endpoint = self.env["fastapi.endpoint"].search([("app", "=", "api_v2")], limit=1) + self.assertTrue( + endpoint, + "The api_v2 fastapi.endpoint record must exist (shipped by spp_api_v2)", + ) + + # Must not raise ModuleNotFoundError from a stale router import. + routers = endpoint._get_fastapi_routers() + + prefixes = [getattr(r, "prefix", "") for r in routers] + self.assertIn( + "/aggregation", + prefixes, + "The simulation aggregation router must be included in the API V2 app", + ) From 3548d02bd5791436c41e7252cc251cbfa2ebab92 Mon Sep 17 00:00:00 2001 From: Edwin Gonzales Date: Fri, 24 Jul 2026 19:52:07 +0800 Subject: [PATCH 2/4] security(spp_api_v2_simulation): import aggregation_router from the analytics router routers/aggregation.py was renamed to routers/analytics.py (which still exports aggregation_router), but fastapi_endpoint._get_fastapi_routers() still imported from ..routers.aggregation. That import runs during FastAPI app construction, which the dispatcher performs for a request to the API root before endpoint-level OAuth/JWT checks, so an unauthenticated request raised ModuleNotFoundError and took the whole API V2 endpoint down. Import from ..routers.analytics instead. --- spp_api_v2_simulation/models/fastapi_endpoint.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spp_api_v2_simulation/models/fastapi_endpoint.py b/spp_api_v2_simulation/models/fastapi_endpoint.py index 931df10b9..9c9e176fc 100644 --- a/spp_api_v2_simulation/models/fastapi_endpoint.py +++ b/spp_api_v2_simulation/models/fastapi_endpoint.py @@ -19,7 +19,7 @@ def _get_fastapi_routers(self) -> list[APIRouter]: """Add simulation and aggregation routers to API V2.""" routers = super()._get_fastapi_routers() if self.app == "api_v2": - from ..routers.aggregation import aggregation_router + from ..routers.analytics import aggregation_router from ..routers.comparison import comparison_router from ..routers.run import run_router from ..routers.scenario import scenario_router From 6c24c047b5bcf1cf1d438efd63b82616acc5320b Mon Sep 17 00:00:00 2001 From: Edwin Gonzales Date: Fri, 24 Jul 2026 19:52:07 +0800 Subject: [PATCH 3/4] docs(spp_api_v2_simulation): bump version + HISTORY for stale-router-import fix 19.0.2.0.0 -> 19.0.2.0.1. README.rst / index.html to be regenerated from CI. --- spp_api_v2_simulation/__manifest__.py | 2 +- spp_api_v2_simulation/readme/HISTORY.md | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/spp_api_v2_simulation/__manifest__.py b/spp_api_v2_simulation/__manifest__.py index 1c565838a..8d4226f67 100644 --- a/spp_api_v2_simulation/__manifest__.py +++ b/spp_api_v2_simulation/__manifest__.py @@ -2,7 +2,7 @@ { "name": "OpenSPP Simulation API", "category": "OpenSPP/Integration", - "version": "19.0.2.0.0", + "version": "19.0.2.0.1", "sequence": 1, "author": "OpenSPP.org", "website": "https://github.com/OpenSPP/OpenSPP2", diff --git a/spp_api_v2_simulation/readme/HISTORY.md b/spp_api_v2_simulation/readme/HISTORY.md index 4aaf9afef..646e592e4 100644 --- a/spp_api_v2_simulation/readme/HISTORY.md +++ b/spp_api_v2_simulation/readme/HISTORY.md @@ -1,3 +1,14 @@ +### 19.0.2.0.1 + +- fix(security): build the API V2 FastAPI app from the current router module. + ``fastapi_endpoint.py`` still imported ``aggregation_router`` from the removed + ``routers/aggregation`` module (renamed to ``routers/analytics``), so + ``_get_fastapi_routers()`` raised ``ModuleNotFoundError`` during app + construction — which FastAPI dispatch performs, before endpoint OAuth/JWT + checks, for a request to the API root. An unauthenticated request could + therefore take the whole API V2 endpoint down. The import now targets + ``routers/analytics`` (which still exports ``aggregation_router``). + ### 19.0.2.0.0 - Initial migration to OpenSPP2 From d2cfd03d71df85476bcb4898bf09233a51318cf2 Mon Sep 17 00:00:00 2001 From: Edwin Gonzales Date: Fri, 24 Jul 2026 20:00:20 +0800 Subject: [PATCH 4/4] docs(spp_api_v2_simulation): regenerate README for 19.0.2.0.1 Applied verbatim from CI's pinned oca-gen-addon-readme output. --- spp_api_v2_simulation/README.rst | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/spp_api_v2_simulation/README.rst b/spp_api_v2_simulation/README.rst index a9b174502..ee1770222 100644 --- a/spp_api_v2_simulation/README.rst +++ b/spp_api_v2_simulation/README.rst @@ -109,6 +109,19 @@ Dependencies Changelog ========= +19.0.2.0.1 +~~~~~~~~~~ + +- fix(security): build the API V2 FastAPI app from the current router + module. ``fastapi_endpoint.py`` still imported ``aggregation_router`` + from the removed ``routers/aggregation`` module (renamed to + ``routers/analytics``), so ``_get_fastapi_routers()`` raised + ``ModuleNotFoundError`` during app construction — which FastAPI + dispatch performs, before endpoint OAuth/JWT checks, for a request to + the API root. An unauthenticated request could therefore take the + whole API V2 endpoint down. The import now targets + ``routers/analytics`` (which still exports ``aggregation_router``). + 19.0.2.0.0 ~~~~~~~~~~