From 14a032bd917c021af5e431c4a13406162fcf8768 Mon Sep 17 00:00:00 2001 From: John Chilton Date: Tue, 6 Feb 2024 13:11:38 -0500 Subject: [PATCH 1/4] Refactor a bunch of MarkdownSelector into typescript. --- .../src/components/Markdown/LabelSelector.vue | 43 +++++++++++++++++++ .../components/Markdown/MarkdownSelector.vue | 34 +++++---------- 2 files changed, 54 insertions(+), 23 deletions(-) create mode 100644 client/src/components/Markdown/LabelSelector.vue diff --git a/client/src/components/Markdown/LabelSelector.vue b/client/src/components/Markdown/LabelSelector.vue new file mode 100644 index 000000000000..682389c6fd15 --- /dev/null +++ b/client/src/components/Markdown/LabelSelector.vue @@ -0,0 +1,43 @@ + + + diff --git a/client/src/components/Markdown/MarkdownSelector.vue b/client/src/components/Markdown/MarkdownSelector.vue index 0ff5e0b296b6..16baf1106bb2 100644 --- a/client/src/components/Markdown/MarkdownSelector.vue +++ b/client/src/components/Markdown/MarkdownSelector.vue @@ -7,27 +7,12 @@ @ok="onOk" @cancel="onCancel" @hidden="onCancel"> -
-

Select {{ labelTitle }} Label:

-
- - {{ label }} - -
- - No labels found. Please specify labels in the Workflow Editor. - -

- You may add new labels by selecting a step in the workflow editor and then editing the corresponding - label field in the step form. -

