Skip to content

[20.0][MIG] endpoint_route_handler: Migration to 20.0 - #177

Open
DMopen2s wants to merge 87 commits into
OCA:20.0from
DMopen2s:20.0-mig-endpoint_route_handler
Open

DMopen2s wants to merge 87 commits into
OCA:20.0from
DMopen2s:20.0-mig-endpoint_route_handler

Conversation

@DMopen2s

Copy link
Copy Markdown

Migration of to Odoo 20.0.

@simahawk simahawk left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

e_registry = cls._endpoint_route_registry(
http.request.env
if (http.request and hasattr(http.request, "env") and http.request.env)
else (cls._cr_env if hasattr(cls, "_cr_env") else None)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where does _cr_env get set? I only see _last_env. I think without a request (odoo shell, website.technical.page) this would end up in None.cr, right? 19.0 had test_routing_map_no_request for that. Was that dropped on purpose?

("user_endpoint", "User"),
("bearer", "API Key (User)"),
]
return [("public", "Public"), ("user_endpoint", "User")]

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is dropping bearer intentional? As far as I can see, 20.0 still supports it and just needs bearer_scope (and save_session: False) in the routing. Maybe adding that in _get_routing_info() would be enough, and existing bearer records would keep working after the upgrade?

simahawk and others added 23 commits November 4, 2026 13:29
When an endpoint is archived it must be dropped.
When it's unarchive it must be restored.
Routing maps are generated **per env**
which means that every new env will have its own routing map
attached to `ir.http` registry class.

This is not desired (as per core Odoo comment)
but it's like this today :/

Hence, before this change, the routing map could be mis-aligned
across different envs leading to random responses for custom endpoints.

This refactoring simplifies a lot the handling of the rules
leaving to std `_generate_routing_rules` the duty to yield rules
and to `routing_map` to generate them for the new route map.

EndpointRegistry memory consumption is improved too
thanks to smaller data to store and to the usage of __slots__.
To avoid multiple invalidation of all envs on each edit or create
of persistent records, a new flag is introduced: 'registry_sync'.

This flag delays the sync of the rule registry till manual action
occurs. Records in the UI are decorated accordingly to notify users of
the need to reflect changes on ther registry to make them effective.

The sync happens in a post commit hook to ensure all values are in place
for the affected records.
OCA-git-bot and others added 24 commits November 4, 2026 13:29
Updated by "Update PO files to match POT (msgmerge)" hook in Weblate.

Translation: web-api-19.0/web-api-19.0-endpoint_route_handler
Translate-URL: https://translation.odoo-community.org/projects/web-api-19-0/web-api-19-0-endpoint_route_handler/
This commits adds support for CORS setting on endpoints.

Without this, the default behavior is to disallow all origins. That's usually
the opposite of what you want for an API endpoint, as you want consumers to be
able to access the endpoint from their own origin.

Now, by default, the endpoint will allow all origins (default="*"), and it can
be further configured for more specific restrictions.
Add bearer to the auth_type selection so endpoints can use Odoo's
native API key authentication (res.users.apikeys) without requiring
an extra auth module.
Updated by "Update PO files to match POT (msgmerge)" hook in Weblate.

Translation: web-api-19.0/web-api-19.0-endpoint_route_handler
Translate-URL: https://translation.odoo-community.org/projects/web-api-19-0/web-api-19-0-endpoint_route_handler/
Currently translated at 100.0% (31 of 31 strings)

Translation: web-api-19.0/web-api-19.0-endpoint_route_handler
Translate-URL: https://translation.odoo-community.org/projects/web-api-19-0/web-api-19-0-endpoint_route_handler/it/
…ocked request

`_get_mocked_request()` sets `registry._init_modules = set()` to simulate a minimal endpoint environment.
Since registry is the real Odoo Registry singleton, this mutation persisted after the context exited.

Odoo core `ir_http.py` uses `registry._init_modules` to build the routing map, so leaving it as set() caused post_install HttpCase tests from other modules to receive a routing map containing only server-wide modules, resulting in 404 errors on routes registered by those modules.

Fix by saving and restoring _init_modules in a try/finally block.
Without precompute, the required computed field is evaluated after
INSERT (during flush), so creating an endpoint whose route is derived
from other fields by a downstream module fails with "Missing required
value for the field 'Route'" — the INSERT runs before the compute can
populate the column.
…raises

369de3f restores `registry._init_modules` after `_get_mocked_request`, but the
restore happens after the `with MockRequest(...)` block without a `try/finally`.
If the test body raises inside the mocked request, the exception propagates out
of the generator and the restore line is never reached, so the process-wide
registry is left with an empty module set again.

That attribute is not rolled back with the test transaction, and
`ir.http.routing_map()` builds the routing map from exactly that set:

    installed = registry._init_modules.union(config["server_wide_modules"])

