diff --git a/client/src/api/invocations.ts b/client/src/api/invocations.ts index 935fa7463bff..a6cb5957030f 100644 --- a/client/src/api/invocations.ts +++ b/client/src/api/invocations.ts @@ -1,3 +1,6 @@ +import { rethrowSimple } from "@/utils/simple-error"; + +import { GalaxyApi } from "./client"; import { type components } from "./schema"; export type WorkflowInvocationElementView = components["schemas"]["WorkflowInvocationElementView"]; @@ -12,3 +15,15 @@ export type StepJobSummary = | components["schemas"]["InvocationStepJobsResponseCollectionJobsModel"]; export type WorkflowInvocation = components["schemas"]["WorkflowInvocationResponse"]; + +export async function cancelWorkflowScheduling(invocationId: string) { + const { data, error } = await GalaxyApi().DELETE("/api/invocations/{invocation_id}", { + params: { + path: { invocation_id: invocationId }, + }, + }); + if (error) { + rethrowSimple(error); + } + return data; +} diff --git a/client/src/components/WorkflowInvocationState/WorkflowInvocationState.vue b/client/src/components/WorkflowInvocationState/WorkflowInvocationState.vue index 839dda0cf7bb..8eaf178ee656 100644 --- a/client/src/components/WorkflowInvocationState/WorkflowInvocationState.vue +++ b/client/src/components/WorkflowInvocationState/WorkflowInvocationState.vue @@ -5,12 +5,12 @@ import { BAlert, BBadge, BButton, BTab, BTabs } from "bootstrap-vue"; import { computed, onUnmounted, ref, watch } from "vue"; import { type InvocationJobsSummary, type InvocationStep, type WorkflowInvocationElementView } from "@/api/invocations"; +import { cancelWorkflowScheduling } from "@/api/invocations"; import { useAnimationFrameResizeObserver } from "@/composables/sensors/animationFrameResizeObserver"; import { useInvocationStore } from "@/stores/invocationStore"; import { useWorkflowStore } from "@/stores/workflowStore"; import { errorMessageAsString } from "@/utils/simple-error"; -import { cancelWorkflowScheduling } from "./services"; import { errorCount as jobStatesSummaryErrorCount, isTerminal, @@ -53,6 +53,7 @@ const stepStatesInterval = ref(undefined); const jobStatesInterval = ref(undefined); const invocationLoaded = ref(false); const errorMessage = ref(null); +const cancellingInvocation = ref(false); // after the report tab is first activated, no longer lazy-render it from then on const reportActive = ref(false); @@ -219,11 +220,22 @@ async function pollJobStatesUntilTerminal() { function onError(e: any) { console.error(e); } -function onCancel() { - emit("invocation-cancelled"); -} -function cancelWorkflowSchedulingLocal() { - cancelWorkflowScheduling(props.invocationId).then(onCancel).catch(onError); +async function onCancel() { + try { + cancellingInvocation.value = true; + await cancelWorkflowScheduling(props.invocationId); + } catch (e) { + onError(e); + } finally { + emit("invocation-cancelled"); + + // Update the invocation state to reflect the cancellation + setTimeout(async () => { + await invocationStore.fetchInvocationForId({ id: props.invocationId }); + await invocationStore.fetchInvocationJobsSummaryForId({ id: props.invocationId }); + cancellingInvocation.value = false; + }, 3000); + } } @@ -243,6 +255,7 @@ function cancelWorkflowSchedulingLocal() { size="sm" class="text-decoration-none" variant="link" + :disabled="cancellingInvocation" @click="onCancel"> Cancel @@ -302,8 +315,7 @@ function cancelWorkflowSchedulingLocal() { :is-full-page="props.isFullPage" :invocation-and-job-terminal="invocationAndJobTerminal" :invocation-scheduling-terminal="invocationSchedulingTerminal" - :is-subworkflow="isSubworkflow" - @invocation-cancelled="cancelWorkflowSchedulingLocal" /> + :is-subworkflow="isSubworkflow" /> getRootFromIndexLink() + path; - -export function getInvocationJobsSummary(invocationId) { - const url = getUrl(`api/invocations/${invocationId}/jobs_summary`); - return axios.get(url); -} - -export function cancelWorkflowScheduling(invocationId) { - const url = getUrl(`api/invocations/${invocationId}`); - return axios.delete(url); -}