Skip to content

Commit e14777f

Browse files
authored
Add all messages list to ServicePulse (#2275)
1 parent dd565ef commit e14777f

File tree

14 files changed

+483
-28
lines changed

14 files changed

+483
-28
lines changed

src/Frontend/env.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ declare global {
1010
service_control_url: string;
1111
monitoring_urls: string[];
1212
showPendingRetry: boolean;
13+
showAllMessages: boolean;
1314
};
1415
}
1516
}

src/Frontend/src/components/PageHeader.vue

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,15 @@ import EventsMenuItem from "@/components/events/EventsMenuItem.vue";
1212
import DashboardMenuItem from "@/components/dashboard/DashboardMenuItem.vue";
1313
import FeedbackButton from "@/components/FeedbackButton.vue";
1414
import ThroughputMenuItem from "@/views/throughputreport/ThroughputMenuItem.vue";
15+
import AuditMenuItem from "./audit/AuditMenuItem.vue";
1516
1617
// prettier-ignore
1718
const menuItems = computed(
1819
() => [
1920
DashboardMenuItem,
2021
HeartbeatsMenuItem,
2122
...(useIsMonitoringEnabled() ? [MonitoringMenuItem] : []),
23+
...(window.defaultConfig.showAllMessages ? [AuditMenuItem] : []),
2224
FailedMessagesMenuItem,
2325
CustomChecksMenuItem,
2426
EventsMenuItem,
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
<script setup lang="ts">
2+
import { ref } from "vue";
3+
import OnOffSwitch from "./OnOffSwitch.vue";
4+
5+
const props = defineProps<{
6+
id: string;
7+
initialTimeout?: number;
8+
onManualRefresh: () => void;
9+
}>();
10+
11+
const emit = defineEmits<{ change: [newValue: number | null]; manualRefresh: [] }>();
12+
13+
const autoRefresh = ref(props.initialTimeout != null);
14+
const refreshTimeout = ref(props.initialTimeout ?? 5);
15+
16+
function toggleRefresh() {
17+
autoRefresh.value = !autoRefresh.value;
18+
updateTimeout();
19+
}
20+
21+
function updateTimeout() {
22+
validateTimeout();
23+
emit("change", autoRefresh.value ? refreshTimeout.value * 1000 : null);
24+
}
25+
26+
function validateTimeout() {
27+
refreshTimeout.value = Math.max(1, Math.min(600, refreshTimeout.value));
28+
}
29+
</script>
30+
31+
<template>
32+
<div class="refresh-config">
33+
<button class="fa" title="refresh" @click="() => emit('manualRefresh')">
34+
<i class="fa fa-lg fa-refresh" />
35+
</button>
36+
<span>|</span>
37+
<label>Auto-Refresh:</label>
38+
<div>
39+
<OnOffSwitch :id="id" @toggle="toggleRefresh" :value="autoRefresh" />
40+
</div>
41+
<input type="number" v-model="refreshTimeout" min="1" max="600" v-on:change="updateTimeout" />
42+
<span class="unit">s</span>
43+
</div>
44+
</template>
45+
46+
<style scoped>
47+
.refresh-config {
48+
display: flex;
49+
align-items: center;
50+
gap: 0.5em;
51+
}
52+
53+
.refresh-config .unit {
54+
margin-left: -0.45em;
55+
}
56+
57+
.refresh-config label {
58+
margin: 0;
59+
}
60+
61+
.refresh-config input {
62+
width: 3.5em;
63+
}
64+
65+
.refresh-config button {
66+
background: none;
67+
border: none;
68+
width: 2em;
69+
}
70+
71+
.refresh-config button .fa {
72+
transition: all 0.15s ease-in-out;
73+
transition: rotate 0.05s ease-in-out;
74+
transform-origin: center;
75+
}
76+
77+
.refresh-config button:hover .fa {
78+
color: #00a3c4;
79+
transform: scale(1.1);
80+
}
81+
82+
.refresh-config button:active .fa {
83+
transform: rotate(25deg);
84+
text-shadow: #929e9e 0.25px 0.25px;
85+
}
86+
</style>
Lines changed: 224 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,224 @@
1+
<script setup lang="ts">
2+
import routeLinks from "@/router/routeLinks";
3+
import { ColumnNames, useAuditStore } from "@/stores/AuditStore";
4+
import { storeToRefs } from "pinia";
5+
import { useRoute } from "vue-router";
6+
import SortableColumn from "../SortableColumn.vue";
7+
import { MessageStatus } from "@/resources/Message";
8+
import moment from "moment";
9+
import { useFormatTime } from "@/composables/formatter";
10+
import RefreshConfig from "../RefreshConfig.vue";
11+
import ItemsPerPage from "../ItemsPerPage.vue";
12+
import PaginationStrip from "../PaginationStrip.vue";
13+
14+
const route = useRoute();
15+
const store = useAuditStore();
16+
const { messages, sortByInstances, itemsPerPage, selectedPage, totalCount } = storeToRefs(store);
17+
18+
function statusToName(messageStatus: MessageStatus) {
19+
switch (messageStatus) {
20+
case MessageStatus.Successful:
21+
return "Successful";
22+
case MessageStatus.ResolvedSuccessfully:
23+
return "Successful after retries";
24+
case MessageStatus.Failed:
25+
return "Failed";
26+
case MessageStatus.ArchivedFailure:
27+
return "Failed message deleted";
28+
case MessageStatus.RepeatedFailure:
29+
return "Repeated Failures";
30+
case MessageStatus.RetryIssued:
31+
return "Retry requested";
32+
}
33+
}
34+
35+
function statusToIcon(messageStatus: MessageStatus) {
36+
switch (messageStatus) {
37+
case MessageStatus.Successful:
38+
return "fa successful";
39+
case MessageStatus.ResolvedSuccessfully:
40+
return "fa resolved-successfully";
41+
case MessageStatus.Failed:
42+
return "fa failed";
43+
case MessageStatus.ArchivedFailure:
44+
return "fa archived";
45+
case MessageStatus.RepeatedFailure:
46+
return "fa repeated-failure";
47+
case MessageStatus.RetryIssued:
48+
return "fa retry-issued";
49+
}
50+
}
51+
52+
function friendlyTypeName(messageType: string) {
53+
if (messageType == null) return null;
54+
55+
const typeClass = messageType.split(",")[0];
56+
const typeName = typeClass.split(".").reverse()[0];
57+
return typeName.replace(/\+/g, ".");
58+
}
59+
60+
function formatDotNetTimespan(timespan: string) {
61+
//assuming if we have days in the timespan then something is very, very wrong
62+
const [hh, mm, ss] = timespan.split(":");
63+
const time = useFormatTime(((parseInt(hh) * 60 + parseInt(mm)) * 60 + parseFloat(ss)) * 1000);
64+
return `${time.value} ${time.unit}`;
65+
}
66+
</script>
67+
68+
<template>
69+
<section class="section-table" role="table" aria-label="endpoint-instances">
70+
<div class="header">
71+
<RefreshConfig id="auditListRefresh" @change="store.updateRefreshTimer" @manual-refresh="store.refresh" />
72+
<!--Table headings-->
73+
<div role="row" aria-label="column-headers" class="row table-head-row" :style="{ borderTop: 0 }">
74+
<div role="columnheader" :aria-label="ColumnNames.Status" class="status">
75+
<SortableColumn :sort-by="ColumnNames.Status" v-model="sortByInstances" :default-ascending="true">Status</SortableColumn>
76+
</div>
77+
<div role="columnheader" :aria-label="ColumnNames.MessageId" class="col-3">
78+
<SortableColumn :sort-by="ColumnNames.MessageId" v-model="sortByInstances" :default-ascending="true">Message Id</SortableColumn>
79+
</div>
80+
<div role="columnheader" :aria-label="ColumnNames.MessageType" class="col-3">
81+
<SortableColumn :sort-by="ColumnNames.MessageType" v-model="sortByInstances" :default-ascending="true">Type</SortableColumn>
82+
</div>
83+
<div role="columnheader" :aria-label="ColumnNames.TimeSent" class="col-2">
84+
<SortableColumn :sort-by="ColumnNames.TimeSent" v-model="sortByInstances">Time Sent</SortableColumn>
85+
</div>
86+
<div role="columnheader" :aria-label="ColumnNames.ProcessingTime" class="col-2">
87+
<SortableColumn :sort-by="ColumnNames.ProcessingTime" v-model="sortByInstances">Processing Time</SortableColumn>
88+
</div>
89+
</div>
90+
</div>
91+
<!--Table rows-->
92+
<!--NOTE: currently the DataView pages on the client only: we need to make it server data aware (i.e. the total will be the count from the server, not the length of the data we have locally)-->
93+
<div class="messages" role="rowgroup" aria-label="messages">
94+
<div role="row" :aria-label="message.message_id" class="row grid-row" v-for="message in messages" :key="message.id">
95+
<div role="cell" aria-label="status" class="status" :title="statusToName(message.status)">
96+
<div class="status-icon" :class="statusToIcon(message.status)"></div>
97+
</div>
98+
<div role="cell" aria-label="message-id" class="col-3 message-id">
99+
<div class="box-header">
100+
<tippy :aria-label="message.message_id" :delay="[700, 0]" class="no-side-padding lead righ-side-ellipsis endpoint-details-link">
101+
<template #content>
102+
<p :style="{ overflowWrap: 'break-word' }">{{ message.message_id }}</p>
103+
</template>
104+
<RouterLink class="hackToPreventSafariFromShowingTooltip" aria-label="details-link" :to="{ path: routeLinks.messages.message.link(message.id), query: { back: route.path } }">
105+
{{ message.message_id }}
106+
</RouterLink>
107+
</tippy>
108+
</div>
109+
</div>
110+
<div role="cell" aria-label="message-type" class="col-3 message-type">
111+
{{ friendlyTypeName(message.message_type) }}
112+
</div>
113+
<div role="cell" aria-label="time-sent" class="col-2 time-sent">
114+
{{ moment(message.time_sent).local().format("LLLL") }}
115+
</div>
116+
<div role="cell" aria-label="processing-time" class="col-2 processing-time">
117+
{{ formatDotNetTimespan(message.processing_time) }}
118+
</div>
119+
</div>
120+
</div>
121+
<div class="row">
122+
<ItemsPerPage v-model="itemsPerPage" />
123+
<PaginationStrip v-model="selectedPage" :totalCount="totalCount" :itemsPerPage="itemsPerPage" />
124+
</div>
125+
</section>
126+
</template>
127+
128+
<style scoped>
129+
@import "../list.css";
130+
131+
.hackToPreventSafariFromShowingTooltip::after {
132+
content: "";
133+
display: block;
134+
}
135+
136+
.section-table {
137+
overflow: auto;
138+
flex: 1;
139+
display: flex;
140+
flex-direction: column;
141+
}
142+
143+
.messages {
144+
flex: 1;
145+
overflow: auto;
146+
}
147+
148+
.status {
149+
width: 5em;
150+
text-align: center;
151+
}
152+
153+
.status-icon {
154+
color: white;
155+
border-radius: 0.75em;
156+
width: 1.2em;
157+
height: 1.2em;
158+
}
159+
160+
.status-icon::before {
161+
vertical-align: middle;
162+
font-size: 0.85em;
163+
}
164+
165+
.successful {
166+
background: #6cc63f;
167+
}
168+
.successful::before {
169+
content: "\f00c";
170+
}
171+
172+
.resolved-successfully {
173+
background: #3f881b;
174+
}
175+
.resolved-successfully::before {
176+
content: "\f01e";
177+
}
178+
179+
.failed {
180+
background: #c63f3f;
181+
}
182+
.failed::before {
183+
content: "\f00d";
184+
}
185+
186+
.archived {
187+
background: #000000;
188+
}
189+
.archived::before {
190+
content: "\f187";
191+
font-size: 0.85em;
192+
}
193+
194+
.repeated-failure {
195+
background: #c63f3f;
196+
}
197+
.repeated-failure::before {
198+
content: "\f00d\f00d";
199+
font-size: 0.6em;
200+
}
201+
202+
.retry-issued {
203+
background: #cccccc;
204+
color: #000000;
205+
}
206+
.retry-issued::before {
207+
content: "\f01e";
208+
}
209+
210+
.grid-row {
211+
display: flex;
212+
position: relative;
213+
border-top: 1px solid #eee;
214+
border-right: 1px solid #fff;
215+
border-bottom: 1px solid #eee;
216+
border-left: 1px solid #fff;
217+
background-color: #fff;
218+
margin: 0;
219+
}
220+
221+
.grid-row:nth-child(even) {
222+
background-color: #eee;
223+
}
224+
</style>
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<script setup lang="ts">
2+
import { RouterLink } from "vue-router";
3+
import routeLinks from "@/router/routeLinks";
4+
</script>
5+
6+
<template>
7+
<RouterLink :to="routeLinks.messages.root">
8+
<i class="fa fa-envelope icon-white" title="All Messages"></i>
9+
<span class="navbar-label">All Messages</span>
10+
</RouterLink>
11+
</template>
12+
13+
<style scoped>
14+
@import "@/assets/navbar.css";
15+
@import "@/assets/header-menu-item.css";
16+
</style>

src/Frontend/src/components/failedmessages/MessageRedirectForBackwardsCompatibility.vue

Lines changed: 0 additions & 16 deletions
This file was deleted.

0 commit comments

Comments
 (0)