balibabu
		
	commited on
		
		
					Commit 
							
							·
						
						dee7bd1
	
1
								Parent(s):
							
							b61ed5f
								
feat: test buildNodesAndEdgesFromDSLComponents (#940)
Browse files### What problem does this PR solve?
 feat: test buildNodesAndEdgesFromDSLComponents #918
### Type of change
- [x] New Feature (non-breaking change which adds functionality)
- web/jest-setup.ts +2 -0
- web/jest.config.ts +33 -0
- web/package-lock.json +0 -0
- web/package.json +11 -1
- web/src/pages/flow/canvas/index.tsx +9 -33
- web/src/pages/flow/mock.tsx +36 -2
- web/src/pages/flow/utils.test.ts +30 -0
- web/src/pages/flow/utils.ts +70 -19
- web/tsconfig.json +1 -1
    	
        web/jest-setup.ts
    ADDED
    
    | @@ -0,0 +1,2 @@ | |
|  | |
|  | 
|  | |
| 1 | 
            +
            import '@testing-library/jest-dom';
         | 
| 2 | 
            +
            import 'umi/test-setup';
         | 
    	
        web/jest.config.ts
    ADDED
    
    | @@ -0,0 +1,33 @@ | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | 
|  | |
| 1 | 
            +
            import { Config, configUmiAlias, createConfig } from 'umi/test';
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            export default async () => {
         | 
| 4 | 
            +
              return (await configUmiAlias({
         | 
| 5 | 
            +
                ...createConfig({
         | 
| 6 | 
            +
                  target: 'browser',
         | 
| 7 | 
            +
                  jsTransformer: 'esbuild',
         | 
| 8 | 
            +
                  // config opts for esbuild , it will pass to esbuild directly
         | 
| 9 | 
            +
                  jsTransformerOpts: { jsx: 'automatic' },
         | 
| 10 | 
            +
                }),
         | 
| 11 | 
            +
                setupFilesAfterEnv: ['<rootDir>/jest-setup.ts'],
         | 
| 12 | 
            +
                collectCoverageFrom: [
         | 
| 13 | 
            +
                  '**/*.{ts,tsx,js,jsx}',
         | 
| 14 | 
            +
                  '!.umi/**',
         | 
| 15 | 
            +
                  '!.umi-test/**',
         | 
| 16 | 
            +
                  '!.umi-production/**',
         | 
| 17 | 
            +
                  '!.umirc.{js,ts}',
         | 
| 18 | 
            +
                  '!.umirc.*.{js,ts}',
         | 
| 19 | 
            +
                  '!jest.config.{js,ts}',
         | 
| 20 | 
            +
                  '!coverage/**',
         | 
| 21 | 
            +
                  '!dist/**',
         | 
| 22 | 
            +
                  '!config/**',
         | 
| 23 | 
            +
                  '!mock/**',
         | 
| 24 | 
            +
                ],
         | 
| 25 | 
            +
                // if you require some es-module npm package, please uncomment below line and insert your package name
         | 
| 26 | 
            +
                // transformIgnorePatterns: ['node_modules/(?!.*(lodash-es|your-es-pkg-name)/)']
         | 
| 27 | 
            +
                coverageThreshold: {
         | 
| 28 | 
            +
                  global: {
         | 
| 29 | 
            +
                    lines: 1,
         | 
| 30 | 
            +
                  },
         | 
| 31 | 
            +
                },
         | 
| 32 | 
            +
              })) as Config.InitialOptions;
         | 
| 33 | 
            +
            };
         | 
    	
        web/package-lock.json
    CHANGED
    
    | The diff for this file is too large to render. 
		See raw diff | 
|  | 
    	
        web/package.json
    CHANGED
    
    | @@ -7,7 +7,8 @@ | |
| 7 | 
             
                "postinstall": "umi setup",
         | 
| 8 | 
             
                "lint": "umi lint --eslint-only",
         | 
| 9 | 
             
                "setup": "umi setup",
         | 
| 10 | 
            -
                "start": "npm run dev"
         | 
|  | |
| 11 | 
             
              },
         | 
