Skip to content

Commit 02f37c6

Browse files
committed
Introducing the messageview store
1 parent dc19641 commit 02f37c6

File tree

4 files changed

+203
-10
lines changed

4 files changed

+203
-10
lines changed

src/Frontend/src/components/messages/MessageView.vue

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -310,10 +310,10 @@ onUnmounted(() => {
310310
<span v-if="failedMessage.number_of_processing_attempts > 1" :title="'This message has already failed ' + failedMessage.number_of_processing_attempts + ' times'" class="label sidebar-label label-important metadata-label">
311311
{{ failedMessage.number_of_processing_attempts - 1 }} Retry Failures
312312
</span>
313-
<span v-if="failedMessage.edited" v-tippy="`Message was edited`" class="label sidebar-label label-info metadata-label">Edited</span>
314-
<span v-if="failedMessage.edited" class="metadata metadata-link">
315-
<i class="fa fa-history"></i> <RouterLink :to="{ path: routeLinks.messages.message.link(failedMessage.edit_of), query: { back: route.path } }">View previous version</RouterLink>
316-
</span>
313+
<template v-if="failedMessage.edited">
314+
<span v-tippy="`Message was edited`" class="label sidebar-label label-info metadata-label">Edited</span>
315+
<span 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>
316+
</template>
317317
<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>
318318
<span class="metadata"><i class="fa pa-endpoint"></i> Endpoint: {{ failedMessage.receiving_endpoint.name }}</span>
319319
<span class="metadata"><i class="fa fa-laptop"></i> Machine: {{ failedMessage.receiving_endpoint.host }}</span>
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { useTypedFetchFromServiceControl } from "@/composables/serviceServiceControlUrls.ts";
2+
import { EditAndRetryConfig } from "@/resources/Configuration.ts";
3+
import { ref } from "vue";
4+
5+
const editAndRetry = ref<EditAndRetryConfig>();
6+
7+
async function populate() {
8+
const [, data] = await useTypedFetchFromServiceControl<EditAndRetryConfig>("edit/config");
9+
10+
editAndRetry.value = data;
11+
}
12+
populate();
13+
14+
export default function useEditAndRetry(): EditAndRetryConfig {
15+
return editAndRetry.value ?? { enabled: false, locked_headers: [], sensitive_headers: [] };
16+
}

src/Frontend/src/resources/Message.ts

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,6 @@ export default interface Message {
2121
body_size: number;
2222
instance_id: string;
2323
}
24-
export interface ExtendedMessage extends Message {
25-
notFound: boolean;
26-
error: boolean;
27-
headersNotFound: boolean;
28-
messageBodyNotFound: boolean;
29-
}
3024

3125
export enum MessageStatus {
3226
Failed = "failed",
Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
import { acceptHMRUpdate, defineStore } from "pinia";
2+
import { reactive, ref } from "vue";
3+
import Header from "@/resources/Header.ts";
4+
import type EndpointDetails from "@/resources/EndpointDetails.ts";
5+
import FailedMessage, { ExceptionDetails, FailedMessageStatus } from "@/resources/FailedMessage.ts";
6+
import useEditAndRetry from "@/composables/useEditAndRetry.ts";
7+
import { useFetchFromServiceControl, useTypedFetchFromServiceControl } from "@/composables/serviceServiceControlUrls.ts";
8+
import Message from "@/resources/Message.ts";
9+
import moment from "moment/moment";
10+
import { useConfiguration } from "@/composables/configuration.ts";
11+
import { parse, stringify } from "lossless-json";
12+
import xmlFormat from "xml-formatter";
13+
14+
interface DataContainer<T> {
15+
loading?: boolean;
16+
failed_to_load?: boolean;
17+
not_found?: boolean;
18+
data: T;
19+
}
20+
21+
interface Model {
22+
id?: string;
23+
message_id?: string;
24+
conversation_id?: string;
25+
message_type?: string;
26+
sending_endpoint?: EndpointDetails;
27+
receiving_endpoint?: EndpointDetails;
28+
body_url?: string;
29+
failure_status: Partial<{
30+
retried: boolean;
31+
archiving: boolean;
32+
restoring: boolean;
33+
archived: boolean;
34+
resolved: boolean;
35+
delete_soon: boolean;
36+
retry_in_progress: boolean;
37+
delete_in_progress: boolean;
38+
restore_in_progress: boolean;
39+
submitted_for_retrial: boolean;
40+
}>;
41+
failure_metadata: Partial<{
42+
exception: ExceptionDetails;
43+
number_of_processing_attempts: number;
44+
status: FailedMessageStatus;
45+
time_of_failure: string;
46+
last_modified: string;
47+
edited: boolean;
48+
edit_of: string;
49+
deleted_in: string;
50+
redirect: boolean;
51+
}>;
52+
dialog_status: Partial<{
53+
show_delete_confirm: boolean;
54+
show_restore_confirm: boolean;
55+
show_retry_confirm: boolean;
56+
show_edit_retry_modal: boolean;
57+
}>;
58+
}
59+
60+
export const useMessageViewStore = defineStore("MessageViewStore", () => {
61+
const headers = ref<DataContainer<Header[]>>({ data: [] });
62+
const body = ref<DataContainer<{ value?: string; content_type?: string }>>({ data: {} });
63+
const state = reactive<DataContainer<Model>>({ data: { failure_metadata: {}, failure_status: {}, dialog_status: {} } });
64+
65+
async function loadFailedMessage(id: string) {
66+
state.loading = true;
67+
state.failed_to_load = false;
68+
state.not_found = false;
69+
70+
try {
71+
const response = await useFetchFromServiceControl(`errors/last/${id}`);
72+
if (response.status === 404) {
73+
state.not_found = true;
74+
return;
75+
} else if (!response.ok) {
76+
state.failed_to_load = true;
77+
return;
78+
}
79+
80+
const message = (await response.json()) as FailedMessage;
81+
state.data.failure_status.archived = message.status === FailedMessageStatus.Archived;
82+
state.data.failure_status.resolved = message.status === FailedMessageStatus.Resolved;
83+
state.data.failure_status.retried = message.status === FailedMessageStatus.RetryIssued;
84+
state.data.failure_metadata.last_modified = message.last_modified;
85+
} catch {
86+
state.failed_to_load = headers.value.failed_to_load = true;
87+
return;
88+
} finally {
89+
state.loading = headers.value.loading = false;
90+
}
91+
92+
const countdown = moment(state.data.failure_metadata.last_modified).add(error_retention_period, "hours");
93+
state.data.failure_status.delete_soon = countdown < moment();
94+
state.data.failure_metadata.deleted_in = countdown.format();
95+
96+
// TODO: Maintain the mutations of the message in memory until the api returns a newer modified message
97+
}
98+
99+
async function loadMessage(messageId: string, receivingEndpointName: string) {
100+
state.loading = headers.value.loading = true;
101+
state.failed_to_load = headers.value.failed_to_load = false;
102+
state.not_found = headers.value.not_found = false;
103+
104+
try {
105+
const [, data] = await useTypedFetchFromServiceControl<Message[]>(`messages/search/${messageId}`);
106+
107+
const message = data.find((value) => value.receiving_endpoint.name === receivingEndpointName);
108+
109+
if (!message) {
110+
state.not_found = headers.value.not_found = true;
111+
return;
112+
}
113+
114+
state.data.message_id = message.message_id;
115+
state.data.conversation_id = message.conversation_id;
116+
state.data.body_url = message.body_url;
117+
state.data.message_type = message.message_type;
118+
state.data.sending_endpoint = message.sending_endpoint;
119+
state.data.receiving_endpoint = message.receiving_endpoint;
120+
121+
headers.value.data = message.headers;
122+
} catch {
123+
state.failed_to_load = headers.value.failed_to_load = true;
124+
} finally {
125+
state.loading = headers.value.loading = false;
126+
}
127+
}
128+
129+
async function downloadBody() {
130+
if (body.value.not_found) {
131+
return;
132+
}
133+
134+
body.value.loading = true;
135+
body.value.failed_to_load = false;
136+
137+
try {
138+
if (!state.data.body_url) {
139+
return;
140+
}
141+
const response = await useFetchFromServiceControl(state.data.body_url.substring(1));
142+
if (response.status === 404) {
143+
body.value.not_found = true;
144+
145+
return;
146+
}
147+
148+
const contentType = response.headers.get("content-type");
149+
body.value.data.content_type = contentType ?? "text/plain";
150+
body.value.data.value = await response.text();
151+
152+
if (contentType === "application/json") {
153+
body.value.data.value = stringify(parse(body.value.data.value), null, 2) ?? body.value.data.value;
154+
}
155+
if (contentType === "text/xml") {
156+
body.value.data.value = xmlFormat(body.value.data.value, { indentation: " ", collapseContent: true });
157+
}
158+
} catch {
159+
body.value.failed_to_load = true;
160+
} finally {
161+
body.value.loading = false;
162+
}
163+
}
164+
165+
const configuration = useConfiguration();
166+
const error_retention_period = moment.duration(configuration.value?.data_retention.error_retention_period).asHours();
167+
168+
return {
169+
headers,
170+
body,
171+
state,
172+
edit_and_retry_config: useEditAndRetry(),
173+
loadMessage,
174+
loadFailedMessage,
175+
downloadBody,
176+
};
177+
});
178+
179+
if (import.meta.hot) {
180+
import.meta.hot.accept(acceptHMRUpdate(useMessageViewStore, import.meta.hot));
181+
}
182+
183+
export type MessageViewStore = ReturnType<typeof useMessageViewStore>;

0 commit comments

Comments
 (0)