Skip to content

Commit 949d699

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

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
@@ -152,8 +152,11 @@ function findMessageData(messagesData: SagaMessageData[], messageId: string): Sa
152152
function createEmptyMessageData(): SagaMessageData {
153153
return {
154154
message_id: "",
155-
data: "",
156-
type: "json",
157-
error: false,
155+
body: {
156+
data: {},
157+
loading: false,
158+
failed_to_load: false,
159+
not_found: false,
160+
},
158161
};
159162
}
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);
@@ -71,67 +72,38 @@ export const useSagaDiagramStore = defineStore("SagaDiagramStore", () => {
7172

7273
async function fetchSagaMessageData(message: SagaMessage): Promise<SagaMessageData> {
7374
const bodyUrl = (message.body_url ?? formatUrl(MessageBodyEndpoint, message.message_id)).replace(/^\//, "");
75+
const result: SagaMessageData = {
76+
message_id: message.message_id,
77+
body: { data: {} },
78+
};
7479

75-
try {
76-
const response = await useFetchFromServiceControl(bodyUrl, { cache: "no-store" });
80+
result.body.loading = true;
81+
result.body.failed_to_load = false;
7782

78-
// Treat 404 as empty data, not as an error
83+
try {
84+
const response = await useFetchFromServiceControl(bodyUrl);
7985
if (response.status === 404) {
80-
return {
81-
message_id: message.message_id,
82-
data: "",
83-
type: "json",
84-
error: false,
85-
};
86-
}
87-
88-
// Handle other non-OK responses as errors
89-
if (!response.ok) {
90-
error.value = `HTTP error! status: ${response.status}`;
91-
return {
92-
message_id: message.message_id,
93-
data: "",
94-
type: "json",
95-
error: true,
96-
};
86+
result.body.not_found = true;
87+
return result;
9788
}
9889

99-
const body = await response.text();
90+
const contentType = response.headers.get("content-type");
91+
result.body.data.content_type = contentType ?? "text/plain";
92+
result.body.data.value = await response.text();
10093

101-
if (!body) {
102-
return {
103-
message_id: message.message_id,
104-
data: "",
105-
type: "json",
106-
error: false,
107-
};
94+
if (contentType === "application/json") {
95+
result.body.data.value = stringify(parse(result.body.data.value), null, 2) ?? result.body.data.value;
10896
}
109-
110-
// Determine the content type
111-
if (body.trim().startsWith("<?xml")) {
112-
return {
113-
message_id: message.message_id,
114-
data: body,
115-
type: "xml",
116-
error: false,
117-
};
118-
} else {
119-
return {
120-
message_id: message.message_id,
121-
data: body,
122-
type: "json",
123-
error: false,
124-
};
97+
if (contentType === "text/xml") {
98+
result.body.data.value = xmlFormat(result.body.data.value, { indentation: " ", collapseContent: true });
12599
}
126-
} catch (e) {
127-
error.value = e instanceof Error ? e.message : "Unknown error occurred";
128-
return {
129-
message_id: message.message_id,
130-
data: "",
131-
type: "json",
132-
error: true,
133-
};
100+
} catch {
101+
result.body.failed_to_load = true;
102+
} finally {
103+
result.body.loading = false;
134104
}
105+
106+
return result;
135107
}
136108

137109
async function getAuditMessages(sagaId: string) {

0 commit comments

Comments
 (0)