diff --git a/lib/galaxy/tool_util/xsd/galaxy.xsd b/lib/galaxy/tool_util/xsd/galaxy.xsd
index 3a60a9ce2e12..d9885d4cce71 100644
--- a/lib/galaxy/tool_util/xsd/galaxy.xsd
+++ b/lib/galaxy/tool_util/xsd/galaxy.xsd
@@ -3938,8 +3938,8 @@ Name | Description
``$__tool_data_path__`` | ``config/galaxy.ini``'s tool_data_path value
``$__root_dir__`` | Top-level Galaxy source directory made absolute via ``os.path.abspath()``
``$__datatypes_config__`` | ``config/galaxy.ini``'s datatypes_config value
-``$__user_id__`` | Email's numeric ID (id column of ``galaxy_user`` table in the database)
-``$__user_email__`` | User's email address
+``$__user_id__`` | Numeric ID of user (id column of ``galaxy_user`` table in the database)
+``$__user_email__`` | Email address of user
``$__app__`` | The ``galaxy.app.UniverseApplication`` instance, gives access to all other configuration file variables (e.g. $__app__.config.output_size_limit). Should be used as a last resort, may go away in future releases.
``$__target_datatype__`` | Only available in converter tools when run internally by Galaxy. Contains the target datatype of the conversion
@@ -4039,6 +4039,16 @@ of "type" specified for this expression block.
+
+
+ Select a request method, defaults to GET if unspecified
+
+
+
+
+
+
+
```
+### ``from_url``
+
+The following example demonstrates getting options from a third-party server
+with server side requests.
+
+```xml
+
+
+
+
+```
+
+Here a GET request is made to [https://usegalaxy.org/api/genomes](https://usegalaxy.org/api/genomes), which returns
+an array of arrays, such as
+
+```json
+[
+ ["unspecified (?)", "?"],
+ ["A. ceylanicum Mar. 2014 (WS243/Acey_2013.11.30.genDNA/ancCey1) (ancCey1)", "ancCey1"],
+ ...
+]
+```
+Each inner array is a user-selectable option, where the first item in the inner array
+is the `name` of the option (as shown in the select field in the user interface), and
+the second option is the `value` that is passed on to the tool. An optional third
+element can be added to the inner array which corresponds to the `selected` state.
+If the third item is `true` then this particular option is pre-selected.
+
+A more complicated example is shown below, where a POST request is made with a templated
+request header and body. The upstream response is then also transformed using an ecma 5.1
+expression:
+
+```xml
+
+
+
+
+ {"x-api-key": "${__user__.extra_preferences.fake_api_key if $__user__ else "anon"}"}
+
+
+ {"name": "value"}
+
+
+ [header, header])
+ }]]]]>
+
+
+```
+
+The header and body templating mechanism can be used to access protected resources,
+and the `postprocess_expression` can be used to transform arbitrary JSON responses
+to arrays of `name` and `value`, or arrays of `name`, `value` and `selected`.
+
+For an example tool see [select_from_url.xml](https://github.com/galaxyproject/galaxy/tree/dev/test/functional/tools/select_from_url.xml).
+
### ``from_file``
The following example is for Blast databases. In this example users maybe select
@@ -4390,7 +4457,7 @@ used to generate dynamic options.
-
+
@@ -4407,6 +4474,16 @@ used to generate dynamic options.
Determine options from a data table.
+
+
+ Determine options from data hosted at specified URL.
+
+
+
+
+ Set the request method to use for options provided using 'from_url'.
+
+
Deprecated.
@@ -4440,9 +4517,12 @@ used to generate dynamic options.
-
-
-
+
+
+
+
+
+
Documentation for file
@@ -4451,6 +4531,20 @@ used to generate dynamic options.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Optional[str]:
+ if maybe_string is not None:
+ if maybe_string.text:
+ return maybe_string.text.strip()
+ return None
+
+
+def parse_from_url_options(elem: Element) -> Optional[FromUrlOptions]:
+ from_url = elem.get("from_url")
+ if from_url:
+ request_method = cast(Literal["GET", "POST"], elem.get("request_method", "GET"))
+ assert request_method in get_args(REQUEST_METHODS)
+ request_headers = strip_or_none(elem.find("request_headers"))
+ request_body = strip_or_none(elem.find("request_body"))
+ postprocess_expression = strip_or_none(elem.find("postprocess_expression"))
+ return FromUrlOptions(
+ from_url,
+ request_method=request_method,
+ request_headers=request_headers,
+ request_body=request_body,
+ postprocess_expression=postprocess_expression,
+ )
+ return None
+
+
+def template_or_none(template: Optional[str], context: Dict[str, Any]) -> Optional[str]:
+ if template:
+ return fill_template(template, context=context)
+ return None
+
+
def _get_ref_data(other_values, ref_name):
"""
get the list of data sets from ref_name
diff --git a/lib/galaxy/work/context.py b/lib/galaxy/work/context.py
index 8a1206018c0a..8e4fc74afc21 100644
--- a/lib/galaxy/work/context.py
+++ b/lib/galaxy/work/context.py
@@ -1,7 +1,10 @@
import abc
from typing import (
+ Any,
+ Dict,
List,
Optional,
+ Tuple,
)
from typing_extensions import Literal
@@ -42,9 +45,16 @@ def __init__(
self.__user_current_roles: Optional[List[Role]] = None
self.__history = history
self._url_builder = url_builder
+ self._short_term_cache: Dict[Tuple[str, ...], Any] = {}
self.workflow_building_mode = workflow_building_mode
self.galaxy_session = galaxy_session
+ def set_cache_value(self, args: Tuple[str, ...], value: Any):
+ self._short_term_cache[args] = value
+
+ def get_cache_value(self, args: Tuple[str, ...], default: Any = None) -> Any:
+ return self._short_term_cache.get(args, default)
+
@property
def app(self):
return self._app
diff --git a/lib/galaxy/workflow/modules.py b/lib/galaxy/workflow/modules.py
index 4682a88fa148..b823ce3d5921 100644
--- a/lib/galaxy/workflow/modules.py
+++ b/lib/galaxy/workflow/modules.py
@@ -18,7 +18,6 @@
Union,
)
-from cwl_utils.expression import do_eval
from typing_extensions import TypedDict
from galaxy import (
@@ -59,6 +58,7 @@
MappingParameters,
PartialJobExecution,
)
+from galaxy.tools.expressions import do_eval
from galaxy.tools.parameters import (
check_param,
params_to_incoming,
@@ -226,10 +226,6 @@ def evaluate_value_from_expressions(progress, step, execution_state, extra_step_
as_cwl_value = do_eval(
when_expression,
step_state,
- [{"class": "InlineJavascriptRequirement"}],
- None,
- None,
- {},
)
except Exception:
# Exception contains script and traceback, which could be helpful for debugging workflows,
diff --git a/test/functional/tools/sample_tool_conf.xml b/test/functional/tools/sample_tool_conf.xml
index ae0f15a5a7bc..8df30a78eff6 100644
--- a/test/functional/tools/sample_tool_conf.xml
+++ b/test/functional/tools/sample_tool_conf.xml
@@ -23,6 +23,7 @@
+
diff --git a/test/functional/tools/select_from_url.xml b/test/functional/tools/select_from_url.xml
new file mode 100644
index 000000000000..60a712e74631
--- /dev/null
+++ b/test/functional/tools/select_from_url.xml
@@ -0,0 +1,87 @@
+
+ '$param_value' &&
+echo '$url_param_value_postprocessed' > '$param_value_postprocessed' &&
+echo '$invalid_url_param_value_postprocessed' > '$invalid_param_value_postprocessed' &&
+echo '$url_param_value_header_and_body' > '$param_value_header_and_body'
+ ]]>
+
+
+
+
+
+
+
+
+ [v.chrom, v.len]) )
+ ]]>
+
+
+
+
+ [v.chrom, v.len])
+ } else {
+ return [["The fallback value", "default"]]
+ }
+ }]]>
+
+
+
+
+
+
+ {"x-api-key": "${__user__.extra_preferences.fake_api_key if $__user__ else "anon"}"}
+
+
+ {"name": "value"}
+
+
+ [header, header])
+ }]]>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/test/unit/app/tools/test_dynamic_options.py b/test/unit/app/tools/test_dynamic_options.py
new file mode 100644
index 000000000000..38626d65b991
--- /dev/null
+++ b/test/unit/app/tools/test_dynamic_options.py
@@ -0,0 +1,57 @@
+from galaxy.app_unittest_utils.galaxy_mock import MockApp
+from galaxy.tools.parameters.dynamic_options import DynamicOptions
+from galaxy.util import XML
+from galaxy.util.bunch import Bunch
+from galaxy.work.context import WorkRequestContext
+
+
+def get_from_url_option():
+ return DynamicOptions(
+ XML(
+ """
+
+
+ {"x-api-key": "${__user__.extra_preferences.resource_api_key if $__user__ else "anon"}"}
+
+
+ {"some_key": "some_value"}
+
+ [v.chrom, v.len])
+ } else {
+ return [["The fallback value", "default"]]
+ }
+ }]]>
+
+"""
+ ),
+ Bunch(),
+ )
+
+
+def test_dynamic_option_parsing():
+ from_url_option = get_from_url_option()
+ assert from_url_option.from_url_options
+ assert from_url_option.from_url_options.from_url == "https://usegalaxy.org/api/genomes/dm6"
+
+
+def test_dynamic_option_cache():
+ app = MockApp()
+ trans = WorkRequestContext(app=app)
+ from_url_option = get_from_url_option()
+ options = from_url_option.from_url_options
+ assert options
+ args = (options.from_url, options.request_method, options.request_body, '{"x-api-key": "anon"}')
+ trans.set_cache_value(
+ args,
+ {
+ "id": "dm6",
+ "reference": True,
+ "chrom_info": [{"chrom": "chr2L", "len": 23513712}],
+ "prev_chroms": False,
+ "next_chroms": False,
+ "start_index": 0,
+ },
+ )
+ assert from_url_option.get_options(trans, {}) == [["chr2L", "23513712", False]]