Skip to content

Commit

Permalink
Lighter-weight invocation view for polling from list.
Browse files Browse the repository at this point in the history
  • Loading branch information
jmchilton committed Mar 25, 2024
1 parent ee7aad9 commit ec403cc
Show file tree
Hide file tree
Showing 8 changed files with 110 additions and 12 deletions.
15 changes: 14 additions & 1 deletion client/src/api/invocations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,16 @@ import { ApiResponse, components, fetcher } from "./schema";

export type WorkflowInvocationElementView = components["schemas"]["WorkflowInvocationElementView"];
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 const invocationsFetcher = fetcher.path("/api/invocations").method("get").create();

export type WorkflowInvocation = WorkflowInvocationElementView | WorkflowInvocationCollectionView;
export type WorkflowInvocation =
| WorkflowInvocationElementView
| WorkflowInvocationCollectionView
| WorkflowInvocationStepStatesView;

export interface WorkflowInvocationStep {
id: string;
Expand All @@ -34,6 +38,15 @@ export async function fetchInvocationDetails(params: { id: string }): Promise<Ap
} as ApiResponse<WorkflowInvocation>;
}

export async function fetchInvocationStepStateDetails(params: {
id: string;
}): Promise<ApiResponse<WorkflowInvocationStepStatesView>> {
const { data } = await axios.get(`${getAppRoot()}api/invocations/${params.id}?view=step_states`);
return {
data,
} as ApiResponse<WorkflowInvocationStepStatesView>;
}

export async function fetchInvocationJobsSummary(params: { id: string }): Promise<ApiResponse<InvocationJobsSummary>> {
const { data } = await axios.get(`${getAppRoot()}api/invocations/${params.id}/jobs_summary`);
return {
Expand Down
61 changes: 59 additions & 2 deletions client/src/api/schema/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7869,7 +7869,7 @@ export interface components {
* InvocationSerializationView
* @enum {string}
*/
InvocationSerializationView: "element" | "collection";
InvocationSerializationView: "element" | "collection" | "step_states";
/**
* InvocationSortByEnum
* @enum {string}
Expand Down Expand Up @@ -12917,7 +12917,8 @@ export interface components {
WorkflowInvocationResponse:
| components["schemas"]["WorkflowInvocationElementView"]
| components["schemas"]["LegacyWorkflowInvocationElementView"]
| components["schemas"]["WorkflowInvocationCollectionView"];
| components["schemas"]["WorkflowInvocationCollectionView"]
| components["schemas"]["WorkflowInvocationStepStatesView"];
/** WorkflowInvocationStateSummary */
WorkflowInvocationStateSummary: {
/**
Expand Down Expand Up @@ -12945,6 +12946,60 @@ export interface components {
[key: string]: number | undefined;
};
};
/** WorkflowInvocationStepStatesView */
WorkflowInvocationStepStatesView: {
/**
* Create Time
* Format: date-time
* @description The time and date this item was created.
*/
create_time: string;
/**
* History ID
* @description The encoded ID of the history associated with the invocation.
* @example 0123456789ABCDEF
*/
history_id: string;
/**
* ID
* @description The encoded ID of the workflow invocation.
* @example 0123456789ABCDEF
*/
id: string;
/**
* Model class
* @description The name of the database model class.
* @constant
*/
model_class: "WorkflowInvocation";
/**
* Invocation state
* @description State of workflow invocation.
*/
state: components["schemas"]["InvocationState"];
/**
* Steps
* @description Steps of the workflow invocation.
*/
steps: components["schemas"]["InvocationStep"][];
/**
* Update Time
* Format: date-time
* @description The last time and date this item was updated.
*/
update_time: string;
/**
* UUID
* @description Universal unique identifier of the workflow invocation.
*/
uuid?: string | string | null;
/**
* Workflow ID
* @description The encoded Workflow ID associated with the invocation.
* @example 0123456789ABCDEF
*/
workflow_id: string;
};
/** WriteInvocationStoreToPayload */
WriteInvocationStoreToPayload: {
/**
Expand Down Expand Up @@ -18602,6 +18657,7 @@ export interface operations {
show_invocation_api_invocations__invocation_id__get: {
/** Get detailed description of a workflow invocation. */
parameters: {
/** @description View to be passed to the serializer */
/** @description Include details for individual invocation steps and populate a steps attribute in the resulting dictionary. */
/**
* @description Populate the invocation step state with the job state instead of the invocation step state.
Expand All @@ -18610,6 +18666,7 @@ export interface operations {
* are not the mapped over step outputs but the individual job outputs.
*/
query?: {
view?: string | null;
step_details?: boolean;
legacy_job_state?: boolean;
};
Expand Down
2 changes: 1 addition & 1 deletion client/src/components/Workflow/InvocationsListState.vue
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ const {
jobStatesSummary,
monitorState,
clearStateMonitor,
} = useInvocationState(toRef(props, "invocationId"));
} = useInvocationState(toRef(props, "invocationId"), true);
onMounted(monitorState);
onBeforeUnmount(clearStateMonitor);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,15 @@ import { isTerminal, jobCount } from "./util";

type OptionalInterval = ReturnType<typeof setInterval> | null;

export function useInvocationState(invocationId: Ref<string>) {
export function useInvocationState(invocationId: Ref<string>, fetchMinimal: boolean = false) {
const invocationStore = useInvocationStore();

const invocation = computed(() => {
return invocationStore.getInvocationById(invocationId.value);
if (fetchMinimal) {
return invocationStore.getInvocationWithStepStatesById(invocationId.value);
} else {
return invocationStore.getInvocationById(invocationId.value);
}
});

let stepStatesInterval: OptionalInterval = null;
Expand Down Expand Up @@ -48,7 +52,11 @@ export function useInvocationState(invocationId: Ref<string>) {

async function pollStepStatesUntilTerminal() {
if (!invocation.value || !invocationSchedulingTerminal.value) {
await invocationStore.fetchInvocationForId({ id: invocationId.value });
if (fetchMinimal) {
await invocationStore.fetchInvocationWithStepStatesForId({ id: invocationId.value });
} else {
await invocationStore.fetchInvocationForId({ id: invocationId.value });
}
stepStatesInterval = setTimeout(pollStepStatesUntilTerminal, 3000);
}
}
Expand Down
6 changes: 6 additions & 0 deletions client/src/stores/invocationStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { defineStore } from "pinia";

import {
fetchInvocationDetails,
fetchInvocationStepStateDetails,
fetchInvocationJobsSummary,
fetchInvocationStep,
type WorkflowInvocation,
Expand All @@ -14,6 +15,9 @@ export const useInvocationStore = defineStore("invocationStore", () => {
const { getItemById: getInvocationById, fetchItemById: fetchInvocationForId } =
useKeyedCache<WorkflowInvocation>(fetchInvocationDetails);

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

const { getItemById: getInvocationJobsSummaryById, fetchItemById: fetchInvocationJobsSummaryForId } =
useKeyedCache<InvocationJobsSummary>(fetchInvocationJobsSummary);

Expand All @@ -27,5 +31,7 @@ export const useInvocationStore = defineStore("invocationStore", () => {
fetchInvocationJobsSummaryForId,
getInvocationStepById,
fetchInvocationStepById,
getInvocationWithStepStatesById,
fetchInvocationWithStepStatesForId,
};
});
7 changes: 4 additions & 3 deletions lib/galaxy/model/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8653,11 +8653,12 @@ def _serialize(self, id_encoder, serialization_options):
return invocation_attrs

def to_dict(self, view="collection", value_mapper=None, step_details=False, legacy_job_state=False):
rval = super().to_dict(view=view, value_mapper=value_mapper)
base_view = view if view != "step_states" else "collection"
rval = super().to_dict(view=base_view, value_mapper=value_mapper)
if rval["state"] is None:
# bugs could result in no state being set
rval["state"] = self.states.FAILED
if view == "element":
if view in ["element", "step_states"]:
steps = []
for step in self.steps:
if step_details:
Expand All @@ -8678,7 +8679,7 @@ def to_dict(self, view="collection", value_mapper=None, step_details=False, lega
else:
steps.append(v)
rval["steps"] = steps

if view == "element":
inputs = {}
for input_item_association in self.input_datasets + self.input_dataset_collections:
if input_item_association.history_content_type == "dataset":
Expand Down
14 changes: 13 additions & 1 deletion lib/galaxy/schema/invocation.py
Original file line number Diff line number Diff line change
Expand Up @@ -566,6 +566,10 @@ class WorkflowInvocationCollectionView(Model, WithModelClass):
model_class: INVOCATION_MODEL_CLASS = ModelClassField(INVOCATION_MODEL_CLASS)


class WorkflowInvocationStepStatesView(WorkflowInvocationCollectionView):
steps: List[InvocationStep] = Field(default=..., title="Steps", description="Steps of the workflow invocation.")


class BaseWorkflowInvocationElementView(WorkflowInvocationCollectionView):
inputs: Dict[str, InvocationInput] = Field(
default=..., title="Inputs", description="Input datasets/dataset collections of the workflow invocation."
Expand Down Expand Up @@ -603,7 +607,12 @@ class WorkflowInvocationElementView(BaseWorkflowInvocationElementView):

class WorkflowInvocationResponse(RootModel):
root: Annotated[
Union[WorkflowInvocationElementView, LegacyWorkflowInvocationElementView, WorkflowInvocationCollectionView],
Union[
WorkflowInvocationElementView,
LegacyWorkflowInvocationElementView,
WorkflowInvocationCollectionView,
WorkflowInvocationStepStatesView,
],
Field(union_mode="left_to_right"),
]

Expand All @@ -614,6 +623,8 @@ def from_dict(as_dict: Dict[str, Any], view: "InvocationSerializationView", lega
# performant, and will likely yield clearer error messages.
if view == InvocationSerializationView.collection:
root = WorkflowInvocationCollectionView(**as_dict)
elif view == InvocationSerializationView.step_states:
root = WorkflowInvocationStepStatesView(**as_dict)
elif legacy_job_state:
root = LegacyWorkflowInvocationElementView(**as_dict)
else:
Expand Down Expand Up @@ -658,6 +669,7 @@ class CreateInvocationFromStore(StoreContentSource):
class InvocationSerializationView(str, Enum):
element = "element"
collection = "collection"
step_states = "step_states" # collection + steps - for monitoring, lighter than element


class InvocationSerializationParams(BaseModel):
Expand Down
3 changes: 2 additions & 1 deletion lib/galaxy/webapps/galaxy/api/workflows.py
Original file line number Diff line number Diff line change
Expand Up @@ -1405,11 +1405,12 @@ def show_invocation(
self,
invocation_id: InvocationIDPathParam,
trans: ProvidesUserContext = DependsOnTrans,
view: SerializationViewQueryParam = None,
step_details: StepDetailQueryParam = False,
legacy_job_state: LegacyJobStateQueryParam = False,
) -> WorkflowInvocationResponse:
serialization_params = InvocationSerializationParams(
step_details=step_details, legacy_job_state=legacy_job_state
view=view, step_details=step_details, legacy_job_state=legacy_job_state
)
return self.invocations_service.show(trans, invocation_id, serialization_params, eager=True)

Expand Down

0 comments on commit ec403cc

Please sign in to comment.