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