diff --git a/lib/galaxy/tool_shed/util/shed_util_common.py b/lib/galaxy/tool_shed/util/shed_util_common.py index 648b9de4eaf..90b8079f8a0 100644 --- a/lib/galaxy/tool_shed/util/shed_util_common.py +++ b/lib/galaxy/tool_shed/util/shed_util_common.py @@ -1,5 +1,6 @@ import logging import re +from urllib.parse import quote from galaxy import util from galaxy.tool_shed.util import repository_util @@ -149,7 +150,10 @@ def set_image_paths(app, text, encoded_repository_id=None, tool_shed_repository= # We're in the tool shed. route_to_images = f"/repository/static/images/{encoded_repository_id}" elif tool_shed_repository and tool_id and tool_version: - route_to_images = f"shed_tool_static/{tool_shed_repository.tool_shed}/{tool_shed_repository.owner}/{tool_shed_repository.name}/{tool_id}/{tool_version}" + route_to_images = quote( + f"shed_tool_static/{tool_shed_repository.tool_shed}/{tool_shed_repository.owner}/{tool_shed_repository.name}/{tool_id}/{tool_version}", + safe="/", + ) else: raise Exception( "encoded_repository_id or tool_shed_repository and tool_id and tool_version must be provided" diff --git a/lib/galaxy/tools/parameters/basic.py b/lib/galaxy/tools/parameters/basic.py index 4b3c46d47f9..7cd07aabb4d 100644 --- a/lib/galaxy/tools/parameters/basic.py +++ b/lib/galaxy/tools/parameters/basic.py @@ -2451,15 +2451,25 @@ class DataToolParameter(BaseDataToolParameter): # Route each to the correct options list by type so the client can # match them by id *and* src. for value in job_input_values: - if isinstance(value, (HistoryDatasetCollectionAssociation, HistoryDatasetAssociation)): + if isinstance(value, HistoryDatasetCollectionAssociation): + # HDCAs are handled by the dataset collections section below; + # only add here if not visible in the current history. + if value.deleted or not value.visible or value.history != history: + if value.deleted: + state = "deleted" + elif not value.visible: + state = "hidden" + else: + state = "not in current history" + append(d["options"]["hdca"], value, f"({state}) {value.name}", "hdca", True) + elif isinstance(value, HistoryDatasetAssociation): if value.deleted: state = "deleted" elif not value.visible: state = "hidden" else: state = "not in current history" - src = "hdca" if isinstance(value, HistoryDatasetCollectionAssociation) else "hda" - append(d["options"][src], value, f"({state}) {value.name}", src, True) + append(d["options"]["hda"], value, f"({state}) {value.name}", "hda", True) elif isinstance(value, DatasetCollectionElement): append_dce(value) elif isinstance(value, LibraryDatasetDatasetAssociation): diff --git a/lib/galaxy/tools/parameters/dynamic_options.py b/lib/galaxy/tools/parameters/dynamic_options.py index a736a0d385b..3474df91b9b 100644 --- a/lib/galaxy/tools/parameters/dynamic_options.py +++ b/lib/galaxy/tools/parameters/dynamic_options.py @@ -1079,10 +1079,10 @@ def _get_ref_data(other_values, ref_name): if is_runtime_value(ref): return [] raise ValueError - if isinstance(ref, DatasetCollectionElement) and ref.hda: - ref = ref.hda + if isinstance(ref, DatasetCollectionElement): + return ref.dataset_instances if isinstance(ref, (DatasetFilenameWrapper, HistoryDatasetAssociation, LibraryDatasetDatasetAssociation)): - ref = [ref] + return [ref] elif isinstance(ref, HistoryDatasetCollectionAssociation): - ref = ref.to_hda_representative(multiple=True) + return ref.to_hda_representative(multiple=True) return ref diff --git a/lib/galaxy/webapps/galaxy/controllers/shed_tool_static.py b/lib/galaxy/webapps/galaxy/controllers/shed_tool_static.py index 069ff8a8f01..6e88939b2ee 100644 --- a/lib/galaxy/webapps/galaxy/controllers/shed_tool_static.py +++ b/lib/galaxy/webapps/galaxy/controllers/shed_tool_static.py @@ -2,7 +2,10 @@ import logging import os from galaxy import web -from galaxy.exceptions import RequestParameterInvalidException +from galaxy.exceptions import ( + ObjectNotFound, + RequestParameterInvalidException, +) from galaxy.util.path import ( join, safe_contains, @@ -34,6 +37,8 @@ class ShedToolStatic(BaseUIController): """ guid = "/".join((shed, "repos", owner, repo, tool, version)) tool = trans.app.toolbox.get_tool(guid) + if tool is None: + raise ObjectNotFound(f"Could not find tool with guid '{guid}'.") repo_path = os.path.abspath(tool._repository_dir) found_path = None diff --git a/lib/galaxy_test/api/test_tools.py b/lib/galaxy_test/api/test_tools.py index 76bcffb62c4..603a4585442 100644 --- a/lib/galaxy_test/api/test_tools.py +++ b/lib/galaxy_test/api/test_tools.py @@ -286,6 +286,26 @@ class TestToolsApi(ApiTestCase, TestsTools): assert "hg18_value" in option_values assert "mm10_value" in option_values + @skip_without_tool("dbkey_filter_collection_input") + def test_run_dbkey_filter_nested_collection_dce(self): + with self.dataset_populator.test_history() as history_id: + list_list = self.dataset_collection_populator.create_list_of_list_in_history(history_id, wait=True).json() + # Set dbkey on the datasets in the inner list + for outer_element in list_list["elements"]: + for inner_element in outer_element["object"]["elements"]: + hda_id = inner_element["object"]["id"] + self.dataset_populator._put( + f"histories/{history_id}/contents/{hda_id}", {"genome_build": "hg19"}, json=True + ) + # Get DCE ID of the inner list element - this is a DatasetCollectionElement + # wrapping a child collection (not an HDA) + dce_id = list_list["elements"][0]["id"] + inputs = { + "inputs": {"src": "dce", "id": dce_id}, + "index": "hg19_value", + } + self._run("dbkey_filter_collection_input", history_id, inputs, assert_ok=True) + @skip_without_tool("cheetah_problem_unbound_var_input") def test_legacy_biotools_xref_injection(self): url = self._api_url("tools/cheetah_problem_unbound_var_input") diff --git a/test/unit/tool_shed/test_shed_util_common.py b/test/unit/tool_shed/test_shed_util_common.py new file mode 100644 index 00000000000..3b49dfbec9b --- /dev/null +++ b/test/unit/tool_shed/test_shed_util_common.py @@ -0,0 +1,56 @@ +from types import SimpleNamespace + +from galaxy.tool_shed.util.shed_util_common import set_image_paths + + +def test_set_image_paths_encodes_special_characters_in_tool_id(): + tool_shed_repository = SimpleNamespace( + tool_shed="toolshed.g2.bx.psu.edu", + owner="devteam", + name="emboss_5", + ) + text = ".. image:: static/images/isochore.png" + result = set_image_paths( + app=None, + text=text, + tool_shed_repository=tool_shed_repository, + tool_id="EMBOSS: isochore47", + tool_version="5.0.0.1", + ) + assert "EMBOSS%3A%20isochore47" in result + assert "EMBOSS: isochore47" not in result + + +def test_set_image_paths_preserves_slashes_in_route(): + tool_shed_repository = SimpleNamespace( + tool_shed="toolshed.g2.bx.psu.edu", + owner="devteam", + name="emboss_5", + ) + text = ".. image:: isochore.png" + result = set_image_paths( + app=None, + text=text, + tool_shed_repository=tool_shed_repository, + tool_id="isochore", + tool_version="5.0.0", + ) + assert "shed_tool_static/toolshed.g2.bx.psu.edu/devteam/emboss_5/isochore/5.0.0/" in result + + +def test_set_image_paths_does_not_modify_http_urls(): + tool_shed_repository = SimpleNamespace( + tool_shed="toolshed.g2.bx.psu.edu", + owner="devteam", + name="emboss_5", + ) + text = ".. image:: https://example.com/image.png" + result = set_image_paths( + app=None, + text=text, + tool_shed_repository=tool_shed_repository, + tool_id="mytool", + tool_version="1.0", + ) + assert ".. image:: https://example.com/image.png" in result + assert "shed_tool_static" not in result