-
+ @@ -36,9 +21,12 @@ import BootstrapVue from "bootstrap-vue"; import Vue from "vue"; +import LabelSelector from "./LabelSelector"; + Vue.use(BootstrapVue); export default { + components: { LabelSelector }, props: { labelTitle: { type: String, @@ -55,7 +43,7 @@ export default { }, data() { return { - selectedValue: 0, + selectedValue: undefined, modalShow: true, }; }, @@ -69,7 +57,7 @@ export default { }, methods: { onOk() { - this.$emit("onOk", this.labels[this.selectedValue]); + this.$emit("onOk", this.selectedValue); }, onCancel() { this.$emit("onCancel"); From 623d45fbd5614a9dfb1c8900b7cf6787dfee4784 Mon Sep 17 00:00:00 2001 From: John Chilton Date: Tue, 6 Feb 2024 13:22:19 -0500 Subject: [PATCH 2/4] Refactor the rest of MarkdownSelector to typescript --- .../components/Markdown/MarkdownSelector.vue | 88 ++++++++----------- 1 file changed, 39 insertions(+), 49 deletions(-) diff --git a/client/src/components/Markdown/MarkdownSelector.vue b/client/src/components/Markdown/MarkdownSelector.vue index 16baf1106bb2..75b95f474e6e 100644 --- a/client/src/components/Markdown/MarkdownSelector.vue +++ b/client/src/components/Markdown/MarkdownSelector.vue @@ -1,3 +1,42 @@ + + - - From 8399ee8a4b766990f00a533113b455545c63162a Mon Sep 17 00:00:00 2001 From: John Chilton Date: Tue, 6 Feb 2024 13:42:08 -0500 Subject: [PATCH 3/4] Fixes for mapping workflow labels to markdown selection. --- .../src/components/Markdown/LabelSelector.vue | 12 +++-- .../components/Markdown/MarkdownDialog.vue | 43 ++++++++++++----- .../components/Markdown/MarkdownSelector.vue | 8 ++-- .../components/Markdown/MarkdownToolBox.vue | 34 ++----------- client/src/components/Markdown/labels.ts | 48 +++++++++++++++++++ 5 files changed, 97 insertions(+), 48 deletions(-) create mode 100644 client/src/components/Markdown/labels.ts diff --git a/client/src/components/Markdown/LabelSelector.vue b/client/src/components/Markdown/LabelSelector.vue index 682389c6fd15..2527704f28c6 100644 --- a/client/src/components/Markdown/LabelSelector.vue +++ b/client/src/components/Markdown/LabelSelector.vue @@ -1,8 +1,10 @@ @@ -31,7 +33,7 @@ function update(index: number) { name="labels" :value="index" @change="update"> - {{ label }} + {{ label.label }} No labels found. Please specify labels in the Workflow Editor. diff --git a/client/src/components/Markdown/MarkdownDialog.vue b/client/src/components/Markdown/MarkdownDialog.vue index 4076696f47ee..7dd49fd8bf75 100644 --- a/client/src/components/Markdown/MarkdownDialog.vue +++ b/client/src/components/Markdown/MarkdownDialog.vue @@ -9,6 +9,8 @@ import { jobsFetcher } from "@/api/jobs"; import { workflowsFetcher } from "@/api/workflows"; import { useHistoryStore } from "@/stores/historyStore"; +import { WorkflowLabel, WorkflowLabels } from "./labels"; + import MarkdownSelector from "./MarkdownSelector.vue"; import MarkdownVisualization from "./MarkdownVisualization.vue"; import DataDialog from "@/components/DataDialog/DataDialog.vue"; @@ -21,7 +23,7 @@ interface MarkdownDialogProps { argumentName?: string; argumentType?: string; argumentPayload?: object; - labels?: string[]; + labels?: WorkflowLabels; useLabels: boolean; } @@ -42,6 +44,22 @@ interface SelectTitles { type SelectType = "job_id" | "invocation_id" | "history_dataset_id" | "history_dataset_collection_id"; +const effectiveLabels = computed(() => { + if (!props.labels) { + return [] as WorkflowLabels; + } + const selectSteps = props.argumentType == "job_id"; + const filteredLabels: WorkflowLabels = []; + for (const label of props.labels) { + if (selectSteps && label.type == "step") { + filteredLabels.push(label); + } else if (!selectSteps && label.type != "step") { + filteredLabels.push(label); + } + } + return filteredLabels; +}); + const selectorConfig = { job_id: { labelTitle: "Step", @@ -50,10 +68,10 @@ const selectorConfig = { labelTitle: "Step", }, history_dataset_id: { - labelTitle: "Output", + labelTitle: "Dataset (Input/Output)", }, history_dataset_collection_id: { - labelTitle: "Output", + labelTitle: "Dataset Collection (Input/Output)", }, }; @@ -127,30 +145,33 @@ function onVisualization(response: string) { emit("onInsert", response); } -function onOk(selectedLabel: string) { - selectedLabel = selectedLabel || ""; +function onOk(selectedLabel: WorkflowLabel | undefined) { + const defaultLabelType: string = + ["history_dataset_id", "history_dataset_collection_id"].indexOf(props.argumentType) >= 0 ? "output" : "step"; + const labelText: string = selectedLabel ? selectedLabel.label : ""; + const labelType: string = selectedLabel ? selectedLabel.type : defaultLabelType; selectedShow.value = false; if (props.argumentType == "history_dataset_id") { if (props.useLabels) { - emit("onInsert", `${props.argumentName}(output="${selectedLabel}")`); + emit("onInsert", `${props.argumentName}(${labelType}="${labelText}")`); } else { dataShow.value = true; } } else if (props.argumentType == "history_dataset_collection_id") { if (props.useLabels) { - emit("onInsert", `${props.argumentName}(output="${selectedLabel}")`); + emit("onInsert", `${props.argumentName}(${labelType}="${labelText}")`); } else { dataCollectionShow.value = true; } } else if (props.argumentType == "job_id") { if (props.useLabels) { - emit("onInsert", `${props.argumentName}(step="${selectedLabel}")`); + emit("onInsert", `${props.argumentName}(${labelType}="${labelText}")`); } else { jobShow.value = true; } } else if (props.argumentType == "invocation_id") { if (props.useLabels) { - emit("onInsert", `${props.argumentName}(step="${selectedLabel}")`); + emit("onInsert", `${props.argumentName}(${labelType}="${labelText}")`); } else { invocationShow.value = true; } @@ -207,7 +228,7 @@ if (props.argumentType == "workflow_id") { v-if="selectedShow" :initial-value="argumentType" :argument-name="argumentName" - :labels="labels" + :labels="effectiveLabels" :label-title="selectedLabelTitle" @onOk="onOk" @onCancel="onCancel" /> @@ -215,7 +236,7 @@ if (props.argumentType == "workflow_id") { v-else-if="visualizationShow" :argument-name="argumentName" :argument-payload="argumentPayload" - :labels="labels" + :labels="effectiveLabels" :use-labels="useLabels" :history="currentHistoryId" @onOk="onVisualization" diff --git a/client/src/components/Markdown/MarkdownSelector.vue b/client/src/components/Markdown/MarkdownSelector.vue index 75b95f474e6e..c41f9c4109f7 100644 --- a/client/src/components/Markdown/MarkdownSelector.vue +++ b/client/src/components/Markdown/MarkdownSelector.vue @@ -2,16 +2,18 @@ import BootstrapVue from "bootstrap-vue"; import Vue, { computed, ref } from "vue"; +import { WorkflowLabel } from "./labels"; + import LabelSelector from "./LabelSelector.vue"; interface MarkdownSelectorProps { labelTitle?: string; - labels: string[]; + labels: WorkflowLabel[]; argumentName?: string; } const props = defineProps(); -const selectedValue = ref(undefined); +const selectedValue = ref(undefined); const modalShow = ref(true); const title = computed(() => { @@ -22,7 +24,7 @@ const hasLabels = computed(() => { }); const emit = defineEmits<{ - (e: "onOk", value: string | undefined): void; + (e: "onOk", value: WorkflowLabel | undefined): void; (e: "onCancel"): void; }>(); diff --git a/client/src/components/Markdown/MarkdownToolBox.vue b/client/src/components/Markdown/MarkdownToolBox.vue index c8f7341f6fed..02d6a513e3bc 100644 --- a/client/src/components/Markdown/MarkdownToolBox.vue +++ b/client/src/components/Markdown/MarkdownToolBox.vue @@ -33,7 +33,7 @@ :argument-type="selectedType" :argument-name="selectedArgumentName" :argument-payload="selectedPayload" - :labels="selectedLabels" + :labels="workflowLabels" :use-labels="isWorkflow" @onInsert="onInsert" @onCancel="onCancel" /> @@ -48,6 +48,7 @@ import { getAppRoot } from "onload/loadConfig"; import Vue from "vue"; import { directiveEntry } from "./directives.ts"; +import { fromSteps } from "./labels.ts"; import MarkdownDialog from "./MarkdownDialog"; Vue.use(BootstrapVue); @@ -105,7 +106,6 @@ export default { return { selectedArgumentName: null, selectedType: null, - selectedLabels: undefined, selectedShow: false, selectedPayload: null, visualizationIndex: {}, @@ -244,33 +244,14 @@ export default { ], }; }, + workflowLabels() { + return fromSteps(this.steps); + }, }, created() { this.getVisualizations(); }, methods: { - getSteps() { - const steps = []; - this.steps && - Object.values(this.steps).forEach((step) => { - if (step.label) { - steps.push(step.label); - } - }); - return steps; - }, - getOutputs() { - const outputLabels = []; - this.steps && - Object.values(this.steps).forEach((step) => { - step.workflow_outputs.forEach((workflowOutput) => { - if (workflowOutput.label) { - outputLabels.push(workflowOutput.label); - } - }); - }); - return outputLabels; - }, getArgumentTitle(argumentName) { return ( argumentName[0].toUpperCase() + @@ -320,7 +301,6 @@ export default { this.selectedArgumentName = argumentName; this.selectedType = "visualization_id"; this.selectedPayload = this.visualizationIndex[argumentName]; - this.selectedLabels = this.getOutputs(); this.selectedShow = true; }, onHistoryId(argumentName) { @@ -331,13 +311,11 @@ export default { onHistoryDatasetId(argumentName) { this.selectedArgumentName = argumentName; this.selectedType = "history_dataset_id"; - this.selectedLabels = this.getOutputs(); this.selectedShow = true; }, onHistoryCollectionId(argumentName) { this.selectedArgumentName = argumentName; this.selectedType = "history_dataset_collection_id"; - this.selectedLabels = this.getOutputs(); this.selectedShow = true; }, onWorkflowId(argumentName) { @@ -348,13 +326,11 @@ export default { onJobId(argumentName) { this.selectedArgumentName = argumentName; this.selectedType = "job_id"; - this.selectedLabels = this.getSteps(); this.selectedShow = true; }, onInvocationId(argumentName) { this.selectedArgumentName = argumentName; this.selectedType = "invocation_id"; - this.selectedLabels = this.getSteps(); this.selectedShow = true; }, async getVisualizations() { diff --git a/client/src/components/Markdown/labels.ts b/client/src/components/Markdown/labels.ts new file mode 100644 index 000000000000..10048e834573 --- /dev/null +++ b/client/src/components/Markdown/labels.ts @@ -0,0 +1,48 @@ +// abstractions for dealing with workflows labels and +// connecting them to the Markdown editor + +type WorkflowLabelKind = "input" | "output" | "step"; + +export interface WorkflowLabel { + label: string; + type: WorkflowLabelKind; +} + +interface StepOutput { + label?: string; +} + +interface Step { + label?: string; + type: string; + workflow_outputs: StepOutput[]; +} + +export type WorkflowLabels = WorkflowLabel[]; + +export function fromSteps(steps?: Step[]): WorkflowLabels { + const labels: WorkflowLabels = []; + + if (!steps) { + return labels; + } + + Object.values(steps).forEach((step) => { + const stepType = step.type; + if (step.label) { + const isInput = ["data_input", "data_collection_input", "parameter_input"].indexOf(stepType) >= 0; + if (isInput) { + labels.push({ type: "input", label: step.label }); + } else { + labels.push({ type: "step", label: step.label }); + } + } + step.workflow_outputs.forEach((workflowOutput) => { + if (workflowOutput.label) { + labels.push({ type: "output", label: workflowOutput.label }); + } + }); + }); + + return labels; +} From 3a389717226a3fe8bd37544f53d4ae0f7fc90904 Mon Sep 17 00:00:00 2001 From: John Chilton Date: Mon, 25 Mar 2024 14:28:42 -0400 Subject: [PATCH 4/4] Cleanup per PR review. --- .../src/components/Markdown/MarkdownDialog.vue | 13 +++++++++---- .../src/components/Markdown/MarkdownSelector.vue | 16 ++++------------ 2 files changed, 13 insertions(+), 16 deletions(-) diff --git a/client/src/components/Markdown/MarkdownDialog.vue b/client/src/components/Markdown/MarkdownDialog.vue index 7dd49fd8bf75..e610692f5a80 100644 --- a/client/src/components/Markdown/MarkdownDialog.vue +++ b/client/src/components/Markdown/MarkdownDialog.vue @@ -151,27 +151,32 @@ function onOk(selectedLabel: WorkflowLabel | undefined) { const labelText: string = selectedLabel ? selectedLabel.label : ""; const labelType: string = selectedLabel ? selectedLabel.type : defaultLabelType; selectedShow.value = false; + + function onInsertArgument() { + emit("onInsert", `${props.argumentName}(${labelType}="${labelText}")`); + } + if (props.argumentType == "history_dataset_id") { if (props.useLabels) { - emit("onInsert", `${props.argumentName}(${labelType}="${labelText}")`); + onInsertArgument(); } else { dataShow.value = true; } } else if (props.argumentType == "history_dataset_collection_id") { if (props.useLabels) { - emit("onInsert", `${props.argumentName}(${labelType}="${labelText}")`); + onInsertArgument(); } else { dataCollectionShow.value = true; } } else if (props.argumentType == "job_id") { if (props.useLabels) { - emit("onInsert", `${props.argumentName}(${labelType}="${labelText}")`); + onInsertArgument(); } else { jobShow.value = true; } } else if (props.argumentType == "invocation_id") { if (props.useLabels) { - emit("onInsert", `${props.argumentName}(${labelType}="${labelText}")`); + onInsertArgument(); } else { invocationShow.value = true; } diff --git a/client/src/components/Markdown/MarkdownSelector.vue b/client/src/components/Markdown/MarkdownSelector.vue index c41f9c4109f7..d6d16cefdfe8 100644 --- a/client/src/components/Markdown/MarkdownSelector.vue +++ b/client/src/components/Markdown/MarkdownSelector.vue @@ -1,6 +1,6 @@