| 12 | 
             
              "dependencies": {
         | 
| 13 | 
             
                "@ant-design/icons": "^5.2.6",
         | 
| @@ -18,6 +19,7 @@ | |
| 18 | 
             
                "antd": "^5.12.7",
         | 
| 19 | 
             
                "axios": "^1.6.3",
         | 
| 20 | 
             
                "classnames": "^2.5.1",
         | 
|  | |
| 21 | 
             
                "dayjs": "^1.11.10",
         | 
| 22 | 
             
                "eventsource-parser": "^1.1.2",
         | 
| 23 | 
             
                "i18next": "^23.7.16",
         | 
| @@ -45,20 +47,28 @@ | |
| 45 | 
             
              },
         | 
| 46 | 
             
              "devDependencies": {
         | 
| 47 | 
             
                "@react-dev-inspector/umi4-plugin": "^2.0.1",
         | 
|  | |
|  | |
|  | |
|  | |
| 48 | 
             
                "@types/lodash": "^4.14.202",
         | 
| 49 | 
             
                "@types/react": "^18.0.33",
         | 
| 50 | 
             
                "@types/react-copy-to-clipboard": "^5.0.7",
         | 
| 51 | 
             
                "@types/react-dom": "^18.0.11",
         | 
| 52 | 
             
                "@types/react-syntax-highlighter": "^15.5.11",
         | 
|  | |
| 53 | 
             
                "@types/uuid": "^9.0.8",
         | 
| 54 | 
             
                "@types/webpack-env": "^1.18.4",
         | 
| 55 | 
             
                "@umijs/lint": "^4.1.1",
         | 
| 56 | 
             
                "@umijs/plugins": "^4.1.0",
         | 
| 57 | 
             
                "cross-env": "^7.0.3",
         | 
|  | |
|  | |
| 58 | 
             
                "prettier": "^3.2.4",
         | 
| 59 | 
             
                "prettier-plugin-organize-imports": "^3.2.4",
         | 
| 60 | 
             
                "prettier-plugin-packagejson": "^2.4.9",
         | 
| 61 | 
             
                "react-dev-inspector": "^2.0.1",
         | 
|  | |
| 62 | 
             
                "typescript": "^5.0.3",
         | 
| 63 | 
             
                "umi-plugin-icons": "^0.1.1"
         | 
| 64 | 
             
              }
         | 
|  | |
| 7 | 
             
                "postinstall": "umi setup",
         | 
| 8 | 
             
                "lint": "umi lint --eslint-only",
         | 
| 9 | 
             
                "setup": "umi setup",
         | 
| 10 | 
            +
                "start": "npm run dev",
         | 
| 11 | 
            +
                "test": "jest --no-cache --coverage"
         | 
| 12 | 
             
              },
         | 
| 13 | 
             
              "dependencies": {
         | 
| 14 | 
             
                "@ant-design/icons": "^5.2.6",
         | 
|  | |
| 19 | 
             
                "antd": "^5.12.7",
         | 
| 20 | 
             
                "axios": "^1.6.3",
         | 
| 21 | 
             
                "classnames": "^2.5.1",
         | 
| 22 | 
            +
                "dagre": "^0.8.5",
         | 
| 23 | 
             
                "dayjs": "^1.11.10",
         | 
| 24 | 
             
                "eventsource-parser": "^1.1.2",
         | 
| 25 | 
             
                "i18next": "^23.7.16",
         | 
|  | |
| 47 | 
             
              },
         | 
