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/23 19:57:41 UTC

[camel-karavan] branch main updated: #905

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 56ed9809 #905
56ed9809 is described below

commit 56ed9809965f9419d048eaaad3a106863eead58c
Author: Marat Gubaidullin <ma...@talismancloud.io>
AuthorDate: Mon Oct 23 15:57:32 2023 -0400

    #905
---
 karavan-core/src/core/api/CamelDefinitionYaml.ts   |   2 +-
 karavan-core/test/beans.spec.ts                    |  37 +++++-
 karavan-core/test/beans3.yaml                      |   7 +
 .../src/designer/beans/BeanProperties.tsx          | 145 +++++++++++++++++----
 .../src/designer/beans/BeansDesigner.tsx           |   5 +-
 .../src/designer/beans/BeanProperties.tsx          | 145 +++++++++++++++++----
 karavan-space/src/designer/beans/BeansDesigner.tsx |   5 +-
 .../webui/src/designer/beans/BeanProperties.tsx    | 145 +++++++++++++++++----
 .../webui/src/designer/beans/BeansDesigner.tsx     |   5 +-
 9 files changed, 401 insertions(+), 95 deletions(-)

diff --git a/karavan-core/src/core/api/CamelDefinitionYaml.ts b/karavan-core/src/core/api/CamelDefinitionYaml.ts
index aa6f425b..7916801a 100644
--- a/karavan-core/src/core/api/CamelDefinitionYaml.ts
+++ b/karavan-core/src/core/api/CamelDefinitionYaml.ts
@@ -120,7 +120,7 @@ export class CamelDefinitionYaml {
         return result;
     };
 
