Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 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
5 changes: 3 additions & 2 deletions src/Frontend/src/components/EventLogItem.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<script setup lang="ts">
import { useRouter } from "vue-router";
import { useRoute, useRouter } from "vue-router";
import TimeSince from "../components/TimeSince.vue";
import type EventLogItem from "@/resources/EventLogItem";
// eslint-disable-next-line no-duplicate-imports
Expand All @@ -8,6 +8,7 @@ import routeLinks from "@/router/routeLinks";

defineProps<{ eventLogItem: EventLogItem }>();
const router = useRouter();
const route = useRoute();

function navigateToEvent(eventLogItem: EventLogItem) {
switch (eventLogItem.category) {
Expand All @@ -26,7 +27,7 @@ function navigateToEvent(eventLogItem: EventLogItem) {
case "MessageFailures":
if (eventLogItem.related_to?.length && eventLogItem.related_to[0].search("message") > 0) {
const messageId = eventLogItem.related_to[0].substring(9);
router.push(routeLinks.failedMessage.message.link(messageId));
router.push({ path: routeLinks.messages.message.link(messageId), query: { back: route.path } });
} else {
router.push(routeLinks.failedMessage.root);
}
Expand Down
5 changes: 3 additions & 2 deletions src/Frontend/src/components/failedmessages/MessageList.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<script setup lang="ts">
import { useRouter } from "vue-router";
import { useRoute, useRouter } from "vue-router";
import TimeSince from "../TimeSince.vue";
import NoData from "../NoData.vue";
import routeLinks from "@/router/routeLinks";
Expand All @@ -17,6 +17,7 @@ export interface IMessageList {

let lastLabelClickedIndex: number | undefined;
const router = useRouter();
const route = useRoute();
const emit = defineEmits(["retryRequested"]);
const props = withDefaults(
defineProps<{
Expand Down Expand Up @@ -77,7 +78,7 @@ function labelClicked($event: MouseEvent, index: number) {
}

function navigateToMessage(messageId: string) {
router.push(routeLinks.failedMessage.message.link(messageId));
router.push({ path: routeLinks.messages.message.link(messageId), query: { back: route.path } });
}

defineExpose<IMessageList>({
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<script setup lang="ts">
Copy link
Contributor

Choose a reason for hiding this comment

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

from memory, redirect can be configured in the router config directly, rather than having to have a separate component

import { onMounted } from "vue";
import { useRoute, useRouter } from "vue-router";
import routeLinks from "@/router/routeLinks";
const route = useRoute();
const router = useRouter();
const id = route.params.id;
onMounted(async () => {
await router.push({ path: routeLinks.messages.message.link(id.toString()), query: { back: routeLinks.failedMessage.failedMessages.link } });
});
</script>
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
<script setup lang="ts">
import { onMounted, ref } from "vue";
import { useTypedFetchFromServiceControl } from "../../composables/serviceServiceControlUrls";
import { useTypedFetchFromServiceControl } from "@/composables/serviceServiceControlUrls";
import { type DefaultEdge, MarkerType, useVueFlow, VueFlow, type Styles, type Node } from "@vue-flow/core";
import TimeSince from "../TimeSince.vue";
import routeLinks from "@/router/routeLinks";
import Message from "@/resources/Message";
import { NServiceBusHeaders } from "@/resources/Header";
import { useRoute } from "vue-router";

const props = defineProps<{
conversationId?: string;
Expand Down Expand Up @@ -42,6 +43,8 @@ interface MappedMessage {
const nodeSpacingX = 300;
const nodeSpacingY = 200;

const route = useRoute();

async function getConversation(conversationId: string) {
const [, data] = await useTypedFetchFromServiceControl<Message[]>(`conversations/${conversationId}`);
return data;
Expand Down Expand Up @@ -206,7 +209,7 @@ function typeIcon(type: MessageType) {
<i class="fa" :class="typeIcon(nodeProps.data.type)" :title="nodeProps.data.type" />
<div class="lead righ-side-ellipsis" :title="nodeProps.data.nodeName">
<strong>
<RouterLink v-if="nodeProps.data.isError" :to="routeLinks.failedMessage.message.link(nodeProps.data.id)">{{ nodeProps.data.nodeName }}</RouterLink>
<RouterLink v-if="nodeProps.data.isError" :to="{ path: routeLinks.messages.message.link(nodeProps.data.id), query: { back: route.path } }">{{ nodeProps.data.nodeName }}</RouterLink>
<span v-else>{{ nodeProps.data.nodeName }}</span>
</strong>
</div>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
<script setup lang="ts">
import { computed, onMounted, onUnmounted, ref, watch } from "vue";
import { RouterLink, useRoute } from "vue-router";
import { serviceControlUrl, useFetchFromServiceControl, useTypedFetchFromServiceControl } from "../../composables/serviceServiceControlUrls";
import { useArchiveMessage, useRetryMessages, useUnarchiveMessage } from "../../composables/serviceFailedMessage";
import { useDownloadFileFromString } from "../../composables/fileDownloadCreator";
import { useShowToast } from "../../composables/toast";
import { RouterLink, useRoute, useRouter } from "vue-router";
import { serviceControlUrl, useFetchFromServiceControl, useTypedFetchFromServiceControl } from "@/composables/serviceServiceControlUrls";
import { useArchiveMessage, useRetryMessages, useUnarchiveMessage } from "@/composables/serviceFailedMessage";
import { useDownloadFileFromString } from "@/composables/fileDownloadCreator";
import { useShowToast } from "@/composables/toast";
import NoData from "../NoData.vue";
import TimeSince from "../TimeSince.vue";
import moment from "moment";
import ConfirmDialog from "../ConfirmDialog.vue";
import FlowDiagram from "./FlowDiagram.vue";
import EditRetryDialog from "./EditRetryDialog.vue";
import EditRetryDialog from "../failedmessages/EditRetryDialog.vue";
import routeLinks from "@/router/routeLinks";
import { EditAndRetryConfig } from "@/resources/Configuration";
import { TYPE } from "vue-toastification";
Expand All @@ -28,6 +28,7 @@ const route = useRoute();
const failedMessage = ref<ExtendedFailedMessage | FailedMessageError>();
const editAndRetryConfiguration = ref<EditAndRetryConfig>();
const backLink = ref<string>(routeLinks.failedMessage.failedMessages.link);
const id = computed(() => route.params.id as string);
watch(id, async () => await loadFailedMessage());
Expand All @@ -40,39 +41,34 @@ const configuration = useConfiguration();
const isMassTransitConnected = useIsMassTransitConnected();
async function loadFailedMessage() {
try {
const response = await useFetchFromServiceControl("errors/last/" + id.value);
if (response.status === 404) {
failedMessage.value = { notFound: true } as FailedMessageError;
return;
} else if (!response.ok) {
failedMessage.value = { error: true } as FailedMessageError;
return;
}
const message = (await response.json()) as ExtendedFailedMessage;
message.archived = message.status === FailedMessageStatus.Archived;
message.resolved = message.status === FailedMessageStatus.Resolved;
message.retried = message.status === FailedMessageStatus.RetryIssued;
message.error_retention_period = moment.duration(configuration.value?.data_retention.error_retention_period).asHours();
message.isEditAndRetryEnabled = editAndRetryConfiguration.value?.enabled ?? false;
// Maintain the mutations of the message in memory until the api returns a newer modified message
if (failedMessage.value && !isError(failedMessage.value) && failedMessage.value.last_modified === message.last_modified) {
message.retried = failedMessage.value?.retried;
message.archiving = failedMessage.value?.archiving;
message.restoring = failedMessage.value?.restoring;
} else {
message.archiving = false;
message.restoring = false;
}
updateMessageDeleteDate(message);
await downloadHeadersAndBody(message);
failedMessage.value = message;
} catch (err) {
Copy link
Member Author

Choose a reason for hiding this comment

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

Removed this catch; it was just swalling exceptions

console.log(err);
const response = await useFetchFromServiceControl(`errors/last/${id.value}`);
if (response.status === 404) {
failedMessage.value = { notFound: true } as FailedMessageError;
return;
} else if (!response.ok) {
failedMessage.value = { error: true } as FailedMessageError;
return;
}
const message = (await response.json()) as ExtendedFailedMessage;
message.archived = message.status === FailedMessageStatus.Archived;
message.resolved = message.status === FailedMessageStatus.Resolved;
message.retried = message.status === FailedMessageStatus.RetryIssued;
message.error_retention_period = moment.duration(configuration.value?.data_retention.error_retention_period).asHours();
message.isEditAndRetryEnabled = editAndRetryConfiguration.value?.enabled ?? false;
// Maintain the mutations of the message in memory until the api returns a newer modified message
if (failedMessage.value && !isError(failedMessage.value) && failedMessage.value.last_modified === message.last_modified) {
message.retried = failedMessage.value?.retried;
message.archiving = failedMessage.value?.archiving;
message.restoring = failedMessage.value?.restoring;
} else {
message.archiving = false;
message.restoring = false;
}
updateMessageDeleteDate(message);
await downloadHeadersAndBody(message);
failedMessage.value = message;
}
async function getEditAndRetryConfig() {
Expand Down Expand Up @@ -361,11 +357,15 @@ function changeRefreshInterval(milliseconds: number) {
}
onMounted(async () => {
const back = useRouter().currentRoute.value.query.back as string;
if (back) {
backLink.value = back;
}
togglePanel(1);
await getEditAndRetryConfig();
startRefreshInterval();
loadFailedMessage();
await loadFailedMessage();
});
onUnmounted(() => {
Expand All @@ -386,6 +386,7 @@ onUnmounted(() => {
<div v-if="!isError(failedMessage)">
<div class="row">
<div class="col-sm-12 no-side-padding">
<RouterLink :to="backLink"><i class="fa fa-chevron-left"></i> Back</RouterLink>
<div class="active break group-title">
<h1 class="message-type-title">{{ failedMessage.message_type }}</h1>
</div>
Expand All @@ -403,7 +404,9 @@ onUnmounted(() => {
{{ failedMessage.number_of_processing_attempts - 1 }} Retry Failures
</span>
<span v-if="failedMessage.edited" v-tippy="`Message was edited`" class="label sidebar-label label-info metadata-label">Edited</span>
<span v-if="failedMessage.edited" class="metadata metadata-link"><i class="fa fa-history"></i> <RouterLink :to="routeLinks.failedMessage.message.link(failedMessage.edit_of)">View previous version</RouterLink></span>
<span v-if="failedMessage.edited" class="metadata metadata-link"
><i class="fa fa-history"></i> <RouterLink :to="{ path: routeLinks.messages.message.link(failedMessage.edit_of), query: { back: route.path } }">View previous version</RouterLink></span
>
<span v-if="failedMessage.time_of_failure" class="metadata"><i class="fa fa-clock-o"></i> Failed: <time-since :date-utc="failedMessage.time_of_failure"></time-since></span>
<span class="metadata"><i class="fa pa-endpoint"></i> Endpoint: {{ failedMessage.receiving_endpoint.name }}</span>
<span class="metadata"><i class="fa fa-laptop"></i> Machine: {{ failedMessage.receiving_endpoint.host }}</span>
Expand All @@ -426,7 +429,7 @@ onUnmounted(() => {
<i class="fa fa-pencil"></i> Edit & retry
</button>
<button v-if="!isMassTransitConnected" type="button" class="btn btn-default" @click="debugInServiceInsight()" title="Browse this message in ServiceInsight, if installed">
<img src="@/assets/si-icon.svg" /> View in ServiceInsight
<img src="@/assets/si-icon.svg" alt="ServiceInsight logo" /> View in ServiceInsight
</button>
<button type="button" class="btn btn-default" @click="exportMessage()"><i class="fa fa-download"></i> Export message</button>
</div>
Expand All @@ -440,7 +443,6 @@ onUnmounted(() => {
<h5 :class="{ active: panel === 3 }" class="nav-item" @click="togglePanel(3)"><a href="javascript:void(0)">Message body</a></h5>
<h5 v-if="!isMassTransitConnected" :class="{ active: panel === 4 }" class="nav-item" @click="togglePanel(4)"><a href="javascript:void(0)">Flow Diagram</a></h5>
</div>
<pre v-if="panel === 0">{{ failedMessage.exception?.message }}</pre>
<pre v-if="panel === 1">{{ failedMessage.exception?.stack_trace }}</pre>
<table class="table" v-if="panel === 2 && !failedMessage.headersNotFound">
<tbody>
Expand Down Expand Up @@ -473,8 +475,8 @@ onUnmounted(() => {
showDeleteConfirm = false;
archiveMessage();
"
:heading="'Are you sure you want to delete this message?'"
:body="'If you delete, this message won\'t be available for retrying unless it is later restored.'"
heading="Are you sure you want to delete this message?"
body="If you delete, this message won't be available for retrying unless it is later restored."
></ConfirmDialog>

<ConfirmDialog
Expand All @@ -484,8 +486,8 @@ onUnmounted(() => {
showRestoreConfirm = false;
unarchiveMessage();
"
:heading="'Are you sure you want to restore this message?'"
:body="'The restored message will be moved back to the list of failed messages.'"
heading="Are you sure you want to restore this message?"
body="The restored message will be moved back to the list of failed messages."
></ConfirmDialog>

<ConfirmDialog
Expand All @@ -495,8 +497,8 @@ onUnmounted(() => {
showRetryConfirm = false;
retryMessage();
"
:heading="'Are you sure you want to retry this message?'"
:body="'Are you sure you want to retry this message?'"
heading="Are you sure you want to retry this message?"
body="Are you sure you want to retry this message?"
Comment on lines +500 to +501
Copy link
Member Author

Choose a reason for hiding this comment

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

This looks wrong, I'll fix it in a subsquent PR.
image

Copy link
Contributor

Choose a reason for hiding this comment

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

body could be left empty?

></ConfirmDialog>

<EditRetryDialog
Expand Down
2 changes: 1 addition & 1 deletion src/Frontend/src/composables/serviceServiceControlUrls.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ export function useFetchFromServiceControl(suffix: string) {

export async function useTypedFetchFromServiceControl<T>(suffix: string): Promise<[Response, T]> {
const response = await fetch(`${serviceControlUrl.value}${suffix}`);
if (!response?.ok) throw new Error(response?.statusText ?? "No response");
if (!response.ok) throw new Error(response?.statusText ?? "No response");
const data = await response.json();

return [response, data];
Expand Down
7 changes: 6 additions & 1 deletion src/Frontend/src/router/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,10 +96,15 @@ const config: RouteItem[] = [
{
path: routeLinks.failedMessage.message.template,
title: "Message",
component: () => import("@/components/failedmessages/MessageView.vue"),
component: () => import("@/components/failedmessages/MessageRedirectForBackwardsCompatibility.vue"),
Copy link
Contributor

Choose a reason for hiding this comment

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

i think redirects can be done directly rather than through a component

},
],
},
{
path: routeLinks.messages.message.template,
title: "Message",
component: () => import("@/components/messages/MessageView.vue"),
},
{
path: routeLinks.monitoring.root,
component: MonitoringView,
Expand Down
8 changes: 8 additions & 0 deletions src/Frontend/src/router/routeLinks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,13 @@ const failedMessagesLinks = (root: string) => {
};
};

const messagesLinks = (root: string) => {
return {
root,
message: { link: (id: string) => `${root}/${id}`, template: "/messages/:id" },
};
};

const configurationLinks = (root: string) => {
function createLink(template: string) {
return { link: `${root}/${template}`, template: template };
Expand Down Expand Up @@ -96,6 +103,7 @@ const routeLinks = {
failedMessage: failedMessagesLinks("/failed-messages"),
customChecks: "/custom-checks",
events: "/events",
messages: messagesLinks("/messages"),
configuration: configurationLinks("/configuration"),
throughput: throughputLinks("/usage"),
};
Expand Down
9 changes: 3 additions & 6 deletions src/Frontend/src/views/FailedMessagesView.vue
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<script setup lang="ts">
import { RouterLink, RouterView } from "vue-router";
import { licenseStatus } from "../composables/serviceLicense";
import { connectionState, stats } from "../composables/serviceServiceControl";
import { licenseStatus } from "@/composables/serviceLicense";
import { connectionState, stats } from "@/composables/serviceServiceControl";
import LicenseExpired from "../components/LicenseExpired.vue";
import routeLinks from "@/router/routeLinks";
import isRouteSelected from "@/composables/isRouteSelected";
Expand Down Expand Up @@ -31,10 +31,7 @@ const showPendingRetry = window.defaultConfig.showPendingRetry;
</h5>

<!--All Failed Messages-->
<h5
v-if="!licenseStatus.isExpired"
:class="{ active: isRouteSelected(routeLinks.failedMessage.failedMessages.link) || isRouteSelected(routeLinks.failedMessage.message.link(`id`)), disabled: !connectionState.connected && !connectionState.connectedRecently }"
>
<h5 v-if="!licenseStatus.isExpired" :class="{ active: isRouteSelected(routeLinks.failedMessage.failedMessages.link), disabled: !connectionState.connected && !connectionState.connectedRecently }">
<RouterLink :to="routeLinks.failedMessage.failedMessages.link">All Failed Messages </RouterLink>
<span v-if="stats.number_of_failed_messages !== 0" class="badge badge-important">{{ stats.number_of_failed_messages }}</span>
</h5>
Expand Down