diff --git a/client/src/components/Workflow/Editor/Forms/FormDefault.test.js b/client/src/components/Workflow/Editor/Forms/FormDefault.test.js
index 91ff8e38948..12b0a9bb637 100644
--- a/client/src/components/Workflow/Editor/Forms/FormDefault.test.js
+++ b/client/src/components/Workflow/Editor/Forms/FormDefault.test.js
@@ -23,16 +23,15 @@ describe("FormDefault", () => {
nodes: [],
};
},
- getNode: () => {
- return {
- name: "node-title",
- type: "subworkflow",
- outputs: outputs,
- activeOutputs: activeOutputs,
- config_form: {
- inputs: [],
- },
- };
+ nodeId: "id",
+ nodeContentId: "id",
+ nodeLabel: "label",
+ nodeName: "node-title",
+ nodeType: "subworkflow",
+ nodeOutputs: outputs,
+ nodeActiveOutputs: activeOutputs,
+ configForm: {
+ inputs: [],
},
},
localVue,
diff --git a/client/src/components/Workflow/Editor/Forms/FormDefault.vue b/client/src/components/Workflow/Editor/Forms/FormDefault.vue
index 5434c434f0c..dedb6decf64 100644
--- a/client/src/components/Workflow/Editor/Forms/FormDefault.vue
+++ b/client/src/components/Workflow/Editor/Forms/FormDefault.vue
@@ -1,5 +1,5 @@
-
+
@@ -68,6 +68,42 @@ export default {
FormOutputLabel,
},
props: {
+ nodeName: {
+ type: String,
+ required: true,
+ },
+ nodeId: {
+ type: String,
+ required: true,
+ },
+ nodeContentId: {
+ type: String,
+ required: true,
+ },
+ nodeAnnotation: {
+ type: String,
+ required: false,
+ },
+ nodeLabel: {
+ type: String,
+ required: true,
+ },
+ nodeType: {
+ type: String,
+ required: true,
+ },
+ nodeActiveOutputs: {
+ type: Object,
+ required: true,
+ },
+ nodeOutputs: {
+ type: Array,
+ required: true,
+ },
+ configForm: {
+ type: Object,
+ required: true,
+ },
datatypes: {
type: Array,
required: true,
@@ -76,43 +112,36 @@ export default {
type: Function,
required: true,
},
- getNode: {
- type: Function,
- required: true,
- },
},
computed: {
- node() {
- return this.getNode();
- },
nodeIcon() {
- return WorkflowIcons[this.node.type];
+ return WorkflowIcons[this.nodeType];
},
workflow() {
return this.getManager();
},
id() {
- return this.node.id;
+ return this.nodeId;
},
inputs() {
- return this.node.config_form.inputs;
+ return this.configForm.inputs;
},
isSubworkflow() {
- return this.node.type == "subworkflow";
+ return this.nodeType == "subworkflow";
},
errorLabel() {
- return checkLabels(this.node.id, this.node.label, this.workflow.nodes);
+ return checkLabels(this.nodeId, this.nodeLabel, this.workflow.nodes);
},
},
methods: {
onAnnotation(newAnnotation) {
- this.$emit("onAnnotation", this.node.id, newAnnotation);
+ this.$emit("onAnnotation", this.nodeId, newAnnotation);
},
onLabel(newLabel) {
- this.$emit("onLabel", this.node.id, newLabel);
+ this.$emit("onLabel", this.nodeId, newLabel);
},
onEditSubworkflow() {
- this.$emit("onEditSubworkflow", this.node.content_id);
+ this.$emit("onEditSubworkflow", this.nodeContentId);
},
onUpgradeSubworkflow() {
this.$emit("onAttemptRefactor", [
@@ -120,10 +149,10 @@ export default {
]);
},
onChange(values) {
- this.$emit("onSetData", this.node.id, {
- id: this.node.id,
- type: this.node.type,
- content_id: this.node.content_id,
+ this.$emit("onSetData", this.nodeId, {
+ id: this.nodeId,
+ type: this.nodeType,
+ content_id: this.nodeContentId,
inputs: values,
});
},
diff --git a/client/src/components/Workflow/Editor/Forms/FormSection.vue b/client/src/components/Workflow/Editor/Forms/FormSection.vue
index 85401ff97d2..f21a23b15be 100644
--- a/client/src/components/Workflow/Editor/Forms/FormSection.vue
+++ b/client/src/components/Workflow/Editor/Forms/FormSection.vue
@@ -18,8 +18,8 @@
v-for="(output, index) in outputs"
:key="index"
:output-name="output.name"
- :active-outputs="node.activeOutputs"
- :inputs="node.inputs"
+ :active-outputs="nodeActiveOutputs"
+ :inputs="nodeInputs"
:datatypes="datatypes"
:form-data="formData"
@onInput="onInput"
@@ -41,14 +41,26 @@ export default {
type: String,
required: true,
},
- getNode: {
- type: Function,
+ nodeInputs: {
+ type: Array,
+ required: true,
+ },
+ nodeOutputs: {
+ type: Array,
+ required: true,
+ },
+ nodeActiveOutputs: {
+ type: Object,
required: true,
},
datatypes: {
type: Array,
required: true,
},
+ postJobActions: {
+ type: Object,
+ required: true,
+ },
},
data() {
return {
@@ -56,14 +68,8 @@ export default {
};
},
computed: {
- node() {
- return this.getNode();
- },
- postJobActions() {
- return this.node.postJobActions;
- },
outputs() {
- return this.node.outputs;
+ return this.nodeOutputs;
},
firstOutput() {
return this.outputs.length > 0 && this.outputs[0];
@@ -87,6 +93,34 @@ export default {
created() {
this.setFormData();
},
+ watch: {
+ formData() {
+ // The formData shape is kind of unfortunate, but it is what we have now.
+ // This should be a properly nested object whose values should be retrieved and set via a store
+ const postJobActions = {};
+ Object.entries(this.formData).forEach(([key, value]) => {
+ const [pja, outputName, actionType, name] = key.split("__", 4);
+ if (pja == "pja") {
+ const pjaKey = `${actionType}${outputName}`;
+ if (!postJobActions[pjaKey]) {
+ postJobActions[pjaKey] = {
+ action_type: actionType,
+ output_name: outputName,
+ action_arguments: {},
+ };
+ }
+ if (name) {
+ if (name == "output_name") {
+ postJobActions[pjaKey]["output_name"] = value;
+ } else {
+ postJobActions[pjaKey]["action_arguments"][name] = value;
+ }
+ }
+ }
+ });
+ this.$emit("onChange", postJobActions);
+ },
+ },
methods: {
setFormData() {
const pjas = {};
@@ -104,9 +138,8 @@ export default {
if (pjas[this.emailPayloadKey]) {
pjas[this.emailActionKey] = true;
}
- this.formData = pjas;
console.debug("FormSection - Setting new data.", this.postJobActions, pjas);
- this.$emit("onChange", this.formData);
+ this.formData = pjas;
},
setEmailAction(pjas) {
if (pjas[this.emailActionKey]) {
@@ -131,7 +164,6 @@ export default {
this.setEmailAction(this.formData);
if (changed) {
this.formData = Object.assign({}, this.formData);
- this.$emit("onChange", this.formData);
}
},
onDatatype(pjaKey, outputName, newDatatype) {
diff --git a/client/src/components/Workflow/Editor/Forms/FormTool.test.js b/client/src/components/Workflow/Editor/Forms/FormTool.test.js
index ffcdbfe88a6..4bbc93c9b0c 100644
--- a/client/src/components/Workflow/Editor/Forms/FormTool.test.js
+++ b/client/src/components/Workflow/Editor/Forms/FormTool.test.js
@@ -14,22 +14,22 @@ describe("FormTool", () => {
propsData: {
id: "input",
datatypes: [],
- getNode: () => {
- return {
- id: "id",
- config_form: {
- id: "tool_id+1.0",
- name: "tool_name",
- version: "1.0",
- description: "description",
- inputs: [],
- help: "help_text",
- versions: ["1.0", "2.0", "3.0"],
- },
- outputs: [],
- postJobActions: [],
- };
+ configForm: {
+ id: "tool_id+1.0",
+ name: "tool_name",
+ version: "1.0",
+ description: "description",
+ inputs: [],
+ help: "help_text",
+ versions: ["1.0", "2.0", "3.0"],
},
+ nodeId: "id",
+ nodeAnnotation: "",
+ nodeLabel: "",
+ nodeInputs: [],
+ nodeOutputs: [],
+ nodeActiveOutputs: {},
+ postJobActions: {},
getManager: () => {
return {};
},
@@ -46,19 +46,16 @@ describe("FormTool", () => {
it("check version change", async () => {
const dropdowns = wrapper.findAll(".dropdown-item");
- let state = wrapper.emitted().onSetData[0][1];
- expect(state.tool_version).toEqual("1.0");
- expect(state.tool_id).toEqual("tool_id+1.0");
let version = dropdowns.at(3);
expect(version.text()).toBe("Switch to 2.0");
await version.trigger("click");
- state = wrapper.emitted().onSetData[1][1];
+ let state = wrapper.emitted().onSetData[0][1];
expect(state.tool_version).toEqual("2.0");
expect(state.tool_id).toEqual("tool_id+2.0");
version = dropdowns.at(2);
expect(version.text()).toBe("Switch to 3.0");
await version.trigger("click");
- state = wrapper.emitted().onSetData[2][1];
+ state = wrapper.emitted().onSetData[1][1];
expect(state.tool_version).toEqual("3.0");
expect(state.tool_id).toEqual("tool_id+3.0");
});
diff --git a/client/src/components/Workflow/Editor/Forms/FormTool.vue b/client/src/components/Workflow/Editor/Forms/FormTool.vue
index f16cf193e49..d13973a2085 100644
--- a/client/src/components/Workflow/Editor/Forms/FormTool.vue
+++ b/client/src/components/Workflow/Editor/Forms/FormTool.vue
@@ -2,12 +2,12 @@
-
+
@@ -58,6 +65,34 @@ export default {
FormSection,
},
props: {
+ nodeId: {
+ type: String,
+ required: true,
+ },
+ nodeAnnotation: {
+ type: String,
+ required: true,
+ },
+ nodeLabel: {
+ type: String,
+ required: true,
+ },
+ nodeInputs: {
+ type: Array,
+ required: true,
+ },
+ nodeOutputs: {
+ type: Array,
+ required: true,
+ },
+ nodeActiveOutputs: {
+ type: Object,
+ required: true,
+ },
+ configForm: {
+ type: Object,
+ required: true,
+ },
datatypes: {
type: Array,
required: true,
@@ -66,40 +101,33 @@ export default {
type: Function,
required: true,
},
- getNode: {
- type: Function,
+ postJobActions: {
+ type: Object,
required: true,
},
},
data() {
return {
mainValues: {},
- sectionValues: {},
messageText: "",
messageVariant: "success",
};
},
computed: {
- node() {
- return this.getNode();
- },
workflow() {
return this.getManager();
},
id() {
- return `${this.node.id}:${this.node.config_form.id}`;
- },
- nodeId() {
- return this.node.id;
+ return `${this.nodeId}:${this.configForm.id}`;
},
hasData() {
- return !!this.node.config_form;
+ return !!this.configForm;
},
errorLabel() {
- return checkLabels(this.node.id, this.node.label, this.workflow.nodes);
+ return checkLabels(this.nodeId, this.nodeLabel, this.workflow.nodes);
},
inputs() {
- const inputs = this.node.config_form.inputs;
+ const inputs = this.configForm.inputs;
Utils.deepeach(inputs, (input) => {
if (input.type) {
if (["data", "data_collection"].indexOf(input.type) != -1) {
@@ -126,35 +154,34 @@ export default {
return inputs;
},
errors() {
- return this.node.config_form.errors;
+ return this.configForm.errors;
},
},
methods: {
onAnnotation(newAnnotation) {
- this.$emit("onAnnotation", this.node.id, newAnnotation);
+ this.$emit("onAnnotation", this.nodeId, newAnnotation);
},
onLabel(newLabel) {
- this.$emit("onLabel", this.node.id, newLabel);
+ this.$emit("onLabel", this.nodeId, newLabel);
},
onChange(values) {
this.mainValues = values;
this.postChanges();
},
- onChangeSection(values) {
- this.sectionValues = values;
- this.postChanges();
+ onChangePostJobActions(postJobActions) {
+ this.$emit("onChangePostJobActions", this.nodeId, postJobActions);
},
onChangeVersion(newVersion) {
- this.messageText = `Now you are using '${this.node.config_form.name}' version ${newVersion}.`;
+ this.messageText = `Now you are using '${this.configForm.name}' version ${newVersion}.`;
this.postChanges(newVersion);
},
onUpdateFavorites(user, newFavorites) {
user.preferences["favorites"] = newFavorites;
},
postChanges(newVersion) {
- const payload = Object.assign({}, this.mainValues, this.sectionValues);
+ const payload = Object.assign({}, this.mainValues);
console.debug("FormTool - Posting changes.", payload);
- const options = this.node.config_form;
+ const options = this.configForm;
let toolId = options.id;
let toolVersion = options.version;
if (newVersion) {
@@ -162,7 +189,7 @@ export default {
toolVersion = newVersion;
console.debug("FormTool - Tool version changed.", toolId, toolVersion);
}
- this.$emit("onSetData", this.node.id, {
+ this.$emit("onSetData", this.nodeId, {
tool_id: toolId,
tool_version: toolVersion,
type: "tool",
diff --git a/client/src/components/Workflow/Editor/Index.vue b/client/src/components/Workflow/Editor/Index.vue
index e571efb4ea9..87a98a57204 100644
--- a/client/src/components/Workflow/Editor/Index.vue
+++ b/client/src/components/Workflow/Editor/Index.vue
@@ -135,15 +135,31 @@
v-if="hasActiveNodeTool"
:key="activeNodeId"
:get-manager="getManager"
- :get-node="getNode"
+ :node-id="activeNodeId"
+ :node-annotation="activeNodeAnnotation"
+ :node-label="activeNodeLabel"
+ :node-inputs="activeNodeInputs"
+ :node-outputs="activeNodeOutputs"
+ :node-active-outputs="activeNodeActiveOutputs"
+ :config-form="activeNodeConfigForm"
:datatypes="datatypes"
+ :post-job-actions="postJobActions"
+ @onChangePostJobActions="onChangePostJobActions"
@onAnnotation="onAnnotation"
@onLabel="onLabel"
@onSetData="onSetData" />
output.name);
this.activeOutputs.initialize(this.outputs, data.workflow_outputs);
this.activeOutputs.filterOutputs(outputNames);
- this.postJobActions = data.post_job_actions || {};
+ // data coming from the workflow editor API has post job actions,
+ // data coming from the build_module call does not (and should not)
+ this.postJobActions = data.post_job_actions || this.postJobActions;
this.config_form = data.config_form;
},
initData(data) {
diff --git a/client/src/components/Workflow/Editor/NodeOutput.vue b/client/src/components/Workflow/Editor/NodeOutput.vue
index 0a28967de1a..3209904d101 100644
--- a/client/src/components/Workflow/Editor/NodeOutput.vue
+++ b/client/src/components/Workflow/Editor/NodeOutput.vue
@@ -80,6 +80,9 @@ export default {
}
return extensions;
},
+ effectiveOutput() {
+ return { ...this.output, extensions: this.extensions };
+ },
},
watch: {
label() {
@@ -88,10 +91,10 @@ export default {
this.$emit("onChange");
});
},
- output(newOutput) {
+ effectiveOutput(newOutput) {
const oldTerminal = this.terminal;
if (oldTerminal instanceof this.terminalClassForOutput(newOutput)) {
- oldTerminal.update({ ...newOutput, extensions: this.extensions });
+ oldTerminal.update(newOutput);
oldTerminal.destroyInvalidConnections();
} else {
// create new terminal, connect like old terminal, destroy old terminal
diff --git a/lib/galaxy/webapps/galaxy/api/workflows.py b/lib/galaxy/webapps/galaxy/api/workflows.py
index 3a4583acd8d..45a10417cbe 100644
--- a/lib/galaxy/webapps/galaxy/api/workflows.py
+++ b/lib/galaxy/webapps/galaxy/api/workflows.py
@@ -670,7 +670,6 @@ class WorkflowsAPIController(
"inputs": module.get_all_inputs(connectable_only=True),
"outputs": module.get_all_outputs(),
"config_form": module.get_config_form(),
- "post_job_actions": module.get_post_job_actions(inputs),
}
@expose_api
diff --git a/lib/galaxy_test/selenium/test_workflow_editor.py b/lib/galaxy_test/selenium/test_workflow_editor.py
index f39b4357011..831a499847f 100644
--- a/lib/galaxy_test/selenium/test_workflow_editor.py
+++ b/lib/galaxy_test/selenium/test_workflow_editor.py
@@ -558,6 +558,31 @@ steps:
editor.node.output_data_row(output_name="out_file1", extension="bam").wait_for_visible()
self.assert_not_connected("create_2#out_file1", "checksum#input")
+ @selenium_test
+ def test_change_datatype_post_job_action_lost_regression(self):
+ self.open_in_workflow_editor(
+ """
+class: GalaxyWorkflow
+inputs: []
+steps:
+ - tool_id: create_2
+ label: create_2
+ outputs:
+ out_file1:
+ change_datatype: bam
+ - tool_id: metadata_bam
+ label: metadata_bam
+ in:
+ input_bam: create_2/out_file1
+"""
+ )
+ self.assert_connected("create_2#out_file1", "metadata_bam#input_bam")
+ editor = self.components.workflow_editor
+ node = editor.node._(label="create_2")
+ node.wait_for_and_click()
+ self.assert_connected("create_2#out_file1", "metadata_bam#input_bam")
+
+ @selenium_test
def test_change_datatype_in_subworkflow(self):
self.open_in_workflow_editor(
"""