Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Place invocation state in list #17708

Draft
wants to merge 12 commits into
base: dev
Choose a base branch
from
Draft
2 changes: 2 additions & 0 deletions client/src/api/invocations.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import { type components } from "./schema";

export type WorkflowInvocationElementView = components["schemas"]["WorkflowInvocationElementView"];
export type LegacyWorkflowInvocationElementView = components["schemas"]["LegacyWorkflowInvocationElementView"];
export type WorkflowInvocationCollectionView = components["schemas"]["WorkflowInvocationCollectionView"];
export type WorkflowInvocationStepStatesView = components["schemas"]["WorkflowInvocationStepStatesView"];
export type InvocationJobsSummary = components["schemas"]["InvocationJobsResponse"];
export type InvocationStep = components["schemas"]["InvocationStep"];
export type LegacyInvocationStep = components["schemas"]["LegacyInvocationStep"];
export type InvocationMessage = components["schemas"]["InvocationMessageResponseUnion"];

export type StepJobSummary =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { BAlert, BButton, BCard, BCardBody, BCardHeader } from "bootstrap-vue";
import { storeToRefs } from "pinia";
import { computed, onUnmounted, ref, watch } from "vue";

import type { WorkflowInvocationElementView } from "@/api/invocations";
import type { LegacyWorkflowInvocationElementView } from "@/api/invocations";
import { JobProvider } from "@/components/providers";
import { useDatatypesMapper } from "@/composables/datatypesMapper";
import { useInvocationGraph } from "@/composables/useInvocationGraph";
Expand All @@ -35,7 +35,7 @@ library.add(faArrowDown, faChevronDown, faChevronUp, faSignInAlt, faSitemap, faT

interface Props {
/** The invocation to display */
invocation: WorkflowInvocationElementView;
invocation: LegacyWorkflowInvocationElementView;
/** The workflow which was run */
workflow: Workflow;
/** Whether the invocation is terminal */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { faChevronDown, faChevronUp, faSignInAlt } from "@fortawesome/free-solid
import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome";
import { computed, nextTick, ref, watch } from "vue";

import type { WorkflowInvocationElementView } from "@/api/invocations";
import type { LegacyWorkflowInvocationElementView } from "@/api/invocations";
import { isWorkflowInput } from "@/components/Workflow/constants";
import type { GraphStep } from "@/composables/useInvocationGraph";
import type { Workflow } from "@/stores/workflowStore";
Expand All @@ -19,7 +19,7 @@ interface Props {
/** The store id for the invocation graph */
storeId: string;
/** The invocation to display */
invocation: WorkflowInvocationElementView;
invocation: LegacyWorkflowInvocationElementView;
/** The workflow which was run */
workflow: Workflow;
/** Whether the invocation graph is hidden */
Expand Down
10 changes: 3 additions & 7 deletions client/src/components/Workflow/InvocationsListState.vue
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
<script setup lang="ts">
import { computed, onBeforeUnmount, onMounted, toRef } from "vue";
import { onBeforeUnmount, onMounted, toRef } from "vue";

import type { WorkflowInvocationElementView } from "@/api/invocations";
import { useInvocationState } from "@/components/WorkflowInvocationState/usesInvocationState";

import HelpText from "@/components/Help/HelpText.vue";
Expand All @@ -17,17 +16,14 @@ interface Props {
const props = defineProps<Props>();

const {
invocation: invocationFromState,
invocation,
invocationState,
invocationSchedulingTerminal,
invocationAndJobTerminal,
jobStatesSummary,
monitorState,
clearStateMonitor,
} = useInvocationState(toRef(props, "invocationId"), true);

// TODO: This is a workaround to type invocation; I would've expected it to already be typed
const invocation = computed(() => invocationFromState.value as WorkflowInvocationElementView | undefined);
} = useInvocationState(toRef(props, "invocationId"), "step_states");

onMounted(monitorState);
onBeforeUnmount(clearStateMonitor);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
<script setup lang="ts">
import { computed } from "vue";

import { type InvocationStep, type WorkflowInvocationElementView } from "@/api/invocations";
import {
type LegacyWorkflowInvocationElementView,
type WorkflowInvocationElementView,
type WorkflowInvocationStepStatesView,
} from "@/api/invocations";

import ProgressBar from "@/components/ProgressBar.vue";

interface Props {
invocation?: WorkflowInvocationElementView;
invocation?: WorkflowInvocationElementView | WorkflowInvocationStepStatesView | LegacyWorkflowInvocationElementView;
invocationState: string;
invocationSchedulingTerminal: boolean;
}
Expand All @@ -24,12 +28,12 @@ const stepStates = computed<StepStateType>(() => {
if (!props.invocation) {
return {};
}
const steps: InvocationStep[] = props.invocation?.steps || [];
const steps = props.invocation?.steps || [];
for (const step of steps) {
if (!step) {
const stepState = step.state;
if (!stepState) {
continue;
}
const stepState: string = step.state;
if (!stepStates[stepState]) {
stepStates[stepState] = 1;
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { computed, ref } from "vue";
import { RouterLink } from "vue-router";

import { isRegisteredUser } from "@/api";
import type { WorkflowInvocationElementView } from "@/api/invocations";
import type { WorkflowInvocation } from "@/api/invocations";
import { useWorkflowInstance } from "@/composables/useWorkflowInstance";
import { useUserStore } from "@/stores/userStore";
import type { Workflow } from "@/stores/workflowStore";
Expand All @@ -25,7 +25,7 @@ import WorkflowInvocationsCount from "../Workflow/WorkflowInvocationsCount.vue";
import WorkflowRunButton from "../Workflow/WorkflowRunButton.vue";

interface Props {
invocation: WorkflowInvocationElementView;
invocation: WorkflowInvocation;
fromPanel?: boolean;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import { BAlert, BButton } from "bootstrap-vue";
import { computed } from "vue";

import { type InvocationJobsSummary, type WorkflowInvocationElementView } from "@/api/invocations";
import { type InvocationJobsSummary, type LegacyWorkflowInvocationElementView } from "@/api/invocations";
import { useWorkflowInstance } from "@/composables/useWorkflowInstance";
import { getRootFromIndexLink } from "@/onload";
import { withPrefix } from "@/utils/redirect";
Expand All @@ -22,7 +22,7 @@ function getUrl(path: string): string {
}

interface Props {
invocation: WorkflowInvocationElementView;
invocation: LegacyWorkflowInvocationElementView;
invocationAndJobTerminal: boolean;
invocationSchedulingTerminal: boolean;
isFullPage?: boolean;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import { BAlert, BTab, BTabs } from "bootstrap-vue";
import { computed, onBeforeUnmount, onMounted, onUnmounted, ref, toRef, watch } from "vue";

import { type InvocationJobsSummary, type WorkflowInvocationElementView } from "@/api/invocations";
import { type InvocationJobsSummary } from "@/api/invocations";
import { useAnimationFrameResizeObserver } from "@/composables/sensors/animationFrameResizeObserver";
import { useInvocationStore } from "@/stores/invocationStore";
import { useWorkflowStore } from "@/stores/workflowStore";
Expand Down Expand Up @@ -120,16 +120,13 @@ async function pollJobStatesUntilTerminal() {
}

const {
invocation: invocationFromState,
invocation,
invocationSchedulingTerminal,
invocationAndJobTerminal,
jobStatesSummary,
monitorState,
clearStateMonitor,
} = useInvocationState(toRef(props, "invocationId"));

// TODO: This is a workaround to type invocation; I would've expected it to already be typed
const invocation = computed(() => invocationFromState.value as WorkflowInvocationElementView | undefined);
} = useInvocationState(toRef(props, "invocationId"), "legacy");

onMounted(monitorState);
onBeforeUnmount(clearStateMonitor);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,20 +1,31 @@
import { computed, type Ref } from "vue";

import { type InvocationJobsSummary } from "@/api/invocations";
import {
type InvocationJobsSummary,
type LegacyWorkflowInvocationElementView,
type WorkflowInvocationElementView,
type WorkflowInvocationStepStatesView,
} from "@/api/invocations";
import { useInvocationStore } from "@/stores/invocationStore";

import { isTerminal, jobCount } from "./util";

type OptionalInterval = ReturnType<typeof setInterval> | null;

export function useInvocationState(invocationId: Ref<string>, fetchMinimal: boolean = false) {
export function useInvocationState(invocationId: Ref<string>, view: "element" | "step_states" | "legacy" = "element") {
const invocationStore = useInvocationStore();

const invocation = computed(() => {
if (fetchMinimal) {
return invocationStore.getInvocationWithStepStatesById(invocationId.value);
if (view === "step_states") {
return invocationStore.getInvocationWithStepStatesById(
invocationId.value
) as WorkflowInvocationStepStatesView;
} else if (view === "legacy") {
return invocationStore.getInvocationWithJobStepIdsById(
invocationId.value
) as LegacyWorkflowInvocationElementView;
} else {
return invocationStore.getInvocationById(invocationId.value);
return invocationStore.getInvocationById(invocationId.value) as WorkflowInvocationElementView;
}
});

Expand Down Expand Up @@ -51,8 +62,10 @@ export function useInvocationState(invocationId: Ref<string>, fetchMinimal: bool

async function pollStepStatesUntilTerminal() {
if (!invocation.value || !invocationSchedulingTerminal.value) {
if (fetchMinimal) {
if (view === "step_states") {
await invocationStore.fetchInvocationWithStepStatesForId({ id: invocationId.value });
} else if (view === "legacy") {
await invocationStore.fetchInvocationWithJobStepIdsForId({ id: invocationId.value });
} else {
await invocationStore.fetchInvocationForId({ id: invocationId.value });
}
Expand Down
20 changes: 13 additions & 7 deletions client/src/composables/useInvocationGraph.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,11 @@ import {
import { computed, type Ref, ref, set } from "vue";

import { GalaxyApi } from "@/api";
import { type InvocationStep, type StepJobSummary, type WorkflowInvocationElementView } from "@/api/invocations";
import {
type LegacyInvocationStep,
type LegacyWorkflowInvocationElementView,
type StepJobSummary,
} from "@/api/invocations";
import { isWorkflowInput } from "@/components/Workflow/constants";
import { fromSimple } from "@/components/Workflow/Editor/modules/model";
import { getWorkflowFull } from "@/components/Workflow/workflows.services";
Expand Down Expand Up @@ -67,7 +71,7 @@ const ALL_INSTANCES_STATES = ["deleted", "skipped", "new", "queued"];
* @param workflowId - The id of the workflow that was invoked
*/
export function useInvocationGraph(
invocation: Ref<WorkflowInvocationElementView>,
invocation: Ref<LegacyWorkflowInvocationElementView>,
workflowId: string | undefined,
workflowVersion: number | undefined
) {
Expand Down Expand Up @@ -169,7 +173,7 @@ export function useInvocationGraph(
invocationStepSummary = stepsJobsSummary.find((stepJobSummary: StepJobSummary) => {
if (stepJobSummary.model === "ImplicitCollectionJobs") {
return stepJobSummary.id === invocationStep.implicit_collection_jobs_id;
} else {
} else if (stepJobSummary.model === "Job") {
return stepJobSummary.id === invocationStep.job_id;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jmchilton As you can see in this 78f653c commit, I needed to use LegacyWorkflowInvocationElementView because right here, we need the job_id to associate the stepJobSummary with it.
Of course, overall, I need to rebase and readjust these changes, but is this the right approach as far as the view is concerned?

}
});
Expand All @@ -194,7 +198,7 @@ export function useInvocationGraph(
*/
function updateStep(
graphStep: GraphStep,
invocationStep: InvocationStep | undefined,
invocationStep: LegacyInvocationStep | undefined,
invocationStepSummary: StepJobSummary | undefined
) {
/** The new state for the graph step */
Expand Down Expand Up @@ -240,9 +244,11 @@ export function useInvocationGraph(

// If the state still hasn't been set, set it based on the populated state
if (!newState) {
if (populatedState === "scheduled" || populatedState === "ready") {
newState = "queued";
} else if (populatedState === "resubmitted") {
// These states are apparently not in the schema anymore
// if (populatedState === "scheduled" || populatedState === "ready") {
// newState = "queued";
// } else
if (populatedState === "resubmitted") {
newState = "new";
} else if (populatedState === "failed") {
newState = "error";
Expand Down
17 changes: 16 additions & 1 deletion client/src/stores/invocationStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,16 @@ export const useInvocationStore = defineStore("invocationStore", () => {
return data;
}

async function fetchInvocationDetailsJobStepIds(params: FetchParams): Promise<WorkflowInvocation> {
const { data, error } = await GalaxyApi().GET("/api/invocations/{invocation_id}", {
params: { path: { invocation_id: params.id }, query: { legacy_job_state: true } },
});
if (error) {
rethrowSimple(error);
}
return data;
}

async function fetchInvocationJobsSummary(params: FetchParams): Promise<InvocationJobsSummary> {
const { data, error } = await GalaxyApi().GET("/api/invocations/{invocation_id}/jobs_summary", {
params: { path: { invocation_id: params.id } },
Expand All @@ -43,7 +53,7 @@ export const useInvocationStore = defineStore("invocationStore", () => {

async function fetchInvocationStepStateDetails(params: FetchParams): Promise<WorkflowInvocationStepStatesView> {
const { data, error } = await GalaxyApi().GET("/api/invocations/{invocation_id}", {
params: { path: { invocation_id: params.id }, view: "step_states" },
params: { path: { invocation_id: params.id }, query: { view: "step_states" } },
});
if (error) {
rethrowSimple(error);
Expand All @@ -54,6 +64,9 @@ export const useInvocationStore = defineStore("invocationStore", () => {
const { getItemById: getInvocationById, fetchItemById: fetchInvocationForId } =
useKeyedCache<WorkflowInvocation>(fetchInvocationDetails);

const { getItemById: getInvocationWithJobStepIdsById, fetchItemById: fetchInvocationWithJobStepIdsForId } =
useKeyedCache<WorkflowInvocation>(fetchInvocationDetailsJobStepIds);

const { getItemById: getInvocationWithStepStatesById, fetchItemById: fetchInvocationWithStepStatesForId } =
useKeyedCache<WorkflowInvocation>(fetchInvocationStepStateDetails);

Expand All @@ -66,6 +79,8 @@ export const useInvocationStore = defineStore("invocationStore", () => {
return {
getInvocationById,
fetchInvocationForId,
getInvocationWithJobStepIdsById,
fetchInvocationWithJobStepIdsForId,
getInvocationJobsSummaryById,
fetchInvocationJobsSummaryForId,
getInvocationStepById,
Expand Down