From 64ca9210dc68efb01814bd45f90548112ab2eb75 Mon Sep 17 00:00:00 2001 From: John Chilton Date: Wed, 23 Dec 2020 02:32:24 -0500 Subject: [PATCH 1/6] If a workflow defines explicit workflow_outputs, respect those. Currently the editor just makes everything an output that isn't hidden. Without this change, I don't see a way to have non-hidden outputs to tool steps that are not workflow outputs. --- .../Workflow/Editor/modules/model.js | 21 +++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/client/src/components/Workflow/Editor/modules/model.js b/client/src/components/Workflow/Editor/modules/model.js index 15c9c8e677f..789309ab5ba 100644 --- a/client/src/components/Workflow/Editor/modules/model.js +++ b/client/src/components/Workflow/Editor/modules/model.js @@ -31,6 +31,13 @@ export function fromSimple(workflow, data, appendData = false) { }); Vue.nextTick(() => { // Second pass, connections + let using_workflow_outputs = false; + Object.entries(data.steps).forEach(([id, step]) => { + if (step.workflow_outputs && step.workflow_outputs.length > 0) { + using_workflow_outputs = true; + } + }); + Object.entries(data.steps).forEach(([id, step]) => { const nodeIndex = parseInt(id) + offset; const node = workflow.nodes[nodeIndex]; @@ -49,12 +56,14 @@ export function fromSimple(workflow, data, appendData = false) { } }); - // Older workflows contain HideDatasetActions only, but no active outputs yet. - Object.values(node.outputs).forEach((ot) => { - if (!node.postJobActions[`HideDatasetAction${ot.name}`]) { - node.activeOutputs.add(ot.name); - } - }); + if (!using_workflow_outputs) { + // Older workflows contain HideDatasetActions only, but no active outputs yet. + Object.values(node.outputs).forEach((ot) => { + if (!node.postJobActions[`HideDatasetAction${ot.name}`]) { + node.activeOutputs.add(ot.name); + } + }); + } }); }); }); From 1d93a1aa1c667bb9be651cc81bbecc52cd765533 Mon Sep 17 00:00:00 2001 From: John Chilton Date: Mon, 21 Dec 2020 13:29:34 -0500 Subject: [PATCH 2/6] More structured workflow parameter parsing in editor client. --- .../components/Workflow/Editor/Attributes.vue | 9 ++- .../src/components/Workflow/Editor/Index.vue | 6 +- .../Workflow/Editor/modules/utilities.js | 76 +++++++++++++++---- 3 files changed, 68 insertions(+), 23 deletions(-) diff --git a/client/src/components/Workflow/Editor/Attributes.vue b/client/src/components/Workflow/Editor/Attributes.vue index 53e3b4be742..e2ef3e7dd93 100644 --- a/client/src/components/Workflow/Editor/Attributes.vue +++ b/client/src/components/Workflow/Editor/Attributes.vue @@ -19,8 +19,8 @@
Parameters - {{ key + 1 }}: {{ p }} + {{ key + 1 }}: {{ p.name }}
@@ -59,6 +59,7 @@ import Vue from "vue"; import BootstrapVue from "bootstrap-vue"; import moment from "moment"; import { Services } from "components/Workflow/services"; +import { LegacyParameters } from "components/Workflow/Editor/modules/utilities"; import Tags from "components/Common/Tags"; import LicenseSelector from "components/License/LicenseSelector"; import CreatorEditor from "components/SchemaOrg/CreatorEditor"; @@ -104,7 +105,7 @@ export default { default: null, }, parameters: { - type: Array, + type: LegacyParameters, default: null, }, }, @@ -132,7 +133,7 @@ export default { return creator; }, hasParameters() { - return this.parameters.length > 0; + return this.parameters && this.parameters.parameters.length > 0; }, versionOptions() { const versions = []; diff --git a/client/src/components/Workflow/Editor/Index.vue b/client/src/components/Workflow/Editor/Index.vue index 837c3bbb88d..9570d339ce4 100644 --- a/client/src/components/Workflow/Editor/Index.vue +++ b/client/src/components/Workflow/Editor/Index.vue @@ -132,7 +132,7 @@ import { showWarnings, showUpgradeMessage, copyIntoWorkflow, - getWorkflowParameters, + getLegacyWorkflowParameters, showAttributes, showForm, saveAs, @@ -196,7 +196,7 @@ export default { markdownConfig: null, markdownText: null, versions: [], - parameters: [], + parameters: null, zoomLevel: 7, steps: {}, hasChanges: false, @@ -319,7 +319,7 @@ export default { }, onAttributes() { showAttributes(); - this.parameters = getWorkflowParameters(this.nodes); + this.parameters = getLegacyWorkflowParameters(this.nodes); }, onEdit() { this.isCanvas = true; diff --git a/client/src/components/Workflow/Editor/modules/utilities.js b/client/src/components/Workflow/Editor/modules/utilities.js index 582b5cd9f60..8b455b28dc6 100644 --- a/client/src/components/Workflow/Editor/modules/utilities.js +++ b/client/src/components/Workflow/Editor/modules/utilities.js @@ -127,17 +127,68 @@ export function showUpgradeMessage(data) { return hasToolUpgrade; } -export function getWorkflowParameters(nodes) { +class LegacyParameterReference { + constructor(parameter, node) { + //this.node = node; + parameter.references.push(this); + } +} + +class ToolInputLegacyParameterReference extends LegacyParameterReference { + constructor(parameter, node, tool_input) { + super(parameter, node); + //this.tool_input = tool_input; + } +} + +class PjaLegacyParameterReference extends LegacyParameterReference { + constructor(parameter, node, pja) { + super(parameter, node); + //this.pja = pja + } +} + +class LegacyParameter { + constructor(name) { + this.name = name; + this.references = []; + } +} + +export class LegacyParameters { + constructor() { + this.parameters = []; + } + + getParameter(name) { + for (const parameter of this.parameters) { + if (parameter.name == name) { + return parameter; + } + } + const legacyParameter = new LegacyParameter(name); + this.parameters.push(legacyParameter); + return legacyParameter; + } + + getParameterFromMatch(match) { + return this.getParameter(match.substring(2, match.length - 1)); + } +} + +export function getLegacyWorkflowParameters(nodes) { + const legacyParameters = new LegacyParameters(); const parameter_re = /\$\{.+?\}/g; - const parameters = []; - let matches = []; Object.entries(nodes).forEach(([k, node]) => { if (node.config_form && node.config_form.inputs) { Utils.deepeach(node.config_form.inputs, (d) => { if (typeof d.value == "string") { var form_matches = d.value.match(parameter_re); if (form_matches) { - matches = matches.concat(form_matches); + for (const match of form_matches) { + const legacyParameter = legacyParameters.getParameterFromMatch(match); + new ToolInputLegacyParameterReference(legacyParameter, node, d); + } } } }); @@ -149,25 +200,18 @@ export function getWorkflowParameters(nodes) { if (typeof action_argument === "string") { const arg_matches = action_argument.match(parameter_re); if (arg_matches) { - matches = matches.concat(arg_matches); + for (const match of arg_matches) { + const legacyParameter = legacyParameters.getParameterFromMatch(match); + new PjaLegacyParameterReference(legacyParameter, node, pja); + } } } }); } }); } - if (matches) { - Object.entries(matches).forEach(([k, element]) => { - if (parameters.indexOf(element) === -1) { - parameters.push(element); - } - }); - } }); - Object.entries(parameters).forEach(([k, element]) => { - parameters[k] = element.substring(2, element.length - 1); - }); - return parameters; + return legacyParameters; } export function saveAs(workflow) { From 39b9e19ca90b80d3f958778656be5b73585336c1 Mon Sep 17 00:00:00 2001 From: John Chilton Date: Mon, 21 Dec 2020 16:58:44 -0500 Subject: [PATCH 3/6] UI for linting workflows for best practices. --- .../Workflow/Editor/Attributes.test.js | 6 +- .../src/components/Workflow/Editor/Index.vue | 106 +++- .../src/components/Workflow/Editor/Lint.vue | 452 ++++++++++++++++++ .../Workflow/Editor/LintSection.vue | 82 ++++ .../src/components/Workflow/Editor/Node.vue | 3 + .../components/Workflow/Editor/Options.vue | 3 + .../Workflow/Editor/modules/services.js | 17 + .../Workflow/Editor/modules/utilities.js | 90 +++- 8 files changed, 736 insertions(+), 23 deletions(-) create mode 100644 client/src/components/Workflow/Editor/Lint.vue create mode 100644 client/src/components/Workflow/Editor/LintSection.vue diff --git a/client/src/components/Workflow/Editor/Attributes.test.js b/client/src/components/Workflow/Editor/Attributes.test.js index 13dc05ce7eb..d4f15b07be9 100644 --- a/client/src/components/Workflow/Editor/Attributes.test.js +++ b/client/src/components/Workflow/Editor/Attributes.test.js @@ -1,5 +1,6 @@ import { mount, createLocalVue } from "@vue/test-utils"; import Attributes from "./Attributes"; +import { LegacyParameters } from "./modules/utilities"; jest.mock("app"); @@ -9,12 +10,15 @@ const TEST_NAME = "workflow_name"; describe("Attributes", () => { it("test attributes", async () => { const localVue = createLocalVue(); + const legacyParameters = new LegacyParameters(); + legacyParameters.getParameter("workflow_parameter_0"); + legacyParameters.getParameter("workflow_parameter_1"); const wrapper = mount(Attributes, { propsData: { id: "workflow_id", name: TEST_NAME, tags: ["workflow_tag_0", "workflow_tag_1"], - parameters: ["workflow_parameter_0", "workflow_parameter_1"], + parameters: legacyParameters, versions: ["workflow_version_0"], annotation: TEST_ANNOTATION, }, diff --git a/client/src/components/Workflow/Editor/Index.vue b/client/src/components/Workflow/Editor/Index.vue index 9570d339ce4..801a5b99cd1 100644 --- a/client/src/components/Workflow/Editor/Index.vue +++ b/client/src/components/Workflow/Editor/Index.vue @@ -93,6 +93,7 @@ @onLayout="onLayout" @onEdit="onEdit" @onAttributes="onAttributes" + @onLint="onLint" /> @@ -115,6 +116,19 @@ @onLicense="onLicense" @onCreator="onCreator" /> +