diff --git a/lib/galaxy/tools/lazy_toolbox.py b/lib/galaxy/tools/lazy_toolbox.py index 1900f4d3434..fdd39ab0a86 100644 --- a/lib/galaxy/tools/lazy_toolbox.py +++ b/lib/galaxy/tools/lazy_toolbox.py @@ -41,10 +41,7 @@ from galaxy.tool_util.toolbox.base import ToolConfRepository from galaxy.util.tool_version import remove_version_from_guid from galaxy.tool_util.parser import get_tool_source from galaxy.tool_util.toolbox.lineages.interface import ToolLineage -from galaxy.tool_util.toolbox.panel import ( - panel_item_types, - ToolSection, -) +from galaxy.tool_util.toolbox.panel import ToolSection from . import ( create_tool_from_source, ToolBox, @@ -1240,33 +1237,7 @@ class LazyToolBox(ToolBox): ] return [] - # === Override to_dict for API responses === - - def to_dict( - self, trans, in_panel: bool = True, tool_help: bool = False, view: Optional[str] = None, **kwds - ) -> list[dict[str, Any]]: - """Toolbox API serialisation. - - For ``in_panel=False`` (the flat ``/api/tools`` listing the - Galaxy client uses), walk ``_tools_by_id`` directly: every value - is a ``LazyTool`` stub or a real ``Tool``, both implement - ``ToolFilterContext`` (``allow_user_access``, ``require_login``, - ``tool_type``, ...) and ``to_dict(link_details=False)`` returns - the listing-shaped dict from the entry without parsing XML. - - For ``in_panel=True`` (the section-aware response), defer to the - parent — it walks the eager-populated ``_tool_panel_view_rendered`` - which already holds ``LazyTool`` stubs from the seam-driven boot. - """ - if in_panel: - return super().to_dict(trans, in_panel=True, tool_help=tool_help, view=view, **kwds) - - filter_method = self._build_filter_method(trans) - rval = [] - for _tool_id, tool in list(self._tools_by_id.items()): - if not filter_method(tool, panel_item_types.TOOL): - continue - rval.append(tool.to_dict(trans, link_details=False)) - - log.debug("LazyToolBox.to_dict: returning %d tools (in_panel=False)", len(rval)) - return rval + # ``to_dict`` is NOT overridden: ``AbstractToolBox.to_dict`` runs the + # ``FilterFactory`` pass for both the panel and the flat listing, and + # its ``get_tool_to_dict`` serves ``LazyTool`` stubs via + # ``to_panel_entry`` — filtered AND non-materialising. diff --git a/lib/galaxy/webapps/galaxy/services/tools.py b/lib/galaxy/webapps/galaxy/services/tools.py index 85d12db4649..fbfd3b5d1af 100644 --- a/lib/galaxy/webapps/galaxy/services/tools.py +++ b/lib/galaxy/webapps/galaxy/services/tools.py @@ -656,11 +656,12 @@ class ToolsService(ServiceBase): For panel listings or when the index is unavailable, falls back to the traditional toolbox. """ - if not in_panel: - lazy_toolbox = self._get_lazy_toolbox(trans) - if lazy_toolbox and lazy_toolbox.tool_index: - entries = lazy_toolbox.tool_index.list_all() - return [entry.to_api_dict() for entry in entries] + # Both modes go through ``AbstractToolBox.to_dict``: the flat listing + # runs the ``FilterFactory`` pass (admin/user tool filters and + # ``allow_user_access``) over every tool, and ``get_tool_to_dict`` + # serves ``LazyTool`` stubs from the index without materialising — + # so lazy mode stays O(1) parses while honoring the same filters as + # the eager toolbox. return trans.app.toolbox.to_dict(trans, in_panel=in_panel, tool_help=tool_help, view=view) def search_tools( @@ -677,5 +678,21 @@ class ToolsService(ServiceBase): :class:`ToolBoxSearch` walking ``tool_cache``. Both expose the same ``search(q, panel_view, config)`` interface, so this method doesn't branch on which toolbox flavour is active. + + Every hit is resolved through :meth:`_get_tool` so per-tool access + control (``allow_user_access``, e.g. ``require_login`` tools for + anonymous users) filters the results — same contract as the old + controller loop. In lazy mode ``get_tool`` returns the registered + ``LazyTool`` stub, so this does not materialise the hits. """ - return list(self._search(query, view) or []) + results: list[str] = [] + for hit in self._search(query, view) or []: + try: + tool = self._get_tool(trans, hit, user=trans.user) + if tool: + results.append(tool.id) + except exceptions.AuthenticationFailed: + pass + except exceptions.ObjectNotFound: + pass + return results