| 48 | 
             
              "devDependencies": {
         | 
| 49 | 
             
                "@react-dev-inspector/umi4-plugin": "^2.0.1",
         | 
| 50 | 
            +
                "@testing-library/jest-dom": "^6.4.5",
         | 
| 51 | 
            +
                "@testing-library/react": "^15.0.7",
         | 
| 52 | 
            +
                "@types/dagre": "^0.7.52",
         | 
| 53 | 
            +
                "@types/jest": "^29.5.12",
         | 
| 54 | 
             
                "@types/lodash": "^4.14.202",
         | 
| 55 | 
             
                "@types/react": "^18.0.33",
         | 
| 56 | 
             
                "@types/react-copy-to-clipboard": "^5.0.7",
         | 
| 57 | 
             
                "@types/react-dom": "^18.0.11",
         | 
| 58 | 
             
                "@types/react-syntax-highlighter": "^15.5.11",
         | 
| 59 | 
            +
                "@types/testing-library__jest-dom": "^6.0.0",
         | 
| 60 | 
             
                "@types/uuid": "^9.0.8",
         | 
| 61 | 
             
                "@types/webpack-env": "^1.18.4",
         | 
| 62 | 
             
                "@umijs/lint": "^4.1.1",
         | 
| 63 | 
             
                "@umijs/plugins": "^4.1.0",
         | 
| 64 | 
             
                "cross-env": "^7.0.3",
         | 
| 65 | 
            +
                "jest": "^29.7.0",
         | 
| 66 | 
            +
                "jest-environment-jsdom": "^29.7.0",
         | 
| 67 | 
             
                "prettier": "^3.2.4",
         | 
| 68 | 
             
                "prettier-plugin-organize-imports": "^3.2.4",
         | 
| 69 | 
             
                "prettier-plugin-packagejson": "^2.4.9",
         | 
| 70 | 
             
                "react-dev-inspector": "^2.0.1",
         | 
| 71 | 
            +
                "ts-node": "^10.9.2",
         | 
| 72 | 
             
                "typescript": "^5.0.3",
         | 
| 73 | 
             
                "umi-plugin-icons": "^0.1.1"
         | 
| 74 | 
             
              }
         | 
    	
        web/src/pages/flow/canvas/index.tsx
    CHANGED
    
    | @@ -8,7 +8,6 @@ import ReactFlow, { | |
| 8 | 
             
              OnConnect,
         | 
| 9 | 
             
              OnEdgesChange,
         | 
| 10 | 
             
              OnNodesChange,
         | 
| 11 | 
            -
              Position,
         | 
| 12 | 
             
              addEdge,
         | 
| 13 | 
             
              applyEdgeChanges,
         | 
| 14 | 
             
              applyNodeChanges,
         | 
| @@ -19,47 +18,24 @@ import { NodeContextMenu, useHandleNodeContextMenu } from './context-menu'; | |
| 19 |  | 
| 20 | 
             
            import FlowDrawer from '../flow-drawer';
         | 
| 21 | 
             
            import { useHandleDrop, useShowDrawer } from '../hooks';
         | 
|  | |
|  | |
| 22 | 
             
            import { TextUpdaterNode } from './node';
         | 
| 23 |  | 
| 24 | 
             
            const nodeTypes = { textUpdater: TextUpdaterNode };
         | 
| 25 |  | 
| 26 | 
            -
            const initialNodes = [
         | 
| 27 | 
            -
              {
         | 
| 28 | 
            -
                sourcePosition: Position.Left,
         | 
| 29 | 
            -
                targetPosition: Position.Right,
         | 
| 30 | 
            -
                id: 'node-1',
         | 
| 31 | 
            -
                type: 'textUpdater',
         | 
| 32 | 
            -
                position: { x: 400, y: 100 },
         | 
| 33 | 
            -
                data: { label: 123 },
         | 
| 34 | 
            -
              },
         | 
| 35 | 
            -
              {
         | 
| 36 | 
            -
                sourcePosition: Position.Right,
         | 
| 37 | 
            -
                targetPosition: Position.Left,
         | 
| 38 | 
            -
                id: '1',
         | 
| 39 | 
            -
                data: { label: 'Hello' },
         | 
| 40 | 
            -
                position: { x: 0, y: 50 },
         | 
| 41 | 
            -
                type: 'input',
         | 
| 42 | 
            -
              },
         | 
| 43 | 
            -
              {
         | 
| 44 | 
            -
                sourcePosition: Position.Right,
         | 
| 45 | 
            -
                targetPosition: Position.Left,
         | 
| 46 | 
            -
                id: '2',
         | 
| 47 | 
            -
                data: { label: 'World' },
         | 
| 48 | 
            -
                position: { x: 200, y: 50 },
         | 
| 49 | 
            -
              },
         | 
| 50 | 
            -
            ];
         | 
| 51 | 
            -
             | 
| 52 | 
            -
            const initialEdges = [
         | 
| 53 | 
            -
              { id: '1-2', source: '1', target: '2', label: 'to the', type: 'step' },
         | 
| 54 | 
            -
            ];
         | 
| 55 | 
            -
             | 
| 56 | 
             
            interface IProps {
         | 
| 57 | 
             
              sideWidth: number;
         | 
| 58 | 
             
            }
         | 
| 59 |  | 
| 60 | 
             
            function FlowCanvas({ sideWidth }: IProps) {
         | 
| 61 | 
            -
              const  | 
| 62 | 
            -
             | 
|  | |
|  | |
|  | |
|  | |
|  | |
| 63 | 
             
              const { ref, menu, onNodeContextMenu, onPaneClick } =
         | 
| 64 | 
             
                useHandleNodeContextMenu(sideWidth);
         | 
| 65 | 
             
              const { drawerVisible, hideDrawer, showDrawer } = useShowDrawer();
         | 
|  | |
| 8 | 
             
              OnConnect,
         | 
| 9 | 
             
              OnEdgesChange,
         | 
| 10 | 
             
              OnNodesChange,
         | 
|  | |
| 11 | 
             
              addEdge,
         | 
| 12 | 
             
              applyEdgeChanges,
         | 
| 13 | 
             
              applyNodeChanges,
         | 
|  | |
| 18 |  | 
| 19 | 
             
            import FlowDrawer from '../flow-drawer';
         | 
| 20 | 
             
            import { useHandleDrop, useShowDrawer } from '../hooks';
         | 
| 21 | 
            +
            import { initialEdges, initialNodes } from '../mock';
         | 
| 22 | 
            +
            import { getLayoutedElements } from '../utils';
         | 
| 23 | 
             
            import { TextUpdaterNode } from './node';
         | 
| 24 |  | 
| 25 | 
             
            const nodeTypes = { textUpdater: TextUpdaterNode };
         | 
| 26 |  | 
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
| 27 | 
             
            interface IProps {
         | 
| 28 | 
             
              sideWidth: number;
         | 
| 29 | 
             
            }
         | 
| 30 |  | 
| 31 | 
             
            function FlowCanvas({ sideWidth }: IProps) {
         | 
| 32 | 
            +
              const { nodes: layoutedNodes, edges: layoutedEdges } = getLayoutedElements(
         | 
| 33 | 
            +
                initialNodes,
         | 
| 34 | 
            +
                initialEdges,
         | 
| 35 | 
            +
                'LR',
         | 
| 36 | 
            +
              );
         | 
| 37 | 
            +
              const [nodes, setNodes] = useState<Node[]>(layoutedNodes);
         | 
| 38 | 
            +
              const [edges, setEdges] = useState<Edge[]>(layoutedEdges);
         | 
| 39 | 
             
              const { ref, menu, onNodeContextMenu, onPaneClick } =
         | 
| 40 | 
             
                useHandleNodeContextMenu(sideWidth);
         | 
| 41 | 
             
              const { drawerVisible, hideDrawer, showDrawer } = useShowDrawer();
         | 
    	
        web/src/pages/flow/mock.tsx
    CHANGED
    
    | @@ -3,6 +3,7 @@ import { | |
| 3 | 
             
              RocketOutlined,
         | 
| 4 | 
             
              SendOutlined,
         | 
| 5 | 
             
            } from '@ant-design/icons';
         | 
|  | |
| 6 |  | 
| 7 | 
             
            export const componentList = [
         | 
| 8 | 
             
              { name: 'Begin', icon: <SendOutlined />, description: '' },
         | 
| @@ -10,6 +11,39 @@ export const componentList = [ | |
| 10 | 
             
              { name: 'Generate', icon: <MergeCellsOutlined />, description: '' },
         | 
| 11 | 
             
            ];
         | 
| 12 |  | 
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
| 13 | 
             
            export const dsl = {
         | 
| 14 | 
             
              components: {
         | 
| 15 | 
             
                begin: {
         | 
| @@ -17,8 +51,8 @@ export const dsl = { | |
| 17 | 
             
                    component_name: 'Begin',
         | 
| 18 | 
             
                    params: {},
         | 
| 19 | 
             
                  },
         | 
| 20 | 
            -
                  downstream: ['Answer:China'],
         | 
| 21 | 
            -
                  upstream: [],
         | 
| 22 | 
             
                },
         | 
| 23 | 
             
                'Answer:China': {
         | 
| 24 | 
             
                  obj: {
         | 
|  | |
| 3 | 
             
              RocketOutlined,
         | 
| 4 | 
             
              SendOutlined,
         | 
| 5 | 
             
            } from '@ant-design/icons';
         | 
| 6 | 
            +
            import { Position } from 'reactflow';
         | 
| 7 |  | 
| 8 | 
             
            export const componentList = [
         | 
| 9 | 
             
              { name: 'Begin', icon: <SendOutlined />, description: '' },
         | 
|  | |
| 11 | 
             
              { name: 'Generate', icon: <MergeCellsOutlined />, description: '' },
         | 
| 12 | 
             
            ];
         | 
| 13 |  | 
| 14 | 
            +
            export const initialNodes = [
         | 
| 15 | 
            +
              {
         | 
| 16 | 
            +
                sourcePosition: Position.Left,
         | 
| 17 | 
            +
                targetPosition: Position.Right,
         | 
| 18 | 
            +
                id: 'node-1',
         | 
| 19 | 
            +
                type: 'textUpdater',
         | 
| 20 | 
            +
                position: { x: 0, y: 0 },
         | 
| 21 | 
            +
                // position: { x: 400, y: 100 },
         | 
| 22 | 
            +
                data: { label: 123 },
         | 
| 23 | 
            +
              },
         | 
| 24 | 
            +
              {
         | 
| 25 | 
            +
                sourcePosition: Position.Right,
         | 
| 26 | 
            +
                targetPosition: Position.Left,
         | 
| 27 | 
            +
                id: '1',
         | 
| 28 | 
            +
                data: { label: 'Hello' },
         | 
| 29 | 
            +
                position: { x: 0, y: 0 },
         | 
| 30 | 
            +
                // position: { x: 0, y: 50 },
         | 
| 31 | 
            +
                type: 'input',
         | 
| 32 | 
            +
              },
         | 
| 33 | 
            +
              {
         | 
| 34 | 
            +
                sourcePosition: Position.Right,
         | 
| 35 | 
            +
                targetPosition: Position.Left,
         | 
| 36 | 
            +
                id: '2',
         | 
| 37 | 
            +
                data: { label: 'World' },
         | 
| 38 | 
            +
                position: { x: 0, y: 0 },
         | 
| 39 | 
            +
                // position: { x: 200, y: 50 },
         | 
| 40 | 
            +
              },
         | 
| 41 | 
            +
            ];
         | 
| 42 | 
            +
             | 
| 43 | 
            +
            export const initialEdges = [
         | 
| 44 | 
            +
              { id: '1-2', source: '1', target: '2', label: 'to the', type: 'step' },
         | 
| 45 | 
            +
            ];
         | 
| 46 | 
            +
             | 
| 47 | 
             
            export const dsl = {
         | 
| 48 | 
             
              components: {
         | 
| 49 | 
             
                begin: {
         | 
|  | |
| 51 | 
             
                    component_name: 'Begin',
         | 
| 52 | 
             
                    params: {},
         | 
| 53 | 
             
                  },
         | 
| 54 | 
            +
                  downstream: ['Answer:China'], // other edge target is downstream, edge source is current node id
         | 
| 55 | 
            +
                  upstream: [], // edge source is upstream, edge target is current node id
         | 
| 56 | 
             
                },
         | 
| 57 | 
             
                'Answer:China': {
         | 
| 58 | 
             
                  obj: {
         | 
    	
        web/src/pages/flow/utils.test.ts
    ADDED
    
    | @@ -0,0 +1,30 @@ | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | 
|  | |
| 1 | 
            +
            import { dsl } from './mock';
         | 
| 2 | 
            +
            import { buildNodesAndEdgesFromDSLComponents } from './utils';
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            test('buildNodesAndEdgesFromDSLComponents', () => {
         | 
| 5 | 
            +
              const { edges, nodes } = buildNodesAndEdgesFromDSLComponents(dsl.components);
         | 
| 6 | 
            +
             | 
| 7 | 
            +
              expect(nodes.length).toEqual(4);
         | 
| 8 | 
            +
              expect(edges.length).toEqual(4);
         | 
| 9 | 
            +
             | 
| 10 | 
            +
              expect(edges).toEqual(
         | 
| 11 | 
            +
                expect.arrayContaining([
         | 
| 12 | 
            +
                  expect.objectContaining({
         | 
| 13 | 
            +
                    source: 'begin',
         | 
| 14 | 
            +
                    target: 'Answer:China',
         | 
| 15 | 
            +
                  }),
         | 
| 16 | 
            +
                  expect.objectContaining({
         | 
| 17 | 
            +
                    source: 'Answer:China',
         | 
| 18 | 
            +
                    target: 'Retrieval:China',
         | 
| 19 | 
            +
                  }),
         | 
| 20 | 
            +
                  expect.objectContaining({
         | 
| 21 | 
            +
                    source: 'Retrieval:China',
         | 
| 22 | 
            +
                    target: 'Generate:China',
         | 
| 23 | 
            +
                  }),
         | 
| 24 | 
            +
                  expect.objectContaining({
         | 
| 25 | 
            +
                    source: 'Generate:China',
         | 
| 26 | 
            +
                    target: 'Answer:China',
         | 
| 27 | 
            +
                  }),
         | 
| 28 | 
            +
                ]),
         | 
| 29 | 
            +
              );
         | 
| 30 | 
            +
            });
         | 
    	
        web/src/pages/flow/utils.ts
    CHANGED
    
    | @@ -1,10 +1,32 @@ | |
| 1 | 
             
            import { DSLComponents } from '@/interfaces/database/flow';
         | 
|  | |
| 2 | 
             
            import { Edge, Node, Position } from 'reactflow';
         | 
| 3 | 
             
            import { v4 as uuidv4 } from 'uuid';
         | 
| 4 |  | 
| 5 | 
            -
             | 
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
| 6 | 
             
              const nodes: Node[] = [];
         | 
| 7 | 
            -
               | 
| 8 |  | 
| 9 | 
             
              Object.entries(data).forEach(([key, value]) => {
         | 
| 10 | 
             
                const downstream = [...value.downstream];
         | 
| @@ -23,22 +45,51 @@ export const buildNodesFromDSLComponents = (data: DSLComponents) => { | |
| 23 | 
             
                  targetPosition: Position.Right,
         | 
| 24 | 
             
                });
         | 
| 25 |  | 
| 26 | 
            -
                 | 
| 27 | 
            -
                 | 
| 28 | 
            -
             | 
| 29 | 
            -
             | 
| 30 | 
            -
             | 
| 31 | 
            -
             | 
| 32 | 
            -
             | 
| 33 | 
            -
             | 
| 34 | 
            -
             | 
| 35 | 
            -
             | 
| 36 | 
            -
             | 
| 37 | 
            -
             | 
| 38 | 
            -
             | 
| 39 | 
            -
             | 
| 40 | 
            -
             | 
| 41 | 
            -
             | 
| 42 | 
            -
             | 
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
| 43 | 
             
              });
         | 
|  | |
|  | |
| 44 | 
             
            };
         | 
|  | |
| 1 | 
             
            import { DSLComponents } from '@/interfaces/database/flow';
         | 
| 2 | 
            +
            import dagre from 'dagre';
         | 
| 3 | 
             
            import { Edge, Node, Position } from 'reactflow';
         | 
| 4 | 
             
            import { v4 as uuidv4 } from 'uuid';
         | 
| 5 |  | 
| 6 | 
            +
            const buildEdges = (
         | 
| 7 | 
            +
              operatorIds: string[],
         | 
| 8 | 
            +
              currentId: string,
         | 
| 9 | 
            +
              allEdges: Edge[],
         | 
| 10 | 
            +
              isUpstream = false,
         | 
| 11 | 
            +
            ) => {
         | 
| 12 | 
            +
              operatorIds.forEach((cur) => {
         | 
| 13 | 
            +
                const source = isUpstream ? cur : currentId;
         | 
| 14 | 
            +
                const target = isUpstream ? currentId : cur;
         | 
| 15 | 
            +
                if (!allEdges.some((e) => e.source === source && e.target === target)) {
         | 
| 16 | 
            +
                  allEdges.push({
         | 
| 17 | 
            +
                    id: uuidv4(),
         | 
| 18 | 
            +
                    label: '',
         | 
| 19 | 
            +
                    type: 'step',
         | 
| 20 | 
            +
                    source: source,
         | 
| 21 | 
            +
                    target: target,
         | 
| 22 | 
            +
                  });
         | 
| 23 | 
            +
                }
         | 
| 24 | 
            +
              });
         | 
| 25 | 
            +
            };
         | 
| 26 | 
            +
             | 
| 27 | 
            +
            export const buildNodesAndEdgesFromDSLComponents = (data: DSLComponents) => {
         | 
| 28 | 
             
              const nodes: Node[] = [];
         | 
| 29 | 
            +
              let edges: Edge[] = [];
         | 
| 30 |  | 
| 31 | 
             
              Object.entries(data).forEach(([key, value]) => {
         | 
| 32 | 
             
                const downstream = [...value.downstream];
         | 
|  | |
| 45 | 
             
                  targetPosition: Position.Right,
         | 
| 46 | 
             
                });
         | 
| 47 |  | 
| 48 | 
            +
                buildEdges(upstream, key, edges, true);
         | 
| 49 | 
            +
                buildEdges(downstream, key, edges, false);
         | 
| 50 | 
            +
              });
         | 
| 51 | 
            +
             | 
| 52 | 
            +
              return { nodes, edges };
         | 
| 53 | 
            +
            };
         | 
| 54 | 
            +
             | 
| 55 | 
            +
            const dagreGraph = new dagre.graphlib.Graph();
         | 
| 56 | 
            +
            dagreGraph.setDefaultEdgeLabel(() => ({}));
         | 
| 57 | 
            +
             | 
| 58 | 
            +
            const nodeWidth = 172;
         | 
| 59 | 
            +
            const nodeHeight = 36;
         | 
| 60 | 
            +
             | 
| 61 | 
            +
            export const getLayoutedElements = (
         | 
| 62 | 
            +
              nodes: Node[],
         | 
| 63 | 
            +
              edges: Edge[],
         | 
| 64 | 
            +
              direction = 'TB',
         | 
| 65 | 
            +
            ) => {
         | 
| 66 | 
            +
              const isHorizontal = direction === 'LR';
         | 
| 67 | 
            +
              dagreGraph.setGraph({ rankdir: direction });
         | 
| 68 | 
            +
             | 
| 69 | 
            +
              nodes.forEach((node) => {
         | 
| 70 | 
            +
                dagreGraph.setNode(node.id, { width: nodeWidth, height: nodeHeight });
         | 
| 71 | 
            +
              });
         | 
| 72 | 
            +
             | 
| 73 | 
            +
              edges.forEach((edge) => {
         | 
| 74 | 
            +
                dagreGraph.setEdge(edge.source, edge.target);
         | 
| 75 | 
            +
              });
         | 
| 76 | 
            +
             | 
| 77 | 
            +
              dagre.layout(dagreGraph);
         | 
| 78 | 
            +
             | 
| 79 | 
            +
              nodes.forEach((node) => {
         | 
| 80 | 
            +
                const nodeWithPosition = dagreGraph.node(node.id);
         | 
| 81 | 
            +
                node.targetPosition = isHorizontal ? Position.Left : Position.Top;
         | 
| 82 | 
            +
                node.sourcePosition = isHorizontal ? Position.Right : Position.Bottom;
         | 
| 83 | 
            +
             | 
| 84 | 
            +
                // We are shifting the dagre node position (anchor=center center) to the top left
         | 
| 85 | 
            +
                // so it matches the React Flow node anchor point (top left).
         | 
| 86 | 
            +
                node.position = {
         | 
| 87 | 
            +
                  x: nodeWithPosition.x - nodeWidth / 2,
         | 
| 88 | 
            +
                  y: nodeWithPosition.y - nodeHeight / 2,
         | 
| 89 | 
            +
                };
         | 
| 90 | 
            +
             | 
| 91 | 
            +
                return node;
         | 
| 92 | 
             
              });
         | 
| 93 | 
            +
             | 
| 94 | 
            +
              return { nodes, edges };
         | 
| 95 | 
             
            };
         | 
    	
        web/tsconfig.json
    CHANGED
    
    | @@ -1,6 +1,6 @@ | |
| 1 | 
             
            {
         | 
| 2 | 
             
              "extends": "./src/.umi/tsconfig.json",
         | 
| 3 | 
             
              "@@/*": [
         | 
| 4 | 
            -
                "src/.umi/*"
         | 
| 5 | 
             
              ],
         | 
| 6 | 
             
            }
         | 
|  | |
| 1 | 
             
            {
         | 
| 2 | 
             
              "extends": "./src/.umi/tsconfig.json",
         | 
| 3 | 
             
              "@@/*": [
         | 
| 4 | 
            +
                "src/.umi/*",
         | 
| 5 | 
             
              ],
         | 
| 6 | 
             
            }
         |