Skip to content

Commit 055792a

Browse files
committed
v1 of using code editor for message data
1 parent 9661c94 commit 055792a

File tree

4 files changed

+121
-100
lines changed

4 files changed

+121
-100
lines changed

src/Frontend/src/components/MaximizableCodeEditor.vue

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,8 @@ import DiffCloseIcon from "@/assets/diff-close.svg";
66
import { Extension } from "@codemirror/state";
77
import { CodeLanguage } from "@/components/codeEditorTypes";
88
9-
// Define the model value to be passed to CodeEditor
109
const modelValue = defineModel<string>({ required: true });
1110
12-
// Define props by extending the CodeEditor props and adding maximize-specific ones
1311
withDefaults(
1412
defineProps<{
1513
language?: CodeLanguage;

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

Lines changed: 51 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,51 @@
11
<script setup lang="ts">
2-
import { SagaMessageDataItem, useSagaDiagramStore } from "@/stores/SagaDiagramStore";
2+
import { SagaMessageData, useSagaDiagramStore } from "@/stores/SagaDiagramStore";
33
import { storeToRefs } from "pinia";
44
import LoadingSpinner from "@/components/LoadingSpinner.vue";
5+
import MaximizableCodeEditor from "@/components/MaximizableCodeEditor.vue";
6+
import { computed } from "vue";
7+
import { CodeLanguage } from "@/components/codeEditorTypes";
8+
import { parse, stringify } from "lossless-json";
59
6-
defineProps<{
7-
messageData: SagaMessageDataItem[];
10+
const props = defineProps<{
11+
messageData: SagaMessageData;
812
}>();
913
1014
const sagaDiagramStore = useSagaDiagramStore();
1115
const { messageDataLoading } = storeToRefs(sagaDiagramStore);
16+
17+
const formattedData = computed(() => {
18+
if (props.messageData.type === "json" && props.messageData.data) {
19+
try {
20+
// Parse and then stringify with indentation to ensure proper formatting
21+
const parsed = parse(props.messageData.data);
22+
return stringify(parsed, null, 2);
23+
} catch {
24+
return props.messageData.data;
25+
}
26+
}
27+
return props.messageData.data;
28+
});
29+
30+
// Ensure language is properly typed as CodeLanguage
31+
const editorLanguage = computed<CodeLanguage>(() => {
32+
const type = props.messageData.type?.toLowerCase();
33+
return (type === "xml" ? "xml" : "json") as CodeLanguage;
34+
});
1235
</script>
1336

1437
<template>
1538
<div v-if="messageDataLoading" class="message-data-loading">
1639
<LoadingSpinner />
1740
</div>
18-
<div v-else-if="!messageDataLoading && messageData.length === 0" class="message-data-box">
41+
<div v-else-if="messageData.error" class="message-data-box message-data-box-error">
42+
<span class="message-data-box-text--error">An error occurred while parsing the message data</span>
43+
</div>
44+
<div v-else-if="!messageDataLoading && messageData.data === ''" class="message-data-box">
1945
<span class="message-data-box-text--empty">Empty</span>
2046
</div>
21-
<div v-else-if="messageData.length > 0" v-for="(item, index) in messageData" :key="index" class="message-data-box">
22-
<b class="message-data-box-text">{{ item.key }}</b>
23-
<span class="message-data-box-text">=</span>
24-
<span class="message-data-box-text--ellipsis" :title="item.value">{{ item.value }}</span>
47+
<div v-else class="message-data-box message-data-box-content">
48+
<MaximizableCodeEditor :model-value="formattedData || ''" :language="editorLanguage" :read-only="true" :show-gutter="false" modalTitle="Message Data" />
2549
</div>
2650
</template>
2751

@@ -30,6 +54,10 @@ const { messageDataLoading } = storeToRefs(sagaDiagramStore);
3054
display: flex;
3155
}
3256
57+
.message-data-box-content {
58+
display: block;
59+
}
60+
3361
.message-data-box-text {
3462
display: inline-block;
3563
margin-right: 0.25rem;
@@ -48,11 +76,26 @@ const { messageDataLoading } = storeToRefs(sagaDiagramStore);
4876
display: inline-block;
4977
width: 100%;
5078
text-align: center;
79+
color: #666;
80+
font-style: italic;
81+
}
82+
83+
.message-data-box-text--error {
84+
display: inline-block;
85+
width: 100%;
86+
text-align: center;
87+
color: #a94442;
88+
font-style: italic;
5189
}
5290
5391
.message-data-loading {
5492
display: flex;
5593
justify-content: center;
5694
align-items: center;
5795
}
96+
97+
.message-data-box-error {
98+
padding: 1rem;
99+
justify-content: center;
100+
}
58101
</style>

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

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
import { SagaHistory } from "@/resources/SagaHistory";
22
import { typeToName } from "@/composables/typeHumanizer";
3-
import { SagaMessageData, SagaMessageDataItem } from "@/stores/SagaDiagramStore";
3+
import { SagaMessageData } from "@/stores/SagaDiagramStore";
44
import { getTimeoutFriendly } from "@/composables/deliveryDelayParser";
55

66
export interface SagaMessageViewModel {
77
MessageId: string;
88
MessageFriendlyTypeName: string;
99
FormattedTimeSent: string;
10-
Data: SagaMessageDataItem[];
10+
Data: SagaMessageData;
1111
IsEventMessage: boolean;
1212
IsCommandMessage: boolean;
1313
}
@@ -16,7 +16,7 @@ export interface InitiatingMessageViewModel {
1616
IsSagaTimeoutMessage: boolean;
1717
FormattedMessageTimestamp: string;
1818
IsEventMessage: boolean;
19-
MessageData: SagaMessageDataItem[];
19+
MessageData: SagaMessageData;
2020
}
2121
export interface SagaTimeoutMessageViewModel extends SagaMessageViewModel {
2222
TimeoutFriendly: string;
@@ -64,7 +64,7 @@ export function parseSagaUpdates(sagaHistory: SagaHistory | null, messagesData:
6464
const initiatingMessageTimestamp = new Date(update.initiating_message?.time_sent || Date.now());
6565

6666
// Find message data for initiating message
67-
const initiatingMessageData = update.initiating_message ? messagesData.find((m) => m.message_id === update.initiating_message.message_id)?.data || [] : [];
67+
const initiatingMessageData = update.initiating_message ? findMessageData(messagesData, update.initiating_message.message_id) : createEmptyMessageData();
6868

6969
// Create common base message objects with shared properties
7070
const outgoingMessages = update.outgoing_messages.map((msg) => {
@@ -75,7 +75,7 @@ export function parseSagaUpdates(sagaHistory: SagaHistory | null, messagesData:
7575
const isEventMessage = msg.intent === "Publish";
7676

7777
// Find corresponding message data
78-
const messageData = messagesData.find((m) => m.message_id === msg.message_id)?.data || [];
78+
const messageData = findMessageData(messagesData, msg.message_id);
7979
return {
8080
MessageType: msg.message_type || "",
8181
MessageId: msg.message_id,
@@ -141,3 +141,19 @@ export function parseSagaUpdates(sagaHistory: SagaHistory | null, messagesData:
141141

142142
return updates;
143143
}
144+
145+
// Helper function to find message data or create empty data if not found
146+
function findMessageData(messagesData: SagaMessageData[], messageId: string): SagaMessageData {
147+
const messageData = messagesData.find((m) => m.message_id === messageId);
148+
return messageData || createEmptyMessageData();
149+
}
150+
151+
// Helper function to create an empty message data object
152+
function createEmptyMessageData(): SagaMessageData {
153+
return {
154+
message_id: "",
155+
data: "",
156+
type: "json",
157+
error: false,
158+
};
159+
}

src/Frontend/src/stores/SagaDiagramStore.ts

Lines changed: 49 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,12 @@ 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 } from "lossless-json";
76

8-
const StandardKeys = ["$type", "Id", "Originator", "OriginalMessageId"];
9-
export interface SagaMessageDataItem {
10-
key: string;
11-
value: string;
12-
}
137
export interface SagaMessageData {
148
message_id: string;
15-
data: SagaMessageDataItem[];
9+
data: string;
10+
type: "json" | "xml";
11+
error: boolean;
1612
}
1713
export const useSagaDiagramStore = defineStore("SagaDiagramStore", () => {
1814
const sagaHistory = ref<SagaHistory | null>(null);
@@ -73,44 +69,68 @@ export const useSagaDiagramStore = defineStore("SagaDiagramStore", () => {
7369
}
7470
}
7571

76-
function createEmptyMessageData(message_id: string): SagaMessageData {
77-
return {
78-
message_id,
79-
data: [],
80-
};
81-
}
82-
8372
async function fetchSagaMessageData(message: SagaMessage): Promise<SagaMessageData> {
8473
const bodyUrl = (message.body_url ?? formatUrl(MessageBodyEndpoint, message.message_id)).replace(/^\//, "");
8574

8675
try {
8776
const response = await useFetchFromServiceControl(bodyUrl, { cache: "no-store" });
77+
78+
// Treat 404 as empty data, not as an error
79+
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
8889
if (!response.ok) {
89-
throw new Error(`HTTP error! status: ${response.status}`);
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+
};
9097
}
91-
const body = await response.json();
98+
99+
const body = await response.text();
92100

93101
if (!body) {
94-
return createEmptyMessageData(message.message_id);
102+
return {
103+
message_id: message.message_id,
104+
data: "",
105+
type: "json",
106+
error: false,
107+
};
95108
}
96109

97-
let data: SagaMessageDataItem[];
98-
if (typeof body === "string" && body.trim().startsWith("<?xml")) {
99-
data = getXmlData(body);
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+
};
100118
} else {
101-
data = processJsonValues(body);
102-
}
103-
// Check if parsed data is empty
104-
if (!data || data.length === 0) {
105-
return createEmptyMessageData(message.message_id);
119+
return {
120+
message_id: message.message_id,
121+
data: body,
122+
type: "json",
123+
error: false,
124+
};
106125
}
126+
} catch (e) {
127+
error.value = e instanceof Error ? e.message : "Unknown error occurred";
107128
return {
108129
message_id: message.message_id,
109-
data,
130+
data: "",
131+
type: "json",
132+
error: true,
110133
};
111-
} catch (e) {
112-
error.value = e instanceof Error ? e.message : "Unknown error occurred";
113-
return createEmptyMessageData(message.message_id);
114134
}
115135
}
116136

@@ -127,62 +147,6 @@ export const useSagaDiagramStore = defineStore("SagaDiagramStore", () => {
127147
}
128148
}
129149

130-
function getXmlData(xmlString: string): SagaMessageDataItem[] {
131-
try {
132-
const parser = new DOMParser();
133-
const xmlDoc = parser.parseFromString(xmlString, "application/xml");
134-
135-
// Get the root element
136-
const rootElement = xmlDoc.documentElement;
137-
if (!rootElement) {
138-
return [];
139-
}
140-
141-
// Handle both v5 and pre-v5 message formats
142-
const messageRoot = rootElement.nodeName === "Messages" ? Array.from(rootElement.children)[0] : rootElement;
143-
144-
if (!messageRoot) {
145-
return [];
146-
}
147-
148-
// Convert child elements to SagaMessageDataItems
149-
return Array.from(messageRoot.children).map((node) => ({
150-
key: node.nodeName,
151-
value: node.textContent?.trim() || "",
152-
}));
153-
} catch (error) {
154-
console.error("Error parsing message data:", error);
155-
return [];
156-
}
157-
}
158-
159-
function processJsonValues(jsonBody: string | Record<string, unknown>): SagaMessageDataItem[] {
160-
let parsedBody: Record<string, unknown>;
161-
if (typeof jsonBody === "string") {
162-
try {
163-
parsedBody = parse(jsonBody) as Record<string, unknown>;
164-
} catch (e) {
165-
console.error("Error parsing JSON:", e);
166-
return [];
167-
}
168-
} else {
169-
parsedBody = jsonBody;
170-
}
171-
172-
const items: SagaMessageDataItem[] = [];
173-
174-
for (const key in parsedBody) {
175-
if (!StandardKeys.includes(key)) {
176-
items.push({
177-
key: key,
178-
value: String(parsedBody[key] ?? ""),
179-
});
180-
}
181-
}
182-
183-
return items;
184-
}
185-
186150
function clearSagaHistory() {
187151
sagaHistory.value = null;
188152
sagaId.value = null;

0 commit comments

Comments
 (0)