Skip to content

Commit 4955477

Browse files
committed
improved loading of the body - and formatted xml
Inspired by the one in MessageStore. Potential removal of duplication a
1 parent 89cb1fb commit 4955477

File tree

5 files changed

+65
-87
lines changed

5 files changed

+65
-87
lines changed

src/Frontend/src/components/messages2/SagaDiagram/MessageDataBox.vue

Lines changed: 21 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,8 @@ import { storeToRefs } from "pinia";
44
import LoadingSpinner from "@/components/LoadingSpinner.vue";
55
import MaximizableCodeEditor from "@/components/MaximizableCodeEditor.vue";
66
import { computed } from "vue";
7-
import { CodeLanguage } from "@/components/codeEditorTypes";
8-
import { parse, stringify } from "lossless-json";
97
import { EditorView } from "@codemirror/view";
8+
import parseContentType from "@/composables/contentTypeParser";
109
1110
const messageDataBoxTheme = EditorView.baseTheme({
1211
".maximazable-code-editor--inline-instance .cm-editor": {
@@ -31,37 +30,29 @@ const modalTitle = computed(() => {
3130
const sagaDiagramStore = useSagaDiagramStore();
3231
const { messageDataLoading } = storeToRefs(sagaDiagramStore);
3332
34-
const formattedData = computed(() => {
35-
if (props.messageData.type === "json" && props.messageData.data) {
36-
try {
37-
// Parse and then stringify with indentation to ensure proper formatting
38-
const parsed = parse(props.messageData.data);
39-
return stringify(parsed, null, 2);
40-
} catch {
41-
return props.messageData.data;
42-
}
43-
}
44-
return props.messageData.data;
45-
});
33+
const contentType = computed(() => parseContentType(props.messageData.body.data.content_type));
4634
47-
const editorLanguage = computed<CodeLanguage>(() => {
48-
const type = props.messageData.type?.toLowerCase();
49-
return (type === "xml" ? "xml" : "json") as CodeLanguage;
50-
});
35+
const body = computed(() => props.messageData.body.data.value || "");
5136
</script>
5237

5338
<template>
5439
<div v-if="messageDataLoading" class="message-data-loading">
5540
<LoadingSpinner />
5641
</div>
57-
<div v-else-if="messageData.error" class="message-data-box message-data-box-error">
42+
<div v-else-if="messageData.body.failed_to_load" class="message-data-box message-data-box-error">
5843
<span class="message-data-box-text--error">An error occurred while retrieving the message data</span>
5944
</div>
60-
<div v-else-if="!messageDataLoading && messageData.data === ''" class="message-data-box">
45+
<div v-else-if="messageData.body.not_found" class="message-data-box message-data-box-error">
46+
<span class="message-data-box-text--error">Message body not found</span>
47+
</div>
48+
<div v-else-if="!messageDataLoading && !messageData.body.data.value" class="message-data-box">
6149
<span class="message-data-box-text--empty">Empty</span>
6250
</div>
63-
<div v-else class="message-data-box message-data-box-content">
64-
<MaximizableCodeEditor :model-value="formattedData || ''" :language="editorLanguage" :read-only="true" :show-gutter="false" :modalTitle="modalTitle" :extensions="[messageDataBoxTheme]" />
51+
<div v-else-if="contentType.isSupported" class="message-data-box message-data-box-content">
52+
<MaximizableCodeEditor :model-value="body" :language="contentType.language" :readOnly="true" :showGutter="false" :modalTitle="modalTitle" :extensions="[messageDataBoxTheme]" />
53+
</div>
54+
<div v-else class="message-data-box message-data-box-error">
55+
<span class="message-data-box-text--unsupported">Message body cannot be displayed because content type "{{ messageData.body.data.content_type }}" is not supported.</span>
6556
</div>
6657
</template>
6758

@@ -104,6 +95,14 @@ const editorLanguage = computed<CodeLanguage>(() => {
10495
font-style: italic;
10596
}
10697
98+
.message-data-box-text--unsupported {
99+
display: inline-block;
100+
width: 100%;
101+
text-align: center;
102+
color: #8a6d3b;
103+
font-style: italic;
104+
}
105+
107106
.message-data-loading {
108107
display: flex;
109108
justify-content: center;

src/Frontend/src/components/messages2/SagaDiagram/SagaDiagramParser.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -171,8 +171,11 @@ function findMessageData(messagesData: SagaMessageData[], messageId: string): Sa
171171
function createEmptyMessageData(): SagaMessageData {
172172
return {
173173
message_id: "",
174-
data: "",
175-
type: "json",
176-
error: false,
174+
body: {
175+
data: {},
176+
loading: false,
177+
failed_to_load: false,
178+
not_found: false,
179+
},
177180
};
178181
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
/**
2+
* A container for data with loading states.
3+
* Used to track loading, error, and not found states for data fetched from APIs.
4+
*/
5+
export interface DataContainer<T> {
6+
loading?: boolean;
7+
failed_to_load?: boolean;
8+
not_found?: boolean;
9+
data: T;
10+
}

src/Frontend/src/stores/MessageStore.ts

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,7 @@ import moment from "moment/moment";
1111
import { parse, stringify } from "lossless-json";
1212
import xmlFormat from "xml-formatter";
1313
import { useArchiveMessage, useRetryMessages, useUnarchiveMessage } from "@/composables/serviceFailedMessage";
14-
15-
interface DataContainer<T> {
16-
loading?: boolean;
17-
failed_to_load?: boolean;
18-
not_found?: boolean;
19-
data: T;
20-
}
14+
import { DataContainer } from "./DataContainer";
2115

2216
interface Model {
2317
id?: string;

src/Frontend/src/stores/SagaDiagramStore.ts

Lines changed: 27 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,13 @@ import { ref, watch } from "vue";
33
import { SagaHistory, SagaMessage } from "@/resources/SagaHistory";
44
import { useFetchFromServiceControl } from "@/composables/serviceServiceControlUrls";
55
import Message from "@/resources/Message";
6+
import { parse, stringify } from "lossless-json";
7+
import xmlFormat from "xml-formatter";
8+
import { DataContainer } from "./DataContainer";
69

710
export interface SagaMessageData {
811
message_id: string;
9-
data: string;
10-
type: "json" | "xml";
11-
error: boolean;
12+
body: DataContainer<{ value?: string; content_type?: string }>;
1213
}
1314
export const useSagaDiagramStore = defineStore("SagaDiagramStore", () => {
1415
const sagaHistory = ref<SagaHistory | null>(null);
@@ -87,67 +88,38 @@ export const useSagaDiagramStore = defineStore("SagaDiagramStore", () => {
8788

8889
async function fetchSagaMessageData(message: SagaMessage): Promise<SagaMessageData> {
8990
const bodyUrl = (message.body_url ?? formatUrl(MessageBodyEndpoint, message.message_id)).replace(/^\//, "");
91+
const result: SagaMessageData = {
92+
message_id: message.message_id,
93+
body: { data: {} },
94+
};
9095

91-
try {
92-
const response = await useFetchFromServiceControl(bodyUrl, { cache: "no-store" });
96+
result.body.loading = true;
97+
result.body.failed_to_load = false;
9398

94-
// Treat 404 as empty data, not as an error
99+
try {
100+
const response = await useFetchFromServiceControl(bodyUrl);
95101
if (response.status === 404) {
96-
return {
97-
message_id: message.message_id,
98-
data: "",
99-
type: "json",
100-
error: false,
101-
};
102-
}
103-
104-
// Handle other non-OK responses as errors
105-
if (!response.ok) {
106-
error.value = `HTTP error! status: ${response.status}`;
107-
return {
108-
message_id: message.message_id,
109-
data: "",
110-
type: "json",
111-
error: true,
112-
};
102+
result.body.not_found = true;
103+
return result;
113104
}
114105

115-
const body = await response.text();
106+
const contentType = response.headers.get("content-type");
107+
result.body.data.content_type = contentType ?? "text/plain";
108+
result.body.data.value = await response.text();
116109

117-
if (!body) {
118-
return {
119-
message_id: message.message_id,
120-
data: "",
121-
type: "json",
122-
error: false,
123-
};
110+
if (contentType === "application/json") {
111+
result.body.data.value = stringify(parse(result.body.data.value), null, 2) ?? result.body.data.value;
124112
}
125-
126-
// Determine the content type
127-
if (body.trim().startsWith("<?xml")) {
128-
return {
129-
message_id: message.message_id,
130-
data: body,
131-
type: "xml",
132-
error: false,
133-
};
134-
} else {
135-
return {
136-
message_id: message.message_id,
137-
data: body,
138-
type: "json",
139-
error: false,
140-
};
113+
if (contentType === "text/xml") {
114+
result.body.data.value = xmlFormat(result.body.data.value, { indentation: " ", collapseContent: true });
141115
}
142-
} catch (e) {
143-
error.value = e instanceof Error ? e.message : "Unknown error occurred";
144-
return {
145-
message_id: message.message_id,
146-
data: "",
147-
type: "json",
148-
error: true,
149-
};
116+
} catch {
117+
result.body.failed_to_load = true;
118+
} finally {
119+
result.body.loading = false;
150120
}
121+
122+
return result;
151123
}
152124

153125
async function getAuditMessages(sagaId: string) {

0 commit comments

Comments
 (0)