Unverified Commit 71fe2ff8 authored by Marius van den Beek's avatar Marius van den Beek Committed by GitHub
Browse files

Merge pull request #20369 from ahmedhamidawan/sync_invocation_error_below_graph

[25.0] Make invocation errors more compact
parents a237775e d9ae5e1c
Loading
Loading
Loading
Loading
+96 −34
Original line number Diff line number Diff line
<script setup lang="ts">
import { faExternalLinkAlt } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome";
import { storeToRefs } from "pinia";
import { computed } from "vue";

import type { InvocationMessage } from "@/api/invocations";
import type { InvocationMessage, WorkflowInvocationElementView } from "@/api/invocations";
import { useWorkflowInstance } from "@/composables/useWorkflowInstance";
import { useWorkflowStateStore } from "@/stores/workflowEditorStateStore";

import GCard from "../Common/GCard.vue";
import WorkflowStepTitle from "./WorkflowStepTitle.vue";
import GenericHistoryItem from "@/components/History/Content/GenericItem.vue";
import JobInformation from "@/components/JobInformation/JobInformation.vue";
import WorkflowInvocationStep from "@/components/WorkflowInvocationState/WorkflowInvocationStep.vue";

type ReasonToLevel = {
    history_deleted: "cancel";
@@ -44,17 +48,18 @@ const levelClasses = {
    error: "errormessage",
};

interface Invocation {
    workflow_id: string;
    state: string;
}

interface InvocationMessageProps {
    invocationMessage: InvocationMessage;
    invocation: Invocation;
    invocation: WorkflowInvocationElementView;
    storeId: string;
}

const props = defineProps<InvocationMessageProps>();

const emit = defineEmits<{
    (e: "view-step", stepId: number): void;
}>();

const levelClass = computed(() => levelClasses[level[props.invocationMessage.reason]]);

const workflow = computed(() => {
@@ -76,6 +81,12 @@ const workflowStep = computed(() => {
    }
    return undefined;
});
const invocationStep = computed(() => {
    if (workflowStep.value) {
        return props.invocation.steps[workflowStep.value.id];
    }
    return undefined;
});

const dependentWorkflowStep = computed(() => {
    if ("dependent_workflow_step_id" in props.invocationMessage && workflow.value) {
@@ -86,6 +97,15 @@ const dependentWorkflowStep = computed(() => {
    }
    return undefined;
});
const dependentInvocationStep = computed(() => {
    if (dependentWorkflowStep.value) {
        return props.invocation.steps[dependentWorkflowStep.value.id];
    }
    return undefined;
});

// This is used to indicate on the step cards whether the step is currently active in the invocation graph.\
const { activeNodeId } = storeToRefs(useWorkflowStateStore(props.storeId));

const jobId = computed(() => "job_id" in props.invocationMessage && props.invocationMessage.job_id);
const HdaId = computed(() => "hda_id" in props.invocationMessage && props.invocationMessage.hda_id);
@@ -175,6 +195,11 @@ const infoString = computed(() => {
        return reason;
    }
});

function openJobInNewTab(jobId: string) {
    const url = `/jobs/${jobId}/view`;
    window.open(url, "_blank");
}
</script>

<template>
@@ -182,31 +207,68 @@ const infoString = computed(() => {
        <div :class="levelClass" style="text-align: center">
            {{ infoString }}
        </div>
        <div v-if="dependentWorkflowStep">
        <div class="invocation-error-grid d-flex flex-wrap">
            <GCard
                v-if="dependentWorkflowStep"
                clickable
                :current="activeNodeId === dependentWorkflowStep.id"
                grid-view
                @click="emit('view-step', dependentWorkflowStep.id)">
                Problem occurred at this step:
            <WorkflowInvocationStep
                :invocation="invocation"
                :workflow="workflow"
                :workflow-step="dependentWorkflowStep"></WorkflowInvocationStep>
        </div>
        <div v-if="workflowStep">
            {{ stepDescription }}
            <WorkflowInvocationStep
                :invocation="invocation"
                :workflow="workflow"
                :workflow-step="workflowStep"></WorkflowInvocationStep>
        </div>
        <div v-if="HdaId">
                <strong>
                    <WorkflowStepTitle
                        :step-index="dependentWorkflowStep.id"
                        :step-label="
                            dependentInvocationStep?.workflow_step_label || `Step ${dependentWorkflowStep.id + 1}`
                        "
                        :step-type="dependentWorkflowStep.type"
                        :step-tool-id="dependentWorkflowStep.tool_id"
                        :step-subworkflow-id="
                            'workflow_id' in dependentWorkflowStep ? dependentWorkflowStep.workflow_id : null
                        " />
                </strong>
            </GCard>
            <GCard
                v-if="workflowStep"
                clickable
                :current="activeNodeId === workflowStep.id"
                grid-view
                @click="emit('view-step', workflowStep.id)">
                {{ stepDescription }}:
                <strong>
                    <WorkflowStepTitle
                        :step-index="workflowStep.id"
                        :step-label="invocationStep?.workflow_step_label || `Step ${workflowStep.id + 1}`"
                        :step-type="workflowStep.type"
                        :step-tool-id="workflowStep.tool_id"
                        :step-subworkflow-id="'workflow_id' in workflowStep ? workflowStep.workflow_id : null" />
                </strong>
            </GCard>
            <GCard v-if="HdaId" grid-view>
                This dataset failed:
                <GenericHistoryItem :item-id="HdaId" item-src="hda" />
        </div>
        <div v-if="HdcaId">
            </GCard>
            <GCard v-if="HdcaId" grid-view>
                This dataset collection failed:
                <GenericHistoryItem :item-id="HdcaId" item-src="hdca" />
        </div>
        <div v-if="jobId">
            This job failed:
            <JobInformation :job_id="jobId" />
            </GCard>
            <GCard v-if="jobId" clickable grid-view @click="openJobInNewTab(jobId)">
                <span>
                    This job failed: <strong> {{ jobId }} </strong>
                </span>
                <i>
                    Click to view job details in a new tab
                    <FontAwesomeIcon :icon="faExternalLinkAlt" />
                </i>
            </GCard>
        </div>
    </div>
</template>

<style scoped lang="scss">
@import "_breakpoints.scss";

.invocation-error-grid {
    container: cards-list / inline-size;
}
</style>
+23 −2
Original line number Diff line number Diff line
<script setup lang="ts">
import { BAlert } from "bootstrap-vue";
import { computed } from "vue";
import { storeToRefs } from "pinia";
import { computed, nextTick, ref } from "vue";

import type { WorkflowInvocationElementView } from "@/api/invocations";
import { useWorkflowInstance } from "@/composables/useWorkflowInstance";
import { useWorkflowStateStore } from "@/stores/workflowEditorStateStore";
import { withPrefix } from "@/utils/redirect";

import ExternalLink from "../ExternalLink.vue";
@@ -23,11 +25,27 @@ const props = defineProps<Props>();

const { workflow, loading, error } = useWorkflowInstance(props.invocation.workflow_id);

const invocationGraph = ref<InstanceType<typeof InvocationGraph> | null>(null);

const uniqueMessages = computed(() => {
    const messages = props.invocation.messages || [];
    const uniqueMessagesSet = new Set(messages.map((message) => JSON.stringify(message)));
    return Array.from(uniqueMessagesSet).map((message) => JSON.parse(message)) as typeof messages;
});

// TODO: Refactor so that `storeId` is only defined here, and then used in all children components/composables.
const storeId = computed(() => `invocation-${props.invocation.id}`);
const stateStore = useWorkflowStateStore(storeId.value);
const { activeNodeId } = storeToRefs(stateStore);

async function showStep(stepId: number) {
    if (invocationGraph.value) {
        activeNodeId.value = stepId;
        await nextTick();
        const graphSelector = invocationGraph.value?.$el?.querySelector(".invocation-graph");
        graphSelector?.scrollIntoView({ behavior: "smooth", block: "start" });
    }
}
</script>

<template>
@@ -44,7 +62,9 @@ const uniqueMessages = computed(() => {
                :key="message.reason"
                class="steps-progress my-1 w-100"
                :invocation-message="message"
                :invocation="invocation">
                :invocation="invocation"
                :store-id="storeId"
                @view-step="showStep">
            </InvocationMessage>
        </div>
        <!-- Once the workflow for the invocation has been loaded, display the graph -->
@@ -56,6 +76,7 @@ const uniqueMessages = computed(() => {
        </BAlert>
        <div v-else-if="workflow && !isSubworkflow">
            <InvocationGraph
                ref="invocationGraph"
                class="mt-1"
                data-description="workflow invocation graph"
                :invocation="invocation"
+2 −2
Original line number Diff line number Diff line
@@ -11,8 +11,8 @@ interface WorkflowInvocationStepTitleProps {
    stepIndex: number;
    stepLabel?: string;
    stepType: string;
    stepToolId?: string;
    stepSubworkflowId?: string;
    stepToolId?: string | null;
    stepSubworkflowId?: string | null;
}

const props = defineProps<WorkflowInvocationStepTitleProps>();