You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by ma...@apache.org on 2023/10/20 16:57:17 UTC

[camel-karavan] branch main updated: Topology creates edge between consumer-producer

This is an automated email from the ASF dual-hosted git repository.

marat pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel-karavan.git


The following commit(s) were added to refs/heads/main by this push:
     new 8807c3ef Topology creates edge between consumer-producer
8807c3ef is described below

commit 8807c3efd6761b62a764c7673c8472c2079bb869
Author: Marat Gubaidullin <ma...@talismancloud.io>
AuthorDate: Fri Oct 20 12:57:10 2023 -0400

    Topology creates edge between consumer-producer
---
 karavan-core/src/core/api/CamelDefinitionApiExt.ts |  6 +-
 karavan-core/src/core/api/CamelUtil.ts             | 16 +++--
 karavan-core/src/core/api/TopologyUtils.ts         | 62 ++++++++++++++++--
 karavan-core/src/core/model/TopologyDefinition.ts  | 18 ++++--
 karavan-designer/public/example/demo.camel.yaml    | 74 ++++++++++++++++++----
 karavan-designer/src/topology/CustomNode.tsx       |  7 +-
 karavan-designer/src/topology/TopologyApi.tsx      | 31 +++++++--
 karavan-designer/src/topology/TopologyTab.tsx      |  1 -
 8 files changed, 177 insertions(+), 38 deletions(-)

diff --git a/karavan-core/src/core/api/CamelDefinitionApiExt.ts b/karavan-core/src/core/api/CamelDefinitionApiExt.ts
index 6ec8ffe5..f9372830 100644
--- a/karavan-core/src/core/api/CamelDefinitionApiExt.ts
+++ b/karavan-core/src/core/api/CamelDefinitionApiExt.ts
@@ -690,11 +690,7 @@ export class CamelDefinitionApiExt {
         return [];
     };
 
