diff --git a/client/src/components/Workflow/Editor/Forms/FormTool.vue b/client/src/components/Workflow/Editor/Forms/FormTool.vue
index 0a10578d2e8d..5ea1f49bbcf9 100644
--- a/client/src/components/Workflow/Editor/Forms/FormTool.vue
+++ b/client/src/components/Workflow/Editor/Forms/FormTool.vue
@@ -116,7 +116,10 @@ export default {
},
computed: {
id() {
- return `${this.stepId}:${this.configForm.id}`;
+ // Make sure we compute a unique id. Local tools don't include the version in the id,
+ // but updating tool form when switching tool versions requires that the id changes.
+ // (see https://github.com/galaxyproject/galaxy/blob/f5e07b11f0996e75b2b6f27896b2301d8fa8717d/client/src/components/Form/FormDisplay.vue#L108)
+ return `${this.stepId}:${this.configForm.id}/${this.configForm.version}`;
},
toolCardId() {
return `${this.stepId}`;
diff --git a/lib/galaxy/managers/workflows.py b/lib/galaxy/managers/workflows.py
index fd955910be3d..89cc96698b83 100644
--- a/lib/galaxy/managers/workflows.py
+++ b/lib/galaxy/managers/workflows.py
@@ -1461,8 +1461,14 @@ def _workflow_to_dict_export(self, trans, stored=None, workflow=None, internal=F
if not annotation_str and annotation_owner:
annotation_str = self.get_item_annotation_str(trans.sa_session, annotation_owner, step) or ""
content_id = module.get_content_id() if allow_upgrade else step.content_id
- # Export differences for backward compatibility
- tool_state = module.get_export_state()
+ errors = {}
+ try:
+ tool_state = module.get_export_state()
+ except ValueError:
+ # Fix state if necessary
+ errors = module.check_and_update_state()
+ tool_state = module.get_export_state()
+
# Step info
step_dict = {
"id": step.order_index,
@@ -1472,7 +1478,7 @@ def _workflow_to_dict_export(self, trans, stored=None, workflow=None, internal=F
"tool_version": module.get_version() if allow_upgrade else step.tool_version,
"name": module.get_name(),
"tool_state": json.dumps(tool_state),
- "errors": module.get_errors(),
+ "errors": errors or module.get_errors(),
"uuid": str(step.uuid),
"label": step.label or None,
"annotation": annotation_str,
diff --git a/lib/galaxy/webapps/galaxy/api/workflows.py b/lib/galaxy/webapps/galaxy/api/workflows.py
index 686980264f81..54386e7596c6 100644
--- a/lib/galaxy/webapps/galaxy/api/workflows.py
+++ b/lib/galaxy/webapps/galaxy/api/workflows.py
@@ -541,15 +541,18 @@ def build_module(self, trans: GalaxyWebTransaction, payload=None):
module = module_factory.from_dict(trans, payload, from_tool_form=True)
if "tool_state" not in payload:
module_state: Dict[str, Any] = {}
- populate_state(trans, module.get_inputs(), inputs, module_state, check=False)
+ errors: Dict[str, str] = {}
+ populate_state(trans, module.get_inputs(), inputs, module_state, errors=errors, check=True)
module.recover_state(module_state, from_tool_form=True)
+ module.check_and_update_state()
step_dict = {
"name": module.get_name(),
- "tool_state": module.get_state(),
+ "tool_state": module_state,
"content_id": module.get_content_id(),
"inputs": module.get_all_inputs(connectable_only=True),
"outputs": module.get_all_outputs(),
"config_form": module.get_config_form(),
+ "errors": errors or None,
}
if payload["type"] == "tool":
step_dict["tool_version"] = module.get_version()
diff --git a/lib/galaxy_test/api/test_workflows.py b/lib/galaxy_test/api/test_workflows.py
index a5b884ec3b8a..8ff66bf90e5e 100644
--- a/lib/galaxy_test/api/test_workflows.py
+++ b/lib/galaxy_test/api/test_workflows.py
@@ -970,6 +970,36 @@ def test_refactor(self):
workflow_dict = self.workflow_populator.download_workflow(workflow_id)
assert workflow_dict["steps"]["0"]["label"] == "new_label"
+ def test_refactor_tool_state_upgrade(self):
+ workflow_id = self.workflow_populator.upload_yaml_workflow(
+ """
+class: GalaxyWorkflow
+inputs: {}
+steps:
+ multiple_versions_changes:
+ tool_id: multiple_versions_changes
+ tool_version: "0.1"
+ state:
+ inttest: 1
+ cond:
+ bool_to_select: false
+"""
+ )
+ actions = [{"action_type": "upgrade_all_steps"}]
+ refactor_response = self.workflow_populator.refactor_workflow(workflow_id, actions, dry_run=True)
+ refactor_response.raise_for_status()
+ refactor_result = refactor_response.json()
+ upgrade_result = refactor_result["action_executions"][0]
+ assert upgrade_result["action"]["action_type"] == "upgrade_all_steps"
+ message_one, message_two = upgrade_result["messages"]
+ assert message_one["message"] == "No value found for 'floattest'. Using default: '1.0'."
+ assert message_one["input_name"] == "floattest"
+ assert message_two["message"] == "The selected case is unavailable/invalid. Using default: 'b'."
+ assert message_two["input_name"] == "cond|bool_to_select"
+
+ refactor_response = self.workflow_populator.refactor_workflow(workflow_id, actions, dry_run=False)
+ refactor_response.raise_for_status()
+
def test_update_no_tool_id(self):
workflow_object = self.workflow_populator.load_workflow(name="test_import")
upload_response = self.__test_upload(workflow=workflow_object)
diff --git a/test/functional/tools/multiple_versions_changes_v01.xml b/test/functional/tools/multiple_versions_changes_v01.xml
index b7b58ed3c65f..da49bbcda794 100644
--- a/test/functional/tools/multiple_versions_changes_v01.xml
+++ b/test/functional/tools/multiple_versions_changes_v01.xml
@@ -4,6 +4,11 @@
+
+
+
+
+
diff --git a/test/functional/tools/multiple_versions_changes_v02.xml b/test/functional/tools/multiple_versions_changes_v02.xml
index e10c84b87922..14ef9dafeebc 100644
--- a/test/functional/tools/multiple_versions_changes_v02.xml
+++ b/test/functional/tools/multiple_versions_changes_v02.xml
@@ -5,6 +5,15 @@
+
+
+
+
+
+
+
+
+
diff --git a/test/integration/test_workflow_refactoring.py b/test/integration/test_workflow_refactoring.py
index 4fd5a2d712ce..6a03d818ce31 100644
--- a/test/integration/test_workflow_refactoring.py
+++ b/test/integration/test_workflow_refactoring.py
@@ -559,7 +559,7 @@ def test_tool_version_upgrade_state_added(self):
assert len(action_executions) == 1
messages = action_executions[0].messages
- assert len(messages) == 1
+ assert len(messages) == 2
message = messages[0]
assert message.message_type == RefactorActionExecutionMessageTypeEnum.tool_state_adjustment
assert message.order_index == 0