From 1d03cc72aed65c4524c2f360ed8c937fa6218667 Mon Sep 17 00:00:00 2001 From: "Ricardo M." Date: Mon, 19 Aug 2024 16:19:19 +0000 Subject: [PATCH] chore(Canvas): Split `CanvasService` in `ControllerService` and `FlowService` Split `CanvasService` for better separation of concerns. fix: https://github.com/KaotoIO/kaoto/issues/1329 --- .../Visualization/Canvas/Canvas.tsx | 3 +- .../Visualization/Canvas/CanvasForm.test.tsx | 22 ++-- ...test.ts.snap => flow.service.test.ts.snap} | 16 +-- .../Canvas/canvas.service.test.ts | 109 ----------------- .../Visualization/Canvas/canvas.service.ts | 113 +---------------- .../Visualization/Canvas/flow.service.test.ts | 112 +++++++++++++++++ .../Visualization/Canvas/flow.service.ts | 114 ++++++++++++++++++ packages/ui/src/tests/nodes-edges.test.ts | 8 +- 8 files changed, 252 insertions(+), 245 deletions(-) rename packages/ui/src/components/Visualization/Canvas/__snapshots__/{canvas.service.test.ts.snap => flow.service.test.ts.snap} (94%) create mode 100644 packages/ui/src/components/Visualization/Canvas/flow.service.test.ts create mode 100644 packages/ui/src/components/Visualization/Canvas/flow.service.ts diff --git a/packages/ui/src/components/Visualization/Canvas/Canvas.tsx b/packages/ui/src/components/Visualization/Canvas/Canvas.tsx index ecb49b20b..a941a8a55 100644 --- a/packages/ui/src/components/Visualization/Canvas/Canvas.tsx +++ b/packages/ui/src/components/Visualization/Canvas/Canvas.tsx @@ -35,6 +35,7 @@ import { CanvasSideBar } from './CanvasSideBar'; import { CanvasDefaults } from './canvas.defaults'; import { CanvasEdge, CanvasNode, LayoutType } from './canvas.models'; import { CanvasService } from './canvas.service'; +import { FlowService } from './flow.service'; interface CanvasProps { contextToolbar?: ReactNode; @@ -162,7 +163,7 @@ export const Canvas: FunctionComponent> = ({ enti entities.forEach((entity) => { if (visibleFlows[entity.id]) { - const { nodes: childNodes, edges: childEdges } = CanvasService.getFlowDiagram(entity.toVizNode()); + const { nodes: childNodes, edges: childEdges } = FlowService.getFlowDiagram(entity.toVizNode()); nodes.push(...childNodes); edges.push(...childEdges); } diff --git a/packages/ui/src/components/Visualization/Canvas/CanvasForm.test.tsx b/packages/ui/src/components/Visualization/Canvas/CanvasForm.test.tsx index cb8c9ea38..9bcaa4674 100644 --- a/packages/ui/src/components/Visualization/Canvas/CanvasForm.test.tsx +++ b/packages/ui/src/components/Visualization/Canvas/CanvasForm.test.tsx @@ -15,19 +15,19 @@ import { import { IVisualizationNode, VisualComponentSchema } from '../../../models/visualization/base-visual-entity'; import { VisualFlowsApi } from '../../../models/visualization/flows/support/flows-visibility'; import { - VisibleFlowsContext, - VisibleFlowsProvider, CanvasFormTabsContext, CanvasFormTabsProvider, + VisibleFlowsContext, + VisibleFlowsProvider, } from '../../../providers'; import { EntitiesContext, EntitiesProvider } from '../../../providers/entities.provider'; import { camelRouteJson, kameletJson } from '../../../stubs'; import { getFirstCatalogMap } from '../../../stubs/test-load-catalog'; +import { ROOT_PATH } from '../../../utils'; import { CanvasForm } from './CanvasForm'; import { CanvasNode } from './canvas.models'; -import { CanvasService } from './canvas.service'; import { FormTabsModes } from './canvasformtabs.modes'; -import { ROOT_PATH } from '../../../utils'; +import { FlowService } from './flow.service'; describe('CanvasForm', () => { let camelRouteVisualEntity: CamelRouteVisualEntity; @@ -54,7 +54,7 @@ describe('CanvasForm', () => { beforeEach(() => { camelRouteVisualEntity = new CamelRouteVisualEntity(camelRouteJson); - const { nodes } = CanvasService.getFlowDiagram(camelRouteVisualEntity.toVizNode()); + const { nodes } = FlowService.getFlowDiagram(camelRouteVisualEntity.toVizNode()); selectedNode = nodes[2]; // choice }); @@ -175,7 +175,7 @@ describe('CanvasForm', () => { const flowId = camelRouteVisualEntity.id; const dispatchSpy = jest.fn(); const visualFlowsApi = new VisualFlowsApi(dispatchSpy); - const { nodes } = CanvasService.getFlowDiagram(camelRouteVisualEntity.toVizNode()); + const { nodes } = FlowService.getFlowDiagram(camelRouteVisualEntity.toVizNode()); selectedNode = nodes[nodes.length - 1]; render( @@ -210,7 +210,7 @@ describe('CanvasForm', () => { const flowId = camelRouteVisualEntity.id; const dispatchSpy = jest.fn(); const visualFlowsApi = new VisualFlowsApi(dispatchSpy); - const { nodes } = CanvasService.getFlowDiagram(camelRouteVisualEntity.toVizNode()); + const { nodes } = FlowService.getFlowDiagram(camelRouteVisualEntity.toVizNode()); selectedNode = nodes[nodes.length - 1]; render( @@ -246,7 +246,7 @@ describe('CanvasForm', () => { const newName = 'MyNewId'; const dispatchSpy = jest.fn(); const visualFlowsApi = new VisualFlowsApi(dispatchSpy); - const { nodes } = CanvasService.getFlowDiagram(camelRouteVisualEntity.toVizNode()); + const { nodes } = FlowService.getFlowDiagram(camelRouteVisualEntity.toVizNode()); selectedNode = nodes[nodes.length - 1]; render( @@ -284,7 +284,7 @@ describe('CanvasForm', () => { const newName = 'MyNewName'; const dispatchSpy = jest.fn(); const visualFlowsApi = new VisualFlowsApi(dispatchSpy); - const { nodes } = CanvasService.getFlowDiagram(kameletVisualEntity.toVizNode()); + const { nodes } = FlowService.getFlowDiagram(kameletVisualEntity.toVizNode()); selectedNode = nodes[nodes.length - 1]; render( @@ -312,7 +312,7 @@ describe('CanvasForm', () => { describe('should show the User-updated field under the modified tab', () => { beforeEach(() => { camelRouteVisualEntity = new CamelRouteVisualEntity(camelRouteJson); - const { nodes } = CanvasService.getFlowDiagram(camelRouteVisualEntity.toVizNode()); + const { nodes } = FlowService.getFlowDiagram(camelRouteVisualEntity.toVizNode()); selectedNode = nodes[0]; // timer }); @@ -640,7 +640,7 @@ describe('CanvasForm', () => { describe('should show the Required field under the required tab', () => { beforeEach(() => { camelRouteVisualEntity = new CamelRouteVisualEntity(camelRouteJson); - const { nodes } = CanvasService.getFlowDiagram(camelRouteVisualEntity.toVizNode()); + const { nodes } = FlowService.getFlowDiagram(camelRouteVisualEntity.toVizNode()); selectedNode = nodes[0]; // timer }); diff --git a/packages/ui/src/components/Visualization/Canvas/__snapshots__/canvas.service.test.ts.snap b/packages/ui/src/components/Visualization/Canvas/__snapshots__/flow.service.test.ts.snap similarity index 94% rename from packages/ui/src/components/Visualization/Canvas/__snapshots__/canvas.service.test.ts.snap rename to packages/ui/src/components/Visualization/Canvas/__snapshots__/flow.service.test.ts.snap index 954523be1..08bbfe0a5 100644 --- a/packages/ui/src/components/Visualization/Canvas/__snapshots__/canvas.service.test.ts.snap +++ b/packages/ui/src/components/Visualization/Canvas/__snapshots__/flow.service.test.ts.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`CanvasService getFlowDiagram should return nodes and edges for a group with children 1`] = ` +exports[`FlowService getFlowDiagram should return nodes and edges for a group with children 1`] = ` [ { "data": { @@ -206,9 +206,9 @@ exports[`CanvasService getFlowDiagram should return nodes and edges for a group ] `; -exports[`CanvasService getFlowDiagram should return nodes and edges for a group with children 2`] = `[]`; +exports[`FlowService getFlowDiagram should return nodes and edges for a group with children 2`] = `[]`; -exports[`CanvasService getFlowDiagram should return nodes and edges for a multiple nodes VisualizationNode 1`] = ` +exports[`FlowService getFlowDiagram should return nodes and edges for a multiple nodes VisualizationNode 1`] = ` [ { "data": { @@ -381,7 +381,7 @@ exports[`CanvasService getFlowDiagram should return nodes and edges for a multip ] `; -exports[`CanvasService getFlowDiagram should return nodes and edges for a multiple nodes VisualizationNode 2`] = ` +exports[`FlowService getFlowDiagram should return nodes and edges for a multiple nodes VisualizationNode 2`] = ` [ { "edgeStyle": "solid", @@ -393,7 +393,7 @@ exports[`CanvasService getFlowDiagram should return nodes and edges for a multip ] `; -exports[`CanvasService getFlowDiagram should return nodes and edges for a simple VisualizationNode 1`] = ` +exports[`FlowService getFlowDiagram should return nodes and edges for a simple VisualizationNode 1`] = ` [ { "data": { @@ -425,9 +425,9 @@ exports[`CanvasService getFlowDiagram should return nodes and edges for a simple ] `; -exports[`CanvasService getFlowDiagram should return nodes and edges for a simple VisualizationNode 2`] = `[]`; +exports[`FlowService getFlowDiagram should return nodes and edges for a simple VisualizationNode 2`] = `[]`; -exports[`CanvasService getFlowDiagram should return nodes and edges for a two-nodes VisualizationNode 1`] = ` +exports[`FlowService getFlowDiagram should return nodes and edges for a two-nodes VisualizationNode 1`] = ` [ { "data": { @@ -478,4 +478,4 @@ exports[`CanvasService getFlowDiagram should return nodes and edges for a two-no ] `; -exports[`CanvasService getFlowDiagram should return nodes and edges for a two-nodes VisualizationNode 2`] = `[]`; +exports[`FlowService getFlowDiagram should return nodes and edges for a two-nodes VisualizationNode 2`] = `[]`; diff --git a/packages/ui/src/components/Visualization/Canvas/canvas.service.test.ts b/packages/ui/src/components/Visualization/Canvas/canvas.service.test.ts index 1c1a088a6..8cc78bdba 100644 --- a/packages/ui/src/components/Visualization/Canvas/canvas.service.test.ts +++ b/packages/ui/src/components/Visualization/Canvas/canvas.service.test.ts @@ -9,27 +9,12 @@ import { ModelKind, Visualization, } from '@patternfly/react-topology'; -import { createVisualizationNode } from '../../../models/visualization'; -import { BaseVisualCamelEntity } from '../../../models/visualization/base-visual-entity'; import { CustomGroupWithSelection } from '../Custom'; import { CanvasDefaults } from './canvas.defaults'; import { LayoutType } from './canvas.models'; import { CanvasService } from './canvas.service'; describe('CanvasService', () => { - beforeEach(() => { - CanvasService.nodes = []; - CanvasService.edges = []; - }); - - it('should start with an empty nodes array', () => { - expect(CanvasService.nodes).toEqual([]); - }); - - it('should start with an empty edges array', () => { - expect(CanvasService.edges).toEqual([]); - }); - it('should allow consumers to create a new controller and register its factories', () => { const layoutFactorySpy = jest.spyOn(Visualization.prototype, 'registerLayoutFactory'); const componentFactorySpy = jest.spyOn(Visualization.prototype, 'registerComponentFactory'); @@ -104,98 +89,4 @@ describe('CanvasService', () => { expect(layoutFactory).toBeInstanceOf(layout); }); - - describe('getFlowDiagram', () => { - it('should return nodes and edges for a simple VisualizationNode', () => { - const vizNode = createVisualizationNode('node', {}); - - const { nodes, edges } = CanvasService.getFlowDiagram(vizNode); - - expect(nodes).toMatchSnapshot(); - expect(edges).toMatchSnapshot(); - }); - - it('should return nodes and edges for a group with children', () => { - const groupVizNode = createVisualizationNode('group', { isGroup: true }); - const child1VizNode = createVisualizationNode('child1', {}); - const child2VizNode = createVisualizationNode('child2', {}); - groupVizNode.addChild(child1VizNode); - groupVizNode.addChild(child2VizNode); - - const { nodes, edges } = CanvasService.getFlowDiagram(groupVizNode); - - expect(nodes).toMatchSnapshot(); - expect(edges).toMatchSnapshot(); - }); - - it('should return nodes and edges for a two-nodes VisualizationNode', () => { - const vizNode = createVisualizationNode('node', {}); - const childNode = createVisualizationNode('child', {}); - vizNode.addChild(childNode); - - const { nodes, edges } = CanvasService.getFlowDiagram(vizNode); - - expect(nodes).toMatchSnapshot(); - expect(edges).toMatchSnapshot(); - }); - - it('should return nodes and edges for a multiple nodes VisualizationNode', () => { - const vizNode = createVisualizationNode('node', {}); - - const setHeaderNode = createVisualizationNode('set-header', {}); - vizNode.setNextNode(setHeaderNode); - setHeaderNode.setPreviousNode(vizNode); - - const choiceNode = createVisualizationNode('choice', {}); - setHeaderNode.setNextNode(choiceNode); - choiceNode.setPreviousNode(setHeaderNode); - - const directNode = createVisualizationNode('direct', {}); - choiceNode.setNextNode(directNode); - directNode.setPreviousNode(choiceNode); - - const whenNode = createVisualizationNode('when', {}); - choiceNode.addChild(whenNode); - - const otherwiseNode = createVisualizationNode('otherwise', {}); - choiceNode.addChild(otherwiseNode); - - const whenLeafNode = createVisualizationNode('when-leaf', {}); - whenNode.addChild(whenLeafNode); - - const processNode = createVisualizationNode('process', {}); - otherwiseNode.addChild(processNode); - const logNode = createVisualizationNode('log', {}); - processNode.addChild(logNode); - - const { nodes, edges } = CanvasService.getFlowDiagram(vizNode); - - expect(nodes).toMatchSnapshot(); - expect(edges).toMatchSnapshot(); - }); - - it('should return a group node for a multiple nodes VisualizationNode with a group', () => { - const routeNode = createVisualizationNode('route', { - entity: { getId: () => 'myId' } as BaseVisualCamelEntity, - isGroup: true, - }); - - const fromNode = createVisualizationNode('timer', { - path: 'from', - icon: undefined, - processorName: 'from', - componentName: 'timer', - }); - routeNode.addChild(fromNode); - - const { nodes, edges } = CanvasService.getFlowDiagram(routeNode); - - expect(nodes).toHaveLength(2); - expect(edges).toHaveLength(0); - - const group = nodes[nodes.length - 1]; - expect(group.children).toEqual(['timer-1234']); - expect(group.group).toBeTruthy(); - }); - }); }); diff --git a/packages/ui/src/components/Visualization/Canvas/canvas.service.ts b/packages/ui/src/components/Visualization/Canvas/canvas.service.ts index a6e172a4c..df2ad0959 100644 --- a/packages/ui/src/components/Visualization/Canvas/canvas.service.ts +++ b/packages/ui/src/components/Visualization/Canvas/canvas.service.ts @@ -6,7 +6,6 @@ import { ConcentricLayout, DagreGroupsLayout, DefaultEdge, - EdgeStyle, ForceLayout, Graph, GraphComponent, @@ -19,16 +18,10 @@ import { Visualization, withPanZoom, } from '@patternfly/react-topology'; -import { IVisualizationNode } from '../../../models/visualization/base-visual-entity'; import { CustomGroupWithSelection, CustomNodeWithSelection, NoBendpointsEdge } from '../Custom'; -import { CanvasDefaults } from './canvas.defaults'; -import { CanvasEdge, CanvasNode, CanvasNodesAndEdges, LayoutType } from './canvas.models'; +import { LayoutType } from './canvas.models'; export class CanvasService { - static nodes: CanvasNode[] = []; - static edges: CanvasEdge[] = []; - private static visitedNodes: string[] = []; - static createController(): Visualization { const newController = new Visualization(); @@ -131,108 +124,4 @@ export class CanvasService { return undefined; } } - - static getFlowDiagram(vizNode: IVisualizationNode): CanvasNodesAndEdges { - this.nodes = []; - this.edges = []; - this.visitedNodes = []; - - this.appendNodesAndEdges(vizNode); - - return { nodes: this.nodes, edges: this.edges }; - } - - /** Method for iterating over all the IVisualizationNode and its children using a depth-first algorithm */ - private static appendNodesAndEdges(vizNodeParam: IVisualizationNode): void { - if (this.visitedNodes.includes(vizNodeParam.id)) { - return; - } - - let node: CanvasNode; - - const children = vizNodeParam.getChildren(); - if (vizNodeParam.data.isGroup && children) { - children.forEach((child) => { - this.appendNodesAndEdges(child); - }); - - const containerId = vizNodeParam.id; - node = this.getContainer(containerId, { - label: containerId, - children: children.map((child) => child.id), - parentNode: vizNodeParam.getParentNode()?.id, - data: { vizNode: vizNodeParam }, - }); - } else { - node = this.getCanvasNode(vizNodeParam); - } - - /** Add node */ - this.nodes.push(node); - this.visitedNodes.push(node.id); - - /** Add edges */ - this.edges.push(...this.getEdgesFromVizNode(vizNodeParam)); - } - - private static getCanvasNode(vizNodeParam: IVisualizationNode): CanvasNode { - /** Join the parent if exist to form a group */ - const parentNode = - vizNodeParam.getParentNode()?.getChildren() !== undefined ? vizNodeParam.getParentNode()?.id : undefined; - - return this.getNode(vizNodeParam.id, { - parentNode, - data: { vizNode: vizNodeParam }, - }); - } - - private static getEdgesFromVizNode(vizNodeParam: IVisualizationNode): CanvasEdge[] { - const edges: CanvasEdge[] = []; - - if (vizNodeParam.getNextNode() !== undefined) { - edges.push(this.getEdge(vizNodeParam.id, vizNodeParam.getNextNode()!.id)); - } - - return edges; - } - - private static getContainer( - id: string, - options: { label?: string; children?: string[]; parentNode?: string; data?: CanvasNode['data'] } = {}, - ): CanvasNode { - return { - id, - type: 'group', - group: true, - label: options.label ?? id, - children: options.children ?? [], - parentNode: options.parentNode, - data: options.data, - style: { - padding: CanvasDefaults.DEFAULT_NODE_DIAMETER * 0.8, - }, - }; - } - - private static getNode(id: string, options: { parentNode?: string; data?: CanvasNode['data'] } = {}): CanvasNode { - return { - id, - type: 'node', - parentNode: options.parentNode, - data: options.data, - width: CanvasDefaults.DEFAULT_NODE_DIAMETER, - height: CanvasDefaults.DEFAULT_NODE_DIAMETER, - shape: CanvasDefaults.DEFAULT_NODE_SHAPE, - }; - } - - private static getEdge(source: string, target: string): CanvasEdge { - return { - id: `${source}-to-${target}`, - type: 'edge', - source, - target, - edgeStyle: EdgeStyle.solid, - }; - } } diff --git a/packages/ui/src/components/Visualization/Canvas/flow.service.test.ts b/packages/ui/src/components/Visualization/Canvas/flow.service.test.ts new file mode 100644 index 000000000..25a71e49c --- /dev/null +++ b/packages/ui/src/components/Visualization/Canvas/flow.service.test.ts @@ -0,0 +1,112 @@ +import { createVisualizationNode } from '../../../models/visualization'; +import { BaseVisualCamelEntity } from '../../../models/visualization/base-visual-entity'; +import { FlowService } from './flow.service'; + +describe('FlowService', () => { + beforeEach(() => { + FlowService.nodes = []; + FlowService.edges = []; + }); + + it('should start with an empty nodes array', () => { + expect(FlowService.nodes).toEqual([]); + }); + + it('should start with an empty edges array', () => { + expect(FlowService.edges).toEqual([]); + }); + + describe('getFlowDiagram', () => { + it('should return nodes and edges for a simple VisualizationNode', () => { + const vizNode = createVisualizationNode('node', {}); + + const { nodes, edges } = FlowService.getFlowDiagram(vizNode); + + expect(nodes).toMatchSnapshot(); + expect(edges).toMatchSnapshot(); + }); + + it('should return nodes and edges for a group with children', () => { + const groupVizNode = createVisualizationNode('group', { isGroup: true }); + const child1VizNode = createVisualizationNode('child1', {}); + const child2VizNode = createVisualizationNode('child2', {}); + groupVizNode.addChild(child1VizNode); + groupVizNode.addChild(child2VizNode); + + const { nodes, edges } = FlowService.getFlowDiagram(groupVizNode); + + expect(nodes).toMatchSnapshot(); + expect(edges).toMatchSnapshot(); + }); + + it('should return nodes and edges for a two-nodes VisualizationNode', () => { + const vizNode = createVisualizationNode('node', {}); + const childNode = createVisualizationNode('child', {}); + vizNode.addChild(childNode); + + const { nodes, edges } = FlowService.getFlowDiagram(vizNode); + + expect(nodes).toMatchSnapshot(); + expect(edges).toMatchSnapshot(); + }); + + it('should return nodes and edges for a multiple nodes VisualizationNode', () => { + const vizNode = createVisualizationNode('node', {}); + + const setHeaderNode = createVisualizationNode('set-header', {}); + vizNode.setNextNode(setHeaderNode); + setHeaderNode.setPreviousNode(vizNode); + + const choiceNode = createVisualizationNode('choice', {}); + setHeaderNode.setNextNode(choiceNode); + choiceNode.setPreviousNode(setHeaderNode); + + const directNode = createVisualizationNode('direct', {}); + choiceNode.setNextNode(directNode); + directNode.setPreviousNode(choiceNode); + + const whenNode = createVisualizationNode('when', {}); + choiceNode.addChild(whenNode); + + const otherwiseNode = createVisualizationNode('otherwise', {}); + choiceNode.addChild(otherwiseNode); + + const whenLeafNode = createVisualizationNode('when-leaf', {}); + whenNode.addChild(whenLeafNode); + + const processNode = createVisualizationNode('process', {}); + otherwiseNode.addChild(processNode); + const logNode = createVisualizationNode('log', {}); + processNode.addChild(logNode); + + const { nodes, edges } = FlowService.getFlowDiagram(vizNode); + + expect(nodes).toMatchSnapshot(); + expect(edges).toMatchSnapshot(); + }); + + it('should return a group node for a multiple nodes VisualizationNode with a group', () => { + const routeNode = createVisualizationNode('route', { + entity: { getId: () => 'myId' } as BaseVisualCamelEntity, + isGroup: true, + }); + + const fromNode = createVisualizationNode('timer', { + path: 'from', + icon: undefined, + processorName: 'from', + componentName: 'timer', + }); + routeNode.addChild(fromNode); + + const { nodes, edges } = FlowService.getFlowDiagram(routeNode); + + expect(nodes).toHaveLength(2); + expect(edges).toHaveLength(0); + + const group = nodes[nodes.length - 1]; + expect(group.children).toEqual(['timer-1234']); + expect(group.group).toBeTruthy(); + }); + }); +}); diff --git a/packages/ui/src/components/Visualization/Canvas/flow.service.ts b/packages/ui/src/components/Visualization/Canvas/flow.service.ts new file mode 100644 index 000000000..2b9d5b5f2 --- /dev/null +++ b/packages/ui/src/components/Visualization/Canvas/flow.service.ts @@ -0,0 +1,114 @@ +import { EdgeStyle } from '@patternfly/react-topology'; +import { IVisualizationNode } from '../../../models/visualization/base-visual-entity'; +import { CanvasDefaults } from './canvas.defaults'; +import { CanvasEdge, CanvasNode, CanvasNodesAndEdges } from './canvas.models'; + +export class FlowService { + static nodes: CanvasNode[] = []; + static edges: CanvasEdge[] = []; + private static visitedNodes: string[] = []; + + static getFlowDiagram(vizNode: IVisualizationNode): CanvasNodesAndEdges { + this.nodes = []; + this.edges = []; + this.visitedNodes = []; + + this.appendNodesAndEdges(vizNode); + + return { nodes: this.nodes, edges: this.edges }; + } + + /** Method for iterating over all the IVisualizationNode and its children using a depth-first algorithm */ + private static appendNodesAndEdges(vizNodeParam: IVisualizationNode): void { + if (this.visitedNodes.includes(vizNodeParam.id)) { + return; + } + + let node: CanvasNode; + + const children = vizNodeParam.getChildren(); + if (vizNodeParam.data.isGroup && children) { + children.forEach((child) => { + this.appendNodesAndEdges(child); + }); + + const containerId = vizNodeParam.id; + node = this.getContainer(containerId, { + label: containerId, + children: children.map((child) => child.id), + parentNode: vizNodeParam.getParentNode()?.id, + data: { vizNode: vizNodeParam }, + }); + } else { + node = this.getCanvasNode(vizNodeParam); + } + + /** Add node */ + this.nodes.push(node); + this.visitedNodes.push(node.id); + + /** Add edges */ + this.edges.push(...this.getEdgesFromVizNode(vizNodeParam)); + } + + private static getCanvasNode(vizNodeParam: IVisualizationNode): CanvasNode { + /** Join the parent if exist to form a group */ + const parentNode = + vizNodeParam.getParentNode()?.getChildren() !== undefined ? vizNodeParam.getParentNode()?.id : undefined; + + return this.getNode(vizNodeParam.id, { + parentNode, + data: { vizNode: vizNodeParam }, + }); + } + + private static getEdgesFromVizNode(vizNodeParam: IVisualizationNode): CanvasEdge[] { + const edges: CanvasEdge[] = []; + + if (vizNodeParam.getNextNode() !== undefined) { + edges.push(this.getEdge(vizNodeParam.id, vizNodeParam.getNextNode()!.id)); + } + + return edges; + } + + private static getContainer( + id: string, + options: { label?: string; children?: string[]; parentNode?: string; data?: CanvasNode['data'] } = {}, + ): CanvasNode { + return { + id, + type: 'group', + group: true, + label: options.label ?? id, + children: options.children ?? [], + parentNode: options.parentNode, + data: options.data, + style: { + padding: CanvasDefaults.DEFAULT_NODE_DIAMETER * 0.8, + }, + }; + } + + private static getNode(id: string, options: { parentNode?: string; data?: CanvasNode['data'] } = {}): CanvasNode { + return { + id, + type: 'node', + parentNode: options.parentNode, + data: options.data, + width: CanvasDefaults.DEFAULT_NODE_DIAMETER, + height: CanvasDefaults.DEFAULT_NODE_DIAMETER, + shape: CanvasDefaults.DEFAULT_NODE_SHAPE, + }; + } + + private static getEdge(source: string, target: string): CanvasEdge { + return { + id: `${source}-to-${target}`, + type: 'edge', + source, + target, + edgeStyle: EdgeStyle.solid, + }; + } +} diff --git a/packages/ui/src/tests/nodes-edges.test.ts b/packages/ui/src/tests/nodes-edges.test.ts index 0d76696e1..62bce37db 100644 --- a/packages/ui/src/tests/nodes-edges.test.ts +++ b/packages/ui/src/tests/nodes-edges.test.ts @@ -1,11 +1,11 @@ +import { FlowService } from '../components/Visualization/Canvas/flow.service'; import { CamelRouteResource } from '../models/camel'; -import { CanvasService } from '../components/Visualization/Canvas/canvas.service'; import { camelRouteBranch } from '../stubs/camel-route-branch'; describe('Nodes and Edges', () => { beforeEach(() => { - CanvasService.nodes = []; - CanvasService.edges = []; + FlowService.nodes = []; + FlowService.edges = []; }); it('should generate edges for steps with branches', () => { @@ -13,7 +13,7 @@ describe('Nodes and Edges', () => { const [camelRoute] = camelResource.getVisualEntities(); const rootVizNode = camelRoute.toVizNode(); - const { nodes, edges } = CanvasService.getFlowDiagram(rootVizNode); + const { nodes, edges } = FlowService.getFlowDiagram(rootVizNode); expect(nodes).toMatchSnapshot(); expect(edges).toMatchSnapshot();