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

Refactor of User-Reported Dataset Error Emails #19401

Draft
wants to merge 1 commit into
base: dev
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
88 changes: 88 additions & 0 deletions client/src/components/Collections/common/UserReportingError.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
<script setup lang="ts">
import { library } from "@fortawesome/fontawesome-svg-core";
import { faBug } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome";
import { BAlert, BButton } from "bootstrap-vue";
import { computed, ref } from "vue";

import { submitReport } from "@/components/Collections/common/reporting";
import { useMarkdown } from "@/composables/markdown";
import localize from "@/utils/localization";

import FormElement from "@/components/Form/FormElement.vue";

library.add(faBug);

interface Props {
reportableData: object;
reportingEmail: string;
}

const props = defineProps<Props>();
const { renderMarkdown } = useMarkdown({ openLinksInNewPage: true });
const message = ref("");
const errorMessage = ref("");
const resultMessages = ref<string[][]>([]);
const showForm = computed(() => {
const noResult = !resultMessages.value.length;
const hasError = resultMessages.value.some((msg) => msg[1] === "danger");
return noResult || hasError;
});
const FIELD_MESSAGE = {
loginRequired: localize("You must be logged in to send emails."),
dataRequired: localize("You must provide a valid object to send emails."),
};
const fieldMessages = computed(() =>
[!props.reportableData && FIELD_MESSAGE.dataRequired, !props.reportingEmail && FIELD_MESSAGE.loginRequired].filter(
Boolean
)
);

async function handleSubmit(data?: any, email?: string | null) {
if (!data || !email) {
return;
}

const { messages, error } = await submitReport(data, message.value, email);

if (error) {
errorMessage.value = error;
} else {
resultMessages.value = messages;
}
}
</script>

<template>
<div>
<BAlert v-for="(resultMessage, index) in resultMessages" :key="index" :variant="resultMessage[1]" show>
<span v-html="renderMarkdown(resultMessage[0])" />
</BAlert>

<div v-if="showForm" id="data-error-form">
<div>
<span class="mr-2 font-weight-bold">{{ localize("Your email address") }}</span>
<span v-if="props.reportingEmail">{{ props.reportingEmail }}</span>
<span v-else>{{ FIELD_MESSAGE.loginRequired }}</span>
</div>
<div>
<span class="mr-2 font-weight-bold">{{
localize("Please provide detailed information on the activities leading to this issue:")
}}</span>
<span v-if="!props.reportableData">{{ FIELD_MESSAGE.dataRequired }}</span>
</div>
<FormElement v-if="props.reportableData" id="object-error-message" v-model="message" :area="true" />
</div>

<BButton
id="data-error-submit"
v-b-tooltip.hover
:title="fieldMessages.join('\n')"
variant="primary"
class="mt-3"
@click="handleSubmit(props.reportableData, props.reportingEmail)">
<FontAwesomeIcon :icon="faBug" class="mr-1" />
Report
</BButton>
</div>
</template>
34 changes: 34 additions & 0 deletions client/src/components/Collections/common/reporting.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { GalaxyApi } from "@/api";
import { type HDADetailed } from "@/api";
import { errorMessageAsString } from "@/utils/simple-error";

export interface ReportableObject {
id: string;
creating_job: string;
}

export async function submitReport(
reportableData: HDADetailed,
message: string,
email: string
): Promise<{ messages: string[][]; error?: string }> {
try {
const { data, error } = await GalaxyApi().POST("/api/jobs/{job_id}/error", {
params: {
path: { job_id: reportableData.creating_job },
},
body: {
dataset_id: reportableData.id,
message,
email,
},
});

if (error) {
return { messages: [], error: errorMessageAsString(error) };
}
return { messages: data.messages };
} catch (err) {
return { messages: [], error: errorMessageAsString(err) };
}
}
72 changes: 4 additions & 68 deletions client/src/components/DatasetInformation/DatasetError.vue
Original file line number Diff line number Diff line change
@@ -1,21 +1,18 @@
<script setup lang="ts">
import { library } from "@fortawesome/fontawesome-svg-core";
import { faBug } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome";
import { BAlert, BButton, BCard } from "bootstrap-vue";
import { BAlert, BCard } from "bootstrap-vue";
import { storeToRefs } from "pinia";
import { computed, onMounted, ref } from "vue";
import { onMounted, ref } from "vue";

