Skip to content
Snippets Groups Projects
Commit a395b6a0 authored by Daniel Göbel's avatar Daniel Göbel
Browse files

Merge branch 'feature/83-display-workflow-execution-parameters' into 'development'

Resolve "Display workflow execution parameters"

Closes #83

See merge request !79
parents 910f3dac d5513593
No related branches found
No related tags found
2 merge requests!84Remove development branch,!79Resolve "Display workflow execution parameters"
Pipeline #40269 passed
<script setup lang="ts">
import BootstrapModal from "@/components/modals/BootstrapModal.vue";
import { computed, onMounted, reactive, watch } from "vue";
import { useWorkflowExecutionStore } from "@/stores/workflowExecutions";
import type { RouteParamsRaw } from "vue-router";
import { Modal } from "bootstrap";
import { useRouter } from "vue-router";
import FontAwesomeIcon from "@/components/FontAwesomeIcon.vue";
import { useWorkflowStore } from "@/stores/workflows";
const workflowRepository = useWorkflowStore();
const executionRepository = useWorkflowExecutionStore();
const router = useRouter();
let parameterModal: Modal | null = null;
const props = defineProps<{
modalID: string;
executionId?: string;
}>();
const parameterState = reactive<{
loading: boolean;
error: boolean;
}>({
loading: false,
error: false,
});
const workflowName = computed<string>(() => {
if (props.executionId) {
const execution = executionRepository.executionMapping[props.executionId];
if (
execution?.workflow_id != undefined &&
execution?.workflow_version_id != undefined
) {
return (
workflowRepository.getName(execution.workflow_id) +
"@" +
workflowRepository.getName(execution.workflow_version_id)
);
}
}
return "";
});
function fetchWorkflowExecutionParameters(executionId?: string) {
parameterState.error = false;
if (executionId != undefined) {
parameterState.loading = true;
executionRepository
.fetchExecutionParameters(executionId)
.catch(() => {
parameterState.error = true;
})
.finally(() => {
parameterState.loading = false;
});
}
}
function handleBucketLinkClick(s3String: string) {
parameterModal?.hide();
router.push({
name: "bucket",
params: getS3LinkParameters(s3String),
});
}
function getS3LinkParameters(s3String: string): RouteParamsRaw {
const pathComponents = s3String.slice(5).split("/");
const s3File =
pathComponents.length > 1 &&
pathComponents[pathComponents.length - 1].includes(".");
return {
bucketName: pathComponents[0],
subFolders: pathComponents.slice(1, s3File ? -1 : undefined),
};
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
function isBucketLink(value: any): boolean {
if (typeof value === "string") {
return value.startsWith("s3://");
}
return false;
}
watch(
() => props.executionId,
(newId, oldId) => {
if (newId != oldId) {
fetchWorkflowExecutionParameters(newId);
}
},
);
onMounted(() => {
fetchWorkflowExecutionParameters(props.executionId);
parameterModal = Modal.getOrCreateInstance("#" + props.modalID);
});
</script>
<template>
<bootstrap-modal
:modalID="modalID"
:static-backdrop="false"
modal-label="Workflow Execution Parameters Modal"
>
<template v-slot:header
>Workflow Execution Parameters
<b>
{{ workflowName }}
</b>
</template>
<template v-slot:body>
<div v-if="parameterState.error" class="text-center fs-4 mt-5">
<font-awesome-icon
icon="fa-solid fa-magnifying-glass"
class="mb-3 fs-0"
style="color: var(--bs-secondary)"
/>
<p>
Workflow Execution <i>{{ props.executionId }}</i> not found
</p>
</div>
<table v-else-if="props.executionId" class="table table-hover">
<caption class="placeholder-glow">
<span v-if="parameterState.loading" class="placeholder col-1"></span>
<template v-else>
{{
Object.keys(executionRepository.parameters[props.executionId])
.length
}}
</template>
Parameters
</caption>
<tbody>
<template v-if="parameterState.loading">
<tr v-for="n in 6" :key="n">
<th scope="row" style="width: 20%" class="placeholder-glow">
<div class="placeholder col-12"></div>
</th>
<td class="placeholder-glow">
<div class="placeholder col-8"></div>
</td>
</tr>
</template>
<template v-else>
<tr
v-for="(value, name) in executionRepository.parameters[
props.executionId
]"
:key="name"
>
<th scope="row" style="width: 10%" class="text-end">
<b>{{ name }}</b>
</th>
<td>
<router-link
v-if="isBucketLink(value)"
:to="{
name: 'bucket',
params: getS3LinkParameters(value),
}"
@click.prevent="handleBucketLinkClick(value)"
>{{ value }}
</router-link>
<template v-else>{{ value }}</template>
</td>
</tr>
</template>
</tbody>
</table>
</template>
<template v-slot:footer>
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">
Close
</button>
</template>
</bootstrap-modal>
</template>
<style scoped></style>
...@@ -12,6 +12,7 @@ import { ...@@ -12,6 +12,7 @@ import {
} from "@/client/workflow"; } from "@/client/workflow";
import { useAuthStore } from "@/stores/users"; import { useAuthStore } from "@/stores/users";
import dayjs from "dayjs"; import dayjs from "dayjs";
import { set, get } from "idb-keyval";
export const useWorkflowExecutionStore = defineStore({ export const useWorkflowExecutionStore = defineStore({
id: "workflow-executions", id: "workflow-executions",
...@@ -19,9 +20,12 @@ export const useWorkflowExecutionStore = defineStore({ ...@@ -19,9 +20,12 @@ export const useWorkflowExecutionStore = defineStore({
({ ({
executionMapping: {}, executionMapping: {},
anonymizedExecutions: [], anonymizedExecutions: [],
parameters: {},
}) as { }) as {
executionMapping: Record<string, WorkflowExecutionOut>; executionMapping: Record<string, WorkflowExecutionOut>;
anonymizedExecutions: AnonymizedWorkflowExecution[]; anonymizedExecutions: AnonymizedWorkflowExecution[];
// eslint-disable-next-line @typescript-eslint/no-explicit-any
parameters: Record<string, Record<string, any>>;
}, },
getters: { getters: {
executions(): WorkflowExecutionOut[] { executions(): WorkflowExecutionOut[] {
...@@ -79,6 +83,31 @@ export const useWorkflowExecutionStore = defineStore({ ...@@ -79,6 +83,31 @@ export const useWorkflowExecutionStore = defineStore({
}) })
.finally(onFinally); .finally(onFinally);
}, },
fetchExecutionParameters(
executionId: string,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
): Promise<Record<string, any>> {
if (Object.keys(this.parameters).includes(executionId)) {
return Promise.resolve(this.parameters[executionId]);
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
return get<Record<string, any>>(executionId)
.then((parameters) => {
if (parameters != undefined) {
return parameters;
}
return WorkflowExecutionService.workflowExecutionGetWorkflowExecutionParams(
executionId,
).then((parameters) => {
set(executionId, parameters);
return parameters;
});
})
.then((parameters) => {
this.parameters[executionId] = parameters;
return parameters;
});
},
deleteExecution(executionId: string): Promise<void> { deleteExecution(executionId: string): Promise<void> {
return WorkflowExecutionService.workflowExecutionDeleteWorkflowExecution( return WorkflowExecutionService.workflowExecutionDeleteWorkflowExecution(
executionId, executionId,
......
...@@ -7,6 +7,7 @@ import { Tooltip } from "bootstrap"; ...@@ -7,6 +7,7 @@ import { Tooltip } from "bootstrap";
import DeleteModal from "@/components/modals/DeleteModal.vue"; import DeleteModal from "@/components/modals/DeleteModal.vue";
import { useWorkflowStore } from "@/stores/workflows"; import { useWorkflowStore } from "@/stores/workflows";
import { useWorkflowExecutionStore } from "@/stores/workflowExecutions"; import { useWorkflowExecutionStore } from "@/stores/workflowExecutions";
import ParameterModal from "@/components/workflows/modals/ParameterModal.vue";
const workflowRepository = useWorkflowStore(); const workflowRepository = useWorkflowStore();
const executionRepository = useWorkflowExecutionStore(); const executionRepository = useWorkflowExecutionStore();
...@@ -17,9 +18,11 @@ let intervalId: NodeJS.Timer | undefined = undefined; ...@@ -17,9 +18,11 @@ let intervalId: NodeJS.Timer | undefined = undefined;
const executionsState = reactive<{ const executionsState = reactive<{
loading: boolean; loading: boolean;
executionToDelete?: WorkflowExecutionOut; executionToDelete?: WorkflowExecutionOut;
executionParameters?: string;
}>({ }>({
loading: true, loading: true,
executionToDelete: undefined, executionToDelete: undefined,
executionParameters: undefined,
}); });
const statusToColorMapping = { const statusToColorMapping = {
...@@ -133,6 +136,10 @@ onUnmounted(() => { ...@@ -133,6 +136,10 @@ onUnmounted(() => {
deleteWorkflowExecution(executionsState.executionToDelete?.execution_id) deleteWorkflowExecution(executionsState.executionToDelete?.execution_id)
" "
/> />
<parameter-modal
modal-i-d="workflowExecutionParameterModal"
:execution-id="executionsState.executionParameters"
/>
<div <div
class="row m-2 border-bottom mb-4 justify-content-between align-items-center" class="row m-2 border-bottom mb-4 justify-content-between align-items-center"
> >
...@@ -258,6 +265,20 @@ onUnmounted(() => { ...@@ -258,6 +265,20 @@ onUnmounted(() => {
<span class="visually-hidden">Toggle Dropdown</span> <span class="visually-hidden">Toggle Dropdown</span>
</button> </button>
<ul class="dropdown-menu dropdown-menu"> <ul class="dropdown-menu dropdown-menu">
<li>
<button
class="dropdown-item align-middle"
type="button"
data-bs-toggle="modal"
data-bs-target="#workflowExecutionParameterModal"
@click="
executionsState.executionParameters =
execution.execution_id
"
>
<span class="ms-1">Parameters</span>
</button>
</li>
<li v-if="workflowExecutionCancelable(execution)"> <li v-if="workflowExecutionCancelable(execution)">
<button <button
class="dropdown-item text-danger align-middle" class="dropdown-item text-danger align-middle"
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment