Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(RouteDiagram): Proper visualisation of parallel processing of the multicast #1022

Merged
merged 1 commit into from
Jul 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { visualizationService } from './visualization-service'
import { CamelNodeData, visualizationService } from './visualization-service'
import path from 'path'
import fs from 'fs'
import { RouteStats } from '@hawtiosrc/plugins/camel/routes-service'
Expand All @@ -9,9 +9,16 @@ jest.mock('@hawtiosrc/plugins/shared/jolokia-service')
describe('visualization-service', () => {
const routesXmlPath = path.resolve(__dirname, '../testdata', 'camel-choice-route.xml')
const sampleRoutesXml = fs.readFileSync(routesXmlPath, { encoding: 'utf8', flag: 'r' })

const routesStatsXmlPath = path.resolve(__dirname, '../testdata', 'camel-routes-stats.xml')
const sampleRoutesStatsXml = fs.readFileSync(routesStatsXmlPath, { encoding: 'utf8', flag: 'r' })

const multicastParallelRoutesXmlPath = path.resolve(__dirname, '../testdata', 'camel-multicast-parallel-route.xml')
const multicastParallelRouteXml = fs.readFileSync(multicastParallelRoutesXmlPath, { encoding: 'utf8', flag: 'r' })

const multicastRoutesXmlPath = path.resolve(__dirname, '../testdata', 'camel-multicast-route.xml')
const multicastRouteXml = fs.readFileSync(multicastRoutesXmlPath, { encoding: 'utf8', flag: 'r' })

describe('loadRouteXmlNodes', () => {
test('nodes and edges were correctly loaded from the file', async () => {
const node = new MBeanNode(null, 'test', true)
Expand All @@ -36,7 +43,36 @@ describe('visualization-service', () => {
// check if label is parsed correctly
expect(when2?.data.label).toBe(`When: \${body} == 'Hello Camel! - simple'`)
})

test('should correctly link child nodes for multicast', async () => {
const node = new MBeanNode(null, 'test', true)
const { camelNodes, edges } = await visualizationService.loadRouteXmlNodes(node, multicastRouteXml)
expect(camelNodes.length).toBe(5)
expect(edges.length).toBe(4)

expect(edges[2]!.target).toEqual(camelNodes[3]!.id)
expect(edges[2]!.source).toEqual(camelNodes[2]!.id)

expect((camelNodes[2]!.data as CamelNodeData).uri).toEqual('direct:first')
expect((camelNodes[3]!.data as CamelNodeData).uri).toEqual('direct:second')

expect(edges[3]!.target).toEqual(camelNodes[4]!.id)
expect((camelNodes[4]!.data as CamelNodeData).uri).toEqual('direct:outside')
})

test('should correctly link child nodes in parallel for multicast', async () => {
const node = new MBeanNode(null, 'test', true)
const { camelNodes, edges } = await visualizationService.loadRouteXmlNodes(node, multicastParallelRouteXml)
expect(camelNodes.length).toBe(5)
expect(edges.length).toBe(5)

// check that both 'to' nodes are connected to direct:outsideNode
expect(edges[3]!.target).toEqual(camelNodes[4]!.id)
expect(edges[4]!.target).toEqual(camelNodes[4]!.id)
expect((camelNodes[4]!.data as CamelNodeData).uri).toEqual('direct:outside') // Each 'to' node inside multicast should have its own link
})
})

describe('updateStats', () => {
test('processor stats were updates on the nodes', async () => {
const node = new MBeanNode(null, 'test', true)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ export type CamelNodeData = {
type: string
uri: string
routeId: string
isParallel: boolean
stats?: Statistics

nodeClicked?: (node: Node) => void
Expand Down Expand Up @@ -157,26 +158,26 @@ class VisualizationService {
): Promise<number[]> {
let rid = parent.getAttribute('id')
let siblingNodes: number[] = []
const parenNodeName: string = parent.localName
const parentNodeName: string = parent.localName

/*
* Whereas the id is unique across all routes in the xml, the
* routeIdx defines an id for each node in the route so
*/
let routeIdx = -1
for (const route of Array.from(parent.children)) {
for (const routeElement of Array.from(parent.children)) {
const id: string = nodeDatas.length + ''
routeIdx++
// from acts as a parent even though its a previous sibling :)
const nodeId = route.localName
const nodeId = routeElement.localName
if (nodeId === 'from' && parentId !== '-1') {
parentId = id
}
const nodeSettings = await schemaService.getSchema(node, nodeId)
let nodeData: CamelNodeData | null = null
if (nodeSettings) {
let label: string = (nodeSettings['title'] as string) || (nodeId as string)
const uri = this.getRouteNodeUri(route)
const uri = this.getRouteNodeUri(routeElement)
if (uri) {
label += ` ${uri.split('?')[0]}`
}
Expand All @@ -185,10 +186,10 @@ class VisualizationService {
tooltip += ' ' + uri
}
const { ignoreIdForLabel, maximumLabelWidth } = camelPreferencesService.loadOptions()
const elementID = route.getAttribute('id')
const elementID = routeElement.getAttribute('id')
let labelSummary = label
if (elementID) {
const customId = route.getAttribute('customId')
const customId = routeElement.getAttribute('customId')
if (ignoreIdForLabel || !customId || customId === 'false') {
labelSummary = 'id: ' + elementID
} else {
Expand Down Expand Up @@ -216,7 +217,8 @@ class VisualizationService {
}
}

let cid = route.getAttribute('_cid') || route.getAttribute('id')
let cid = routeElement.getAttribute('_cid') || routeElement.getAttribute('id')
const parallelProcessing: boolean = routeElement.getAttribute('parallelProcessing')?.toLowerCase() === 'true'
nodeData = {
id: id,
routeIdx: routeIdx,
Expand All @@ -231,7 +233,9 @@ class VisualizationService {
type: nodeId,
uri: uri ?? '',
routeId: routeId,
isParallel: parallelProcessing,
}

if (rid) {
nodeData.cid = rid
}
Expand All @@ -244,13 +248,18 @@ class VisualizationService {
// only use the route id on the first from node
rid = null
nodeDatas.push(nodeData)
const isParallelMulticastParent = (parentNodeName === 'multicast' && parentNode?.isParallel) || false
if (parentId !== null && parentId !== id) {
if (siblingNodes.length === 0 || parenNodeName === 'choice') {
if (siblingNodes.length === 0 || parentNodeName === 'choice' || isParallelMulticastParent) {
links.push({ id: parentId + '-' + id, source: parentId + '', target: id })
if (isParallelMulticastParent) {
siblingNodes.push(parseInt(id))
}
} else {
siblingNodes.forEach(function (nodeId) {
links.push({ id: nodeId + '-' + id, source: nodeId + '', target: id })
})

siblingNodes.length = 0
}
}
Expand All @@ -260,19 +269,20 @@ class VisualizationService {
if (langSettings && parentNode) {
// lets add the language kind
const name = langSettings['name'] || nodeId
const text = route.textContent
const text = routeElement.textContent

if (text) {
parentNode.tooltip = parentNode.label + ' ' + name + ' ' + text
parentNode.label += ': ' + this.appendLabel(route, text, true)
parentNode.label += ': ' + this.appendLabel(routeElement, text, true)
} else {
parentNode.label += ': ' + this.appendLabel(route, name, false)
parentNode.label += ': ' + this.appendLabel(routeElement, name, false)
}
}
}

const siblings = await this.addRouteXmlChildren(node, route, nodeDatas, links, routeId, id, nodeData)
if (parenNodeName === 'choice') {
const siblings = await this.addRouteXmlChildren(node, routeElement, nodeDatas, links, routeId, id, nodeData)

if (parentNodeName === 'choice' || (parentNodeName === 'multicast' && parentNode?.isParallel)) {
siblingNodes = siblingNodes.concat(siblings)
} else if (
nodeId === 'aggregate' ||
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<routes xmlns="http://camel.apache.org/schema/spring">
<route id="testRoute">
<from uri="direct:a"/>
<multicast parallelProcessing="true">
<to uri="direct:first"/>
<to uri="direct:second"/>
</multicast>
<to uri="direct:outside"/>
</route>
</routes>
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<routes xmlns="http://camel.apache.org/schema/spring">
<route id="testRoute">
<from uri="direct:a"/>
<multicast>
<to uri="direct:first"/>
<to uri="direct:second"/>
</multicast>
<to uri="direct:outside"/>
</route>
</routes>
Loading