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:09 UTC
[camel-karavan] 05/07: Kamelet Editor in App for #315
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="<Transparent Rectangle>"
+ 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="<Transparent Rectangle>" 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