Skip to content
Merged
Show file tree
Hide file tree
Changes from 16 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
35b8fb4
saga message data prelim work
soujay Apr 17, 2025
2d971c2
Display message data
soujay Apr 18, 2025
af54a61
Remove test code, add option for cache while fetching
soujay Apr 18, 2025
68ca0f3
fix ts and lint errors, add dummy data for test
soujay Apr 18, 2025
0bb5f4c
Remove unsed variables, Add display for initiating timeout message di…
soujay Apr 21, 2025
3985369
renamed file
soujay Apr 21, 2025
62a62a4
Rename interface
soujay Apr 21, 2025
7322263
refactor code adn code optimizationrename interface
soujay Apr 22, 2025
93cae94
Margin issue fix for outgoingMessage component
cquirosj Apr 22, 2025
eebf2bf
rn NonTimeoutMessages to OutgoingMessage
cquirosj Apr 22, 2025
9adf576
rn TimeoutMessages to SagaOutgoingTimeoutMessage
cquirosj Apr 22, 2025
5153872
Update logic for content EMTPY
cquirosj Apr 22, 2025
9ed7a10
Updating watcher for saga_id
cquirosj Apr 23, 2025
52e3388
move has saga watch from component to store
cquirosj Apr 23, 2025
8048be2
update store internal name to be SagaDiagramStore
cquirosj Apr 23, 2025
60d12b9
add friendly display of timeout timespan
soujay Apr 24, 2025
a626958
Making show message data lazy load again
cquirosj Apr 25, 2025
e877a27
Update src/Frontend/src/stores/SagaDiagramStore.ts
soujay Apr 25, 2025
d5ccddd
MessageData empty - fix flickering
cquirosj Apr 25, 2025
872d4ff
removing redundant names
cquirosj Apr 25, 2025
acdcb7e
Update src/Frontend/src/stores/SagaDiagramStore.ts
soujay Apr 25, 2025
a58862a
Update src/Frontend/src/stores/SagaDiagramStore.ts
soujay Apr 25, 2025
625b0b2
remove extra space
soujay Apr 25, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 28 additions & 7 deletions src/Frontend/src/components/messages2/SagaDiagram.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { SagaHistory } from "@/resources/SagaHistory";
import makeRouter from "@/router";
import { createTestingPinia } from "@pinia/testing";
import { MessageStore } from "@/stores/MessageStore";
import { MessageStatus } from "@/resources/Message";