import { GalaxyApi, type HDADetailed } from "@/api";
import { fetchDatasetDetails } from "@/api/datasets";
import { type JobDetails, type JobInputSummary } from "@/api/jobs";
import { useMarkdown } from "@/composables/markdown";
import { useUserStore } from "@/stores/userStore";
import localize from "@/utils/localization";
import { errorMessageAsString } from "@/utils/simple-error";

import UserReportingError from "../Collections/common/UserReportingError.vue";
import DatasetErrorDetails from "@/components/DatasetInformation/DatasetErrorDetails.vue";
import FormElement from "@/components/Form/FormElement.vue";
import GalaxyWizard from "@/components/GalaxyWizard.vue";

library.add(faBug);
Expand All @@ -25,28 +22,15 @@ interface Props {
}

const props = defineProps<Props>();

const userStore = useUserStore();
const { currentUser } = storeToRefs(userStore);

const { renderMarkdown } = useMarkdown({ openLinksInNewPage: true });

const message = ref("");
const jobLoading = ref(true);
const errorMessage = ref("");
const datasetLoading = ref(false);
const jobDetails = ref<JobDetails>();
const jobProblems = ref<JobInputSummary>();
const resultMessages = ref<string[][]>([]);
const dataset = ref<HDADetailed>();

const showForm = computed(() => {
const noResult = !resultMessages.value.length;
const hasError = resultMessages.value.some((msg) => msg[1] === "danger");

return noResult || hasError;
});

