Skip to content

Commit

Permalink
[24.1] Handle error when workflow is unowned in Invocation view
Browse files Browse the repository at this point in the history
Found this when investigating galaxyproject#18697; that if (in this case) I run another user's shared workflow with a subworkflow, the invocation view for the parent renders everything fine, but there is an unhandled error caused by the subworkflow not being shared. Now this fix handles that error and shows it on hover on the `WorkflowStepTitle`
  • Loading branch information
ahmedhamidawan committed Aug 22, 2024
1 parent be7ec30 commit 991eb9e
Show file tree
Hide file tree
Showing 4 changed files with 45 additions and 36 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ import { BAlert, BButton } from "bootstrap-vue";
import { computed } from "vue";
import { InvocationJobsSummary, InvocationStep, WorkflowInvocationElementView } from "@/api/invocations";
import { useWorkflowInstance } from "@/composables/useWorkflowInstance";
import { getRootFromIndexLink } from "@/onload";
import { type Workflow } from "@/stores/workflowStore";
import { withPrefix } from "@/utils/redirect";
import {
Expand All @@ -28,6 +28,7 @@ function getUrl(path: string): string {
interface Props {
invocation?: WorkflowInvocationElementView;
workflow?: Workflow;
invocationAndJobTerminal: boolean;
invocationSchedulingTerminal: boolean;
isFullPage?: boolean;
Expand All @@ -40,8 +41,6 @@ const props = defineProps<Props>();
const generatePdfTooltip = "Generate PDF report for this workflow invocation";
const { workflow } = useWorkflowInstance(props.invocation?.workflow_id ?? "");
const invocationId = computed<string | undefined>(() => props.invocation?.id);
const indexStr = computed(() => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,9 @@ import { BAlert, BButton, BButtonGroup, BTab, BTabs } from "bootstrap-vue";
import { computed, onMounted, onUnmounted, ref, watch } from "vue";
import { type InvocationJobsSummary, type WorkflowInvocationElementView } from "@/api/invocations";
import type { StoredWorkflowDetailed } from "@/api/workflows";
import { useAnimationFrameResizeObserver } from "@/composables/sensors/animationFrameResizeObserver";
import { useInvocationStore } from "@/stores/invocationStore";
import { useWorkflowStore } from "@/stores/workflowStore";
import { useWorkflowStore, type Workflow } from "@/stores/workflowStore";
import localize from "@/utils/localization";
import { errorMessageAsString } from "@/utils/simple-error";
Expand Down Expand Up @@ -51,6 +50,7 @@ const invocationStore = useInvocationStore();
const stepStatesInterval = ref<any>(undefined);
const jobStatesInterval = ref<any>(undefined);
const initialLoading = ref(true);
const invocationLoaded = ref(false);
const errorMessage = ref<string | null>(null);
// after the report tab is first activated, no longer lazy-render it from then on
Expand All @@ -73,7 +73,7 @@ useAnimationFrameResizeObserver(scrollableDiv, ({ clientSize, scrollSize }) => {
});
const invocation = computed(() =>
!initialLoading.value && !errorMessage.value
invocationLoaded.value
? (invocationStore.getInvocationById(props.invocationId) as WorkflowInvocationElementView)
: null
);
Expand Down Expand Up @@ -107,14 +107,17 @@ const invocationStateSuccess = computed(() => {
const workflowStore = useWorkflowStore();
const isDeletedWorkflow = computed(() => getWorkflow()?.deleted === true);
const workflowVersion = computed(() => getWorkflow()?.version);
const workflow = ref<Workflow | undefined>(undefined);
const isDeletedWorkflow = computed(() => workflow.value?.deleted === true);
const workflowVersion = computed(() => workflow.value?.version);
onMounted(async () => {
try {
await invocationStore.fetchInvocationForId({ id: props.invocationId });
initialLoading.value = false;
invocationLoaded.value = true;
if (invocation.value) {
await workflowStore.fetchWorkflowForInstanceId(invocation.value.workflow_id);
workflow.value = workflowStore.getStoredWorkflowByInstanceId(invocation.value.workflow_id);
await pollStepStatesUntilTerminal();
await pollJobStatesUntilTerminal();
}
Expand Down Expand Up @@ -151,23 +154,8 @@ function onCancel() {
function cancelWorkflowSchedulingLocal() {
cancelWorkflowScheduling(props.invocationId).then(onCancel).catch(onError);
}
function getWorkflow() {
return invocation.value?.workflow_id
? (workflowStore.getStoredWorkflowByInstanceId(
invocation.value?.workflow_id
) as unknown as StoredWorkflowDetailed)
: undefined;
}
function getWorkflowId() {
return invocation.value?.workflow_id
? workflowStore.getStoredWorkflowIdByInstanceId(invocation.value?.workflow_id)
: undefined;
}
function getWorkflowName() {
return workflowStore.getStoredWorkflowNameByInstanceId(invocation.value?.workflow_id || "");
return workflow.value?.name || "...";
}
</script>

Expand Down Expand Up @@ -202,13 +190,13 @@ function getWorkflowName() {
<SwitchToHistoryLink :history-id="invocation.history_id" />
</span>
</div>
<div v-if="getWorkflow()" class="d-flex flex-gapx-1 align-items-center">
<div v-if="workflow" class="d-flex flex-gapx-1 align-items-center">
<div class="d-flex flex-column align-items-end mr-2">
<span v-if="workflowVersion !== undefined" class="mb-1">
<FontAwesomeIcon :icon="faSitemap" />
Workflow Version: {{ workflowVersion + 1 }}
</span>
<WorkflowInvocationsCount class="float-right" :workflow="getWorkflow()" />
<WorkflowInvocationsCount class="float-right" :workflow="workflow" />
</div>
<BButtonGroup vertical>
<BButton
Expand All @@ -221,12 +209,12 @@ function getWorkflowName() {
size="sm"
variant="secondary"
:disabled="isDeletedWorkflow"
:to="`/workflows/edit?id=${getWorkflowId()}&version=${workflowVersion}`">
:to="`/workflows/edit?id=${workflow.id}&version=${workflowVersion}`">
<FontAwesomeIcon :icon="faEdit" />
<span v-localize>Edit</span>
</BButton>
<WorkflowRunButton
:id="getWorkflowId() || ''"
:id="workflow.id || ''"
:title="
!isDeletedWorkflow
? `<b>Rerun</b><br>${getWorkflowName()}`
Expand All @@ -243,9 +231,14 @@ function getWorkflowName() {
class="mt-1 d-flex flex-column overflow-auto"
:content-class="['overflow-auto', isScrollable ? 'pr-2' : '']">
<BTab key="0" title="Overview" active>
<BAlert v-if="initialLoading" variant="info" show>
<LoadingSpan message="Loading workflow" />
</BAlert>
<WorkflowInvocationOverview
v-else
class="invocation-overview"
:invocation="invocation"
:workflow="workflow"
:index="index"
:is-full-page="props.isFullPage"
:invocation-and-job-terminal="invocationAndJobTerminal"
Expand All @@ -261,7 +254,7 @@ function getWorkflowName() {
<BTab
title="Report"
title-item-class="invocation-report-tab"
:disabled="!invocationStateSuccess"
:disabled="!invocationStateSuccess || !workflow"
:lazy="reportLazy"
:active.sync="reportActive">
<InvocationReport v-if="invocationStateSuccess" :invocation-id="invocation.id" />
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
<script setup lang="ts">
import { computed, watch } from "vue";
import { faExclamationCircle } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome";
import { computed, ref, watch } from "vue";
import { useToolStore } from "@/stores/toolStore";
import { useWorkflowStore } from "@/stores/workflowStore";
import { errorMessageAsString } from "@/utils/simple-error";
interface WorkflowInvocationStepTitleProps {
stepIndex: number;
Expand Down Expand Up @@ -54,26 +57,39 @@ const title = computed(() => {
return `Step ${oneBasedStepIndex}: Unknown step type '${workflowStepType}'`;
}
});
const hoverError = ref("");
function initStores() {
async function initStores() {
if (props.stepToolId && !toolStore.getToolForId(props.stepToolId)) {
toolStore.fetchToolForId(props.stepToolId);
}
if (props.stepSubworkflowId) {
workflowStore.fetchWorkflowForInstanceId(props.stepSubworkflowId);
try {
await workflowStore.fetchWorkflowForInstanceId(props.stepSubworkflowId);
} catch (e) {
hoverError.value = errorMessageAsString(e, "Error fetching subworkflow");
}
}
}
watch(
props,
() => {
initStores();
async () => {
await initStores();
},
{ immediate: true }
);
</script>

<template>
<span>{{ title }}</span>
<span>
{{ title }}
<span v-b-tooltip.noninteractive.hover.v-danger :title="hoverError">
<FontAwesomeIcon
v-if="hoverError"
class="text-danger"
:icon="faExclamationCircle" />
</span>
</span>
</template>
1 change: 1 addition & 0 deletions client/src/stores/workflowStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export interface Workflow {
step_count?: number;
latest_id?: string;
version: number;
deleted?: boolean;
}

export const useWorkflowStore = defineStore("workflowStore", () => {
Expand Down

0 comments on commit 991eb9e

Please sign in to comment.