-    static yamlDump = (integration: Integration): string => {
+    static yamlDump = (integration: any): string => {
         return yaml.dump(integration, {
             noRefs: false,
             noArrayIndent: false,
diff --git a/karavan-core/test/beans.spec.ts b/karavan-core/test/beans.spec.ts
index 8a4ff26b..7e372f34 100644
--- a/karavan-core/test/beans.spec.ts
+++ b/karavan-core/test/beans.spec.ts
@@ -14,17 +14,20 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {expect} from 'chai';
+import { expect } from 'chai';
 import * as fs from 'fs';
 import 'mocha';
-import {CamelDefinitionYaml} from "../src/core/api/CamelDefinitionYaml";
+import { CamelDefinitionYaml } from '../src/core/api/CamelDefinitionYaml';
+import { Integration } from '../lib/model/IntegrationDefinition';
+import { RegistryBeanDefinition } from '../src/core/model/CamelDefinition';
+import * as yaml from 'js-yaml';
 
 
 describe('bean configuration', () => {
 
     it('Read beans from plain YAML', () => {
-        const yaml = fs.readFileSync('test/beans1.yaml', {encoding: 'utf8', flag: 'r'});
-        const i = CamelDefinitionYaml.yamlToIntegration("beans.yaml", yaml);
+        const yaml = fs.readFileSync('test/beans1.yaml', { encoding: 'utf8', flag: 'r' });
+        const i = CamelDefinitionYaml.yamlToIntegration('beans.yaml', yaml);
         expect(i.metadata.name).to.equal('beans.yaml');
         expect(i.kind).to.equal('Integration');
         expect(i.spec.flows?.length).to.equal(3);
@@ -38,12 +41,12 @@ describe('bean configuration', () => {
     });
 
     it('Read beans from Integration', () => {
-        const yaml = fs.readFileSync('test/beans2.yaml',{encoding:'utf8', flag:'r'});
-        const i = CamelDefinitionYaml.yamlToIntegration("beans.yaml", yaml);
+        const yaml = fs.readFileSync('test/beans2.yaml', { encoding: 'utf8', flag: 'r' });
+        const i = CamelDefinitionYaml.yamlToIntegration('beans.yaml', yaml);
         expect(i.metadata.name).to.equal('Beans');
         expect(i.kind).to.equal('Integration');
         expect(i.spec.flows?.length).to.equal(3);
-        expect(i.type).to.equal("crd");
+        expect(i.type).to.equal('crd');
         if (i.spec.flows) {
             expect(i.spec.flows[2].beans[0].name).to.equal('myNested');
             expect(i.spec.flows[2].beans[0].type).to.equal('${MyBean.class.name}');
@@ -52,4 +55,24 @@ describe('bean configuration', () => {
         }
     });
 
+    class Val {
+
+    }
+
+    it('Bean constructor', () => {
+        const text = fs.readFileSync('test/beans3.yaml', { encoding: 'utf8', flag: 'r' });
+        const i = CamelDefinitionYaml.yamlToIntegration('beans.yaml', text);
+
+        const b = Integration.createNew('beans');
+        const bean = new RegistryBeanDefinition({
+            name: 'Name', type: 'Type', constructors: {
+                0: 'zero',
+                1: 'one',
+                2: 'two',
+            },
+        });
+        b.spec.flows?.push(bean);
+        // console.log(CamelDefinitionYaml.integrationToYaml(b))
+    });
+
 });
diff --git a/karavan-core/test/beans3.yaml b/karavan-core/test/beans3.yaml
new file mode 100644
index 00000000..1f3a137a
--- /dev/null
+++ b/karavan-core/test/beans3.yaml
@@ -0,0 +1,7 @@
+- beans:
+    - constructors:
+        0: zero
+        1: one
+        2: two
+      name: Name
+      type: Type
\ No newline at end of file
diff --git a/karavan-designer/src/designer/beans/BeanProperties.tsx b/karavan-designer/src/designer/beans/BeanProperties.tsx
index 455291c4..df72edca 100644
--- a/karavan-designer/src/designer/beans/BeanProperties.tsx
+++ b/karavan-designer/src/designer/beans/BeanProperties.tsx
@@ -58,9 +58,11 @@ export function BeanProperties (props: Props) {
     const [infrastructureSelectorProperty, setInfrastructureSelectorProperty] = useState<string | undefined>(undefined);
     const [infrastructureSelectorUuid, setInfrastructureSelectorUuid] = useState<string | undefined>(undefined);
     const [properties, setProperties] = useState<Map<string, [string, string, boolean]>>(new Map<string, [string, string, boolean]>());
+    const [constructors, setConstructors] = useState<Map<string, [number, string, boolean]>>(new Map<string, [number, string, boolean]>());
 
     useEffect(()=> {
         setProperties(preparePropertiesMap((selectedStep as RegistryBeanDefinition)?.properties))
+        setConstructors(prepareConstructorsMap((selectedStep as RegistryBeanDefinition)?.constructors))
     }, [selectedStep?.uuid])
 
     function preparePropertiesMap (properties: any): Map<string, [string, string, boolean]>  {
@@ -71,6 +73,14 @@ export function BeanProperties (props: Props) {
         return result;
     }
 
+    function prepareConstructorsMap (constructors: any): Map<string, [number, string, boolean]>  {
+        const result = new Map<string, [number, string, boolean]>();
+        if (constructors) {
+            Object.keys(constructors).forEach((k, i, a) => result.set(uuidv4(), [parseInt(k), constructors[k], false]));
+        }
+        return result;
+    }
+
     function onBeanPropertyUpdate ()  {
         if (selectedStep) {
             const bean = CamelUtil.cloneBean(selectedStep as RegistryBeanDefinition);
@@ -81,6 +91,16 @@ export function BeanProperties (props: Props) {
         }
     }
 
+    function onBeanConstructorsUpdate ()  {
+        if (selectedStep) {
+            const bean = CamelUtil.cloneBean(selectedStep as RegistryBeanDefinition);
+            const beanConstructors: any = {};
+            constructors.forEach((p: any) => beanConstructors[p[0]] = p[1]);
+            bean.constructors = beanConstructors;
+            props.onChange(bean);
+        }
+    }
+
     function beanFieldChanged (fieldId: string, value: string) {
         if (selectedStep) {
             const bean = CamelUtil.cloneBean(selectedStep as RegistryBeanDefinition);
@@ -97,6 +117,14 @@ export function BeanProperties (props: Props) {
         onBeanPropertyUpdate();
     }
 
+    function constructorChanged (uuid: string, key: number, value: string, showPassword: boolean)  {
+        setConstructors(prevState => {
+            prevState.set(uuid, [key, value, showPassword]);
+            return prevState;
+        });
+        onBeanConstructorsUpdate();
+    }
+
     function propertyDeleted (uuid: string)  {
         setProperties(prevState => {
             prevState.delete(uuid);
@@ -105,6 +133,14 @@ export function BeanProperties (props: Props) {
         onBeanPropertyUpdate();
     }
 
+    function constructorDeleted (uuid: string)  {
+        setConstructors(prevState => {
+            prevState.delete(uuid);
+            return prevState;
+        })
+        onBeanConstructorsUpdate();
+    }
+
     function selectInfrastructure (value: string)  {
         const propertyId = infrastructureSelectorProperty;
         const uuid = infrastructureSelectorUuid;
@@ -163,26 +199,57 @@ export function BeanProperties (props: Props) {
                 </Popover>
         )
     }
-    function getBeanForm() {
-        const bean = (selectedStep as RegistryBeanDefinition);
+
+    function getBeanConstructors() {
+        return (
+            <FormGroup label="Constructors" fieldId="constructors" className="bean-properties">
+                {Array.from(constructors.entries()).map((v, index, array) => {
+                    const i = v[0];
+                    const key = v[1][0];
+                    const value = v[1][1];
+                    const showPassword = v[1][2];
+                    const isSecret = false;
+                    return (
+                        <div key={"key-" + i} className="bean-property">
+                            <TextInput placeholder="Argument Index" className="text-field" isRequired type="text" id={"key-" + i}
+                                       name={"key-" + i} value={key}
+                                       onChange={(_, beanFieldName) => {
+                                           constructorChanged(i, parseInt(beanFieldName) , value, showPassword)
+                                       }}/>
+                            <InputGroup>
+                                <InputGroupItem isFill>
+                                    <TextInput
+                                        placeholder="Argument Value"
+                                        type={isSecret && !showPassword ? "password" : "text"}
+                                        className="text-field"
+                                        isRequired
+                                        id={"value-" + i}
+                                        name={"value-" + i}
+                                        value={value}
+                                        onChange={(_, value) => {
+                                            constructorChanged(i, key, value, showPassword)
+                                        }}/>
+                                </InputGroupItem>
+                                {isSecret && <Tooltip position="bottom-end" content={showPassword ? "Hide" : "Show"}>
+                                    <Button variant="control" onClick={e => constructorChanged(i, key, value, !showPassword)}>
+                                        {showPassword ? <ShowIcon/> : <HideIcon/>}
+                                    </Button>
+                                </Tooltip>}
+                            </InputGroup>
+                            <Button variant="link" className="delete-button" onClick={e => constructorDeleted(i)}>
+                                <DeleteIcon/>
+                            </Button>
+                        </div>
+                    )
+                })}
+                <Button variant="link" className="add-button" onClick={e => constructorChanged(uuidv4(), constructors.size, '', false)}>
+                    <AddIcon/>Add argument</Button>
+            </FormGroup>
+        )
+    }
+
+    function getBeanProperties() {
         return (
-            <>
-                <div className="headers">
-                    <div className="top">
-                        <Title headingLevel="h1" size="md">Bean</Title>
-                        <Tooltip content="Clone bean" position="bottom">
-                            <Button variant="link" onClick={() => cloneBean()} icon={<CloneIcon/>}/>
-                        </Tooltip>
-                    </div>
-                </div>
-                <FormGroup label="Name" fieldId="name" isRequired labelIcon={getLabelIcon("Name", "Bean name used as a reference ex: myBean")}>
-                    <TextInput className="text-field" isRequired type="text" id="name" name="name" value={bean?.name}
-                                onChange={(_, value)=> beanFieldChanged("name", value)}/>
-                </FormGroup>
-                <FormGroup label="Type" fieldId="type" isRequired labelIcon={getLabelIcon("Type", "Bean class Canonical Name ex: org.demo.MyBean")}>
-                    <TextInput className="text-field" isRequired type="text" id="type" name="type" value={bean?.type}
-                        onChange={(_, value) => beanFieldChanged("type", value)}/>
-                </FormGroup>
                 <FormGroup label="Properties" fieldId="properties" className="bean-properties">
                     {Array.from(properties.entries()).map((v, index, array) => {
                         const i = v[0];
@@ -196,16 +263,16 @@ export function BeanProperties (props: Props) {
                             <div key={"key-" + i} className="bean-property">
                                 <TextInput placeholder="Bean Field Name" className="text-field" isRequired type="text" id={"key-" + i}
                                            name={"key-" + i} value={key}
-                                            onChange={(_, beanFieldName) => {
-                                                propertyChanged(i, beanFieldName, value, showPassword)
-                                            }}/>
+                                           onChange={(_, beanFieldName) => {
+                                               propertyChanged(i, beanFieldName, value, showPassword)
+                                           }}/>
                                 <InputGroup>
                                     {inInfrastructure &&
                                         <Tooltip position="bottom-end" content="Select value from Infrastructure">
-                                        <Button variant="control" onClick={e => openInfrastructureSelector(i, key)}>
-                                            {icon}
-                                        </Button>
-                                    </Tooltip>}
+                                            <Button variant="control" onClick={e => openInfrastructureSelector(i, key)}>
+                                                {icon}
+                                            </Button>
+                                        </Tooltip>}
                                     <InputGroupItem isFill>
                                         <TextInput
                                             placeholder="Bean Field Value"
@@ -232,6 +299,32 @@ export function BeanProperties (props: Props) {
                     <Button variant="link" className="add-button" onClick={e => propertyChanged(uuidv4(), '', '', false)}>
                         <AddIcon/>Add property</Button>
                 </FormGroup>
+        )
+    }
+
+
+    function getBeanForm() {
+        const bean = (selectedStep as RegistryBeanDefinition);
+        return (
+            <>
+                <div className="headers">
+                    <div className="top">
+                        <Title headingLevel="h1" size="md">Bean</Title>
+                        <Tooltip content="Clone bean" position="bottom">
+                            <Button variant="link" onClick={() => cloneBean()} icon={<CloneIcon/>}/>
+                        </Tooltip>
+                    </div>
+                </div>
+                <FormGroup label="Name" fieldId="name" isRequired labelIcon={getLabelIcon("Name", "Bean name used as a reference ex: myBean")}>
+                    <TextInput className="text-field" isRequired type="text" id="name" name="name" value={bean?.name}
+                                onChange={(_, value)=> beanFieldChanged("name", value)}/>
+                </FormGroup>
+                <FormGroup label="Type" fieldId="type" isRequired labelIcon={getLabelIcon("Type", "Bean class Canonical Name ex: org.demo.MyBean")}>
+                    <TextInput className="text-field" isRequired type="text" id="type" name="type" value={bean?.type}
+                        onChange={(_, value) => beanFieldChanged("type", value)}/>
+                </FormGroup>
+                {getBeanConstructors()}
+                {getBeanProperties()}
             </>
         )
     }
diff --git a/karavan-designer/src/designer/beans/BeansDesigner.tsx b/karavan-designer/src/designer/beans/BeansDesigner.tsx
index 8cd99f25..2e7b67e0 100644
--- a/karavan-designer/src/designer/beans/BeansDesigner.tsx
+++ b/karavan-designer/src/designer/beans/BeansDesigner.tsx
@@ -123,9 +123,8 @@ export function BeansDesigner() {
                                  }}
                         >
                             {beans?.map((bean, index) => (
-                                <GalleryItem>
-                                    <BeanCard key={bean.uuid + index}
-                                              bean={bean}
+                                <GalleryItem key={bean.uuid + index}>
+                                    <BeanCard bean={bean}
                                               selectElement={selectBean}
                                               deleteElement={onShowDeleteConfirmation}
                                     />
diff --git a/karavan-space/src/designer/beans/BeanProperties.tsx b/karavan-space/src/designer/beans/BeanProperties.tsx
index 455291c4..df72edca 100644
--- a/karavan-space/src/designer/beans/BeanProperties.tsx
+++ b/karavan-space/src/designer/beans/BeanProperties.tsx
@@ -58,9 +58,11 @@ export function BeanProperties (props: Props) {
     const [infrastructureSelectorProperty, setInfrastructureSelectorProperty] = useState<string | undefined>(undefined);
     const [infrastructureSelectorUuid, setInfrastructureSelectorUuid] = useState<string | undefined>(undefined);
     const [properties, setProperties] = useState<Map<string, [string, string, boolean]>>(new Map<string, [string, string, boolean]>());
+    const [constructors, setConstructors] = useState<Map<string, [number, string, boolean]>>(new Map<string, [number, string, boolean]>());
 
     useEffect(()=> {
         setProperties(preparePropertiesMap((selectedStep as RegistryBeanDefinition)?.properties))
+        setConstructors(prepareConstructorsMap((selectedStep as RegistryBeanDefinition)?.constructors))
     }, [selectedStep?.uuid])
 
     function preparePropertiesMap (properties: any): Map<string, [string, string, boolean]>  {
@@ -71,6 +73,14 @@ export function BeanProperties (props: Props) {
         return result;
     }
 
+    function prepareConstructorsMap (constructors: any): Map<string, [number, string, boolean]>  {
+        const result = new Map<string, [number, string, boolean]>();
+        if (constructors) {
+            Object.keys(constructors).forEach((k, i, a) => result.set(uuidv4(), [parseInt(k), constructors[k], false]));
+        }
+        return result;
+    }
+
     function onBeanPropertyUpdate ()  {
         if (selectedStep) {
             const bean = CamelUtil.cloneBean(selectedStep as RegistryBeanDefinition);
@@ -81,6 +91,16 @@ export function BeanProperties (props: Props) {
         }
     }
 
+    function onBeanConstructorsUpdate ()  {
+        if (selectedStep) {
+            const bean = CamelUtil.cloneBean(selectedStep as RegistryBeanDefinition);
+            const beanConstructors: any = {};
+            constructors.forEach((p: any) => beanConstructors[p[0]] = p[1]);
+            bean.constructors = beanConstructors;
+            props.onChange(bean);
+        }
+    }
+
     function beanFieldChanged (fieldId: string, value: string) {
         if (selectedStep) {
             const bean = CamelUtil.cloneBean(selectedStep as RegistryBeanDefinition);
@@ -97,6 +117,14 @@ export function BeanProperties (props: Props) {
         onBeanPropertyUpdate();
     }
 
+    function constructorChanged (uuid: string, key: number, value: string, showPassword: boolean)  {
+        setConstructors(prevState => {
+            prevState.set(uuid, [key, value, showPassword]);
+            return prevState;
+        });
+        onBeanConstructorsUpdate();
+    }
+
     function propertyDeleted (uuid: string)  {
         setProperties(prevState => {
             prevState.delete(uuid);
@@ -105,6 +133,14 @@ export function BeanProperties (props: Props) {
         onBeanPropertyUpdate();
     }
 
+    function constructorDeleted (uuid: string)  {
+        setConstructors(prevState => {
+            prevState.delete(uuid);
+            return prevState;
+        })
+        onBeanConstructorsUpdate();
+    }
+
     function selectInfrastructure (value: string)  {
         const propertyId = infrastructureSelectorProperty;
         const uuid = infrastructureSelectorUuid;
@@ -163,26 +199,57 @@ export function BeanProperties (props: Props) {
                 </Popover>
         )
     }
-    function getBeanForm() {
-        const bean = (selectedStep as RegistryBeanDefinition);
+
+    function getBeanConstructors() {
+        return (
+            <FormGroup label="Constructors" fieldId="constructors" className="bean-properties">
+                {Array.from(constructors.entries()).map((v, index, array) => {
+                    const i = v[0];
+                    const key = v[1][0];
+                    const value = v[1][1];
+                    const showPassword = v[1][2];
+                    const isSecret = false;
+                    return (
+                        <div key={"key-" + i} className="bean-property">
+                            <TextInput placeholder="Argument Index" className="text-field" isRequired type="text" id={"key-" + i}
+                                       name={"key-" + i} value={key}
+                                       onChange={(_, beanFieldName) => {
+                                           constructorChanged(i, parseInt(beanFieldName) , value, showPassword)
+                                       }}/>
+                            <InputGroup>
+                                <InputGroupItem isFill>
+                                    <TextInput
+                                        placeholder="Argument Value"
+                                        type={isSecret && !showPassword ? "password" : "text"}
+                                        className="text-field"
+                                        isRequired
+                                        id={"value-" + i}
+                                        name={"value-" + i}
+                                        value={value}
+                                        onChange={(_, value) => {
+                                            constructorChanged(i, key, value, showPassword)
+                                        }}/>
+                                </InputGroupItem>
+                                {isSecret && <Tooltip position="bottom-end" content={showPassword ? "Hide" : "Show"}>
+                                    <Button variant="control" onClick={e => constructorChanged(i, key, value, !showPassword)}>
+                                        {showPassword ? <ShowIcon/> : <HideIcon/>}
+                                    </Button>
+                                </Tooltip>}
+                            </InputGroup>
+                            <Button variant="link" className="delete-button" onClick={e => constructorDeleted(i)}>
+                                <DeleteIcon/>
+                            </Button>
+                        </div>
+                    )
+                })}
+                <Button variant="link" className="add-button" onClick={e => constructorChanged(uuidv4(), constructors.size, '', false)}>
+                    <AddIcon/>Add argument</Button>
+            </FormGroup>
+        )
+    }
+
+    function getBeanProperties() {
         return (
-            <>
-                <div className="headers">
-                    <div className="top">
-                        <Title headingLevel="h1" size="md">Bean</Title>
-                        <Tooltip content="Clone bean" position="bottom">
-                            <Button variant="link" onClick={() => cloneBean()} icon={<CloneIcon/>}/>
-                        </Tooltip>
-                    </div>
-                </div>
-                <FormGroup label="Name" fieldId="name" isRequired labelIcon={getLabelIcon("Name", "Bean name used as a reference ex: myBean")}>
-                    <TextInput className="text-field" isRequired type="text" id="name" name="name" value={bean?.name}
-                                onChange={(_, value)=> beanFieldChanged("name", value)}/>
-                </FormGroup>
-                <FormGroup label="Type" fieldId="type" isRequired labelIcon={getLabelIcon("Type", "Bean class Canonical Name ex: org.demo.MyBean")}>
-                    <TextInput className="text-field" isRequired type="text" id="type" name="type" value={bean?.type}
-                        onChange={(_, value) => beanFieldChanged("type", value)}/>
-                </FormGroup>
                 <FormGroup label="Properties" fieldId="properties" className="bean-properties">
                     {Array.from(properties.entries()).map((v, index, array) => {
                         const i = v[0];
@@ -196,16 +263,16 @@ export function BeanProperties (props: Props) {
                             <div key={"key-" + i} className="bean-property">
                                 <TextInput placeholder="Bean Field Name" className="text-field" isRequired type="text" id={"key-" + i}
                                            name={"key-" + i} value={key}
-                                            onChange={(_, beanFieldName) => {
-                                                propertyChanged(i, beanFieldName, value, showPassword)
-                                            }}/>
+                                           onChange={(_, beanFieldName) => {
+                                               propertyChanged(i, beanFieldName, value, showPassword)
+                                           }}/>
                                 <InputGroup>
                                     {inInfrastructure &&
                                         <Tooltip position="bottom-end" content="Select value from Infrastructure">
-                                        <Button variant="control" onClick={e => openInfrastructureSelector(i, key)}>
-                                            {icon}
-                                        </Button>
-                                    </Tooltip>}
+                                            <Button variant="control" onClick={e => openInfrastructureSelector(i, key)}>
+                                                {icon}
+                                            </Button>
+                                        </Tooltip>}
                                     <InputGroupItem isFill>
                                         <TextInput
                                             placeholder="Bean Field Value"
@@ -232,6 +299,32 @@ export function BeanProperties (props: Props) {
                     <Button variant="link" className="add-button" onClick={e => propertyChanged(uuidv4(), '', '', false)}>
                         <AddIcon/>Add property</Button>
                 </FormGroup>
+        )
+    }
+
+
+    function getBeanForm() {
+        const bean = (selectedStep as RegistryBeanDefinition);
+        return (
+            <>
+                <div className="headers">
+                    <div className="top">
+                        <Title headingLevel="h1" size="md">Bean</Title>
+                        <Tooltip content="Clone bean" position="bottom">
+                            <Button variant="link" onClick={() => cloneBean()} icon={<CloneIcon/>}/>
+                        </Tooltip>
+                    </div>
+                </div>
+                <FormGroup label="Name" fieldId="name" isRequired labelIcon={getLabelIcon("Name", "Bean name used as a reference ex: myBean")}>
+                    <TextInput className="text-field" isRequired type="text" id="name" name="name" value={bean?.name}
+                                onChange={(_, value)=> beanFieldChanged("name", value)}/>
+                </FormGroup>
+                <FormGroup label="Type" fieldId="type" isRequired labelIcon={getLabelIcon("Type", "Bean class Canonical Name ex: org.demo.MyBean")}>
+                    <TextInput className="text-field" isRequired type="text" id="type" name="type" value={bean?.type}
+                        onChange={(_, value) => beanFieldChanged("type", value)}/>
+                </FormGroup>
+                {getBeanConstructors()}
+                {getBeanProperties()}
             </>
         )
     }
diff --git a/karavan-space/src/designer/beans/BeansDesigner.tsx b/karavan-space/src/designer/beans/BeansDesigner.tsx
index 8cd99f25..2e7b67e0 100644
--- a/karavan-space/src/designer/beans/BeansDesigner.tsx
+++ b/karavan-space/src/designer/beans/BeansDesigner.tsx
@@ -123,9 +123,8 @@ export function BeansDesigner() {
                                  }}
                         >
                             {beans?.map((bean, index) => (
-                                <GalleryItem>
-                                    <BeanCard key={bean.uuid + index}
-                                              bean={bean}
+                                <GalleryItem key={bean.uuid + index}>
+                                    <BeanCard bean={bean}
                                               selectElement={selectBean}
                                               deleteElement={onShowDeleteConfirmation}
                                     />
diff --git a/karavan-web/karavan-app/src/main/webui/src/designer/beans/BeanProperties.tsx b/karavan-web/karavan-app/src/main/webui/src/designer/beans/BeanProperties.tsx
index 455291c4..df72edca 100644
--- a/karavan-web/karavan-app/src/main/webui/src/designer/beans/BeanProperties.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/designer/beans/BeanProperties.tsx
@@ -58,9 +58,11 @@ export function BeanProperties (props: Props) {
     const [infrastructureSelectorProperty, setInfrastructureSelectorProperty] = useState<string | undefined>(undefined);
     const [infrastructureSelectorUuid, setInfrastructureSelectorUuid] = useState<string | undefined>(undefined);
     const [properties, setProperties] = useState<Map<string, [string, string, boolean]>>(new Map<string, [string, string, boolean]>());
+    const [constructors, setConstructors] = useState<Map<string, [number, string, boolean]>>(new Map<string, [number, string, boolean]>());
 
     useEffect(()=> {
         setProperties(preparePropertiesMap((selectedStep as RegistryBeanDefinition)?.properties))
+        setConstructors(prepareConstructorsMap((selectedStep as RegistryBeanDefinition)?.constructors))
     }, [selectedStep?.uuid])
 
     function preparePropertiesMap (properties: any): Map<string, [string, string, boolean]>  {
@@ -71,6 +73,14 @@ export function BeanProperties (props: Props) {
         return result;
     }
 
+    function prepareConstructorsMap (constructors: any): Map<string, [number, string, boolean]>  {
+        const result = new Map<string, [number, string, boolean]>();
+        if (constructors) {
+            Object.keys(constructors).forEach((k, i, a) => result.set(uuidv4(), [parseInt(k), constructors[k], false]));
+        }
+        return result;
+    }
+
     function onBeanPropertyUpdate ()  {
         if (selectedStep) {
             const bean = CamelUtil.cloneBean(selectedStep as RegistryBeanDefinition);
@@ -81,6 +91,16 @@ export function BeanProperties (props: Props) {
         }
     }
 
+    function onBeanConstructorsUpdate ()  {
+        if (selectedStep) {
+            const bean = CamelUtil.cloneBean(selectedStep as RegistryBeanDefinition);
+            const beanConstructors: any = {};
+            constructors.forEach((p: any) => beanConstructors[p[0]] = p[1]);
+            bean.constructors = beanConstructors;
+            props.onChange(bean);
+        }
+    }
+
     function beanFieldChanged (fieldId: string, value: string) {
         if (selectedStep) {
             const bean = CamelUtil.cloneBean(selectedStep as RegistryBeanDefinition);
@@ -97,6 +117,14 @@ export function BeanProperties (props: Props) {
         onBeanPropertyUpdate();
     }
 
+    function constructorChanged (uuid: string, key: number, value: string, showPassword: boolean)  {
+        setConstructors(prevState => {
+            prevState.set(uuid, [key, value, showPassword]);
+            return prevState;
+        });
+        onBeanConstructorsUpdate();
+    }
+
     function propertyDeleted (uuid: string)  {
         setProperties(prevState => {
             prevState.delete(uuid);
@@ -105,6 +133,14 @@ export function BeanProperties (props: Props) {
         onBeanPropertyUpdate();
     }
 
+    function constructorDeleted (uuid: string)  {
+        setConstructors(prevState => {
+            prevState.delete(uuid);
+            return prevState;
+        })
+        onBeanConstructorsUpdate();
+    }
+
     function selectInfrastructure (value: string)  {
         const propertyId = infrastructureSelectorProperty;
         const uuid = infrastructureSelectorUuid;
@@ -163,26 +199,57 @@ export function BeanProperties (props: Props) {
                 </Popover>
         )
     }
-    function getBeanForm() {
-        const bean = (selectedStep as RegistryBeanDefinition);
+
+    function getBeanConstructors() {
+        return (
+            <FormGroup label="Constructors" fieldId="constructors" className="bean-properties">
+                {Array.from(constructors.entries()).map((v, index, array) => {
+                    const i = v[0];
+                    const key = v[1][0];
+                    const value = v[1][1];
+                    const showPassword = v[1][2];
+                    const isSecret = false;
+                    return (
+                        <div key={"key-" + i} className="bean-property">
+                            <TextInput placeholder="Argument Index" className="text-field" isRequired type="text" id={"key-" + i}
+                                       name={"key-" + i} value={key}
+                                       onChange={(_, beanFieldName) => {
+                                           constructorChanged(i, parseInt(beanFieldName) , value, showPassword)
+                                       }}/>
+                            <InputGroup>
+                                <InputGroupItem isFill>
+                                    <TextInput
+                                        placeholder="Argument Value"
+                                        type={isSecret && !showPassword ? "password" : "text"}
+                                        className="text-field"
+                                        isRequired
+                                        id={"value-" + i}
+                                        name={"value-" + i}
+                                        value={value}
+                                        onChange={(_, value) => {
+                                            constructorChanged(i, key, value, showPassword)
+                                        }}/>
+                                </InputGroupItem>
+                                {isSecret && <Tooltip position="bottom-end" content={showPassword ? "Hide" : "Show"}>
+                                    <Button variant="control" onClick={e => constructorChanged(i, key, value, !showPassword)}>
+                                        {showPassword ? <ShowIcon/> : <HideIcon/>}
+                                    </Button>
+                                </Tooltip>}
+                            </InputGroup>
+                            <Button variant="link" className="delete-button" onClick={e => constructorDeleted(i)}>
+                                <DeleteIcon/>
+                            </Button>
+                        </div>
+                    )
+                })}
+                <Button variant="link" className="add-button" onClick={e => constructorChanged(uuidv4(), constructors.size, '', false)}>
+                    <AddIcon/>Add argument</Button>
+            </FormGroup>
+        )
+    }
+
+    function getBeanProperties() {
         return (
-            <>
-                <div className="headers">
-                    <div className="top">
-                        <Title headingLevel="h1" size="md">Bean</Title>
-                        <Tooltip content="Clone bean" position="bottom">
-                            <Button variant="link" onClick={() => cloneBean()} icon={<CloneIcon/>}/>
-                        </Tooltip>
-                    </div>
-                </div>
-                <FormGroup label="Name" fieldId="name" isRequired labelIcon={getLabelIcon("Name", "Bean name used as a reference ex: myBean")}>
-                    <TextInput className="text-field" isRequired type="text" id="name" name="name" value={bean?.name}
-                                onChange={(_, value)=> beanFieldChanged("name", value)}/>
-                </FormGroup>
-                <FormGroup label="Type" fieldId="type" isRequired labelIcon={getLabelIcon("Type", "Bean class Canonical Name ex: org.demo.MyBean")}>
-                    <TextInput className="text-field" isRequired type="text" id="type" name="type" value={bean?.type}
-                        onChange={(_, value) => beanFieldChanged("type", value)}/>
-                </FormGroup>
                 <FormGroup label="Properties" fieldId="properties" className="bean-properties">
                     {Array.from(properties.entries()).map((v, index, array) => {
                         const i = v[0];
@@ -196,16 +263,16 @@ export function BeanProperties (props: Props) {
                             <div key={"key-" + i} className="bean-property">
                                 <TextInput placeholder="Bean Field Name" className="text-field" isRequired type="text" id={"key-" + i}
                                            name={"key-" + i} value={key}
-                                            onChange={(_, beanFieldName) => {
-                                                propertyChanged(i, beanFieldName, value, showPassword)
-                                            }}/>
+                                           onChange={(_, beanFieldName) => {
+                                               propertyChanged(i, beanFieldName, value, showPassword)
+                                           }}/>
                                 <InputGroup>
                                     {inInfrastructure &&
                                         <Tooltip position="bottom-end" content="Select value from Infrastructure">
-                                        <Button variant="control" onClick={e => openInfrastructureSelector(i, key)}>
-                                            {icon}
-                                        </Button>
-                                    </Tooltip>}
+                                            <Button variant="control" onClick={e => openInfrastructureSelector(i, key)}>
+                                                {icon}
+                                            </Button>
+                                        </Tooltip>}
                                     <InputGroupItem isFill>
                                         <TextInput
                                             placeholder="Bean Field Value"
@@ -232,6 +299,32 @@ export function BeanProperties (props: Props) {
                     <Button variant="link" className="add-button" onClick={e => propertyChanged(uuidv4(), '', '', false)}>
                         <AddIcon/>Add property</Button>
                 </FormGroup>
+        )
+    }
+
+
+    function getBeanForm() {
+        const bean = (selectedStep as RegistryBeanDefinition);
+        return (
+            <>
+                <div className="headers">
+                    <div className="top">
+                        <Title headingLevel="h1" size="md">Bean</Title>
+                        <Tooltip content="Clone bean" position="bottom">
+                            <Button variant="link" onClick={() => cloneBean()} icon={<CloneIcon/>}/>
+                        </Tooltip>
+                    </div>
+                </div>
+                <FormGroup label="Name" fieldId="name" isRequired labelIcon={getLabelIcon("Name", "Bean name used as a reference ex: myBean")}>
+                    <TextInput className="text-field" isRequired type="text" id="name" name="name" value={bean?.name}
+                                onChange={(_, value)=> beanFieldChanged("name", value)}/>
+                </FormGroup>
+                <FormGroup label="Type" fieldId="type" isRequired labelIcon={getLabelIcon("Type", "Bean class Canonical Name ex: org.demo.MyBean")}>
+                    <TextInput className="text-field" isRequired type="text" id="type" name="type" value={bean?.type}
+                        onChange={(_, value) => beanFieldChanged("type", value)}/>
+                </FormGroup>
+                {getBeanConstructors()}
+                {getBeanProperties()}
             </>
         )
     }
diff --git a/karavan-web/karavan-app/src/main/webui/src/designer/beans/BeansDesigner.tsx b/karavan-web/karavan-app/src/main/webui/src/designer/beans/BeansDesigner.tsx
index 8cd99f25..2e7b67e0 100644
--- a/karavan-web/karavan-app/src/main/webui/src/designer/beans/BeansDesigner.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/designer/beans/BeansDesigner.tsx
@@ -123,9 +123,8 @@ export function BeansDesigner() {
                                  }}
                         >
                             {beans?.map((bean, index) => (
-                                <GalleryItem>
-                                    <BeanCard key={bean.uuid + index}
-                                              bean={bean}
+                                <GalleryItem key={bean.uuid + index}>
+                                    <BeanCard bean={bean}
                                               selectElement={selectBean}
                                               deleteElement={onShowDeleteConfirmation}
                                     />