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/26 00:05:04 UTC

[camel-karavan] branch main updated (56ed9809 -> f1128303)

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

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


    from 56ed9809 #905
     new 211dd994 Kamelet Sink in Source Kamelet Selector for #315
     new 2d8216b4 Kamelet Source in Sink Kamelet for #315
     new fd3432e9 nodePrefixId for #315
     new e208f02b Remove WARN IPROTO000002 from Infinispan
     new cce1dd34 Kamelet Editor in App for #315
     new 62ba6f14 Kamelet Editor in App for #315
     new f1128303 Readme update

The 7 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 README.md                                          |  34 ++--
 images/karavan-clouds-large.png                    | Bin 321879 -> 321382 bytes
 images/karavan-dashboard.png                       | Bin 0 -> 68462 bytes
 images/karavan-right.png                           | Bin 0 -> 321382 bytes
 .../src/core/model/IntegrationDefinition.ts        |  10 +-
 karavan-core/src/core/model/KameletModels.ts       |   2 +-
 .../example/aws-cloudwatch-sink.kamelet.yaml       | 185 +++++++++++++++++++++
 karavan-designer/src/App.tsx                       |   3 +-
 karavan-designer/src/designer/KaravanDesigner.tsx  |   2 -
 .../src/designer/icons/ComponentIcons.tsx          |  23 ++-
 .../src/designer/icons/KaravanIcons.tsx            |   3 +-
 .../src/designer/route/DslConnections.tsx          |   2 +
 karavan-designer/src/designer/route/DslElement.tsx |  14 +-
 .../src/designer/route/DslSelector.tsx             |   3 +-
 .../src/designer/route/RouteDesigner.tsx           |  53 +++---
 .../src/designer/route/useRouteDesignerHook.tsx    |  42 ++++-
 karavan-designer/src/designer/utils/CamelUi.tsx    |  12 +-
 .../karavan/infinispan/InfinispanService.java      |  16 +-
 .../src/main/webui/src/api/ProjectService.ts       |  12 ++
 .../main/webui/src/designer/KaravanDesigner.tsx    |   2 -
 .../webui/src/designer/icons/ComponentIcons.tsx    |  23 ++-
 .../main/webui/src/designer/icons/KaravanIcons.tsx |   3 +-
 .../webui/src/designer/route/DslConnections.tsx    |   2 +
 .../main/webui/src/designer/route/DslElement.tsx   |  14 +-
 .../main/webui/src/designer/route/DslSelector.tsx  |   3 +-
 .../webui/src/designer/route/RouteDesigner.tsx     |  53 +++---
 .../src/designer/route/useRouteDesignerHook.tsx    |  42 ++++-
 .../src/main/webui/src/designer/utils/CamelUi.tsx  |  12 +-
 .../webui/src/knowledgebase/KnowledgebasePage.tsx  |  22 ++-
 .../main/webui/src/knowledgebase/eip/EipModal.tsx  |  35 ++--
 .../src/main/webui/src/main/useMainHook.tsx        |  10 +-
 .../webui/src/project/ImageDownloadToolbar.tsx     |   8 +-
 .../src/main/webui/src/project/file/FileEditor.tsx |   2 +-
 .../webui/src/project/files/CreateFileModal.tsx    |  50 ++++--
 .../src/main/webui/src/project/files/FilesTab.tsx  |   2 +-
 .../main/webui/src/project/files/FilesToolbar.tsx  |  29 +++-
 .../src/project/topology/ProjectTopologyTab.tsx    |   2 +-
 37 files changed, 592 insertions(+), 138 deletions(-)
 create mode 100644 images/karavan-dashboard.png
 create mode 100644 images/karavan-right.png
 create mode 100644 karavan-designer/public/example/aws-cloudwatch-sink.kamelet.yaml


[camel-karavan] 07/07: Readme update

Posted by ma...@apache.org.
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

commit f11283032399a8581bd4b8a4e9c0f2f9b2c7071e
Author: Marat Gubaidullin <ma...@talismancloud.io>
AuthorDate: Wed Oct 25 20:04:53 2023 -0400

    Readme update
---
 README.md                       |  34 ++++++++++++++++++++++------------
 images/karavan-clouds-large.png | Bin 321879 -> 321382 bytes
 images/karavan-dashboard.png    | Bin 0 -> 68462 bytes
 images/karavan-right.png        | Bin 0 -> 321382 bytes
 4 files changed, 22 insertions(+), 12 deletions(-)