-    static getParametersValue = (
-        element: CamelElement | undefined,
-        propertyName: string,
-        pathParameter?: boolean,
-    ): any => {
+    static getParametersValue = (element: CamelElement | undefined, propertyName: string, pathParameter?: boolean): any => {
         if (element && (element as any).parameters) {
             return (element as any).parameters[propertyName];
         }
diff --git a/karavan-core/src/core/api/CamelUtil.ts b/karavan-core/src/core/api/CamelUtil.ts
index 7178150f..93544263 100644
--- a/karavan-core/src/core/api/CamelUtil.ts
+++ b/karavan-core/src/core/api/CamelUtil.ts
@@ -159,9 +159,15 @@ export class CamelUtil {
         }
     };
 
-    static getKameletProperties = (element: any): Property[] => {
+    static getKameletProperties = (element: any, requiredOnly: boolean = false): Property[] => {
         const kamelet = CamelUtil.getKamelet(element);
-        return kamelet ? KameletApi.getKameletProperties(kamelet?.metadata.name) : [];
+        const props:Property[] = kamelet ? KameletApi.getKameletProperties(kamelet?.metadata.name) : [];
+        if (requiredOnly) {
+            const required = kamelet?.spec.definition.required;
+            return props.filter(value => required?.includes(value.id));
+        } else {
+            return props;
+        }
     };
 
     static getKameletRequiredParameters = (element: any): string[] => {
@@ -229,11 +235,7 @@ export class CamelUtil {
             if (!CamelUtil.isKameletComponent(element)) {
                 const requiredProperties = CamelUtil.getComponentProperties(element).filter(p => p.required);
                 for (const property of requiredProperties) {
-                    const value = CamelDefinitionApiExt.getParametersValue(
-                        element,
-                        property.name,
-                        property.kind === 'path',
-                    );
+                    const value = CamelDefinitionApiExt.getParametersValue(element, property.name, property.kind === 'path');
                     if (value === undefined || (property.type === 'string' && value.trim().length === 0)) {
                         result[0] = false;
                         result[1].push(`${property.displayName} is required`);
diff --git a/karavan-core/src/core/api/TopologyUtils.ts b/karavan-core/src/core/api/TopologyUtils.ts
index 4f99f295..d3ae1671 100644
--- a/karavan-core/src/core/api/TopologyUtils.ts
+++ b/karavan-core/src/core/api/TopologyUtils.ts
@@ -37,6 +37,8 @@ import {
 import { ComponentApi } from './ComponentApi';
 import { CamelDefinitionApiExt } from './CamelDefinitionApiExt';
 import { CamelDisplayUtil } from './CamelDisplayUtil';
+import { CamelMetadataApi } from '../model/CamelMetadata';
+import { CamelUtil } from './CamelUtil';
 
 const outgoingDefinitions: string[] = ['ToDefinition', 'KameletDefinition', 'ToDynamicDefinition', "PollEnrichDefinition", "EnrichDefinition", "WireTapDefinition", "SagaDefinition"];
 
@@ -58,6 +60,48 @@ export class TopologyUtils {
             (TopologyUtils.isComponentInternal(component.component.label) || TopologyUtils.hasInternalUri(element));
     }
 
+    static getConnectorType = (element: CamelElement): 'component' | 'kamelet' => {
+        return CamelUtil.isKameletComponent(element) ? 'kamelet' : 'component';
+    }
+
+    static cutKameletUriSuffix = (uri: string): string => {
+        if (uri.endsWith("-sink")) {
+            return uri.substring(0, uri.length - 5);
+        } else if (uri.endsWith("-source")) {
+            return uri.substring(0, uri.length - 7);
+        } else if (uri.endsWith("-action")) {
+            return uri.substring(0, uri.length - 7);
+        } else {
+            return uri;
+        }
+    }
+
+    static getUniqueUri = (element: CamelElement): string => {
+        const uri:string = (element as any).uri || '';
+        let result = uri.startsWith("kamelet") ? TopologyUtils.cutKameletUriSuffix(uri).concat(":") : uri.concat(":");
+        const className = element.dslName;
+        if (className === 'FromDefinition' || className === 'ToDefinition') {
+            if (!CamelUtil.isKameletComponent(element)) {
+                const requiredProperties = CamelUtil.getComponentProperties(element).filter(p => p.required);
+                for (const property of requiredProperties) {
+                    const value = CamelDefinitionApiExt.getParametersValue(element, property.name, property.kind === 'path');
+                    if (value !== undefined && property.type === 'string' && value.trim().length > 0) {
+                        result = result + property.name + "=" + value + "&";
+                    }
+                }
+            } else {
+                const requiredProperties = CamelUtil.getKameletProperties(element, true);
+                for (const property of requiredProperties) {
+                    const value = CamelDefinitionApiExt.getParametersValue(element, property.id);
+                    if (value !== undefined && property.type === 'string' && value.trim().length > 0) {
+                        result = result + property.id + "=" + value + "&";
+                    }
+                }
+            }
+        }
+        return result;
+    }
+
     static isComponentInternal = (label: string): boolean => {
         const labels = label.split(",");
         if (labels.includes('core') && (
@@ -143,7 +187,9 @@ export class TopologyUtils {
                 const id = 'incoming-' + r.id;
                 const title = CamelDisplayUtil.getTitle(r.from);
                 const type = TopologyUtils.isElementInternalComponent(r.from) ? 'internal' : 'external';
-                return new TopologyIncomingNode(id, type, r.id, title, filename, r.from);
+                const connectorType = TopologyUtils.getConnectorType(r.from);
+                const uniqueUri = TopologyUtils.getUniqueUri(r.from);
+                return new TopologyIncomingNode(id, type, connectorType, r.id, title, filename, r.from, uniqueUri);
             }) || [];
             result.push(...routeElements)
         })
@@ -177,7 +223,9 @@ export class TopologyUtils {
                     const id = 'outgoing-' + route.id + '-' + e.id;
                     const title = CamelDisplayUtil.getTitle(e);
                     const type = TopologyUtils.isElementInternalComponent(e) ? 'internal' : 'external';
-                    result.push(new TopologyOutgoingNode(id, type, route.id, title, filename, e));
+                    const connectorType = TopologyUtils.getConnectorType(e);
+                    const uniqueUri = TopologyUtils.getUniqueUri(e);
+                    result.push(new TopologyOutgoingNode(id, type, connectorType, route.id, title, filename, e, uniqueUri));
                 })
             })
 
@@ -213,8 +261,6 @@ export class TopologyUtils {
         return result;
     }
 
-
-
     static getNodeIdByUriAndName(tins: TopologyIncomingNode[], uri: string, name: string): string | undefined {
         if (uri && name) {
             const node =  tins
@@ -242,6 +288,14 @@ export class TopologyUtils {
         }
     }
 
+    static getNodeIdByUniqueUri(tins: TopologyIncomingNode[], uniqueUri: string): string | undefined {
+        const node =  tins
+            .filter(r => r.uniqueUri === uniqueUri).at(0);
+        if (node) {
+            return node.id;
+        }
+    }
+
     static getRouteIdByUri(tins: TopologyIncomingNode[], uri: string): string | undefined {
         const parts = uri.split(":");
         if (parts.length > 1) {
diff --git a/karavan-core/src/core/model/TopologyDefinition.ts b/karavan-core/src/core/model/TopologyDefinition.ts
index 9b3cfe40..f238f5b9 100644
--- a/karavan-core/src/core/model/TopologyDefinition.ts
+++ b/karavan-core/src/core/model/TopologyDefinition.ts
@@ -39,18 +39,23 @@ export class TopologyRestNode {
 export class TopologyIncomingNode {
     id: string;
     type: 'internal' | 'external';
+    connectorType: 'component' | 'kamelet';
     routeId: string;
     title: string;
     fileName: string;
     from: FromDefinition;
-    
-    constructor(id: string, type: 'internal' | 'external', routeId: string, title: string, fileName: string, from: FromDefinition) {
+    uniqueUri?: string;
+
+
+    constructor(id: string, type: "internal" | "external", connectorType: "component" | "kamelet", routeId: string, title: string, fileName: string, from: FromDefinition, uniqueUri: string) {
         this.id = id;
         this.type = type;
+        this.connectorType = connectorType;
         this.routeId = routeId;
         this.title = title;
         this.fileName = fileName;
         this.from = from;
+        this.uniqueUri = uniqueUri;
     }
 }
 
@@ -75,17 +80,22 @@ export class TopologyRouteNode {
 export class TopologyOutgoingNode {
     id: string;
     type: 'internal' | 'external';
+    connectorType: 'component' | 'kamelet';
     routeId: string;
     title: string;
     fileName: string;
     step: CamelElement;
+    uniqueUri?: string;
 
-    constructor(id: string, type: 'internal' | 'external', routeId: string, title: string, fileName: string, step: CamelElement) {
+
+    constructor(id: string, type: "internal" | "external", connectorType: "component" | "kamelet", routeId: string, title: string, fileName: string, step: CamelElement, uniqueUri: string) {
         this.id = id;
-        this.type = type
+        this.type = type;
+        this.connectorType = connectorType;
         this.routeId = routeId;
         this.title = title;
         this.fileName = fileName;
         this.step = step;
+        this.uniqueUri = uniqueUri;
     }
 }
\ No newline at end of file
diff --git a/karavan-designer/public/example/demo.camel.yaml b/karavan-designer/public/example/demo.camel.yaml
index 3c682878..69ff1495 100644
--- a/karavan-designer/public/example/demo.camel.yaml
+++ b/karavan-designer/public/example/demo.camel.yaml
@@ -1,20 +1,72 @@
 - route:
-    id: route-fbf9
+    id: route-605c
+    from:
+      uri: activemq
+      id: from-3131
+      parameters:
+        destinationName: test
+      steps:
+        - marshal:
+            id: marshal-1452
+        - log:
+            message: ${body}
+            id: log-c8e7
+- route:
+    id: route-e4bb
     from:
-      uri: kamelet:beer-source
-      id: from-9d5b
+      uri: kamelet:timer-source
+      id: from-dd68
+      parameters:
+        message: '1111'
       steps:
         - to:
             uri: activemq
-            id: to-0fd2
+            id: to-4fca
+            parameters:
+              destinationName: test
+- route:
+    id: route-0a30
+    from:
+      uri: amqp
+      id: from-ca7d
+      parameters:
+        destinationName: hello
+      steps:
+        - log:
+            message: ${body}
+            id: log-d0f4
         - to:
-            uri: asterisk
-            id: to-b9ca
+            uri: kamelet:kafka-not-secured-sink
+            id: to-c86f
+            parameters:
+              topic: topic1
+              bootstrapServers: localhost:9092
+- route:
+    id: route-e39c
+    from:
+      uri: kamelet:kafka-not-secured-source
+      id: from-d419
+      parameters:
+        bootstrapServers: localhost:9092
+        topic: topic1
+      steps:
         - to:
-            uri: aws2-ec2
-            id: to-3c6a
+            uri: direct
+            id: to-7401
+            parameters:
+              name: hello
 - route:
-    id: route-605c
+    id: route-be79
     from:
-      uri: activemq
-      id: from-3131
\ No newline at end of file
+      uri: kamelet:kafka-not-secured-source
+      id: from-27e8
+      parameters:
+        bootstrapServers: localhost:9092
+        topic: topic2
+- route:
+    id: hello
+    from:
+      uri: direct
+      id: from-db43
+      parameters:
+        name: hello
diff --git a/karavan-designer/src/topology/CustomNode.tsx b/karavan-designer/src/topology/CustomNode.tsx
index fb517ae9..d683d04f 100644
--- a/karavan-designer/src/topology/CustomNode.tsx
+++ b/karavan-designer/src/topology/CustomNode.tsx
@@ -43,11 +43,16 @@ function getIcon(data: any) {
 const CustomNode: React.FC<any> = observer(({ element, ...rest }) => {
 
     const data = element.getData();
+    const badge:string = data.badge?.substring(0,1).toUpperCase();
 
     return (
         <DefaultNode
+            badge={badge}
+            showStatusDecorator
             className="common-node"
-            element={element} {...rest}
+            scaleLabel={false}
+            element={element}
+            {...rest}
         >
             {getIcon(data)}
         </DefaultNode>
diff --git a/karavan-designer/src/topology/TopologyApi.tsx b/karavan-designer/src/topology/TopologyApi.tsx
index 44ff57a2..c60c197b 100644
--- a/karavan-designer/src/topology/TopologyApi.tsx
+++ b/karavan-designer/src/topology/TopologyApi.tsx
@@ -38,9 +38,9 @@ import {
     TopologyRestNode,
     TopologyRouteNode
 } from "karavan-core/lib/model/TopologyDefinition";
-import CustomGroup from "./CustomGroup";
 import CustomEdge from "./CustomEdge";
 import {IntegrationFile} from "./TopologyStore";
+import CustomGroup from "./CustomGroup";
 
 const NODE_DIAMETER = 60;
 
@@ -62,7 +62,7 @@ export function getIncomingNodes(tins: TopologyIncomingNode[]): NodeModel[] {
             status: NodeStatus.default,
             data: {
                 isAlternate: false,
-                badge: tin.type,
+                badge: tin.connectorType,
                 icon: 'element',
                 type: 'step',
                 step: tin.from,
@@ -110,7 +110,7 @@ export function getOutgoingNodes(tons: TopologyOutgoingNode[]): NodeModel[] {
                 icon: 'element',
                 type: 'step',
                 step: tin.step,
-                badge: tin.type,
+                badge: tin.connectorType,
                 fileName: tin.fileName
             }
         }
@@ -146,6 +146,26 @@ export function getOutgoingEdges(tons: TopologyOutgoingNode[]): EdgeModel[] {
     });
 }
 
+export function getExternalEdges(tons: TopologyOutgoingNode[], tins: TopologyIncomingNode[]): EdgeModel[] {
+    const result: EdgeModel[]= [];
+    tons.filter(ton => ton.type === 'external').forEach((ton, index) => {
+        const uniqueUri = ton.uniqueUri;
+        if (uniqueUri) {
+            const target = TopologyUtils.getNodeIdByUniqueUri(tins, uniqueUri);
+            const node: EdgeModel = {
+                id: 'external-' + ton.id + '-' + index,
+                type: 'edge',
+                source: ton.id,
+                target: target,
+                edgeStyle: EdgeStyle.dotted,
+                animationSpeed: EdgeAnimationSpeed.slow
+            }
+            if (target) result.push(node);
+        }
+    });
+    return result;
+}
+
 export function getRestNodes(tins: TopologyRestNode[]): NodeModel[] {
     return tins.map(tin => {
         return {
@@ -217,8 +237,8 @@ export function getModel(files: IntegrationFile[]): Model {
     const nodes: NodeModel[] = [];
     const groups: NodeModel[] = troutes.map(r => {
         const children = [r.id]
-        children.push(... tins.filter(i => i.routeId === r.routeId && i.type === 'external').map(i => i.id));
-        children.push(... tons.filter(i => i.routeId === r.routeId && i.type === 'external').map(i => i.id));
+        children.push(...tins.filter(i => i.routeId === r.routeId && i.type === 'external').map(i => i.id));
+        children.push(...tons.filter(i => i.routeId === r.routeId && i.type === 'external').map(i => i.id));
         return   {
             id: 'group-' + r.routeId,
             children: children,
@@ -242,6 +262,7 @@ export function getModel(files: IntegrationFile[]): Model {
     edges.push(...getOutgoingEdges(tons));
     edges.push(...getRestEdges(trestns, tins));
     edges.push(...getInternalEdges(tons, tins));
+    edges.push(...getExternalEdges(tons,tins));
 
     return {nodes: nodes, edges: edges, graph: {id: 'g1', type: 'graph', layout: 'Dagre'}};
 }
diff --git a/karavan-designer/src/topology/TopologyTab.tsx b/karavan-designer/src/topology/TopologyTab.tsx
index 306ca8fb..520bf8d2 100644
--- a/karavan-designer/src/topology/TopologyTab.tsx
+++ b/karavan-designer/src/topology/TopologyTab.tsx
@@ -68,7 +68,6 @@ export function TopologyTab (props: Props) {
     }
 
     const controller = React.useMemo(() => {
-        console.log(props.files)
         const model = getModel(props.files);
         const newController = new Visualization();
         newController.registerLayoutFactory((_, graph) => new DagreLayout(graph));