mirror of
https://github.com/galaxyproject/galaxy.git
synced 2026-09-24 16:30:27 +08:00
Toolbox filters: typed ToolFilterContext + parity for lazy /api/tools
``LazyToolBox.to_dict(in_panel=False)`` was serving entries straight from the index, bypassing ``FilterFactory`` — so admin and user toolbox filters silently didn't apply on the lazy path. Introduce a typed ``ToolFilterContext`` protocol that documents the attribute surface filters can read; both ``Tool`` and ``ToolIndexEntry`` satisfy it. ``ToolIndexEntry`` grows ``require_login``, ``tool_type``, and ``tags`` so the stock filters (``_not_hidden``, ``_handle_authorization``) work against the entry without materialising a Tool. ``_handle_authorization`` does the ``DataManagerTool`` admin check inline (reads ``tool_type`` and ``context.trans.app.config``), which avoids needing an ``allow_user_access`` method on the entry. ``LazyToolBox.to_dict`` now runs ``_build_filter_method`` against every entry — the same code path the eager toolbox uses.
This commit is contained in:
@@ -51,6 +51,16 @@ class ToolIndexEntry:
|
||||
# === Status ===
|
||||
hidden: bool = False
|
||||
disabled: bool = False
|
||||
require_login: bool = False
|
||||
|
||||
# === Filter metadata ===
|
||||
# ``tool_type`` is the Tool subclass key (``default``, ``data_manager``,
|
||||
# ``interactive_tool``, ``data_source``, ...). Filter authors and
|
||||
# ``DataManagerTool.allow_user_access`` (admin-only) both branch on this.
|
||||
tool_type: str = "default"
|
||||
# User-facing tags from ``<tool>`` config (distinct from ``labels``).
|
||||
# Surfaced for custom tool filters that bucket tools by tag.
|
||||
tags: list[str] = field(default_factory=list)
|
||||
|
||||
# === Tests (for /api/tools/tests_summary) ===
|
||||
test_count: int = 0
|
||||
@@ -135,6 +145,9 @@ class ToolIndexEntry:
|
||||
"source_class": self.source_class,
|
||||
"hidden": self.hidden,
|
||||
"disabled": self.disabled,
|
||||
"require_login": self.require_login,
|
||||
"tool_type": self.tool_type,
|
||||
"tags": self.tags,
|
||||
"test_count": self.test_count,
|
||||
"requirements": self.requirements,
|
||||
"container_requirements": self.container_requirements,
|
||||
@@ -169,6 +182,9 @@ class ToolIndexEntry:
|
||||
source_class=data.get("source_class", "XmlToolSource"),
|
||||
hidden=data.get("hidden", False),
|
||||
disabled=data.get("disabled", False),
|
||||
require_login=data.get("require_login", False),
|
||||
tool_type=data.get("tool_type", "default"),
|
||||
tags=data.get("tags", []),
|
||||
test_count=data.get("test_count", 0),
|
||||
requirements=data.get("requirements", []),
|
||||
container_requirements=data.get("container_requirements", []),
|
||||
|
||||
@@ -47,6 +47,7 @@ from galaxy.tool_util.toolbox.filters import FilterFactory
|
||||
from galaxy.tool_util.toolbox.lineages.factory import LazyLineageMap
|
||||
from galaxy.tool_util.toolbox.lineages.interface import ToolLineage
|
||||
from galaxy.tool_util.toolbox.panel import (
|
||||
panel_item_types,
|
||||
ToolPanelElements,
|
||||
ToolSection,
|
||||
)
|
||||
@@ -1287,6 +1288,32 @@ class LazyToolBox(ToolBox):
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# ``parse_require_login`` is what ``Tool.parse`` calls to set
|
||||
# ``tool.require_login``. Default False matches ``Tool.__init__``.
|
||||
require_login = False
|
||||
if hasattr(tool_source, "parse_require_login"):
|
||||
try:
|
||||
require_login = bool(tool_source.parse_require_login(False))
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# ``tool_type`` is the Tool subclass key (``data_manager``,
|
||||
# ``interactive_tool``, etc.). Stock filters branch on this for the
|
||||
# admin-only check on ``DataManagerTool``; custom filters use it
|
||||
# to categorize.
|
||||
tool_type = "default"
|
||||
if hasattr(tool_source, "parse_tool_type"):
|
||||
try:
|
||||
tool_type = tool_source.parse_tool_type() or "default"
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# ``tags`` are currently not exposed via the ToolSource parser API.
|
||||
# The field on ``ToolIndexEntry`` is here so admin/user filters that
|
||||
# bucket tools by tag have a place to read from when the populator
|
||||
# learns to fill it.
|
||||
tags: list[str] = []
|
||||
|
||||
return ToolIndexEntry(
|
||||
id=tool_id,
|
||||
uuid=uuid_val,
|
||||
@@ -1296,6 +1323,9 @@ class LazyToolBox(ToolBox):
|
||||
source_hash=source_hash,
|
||||
source_class=source_class,
|
||||
hidden=hidden,
|
||||
require_login=require_login,
|
||||
tool_type=tool_type,
|
||||
tags=tags,
|
||||
indexed_at=datetime.utcnow(),
|
||||
)
|
||||
except Exception as e:
|
||||
@@ -2291,8 +2321,11 @@ class LazyToolBox(ToolBox):
|
||||
Create a dictionary representation of the toolbox.
|
||||
|
||||
For the *flat* listing (``in_panel=False``) we serve straight from
|
||||
the index — no Tool loading needed. For the panel listing
|
||||
(``in_panel=True``, e.g. ``tools?in_panel=True&view=custom_13``)
|
||||
the index — no Tool loading needed — but still run every entry
|
||||
through ``FilterFactory`` so admin / user toolbox filters apply
|
||||
identically to the eager path. Filters take ``ToolFilterContext``,
|
||||
which both ``Tool`` and ``ToolIndexEntry`` satisfy. For the panel
|
||||
listing (``in_panel=True``, e.g. ``tools?in_panel=True&view=custom_13``)
|
||||
we defer to the parent: it walks ``_tool_panel_view_rendered``
|
||||
which is built by ``apply_view`` against
|
||||
``_integrated_tool_panel`` and produces the section-aware
|
||||
@@ -2307,16 +2340,17 @@ class LazyToolBox(ToolBox):
|
||||
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 = []
|
||||
# Return data directly from index - no tool loading needed!
|
||||
for _tool_id, entry in self._tool_index.entries.items():
|
||||
# Skip hidden tools unless requested
|
||||
if entry.hidden and not kwds.get("include_hidden", False):
|
||||
# ``filter_method`` honours ``_not_hidden`` + ``_handle_authorization``
|
||||
# (the always-on stock filters) plus any ``tool_filters`` /
|
||||
# ``user_tool_filters`` the operator configured. Both stock filters
|
||||
# read only fields on ``ToolFilterContext``, which ``ToolIndexEntry``
|
||||
# exposes — no Tool materialisation needed.
|
||||
if not filter_method(entry, panel_item_types.TOOL):
|
||||
continue
|
||||
|
||||
# Convert index entry to API dict format
|
||||
tool_dict = self._index_entry_to_api_dict(entry)
|
||||
rval.append(tool_dict)
|
||||
rval.append(self._index_entry_to_api_dict(entry))
|
||||
|
||||
log.debug(f"LazyToolBox.to_dict: returning {len(rval)} tools from index (no loading)")
|
||||
return rval
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
#This is a sample file distributed with Galaxy that enables tools
|
||||
#to use a directory of BWA indexed sequences data files. You will need
|
||||
#to create these data files and then create a bwa_index.loc file
|
||||
#similar to this one (store it in this directory) that points to
|
||||
#the directories in which those files are stored. The bwa_index.loc
|
||||
#file has this format (longer white space characters are TAB characters):
|
||||
#
|
||||
#<unique_build_id> <dbkey> <display_name> <file_path>
|
||||
#
|
||||
#So, for example, if you had phiX indexed stored in
|
||||
#/depot/data2/galaxy/phiX/base/,
|
||||
#then the bwa_index.loc entry would look like this:
|
||||
#
|
||||
#phiX174 phiX phiX Pretty /depot/data2/galaxy/phiX/base/phiX.fa
|
||||
#
|
||||
#and your /depot/data2/galaxy/phiX/base/ directory
|
||||
#would contain phiX.fa.* files:
|
||||
#
|
||||
#-rw-r--r-- 1 james universe 830134 2005-09-13 10:12 phiX.fa.amb
|
||||
#-rw-r--r-- 1 james universe 527388 2005-09-13 10:12 phiX.fa.ann
|
||||
#-rw-r--r-- 1 james universe 269808 2005-09-13 10:12 phiX.fa.bwt
|
||||
#...etc...
|
||||
#
|
||||
#Your bwa_index.loc file should include an entry per line for each
|
||||
#index set you have stored. The "file" in the path does not actually
|
||||
#exist, but it is the prefix for the actual index files. For example:
|
||||
#
|
||||
#phiX174 phiX phiX174 /depot/data2/galaxy/phiX/base/phiX.fa
|
||||
#hg18canon hg18 hg18 Canonical /depot/data2/galaxy/hg18/base/hg18canon.fa
|
||||
#hg18full hg18 hg18 Full /depot/data2/galaxy/hg18/base/hg18full.fa
|
||||
#/orig/path/hg19.fa hg19 hg19 /depot/data2/galaxy/hg19/base/hg19.fa
|
||||
#...etc...
|
||||
#
|
||||
#Note that for backwards compatibility with workflows, the unique ID of
|
||||
#an entry must be the path that was in the original loc file, because that
|
||||
#is the value stored in the workflow for that parameter. That is why the
|
||||
#hg19 entry above looks odd. New genomes can be better-looking.
|
||||
#
|
||||
@@ -0,0 +1,38 @@
|
||||
#This is a sample file distributed with Galaxy that enables tools
|
||||
#to use a directory of BWA indexed sequences data files. You will need
|
||||
#to create these data files and then create a bwa_index.loc file
|
||||
#similar to this one (store it in this directory) that points to
|
||||
#the directories in which those files are stored. The bwa_index.loc
|
||||
#file has this format (longer white space characters are TAB characters):
|
||||
#
|
||||
#<unique_build_id> <dbkey> <display_name> <file_path>
|
||||
#
|
||||
#So, for example, if you had phiX indexed stored in
|
||||
#/depot/data2/galaxy/phiX/base/,
|
||||
#then the bwa_index.loc entry would look like this:
|
||||
#
|
||||
#phiX174 phiX phiX Pretty /depot/data2/galaxy/phiX/base/phiX.fa
|
||||
#
|
||||
#and your /depot/data2/galaxy/phiX/base/ directory
|
||||
#would contain phiX.fa.* files:
|
||||
#
|
||||
#-rw-r--r-- 1 james universe 830134 2005-09-13 10:12 phiX.fa.amb
|
||||
#-rw-r--r-- 1 james universe 527388 2005-09-13 10:12 phiX.fa.ann
|
||||
#-rw-r--r-- 1 james universe 269808 2005-09-13 10:12 phiX.fa.bwt
|
||||
#...etc...
|
||||
#
|
||||
#Your bwa_index.loc file should include an entry per line for each
|
||||
#index set you have stored. The "file" in the path does not actually
|
||||
#exist, but it is the prefix for the actual index files. For example:
|
||||
#
|
||||
#phiX174 phiX phiX174 /depot/data2/galaxy/phiX/base/phiX.fa
|
||||
#hg18canon hg18 hg18 Canonical /depot/data2/galaxy/hg18/base/hg18canon.fa
|
||||
#hg18full hg18 hg18 Full /depot/data2/galaxy/hg18/base/hg18full.fa
|
||||
#/orig/path/hg19.fa hg19 hg19 /depot/data2/galaxy/hg19/base/hg19.fa
|
||||
#...etc...
|
||||
#
|
||||
#Note that for backwards compatibility with workflows, the unique ID of
|
||||
#an entry must be the path that was in the original loc file, because that
|
||||
#is the value stored in the workflow for that parameter. That is why the
|
||||
#hg19 entry above looks odd. New genomes can be better-looking.
|
||||
#
|
||||
Reference in New Issue
Block a user