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

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


The following commit(s) were added to refs/heads/main by this push:
     new 157c868  Clone for beans, REST and dependencies (#263)
157c868 is described below

commit 157c868f53cb0d4ca7e826dc4f302ab898481833
Author: Marat Gubaidullin <marat.gubaidul...@gmail.com>
AuthorDate: Wed Mar 30 20:17:07 2022 -0400

    Clone for beans, REST and dependencies (#263)
    
    * Clone bean and dependency
    
    * Rest clone
---
 karavan-core/src/core/api/CamelDefinitionApiExt.ts | 54 ++++++++++++++++++----
 karavan-designer/src/App.tsx                       |  2 +-
 .../src/designer/beans/BeanProperties.tsx          | 20 +++++++-
 .../src/designer/beans/BeansDesigner.tsx           |  3 +-
 .../designer/dependencies/DependenciesDesigner.tsx |  4 +-
 .../designer/dependencies/DependencyProperties.tsx | 24 +++++++++-
 .../src/designer/rest/RestDesigner.tsx             | 25 +++++++++-
 .../src/designer/route/DslProperties.tsx           | 33 ++++++++++++-
 .../src/designer/route/RouteDesigner.tsx           | 17 +++----
 9 files changed, 158 insertions(+), 24 deletions(-)

diff --git a/karavan-core/src/core/api/CamelDefinitionApiExt.ts 
b/karavan-core/src/core/api/CamelDefinitionApiExt.ts
index 1c3e309..4936d27 100644
--- a/karavan-core/src/core/api/CamelDefinitionApiExt.ts
+++ b/karavan-core/src/core/api/CamelDefinitionApiExt.ts
@@ -201,7 +201,7 @@ export class CamelDefinitionApiExt {
             flows.push(...integration.spec.flows?.filter(flow => flow.dslName 
!== 'Beans') || []);
             integration.spec.flows?.filter(flow => flow.dslName === 
'Beans').forEach(flow => {
                 const beans: NamedBeanDefinition[] = [];
-                if ((flow as Beans).beans.filter(b => b.uuid === 
bean.uuid).length === 0){
+                if ((flow as Beans).beans.filter(b => b.uuid === 
bean.uuid).length === 0) {
                     beans.push(...(flow as Beans).beans.filter(b => b.uuid !== 
bean.uuid));
                     beans.push(bean);
                 } else {
@@ -261,13 +261,25 @@ export class CamelDefinitionApiExt {
             if (rest.uuid !== restUuid) {
                 flows.push(rest);
             } else {
-                switch (method.dslName){
-                    case 'GetDefinition': rest.get = 
this.addRestMethodToRestMethods(rest.get, method); break;
-                    case 'PostDefinition': rest.post = 
this.addRestMethodToRestMethods(rest.post, method); break;
-                    case 'PutDefinition': rest.put = 
this.addRestMethodToRestMethods(rest.put, method); break;
-                    case 'PatchDefinition': rest.patch = 
this.addRestMethodToRestMethods(rest.patch, method); break;
-                    case 'DeleteDefinition': rest.delete = 
this.addRestMethodToRestMethods(rest.delete, method); break;
-                    case 'HeadDefinition': rest.head = 
this.addRestMethodToRestMethods(rest.head, method); break;
+                switch (method.dslName) {
+                    case 'GetDefinition':
+                        rest.get = this.addRestMethodToRestMethods(rest.get, 
method);
+                        break;
+                    case 'PostDefinition':
+                        rest.post = this.addRestMethodToRestMethods(rest.post, 
method);
+                        break;
+                    case 'PutDefinition':
+                        rest.put = this.addRestMethodToRestMethods(rest.put, 
method);
+                        break;
+                    case 'PatchDefinition':
+                        rest.patch = 
this.addRestMethodToRestMethods(rest.patch, method);
+                        break;
+                    case 'DeleteDefinition':
+                        rest.delete = 
this.addRestMethodToRestMethods(rest.delete, method);
+                        break;
+                    case 'HeadDefinition':
+                        rest.head = this.addRestMethodToRestMethods(rest.head, 
method);
+                        break;
                 }
                 flows.push(rest);
             }
@@ -286,6 +298,32 @@ export class CamelDefinitionApiExt {
         return elements;
     }
 
+    static findRestMethodParent = (integration: Integration, method: 
CamelElement): string | undefined => {
+        const rests: RestDefinition[] = integration.spec.flows?.filter(flow => 
flow.dslName === 'RestDefinition') || [];
+        for (let rest of rests) {
+            switch (method.dslName) {
+                case 'GetDefinition':
+                    if (rest.get?.find(m => m.uuid === method.uuid)) return 
rest.uuid;
+                    else break;
+                case 'PostDefinition':
+                    if (rest.post?.find(m => m.uuid === method.uuid)) return 
rest.uuid;
+                    else break;
+                case 'PutDefinition':
+                    if (rest.put?.find(m => m.uuid === method.uuid)) return 
rest.uuid;
+                    else break;
+                case 'PatchDefinition':
+                    if (rest.patch?.find(m => m.uuid === method.uuid)) return 
rest.uuid;
+                    else break;
+                case 'DeleteDefinition':
+                    if (rest.delete?.find(m => m.uuid === method.uuid)) return 
rest.uuid;
+                    else break;
+                case 'HeadDefinition':
+                    if (rest.head?.find(m => m.uuid === method.uuid)) return 
rest.uuid;
+                    else break;
+            }
+        }
+    }
+
     static deleteRestConfigurationFromIntegration = (integration: 
Integration): Integration => {
         const flows: any[] = [];
         integration.spec.flows?.filter(flow => flow.dslName !== 
'RestConfigurationDefinition').forEach(x => flows.push(x));
diff --git a/karavan-designer/src/App.tsx b/karavan-designer/src/App.tsx
index 403b964..35f5a3a 100644
--- a/karavan-designer/src/App.tsx
+++ b/karavan-designer/src/App.tsx
@@ -98,7 +98,7 @@ class App extends React.Component<Props, State> {
 
     save(filename: string, yaml: string, propertyOnly: boolean) {
         // console.log(filename);
-        // console.log(yaml);
+        console.log(yaml);
         // console.log(propertyOnly);
     }
 
diff --git a/karavan-designer/src/designer/beans/BeanProperties.tsx 
b/karavan-designer/src/designer/beans/BeanProperties.tsx
index a25320f..d0a9138 100644
--- a/karavan-designer/src/designer/beans/BeanProperties.tsx
+++ b/karavan-designer/src/designer/beans/BeanProperties.tsx
@@ -18,7 +18,7 @@ import React from 'react';
 import {
     Form,
     FormGroup,
-    TextInput, Button,
+    TextInput, Button, Title, Tooltip, Text, TextVariants,
 } from '@patternfly/react-core';
 import '../karavan.css';
 import "@patternfly/patternfly/patternfly.css";
@@ -31,12 +31,14 @@ import {v4 as uuidv4} from "uuid";
 import DeleteIcon from "@patternfly/react-icons/dist/js/icons/times-icon";
 import AddIcon from "@patternfly/react-icons/dist/js/icons/plus-circle-icon";
 import {IntegrationHeader} from "../utils/KaravanComponents";
+import CloneIcon from '@patternfly/react-icons/dist/esm/icons/clone-icon'
 
 interface Props {
     integration: Integration
     bean?: NamedBeanDefinition
     dark: boolean
     onChange: (bean: NamedBeanDefinition) => void
+    onClone: (bean: NamedBeanDefinition) => void
 }
 
 interface State {
@@ -103,10 +105,26 @@ export class BeanProperties extends 
React.Component<Props, State> {
         })
     }
 
+    cloneBean = () => {
+        if (this.state.bean) {
+            const bean = CamelUtil.cloneBean(this.state.bean);
+            bean.uuid = uuidv4();
+            this.props.onClone?.call(this, bean);
+        }
+    }
+
     getBeanForm() {
         const bean = this.state.bean;
         return (
             <>
+                <div className="headers">
+                    <div className="top">
+                        <Title headingLevel="h1" size="md">Bean</Title>
+                        <Tooltip content="Clone bean" position="bottom">
+                            <Button variant="link" onClick={() => 
this.cloneBean()} icon={<CloneIcon/>}/>
+                        </Tooltip>
+                    </div>
+                </div>
                 <FormGroup label="Name" fieldId="name" isRequired>
                     <TextInput className="text-field" isRequired type="text" 
id="name" name="name" value={bean?.name}
                                onChange={e => this.beanChanged("name", e)}/>
diff --git a/karavan-designer/src/designer/beans/BeansDesigner.tsx 
b/karavan-designer/src/designer/beans/BeansDesigner.tsx
index 30991b7..b61e8f6 100644
--- a/karavan-designer/src/designer/beans/BeansDesigner.tsx
+++ b/karavan-designer/src/designer/beans/BeansDesigner.tsx
@@ -123,7 +123,8 @@ export class BeansDesigner extends React.Component<Props, 
State> {
                 <BeanProperties integration={this.props.integration}
                                 bean={this.state.selectedBean}
                                 dark={this.props.dark}
-                                onChange={this.changeBean}/>
+                                onChange={this.changeBean}
+                                onClone={this.changeBean}/>
             </DrawerPanelContent>
         )
     }
diff --git 
a/karavan-designer/src/designer/dependencies/DependenciesDesigner.tsx 
b/karavan-designer/src/designer/dependencies/DependenciesDesigner.tsx
index ef1e16a..fc40f30 100644
--- a/karavan-designer/src/designer/dependencies/DependenciesDesigner.tsx
+++ b/karavan-designer/src/designer/dependencies/DependenciesDesigner.tsx
@@ -26,6 +26,7 @@ import {CamelUtil} from "karavan-core/lib/api/CamelUtil";
 import {Integration, Dependency} from 
"karavan-core/lib/model/IntegrationDefinition";
 import {CamelDefinitionApiExt} from 
"karavan-core/lib/api/CamelDefinitionApiExt";
 import {DependencyCard} from "./DependencyCard";
+import {NamedBeanDefinition} from 
"../../../../karavan-core/lib/model/CamelDefinition";
 
 interface Props {
     onSave?: (integration: Integration, propertyOnly: boolean) => void
@@ -126,7 +127,8 @@ export class DependenciesDesigner extends 
React.Component<Props, State> {
                 <DependencyProperties integration={this.props.integration}
                                       dependency={this.state.selectedDep}
                                       dark={this.props.dark}
-                                      onChange={this.changeDep}/>
+                                      onChange={this.changeDep}
+                                      onClone={this.changeDep}/>
             </DrawerPanelContent>
         )
     }
diff --git 
a/karavan-designer/src/designer/dependencies/DependencyProperties.tsx 
b/karavan-designer/src/designer/dependencies/DependencyProperties.tsx
index 5a31ea4..c629a55 100644
--- a/karavan-designer/src/designer/dependencies/DependencyProperties.tsx
+++ b/karavan-designer/src/designer/dependencies/DependencyProperties.tsx
@@ -16,21 +16,26 @@
  */
 import React from 'react';
 import {
+    Button,
     Form,
     FormGroup,
-    TextInput,
+    TextInput, Title, Tooltip,
 } from '@patternfly/react-core';
 import '../karavan.css';
 import "@patternfly/patternfly/patternfly.css";
 import {Integration, Dependency} from 
"karavan-core/lib/model/IntegrationDefinition";
 import {CamelUtil} from "karavan-core/lib/api/CamelUtil";
 import {IntegrationHeader} from "../utils/KaravanComponents";
+import {NamedBeanDefinition} from 
"../../../../karavan-core/lib/model/CamelDefinition";
+import {v4 as uuidv4} from "uuid";
+import CloneIcon from "@patternfly/react-icons/dist/esm/icons/clone-icon";
 
 interface Props {
     integration: Integration
     dependency?: Dependency
     dark: boolean
     onChange: (dependency: Dependency) => void
+    onClone: (dependency: Dependency) => void
 }
 
 interface State {
@@ -58,10 +63,27 @@ export class DependencyProperties extends 
React.Component<Props, State> {
         }
     }
 
+
+    cloneDependency = () => {
+        if (this.state.dependency) {
+            const dependency = 
CamelUtil.cloneDependency(this.state.dependency);
+            dependency.uuid = uuidv4();
+            this.props.onClone?.call(this, dependency);
+        }
+    }
+
     getDependencyForm() {
         const dependency = this.state.dependency;
         return (
             <>
+                <div className="headers">
+                    <div className="top">
+                        <Title headingLevel="h1" size="md">Dependency</Title>
+                        <Tooltip content="Clone dependency" position="bottom">
+                            <Button variant="link" onClick={() => 
this.cloneDependency()} icon={<CloneIcon/>}/>
+                        </Tooltip>
+                    </div>
+                </div>
                 <FormGroup label="Group" fieldId="group" isRequired>
                     <TextInput className="text-field" isRequired type="text" 
id="group" name="group" value={dependency?.group}
                                onChange={e => this.dependencyChanged("group", 
e)}/>
diff --git a/karavan-designer/src/designer/rest/RestDesigner.tsx 
b/karavan-designer/src/designer/rest/RestDesigner.tsx
index d7630a6..083717c 100644
--- a/karavan-designer/src/designer/rest/RestDesigner.tsx
+++ b/karavan-designer/src/designer/rest/RestDesigner.tsx
@@ -32,6 +32,7 @@ import {RestMethodSelector} from "./RestMethodSelector";
 import {DslMetaModel} from "../utils/DslMetaModel";
 import {CamelDefinitionApi} from "karavan-core/lib/api/CamelDefinitionApi";
 import {RestConfigurationCard} from "./RestConfigurationCard";
+import {v4 as uuidv4} from "uuid";
 
 interface Props {
     onSave?: (integration: Integration, propertyOnly: boolean) => void
@@ -167,6 +168,27 @@ export class RestDesigner extends React.Component<Props, 
State> {
         }
     }
 
+    cloneRest = (rest: CamelElement) => {
+        if (rest.dslName === 'RestDefinition'){
+            const cloneRest = CamelUtil.cloneStep(rest);
+            cloneRest.uuid = uuidv4();
+            const cloneIntegration = 
CamelUtil.cloneIntegration(this.state.integration);
+            const i = 
CamelDefinitionApiExt.addRestToIntegration(cloneIntegration, cloneRest);
+            this.setState({integration: i, propertyOnly: false, key: 
Math.random().toString(), selectedStep: cloneRest});
+        } else if (rest.dslName === 'RestConfigurationDefinition') {
+            // could be only one RestConfigurationDefinition
+        } else if (this.state.selectedStep) {
+            const parentId = 
CamelDefinitionApiExt.findRestMethodParent(this.state.integration, rest);
+            if (parentId){
+                const cloneRest = CamelUtil.cloneStep(rest);
+                cloneRest.uuid = uuidv4();
+                const cloneIntegration = 
CamelUtil.cloneIntegration(this.state.integration);
+                const i = 
CamelDefinitionApiExt.addRestMethodToIntegration(cloneIntegration, cloneRest, 
parentId);
+                this.setState({integration: i, key: Math.random().toString(), 
selectedStep: cloneRest, showSelector: false});
+            }
+        }
+    }
+
     selectMethod = (element: CamelElement) => {
         this.setState({selectedStep: element, showSelector: true})
     }
@@ -219,7 +241,8 @@ export class RestDesigner extends React.Component<Props, 
State> {
                     onIntegrationUpdate={this.onIntegrationUpdate}
                     onPropertyUpdate={this.onPropertyUpdate}
                     clipboardStep={undefined}
-                    onSaveClipboardStep={element => {}}
+                    isRouteDesigner={false}
+                    onClone={this.cloneRest}
                 />
             </DrawerPanelContent>
         )
diff --git a/karavan-designer/src/designer/route/DslProperties.tsx 
b/karavan-designer/src/designer/route/DslProperties.tsx
index 23ad9e9..d7540b5 100644
--- a/karavan-designer/src/designer/route/DslProperties.tsx
+++ b/karavan-designer/src/designer/route/DslProperties.tsx
@@ -38,6 +38,7 @@ import {CamelMetadataApi, PropertyMeta} from 
"karavan-core/lib/model/CamelMetada
 import {IntegrationHeader} from "../utils/KaravanComponents";
 import CopyIcon from '@patternfly/react-icons/dist/esm/icons/copy-icon'
 import PasteIcon from '@patternfly/react-icons/dist/esm/icons/paste-icon'
+import CloneIcon from "@patternfly/react-icons/dist/esm/icons/clone-icon";
 
 interface Props {
     integration: Integration,
@@ -45,7 +46,9 @@ interface Props {
     onIntegrationUpdate?: any,
     onPropertyUpdate?: (element: CamelElement, updatedUuid: string, newRoute?: 
RouteToCreate) => void
     clipboardStep?: CamelElement
-    onSaveClipboardStep: (element?: CamelElement) => void
+    onSaveClipboardStep?: (element?: CamelElement) => void
+    onClone?: (element: CamelElement) => void
+    isRouteDesigner: boolean
 }
 
 interface State {
@@ -111,6 +114,12 @@ export class DslProperties extends React.Component<Props, 
State> {
         }
     }
 
+    cloneElement = () => {
+        if (this.state.step) {
+            this.props.onClone?.call(this, this.state.step);
+        }
+    }
+
     componentDidUpdate = (prevProps: Readonly<Props>, prevState: 
Readonly<State>, snapshot?: any) => {
         if (prevProps.step !== this.props.step) {
             this.setStep(this.props.step);
@@ -124,7 +133,7 @@ export class DslProperties extends React.Component<Props, 
State> {
         });
     }
 
-    getComponentHeader = (): JSX.Element => {
+    getRouteHeader= (): JSX.Element => {
         const title = this.state.step && CamelUi.getTitle(this.state.step)
         const kamelet = this.state.step && CamelUi.getKamelet(this.state.step)
         const description = this.state.step && kamelet
@@ -142,11 +151,31 @@ export class DslProperties extends React.Component<Props, 
State> {
                     </Tooltip>
                 </div>
                 <Text component={TextVariants.p}>{description}</Text>
+            </div>
+        )
+    }
 
+    getClonableElementHeader = (): JSX.Element => {
+        const title = this.state.step && CamelUi.getTitle(this.state.step)
+        const description = this.state.step?.dslName ? 
CamelMetadataApi.getCamelModelMetadataByClassName(this.state.step?.dslName)?.description
 : title;
+        return (
+            <div className="headers">
+                <div className="top">
+                    <Title headingLevel="h1" size="md">{title}</Title>
+                    <Tooltip content="Clone element" position="bottom">
+                        <Button variant="link" onClick={() => 
this.cloneElement()} icon={<CloneIcon/>}/>
+                    </Tooltip>
+                </div>
+                <Text component={TextVariants.p}>{description}</Text>
             </div>
         )
     }
 
+    getComponentHeader = (): JSX.Element => {
+        if (this.props.isRouteDesigner) return this.getRouteHeader()
+        else return this.getClonableElementHeader();
+    }
+
     getProperties = (): PropertyMeta[] => {
         const dslName = this.state.step?.dslName;
         return CamelDefinitionApiExt.getElementProperties(dslName)
diff --git a/karavan-designer/src/designer/route/RouteDesigner.tsx 
b/karavan-designer/src/designer/route/RouteDesigner.tsx
index 18e4f90..d493ba1 100644
--- a/karavan-designer/src/designer/route/RouteDesigner.tsx
+++ b/karavan-designer/src/designer/route/RouteDesigner.tsx
@@ -313,14 +313,15 @@ export class RouteDesigner extends React.Component<Props, 
State> {
     getPropertiesPanel() {
         return (
             <DrawerPanelContent isResizable hasNoBorder defaultSize={'400px'} 
maxSize={'800px'} minSize={'300px'}>
-                    <DslProperties ref={this.state.ref}
-                        integration={this.state.integration}
-                        step={this.state.selectedStep}
-                        onIntegrationUpdate={this.onIntegrationUpdate}
-                        onPropertyUpdate={this.onPropertyUpdate}
-                        clipboardStep={this.state.clipboardStep}
-                        onSaveClipboardStep={this.saveToClipboard}
-                    />
+                <DslProperties ref={this.state.ref}
+                               integration={this.state.integration}
+                               step={this.state.selectedStep}
+                               onIntegrationUpdate={this.onIntegrationUpdate}
+                               onPropertyUpdate={this.onPropertyUpdate}
+                               clipboardStep={this.state.clipboardStep}
+                               isRouteDesigner={true}
+                               onSaveClipboardStep={this.saveToClipboard}
+                />
             </DrawerPanelContent>
         )
     }

Reply via email to