so the next routing-map regeneration yields no module controllers at all and
every later `HttpCase` in the same process gets a 404 for any route outside
`base` - the very failure 369de3f set out to fix, just on the error path.

Wrap the `yield` in `try/finally` so the restore always runs.

Assisted-by: Claude Opus 5
routing_map() is wrapped with an ormcache whose key calls
_endpoint_route_last_version(), and the routing rules are injected by
_endpoint_routing_rules(). Both are classmethods, so neither has self.env
and both reach the endpoint registry through http.request.env. Off a
request that proxy is unbound and raises "RuntimeError: object is not
bound".

Odoo 19 calls routing_map() that way: website.technical.page builds its
SQL view from get_static_routes(), which walks the map, and website's
TestWebsiteTechnicalPage exercises it from a TransactionCase. Crons and
`odoo shell` hit the same wall. The existing tests never caught it
because they all wrap their calls in MockRequest.

routing_map() is a model method, so self.env already holds a cursor and
the request was never needed. Make the helpers instance methods reading
self.env: _endpoint_routing_rules() and _endpoint_route_last_version(),
plus the two they call, _endpoint_route_registry() and
_get_routing_map_last_version(), which only took an env argument because
their classmethod callers had none of their own. The classmethod shape
dates from 16.0, where core's routing_map() was itself a classmethod.
All call sites are in this module, and the tests already call
_endpoint_route_last_version() on a recordset.

last_version() reads a sequence, so any cursor on the database returns
the same value and the cache key is unchanged.

Add a regression test that builds the map with no request bound.
@DMopen2s
DMopen2s force-pushed the 20.0-mig-endpoint_route_handler branch from 1102b0f to db207a7 Compare November 4, 2026 22:21
@oca-cla-bot

oca-cla-bot Bot commented Nov 4, 2026

Copy link
Copy Markdown

The following commit authors need to sign the Contributor License Agreement:

CLA not signed

@DMopen2s
DMopen2s force-pushed the 20.0-mig-endpoint_route_handler branch from db207a7 to 33b42e7 Compare November 5, 2026 16:23
@OpenSourceSolutions-open2s

Copy link
Copy Markdown

Hi @simahawk, @NiklasGeier,

The PR has been updated and all CI tests (test with Odoo, test with OCB) and pre-commit checks are now passing green:

  1. Commit History & Structure (@simahawk):

    • Preserved full git history from 19.0 via git format-patch / git am.
    • Separate formatting commit: [IMP] endpoint_route_handler: pre-commit auto fixes.
    • Migration commit: [MIG] endpoint_route_handler: Migration to 20.0 (without [20.0] in git commit title).
  2. Routing map without request context (@NiklasGeier):

    • In models/ir_http.py, _endpoint_route_registry() now falls back to self.env.cr when http.request is not bound (odoo shell, crons, website.technical.page), while safely using http.request.env.cr during HTTP/test requests.
    • Restored and validated test_routing_map_no_request in tests/test_endpoint.py.
  3. bearer authentication support (@NiklasGeier):

    • Restored bearer in _selection_auth_type().
    • Configured bearer_scope = "rpc" and save_session = False in _get_routing_info(), as required by Odoo 20.
    • Added unit test coverage in test_auth_type_routing_info.
  4. Cache consistency in multi-env / Odoo 20:

    • In Odoo 20, transaction-layer caches (ormcache) require invalidating the base routing layer on version changes; this has been fixed in ir.http.routing_map(), achieving 100% test pass rate across environments.

Regarding the CLA check:
The bot is flagging three authors from the inherited 19.0 history (omar.assouma@dataone.eu, oriol.miranda@forgeflow.com, camille.morand@camptocamp.com).
@OriolMForgeFlow @camille-morand @omarassouma could you please sign the CLA at https://clabot.odoo-community.org/ if needed?

Could you please review when you have a chance? Thank you!


@api.ormcache("key", "self._endpoint_route_last_version()", cache="routing")
def routing_map(self, key=None):
self.env.transaction.invalidate_ormcache("routing")

@NiklasGeier NiklasGeier Nov 7, 2026 •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the update!

I think this line maybe breaks the cache. ormcache gets the cache layer before the method runs and stores the result there after (odoo/orm/cache.py:143, 178). invalidate_ormcache inside the method replaces that layer, so the result is never really cached and the map gets rebuilt on every request. It also clears routing.rewrites and templates.cached_values each time and signals all workers on commit.

Should be fine to just remove it, register/unregister already invalidate when endpoints change and other workers get that through the normal signaling after commit.

test_cross_env_consistency will probably fail then, env2 needs a env2.transaction.invalidate_ormcache("routing") before routing_map since it only sees env1's invalidation after commit.

Suggested change
self.env.transaction.invalidate_ormcache("routing")

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

mod:endpoint_route_handler Module endpoint_route_handler series:20.0

Projects

None yet

Development

Successfully merging this pull request may close these issues.