diff --git a/client/src/api/invocations.ts b/client/src/api/invocations.ts
index a6cb5957030f..935fa7463bff 100644
--- a/client/src/api/invocations.ts
+++ b/client/src/api/invocations.ts
@@ -1,6 +1,3 @@
-import { rethrowSimple } from "@/utils/simple-error";
-
-import { GalaxyApi } from "./client";
import { type components } from "./schema";
export type WorkflowInvocationElementView = components["schemas"]["WorkflowInvocationElementView"];
@@ -15,15 +12,3 @@ 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/Workflow/Invocation/Graph/InvocationGraph.vue b/client/src/components/Workflow/Invocation/Graph/InvocationGraph.vue
index 4d6f6e68717d..448cacdf3989 100644
--- a/client/src/components/Workflow/Invocation/Graph/InvocationGraph.vue
+++ b/client/src/components/Workflow/Invocation/Graph/InvocationGraph.vue
@@ -34,8 +34,6 @@ interface Props {
workflow: Workflow;
/** Whether the invocation is terminal */
isTerminal: boolean;
- /** Whether the invocation is scheduled */
- isScheduled: boolean;
/** The zoom level for the graph */
zoom?: number;
/** Whether to show the minimap */
diff --git a/client/src/components/WorkflowInvocationState/WorkflowInvocationOverview.vue b/client/src/components/WorkflowInvocationState/WorkflowInvocationOverview.vue
index d076e639daf0..fb79d5bb3d9e 100644
--- a/client/src/components/WorkflowInvocationState/WorkflowInvocationOverview.vue
+++ b/client/src/components/WorkflowInvocationState/WorkflowInvocationOverview.vue
@@ -15,7 +15,6 @@ import InvocationMessage from "@/components/WorkflowInvocationState/InvocationMe
interface Props {
invocation: WorkflowInvocationElementView;
invocationAndJobTerminal: boolean;
- invocationSchedulingTerminal: boolean;
isFullPage?: boolean;
isSubworkflow?: boolean;
}
@@ -62,7 +61,6 @@ const uniqueMessages = computed(() => {
:invocation="invocation"
:workflow="workflow"
:is-terminal="invocationAndJobTerminal"
- :is-scheduled="invocationSchedulingTerminal"
:is-full-page="isFullPage"
:show-minimap="isFullPage" />
diff --git a/client/src/components/WorkflowInvocationState/WorkflowInvocationState.vue b/client/src/components/WorkflowInvocationState/WorkflowInvocationState.vue
index 8eaf178ee656..7ab995fe87b8 100644
--- a/client/src/components/WorkflowInvocationState/WorkflowInvocationState.vue
+++ b/client/src/components/WorkflowInvocationState/WorkflowInvocationState.vue
@@ -5,7 +5,6 @@ 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";
@@ -200,6 +199,16 @@ watch(
const storeId = computed(() => (invocation.value ? `invocation-${invocation.value.id}` : undefined));
+watch(
+ () => invocationSchedulingTerminal.value,
+ async (newVal, oldVal) => {
+ if (oldVal && !newVal) {
+ // If the invocation was terminal and now is not, start polling again
+ await pollStepStatesUntilTerminal();
+ }
+ }
+);
+
onUnmounted(() => {
clearTimeout(stepStatesInterval.value);
clearTimeout(jobStatesInterval.value);
@@ -223,18 +232,12 @@ function onError(e: any) {
async function onCancel() {
try {
cancellingInvocation.value = true;
- await cancelWorkflowScheduling(props.invocationId);
+ await invocationStore.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);
+ cancellingInvocation.value = false;
}
}
@@ -255,7 +258,7 @@ async function onCancel() {
size="sm"
class="text-decoration-none"
variant="link"
- :disabled="cancellingInvocation"
+ :disabled="cancellingInvocation || invocationState == 'cancelling'"
@click="onCancel">
Cancel
@@ -314,7 +317,6 @@ async function onCancel() {
:invocation="invocation"
:is-full-page="props.isFullPage"
:invocation-and-job-terminal="invocationAndJobTerminal"
- :invocation-scheduling-terminal="invocationSchedulingTerminal"
:is-subworkflow="isSubworkflow" />
diff --git a/client/src/composables/useInvocationGraph.ts b/client/src/composables/useInvocationGraph.ts
index 89e3570ce459..211db6cd7979 100644
--- a/client/src/composables/useInvocationGraph.ts
+++ b/client/src/composables/useInvocationGraph.ts
@@ -69,7 +69,7 @@ export const statePlaceholders: Record = {
};
/** Only one job needs to be in one of these states for the graph step to be in that state */
-const SINGLE_INSTANCE_STATES = ["error", "running", "paused"];
+const SINGLE_INSTANCE_STATES = ["error", "running", "paused", "deleting"];
/** All jobs need to be in one of these states for the graph step to be in that state */
const ALL_INSTANCES_STATES = ["deleted", "skipped", "new", "queued"];
@@ -293,6 +293,9 @@ export function useInvocationGraph(
function getStepStateFromJobStates(jobStates: string[]): GraphStep["state"] | undefined {
for (const state of SINGLE_INSTANCE_STATES) {
if (jobStates.includes(state)) {
+ if (state === "deleting") {
+ return "deleted";
+ }
return state as GraphStep["state"];
}
}
diff --git a/client/src/stores/invocationStore.ts b/client/src/stores/invocationStore.ts
index 0ce8a6337fba..d212f8c6e644 100644
--- a/client/src/stores/invocationStore.ts
+++ b/client/src/stores/invocationStore.ts
@@ -42,8 +42,24 @@ export const useInvocationStore = defineStore("invocationStore", () => {
return data;
}
- const { getItemById: getInvocationById, fetchItemById: fetchInvocationForId } =
- useKeyedCache(fetchInvocationDetails);
+ async function cancelWorkflowScheduling(invocationId: string) {
+ const { data, error } = await GalaxyApi().DELETE("/api/invocations/{invocation_id}", {
+ params: {
+ path: { invocation_id: invocationId },
+ },
+ });
+ if (error) {
+ rethrowSimple(error);
+ }
+ storedInvocations.value[invocationId] = data;
+ return data;
+ }
+
+ const {
+ getItemById: getInvocationById,
+ fetchItemById: fetchInvocationForId,
+ storedItems: storedInvocations,
+ } = useKeyedCache(fetchInvocationDetails);
const { getItemById: getInvocationJobsSummaryById, fetchItemById: fetchInvocationJobsSummaryForId } =
useKeyedCache(fetchInvocationJobsSummary);
@@ -58,6 +74,7 @@ export const useInvocationStore = defineStore("invocationStore", () => {
fetchInvocationJobsSummaryForId,
getInvocationStepById,
fetchInvocationStepById,
+ cancelWorkflowScheduling,
graphStepsByStoreId,
};
});