async function getDatasetDetails() {
datasetLoading.value = true;
try {
Expand Down Expand Up @@ -93,31 +77,6 @@ async function getJobProblems(jobId: string) {
jobProblems.value = data;
}

async function submit(dataset?: HDADetailed, userEmailJob?: string | null) {
if (!dataset) {
errorMessage.value = "No dataset found.";
return;
}

const { data, error } = await GalaxyApi().POST("/api/jobs/{job_id}/error", {
params: {
path: { job_id: dataset.creating_job },
},
body: {
dataset_id: dataset.id,
message: message.value,
email: userEmailJob,
},
});

if (error) {
errorMessage.value = errorMessageAsString(error);
return;
}

resultMessages.value = data.messages;
}

function onMissingJobId() {
errorMessage.value = "No job ID found for this dataset.";
}
Expand Down Expand Up @@ -205,30 +164,7 @@ onMounted(async () => {
</p>

<h4 class="mb-3 h-md">Issue Report</h4>
<BAlert v-for="(resultMessage, index) in resultMessages" :key="index" :variant="resultMessage[1]" show>
<span v-html="renderMarkdown(resultMessage[0])" />
</BAlert>

<div v-if="showForm" id="dataset-error-form">
<span class="mr-2 font-weight-bold">{{ localize("Your email address") }}</span>
<span v-if="currentUser?.email">{{ currentUser.email }}</span>
<span v-else>{{ localize("You must be logged in to receive emails") }}</span>

<FormElement
id="dataset-error-message"
v-model="message"
:area="true"
title="Please provide detailed information on the activities leading to this issue:" />

<BButton
id="dataset-error-submit"
variant="primary"
class="mt-3"
@click="submit(dataset, jobDetails?.user_email)">
<FontAwesomeIcon :icon="faBug" class="mr-1" />
Report
</BButton>
</div>
<UserReportingError :reportable-data="dataset" :reporting-email="currentUser?.email" />
</div>
</div>
</template>
87 changes: 87 additions & 0 deletions lib/galaxy/config/templates/mail/error-report-dataset.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
<html>
<body>
<h1>Galaxy Tool Error Report</h1>
<span class="sub">
<i>from </i>
<span style="font-family: monospace;">
<a href="{{ host }}">{{ host }}</a>
</span>
</span>
<h3>Error Localization</h3>
<table style="margin:1em">
<tbody>
<tr>
<td>Dataset</td>
<td>
<a href="{{ hda_show_params_link }}">{{ dataset_id }} ({{ dataset_id_encoded }})</a>
</td>
</tr>
<tr style="background-color: #f2f2f2">
<td>History</td>
<td>
<a href="{{ history_view_link }}">{{ history_id }} ({{ history_id_encoded }})</a>
</td>
</tr>
<tr>
<td>Failed Job</td>
<td>{{ hid }}: {{ history_item_name }} ({{ hda_id_encoded }})</td>
</tr>
</tbody>
</table>
<h3>User Provided Information</h3>
The user
<span style="font-family: monospace;">{{ email_str }}</span> provided the following information:
<pre style="white-space: pre-wrap;background: #eeeeee;border:1px solid black;padding:1em;">
{{ message }}
</pre>
<h3>Detailed Job Information</h3>
Job environment and execution information is available at the job
<a href="{{ hda_show_params_link }}">info page</a>.
<table style="margin:1em">
<tbody>
<tr>
<td>Job ID</td>
<td>{{ job_id }} ({{ job_id_encoded }})</td>
</tr>
<tr style="background-color: #f2f2f2">
<td>Tool ID</td>
<td>{{ job_tool_id }}</td>
</tr>
<tr>
<td>Tool Version</td>
<td>{{ tool_version }}</td>
</tr>
<tr style="background-color: #f2f2f2">
<td>Job PID or DRM id</td>
<td>{{ job_runner_external_id }}</td>
</tr>
<tr>
<td>Job Tool Version</td>
<td>{{ job_tool_version }}</td>
</tr>
</tbody>
</table>
<h3>Job Execution and Failure Information</h3>
<h4>Command Line</h4>
<pre style="white-space: pre-wrap;background: #eeeeee;border:1px solid black;padding:1em;">
{{ job_command_line }}
</pre>
<h4>stderr</h4>
<pre style="white-space: pre-wrap;background: #eeeeee;border:1px solid black;padding:1em;">
{{ job_stderr }}
</pre>
<h4>stdout</h4>
<pre style="white-space: pre-wrap;background: #eeeeee;border:1px solid black;padding:1em;">
{{ job_stdout }}
</pre>
<h4>Job Information</h4>
<pre style="white-space: pre-wrap;background: #eeeeee;border:1px solid black;padding:1em;">
{{ job_info }}
</pre>
<h4>Job Traceback</h4>
<pre style="white-space: pre-wrap;background: #eeeeee;border:1px solid black;padding:1em;">
{{ job_traceback }}
</pre>
This is an automated message. Do not reply to this address.
</body>
</html>
44 changes: 44 additions & 0 deletions lib/galaxy/config/templates/mail/error-report-dataset.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
GALAXY TOOL ERROR REPORT
------------------------

This error report was sent from the Galaxy instance hosted on the server
"{{ host }}"
-----------------------------------------------------------------------------
This is in reference to dataset id {{ dataset_id }} ({{ dataset_id_encoded }}) from history id {{ history_id }} ({{ history_id_encoded }})
-----------------------------------------------------------------------------
You should be able to view the history containing the related history item ({{ hda_id_encoded }})

{{ hid }}: {{ history_item_name }}

by logging in as a Galaxy admin user to the Galaxy instance referenced above
and pointing your browser to the following link.

{{ history_view_link }}
-----------------------------------------------------------------------------
The user {{ email_str }} provided the following information:

{{ message }}
-----------------------------------------------------------------------------
info url: {{ hda_show_params_link }}
job id: {{ job_id }} ({{ job_id_encoded }})
tool id: {{ job_tool_id }}
tool version: {{ tool_version }}
job pid or drm id: {{ job_runner_external_id }}
job tool version: {{ job_tool_version }}
-----------------------------------------------------------------------------
job command line:
{{ job_command_line }}
-----------------------------------------------------------------------------
job stderr:
{{ job_stderr }}
-----------------------------------------------------------------------------
job stdout:
{{ job_stdout }}
-----------------------------------------------------------------------------
job info:
{{ job_info }}
-----------------------------------------------------------------------------
job traceback:
{{ job_traceback }}
-----------------------------------------------------------------------------
(This is an automated message).
Loading