-
+
Generate PDF
@@ -188,9 +193,9 @@ function onCancel() {
note="Loading step state summary..."
:loading="true"
class="steps-progress" />
-
+
-
-
-
-
-
-
- This subworkflow is
- .
- Click here
- to view this subworkflow in graph view.
-
+
+
+
+
+
+ {{ error }}
+
+
+
+
No workflow found for this invocation.
diff --git a/client/src/components/WorkflowInvocationState/WorkflowInvocationState.test.ts b/client/src/components/WorkflowInvocationState/WorkflowInvocationState.test.ts
index 93b3a22c9b09..63ed137bb97f 100644
--- a/client/src/components/WorkflowInvocationState/WorkflowInvocationState.test.ts
+++ b/client/src/components/WorkflowInvocationState/WorkflowInvocationState.test.ts
@@ -1,5 +1,5 @@
import { createTestingPinia } from "@pinia/testing";
-import { mount, shallowMount, Wrapper } from "@vue/test-utils";
+import { shallowMount, Wrapper } from "@vue/test-utils";
import flushPromises from "flush-promises";
import { PiniaVuePlugin, setActivePinia } from "pinia";
import { getLocalVue } from "tests/jest/helpers";
@@ -13,6 +13,10 @@ localVue.use(PiniaVuePlugin);
const selectors = {
invocationSummary: ".invocation-overview",
+ bAlertStub: "balert-stub",
+ spanElement: "span",
+ invocationReportTab: "btab-stub[title='Report']",
+ fullPageHeading: "anonymous-stub[h1='true']",
};
/** Invocation data to be expected in the store */
@@ -75,32 +79,42 @@ jest.mock("@/stores/invocationStore", () => {
};
});
+// Mock the workflow store to return a workflow for `getStoredWorkflowByInstanceId`
+jest.mock("@/stores/workflowStore", () => {
+ const originalModule = jest.requireActual("@/stores/workflowStore");
+ return {
+ ...originalModule,
+ useWorkflowStore: () => ({
+ ...originalModule.useWorkflowStore(),
+ getStoredWorkflowByInstanceId: jest.fn().mockImplementation(() => {
+ return {
+ id: "workflow-id",
+ name: "Test Workflow",
+ version: 0,
+ };
+ }),
+ }),
+ };
+});
+
/** Mount the WorkflowInvocationState component with the given invocation ID
* @param invocationId The invocation ID to be passed as a prop
+ * @param shallow Whether to use shallowMount or mount
+ * @param fullPage Whether to render the header as well or just the invocation state tabs
* @returns The mounted wrapper
*/
-async function mountWorkflowInvocationState(invocationId: string, shallow = true) {
+async function mountWorkflowInvocationState(invocationId: string, isFullPage = false) {
const pinia = createTestingPinia();
setActivePinia(pinia);
- let wrapper;
- if (shallow) {
- wrapper = shallowMount(WorkflowInvocationState as object, {
- propsData: {
- invocationId,
- },
- pinia,
- localVue,
- });
- } else {
- wrapper = mount(WorkflowInvocationState as object, {
- propsData: {
- invocationId,
- },
- pinia,
- localVue,
- });
- }
+ const wrapper = shallowMount(WorkflowInvocationState as object, {
+ propsData: {
+ invocationId,
+ isFullPage,
+ },
+ pinia,
+ localVue,
+ });
await flushPromises();
return wrapper;
}
@@ -124,9 +138,9 @@ describe("WorkflowInvocationState check invocation and job terminal states", ()
assertJobsSummaryFetched(0);
// expect there to be an alert for the missing invocation
- const alert = wrapper.find("balert-stub");
+ const alert = wrapper.find(selectors.bAlertStub);
expect(alert.attributes("variant")).toBe("info");
- const span = alert.find("span");
+ const span = alert.find(selectors.spanElement);
expect(span.text()).toBe("Invocation not found.");
});
@@ -157,22 +171,22 @@ describe("WorkflowInvocationState check invocation and job terminal states", ()
assertJobsSummaryFetched(0);
// expect there to be an alert for the handled error
- const alert = wrapper.find("balert-stub");
+ const alert = wrapper.find(selectors.bAlertStub);
expect(alert.attributes("variant")).toBe("danger");
expect(alert.text()).toBe("User does not own specified item.");
});
});
-describe("WorkflowInvocationState check 'Report' tab disabled state", () => {
+describe("WorkflowInvocationState check 'Report' tab disabled state and header", () => {
it("determines that 'Report' tab is disabled for non-terminal invocation", async () => {
- const wrapper = await mountWorkflowInvocationState("non-terminal-id", false);
- const reportTab = wrapper.find(".invocation-report-tab").find(".nav-link");
- expect(reportTab.classes()).toContain("disabled");
+ const wrapper = await mountWorkflowInvocationState("non-terminal-id");
+ const reportTab = wrapper.find(selectors.invocationReportTab);
+ expect(reportTab.attributes("disabled")).toBe("true");
});
it("determines that 'Report' tab is not disabled for terminal invocation", async () => {
- const wrapper = await mountWorkflowInvocationState(invocationData.id, false);
- const reportTab = wrapper.find(".invocation-report-tab").find(".nav-link");
- expect(reportTab.classes()).not.toContain("disabled");
+ const wrapper = await mountWorkflowInvocationState(invocationData.id);
+ const reportTab = wrapper.find(selectors.invocationReportTab);
+ expect(reportTab.attributes("disabled")).toBeUndefined();
});
});
diff --git a/client/src/components/WorkflowInvocationState/WorkflowInvocationState.vue b/client/src/components/WorkflowInvocationState/WorkflowInvocationState.vue
index b1e590cd203d..e035bc9b7dbe 100644
--- a/client/src/components/WorkflowInvocationState/WorkflowInvocationState.vue
+++ b/client/src/components/WorkflowInvocationState/WorkflowInvocationState.vue
@@ -1,34 +1,23 @@
-
-
- Invoked Workflow: "{{ getWorkflowName() }}"
-
-
-
-
-
- Invocations List
-
-
-
-
-
-
- invoked
-
-
-
- History:
-
-
-
-
-
-
-
- Workflow Version: {{ workflowVersion + 1 }}
-
-
-
-
-
-
- Edit
-
-
-
-
-
+
TODO: Insert readonly version of workflow editor here
-->
@@ -276,12 +187,12 @@ function getWorkflowName() {
-
-
-
{{ errorMessage }}
+
+
+
Invocation not found.
diff --git a/client/src/components/WorkflowInvocationState/WorkflowStepTitle.vue b/client/src/components/WorkflowInvocationState/WorkflowStepTitle.vue
index 1253d73e3e40..3735415dd586 100644
--- a/client/src/components/WorkflowInvocationState/WorkflowStepTitle.vue
+++ b/client/src/components/WorkflowInvocationState/WorkflowStepTitle.vue
@@ -1,8 +1,11 @@
- {{ title }}
+
+ {{ title }}
+
+
+
+
diff --git a/client/src/components/WorkflowInvocationState/invocationMessageModel.ts b/client/src/components/WorkflowInvocationState/invocationMessageModel.ts
deleted file mode 100644
index 29ab6a989847..000000000000
--- a/client/src/components/WorkflowInvocationState/invocationMessageModel.ts
+++ /dev/null
@@ -1,250 +0,0 @@
-/* tslint:disable */
-/* eslint-disable */
-/**
-/* This file was automatically generated from pydantic models by running pydantic2ts.
-/* Do not modify it by hand - just update the pydantic models and then re-run the script
-*/
-
-export type InvocationMessageResponseModel =
- | GenericInvocationCancellationReviewFailedEncodedDatabaseIdField
- | GenericInvocationCancellationHistoryDeletedEncodedDatabaseIdField
- | GenericInvocationCancellationUserRequestEncodedDatabaseIdField
- | GenericInvocationFailureDatasetFailedEncodedDatabaseIdField
- | GenericInvocationFailureCollectionFailedEncodedDatabaseIdField
- | GenericInvocationFailureJobFailedEncodedDatabaseIdField
- | GenericInvocationFailureOutputNotFoundEncodedDatabaseIdField
- | GenericInvocationFailureExpressionEvaluationFailedEncodedDatabaseIdField
- | GenericInvocationFailureWhenNotBooleanEncodedDatabaseIdField
- | GenericInvocationUnexpectedFailureEncodedDatabaseIdField
- | GenericInvocationEvaluationWarningWorkflowOutputNotFoundEncodedDatabaseIdField;
-
-export interface GenericInvocationCancellationHistoryDeletedEncodedDatabaseIdField {
- reason: "history_deleted";
- /**
- * History ID of history that was deleted.
- */
- history_id: string;
-}
-export interface GenericInvocationCancellationHistoryDeletedInt {
- reason: "history_deleted";
- /**
- * History ID of history that was deleted.
- */
- history_id: number;
-}
-export interface GenericInvocationCancellationReviewFailedEncodedDatabaseIdField {
- reason: "cancelled_on_review";
- /**
- * Workflow step id of paused step that did not pass review.
- */
- workflow_step_id: number;
-}
-export interface GenericInvocationCancellationReviewFailedInt {
- reason: "cancelled_on_review";
- /**
- * Workflow step id of paused step that did not pass review.
- */
- workflow_step_id: number;
-}
-export interface GenericInvocationCancellationUserRequestEncodedDatabaseIdField {
- reason: "user_request";
-}
-export interface GenericInvocationCancellationUserRequestInt {
- reason: "user_request";
-}
-export interface GenericInvocationEvaluationWarningWorkflowOutputNotFoundEncodedDatabaseIdField {
- reason: "workflow_output_not_found";
- workflow_step_id: number;
- /**
- * Output that was designated as workflow output but that has not been found
- */
- output_name: string;
-}
-export interface GenericInvocationEvaluationWarningWorkflowOutputNotFoundInt {
- reason: "workflow_output_not_found";
- workflow_step_id: number;
- /**
- * Output that was designated as workflow output but that has not been found
- */
- output_name: string;
-}
-export interface GenericInvocationFailureCollectionFailedEncodedDatabaseIdField {
- reason: "collection_failed";
- /**
- * Workflow step id of step that failed.
- */
- workflow_step_id: number;
- /**
- * HistoryDatasetCollectionAssociation ID that relates to failure.
- */
- hdca_id: string;
- /**
- * Workflow step id of step that caused failure.
- */
- dependent_workflow_step_id: number;
-}
-export interface GenericInvocationFailureCollectionFailedInt {
- reason: "collection_failed";
- /**
- * Workflow step id of step that failed.
- */
- workflow_step_id: number;
- /**
- * HistoryDatasetCollectionAssociation ID that relates to failure.
- */
- hdca_id: number;
- /**
- * Workflow step id of step that caused failure.
- */
- dependent_workflow_step_id: number;
-}
-export interface GenericInvocationFailureDatasetFailedEncodedDatabaseIdField {
- reason: "dataset_failed";
- /**
- * Workflow step id of step that failed.
- */
- workflow_step_id: number;
- /**
- * HistoryDatasetAssociation ID that relates to failure.
- */
- hda_id: string;
- /**
- * Workflow step id of step that caused failure.
- */
- dependent_workflow_step_id?: number;
-}
-export interface GenericInvocationFailureDatasetFailedInt {
- reason: "dataset_failed";
- /**
- * Workflow step id of step that failed.
- */
- workflow_step_id: number;
- /**
- * HistoryDatasetAssociation ID that relates to failure.
- */
- hda_id: number;
- /**
- * Workflow step id of step that caused failure.
- */
- dependent_workflow_step_id?: number;
-}
-export interface GenericInvocationFailureExpressionEvaluationFailedEncodedDatabaseIdField {
- reason: "expression_evaluation_failed";
- /**
- * Workflow step id of step that failed.
- */
- workflow_step_id: number;
- /**
- * May contain details to help troubleshoot this problem.
- */
- details?: string;
-}
-export interface GenericInvocationFailureExpressionEvaluationFailedInt {
- reason: "expression_evaluation_failed";
- /**
- * Workflow step id of step that failed.
- */
- workflow_step_id: number;
- /**
- * May contain details to help troubleshoot this problem.
- */
- details?: string;
-}
-export interface GenericInvocationFailureJobFailedEncodedDatabaseIdField {
- reason: "job_failed";
- /**
- * Workflow step id of step that failed.
- */
- workflow_step_id: number;
- /**
- * Job ID that relates to failure.
- */
- job_id: string;
- /**
- * Workflow step id of step that caused failure.
- */
- dependent_workflow_step_id: number;
-}
-export interface GenericInvocationFailureJobFailedInt {
- reason: "job_failed";
- /**
- * Workflow step id of step that failed.
- */
- workflow_step_id: number;
- /**
- * Job ID that relates to failure.
- */
- job_id: number;
- /**
- * Workflow step id of step that caused failure.
- */
- dependent_workflow_step_id: number;
-}
-export interface GenericInvocationFailureOutputNotFoundEncodedDatabaseIdField {
- reason: "output_not_found";
- /**
- * Workflow step id of step that failed.
- */
- workflow_step_id: number;
- output_name: string;
- /**
- * Workflow step id of step that caused failure.
- */
- dependent_workflow_step_id: number;
-}
-export interface GenericInvocationFailureOutputNotFoundInt {
- reason: "output_not_found";
- /**
- * Workflow step id of step that failed.
- */
- workflow_step_id: number;
- output_name: string;
- /**
- * Workflow step id of step that caused failure.
- */
- dependent_workflow_step_id: number;
-}
-export interface GenericInvocationFailureWhenNotBooleanEncodedDatabaseIdField {
- reason: "when_not_boolean";
- /**
- * Workflow step id of step that failed.
- */
- workflow_step_id: number;
- /**
- * Contains details to help troubleshoot this problem.
- */
- details: string;
-}
-export interface GenericInvocationFailureWhenNotBooleanInt {
- reason: "when_not_boolean";
- /**
- * Workflow step id of step that failed.
- */
- workflow_step_id: number;
- /**
- * Contains details to help troubleshoot this problem.
- */
- details: string;
-}
-export interface GenericInvocationUnexpectedFailureEncodedDatabaseIdField {
- reason: "unexpected_failure";
- /**
- * May contains details to help troubleshoot this problem.
- */
- details?: string;
- /**
- * Workflow step id of step that failed.
- */
- workflow_step_id?: number;
-}
-export interface GenericInvocationUnexpectedFailureInt {
- reason: "unexpected_failure";
- /**
- * May contains details to help troubleshoot this problem.
- */
- details?: string;
- /**
- * Workflow step id of step that failed.
- */
- workflow_step_id?: number;
-}
diff --git a/client/src/composables/useWorkflowInstance.ts b/client/src/composables/useWorkflowInstance.ts
index 426c9269bfbc..53bb6d287dee 100644
--- a/client/src/composables/useWorkflowInstance.ts
+++ b/client/src/composables/useWorkflowInstance.ts
@@ -1,11 +1,13 @@
import { computed, ref } from "vue";
import { useWorkflowStore } from "@/stores/workflowStore";
+import { errorMessageAsString } from "@/utils/simple-error";
export function useWorkflowInstance(workflowId: string) {
const workflowStore = useWorkflowStore();
const workflow = computed(() => workflowStore.getStoredWorkflowByInstanceId(workflowId));
const loading = ref(false);
+ const error = ref(null);
async function getWorkflowInstance() {
if (!workflow.value) {
@@ -13,7 +15,7 @@ export function useWorkflowInstance(workflowId: string) {
try {
await workflowStore.fetchWorkflowForInstanceId(workflowId);
} catch (e) {
- console.error("unable to fetch workflow \n", e);
+ error.value = errorMessageAsString(e);
} finally {
loading.value = false;
}
@@ -21,5 +23,5 @@ export function useWorkflowInstance(workflowId: string) {
}
getWorkflowInstance();
- return { workflow, loading };
+ return { workflow, loading, error };
}
diff --git a/client/src/stores/workflowStore.ts b/client/src/stores/workflowStore.ts
index b757fe868b20..2cd194a093c1 100644
--- a/client/src/stores/workflowStore.ts
+++ b/client/src/stores/workflowStore.ts
@@ -12,6 +12,7 @@ export interface Workflow {
step_count?: number;
latest_id?: string;
version: number;
+ deleted?: boolean;
}
export const useWorkflowStore = defineStore("workflowStore", () => {
diff --git a/lib/galaxy/schema/invocation.py b/lib/galaxy/schema/invocation.py
index 1d433fa5584a..4d5ce80548e8 100644
--- a/lib/galaxy/schema/invocation.py
+++ b/lib/galaxy/schema/invocation.py
@@ -20,6 +20,7 @@
from typing_extensions import (
Annotated,
Literal,
+ TypeAliasType,
)
from galaxy.schema import schema
@@ -253,7 +254,7 @@ class GenericInvocationEvaluationWarningWorkflowOutputNotFound(
EncodedDatabaseIdField
]
-InvocationMessageResponseUnion = Annotated[
+_InvocationMessageResponseUnion = Annotated[
Union[
InvocationCancellationReviewFailedResponseModel,
InvocationCancellationHistoryDeletedResponseModel,
@@ -270,6 +271,8 @@ class GenericInvocationEvaluationWarningWorkflowOutputNotFound(
Field(discriminator="reason"),
]
+InvocationMessageResponseUnion = TypeAliasType("InvocationMessageResponseUnion", _InvocationMessageResponseUnion)
+
class InvocationMessageResponseModel(RootModel):
root: InvocationMessageResponseUnion