diff --git a/README.md b/README.md
index 3b3a446d..9e99cc32 100644
--- a/README.md
+++ b/README.md
@@ -6,31 +6,41 @@
 ![Typescript](https://img.shields.io/badge/-Typescript-blue.svg?style=for-the-badge&logo=typescript)
 ![License](https://img.shields.io/badge/License-Apache-blue.svg?style=for-the-badge&logo=apache)
 
-![karavan-logo](images/karavan-logo-dark.png#gh-dark-mode-only)
-![karavan-logo](images/karavan-logo-light.png#gh-light-mode-only)
+**Apache Camel Karavan** is an **Open-Source Low-code Data Integration Platform** 
 
-Karavan is an Integration Toolkit for Apache Camel, which makes integration easy and fun through the visualization of pipelines, integration with runtimes and package, image build and deploy to Docker or Kubernetes out-of-the-box.
+<img align="right" width="400" src="images/karavan-clouds-large.png">
 
-[![Introduction](images/introduction.png)](https://www.youtube.com/watch?v=RA8sH3AH8Gg)
 
-Integrations could be created using visual designer that includes Enterprise Integration Patterns DSL, REST API and Beans builder, all Camel Kamelets and Components. Karavan uses YAML to read/write integrations. Integrations could be run directly from Karavan or they could be exported in the Maven project with preconfigured Camel Quarkus, Camel Spring-Boot or Camel Main runtime. Integration project output is a runnable uber-jar or an OCI image for local environment or a deployed applicat [...]
+It simplifies the Apache Camel experience and accelerates developer performance by visually designing and rapidly deploying integration microservices.
+
+* Full power of Apache Camel through Visual Tool
+* From Low-code use-cases to Pro-code projects
+* Powered by 10+ years of Community Intelligence
+
 
 ## Features
-### Visual Designer
+### Designer
 * Enterprise Integration Patterns
+* 300+ Integration Components 
 * REST API designer with OpenAPI to REST DSL generator
-* 300+ Components 
-* Custom Java code snippets
+* YAML for Integration and Java custom code
 
-### All in Git
-* Integration routes (YAML, Java)
+### Deployer
+* Simple shell scripts for build and deploy
 * Configuration (application.properties, docker-compose.yaml, deployment.yaml, etc)
-* Customizable build scripts 
+* Predefined customizable scripts for different platforms
 
-### Target deployment
+### Dashboards
+* Monitoring running integration microservices
+* View live logs during development
+* Tracing Exchange Data 
+
+### Supported platforms
 * [Docker](docs/WEB_DOCKER.md)
 * [Kubernetes](docs/WEB_KUBERNETES.md)
 * [Openshift](docs/WEB_OPENSHIFT.md)
 
 ## Documentation
 [Karavan documentation](docs/README.md)
+
+If you haven't done so yet, please be sure to download Karavan and give it a try. We're excited to receive your feedback and learn about your experiences!
diff --git a/images/karavan-clouds-large.png b/images/karavan-clouds-large.png
index 2fb0b93e..b350b44d 100644
Binary files a/images/karavan-clouds-large.png and b/images/karavan-clouds-large.png differ
diff --git a/images/karavan-dashboard.png b/images/karavan-dashboard.png
new file mode 100644
index 00000000..79d54234
Binary files /dev/null and b/images/karavan-dashboard.png differ
diff --git a/images/karavan-right.png b/images/karavan-right.png
new file mode 100644
index 00000000..b350b44d
Binary files /dev/null and b/images/karavan-right.png differ


[camel-karavan] 04/07: Remove WARN IPROTO000002 from Infinispan

Posted by ma...@apache.org.
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

commit e208f02b84cd41cce47d4f269dd30045044fa076
Author: Marat Gubaidullin <ma...@talismancloud.io>
AuthorDate: Mon Oct 23 19:31:12 2023 -0400

    Remove WARN IPROTO000002 from Infinispan
---
 .../camel/karavan/infinispan/InfinispanService.java      | 16 +++++++++-------
 1 file changed, 9 insertions(+), 7 deletions(-)

diff --git a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/infinispan/InfinispanService.java b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/infinispan/InfinispanService.java
index bfacd49b..30440c1c 100644
--- a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/infinispan/InfinispanService.java
+++ b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/infinispan/InfinispanService.java
@@ -16,12 +16,8 @@
  */
 package org.apache.camel.karavan.infinispan;
 
-import io.smallrye.mutiny.tuples.Tuple2;
 import jakarta.enterprise.inject.Default;
 import jakarta.inject.Singleton;
-import jakarta.transaction.NotSupportedException;
-import jakarta.transaction.SystemException;
-import jakarta.transaction.TransactionManager;
 import org.apache.camel.karavan.infinispan.model.*;
 import org.eclipse.microprofile.config.inject.ConfigProperty;
 import org.eclipse.microprofile.faulttolerance.Retry;
@@ -35,6 +31,9 @@ import org.infinispan.client.hotrod.configuration.ClientIntelligence;
 import org.infinispan.client.hotrod.configuration.ConfigurationBuilder;
 import org.infinispan.commons.configuration.StringConfiguration;
 import org.infinispan.commons.marshall.ProtoStreamMarshaller;
+import org.infinispan.protostream.ProtobufUtil;
+import org.infinispan.protostream.SerializationContext;
+import org.infinispan.protostream.config.Configuration;
 import org.infinispan.query.dsl.QueryFactory;
 import org.jboss.logging.Logger;
 
@@ -84,7 +83,10 @@ public class InfinispanService implements HealthCheck {
     void start() throws Exception {
         LOGGER.info("InfinispanService is starting in remote mode");
 
-        ProtoStreamMarshaller marshaller = new ProtoStreamMarshaller();
+        Configuration.Builder cfgBuilder = Configuration.builder().setLogOutOfSequenceWrites(false);
+        SerializationContext ctx = ProtobufUtil.newSerializationContext(cfgBuilder.build());
+
+        ProtoStreamMarshaller marshaller = new ProtoStreamMarshaller(ctx);
         marshaller.register(new KaravanSchemaImpl());
 
         ConfigurationBuilder builder = new ConfigurationBuilder();
@@ -154,9 +156,9 @@ public class InfinispanService implements HealthCheck {
 
     public ProjectFile getProjectFile(String projectId, String filename) {
         QueryFactory queryFactory = Search.getQueryFactory(files);
-        List<ProjectFile> list = queryFactory.<ProjectFile>create("FROM karavan.ProjectFile WHERE projectId = :projectId AND name = :name")
-                .setParameter("projectId", projectId)
+        List<ProjectFile> list = queryFactory.<ProjectFile>create("FROM karavan.ProjectFile WHERE name = :name AND projectId = :projectId")
                 .setParameter("name", filename)
+                .setParameter("projectId", projectId)
                 .execute().list();
         return list.size() > 0 ? list.get(0) : null;
     }


[camel-karavan] 06/07: Kamelet Editor in App for #315

Posted by ma...@apache.org.
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

commit 62ba6f147b9f89b417192a800cba46cff2118cd2
Author: Marat Gubaidullin <ma...@talismancloud.io>
AuthorDate: Mon Oct 23 20:39:22 2023 -0400

    Kamelet Editor in App for #315
---
 .../src/main/webui/src/api/ProjectService.ts       | 12 +++++++++
 .../webui/src/knowledgebase/KnowledgebasePage.tsx  | 22 +++++++++++++++-
 .../src/main/webui/src/main/useMainHook.tsx        | 10 ++------
 .../webui/src/project/ImageDownloadToolbar.tsx     |  8 +++---
 .../webui/src/project/files/CreateFileModal.tsx    |  1 -
 .../main/webui/src/project/files/FilesToolbar.tsx  | 29 +++++++++++++++++++++-
 6 files changed, 67 insertions(+), 15 deletions(-)

diff --git a/karavan-web/karavan-app/src/main/webui/src/api/ProjectService.ts b/karavan-web/karavan-app/src/main/webui/src/api/ProjectService.ts
index 7f3f2128..5e074888 100644
--- a/karavan-web/karavan-app/src/main/webui/src/api/ProjectService.ts
+++ b/karavan-web/karavan-app/src/main/webui/src/api/ProjectService.ts
@@ -29,6 +29,7 @@ import {
 } from './ProjectStore';
 import {ProjectEventBus} from './ProjectEventBus';
 import {EventBus} from "../designer/utils/EventBus";
+import {KameletApi} from "karavan-core/lib/api/KameletApi";
 
 export class ProjectService {
 
@@ -135,6 +136,17 @@ export class ProjectService {
         });
     }
 
+    public static reloadKamelets() {
+        KaravanApi.getKamelets(yamls => {
+            const kamelets: string[] = [];
+            yamls.split("\n---\n").map(c => c.trim()).forEach(z => kamelets.push(z));
+            KameletApi.saveKamelets(kamelets, true);
+        })
+        KaravanApi.getCustomKameletNames(names => {
+            KameletApi.saveCustomKameletNames(names);
+        })
+    }
+
     public static saveFile(file: ProjectFile, active: boolean) {
         KaravanApi.postProjectFile(file, res => {
             if (res.status === 200) {
diff --git a/karavan-web/karavan-app/src/main/webui/src/knowledgebase/KnowledgebasePage.tsx b/karavan-web/karavan-app/src/main/webui/src/knowledgebase/KnowledgebasePage.tsx
index 02c6b641..35368b54 100644
--- a/karavan-web/karavan-app/src/main/webui/src/knowledgebase/KnowledgebasePage.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/knowledgebase/KnowledgebasePage.tsx
@@ -16,7 +16,21 @@
  */
 import React, {useState} from 'react';
 import '../designer/karavan.css';
-import {Flex, FlexItem, PageSection, Switch, Tab, Tabs, Text, TextContent, TextInput, Toolbar, ToolbarContent, ToolbarItem} from "@patternfly/react-core";
+import {
+    Button,
+    Flex,
+    FlexItem,
+    PageSection,
+    Switch,
+    Tab,
+    Tabs,
+    Text,
+    TextContent,
+    TextInput,
+    Toolbar,
+    ToolbarContent,
+    ToolbarItem
+} from "@patternfly/react-core";
 import {MainToolbar} from "../designer/MainToolbar";
 import {KameletsTab} from "./kamelets/KameletsTab";
 import {EipTab} from "./eip/EipTab";
@@ -24,6 +38,7 @@ import {ComponentsTab} from "./components/ComponentsTab";
 
 interface Props {
     dark: boolean,
+    onKameletsReload? (): void;
 }
 
 export const KnowledgebasePage = (props: Props) => {
@@ -41,6 +56,11 @@ export const KnowledgebasePage = (props: Props) => {
     function getTools() {
         return <Toolbar id="toolbar-group-types">
             <ToolbarContent>
+                {tab === 'kamelets' && <ToolbarItem>
+                    <Button
+                        onClick={(_event) => props.onKameletsReload?.()}
+                    >Reload</Button>
+                </ToolbarItem>}
                 {tab === 'kamelets' && <ToolbarItem>
                     <Switch
                         label="Custom only"
diff --git a/karavan-web/karavan-app/src/main/webui/src/main/useMainHook.tsx b/karavan-web/karavan-app/src/main/webui/src/main/useMainHook.tsx
index c0433632..a11c87c9 100644
--- a/karavan-web/karavan-app/src/main/webui/src/main/useMainHook.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/main/useMainHook.tsx
@@ -23,6 +23,7 @@ import {AppConfig, ContainerStatus} from "../api/ProjectModels";
 import {useAppConfigStore, useStatusesStore} from "../api/ProjectStore";
 import {InfrastructureAPI} from "../designer/utils/InfrastructureAPI";
 import {shallow} from "zustand/shallow";
+import {ProjectService} from "../api/ProjectService";
 
 export function useMainHook () {
 
@@ -51,14 +52,7 @@ export function useMainHook () {
 
     async function updateKamelets(): Promise<void> {
         await new Promise(resolve => {
-            KaravanApi.getKamelets(yamls => {
-                const kamelets: string[] = [];
-                yamls.split("\n---\n").map(c => c.trim()).forEach(z => kamelets.push(z));
-                KameletApi.saveKamelets(kamelets, true);
-            })
-            KaravanApi.getCustomKameletNames(names => {
-                KameletApi.saveCustomKameletNames(names);
-            })
+            ProjectService.reloadKamelets();
         });
     }
 
diff --git a/karavan-web/karavan-app/src/main/webui/src/project/ImageDownloadToolbar.tsx b/karavan-web/karavan-app/src/main/webui/src/project/ImageDownloadToolbar.tsx
index 82b5e6f8..3316b008 100644
--- a/karavan-web/karavan-app/src/main/webui/src/project/ImageDownloadToolbar.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/project/ImageDownloadToolbar.tsx
@@ -55,10 +55,10 @@ export function ImageDownloadToolbar() {
             <ToolbarContent>
                 {isIntegration() &&
                     <FlexItem>
-                        <Tooltip content="Download image" position={"bottom-end"}>
-                            <Button size="sm" variant="control" icon={<DownloadImageIcon/>}
-                                    onClick={e => downloadImage()}/>
-                        </Tooltip>
+                        {/*<Tooltip content="Download image" position={"bottom-end"}>*/}
+                        {/*    <Button size="sm" variant="control" icon={<DownloadImageIcon/>}*/}
+                        {/*            onClick={e => downloadImage()}/>*/}
+                        {/*</Tooltip>*/}
                     </FlexItem>
                 }
             </ToolbarContent>
diff --git a/karavan-web/karavan-app/src/main/webui/src/project/files/CreateFileModal.tsx b/karavan-web/karavan-app/src/main/webui/src/project/files/CreateFileModal.tsx
index 1221dc38..60723466 100644
--- a/karavan-web/karavan-app/src/main/webui/src/project/files/CreateFileModal.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/project/files/CreateFileModal.tsx
@@ -71,7 +71,6 @@ export function CreateFileModal (props: Props) {
             const integration = Integration.createNew(kameletName, 'kamelet');
             const meta:MetadataLabels = new MetadataLabels({"camel.apache.org/kamelet.type": kameletType});
             integration.metadata.labels = meta;
-            console.log(integration);
             return CamelDefinitionYaml.integrationToYaml(integration);
         } else {
             return '';
diff --git a/karavan-web/karavan-app/src/main/webui/src/project/files/FilesToolbar.tsx b/karavan-web/karavan-app/src/main/webui/src/project/files/FilesToolbar.tsx
index e73a5ace..9acb08b9 100644
--- a/karavan-web/karavan-app/src/main/webui/src/project/files/FilesToolbar.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/project/files/FilesToolbar.tsx
@@ -18,7 +18,16 @@ import React, {useEffect, useState} from 'react';
 import {
     Button,
     Flex,
-    FlexItem, Form, FormGroup, FormHelperText, Label, Modal, ModalVariant, TextInput, Tooltip, TooltipPosition,
+    FlexItem,
+    Form,
+    FormGroup,
+    FormHelperText,
+    Label,
+    Modal,
+    ModalVariant,
+    TextInput,
+    Tooltip,
+    TooltipPosition,
 } from '@patternfly/react-core';
 import '../../designer/karavan.css';
 import UploadIcon from "@patternfly/react-icons/dist/esm/icons/upload-icon";
@@ -27,6 +36,7 @@ import {useFilesStore, useFileStore, useProjectStore} from "../../api/ProjectSto
 import {shallow} from "zustand/shallow";
 import {ProjectService} from "../../api/ProjectService";
 import PushIcon from "@patternfly/react-icons/dist/esm/icons/code-branch-icon";
+import RefreshIcon from "@patternfly/react-icons/dist/esm/icons/sync-alt-icon";
 
 export function FileToolbar () {
 
@@ -108,7 +118,24 @@ export function FileToolbar () {
         )
     }
 
+    function isKameletsProject(): boolean {
+        return project.projectId === 'kamelets';
+    }
+
     return <Flex className="toolbar" direction={{default: "row"}} justifyContent={{default: "justifyContentFlexEnd"}}>
+        {isKameletsProject() && <FlexItem align={{default: "alignLeft"}} flex={{default: "flex_3"}}>
+            <Tooltip content="Load Custom Kamelets to Library" position={TooltipPosition.right}>
+                <Button size="sm"
+                        variant={"secondary"}
+                        className="project-button"
+                        icon={<RefreshIcon/>}
+                        onClick={() => {
+                            ProjectService.reloadKamelets();
+                        }}>
+                    Load
+                </Button>
+            </Tooltip>
+        </FlexItem>}
         <FlexItem>{getLastUpdatePanel()}</FlexItem>
         <FlexItem>
             <Tooltip content="Commit and push to git" position={"bottom-end"}>


[camel-karavan] 05/07: Kamelet Editor in App for #315

Posted by ma...@apache.org.
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

commit cce1dd34b98e62a12afa7499ee8ff18ee6de4902
Author: Marat Gubaidullin <ma...@talismancloud.io>
AuthorDate: Mon Oct 23 20:10:47 2023 -0400

    Kamelet Editor in App for #315
---
 .../src/core/model/IntegrationDefinition.ts        | 10 ++--
 .../main/webui/src/designer/KaravanDesigner.tsx    |  2 -
 .../webui/src/designer/icons/ComponentIcons.tsx    | 23 +++++++++-
 .../main/webui/src/designer/icons/KaravanIcons.tsx |  3 +-
 .../webui/src/designer/route/DslConnections.tsx    |  2 +
 .../main/webui/src/designer/route/DslElement.tsx   | 14 +++++-
 .../main/webui/src/designer/route/DslSelector.tsx  |  3 +-
 .../webui/src/designer/route/RouteDesigner.tsx     | 53 ++++++++++++++--------
 .../src/designer/route/useRouteDesignerHook.tsx    | 42 +++++++++++++++--
 .../src/main/webui/src/designer/utils/CamelUi.tsx  | 12 ++++-
 .../main/webui/src/knowledgebase/eip/EipModal.tsx  | 35 ++++++--------
 .../src/main/webui/src/project/file/FileEditor.tsx |  2 +-
 .../webui/src/project/files/CreateFileModal.tsx    | 51 +++++++++++++++++----
 .../src/main/webui/src/project/files/FilesTab.tsx  |  2 +-
 .../src/project/topology/ProjectTopologyTab.tsx    |  2 +-
 15 files changed, 186 insertions(+), 70 deletions(-)

diff --git a/karavan-core/src/core/model/IntegrationDefinition.ts b/karavan-core/src/core/model/IntegrationDefinition.ts
index fb1c11f3..18d01dc8 100644
--- a/karavan-core/src/core/model/IntegrationDefinition.ts
+++ b/karavan-core/src/core/model/IntegrationDefinition.ts
@@ -73,8 +73,10 @@ export class Spec {
     }
 }
 
+export type KameletTypes =  "sink" | "source" | "action";
+
 export class MetadataLabels {
-    "camel.apache.org/kamelet.type": "sink" | "source" | "action" = 'source'
+    "camel.apache.org/kamelet.type": KameletTypes = 'source'
 
     public constructor(init?: Partial<MetadataLabels>) {
         Object.assign(this, init);
@@ -82,10 +84,10 @@ export class MetadataLabels {
 }
 
 export class MetadataAnnotations {
-    "camel.apache.org/kamelet.support.level:": string = 'Preview';
+    "camel.apache.org/kamelet.support.level": string = 'Preview';
     "camel.apache.org/catalog.version": string = '';
-    "camel.apache.org/kamelet.icon": string = '';
-    "camel.apache.org/provider": string = '';
+    "camel.apache.org/kamelet.icon": string = "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='%23000000' viewBox='0 0 32 32' id='icon'%3E%3Cdefs%3E%3Cstyle%3E.cls-1%7Bfill:none;%7D%3C/style%3E%3C/defs%3E%3Ctitle%3Eapplication%3C/title%3E%3Cpath d='M16,18H6a2,2,0,0,1-2-2V6A2,2,0,0,1,6,4H16a2,2,0,0,1,2,2V16A2,2,0,0,1,16,18ZM6,6V16H16V6Z' transform='translate(0 0)'/%3E%3Cpath d='M26,12v4H22V12h4m0-2H22a2,2,0,0,0-2,2v4a2,2,0,0,0,2,2h4a2,2,0,0,0,2-2V12a2,2,0,0,0-2-2Z' tran [...]
+    "camel.apache.org/provider": string = 'Custom';
     "camel.apache.org/kamelet.group": string = '';
     "camel.apache.org/kamelet.namespace": string = '';
 
diff --git a/karavan-web/karavan-app/src/main/webui/src/designer/KaravanDesigner.tsx b/karavan-web/karavan-app/src/main/webui/src/designer/KaravanDesigner.tsx
index 4b7b3c7a..b1af73c5 100644
--- a/karavan-web/karavan-app/src/main/webui/src/designer/KaravanDesigner.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/designer/KaravanDesigner.tsx
@@ -24,8 +24,6 @@ import {
     Tabs,
     TabTitleIcon,
     TabTitleText,
-    Tooltip,
-    TooltipPosition,
 } from '@patternfly/react-core';
 import './karavan.css';
 import {RouteDesigner} from "./route/RouteDesigner";
diff --git a/karavan-web/karavan-app/src/main/webui/src/designer/icons/ComponentIcons.tsx b/karavan-web/karavan-app/src/main/webui/src/designer/icons/ComponentIcons.tsx
index a05f5e70..bda35d53 100644
--- a/karavan-web/karavan-app/src/main/webui/src/designer/icons/ComponentIcons.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/designer/icons/ComponentIcons.tsx
@@ -1165,7 +1165,28 @@ export function ApiIcon() {
     );
 }
 
-export function MonitoringIcon() {
+export function KameletIcon() {
+    return (
+        <svg
+            className="icon" id="icon"
+            xmlns="http://www.w3.org/2000/svg"
+            viewBox="0 0 32 32"
+        >
+            <title>{"application"}</title>
+            <path d="M16 18H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2v10a2 2 0 0 1-2 2ZM6 6v10h10V6ZM26 12v4h-4v-4h4m0-2h-4a2 2 0 0 0-2 2v4a2 2 0 0 0 2 2h4a2 2 0 0 0 2-2v-4a2 2 0 0 0-2-2ZM26 22v4h-4v-4h4m0-2h-4a2 2 0 0 0-2 2v4a2 2 0 0 0 2 2h4a2 2 0 0 0 2-2v-4a2 2 0 0 0-2-2ZM16 22v4h-4v-4h4m0-2h-4a2 2 0 0 0-2 2v4a2 2 0 0 0 2 2h4a2 2 0 0 0 2-2v-4a2 2 0 0 0-2-2Z" />
+            <path
+                d="M0 0h32v32H0z"
+                data-name="&lt;Transparent Rectangle&gt;"
+                style={{
+                    fill: "none",
+                }}
+            />
+        </svg>
+    )
+}
+
+
+    export function MonitoringIcon() {
     return (
         <svg
             xmlns="http://www.w3.org/2000/svg"
diff --git a/karavan-web/karavan-app/src/main/webui/src/designer/icons/KaravanIcons.tsx b/karavan-web/karavan-app/src/main/webui/src/designer/icons/KaravanIcons.tsx
index b7165b93..b0b8042f 100644
--- a/karavan-web/karavan-app/src/main/webui/src/designer/icons/KaravanIcons.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/designer/icons/KaravanIcons.tsx
@@ -260,7 +260,7 @@ export function CamelIcon(props?: (JSX.IntrinsicAttributes & React.SVGProps<SVGS
     );
 }
 
-export function getDesignerIcon(icon: string) {
+export function getDesignerIcon(icon: string): React.JSX.Element {
     if (icon === 'kamelet') return (
         <svg
             className="top-icon" id="icon"
@@ -429,6 +429,7 @@ export function getDesignerIcon(icon: string) {
             <rect id="_Transparent_Rectangle_" data-name="&lt;Transparent Rectangle&gt;" className="cls-1" width="32"
                   height="32" transform="translate(0 32) rotate(-90)"/>
         </svg>)
+    return <></>;
 }
 
 
diff --git a/karavan-web/karavan-app/src/main/webui/src/designer/route/DslConnections.tsx b/karavan-web/karavan-app/src/main/webui/src/designer/route/DslConnections.tsx
index de6a0060..a636339e 100644
--- a/karavan-web/karavan-app/src/main/webui/src/designer/route/DslConnections.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/designer/route/DslConnections.tsx
@@ -63,6 +63,7 @@ export function DslConnections() {
             .filter(pos => ["FromDefinition"].includes(pos.step.dslName))
             .filter(pos => !TopologyUtils.isElementInternalComponent(pos.step))
             .filter(pos => !(pos.step.dslName === 'FromDefinition' && TopologyUtils.hasInternalUri(pos.step)))
+            .filter(pos => !(pos.step.dslName === 'FromDefinition' && (pos.step as any).uri === 'kamelet:source'))
             .sort((pos1: DslPosition, pos2: DslPosition) => {
                 const y1 = pos1.headerRect.y + pos1.headerRect.height / 2;
                 const y2 = pos2.headerRect.y + pos2.headerRect.height / 2;
@@ -142,6 +143,7 @@ export function DslConnections() {
             .filter(pos => pos.step.dslName === 'ToDefinition' && !CamelUi.isActionKamelet(pos.step) && !TopologyUtils.isElementInternalComponent(pos.step))
             .filter(pos => !(outgoingDefinitions.includes(pos.step.dslName) && TopologyUtils.hasInternalUri(pos.step)))
             .filter(pos => pos.step.dslName !== 'SagaDefinition')
+            .filter(pos => !CamelUi.isKameletSink(pos.step))
             .sort((pos1: DslPosition, pos2: DslPosition) => {
                 const y1 = pos1.headerRect.y + pos1.headerRect.height / 2;
                 const y2 = pos2.headerRect.y + pos2.headerRect.height / 2;
diff --git a/karavan-web/karavan-app/src/main/webui/src/designer/route/DslElement.tsx b/karavan-web/karavan-app/src/main/webui/src/designer/route/DslElement.tsx
index 305b7859..9e4a8c70 100644
--- a/karavan-web/karavan-app/src/main/webui/src/designer/route/DslElement.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/designer/route/DslElement.tsx
@@ -42,7 +42,7 @@ interface Props {
 export function DslElement(props: Props) {
 
     const headerRef = React.useRef<HTMLDivElement>(null);
-    const {selectElement, moveElement, onShowDeleteConfirmation, openSelector} = useRouteDesignerHook();
+    const {selectElement, moveElement, onShowDeleteConfirmation, openSelector, isKamelet, isSourceKamelet, isActionKamelet} = useRouteDesignerHook();
 
     const [integration] = useIntegrationStore((s) => [s.integration, s.setIntegration], shallow)
 
@@ -241,9 +241,19 @@ export function DslElement(props: Props) {
         )
     }
 
+    function getHeaderText(step: CamelElement): string {
+        if (isKamelet() && step.dslName === 'ToDefinition' && (step as any).uri === 'kamelet:sink') {
+            return "Sink";
+        } else if (isKamelet() && step.dslName === 'FromDefinition' && (step as any).uri === 'kamelet:source') {
+            return "Source";
+        } else {
+            return (step as any).description ? (step as any).description : CamelUi.getElementTitle(props.step);
+        }
+    }
+
     function getHeaderTextWithTooltip(step: CamelElement) {
         const checkRequired = CamelUtil.checkRequired(step);
-        const title = (step as any).description ? (step as any).description : CamelUi.getElementTitle(props.step);
+        const title = getHeaderText(step);
         let className = hasWideChildrenElement() ? "text text-right" : "text text-bottom";
         if (!checkRequired[0]) className = className + " header-text-required";
         if (checkRequired[0]) {
diff --git a/karavan-web/karavan-app/src/main/webui/src/designer/route/DslSelector.tsx b/karavan-web/karavan-app/src/main/webui/src/designer/route/DslSelector.tsx
index 8f815536..a80525ba 100644
--- a/karavan-web/karavan-app/src/main/webui/src/designer/route/DslSelector.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/designer/route/DslSelector.tsx
@@ -24,7 +24,7 @@ import {
 import '../karavan.css';
 import {CamelUi} from "../utils/CamelUi";
 import {DslMetaModel} from "../utils/DslMetaModel";
-import {useDesignerStore, useSelectorStore} from "../DesignerStore";
+import {useDesignerStore, useIntegrationStore, useSelectorStore} from "../DesignerStore";
 import {shallow} from "zustand/shallow";
 import {useRouteDesignerHook} from "./useRouteDesignerHook";
 
@@ -40,7 +40,6 @@ export function DslSelector (props: Props) {
             [s.showSelector, s.showSteps, s.parentId, s.parentDsl, s.selectorTabIndex, s.setShowSelector, s.setSelectorTabIndex,
                 s.selectedPosition, s.selectedLabels, s.addSelectedLabel, s.deleteSelectedLabel], shallow)
 
-
     const [dark] = useDesignerStore((s) => [s.dark], shallow)
 
     const {onDslSelect} = useRouteDesignerHook();
diff --git a/karavan-web/karavan-app/src/main/webui/src/designer/route/RouteDesigner.tsx b/karavan-web/karavan-app/src/main/webui/src/designer/route/RouteDesigner.tsx
index 215f334b..c57260db 100644
--- a/karavan-web/karavan-app/src/main/webui/src/designer/route/RouteDesigner.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/designer/route/RouteDesigner.tsx
@@ -40,7 +40,8 @@ import {DslElementMoveModal} from "./DslElementMoveModal";
 
 export function RouteDesigner() {
 
-    const {openSelector, createRouteConfiguration, onCommand, handleKeyDown, handleKeyUp, unselectElement} = useRouteDesignerHook();
+    const {openSelector, createRouteConfiguration, onCommand, handleKeyDown, handleKeyUp, unselectElement, onDslSelect,
+        isSourceKamelet, isActionKamelet, isKamelet, isSinkKamelet} = useRouteDesignerHook();
 
     const [integration] = useIntegrationStore((state) => [state.integration], shallow)
     const [showDeleteConfirmation, setPosition, width, height, top, left, hideLogDSL, showMoveConfirmation, setShowMoveConfirmation] =
@@ -104,6 +105,37 @@ export function RouteDesigner() {
         )
     }
 
+    function getGraphButtons() {
+        const routes = CamelUi.getRoutes(integration);
+        const showNewRoute = (isKamelet() && routes.length === 0) || !isKamelet();
+        const showNewRouteConfiguration = !isKamelet();
+        return (
+            <div className="add-flow">
+                {showNewRoute && <Button
+                    variant={routes.length === 0 ? "primary" : "secondary"}
+                    icon={<PlusIcon/>}
+                    onClick={e => {
+                        if (isSinkKamelet() || isActionKamelet()) {
+                            const dsl = CamelUi.getDslMetaModel('FromDefinition');
+                            dsl.uri = 'kamelet:source';
+                            onDslSelect(dsl, '', undefined);
+                        } else {
+                            openSelector(undefined, undefined)
+                        }
+                    }}
+                >
+                    Create route
+                </Button>}
+                {showNewRouteConfiguration && <Button
+                    variant="secondary"
+                    icon={<PlusIcon/>}
+                    onClick={e => createRouteConfiguration()}
+                >
+                    Create configuration
+                </Button>}
+            </div>
+        )
+    }
     function getGraph() {
         const routes = CamelUi.getRoutes(integration);
         const routeConfigurations = CamelUi.getRouteConfigurations(integration);
@@ -129,24 +161,7 @@ export function RouteDesigner() {
                                     step={route}
                                     parent={undefined}/>
                     ))}
-                    <div className="add-flow">
-                        <Button
-                            variant={routes.length === 0 ? "primary" : "secondary"}
-                            icon={<PlusIcon/>}
-                            onClick={e => {
-                                openSelector(undefined, undefined)
-                            }}
-                        >
-                            Create route
-                        </Button>
-                        <Button
-                            variant="secondary"
-                            icon={<PlusIcon/>}
-                            onClick={e => createRouteConfiguration()}
-                        >
-                            Create configuration
-                        </Button>
-                    </div>
+                    {getGraphButtons()}
                 </div>
             </div>)
     }
diff --git a/karavan-web/karavan-app/src/main/webui/src/designer/route/useRouteDesignerHook.tsx b/karavan-web/karavan-app/src/main/webui/src/designer/route/useRouteDesignerHook.tsx
index fe61d410..f48fc850 100644
--- a/karavan-web/karavan-app/src/main/webui/src/designer/route/useRouteDesignerHook.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/designer/route/useRouteDesignerHook.tsx
@@ -19,7 +19,7 @@ import '../karavan.css';
 import {DslMetaModel} from "../utils/DslMetaModel";
 import {CamelUtil} from "karavan-core/lib/api/CamelUtil";
 import {ChoiceDefinition, FromDefinition, LogDefinition, RouteConfigurationDefinition, RouteDefinition} from "karavan-core/lib/model/CamelDefinition";
-import {CamelElement} from "karavan-core/lib/model/IntegrationDefinition";
+import {CamelElement, MetadataLabels} from "karavan-core/lib/model/IntegrationDefinition";
 import {CamelDefinitionApiExt} from "karavan-core/lib/api/CamelDefinitionApiExt";
 import {CamelDefinitionApi} from "karavan-core/lib/api/CamelDefinitionApi";
 import {Command, EventBus} from "../utils/EventBus";
@@ -27,6 +27,7 @@ import {CamelDisplayUtil} from "karavan-core/lib/api/CamelDisplayUtil";
 import {toPng} from 'html-to-image';
 import {useDesignerStore, useIntegrationStore, useSelectorStore} from "../DesignerStore";
 import {shallow} from "zustand/shallow";
+import {v4 as uuidv4} from 'uuid';
 
 export function useRouteDesignerHook () {
 
@@ -46,6 +47,34 @@ export function useRouteDesignerHook () {
         }
     }
 
+    function isKamelet(): boolean {
+        return integration.type === 'kamelet';
+    }
+
+    function isSourceKamelet(): boolean {
+        if (isKamelet()){
+            const m: MetadataLabels | undefined = integration.metadata.labels;
+            return m !== undefined && m["camel.apache.org/kamelet.type"] === 'source';
+        }
+        return false;
+    }
+
+    function isSinkKamelet(): boolean {
+        if (isKamelet()){
+            const m: MetadataLabels | undefined = integration.metadata.labels;
+            return m !== undefined && m["camel.apache.org/kamelet.type"] === 'sink';
+        }
+        return false;
+    }
+
+    function isActionKamelet(): boolean {
+        if (isKamelet()){
+            const m: MetadataLabels | undefined = integration.metadata.labels;
+            return m !== undefined && m["camel.apache.org/kamelet.type"] === 'action';
+        }
+        return false;
+    }
+
     const onShowDeleteConfirmation = (id: string) => {
         let message: string;
         const uuidsToDelete:string [] = [id];
@@ -193,13 +222,17 @@ export function useRouteDesignerHook () {
         setSelectorTabIndex((parentId === undefined && parentDsl === undefined) ? 'kamelet' : 'eip');
     }
 
-    const onDslSelect = (dsl: DslMetaModel, parentId: string, position?: number | undefined) => {
+    function onDslSelect (dsl: DslMetaModel, parentId: string, position?: number | undefined) {
         switch (dsl.dsl) {
             case 'FromDefinition' :
-                const route = CamelDefinitionApi.createRouteDefinition({from: new FromDefinition({uri: dsl.uri})});
+                const nodePrefixId = isKamelet() ? integration.metadata.name : 'route-' + uuidv4().substring(0,3);
+                const route = CamelDefinitionApi.createRouteDefinition({from: new FromDefinition({uri: dsl.uri}), nodePrefixId: nodePrefixId});
                 addStep(route, parentId, position)
                 break;
             case 'ToDefinition' :
+                if (dsl.uri === undefined && isKamelet()) {
+                    dsl.uri = 'kamelet:sink';
+                }
                 const to = CamelDefinitionApi.createStep(dsl.dsl, {uri: dsl.uri});
                 addStep(to, parentId, position)
                 break;
@@ -291,5 +324,6 @@ export function useRouteDesignerHook () {
     }
 
     return { deleteElement, selectElement, moveElement, onShowDeleteConfirmation, onDslSelect, openSelector,
-        createRouteConfiguration, onCommand, handleKeyDown, handleKeyUp, unselectElement}
+        createRouteConfiguration, onCommand, handleKeyDown, handleKeyUp, unselectElement, isKamelet, isSourceKamelet,
+        isActionKamelet, isSinkKamelet}
 }
\ No newline at end of file
diff --git a/karavan-web/karavan-app/src/main/webui/src/designer/utils/CamelUi.tsx b/karavan-web/karavan-app/src/main/webui/src/designer/utils/CamelUi.tsx
index 6864361b..2b672a8d 100644
--- a/karavan-web/karavan-app/src/main/webui/src/designer/utils/CamelUi.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/designer/utils/CamelUi.tsx
@@ -51,7 +51,7 @@ import {
     IgniteIcon,
     InfinispanIcon,
     IotIcon,
-    KafkaIcon,
+    KafkaIcon, KameletIcon,
     KubernetesIcon,
     MachineLearningIcon,
     MailIcon,
@@ -93,6 +93,7 @@ import {
 import React from "react";
 import {TopologyUtils} from "karavan-core/lib/api/TopologyUtils";
 import {CamelDisplayUtil} from "karavan-core/lib/api/CamelDisplayUtil";
+import {getDesignerIcon} from "../icons/KaravanIcons";
 
 const StepElements: string[] = [
     "AggregateDefinition",
@@ -107,6 +108,7 @@ const StepElements: string[] = [
     // "ErrorHandlerDefinition",
     "FilterDefinition",
     "IdempotentConsumerDefinition",
+    "KameletDefinition",
     "LogDefinition",
     "LoopDefinition",
     "MarshalDefinition",
@@ -306,6 +308,10 @@ export class CamelUi {
         else return false;
     }
 
+    static isKameletSink = (element: CamelElement): boolean => {
+        return element.dslName === 'ToDefinition' && (element as any).uri === 'kamelet:sink';
+    }
+
     static getInternalRouteUris = (integration: Integration, componentName: string, showComponentName: boolean = true): string[] => {
         const result: string[] = [];
         integration.spec.flows?.filter(f => f.dslName === 'RouteDefinition')
@@ -519,7 +525,7 @@ export class CamelUi {
         }
     }
 
-    static getIconForDsl = (dsl: DslMetaModel): JSX.Element => {
+    static getIconForDsl = (dsl: DslMetaModel): React.JSX.Element => {
         if (dsl.dsl && (dsl.dsl === "KameletDefinition" || dsl.navigation === 'kamelet')) {
             return this.getIconFromSource(CamelUi.getKameletIconByName(dsl.name));
         } else if ((dsl.dsl && dsl.dsl === "FromDefinition")
@@ -687,6 +693,8 @@ export class CamelUi {
                 return <ApiIcon/>;
             case 'HeadDefinition' :
                 return <ApiIcon/>;
+            case 'KameletDefinition' :
+                return <KameletIcon/>;
             default:
                 return this.getIconFromSource(CamelUi.getIconSrcForName(dslName))
         }
diff --git a/karavan-web/karavan-app/src/main/webui/src/knowledgebase/eip/EipModal.tsx b/karavan-web/karavan-app/src/main/webui/src/knowledgebase/eip/EipModal.tsx
index d2100a25..902a3ada 100644
--- a/karavan-web/karavan-app/src/main/webui/src/knowledgebase/eip/EipModal.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/knowledgebase/eip/EipModal.tsx
@@ -15,16 +15,9 @@
  * limitations under the License.
  */
 import React from 'react';
-import {
-    Button,
-    Modal,
-    ActionGroup,
-    Text,
-    CardHeader,
-    Badge, Flex, CardTitle,
-} from '@patternfly/react-core';
+import {ActionGroup, Badge, Button, CardHeader, CardTitle, Flex, Modal, Text,} from '@patternfly/react-core';
 import '../../designer/karavan.css';
-import {Table, Tbody, Td, Th, Thead, Tr} from "@patternfly/react-table";
+import {Table, TableText, Tbody, Td, Th, Thead, Tr, WrapModifier} from "@patternfly/react-table";
 import {CamelUi} from "../../designer/utils/CamelUi";
 import {PropertyMeta} from "karavan-core/lib/model/CamelMetadata";
 import {useKnowledgebaseStore} from "../KnowledgebaseStore";
@@ -45,7 +38,7 @@ export function EipModal() {
             isOpen={isModalOpen}
             onClose={() => setModalOpen(false)}
             actions={[
-                <div className="modal-footer">
+                <div className="modal-footer" key="buttons">
                     <ActionGroup className="deploy-buttons">
                         <Button key="cancel" variant="primary"
                                 onClick={e => setModalOpen(false)}>Close</Button>
@@ -53,8 +46,7 @@ export function EipModal() {
                 </div>
             ]}
         >
-            <Flex direction={{default: 'column'}} key={element?.name}
-                  className="kamelet-modal-card">
+            <Flex direction={{default: 'column'}} key={element?.name} className="kamelet-modal-card">
                 <CardHeader actions={{ actions: <><Badge className="badge"
                                                          isRead> {element?.labels}</Badge></>, hasNoOffset: false, className: undefined}} >
                     {element && CamelUi.getIconForDslName(element?.className)}
@@ -67,27 +59,30 @@ export function EipModal() {
                         <Table aria-label="Simple table" variant='compact'>
                             <Thead>
                                 <Tr>
-                                    <Th key='name'>Display Name / Name</Th>
+                                    <Th key='name' width={10}>Name</Th>
+                                    <Th key='label'>Label</Th>
+                                    <Th key='display' width={10}>Display Name</Th>
                                     <Th key='desc'>Description</Th>
                                     <Th key='type'>Type</Th>
-                                    <Th key='label'>Label</Th>
                                 </Tr>
                             </Thead>
                             <Tbody>
                                 {element?.properties.map((p: PropertyMeta, idx: number) => (
                                     <Tr key={idx}>
-                                        <Td key={`${idx}_name`}>
-                                            <div>
-                                                <b>{p.displayName}</b>
-                                                <div>{p.name}</div>
-                                            </div>
+                                        <Td modifier={"fitContent"}>
+                                            {p.name}
+                                        </Td>
+                                        <Td modifier={"fitContent"}>
+                                            <Badge className="badge" isRead>{p.label}</Badge>
+                                        </Td>
+                                        <Td modifier={"fitContent"}>
+                                            {p.displayName}
                                         </Td>
                                         <Td key={`${idx}_desc`}><div>
                                             <div>{p.description}</div>
                                             {p.defaultValue && p.defaultValue.toString().length > 0 && <div>{"Default value: " + p.defaultValue}</div>}
                                         </div></Td>
                                         <Td key={`${idx}_type`}>{p.type}</Td>
-                                        <Td key={`${idx}_label`}>{p.label}</Td>
                                     </Tr>
                                 ))}
                             </Tbody>
diff --git a/karavan-web/karavan-app/src/main/webui/src/project/file/FileEditor.tsx b/karavan-web/karavan-app/src/main/webui/src/project/file/FileEditor.tsx
index 073b3bc3..8271c19f 100644
--- a/karavan-web/karavan-app/src/main/webui/src/project/file/FileEditor.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/project/file/FileEditor.tsx
@@ -88,7 +88,7 @@ export function FileEditor (props: Props) {
     const isKameletYaml = file !== undefined && file.name.endsWith(".kamelet.yaml");
     const isIntegration = isCamelYaml && file?.code && CamelDefinitionYaml.yamlIsIntegration(file.code);
     const isProperties = file !== undefined && file.name.endsWith("properties");
-    const showDesigner = isCamelYaml && isIntegration;
+    const showDesigner = (isCamelYaml && isIntegration) || isKameletYaml;
     const showEditor = !showDesigner && !isProperties;
     return (
         <>
diff --git a/karavan-web/karavan-app/src/main/webui/src/project/files/CreateFileModal.tsx b/karavan-web/karavan-app/src/main/webui/src/project/files/CreateFileModal.tsx
index 36dc24ea..1221dc38 100644
--- a/karavan-web/karavan-app/src/main/webui/src/project/files/CreateFileModal.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/project/files/CreateFileModal.tsx
@@ -25,16 +25,18 @@ import {
     ToggleGroupItem, ToggleGroup, FormHelperText, HelperText, HelperTextItem, TextInput
 } from '@patternfly/react-core';
 import '../../designer/karavan.css';
-import {Integration} from "karavan-core/lib/model/IntegrationDefinition";
+import {Integration, KameletTypes, MetadataLabels} from "karavan-core/lib/model/IntegrationDefinition";
 import {CamelDefinitionYaml} from "karavan-core/lib/api/CamelDefinitionYaml";
 import {useFileStore, useProjectStore} from "../../api/ProjectStore";
 import {ProjectFile, ProjectFileTypes} from "../../api/ProjectModels";
 import {CamelUi} from "../../designer/utils/CamelUi";
 import {ProjectService} from "../../api/ProjectService";
 import {shallow} from "zustand/shallow";
+import {CamelUtil} from "karavan-core/lib/api/CamelUtil";
 
 interface Props {
-    types: string[]
+    types: string[],
+    isKameletsProject: boolean
 }
 
 export function CreateFileModal (props: Props) {
@@ -43,6 +45,7 @@ export function CreateFileModal (props: Props) {
     const [operation, setFile] = useFileStore((s) => [s.operation, s.setFile], shallow);
     const [name, setName] = useState<string>( '');
     const [fileType, setFileType] = useState<string>();
+    const [kameletType, setKameletType] = useState<KameletTypes>('source');
 
     useEffect(() => {
         if (props.types.length > 0) {
@@ -60,14 +63,28 @@ export function CreateFileModal (props: Props) {
         cleanValues();
     }
 
+    function getCode(): string {
+        if (fileType === 'INTEGRATION') {
+            return CamelDefinitionYaml.integrationToYaml(Integration.createNew(name, 'plain'));
+        } else if (fileType === 'KAMELET') {
+            const kameletName = name + (isKamelet ? '-'+kameletType : '');
+            const integration = Integration.createNew(kameletName, 'kamelet');
+            const meta:MetadataLabels = new MetadataLabels({"camel.apache.org/kamelet.type": kameletType});
+            integration.metadata.labels = meta;
+            console.log(integration);
+            return CamelDefinitionYaml.integrationToYaml(integration);
+        } else {
+            return '';
+        }
+    }
+
     function confirmAndCloseModal () {
         const extension = ProjectFileTypes.filter(value => value.name === fileType)[0].extension;
         const filename = (extension !== 'java') ? fileNameCheck(name) : CamelUi.javaNameFromTitle(name);
-        const code = fileType === 'INTEGRATION'
-            ? CamelDefinitionYaml.integrationToYaml(Integration.createNew(name, 'plain'))
-            : '';
+        const code = getCode();
         if (filename && extension) {
-            const file = new ProjectFile(filename + '.' + extension, project.projectId, code, Date.now());
+            const fullFileName = filename + (isKamelet ? '-'+kameletType : '') + '.' + extension;
+            const file = new ProjectFile(fullFileName, project.projectId, code, Date.now());
             ProjectService.createFile(file);
             cleanValues();
             if (code) {
@@ -82,10 +99,12 @@ export function CreateFileModal (props: Props) {
         return title.replace(/[^0-9a-zA-Z.]+/gi, "-").toLowerCase();
     }
 
+    const isKamelet = props.isKameletsProject;
     const extension = ProjectFileTypes.filter(value => value.name === fileType)[0]?.extension;
     const filename = (extension !== 'java')
         ? fileNameCheck(name)
-        : CamelUi.javaNameFromTitle(name)
+        : CamelUi.javaNameFromTitle(name);
+    const fullFileName = filename + (isKamelet ? '-'+kameletType : '') + '.' + extension;
     return (
         <Modal
             title="Create"
@@ -98,7 +117,7 @@ export function CreateFileModal (props: Props) {
             ]}
         >
             <Form autoComplete="off" isHorizontal className="create-file-form">
-                <FormGroup label="Type" fieldId="type" isRequired>
+                {!isKamelet && <FormGroup label="Type" fieldId="type" isRequired>
                     <ToggleGroup aria-label="Type" isCompact>
                         {ProjectFileTypes.filter(p => props.types.includes(p.name))
                             .map(p => {
@@ -110,12 +129,24 @@ export function CreateFileModal (props: Props) {
                                                         }}/>
                             })}
                     </ToggleGroup>
-                </FormGroup>
+                </FormGroup>}
+                {isKamelet && <FormGroup label="Kamelet Type" fieldId="kameletType" isRequired>
+                    <ToggleGroup aria-label="Kamelet Type">
+                        {['source', 'action', 'sink'].map((type) => {
+                            const title = CamelUtil.capitalizeName(type);
+                            return <ToggleGroupItem key={type} text={title} buttonId={type}
+                                                    isSelected={kameletType === type}
+                                                    onChange={(_, selected) => {
+                                                        setKameletType(type as KameletTypes);
+                                                    }}/>
+                        })}
+                    </ToggleGroup>
+                </FormGroup>}
                 <FormGroup label="Name" fieldId="name" isRequired>
                     <TextInput id="name" aria-label="name" value={name} onChange={(_, value) => setName(value)}/>
                     <FormHelperText  >
                         <HelperText id="helper-text1">
-                            <HelperTextItem variant={'default'}>{filename + '.' + extension}</HelperTextItem>
+                            <HelperTextItem variant={'default'}>{fullFileName}</HelperTextItem>
                         </HelperText>
                     </FormHelperText>
                 </FormGroup>
diff --git a/karavan-web/karavan-app/src/main/webui/src/project/files/FilesTab.tsx b/karavan-web/karavan-app/src/main/webui/src/project/files/FilesTab.tsx
index d0396f96..e9dda209 100644
--- a/karavan-web/karavan-app/src/main/webui/src/project/files/FilesTab.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/project/files/FilesTab.tsx
@@ -159,7 +159,7 @@ export function FilesTab () {
                 </Table>
             </div>
             <UploadFileModal projectId={project.projectId}/>
-            <CreateFileModal types={types}/>
+            <CreateFileModal types={types} isKameletsProject={isKameletsProject()}/>
             <DeleteFileModal />
         </PageSection>
     )
diff --git a/karavan-web/karavan-app/src/main/webui/src/project/topology/ProjectTopologyTab.tsx b/karavan-web/karavan-app/src/main/webui/src/project/topology/ProjectTopologyTab.tsx
index 935a91e8..8775353f 100644
--- a/karavan-web/karavan-app/src/main/webui/src/project/topology/ProjectTopologyTab.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/project/topology/ProjectTopologyTab.tsx
@@ -49,7 +49,7 @@ export const ProjectTopologyTab: React.FC = () => {
                 onClickCreateButton={() => setFile('create')}
                 onSetFile={(fileName) => selectFile(fileName)}
             />
-            <CreateFileModal types={['INTEGRATION']}/>
+            <CreateFileModal types={['INTEGRATION']} isKameletsProject={false}/>
         </>
     );
 }
\ No newline at end of file


[camel-karavan] 01/07: Kamelet Sink in Source Kamelet Selector for #315

Posted by ma...@apache.org.
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

commit 211dd994b3d3873ca5325de4ae93f937850b962c
Author: Marat Gubaidullin <ma...@talismancloud.io>
AuthorDate: Mon Oct 23 16:53:01 2023 -0400

    Kamelet Sink in Source Kamelet Selector for #315
---
 karavan-designer/src/App.tsx                       |  4 ++--
 karavan-designer/src/designer/KaravanDesigner.tsx  |  2 --
 .../src/designer/icons/ComponentIcons.tsx          | 23 +++++++++++++++++++++-
 .../src/designer/icons/KaravanIcons.tsx            |  3 ++-
 .../src/designer/route/DslConnections.tsx          |  1 +
 karavan-designer/src/designer/route/DslElement.tsx | 12 +++++++++--
 .../src/designer/route/DslSelector.tsx             |  3 +--
 .../src/designer/route/useRouteDesignerHook.tsx    |  9 ++++++++-
 karavan-designer/src/designer/utils/CamelUi.tsx    | 12 +++++++++--
 9 files changed, 56 insertions(+), 13 deletions(-)

diff --git a/karavan-designer/src/App.tsx b/karavan-designer/src/App.tsx
index 933c493d..30196dab 100644
--- a/karavan-designer/src/App.tsx
+++ b/karavan-designer/src/App.tsx
@@ -81,8 +81,8 @@ class App extends React.Component<Props, State> {
             fetch("components/components.json"),
             fetch("snippets/org.apache.camel.AggregationStrategy"),
             fetch("snippets/org.apache.camel.Processor"),
-            fetch("example/demo.camel.yaml")
-            // fetch("example/aws-s3-cdc-source.kamelet.yaml")
+            // fetch("example/demo.camel.yaml")
+            fetch("example/aws-s3-cdc-source.kamelet.yaml")
             // fetch("components/supported-components.json"),
         ]).then(responses =>
             Promise.all(responses.map(response => response.text()))
diff --git a/karavan-designer/src/designer/KaravanDesigner.tsx b/karavan-designer/src/designer/KaravanDesigner.tsx
index 4b7b3c7a..b1af73c5 100644
--- a/karavan-designer/src/designer/KaravanDesigner.tsx
+++ b/karavan-designer/src/designer/KaravanDesigner.tsx
@@ -24,8 +24,6 @@ import {
     Tabs,
     TabTitleIcon,
     TabTitleText,
-    Tooltip,
-    TooltipPosition,
 } from '@patternfly/react-core';
 import './karavan.css';
 import {RouteDesigner} from "./route/RouteDesigner";
diff --git a/karavan-designer/src/designer/icons/ComponentIcons.tsx b/karavan-designer/src/designer/icons/ComponentIcons.tsx
index a05f5e70..bda35d53 100644
--- a/karavan-designer/src/designer/icons/ComponentIcons.tsx
+++ b/karavan-designer/src/designer/icons/ComponentIcons.tsx
@@ -1165,7 +1165,28 @@ export function ApiIcon() {
     );
 }
 
-export function MonitoringIcon() {
+export function KameletIcon() {
+    return (
+        <svg
+            className="icon" id="icon"
+            xmlns="http://www.w3.org/2000/svg"
+            viewBox="0 0 32 32"
+        >
+            <title>{"application"}</title>
+            <path d="M16 18H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2v10a2 2 0 0 1-2 2ZM6 6v10h10V6ZM26 12v4h-4v-4h4m0-2h-4a2 2 0 0 0-2 2v4a2 2 0 0 0 2 2h4a2 2 0 0 0 2-2v-4a2 2 0 0 0-2-2ZM26 22v4h-4v-4h4m0-2h-4a2 2 0 0 0-2 2v4a2 2 0 0 0 2 2h4a2 2 0 0 0 2-2v-4a2 2 0 0 0-2-2ZM16 22v4h-4v-4h4m0-2h-4a2 2 0 0 0-2 2v4a2 2 0 0 0 2 2h4a2 2 0 0 0 2-2v-4a2 2 0 0 0-2-2Z" />
+            <path
+                d="M0 0h32v32H0z"
+                data-name="&lt;Transparent Rectangle&gt;"
+                style={{
+                    fill: "none",
+                }}
+            />
+        </svg>
+    )
+}
+
+
+    export function MonitoringIcon() {
     return (
         <svg
             xmlns="http://www.w3.org/2000/svg"
diff --git a/karavan-designer/src/designer/icons/KaravanIcons.tsx b/karavan-designer/src/designer/icons/KaravanIcons.tsx
index b7165b93..b0b8042f 100644
--- a/karavan-designer/src/designer/icons/KaravanIcons.tsx
+++ b/karavan-designer/src/designer/icons/KaravanIcons.tsx
@@ -260,7 +260,7 @@ export function CamelIcon(props?: (JSX.IntrinsicAttributes & React.SVGProps<SVGS
     );
 }
 
-export function getDesignerIcon(icon: string) {
+export function getDesignerIcon(icon: string): React.JSX.Element {
     if (icon === 'kamelet') return (
         <svg
             className="top-icon" id="icon"
@@ -429,6 +429,7 @@ export function getDesignerIcon(icon: string) {
             <rect id="_Transparent_Rectangle_" data-name="&lt;Transparent Rectangle&gt;" className="cls-1" width="32"
                   height="32" transform="translate(0 32) rotate(-90)"/>
         </svg>)
+    return <></>;
 }
 
 
diff --git a/karavan-designer/src/designer/route/DslConnections.tsx b/karavan-designer/src/designer/route/DslConnections.tsx
index de6a0060..10338ae9 100644
--- a/karavan-designer/src/designer/route/DslConnections.tsx
+++ b/karavan-designer/src/designer/route/DslConnections.tsx
@@ -142,6 +142,7 @@ export function DslConnections() {
             .filter(pos => pos.step.dslName === 'ToDefinition' && !CamelUi.isActionKamelet(pos.step) && !TopologyUtils.isElementInternalComponent(pos.step))
             .filter(pos => !(outgoingDefinitions.includes(pos.step.dslName) && TopologyUtils.hasInternalUri(pos.step)))
             .filter(pos => pos.step.dslName !== 'SagaDefinition')
+            .filter(pos => !CamelUi.isKameletSink(pos.step))
             .sort((pos1: DslPosition, pos2: DslPosition) => {
                 const y1 = pos1.headerRect.y + pos1.headerRect.height / 2;
                 const y2 = pos2.headerRect.y + pos2.headerRect.height / 2;
diff --git a/karavan-designer/src/designer/route/DslElement.tsx b/karavan-designer/src/designer/route/DslElement.tsx
index 305b7859..c45dcbe4 100644
--- a/karavan-designer/src/designer/route/DslElement.tsx
+++ b/karavan-designer/src/designer/route/DslElement.tsx
@@ -42,7 +42,7 @@ interface Props {
 export function DslElement(props: Props) {
 
     const headerRef = React.useRef<HTMLDivElement>(null);
-    const {selectElement, moveElement, onShowDeleteConfirmation, openSelector} = useRouteDesignerHook();
+    const {selectElement, moveElement, onShowDeleteConfirmation, openSelector, isKamelet} = useRouteDesignerHook();
 
     const [integration] = useIntegrationStore((s) => [s.integration, s.setIntegration], shallow)
 
@@ -241,9 +241,17 @@ export function DslElement(props: Props) {
         )
     }
 
+    function getHeaderText(step: CamelElement): string {
+        if (isKamelet() && step.dslName === 'ToDefinition' && (step as any).uri === 'kamelet:sink') {
+            return "Sink";
+        } else {
+            return (step as any).description ? (step as any).description : CamelUi.getElementTitle(props.step);
+        }
+    }
+
     function getHeaderTextWithTooltip(step: CamelElement) {
         const checkRequired = CamelUtil.checkRequired(step);
-        const title = (step as any).description ? (step as any).description : CamelUi.getElementTitle(props.step);
+        const title = getHeaderText(step);
         let className = hasWideChildrenElement() ? "text text-right" : "text text-bottom";
         if (!checkRequired[0]) className = className + " header-text-required";
         if (checkRequired[0]) {
diff --git a/karavan-designer/src/designer/route/DslSelector.tsx b/karavan-designer/src/designer/route/DslSelector.tsx
index 8f815536..a80525ba 100644
--- a/karavan-designer/src/designer/route/DslSelector.tsx
+++ b/karavan-designer/src/designer/route/DslSelector.tsx
@@ -24,7 +24,7 @@ import {
 import '../karavan.css';
 import {CamelUi} from "../utils/CamelUi";
 import {DslMetaModel} from "../utils/DslMetaModel";
-import {useDesignerStore, useSelectorStore} from "../DesignerStore";
+import {useDesignerStore, useIntegrationStore, useSelectorStore} from "../DesignerStore";
 import {shallow} from "zustand/shallow";
 import {useRouteDesignerHook} from "./useRouteDesignerHook";
 
@@ -40,7 +40,6 @@ export function DslSelector (props: Props) {
             [s.showSelector, s.showSteps, s.parentId, s.parentDsl, s.selectorTabIndex, s.setShowSelector, s.setSelectorTabIndex,
                 s.selectedPosition, s.selectedLabels, s.addSelectedLabel, s.deleteSelectedLabel], shallow)
 
-
     const [dark] = useDesignerStore((s) => [s.dark], shallow)
 
     const {onDslSelect} = useRouteDesignerHook();
diff --git a/karavan-designer/src/designer/route/useRouteDesignerHook.tsx b/karavan-designer/src/designer/route/useRouteDesignerHook.tsx
index fe61d410..5848acc9 100644
--- a/karavan-designer/src/designer/route/useRouteDesignerHook.tsx
+++ b/karavan-designer/src/designer/route/useRouteDesignerHook.tsx
@@ -46,6 +46,10 @@ export function useRouteDesignerHook () {
         }
     }
 
+    function isKamelet(): boolean {
+        return integration.type === 'kamelet';
+    }
+
     const onShowDeleteConfirmation = (id: string) => {
         let message: string;
         const uuidsToDelete:string [] = [id];
@@ -200,6 +204,9 @@ export function useRouteDesignerHook () {
                 addStep(route, parentId, position)
                 break;
             case 'ToDefinition' :
+                if (dsl.uri === undefined && isKamelet()) {
+                    dsl.uri = 'kamelet:sink';
+                }
                 const to = CamelDefinitionApi.createStep(dsl.dsl, {uri: dsl.uri});
                 addStep(to, parentId, position)
                 break;
@@ -291,5 +298,5 @@ export function useRouteDesignerHook () {
     }
 
     return { deleteElement, selectElement, moveElement, onShowDeleteConfirmation, onDslSelect, openSelector,
-        createRouteConfiguration, onCommand, handleKeyDown, handleKeyUp, unselectElement}
+        createRouteConfiguration, onCommand, handleKeyDown, handleKeyUp, unselectElement, isKamelet}
 }
\ No newline at end of file
diff --git a/karavan-designer/src/designer/utils/CamelUi.tsx b/karavan-designer/src/designer/utils/CamelUi.tsx
index 6864361b..2b672a8d 100644
--- a/karavan-designer/src/designer/utils/CamelUi.tsx
+++ b/karavan-designer/src/designer/utils/CamelUi.tsx
@@ -51,7 +51,7 @@ import {
     IgniteIcon,
     InfinispanIcon,
     IotIcon,
-    KafkaIcon,
+    KafkaIcon, KameletIcon,
     KubernetesIcon,
     MachineLearningIcon,
     MailIcon,
@@ -93,6 +93,7 @@ import {
 import React from "react";
 import {TopologyUtils} from "karavan-core/lib/api/TopologyUtils";
 import {CamelDisplayUtil} from "karavan-core/lib/api/CamelDisplayUtil";
+import {getDesignerIcon} from "../icons/KaravanIcons";
 
 const StepElements: string[] = [
     "AggregateDefinition",
@@ -107,6 +108,7 @@ const StepElements: string[] = [
     // "ErrorHandlerDefinition",
     "FilterDefinition",
     "IdempotentConsumerDefinition",
+    "KameletDefinition",
     "LogDefinition",
     "LoopDefinition",
     "MarshalDefinition",
@@ -306,6 +308,10 @@ export class CamelUi {
         else return false;
     }
 
+    static isKameletSink = (element: CamelElement): boolean => {
+        return element.dslName === 'ToDefinition' && (element as any).uri === 'kamelet:sink';
+    }
+
     static getInternalRouteUris = (integration: Integration, componentName: string, showComponentName: boolean = true): string[] => {
         const result: string[] = [];
         integration.spec.flows?.filter(f => f.dslName === 'RouteDefinition')
@@ -519,7 +525,7 @@ export class CamelUi {
         }
     }
 
-    static getIconForDsl = (dsl: DslMetaModel): JSX.Element => {
+    static getIconForDsl = (dsl: DslMetaModel): React.JSX.Element => {
         if (dsl.dsl && (dsl.dsl === "KameletDefinition" || dsl.navigation === 'kamelet')) {
             return this.getIconFromSource(CamelUi.getKameletIconByName(dsl.name));
         } else if ((dsl.dsl && dsl.dsl === "FromDefinition")
@@ -687,6 +693,8 @@ export class CamelUi {
                 return <ApiIcon/>;
             case 'HeadDefinition' :
                 return <ApiIcon/>;
+            case 'KameletDefinition' :
+                return <KameletIcon/>;
             default:
                 return this.getIconFromSource(CamelUi.getIconSrcForName(dslName))
         }


[camel-karavan] 02/07: Kamelet Source in Sink Kamelet for #315

Posted by ma...@apache.org.
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

commit 2d8216b482315e970141e1ff6398b4f310c55178
Author: Marat Gubaidullin <ma...@talismancloud.io>
AuthorDate: Mon Oct 23 17:58:18 2023 -0400

    Kamelet Source in Sink Kamelet for #315
---
 karavan-core/src/core/model/KameletModels.ts       |   2 +-
 .../example/aws-cloudwatch-sink.kamelet.yaml       | 185 +++++++++++++++++++++
 karavan-designer/src/App.tsx                       |   3 +-
 .../src/designer/route/DslConnections.tsx          |   1 +
 karavan-designer/src/designer/route/DslElement.tsx |   4 +-
 .../src/designer/route/RouteDesigner.tsx           |  53 +++---
 .../src/designer/route/useRouteDesignerHook.tsx    |  31 +++-
 7 files changed, 254 insertions(+), 25 deletions(-)

diff --git a/karavan-core/src/core/model/KameletModels.ts b/karavan-core/src/core/model/KameletModels.ts
index e5c9855e..342d8a35 100644
--- a/karavan-core/src/core/model/KameletModels.ts
+++ b/karavan-core/src/core/model/KameletModels.ts
@@ -49,7 +49,7 @@ export class KameletSpec {
 }
 
 export class Labels {
-    'camel.apache.org/kamelet.type': string | any = '';
+    'camel.apache.org/kamelet.type': "sink" | "source" | "action" = 'source';
 
     public constructor(init?: Partial<Labels>) {
         Object.assign(this, init);
diff --git a/karavan-designer/public/example/aws-cloudwatch-sink.kamelet.yaml b/karavan-designer/public/example/aws-cloudwatch-sink.kamelet.yaml
new file mode 100644
index 00000000..d22ca1fc
--- /dev/null
+++ b/karavan-designer/public/example/aws-cloudwatch-sink.kamelet.yaml
@@ -0,0 +1,185 @@
+# ---------------------------------------------------------------------------
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+# ---------------------------------------------------------------------------
+
+apiVersion: camel.apache.org/v1
+kind: Kamelet
+metadata:
+  name: aws-cloudwatch-sink
+  annotations:
+    camel.apache.org/kamelet.support.level: "Stable"
+    camel.apache.org/catalog.version: "4.0.1"
+    camel.apache.org/kamelet.icon: "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjIwNyIgaGVpZ2h0PSIyNTAwIiB2aWV3Qm94PSIwIDAgMjU2IDI5MCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiBwcmVzZXJ2ZUFzcGVjdFJhdGlvPSJ4TWlkWU1pZCI+PHBhdGggZD0iTTI1NiAxOTkuMzA1bC0xMjcuOTU3LTE4Ljc5N0wwIDE5OS4zMjlsMTI4LjAxIDQ3LjQzOUwyNTYgMTk5LjMwNSIgZmlsbD0iI0I3Q0E5RCIvPjxwYXRoIGQ9Ik0yNS42MjEgMTk3LjExM2wyMS42MyA2Ljc2MSAxLjk3MS0yLjIzOFY1MC4yODRsLTEuOTcxLTIuNTg1LTIxLjYzIDguMjc0djE0MS4xNCIgZmlsbD0iIzRCNjEyQyIvPjxwYXRoIG [...]
+    camel.apache.org/provider: "Apache Software Foundation"
+    camel.apache.org/kamelet.group: "AWS Cloudwatch"
+    camel.apache.org/kamelet.namespace: "AWS"
+  labels:
+    camel.apache.org/kamelet.type: "sink"
+spec:
+  definition:
+    title: "AWS CloudWatch Metrics Sink"
+    description: |-
+      Send data to Amazon CloudWatch metrics.
+
+      The basic authentication method for the AWS CloudWatch metrics service is to specify an access key and a secret key. These parameters are optional because the Kamelet provides a default credentials provider.
+      
+      If you use the default credentials provider, the CloudWatch client loads the credentials through this provider and doesn't use the basic authentication method.
+      
+      You can set the following properties in the header:
+
+      `metric-name` / `ce-metricname` for the metric name.
+      `metric-value` / `ce-metricvalue` for the metric value.
+      `metric-unit` / `ce-metricunit` for the metric unit.
+      `metric-timestamp` / `ce-metrictimestamp` for the metric timestamp.
+      `metric-dimension-name` / `ce-metricdimensionname` for the dimension name.
+      `metric-dimension-value` / `ce-metricdimensionvalue` for the dimension value.
+    required:
+      - cwNamespace
+      - region
+    type: object
+    properties:
+      cwNamespace:
+        title: Cloud Watch Namespace
+        description: The CloudWatch metric namespace.
+        type: string
+      accessKey:
+        title: Access Key
+        description: The access key obtained from AWS.
+        type: string
+        format: password
+        x-descriptors:
+          - urn:alm:descriptor:com.tectonic.ui:password
+          - urn:camel:group:credentials
+      secretKey:
+        title: Secret Key
+        description: The secret key obtained from AWS.
+        type: string
+        format: password
+        x-descriptors:
+          - urn:alm:descriptor:com.tectonic.ui:password
+          - urn:camel:group:credentials
+      region:
+        title: AWS Region
+        description: The AWS region to access.
+        type: string
+        enum: ["ap-south-1", "eu-south-1", "us-gov-east-1", "me-central-1", "ca-central-1", "eu-central-1", "us-iso-west-1", "us-west-1", "us-west-2", "af-south-1", "eu-north-1", "eu-west-3", "eu-west-2", "eu-west-1", "ap-northeast-3", "ap-northeast-2", "ap-northeast-1", "me-south-1", "sa-east-1", "ap-east-1", "cn-north-1", "us-gov-west-1", "ap-southeast-1", "ap-southeast-2", "us-iso-east-1", "ap-southeast-3", "us-east-1", "us-east-2", "cn-northwest-1", "us-isob-east-1", "aws-global", "a [...]
+      useDefaultCredentialsProvider:
+        title: Default Credentials Provider
+        description: If true, the CloudWatch client loads credentials through a default credentials provider. If false, it uses the basic authentication method (access key and secret key).
+        type: boolean
+        x-descriptors:
+          - 'urn:alm:descriptor:com.tectonic.ui:checkbox'
+        default: false
+      uriEndpointOverride:
+        title: Overwrite Endpoint URI
+        description: The overriding endpoint URI. To use this option, you must also select the `overrideEndpoint` option.
+        type: string
+      overrideEndpoint:
+        title: Endpoint Overwrite
+        description: Select this option to override the endpoint URI. To use this option, you must also provide a URI for the `uriEndpointOverride` option.
+        type: boolean
+        x-descriptors:
+          - 'urn:alm:descriptor:com.tectonic.ui:checkbox'
+        default: false
+  dependencies:
+    - "camel:core"
+    - "camel:aws2-cw"
+    - "camel:kamelet"
+  template:
+    from:
+      uri: kamelet:source
+      steps:
+        - choice:
+            when:
+              - simple: "${header[metric-name]}"
+                steps:
+                  - set-header:
+                      name: CamelAwsCwMetricName
+                      simple: "${header[metric-name]}"
+              - simple: "${header[ce-metricname]}"
+                steps:
+                  - set-header:
+                      name: CamelAwsCwMetricName
+                      simple: "${header[ce-metricname]}"
+        - choice:
+            when:
+              - simple: "${header[metric-value]}"
+                steps:
+                  - set-header:
+                      name: CamelAwsCwMetricValue
+                      simple: "${header[metric-value]}"
+              - simple: "${header[ce-metricvalue]}"
+                steps:
+                  - set-header:
+                      name: CamelAwsCwMetricValue
+                      simple: "${header[ce-metricvalue]}"
+        - choice:
+            when:
+              - simple: "${header[metric-unit]}"
+                steps:
+                  - set-header:
+                      name: CamelAwsCwMetricUnit
+                      simple: "${header[metric-unit]}"
+              - simple: "${header[ce-metricunit]}"
+                steps:
+                  - set-header:
+                      name: CamelAwsCwMetricUnit
+                      simple: "${header[ce-metricunit]}"
+        - choice:
+            when:
+              - simple: "${header[metric-timestamp]}"
+                steps:
+                  - set-header:
+                      name: CamelAwsCwMetricTimestamp
+                      simple: "${header[metric-timestamp]}"
+              - simple: "${header[ce-metrictimestamp]}"
+                steps:
+                  - set-header:
+                      name: CamelAwsCwMetricTimestamp
+                      simple: "${header[ce-metrictimestamp]}"
+        - choice:
+            when:
+              - simple: "${header[metric-dimension-name]}"
+                steps:
+                  - set-header:
+                      name: CamelAwsCwMetricDimensionName
+                      simple: "${header[metric-dimension-name]}"
+              - simple: "${header[ce-metricdimensionname]}"
+                steps:
+                  - set-header:
+                      name: CamelAwsCwMetricDimensionName
+                      simple: "${header[ce-metricdimensionname]}"
+        - choice:
+            when:
+              - simple: "${header[metric-dimension-value]}"
+                steps:
+                  - set-header:
+                      name: CamelAwsCwMetricDimensionValue
+                      simple: "${header[metric-dimension-value]}"
+              - simple: "${header[ce-metricdimensionvalue]}"
+                steps:
+                  - set-header:
+                      name: CamelAwsCwMetricDimensionValue
+                      simple: "${header[ce-metricdimensionvalue]}"
+        - to:
+            uri: "aws2-cw:{{cwNamespace}}"
+            parameters:
+              secretKey: "{{?secretKey}}"
+              accessKey: "{{?accessKey}}"
+              region: "{{region}}"
+              useDefaultCredentialsProvider: "{{useDefaultCredentialsProvider}}"
+              uriEndpointOverride: "{{?uriEndpointOverride}}"
+              overrideEndpoint: "{{overrideEndpoint}}"
\ No newline at end of file
diff --git a/karavan-designer/src/App.tsx b/karavan-designer/src/App.tsx
index 30196dab..af23ed39 100644
--- a/karavan-designer/src/App.tsx
+++ b/karavan-designer/src/App.tsx
@@ -82,7 +82,8 @@ class App extends React.Component<Props, State> {
             fetch("snippets/org.apache.camel.AggregationStrategy"),
             fetch("snippets/org.apache.camel.Processor"),
             // fetch("example/demo.camel.yaml")
-            fetch("example/aws-s3-cdc-source.kamelet.yaml")
+            fetch("example/aws-cloudwatch-sink.kamelet.yaml")
+            // fetch("example/aws-s3-cdc-source.kamelet.yaml")
             // fetch("components/supported-components.json"),
         ]).then(responses =>
             Promise.all(responses.map(response => response.text()))
diff --git a/karavan-designer/src/designer/route/DslConnections.tsx b/karavan-designer/src/designer/route/DslConnections.tsx
index 10338ae9..a636339e 100644
--- a/karavan-designer/src/designer/route/DslConnections.tsx
+++ b/karavan-designer/src/designer/route/DslConnections.tsx
@@ -63,6 +63,7 @@ export function DslConnections() {
             .filter(pos => ["FromDefinition"].includes(pos.step.dslName))
             .filter(pos => !TopologyUtils.isElementInternalComponent(pos.step))
             .filter(pos => !(pos.step.dslName === 'FromDefinition' && TopologyUtils.hasInternalUri(pos.step)))
+            .filter(pos => !(pos.step.dslName === 'FromDefinition' && (pos.step as any).uri === 'kamelet:source'))
             .sort((pos1: DslPosition, pos2: DslPosition) => {
                 const y1 = pos1.headerRect.y + pos1.headerRect.height / 2;
                 const y2 = pos2.headerRect.y + pos2.headerRect.height / 2;
diff --git a/karavan-designer/src/designer/route/DslElement.tsx b/karavan-designer/src/designer/route/DslElement.tsx
index c45dcbe4..9e4a8c70 100644
--- a/karavan-designer/src/designer/route/DslElement.tsx
+++ b/karavan-designer/src/designer/route/DslElement.tsx
@@ -42,7 +42,7 @@ interface Props {
 export function DslElement(props: Props) {
 
     const headerRef = React.useRef<HTMLDivElement>(null);
-    const {selectElement, moveElement, onShowDeleteConfirmation, openSelector, isKamelet} = useRouteDesignerHook();
+    const {selectElement, moveElement, onShowDeleteConfirmation, openSelector, isKamelet, isSourceKamelet, isActionKamelet} = useRouteDesignerHook();
 
     const [integration] = useIntegrationStore((s) => [s.integration, s.setIntegration], shallow)
 
@@ -244,6 +244,8 @@ export function DslElement(props: Props) {
     function getHeaderText(step: CamelElement): string {
         if (isKamelet() && step.dslName === 'ToDefinition' && (step as any).uri === 'kamelet:sink') {
             return "Sink";
+        } else if (isKamelet() && step.dslName === 'FromDefinition' && (step as any).uri === 'kamelet:source') {
+            return "Source";
         } else {
             return (step as any).description ? (step as any).description : CamelUi.getElementTitle(props.step);
         }
diff --git a/karavan-designer/src/designer/route/RouteDesigner.tsx b/karavan-designer/src/designer/route/RouteDesigner.tsx
index 215f334b..c57260db 100644
--- a/karavan-designer/src/designer/route/RouteDesigner.tsx
+++ b/karavan-designer/src/designer/route/RouteDesigner.tsx
@@ -40,7 +40,8 @@ import {DslElementMoveModal} from "./DslElementMoveModal";
 
 export function RouteDesigner() {
 
-    const {openSelector, createRouteConfiguration, onCommand, handleKeyDown, handleKeyUp, unselectElement} = useRouteDesignerHook();
+    const {openSelector, createRouteConfiguration, onCommand, handleKeyDown, handleKeyUp, unselectElement, onDslSelect,
+        isSourceKamelet, isActionKamelet, isKamelet, isSinkKamelet} = useRouteDesignerHook();
 
     const [integration] = useIntegrationStore((state) => [state.integration], shallow)
     const [showDeleteConfirmation, setPosition, width, height, top, left, hideLogDSL, showMoveConfirmation, setShowMoveConfirmation] =
@@ -104,6 +105,37 @@ export function RouteDesigner() {
         )
     }
 
+    function getGraphButtons() {
+        const routes = CamelUi.getRoutes(integration);
+        const showNewRoute = (isKamelet() && routes.length === 0) || !isKamelet();
+        const showNewRouteConfiguration = !isKamelet();
+        return (
+            <div className="add-flow">
+                {showNewRoute && <Button
+                    variant={routes.length === 0 ? "primary" : "secondary"}
+                    icon={<PlusIcon/>}
+                    onClick={e => {
+                        if (isSinkKamelet() || isActionKamelet()) {
+                            const dsl = CamelUi.getDslMetaModel('FromDefinition');
+                            dsl.uri = 'kamelet:source';
+                            onDslSelect(dsl, '', undefined);
+                        } else {
+                            openSelector(undefined, undefined)
+                        }
+                    }}
+                >
+                    Create route
+                </Button>}
+                {showNewRouteConfiguration && <Button
+                    variant="secondary"
+                    icon={<PlusIcon/>}
+                    onClick={e => createRouteConfiguration()}
+                >
+                    Create configuration
+                </Button>}
+            </div>
+        )
+    }
     function getGraph() {
         const routes = CamelUi.getRoutes(integration);
         const routeConfigurations = CamelUi.getRouteConfigurations(integration);
@@ -129,24 +161,7 @@ export function RouteDesigner() {
                                     step={route}
                                     parent={undefined}/>
                     ))}
-                    <div className="add-flow">
-                        <Button
-                            variant={routes.length === 0 ? "primary" : "secondary"}
-                            icon={<PlusIcon/>}
-                            onClick={e => {
-                                openSelector(undefined, undefined)
-                            }}
-                        >
-                            Create route
-                        </Button>
-                        <Button
-                            variant="secondary"
-                            icon={<PlusIcon/>}
-                            onClick={e => createRouteConfiguration()}
-                        >
-                            Create configuration
-                        </Button>
-                    </div>
+                    {getGraphButtons()}
                 </div>
             </div>)
     }
diff --git a/karavan-designer/src/designer/route/useRouteDesignerHook.tsx b/karavan-designer/src/designer/route/useRouteDesignerHook.tsx
index 5848acc9..0d97214d 100644
--- a/karavan-designer/src/designer/route/useRouteDesignerHook.tsx
+++ b/karavan-designer/src/designer/route/useRouteDesignerHook.tsx
@@ -19,7 +19,7 @@ import '../karavan.css';
 import {DslMetaModel} from "../utils/DslMetaModel";
 import {CamelUtil} from "karavan-core/lib/api/CamelUtil";
 import {ChoiceDefinition, FromDefinition, LogDefinition, RouteConfigurationDefinition, RouteDefinition} from "karavan-core/lib/model/CamelDefinition";
-import {CamelElement} from "karavan-core/lib/model/IntegrationDefinition";
+import {CamelElement, MetadataLabels} from "karavan-core/lib/model/IntegrationDefinition";
 import {CamelDefinitionApiExt} from "karavan-core/lib/api/CamelDefinitionApiExt";
 import {CamelDefinitionApi} from "karavan-core/lib/api/CamelDefinitionApi";
 import {Command, EventBus} from "../utils/EventBus";
@@ -50,6 +50,30 @@ export function useRouteDesignerHook () {
         return integration.type === 'kamelet';
     }
 
+    function isSourceKamelet(): boolean {
+        if (isKamelet()){
+            const m: MetadataLabels | undefined = integration.metadata.labels;
+            return m !== undefined && m["camel.apache.org/kamelet.type"] === 'source';
+        }
+        return false;
+    }
+
+    function isSinkKamelet(): boolean {
+        if (isKamelet()){
+            const m: MetadataLabels | undefined = integration.metadata.labels;
+            return m !== undefined && m["camel.apache.org/kamelet.type"] === 'sink';
+        }
+        return false;
+    }
+
+    function isActionKamelet(): boolean {
+        if (isKamelet()){
+            const m: MetadataLabels | undefined = integration.metadata.labels;
+            return m !== undefined && m["camel.apache.org/kamelet.type"] === 'action';
+        }
+        return false;
+    }
+
     const onShowDeleteConfirmation = (id: string) => {
         let message: string;
         const uuidsToDelete:string [] = [id];
@@ -197,7 +221,7 @@ export function useRouteDesignerHook () {
         setSelectorTabIndex((parentId === undefined && parentDsl === undefined) ? 'kamelet' : 'eip');
     }
 
-    const onDslSelect = (dsl: DslMetaModel, parentId: string, position?: number | undefined) => {
+    function onDslSelect (dsl: DslMetaModel, parentId: string, position?: number | undefined) {
         switch (dsl.dsl) {
             case 'FromDefinition' :
                 const route = CamelDefinitionApi.createRouteDefinition({from: new FromDefinition({uri: dsl.uri})});
@@ -298,5 +322,6 @@ export function useRouteDesignerHook () {
     }
 
     return { deleteElement, selectElement, moveElement, onShowDeleteConfirmation, onDslSelect, openSelector,
-        createRouteConfiguration, onCommand, handleKeyDown, handleKeyUp, unselectElement, isKamelet}
+        createRouteConfiguration, onCommand, handleKeyDown, handleKeyUp, unselectElement, isKamelet, isSourceKamelet,
+        isActionKamelet, isSinkKamelet}
 }
\ No newline at end of file


[camel-karavan] 03/07: nodePrefixId for #315

Posted by ma...@apache.org.
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

commit fd3432e935445e24ef35c5d8490255528ed03df3
Author: Marat Gubaidullin <ma...@talismancloud.io>
AuthorDate: Mon Oct 23 18:50:28 2023 -0400

    nodePrefixId for #315
---
 karavan-designer/src/designer/route/useRouteDesignerHook.tsx | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/karavan-designer/src/designer/route/useRouteDesignerHook.tsx b/karavan-designer/src/designer/route/useRouteDesignerHook.tsx
index 0d97214d..f48fc850 100644
--- a/karavan-designer/src/designer/route/useRouteDesignerHook.tsx
+++ b/karavan-designer/src/designer/route/useRouteDesignerHook.tsx
@@ -27,6 +27,7 @@ import {CamelDisplayUtil} from "karavan-core/lib/api/CamelDisplayUtil";
 import {toPng} from 'html-to-image';
 import {useDesignerStore, useIntegrationStore, useSelectorStore} from "../DesignerStore";
 import {shallow} from "zustand/shallow";
+import {v4 as uuidv4} from 'uuid';
 
 export function useRouteDesignerHook () {
 
@@ -224,7 +225,8 @@ export function useRouteDesignerHook () {
     function onDslSelect (dsl: DslMetaModel, parentId: string, position?: number | undefined) {
         switch (dsl.dsl) {
             case 'FromDefinition' :
-                const route = CamelDefinitionApi.createRouteDefinition({from: new FromDefinition({uri: dsl.uri})});
+                const nodePrefixId = isKamelet() ? integration.metadata.name : 'route-' + uuidv4().substring(0,3);
+                const route = CamelDefinitionApi.createRouteDefinition({from: new FromDefinition({uri: dsl.uri}), nodePrefixId: nodePrefixId});
                 addStep(route, parentId, position)
                 break;
             case 'ToDefinition' :