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; +}