//Defines a domain-specific language (DSL) for interacting with the system under test (sut)
interface componentDSL {
Expand Down Expand Up @@ -38,7 +39,7 @@ describe("Feature: Message not involved in Saga", () => {
const componentDriver = rendercomponent({
initialState: {
MessageStore: messageStore,
sagaHistory: undefined, // Lets pass undefined to simulate no saga data available
SagaDiagramStore: undefined, // Lets pass undefined to simulate no saga data available
},
});

Expand All @@ -63,7 +64,7 @@ describe("Feature: Detecting no Audited Saga Data Available", () => {
const componentDriver = rendercomponent({
initialState: {
MessageStore: messageStore,
sagaHistory: undefined, // Lets pass undefined to simulate no saga data available
SagaDiagramStore: undefined, // Lets pass undefined to simulate no saga data available
},
});

Expand Down Expand Up @@ -97,7 +98,7 @@ describe("Feature: Navigation and Contextual Information", () => {
const componentDriver = rendercomponent({
initialState: {
MessageStore: messageStore,
sagaHistory: { sagaHistory: sampleSagaHistory },
SagaDiagramStore: { sagaHistory: sampleSagaHistory },
},
});

Expand All @@ -120,7 +121,7 @@ describe("Feature: Navigation and Contextual Information", () => {
const componentDriver = rendercomponent({
initialState: {
MessageStore: messageStore,
sagaHistory: { sagaHistory: sampleSagaHistory },
SagaDiagramStore: { sagaHistory: sampleSagaHistory },
},
});

Expand Down Expand Up @@ -181,7 +182,7 @@ describe("Feature: 3 Visual Representation of Saga Timeline", () => {
const componentDriver = rendercomponent({
initialState: {
MessageStore: messageStore,
sagaHistory: { sagaHistory: sampleSagaHistory },
SagaDiagramStore: { sagaHistory: sampleSagaHistory },
},
});

Expand Down Expand Up @@ -247,7 +248,7 @@ describe("Feature: 3 Visual Representation of Saga Timeline", () => {
const componentDriver = rendercomponent({
initialState: {
MessageStore: messageStore,
sagaHistory: { sagaHistory: sampleSagaHistory },
SagaDiagramStore: { sagaHistory: sampleSagaHistory },
},
});

Expand All @@ -271,7 +272,7 @@ describe("Feature: 3 Visual Representation of Saga Timeline", () => {
});
});

function rendercomponent({ initialState = {} }: { initialState?: { MessageStore?: MessageStore; sagaHistory?: { sagaHistory: SagaHistory } } }): componentDSL {
function rendercomponent({ initialState = {} }: { initialState?: { MessageStore?: MessageStore; SagaDiagramStore?: { sagaHistory: SagaHistory } } }): componentDSL {
const router = makeRouter();

// Render with createTestingPinia
Expand Down Expand Up @@ -380,6 +381,8 @@ const sampleSagaHistory: SagaHistory = {
time_sent: new Date("2025-03-28T03:04:06.321561Z"),
message_type: "ServiceControl.SmokeTest.MyCustomTimeout",
intent: "Send",
body_url: "body_url",
message_status: MessageStatus.Successful,
},
outgoing_messages: [],
endpoint: "Endpoint1",
Expand All @@ -397,6 +400,8 @@ const sampleSagaHistory: SagaHistory = {
time_sent: new Date("2025-03-28T03:04:05.37723Z"),
message_type: "ServiceControl.SmokeTest.MyCustomTimeout",
intent: "Send",
body_url: "body_url",
message_status: MessageStatus.Successful,
},
outgoing_messages: [],
endpoint: "Endpoint1",
Expand All @@ -414,6 +419,8 @@ const sampleSagaHistory: SagaHistory = {
time_sent: new Date("2025-03-28T03:04:06.293765Z"),
message_type: "ServiceControl.SmokeTest.SagaMessage2",
intent: "Send",
body_url: "body_url",
message_status: MessageStatus.Successful,
},
outgoing_messages: [
{
Expand All @@ -423,6 +430,12 @@ const sampleSagaHistory: SagaHistory = {
time_sent: new Date("2025-03-28T03:04:06.3214397Z"),
message_type: "ServiceControl.SmokeTest.MyCustomTimeout",
intent: "Send",
deliver_at: new Date("2025-03-28T03:04:06.293765Z"),
is_saga_timeout_message: false,
originating_endpoint: "Sender",
originating_machine: "mobvm2",
body_url: "body_url",
message_status: MessageStatus.Successful,
},
],
endpoint: "Endpoint1",
Expand All @@ -440,6 +453,8 @@ const sampleSagaHistory: SagaHistory = {
time_sent: new Date("2025-03-28T03:04:05.235534Z"),
message_type: "ServiceControl.SmokeTest.SagaMessage1",
intent: "Send",
body_url: "body_url",
message_status: MessageStatus.Successful,
},
outgoing_messages: [
{
Expand All @@ -449,6 +464,12 @@ const sampleSagaHistory: SagaHistory = {
time_sent: new Date("2025-03-28T03:04:05.3715034Z"),
message_type: "ServiceControl.SmokeTest.MyCustomTimeout",
intent: "Send",
deliver_at: new Date("2025-03-28T03:04:06.293765Z"),
is_saga_timeout_message: false,
originating_endpoint: "Sender",
originating_machine: "mobvm2",
body_url: "body_url",
message_status: MessageStatus.Successful,
},
],
endpoint: "Endpoint1",
Expand Down
32 changes: 13 additions & 19 deletions src/Frontend/src/components/messages2/SagaDiagram.vue
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
<script setup lang="ts">
import { computed, onUnmounted, watch, ref } from "vue";
import { computed, onUnmounted, watch } from "vue";
import routeLinks from "@/router/routeLinks";
import { useSagaDiagramStore } from "@/stores/SagaDiagramStore";
import { useMessageStore } from "@/stores/MessageStore";
import { storeToRefs } from "pinia";
import ToolbarEndpointIcon from "@/assets/Shell_ToolbarEndpoint.svg";
import { SagaViewModel, parseSagaUpdates } from "./SagaDiagram/useSagaDiagramParser";
import { SagaViewModel, parseSagaUpdates } from "./SagaDiagram/SagaDiagramParser";
import { typeToName } from "@/composables/typeHumanizer";

//Subcomponents
Expand All @@ -15,23 +15,17 @@ import SagaHeader from "./SagaDiagram/SagaHeader.vue";
import SagaUpdateNode from "./SagaDiagram/SagaUpdateNode.vue";
import SagaCompletedNode from "./SagaDiagram/SagaCompletedNode.vue";

const showMessageData = ref(false);

const toggleMessageData = () => {
showMessageData.value = !showMessageData.value;
};

const store = useMessageStore();
const { state: messageState } = storeToRefs(store);

const sagaDiagramStore = useSagaDiagramStore();
const { showMessageData } = storeToRefs(sagaDiagramStore);

const messageStore = useMessageStore();

//Watch for message and set saga ID when component mounts or message changes
watch(
() => messageState.value.data.invoked_saga,
(newSagas) => {
if (newSagas.has_saga) {
sagaDiagramStore.setSagaId(newSagas.saga_id || "");
() => messageStore.state.data.invoked_saga?.has_saga,
(hasSaga) => {
const saga = messageStore.state.data.invoked_saga;
if (hasSaga && saga?.saga_id) {
sagaDiagramStore.setSagaId(saga.saga_id);
} else {
sagaDiagramStore.clearSagaHistory();
}
Expand All @@ -47,7 +41,7 @@ const vm = computed<SagaViewModel>(() => {
const completedUpdate = sagaDiagramStore.sagaHistory?.changes.find((update) => update.status === "completed");
const completionTime = completedUpdate ? new Date(completedUpdate.finish_time) : null;

const { data } = messageState.value;
const { data } = messageStore.state;
const { invoked_saga: saga } = data;
const sagaHistory = sagaDiagramStore.sagaHistory;

Expand All @@ -67,7 +61,7 @@ const vm = computed<SagaViewModel>(() => {

// Display data
FormattedCompletionTime: completionTime ? `${completionTime.toLocaleDateString()} ${completionTime.toLocaleTimeString()}` : "",
SagaUpdates: parseSagaUpdates(sagaHistory),
SagaUpdates: parseSagaUpdates(sagaHistory, sagaDiagramStore.messagesData),
ShowMessageData: showMessageData.value,
};
});
Expand All @@ -77,7 +71,7 @@ const vm = computed<SagaViewModel>(() => {
<div class="saga-container">
<!-- Toolbar header -->
<div v-if="vm.HasSagaData" class="header">
<button :class="['saga-button', { 'saga-button--active': vm.ShowMessageData }]" aria-label="show-message-data-button" @click="toggleMessageData">
<button :class="['saga-button', { 'saga-button--active': vm.ShowMessageData }]" aria-label="show-message-data-button" @click="sagaDiagramStore.toggleMessageData">
<img class="saga-button-icon" :src="ToolbarEndpointIcon" alt="" />
{{ vm.ShowMessageData ? "Hide Message Data" : "Show Message Data" }}
</button>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,19 @@
<script setup lang="ts">
import { SagaMessageDataItem } from "@/stores/SagaDiagramStore";

defineProps<{
propertyKey?: string;
propertyValue?: string;
title?: string;
messageData: SagaMessageDataItem[];
}>();
</script>

<template>
<div class="message-data-box">
<b class="message-data-box-text">{{ propertyKey || "OrderId" }}</b>
<div v-if="messageData.length === 0" class="message-data-box">
<span class="message-data-box-text--empty">Empty</span>
</div>
<div v-else v-for="(item, index) in messageData" :key="index" class="message-data-box">
<b class="message-data-box-text">{{ item.key }}</b>
<span class="message-data-box-text">=</span>
<span class="message-data-box-text--ellipsis" :title="title || propertyValue || 'Sample ID'">{{ propertyValue || "Sample ID" }}</span>
<span class="message-data-box-text--ellipsis" :title="item.value">{{ item.value }}</span>
</div>
</template>

Expand All @@ -32,4 +35,9 @@ defineProps<{
white-space: nowrap;
text-overflow: ellipsis;
}
.message-data-box-text--empty {
display: inline-block;
width: 100%;
text-align: center;
}
</style>
Original file line number Diff line number Diff line change
@@ -1,37 +1,40 @@
import { SagaHistory } from "@/resources/SagaHistory";
import { typeToName } from "@/composables/typeHumanizer";
import { SagaMessageData, SagaMessageDataItem } from "@/stores/SagaDiagramStore";
import { getTimeoutFriendly } from "@/composables/deliveryDelayParser";

export interface SagaMessageDataItem {
Key: string;
Value: string;
}

export interface SagaMessage {
export interface SagaMessageViewModel {
MessageId: string;
MessageFriendlyTypeName: string;
FormattedTimeSent: string;
Data: SagaMessageDataItem[];
IsEventMessage: boolean;
IsCommandMessage: boolean;
}

export interface SagaTimeoutMessage extends SagaMessage {
export interface InitiatingMessageViewModel {
InitiatingMessageType: string;
IsInitiatingMessageTimeOut: boolean;
FormattedInitiatingMessageTimestamp: string;
InitiatingMessageData: SagaMessageDataItem[];
}
export interface SagaTimeoutMessageViewModel extends SagaMessageViewModel {
TimeoutFriendly: string;
}

export interface SagaUpdateViewModel {
MessageId: string;
StartTime: Date;
FinishTime: Date;
FormattedStartTime: string;
InitiatingMessageType: string;
FormattedInitiatingMessageTimestamp: string;
InitiatingMessage: InitiatingMessageViewModel;
Status: string;
StatusDisplay: string;
HasTimeout: boolean;
IsFirstNode: boolean;
NonTimeoutMessages: SagaMessage[];
TimeoutMessages: SagaTimeoutMessage[];
HasNonTimeoutMessages: boolean;
HasTimeoutMessages: boolean;
OutgoingMessages: SagaMessageViewModel[];
OutgoingTimeoutMessages: SagaTimeoutMessageViewModel[];
HasOutgoingMessages: boolean;
HasOutgoingTimeoutMessages: boolean;
}

export interface SagaViewModel {
Expand All @@ -47,7 +50,7 @@ export interface SagaViewModel {
ShowMessageData: boolean;
}

export function parseSagaUpdates(sagaHistory: SagaHistory | null): SagaUpdateViewModel[] {
export function parseSagaUpdates(sagaHistory: SagaHistory | null, messagesData: SagaMessageData[]): SagaUpdateViewModel[] {
if (!sagaHistory || !sagaHistory.changes || !sagaHistory.changes.length) return [];

return sagaHistory.changes
Expand All @@ -56,6 +59,10 @@ export function parseSagaUpdates(sagaHistory: SagaHistory | null): SagaUpdateVie
const finishTime = new Date(update.finish_time);
const initiatingMessageTimestamp = new Date(update.initiating_message?.time_sent || Date.now());

// Find message data for initiating message
const initiatingMessageData = update.initiating_message ? messagesData.find((m) => m.message_id === update.initiating_message.message_id)?.data || [] : [];
const isInitiatingMessageTimeOut = update.initiating_message?.is_saga_timeout_message || false;

// Create common base message objects with shared properties
const outgoingMessages = update.outgoing_messages.map((msg) => {
const delivery_delay = msg.delivery_delay || "00:00:00";
Expand All @@ -64,47 +71,55 @@ export function parseSagaUpdates(sagaHistory: SagaHistory | null): SagaUpdateVie
const timeoutSeconds = delivery_delay.split(":")[2] || "0";
const isEventMessage = msg.intent === "Publish";

// Find corresponding message data
const messageData = messagesData.find((m) => m.message_id === msg.message_id)?.data || [];
return {
MessageType: msg.message_type || "",
MessageId: msg.message_id,
FormattedTimeSent: `${timeSent.toLocaleDateString()} ${timeSent.toLocaleTimeString()}`,
HasTimeout: hasTimeout,
TimeoutSeconds: timeoutSeconds,
TimeoutFriendly: getTimeoutFriendly(delivery_delay),
MessageFriendlyTypeName: typeToName(msg.message_type || ""),
Data: [] as SagaMessageDataItem[],
Data: messageData,
IsEventMessage: isEventMessage,
IsCommandMessage: !isEventMessage,
};
});

const timeoutMessages = outgoingMessages
const outgoingTimeoutMessages = outgoingMessages
.filter((msg) => msg.HasTimeout)
.map(
(msg) =>
({
...msg,
TimeoutFriendly: `${msg.TimeoutSeconds}s`, //TODO: Update with logic from ServiceInsight
}) as SagaTimeoutMessage
TimeoutFriendly: `${msg.TimeoutFriendly}`,
}) as SagaTimeoutMessageViewModel
);

const nonTimeoutMessages = outgoingMessages.filter((msg) => !msg.HasTimeout) as SagaMessage[];
const regularMessages = outgoingMessages.filter((msg) => !msg.HasTimeout) as SagaMessageViewModel[];

const hasTimeout = timeoutMessages.length > 0;
const hasTimeout = outgoingTimeoutMessages.length > 0;

return {
MessageId: update.initiating_message?.message_id || "",
StartTime: startTime,
FinishTime: finishTime,
FormattedStartTime: `${startTime.toLocaleDateString()} ${startTime.toLocaleTimeString()}`,
Status: update.status,
StatusDisplay: update.status === "new" ? "Saga Initiated" : "Saga Updated",
InitiatingMessageType: typeToName(update.initiating_message?.message_type || "Unknown Message") || "",
FormattedInitiatingMessageTimestamp: `${initiatingMessageTimestamp.toLocaleDateString()} ${initiatingMessageTimestamp.toLocaleTimeString()}`,
InitiatingMessage: {
InitiatingMessageType: typeToName(update.initiating_message?.message_type || "Unknown Message") || "",
FormattedInitiatingMessageTimestamp: `${initiatingMessageTimestamp.toLocaleDateString()} ${initiatingMessageTimestamp.toLocaleTimeString()}`,
InitiatingMessageData: initiatingMessageData,
IsInitiatingMessageTimeOut: isInitiatingMessageTimeOut,
},
HasTimeout: hasTimeout,
IsFirstNode: update.status === "new",
TimeoutMessages: timeoutMessages,
NonTimeoutMessages: nonTimeoutMessages,
HasNonTimeoutMessages: nonTimeoutMessages.length > 0,
HasTimeoutMessages: timeoutMessages.length > 0,
OutgoingTimeoutMessages: outgoingTimeoutMessages,
OutgoingMessages: regularMessages,
HasOutgoingMessages: regularMessages.length > 0,
HasOutgoingTimeoutMessages: outgoingTimeoutMessages.length > 0,
};
})
.sort((a, b) => a.StartTime.getTime() - b.StartTime.getTime())
Expand Down
Loading