1
1
<script setup lang="ts">
2
2
import { onMounted , ref } from " vue" ;
3
- import { type DefaultEdge , MarkerType , VueFlow , type Styles , type Node } from " @vue-flow/core" ;
3
+ import { type DefaultEdge , MarkerType , type Node , type Styles , VueFlow } from " @vue-flow/core" ;
4
4
import TimeSince from " ../TimeSince.vue" ;
5
5
import routeLinks from " @/router/routeLinks" ;
6
- import Message from " @/resources/Message" ;
6
+ import Message , { MessageStatus } from " @/resources/Message" ;
7
7
import { NServiceBusHeaders } from " @/resources/Header" ;
8
- import { Controls } from " @vue-flow/controls" ;
8
+ import { ControlButton , Controls } from " @vue-flow/controls" ;
9
9
import { useMessageStore } from " @/stores/MessageStore" ;
10
10
import LoadingSpinner from " @/components/LoadingSpinner.vue" ;
11
11
import { storeToRefs } from " pinia" ;
12
+ import EndpointDetails from " @/resources/EndpointDetails.ts" ;
13
+ import { hexToCSSFilter } from " hex-to-css-filter" ;
14
+ import TextEllipses from " @/components/TextEllipses.vue" ;
12
15
13
16
enum MessageType {
14
17
Event = " Event message" ,
@@ -20,8 +23,8 @@ interface MappedMessage {
20
23
nodeName: string ;
21
24
id: string ;
22
25
messageId: string ;
23
- sendingEndpoint: string ;
24
- receivingEndpoint: string ;
26
+ sendingEndpoint: EndpointDetails ;
27
+ receivingEndpoint: EndpointDetails ;
25
28
parentId: string ;
26
29
parentEndpoint: string ;
27
30
type: MessageType ;
@@ -74,15 +77,12 @@ function mapMessage(message: Message): MappedMessage {
74
77
nodeName: message .message_type ,
75
78
id: message .id ,
76
79
messageId: message .message_id ,
77
- sendingEndpoint: message .sending_endpoint ?. name ,
78
- receivingEndpoint: message .receiving_endpoint ?. name ,
80
+ sendingEndpoint: message .sending_endpoint ,
81
+ receivingEndpoint: message .receiving_endpoint ,
79
82
parentId ,
80
83
parentEndpoint ,
81
84
type ,
82
- isError:
83
- message .headers .findIndex (function (x ) {
84
- return x .key === NServiceBusHeaders .ExceptionInfoExceptionType ;
85
- }) > - 1 ,
85
+ isError: message .status !== MessageStatus .Successful && message .status !== MessageStatus .ResolvedSuccessfully ,
86
86
sagaName ,
87
87
link: {
88
88
name: ` Link ${message .id } ` ,
@@ -105,8 +105,8 @@ function constructNodes(mappedMessages: MappedMessage[]): Node[] {
105
105
const previousLevel = level > 0 ? messagesByLevel [level - 1 ] : null ;
106
106
return group .sort (
107
107
(a , b ) =>
108
- (previousLevel ?.findIndex ((plMessage ) => a .parentId === plMessage .messageId && a .parentEndpoint === plMessage .receivingEndpoint ) ?? 1 ) -
109
- (previousLevel ?.findIndex ((plMessage ) => b .parentId === plMessage .messageId && b .parentEndpoint === plMessage .receivingEndpoint ) ?? 1 )
108
+ (previousLevel ?.findIndex ((plMessage ) => a .parentId === plMessage .messageId && a .parentEndpoint === plMessage .receivingEndpoint . name ) ?? 1 ) -
109
+ (previousLevel ?.findIndex ((plMessage ) => b .parentId === plMessage .messageId && b .parentEndpoint === plMessage .receivingEndpoint . name ) ?? 1 )
110
110
);
111
111
})
112
112
// flatten to actual flow diagram nodes, with positioning based on parent node/level
@@ -115,7 +115,7 @@ function constructNodes(mappedMessages: MappedMessage[]): Node[] {
115
115
return group .reduce (
116
116
({ result , currentWidth , previousParent }, message ) => {
117
117
// position on current level needs to be based on parent Node, so see if one exists
118
- const parentMessage = previousLevel ?.find ((plMessage ) => message .parentId === plMessage .messageId && message .parentEndpoint === plMessage .receivingEndpoint ) ?? null ;
118
+ const parentMessage = previousLevel ?.find ((plMessage ) => message .parentId === plMessage .messageId && message .parentEndpoint === plMessage .receivingEndpoint . name ) ?? null ;
119
119
// if the current parent node is the same as the previous parent node, then the current position needs to be to the right of siblings
120
120
const currentParentWidth = previousParent === parentMessage ? currentWidth : 0 ;
121
121
const startX = parentMessage == null ? 0 : parentMessage .XPos ! - parentMessage .width ! / 2 ;
@@ -125,7 +125,7 @@ function constructNodes(mappedMessages: MappedMessage[]): Node[] {
125
125
result: [
126
126
... result ,
127
127
{
128
- id: ` ${message .messageId }##${message .receivingEndpoint } ` ,
128
+ id: ` ${message .messageId }##${message .receivingEndpoint . name } ` ,
129
129
type: " message" ,
130
130
data: message ,
131
131
label: message .nodeName ,
@@ -148,7 +148,7 @@ function constructEdges(mappedMessages: MappedMessage[]): DefaultEdge[] {
148
148
.map ((message ) => ({
149
149
id: ` ${message .parentId }##${message .messageId } ` ,
150
150
source: ` ${message .parentId }##${message .parentEndpoint } ` ,
151
- target: ` ${message .messageId }##${message .receivingEndpoint } ` ,
151
+ target: ` ${message .messageId }##${message .receivingEndpoint . name } ` ,
152
152
markerEnd: MarkerType .ArrowClosed ,
153
153
style: {
154
154
" stroke-dasharray" : message .type === MessageType .Event && " 5, 3" ,
@@ -166,7 +166,7 @@ onMounted(async () => {
166
166
167
167
const assignDescendantLevelsAndWidth = (message : MappedMessage , level = 0 ) => {
168
168
message .level = level ;
169
- const children = mappedMessages .filter ((mm ) => mm .parentId === message .messageId && mm .parentEndpoint === message .receivingEndpoint );
169
+ const children = mappedMessages .filter ((mm ) => mm .parentId === message .messageId && mm .parentEndpoint === message .receivingEndpoint . name );
170
170
message .width =
171
171
children .length === 0
172
172
? 1 // leaf node
@@ -188,34 +188,52 @@ function typeIcon(type: MessageType) {
188
188
return " pa-flow-command" ;
189
189
}
190
190
}
191
+
192
+ const showAddress = ref (false );
193
+
194
+ function toggleAddress() {
195
+ showAddress .value = ! showAddress .value ;
196
+ }
197
+
198
+ const blackColor = hexToCSSFilter (" #000000" ).filter ;
191
199
</script >
192
200
193
201
<template >
194
202
<div v-if =" store.conversationData.failed_to_load" class =" alert alert-info" >FlowDiagram data is unavailable.</div >
195
203
<LoadingSpinner v-else-if =" store.conversationData.loading" />
196
204
<div v-else id =" tree-container" >
197
205
<VueFlow v-model =" elements" :min-zoom =" 0.1" :fit-view-on-init =" true" >
198
- <Controls />
206
+ <Controls position =" top-left" class =" controls" >
207
+ <ControlButton v-tippy =" `Show endpoints`" @click =" toggleAddress" >
208
+ <i class =" fa pa-flow-endpoint" :style =" { filter: blackColor }" ></i >
209
+ </ControlButton >
210
+ </Controls >
199
211
<template #node-message =" { data }: { data: MappedMessage } " >
212
+ <div v-if =" showAddress" >
213
+ <TextEllipses class =" address" :text =" `${data.sendingEndpoint.name}@${data.sendingEndpoint.host}`" />
214
+ </div >
200
215
<div class =" node" :class =" { error: data.isError, 'current-message': data.id === store.state.data.id }" >
201
- <div class =" node-text wordwrap " >
216
+ <div class =" node-text" >
202
217
<i v-if =" data.isError" class =" fa pa-flow-failed" />
203
- <i class =" fa" :class =" typeIcon(data.type)" :title =" data.type" />
204
- <div class =" lead right-side-ellipsis " :title = " data.nodeName " >
218
+ <i class =" fa" :class =" typeIcon(data.type)" v-tippy =" data.type" />
219
+ <div class =" lead" >
205
220
<strong >
206
- <RouterLink v-if =" data.isError" :to =" { path: routeLinks.messages.failedMessage.link(data.id) }" >{{ data.nodeName }} </RouterLink >
207
- <RouterLink v-else :to =" { path: routeLinks.messages.successMessage.link(data.messageId, data.id) }" >{{ data.nodeName }} </RouterLink >
221
+ <RouterLink v-if =" data.isError" :to =" { path: routeLinks.messages.failedMessage.link(data.id) }" >< TextEllipses style = " width : 204 px " :text = " data.nodeName" ellipses-style = " LeftSide " /> </RouterLink >
222
+ <RouterLink v-else :to =" { path: routeLinks.messages.successMessage.link(data.messageId, data.id) }" >< TextEllipses style = " width : 204 px " :text = " data.nodeName" ellipses-style = " LeftSide " /> </RouterLink >
208
223
</strong >
209
224
</div >
210
- <span class =" time-sent" >
225
+ <div class =" time-sent" >
211
226
<time-since class =" time-since" :date-utc =" data.timeSent" />
212
- </span >
227
+ </div >
213
228
<template v-if =" data .sagaName " >
214
229
<i class =" fa pa-flow-saga" />
215
- <div class =" saga lead right-side-ellipsis " :title =" data.sagaName" >{{ data.sagaName }} </div >
230
+ <div class =" saga lead" >< TextEllipses style = " width : 182 px " :text =" data.sagaName" ellipses-style = " LeftSide " /> </div >
216
231
</template >
217
232
</div >
218
233
</div >
234
+ <div v-if =" showAddress" >
235
+ <TextEllipses class =" address" :text =" `${data.receivingEndpoint.name}@${data.receivingEndpoint.host}`" />
236
+ </div >
219
237
</template >
220
238
</VueFlow >
221
239
</div >
@@ -230,6 +248,12 @@ function typeIcon(type: MessageType) {
230
248
<style scoped>
231
249
@import " ../list.css" ;
232
250
251
+ .controls {
252
+ display : flex ;
253
+ flex-wrap : wrap ;
254
+ justify-content : center ;
255
+ }
256
+
233
257
#tree-container {
234
258
width : 90vw ;
235
259
height : 60vh ;
@@ -240,7 +264,6 @@ function typeIcon(type: MessageType) {
240
264
--vf-box-shadow : var (--vf-node-color , #1a192b );
241
265
background : var (--vf-node-bg );
242
266
border-color : var (--vf-node-color , #1a192b );
243
- padding : 10px ;
244
267
border-radius : 3px ;
245
268
font-size : 12px ;
246
269
border-width : 1px ;
@@ -249,11 +272,6 @@ function typeIcon(type: MessageType) {
249
272
text-align : left ;
250
273
}
251
274
252
- .right-side-ellipsis {
253
- direction : rtl ;
254
- text-align : left ;
255
- }
256
-
257
275
.node {
258
276
background-color : #fff ;
259
277
border-color : #cccbcc ;
@@ -269,7 +287,6 @@ function typeIcon(type: MessageType) {
269
287
}
270
288
271
289
.node .time-sent .time-since {
272
- display : block ;
273
290
margin-left : 20px ;
274
291
padding-top : 0 ;
275
292
color : #777f7f ;
@@ -290,7 +307,6 @@ function typeIcon(type: MessageType) {
290
307
291
308
.node-text .lead {
292
309
display : inline-block ;
293
- width : 204px ;
294
310
position : relative ;
295
311
top : 4px ;
296
312
}
@@ -302,7 +318,12 @@ function typeIcon(type: MessageType) {
302
318
303
319
.node-text .lead.saga {
304
320
font-weight : normal ;
305
- width : 182px ;
321
+ }
322
+
323
+ .address {
324
+ color : #777f7f ;
325
+ font-size : 0.8em ;
326
+ width : 264px ;
306
327
}
307
328
308
329
.current-message {
@@ -370,6 +391,14 @@ function typeIcon(type: MessageType) {
370
391
text-decoration : underline ;
371
392
}
372
393
394
+ .pa-flow-endpoint {
395
+ background-image : url (" @/assets/endpoint.svg" );
396
+ background-position : center ;
397
+ background-repeat : no-repeat ;
398
+ height : 15px ;
399
+ width : 15px ;
400
+ }
401
+
373
402
.pa-flow-failed {
374
403
background-image : url (" @/assets/failed-msg.svg" );
375
404
background-position : center ;
0 commit comments