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 11a07098e6e2efbb1c52f70f8f0cacb811ba4817
Author: Marat Gubaidullin <marat.gubaidul...@gmail.com>
AuthorDate: Mon Jan 9 18:33:30 2023 -0500

    RouteConfiguration in Route tab #596
---
 karavan-app/src/main/webui/package-lock.json       |  45 +-
 .../designer/route/property/DslPropertyField.tsx   | 730 +++++++++++++++++++++
 karavan-core/src/core/api/CamelDefinitionApiExt.ts |  17 +-
 3 files changed, 777 insertions(+), 15 deletions(-)

diff --git a/karavan-app/src/main/webui/package-lock.json 
b/karavan-app/src/main/webui/package-lock.json
index 796ed1a..76ae782 100644
--- a/karavan-app/src/main/webui/package-lock.json
+++ b/karavan-app/src/main/webui/package-lock.json
@@ -39,6 +39,28 @@
         "monaco-editor": "0.29.1"
       }
     },
+    "../../../../karavan-core": {
+      "version": "3.18.6",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@types/js-yaml": "^4.0.5",
+        "@types/uuid": "^8.3.4",
+        "typescript": "^4.5.5",
+        "uuid": "8.3.2"
+      },
+      "devDependencies": {
+        "@types/chai": "^4.3.0",
+        "@types/dagre": "^0.7.47",
+        "@types/localforage": "0.0.34",
+        "@types/mocha": "^9.1.0",
+        "@types/node": "^17.0.23",
+        "chai": "^4.3.4",
+        "cross-env": "^7.0.3",
+        "fs": "^0.0.1-security",
+        "mocha": "^9.2.0",
+        "ts-node": "^10.4.0"
+      }
+    },
     "../karavan-core": {
       "extraneous": true
     },
@@ -10889,15 +10911,8 @@
       }
     },
     "node_modules/karavan-core": {
-      "version": "3.18.6",
-      "resolved": "file:../../../../karavan-core",
-      "license": "Apache-2.0",
-      "dependencies": {
-        "@types/js-yaml": "^4.0.5",
-        "@types/uuid": "^8.3.4",
-        "typescript": "^4.5.5",
-        "uuid": "8.3.2"
-      }
+      "resolved": "../../../../karavan-core",
+      "link": true
     },
     "node_modules/keycloak-js": {
       "version": "19.0.1",
@@ -24472,10 +24487,20 @@
       }
     },
     "karavan-core": {
-      "version": "3.18.6",
+      "version": "file:../../../../karavan-core",
       "requires": {
+        "@types/chai": "^4.3.0",
+        "@types/dagre": "^0.7.47",
         "@types/js-yaml": "^4.0.5",
+        "@types/localforage": "0.0.34",
+        "@types/mocha": "^9.1.0",
+        "@types/node": "^17.0.23",
         "@types/uuid": "^8.3.4",
+        "chai": "^4.3.4",
+        "cross-env": "^7.0.3",
+        "fs": "^0.0.1-security",
+        "mocha": "^9.2.0",
+        "ts-node": "^10.4.0",
         "typescript": "^4.5.5",
         "uuid": "8.3.2"
       }
diff --git 
a/karavan-app/src/main/webui/src/designer/route/property/DslPropertyField.tsx 
b/karavan-app/src/main/webui/src/designer/route/property/DslPropertyField.tsx
new file mode 100644
index 0000000..a05575f
--- /dev/null
+++ 
b/karavan-app/src/main/webui/src/designer/route/property/DslPropertyField.tsx
@@ -0,0 +1,730 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import React from 'react';
+import {
+    FormGroup,
+    TextInput,
+    Popover,
+    Switch,
+    Select,
+    SelectVariant,
+    SelectDirection,
+    SelectOption,
+    ExpandableSection,
+    TextArea,
+    Chip,
+    TextInputGroup,
+    TextInputGroupMain,
+    TextInputGroupUtilities,
+    ChipGroup,
+    Button,
+    Text,
+    Tooltip,
+    Card,
+    InputGroup,
+} from '@patternfly/react-core';
+import '../../karavan.css';
+import "@patternfly/patternfly/patternfly.css";
+import HelpIcon from "@patternfly/react-icons/dist/js/icons/help-icon";
+import DeleteIcon from 
"@patternfly/react-icons/dist/js/icons/times-circle-icon";
+import {CamelUtil} from "karavan-core/lib/api/CamelUtil";
+import {PropertyMeta} from "karavan-core/lib/model/CamelMetadata";
+import {CamelDefinitionApiExt} from 
"karavan-core/lib/api/CamelDefinitionApiExt";
+import {ExpressionField} from "./ExpressionField";
+import {CamelUi, RouteToCreate} from "../../utils/CamelUi";
+import {ComponentParameterField} from "./ComponentParameterField";
+import {DataFormatDefinition} from "karavan-core/lib/model/CamelDefinition";
+import {Integration, CamelElement} from 
"karavan-core/lib/model/IntegrationDefinition";
+import {KameletPropertyField} from "./KameletPropertyField";
+import {ExpressionDefinition} from "karavan-core/lib/model/CamelDefinition";
+import PlusIcon from "@patternfly/react-icons/dist/esm/icons/plus-icon";
+import {ObjectField} from "./ObjectField";
+import {CamelDefinitionApi} from "karavan-core/lib/api/CamelDefinitionApi";
+import AddIcon from "@patternfly/react-icons/dist/js/icons/plus-circle-icon";
+import {MediaTypes} from "../../utils/MediaTypes";
+import {ComponentProperty} from "karavan-core/lib/model/ComponentModels";
+import CompressIcon from "@patternfly/react-icons/dist/js/icons/compress-icon";
+import ExpandIcon from "@patternfly/react-icons/dist/js/icons/expand-icon";
+import KubernetesIcon from 
"@patternfly/react-icons/dist/js/icons/openshift-icon";
+import {KubernetesSelector} from "./KubernetesSelector";
+import {KubernetesAPI} from "../../utils/KubernetesAPI";
+import EditorIcon from "@patternfly/react-icons/dist/js/icons/code-icon";
+import {TemplateApi} from "karavan-core/lib/api/TemplateApi";
+import {ModalEditor} from "./ModalEditor";
+import {KaravanInstance} from "../../KaravanDesigner";
+
+interface Props {
+    property: PropertyMeta,
+    value: any,
+    onChange?: (fieldId: string, value: string | number | boolean | any, 
newRoute?: RouteToCreate) => void,
+    onExpressionChange?: (propertyName: string, exp: ExpressionDefinition) => 
void,
+    onDataFormatChange?: (value: DataFormatDefinition) => void,
+    onParameterChange?: (parameter: string, value: string | number | boolean | 
any, pathParameter?: boolean, newRoute?: RouteToCreate) => void,
+    element?: CamelElement
+    integration: Integration,
+    hideLabel?: boolean,
+    dslLanguage?: [string, string, string],
+    dark: boolean
+}
+
+interface State {
+    selectStatus: Map<string, boolean>,
+    isShowAdvanced: Map<string, boolean>,
+    arrayValues: Map<string, string>,
+    showEditor: boolean
+    showKubernetesSelector: boolean
+    kubernetesSelectorProperty?: string
+    customCode?: string
+    ref: any
+}
+
+export class DslPropertyField extends React.Component<Props, State> {
+
+    public state: State = {
+        selectStatus: new Map<string, boolean>(),
+        arrayValues: new Map<string, string>(),
+        isShowAdvanced: new Map<string, boolean>(),
+        showEditor: false,
+        showKubernetesSelector: false,
+        ref: React.createRef(),
+    };
+
+    openSelect = (propertyName: string, isExpanded: boolean) => {
+        this.setState({selectStatus: new Map<string, boolean>([[propertyName, 
isExpanded]])});
+    }
+
+    clearSelection = (propertyName: string) => {
+        this.setState({selectStatus: new Map<string, boolean>([[propertyName, 
false]])});
+    };
+
+    isSelectOpen = (propertyName: string): boolean => {
+        return this.state.selectStatus.has(propertyName) && 
this.state.selectStatus.get(propertyName) === true;
+    }
+
+    propertyChanged = (fieldId: string, value: string | number | boolean | 
any, newRoute?: RouteToCreate) => {
+        if (fieldId === 'id' && 
CamelDefinitionApiExt.hasElementWithId(this.props.integration, value)) {
+            value = this.props.value;
+        }
+        this.props.onChange?.call(this, fieldId, value, newRoute);
+        this.setState({selectStatus: new Map<string, boolean>([[fieldId, 
false]])});
+    }
+
+    arrayChanged = (fieldId: string, value: string) => {
+        const tv = this.state.arrayValues;
+        tv.set(fieldId, value);
+        this.setState({arrayValues: tv});
+    }
+
+    arrayDeleteValue = (fieldId: string, element: string) => {
+        const property: PropertyMeta = this.props.property;
+        let value = this.props.value;
+        if (property.isArray && property.type === 'string') {
+            this.propertyChanged(fieldId, (value as any).filter((x: string) => 
x !== element))
+        }
+    }
+
+    arraySave = (fieldId: string) => {
+        const newValue = this.state.arrayValues.get(fieldId);
+        const property: PropertyMeta = this.props.property;
+        let value = this.props.value;
+        if (property.isArray && property.type === 'string') {
+            if (value) (value as any).push(newValue)
+            else value = [newValue];
+        }
+        this.propertyChanged(fieldId, value);
+        this.arrayChanged(fieldId, "");
+    }
+
+    getLabel = (property: PropertyMeta, value: any) => {
+        if (!this.isMultiValueField(property) && property.isObject && 
!property.isArray && !["ExpressionDefinition"].includes(property.type)) {
+            const tooltip = value ? "Delete " + property.name : "Add " + 
property.name;
+            const className = value ? "change-button delete-button" : 
"change-button add-button";
+            const x = value ? undefined : 
CamelDefinitionApi.createStep(property.type, {});
+            const icon = value ? (<DeleteIcon noVerticalAlign/>) : (<AddIcon 
noVerticalAlign/>);
+            return (
+                <div style={{display: "flex"}}>
+                    <Text>{property.displayName} </Text>
+                    <Tooltip position={"top"} content={<div>{tooltip}</div>}>
+                        <button className={className} onClick={e => 
this.props.onChange?.call(this, property.name, x)} aria-label="Add element">
+                            {icon}
+                        </button>
+                    </Tooltip>
+                </div>
+            )
+        } else if (!["ExpressionDefinition"].includes(property.type)) {
+            return CamelUtil.capitalizeName(property.displayName);
+        }
+    }
+
+    isUriReadOnly = (property: PropertyMeta): boolean => {
+        const dslName: string = this.props.element?.dslName || '';
+        return property.name === 'uri' && !['ToDynamicDefinition', 
'WireTapDefinition'].includes(dslName)
+    }
+
+    selectKubernetes = (value: string) => {
+        // check if there is a selection
+        const textVal = this.state.ref.current;
+        const cursorStart = textVal.selectionStart;
+        const cursorEnd = textVal.selectionEnd;
+        if (cursorStart !== cursorEnd) {
+            const prevValue = this.props.value;
+            const selectedText = prevValue.substring(cursorStart, cursorEnd)
+            value = prevValue.replace(selectedText, value);
+        }
+        const propertyName = this.state.kubernetesSelectorProperty;
+        if (propertyName) {
+            if (value.startsWith("config") || value.startsWith("secret")) 
value = "{{" + value + "}}";
+            this.propertyChanged(propertyName, value);
+            this.setState({showKubernetesSelector: false, 
kubernetesSelectorProperty: undefined})
+        }
+    }
+
+    openKubernetesSelector = (propertyName: string) => {
+        this.setState({kubernetesSelectorProperty: propertyName, 
showKubernetesSelector: true});
+    }
+
+    closeKubernetesSelector = () => {
+        this.setState({showKubernetesSelector: false})
+    }
+
+    getKubernetesSelectorModal() {
+        return (
+            <KubernetesSelector
+                dark={false}
+                isOpen={this.state.showKubernetesSelector}
+                onClose={() => this.closeKubernetesSelector()}
+                onSelect={this.selectKubernetes}/>)
+    }
+
+    getStringInput = (property: PropertyMeta, value: any) => {
+        const showEditor = this.state.showEditor;
+        const inKubernetes = KubernetesAPI.inKubernetes;
+        const noKubeSelectorButton = ["uri", "id", "description", 
"group"].includes(property.name);
+        return (<InputGroup>
+            {inKubernetes && !showEditor && !noKubeSelectorButton &&
+                <Tooltip position="bottom-end" content="Select from 
Kubernetes">
+                    <Button variant="control" onClick={e => 
this.openKubernetesSelector(property.name)}>
+                        <KubernetesIcon/>
+                    </Button>
+                </Tooltip>}
+            {(!showEditor || property.secret) && <TextInput
+                ref={this.state.ref}
+                className="text-field" isRequired 
isReadOnly={this.isUriReadOnly(property)}
+                type={['integer', 'number'].includes(property.type) ? 'number' 
: (property.secret ? "password" : "text")}
+                id={property.name} name={property.name}
+                value={value?.toString()}
+                onChange={e => this.propertyChanged(property.name, ['integer', 
'number'].includes(property.type) ? Number(e) : e)}/>
+            }
+            {showEditor && !property.secret && <TextArea
+                ref={this.state.ref}
+                autoResize={true}
+                className="text-field" isRequired 
isReadOnly={this.isUriReadOnly(property)}
+                type="text"
+                id={property.name} name={property.name}
+                value={value?.toString()}
+                onChange={e => this.propertyChanged(property.name, ['integer', 
'number'].includes(property.type) ? Number(e) : e)}/>
+            }
+            {!property.secret &&
+                <Tooltip position="bottom-end" content={showEditor ? "Change 
to TextField" : "Change to Text Area"}>
+                    <Button variant="control" onClick={e => 
this.setState({showEditor: !showEditor})}>
+                        {showEditor ? <CompressIcon/> : <ExpandIcon/>}
+                    </Button>
+                </Tooltip>
+            }
+        </InputGroup>)
+    }
+
+    showCode = (name: string, javaType: string) => {
+        const {property} = this.props;
+        KaravanInstance.getProps().onGetCustomCode.call(this, name, 
property.javaType).then(value => {
+            if (value === undefined) {
+                const code = TemplateApi.generateCode(property.javaType, name);
+                this.setState({customCode: code, showEditor: true})
+            } else {
+                this.setState({customCode: value, showEditor: true})
+            }
+        }).catch(reason => console.log(reason))
+    }
+
+    getJavaTypeGeneratedInput = (property: PropertyMeta, value: any) => {
+        const {dslLanguage, dark} = this.props;
+        const {showEditor, customCode} = this.state;
+        return (<InputGroup>
+            <TextInput
+                ref={this.state.ref}
+                className="text-field" isRequired 
isReadOnly={this.isUriReadOnly(property)}
+                type="text"
+                id={property.name} name={property.name}
+                value={value?.toString()}
+                onChange={e => this.propertyChanged(property.name, 
CamelUtil.capitalizeName(e?.replace(/\s/g, '')))}/>
+            <Tooltip position="bottom-end" content={"Create Java Class"}>
+                <Button variant="control" onClick={e => this.showCode(value, 
property.javaType)}>
+                    <PlusIcon/>
+                </Button>
+            </Tooltip>
+            <ModalEditor property={property}
+                         customCode={customCode}
+                         showEditor={showEditor}
+                         dark={dark}
+                         dslLanguage={dslLanguage}
+                         title="Java Class"
+                         onClose={() => this.setState({showEditor: false})}
+                         onSave={(fieldId, value1) => {
+                             this.propertyChanged(fieldId, value);
+                             
KaravanInstance.getProps().onSaveCustomCode?.call(this, value, value1);
+                             this.setState({showEditor: false});
+                         }}/>
+        </InputGroup>)
+    }
+
+    getTextArea = (property: PropertyMeta, value: any) => {
+        const {dslLanguage, dark} = this.props;
+        const {showEditor} = this.state;
+        return (
+            <InputGroup>
+                <TextArea
+                    autoResize
+                    className="text-field" isRequired
+                    type={"text"}
+                    id={property.name} name={property.name}
+                    height={"100px"}
+                    value={value?.toString()}
+                    onChange={e => this.propertyChanged(property.name, e)}/>
+                <Tooltip position="bottom-end" content={"Show Editor"}>
+                    <Button variant="control" onClick={e => 
this.setState({showEditor: !showEditor})}>
+                        <EditorIcon/>
+                    </Button>
+                </Tooltip>
+                <ModalEditor property={property}
+                             customCode={value}
+                             showEditor={showEditor}
+                             dark={dark}
+                             dslLanguage={dslLanguage}
+                             title={`Expression (${dslLanguage?.[0]})`}
+                             onClose={() => this.setState({showEditor: false})}
+                             onSave={(fieldId, value1) => {
+                                 this.propertyChanged(fieldId, value1);
+                                 this.setState({showEditor: false});
+                             }}/>
+            </InputGroup>
+        )
+    }
+
+    getExpressionField = (property: PropertyMeta, value: any) => {
+        return (
+            <div className="expression">
+                <ExpressionField property={property}
+                                 value={value}
+                                 
onExpressionChange={this.props.onExpressionChange}
+                                 integration={this.props.integration}
+                                 dark={this.props.dark}/>
+            </div>
+        )
+    }
+
+    getObjectField = (property: PropertyMeta, value: any) => {
+        return (
+            <div className="object">
+                {value && <ObjectField property={property}
+                                       value={value}
+                                       onPropertyUpdate={this.props.onChange}
+                                       integration={this.props.integration}
+                                       dark={this.props.dark}/>}
+            </div>
+        )
+    }
+
+    getSwitch = (property: PropertyMeta, value: any) => {
+        const isChecked = value !== undefined ? Boolean(value) : 
Boolean(property.defaultValue !== undefined && property.defaultValue === 
'true');
+        return (
+            <Switch
+                id={property.name} name={property.name}
+                value={value?.toString()}
+                aria-label={property.name}
+                isChecked={isChecked}
+                onChange={e => this.propertyChanged(property.name, e)}/>
+        )
+    }
+
+    getSelectBean = (property: PropertyMeta, value: any) => {
+        const selectOptions: JSX.Element[] = [];
+        const beans = CamelUi.getBeans(this.props.integration);
+        if (beans) {
+            selectOptions.push(<SelectOption key={0} value={"Select..."} 
isPlaceholder/>);
+            selectOptions.push(...beans.map((bean) => <SelectOption 
key={bean.name} value={bean.name} description={bean.type}/>));
+        }
+        return (
+            <Select
+                variant={SelectVariant.single}
+                aria-label={property.name}
+                onToggle={isExpanded => {
+                    this.openSelect(property.name, isExpanded)
+                }}
+                onSelect={(e, value, isPlaceholder) => 
this.propertyChanged(property.name, (!isPlaceholder ? value : undefined))}
+                selections={value}
+                isOpen={this.isSelectOpen(property.name)}
+                aria-labelledby={property.name}
+                direction={SelectDirection.down}
+            >
+                {selectOptions}
+            </Select>
+        )
+    }
+
+    getSelect = (property: PropertyMeta, value: any) => {
+        const selectOptions: JSX.Element[] = []
+        if (property.enumVals && property.enumVals.length > 0) {
+            selectOptions.push(<SelectOption key={0} value={"Select " + 
property.name} isPlaceholder/>);
+            selectOptions.push(...property.enumVals.split(',').map((value: 
string) =>
+                <SelectOption key={value} value={value.trim()}/>));
+        }
+        return (
+            <Select
+                variant={SelectVariant.single}
+                aria-label={property.name}
+                onToggle={isExpanded => {
+                    this.openSelect(property.name, isExpanded)
+                }}
+                onSelect={(e, value, isPlaceholder) => 
this.propertyChanged(property.name, (!isPlaceholder ? value : undefined))}
+                selections={value}
+                isOpen={this.isSelectOpen(property.name)}
+                aria-labelledby={property.name}
+                direction={SelectDirection.down}
+            >
+                {selectOptions}
+            </Select>
+        )
+    }
+
+    getMediaTypeSelectOptions(filter?: string) {
+        return filter
+            ? MediaTypes.filter(mt => mt.includes(filter)).map((value: string) 
=> <SelectOption key={value} value={value.trim()}/>)
+            : MediaTypes.map((value: string) => <SelectOption key={value} 
value={value.trim()}/>);
+    }
+
+    getMediaTypeSelect = (property: PropertyMeta, value: any) => {
+        return (
+            <Select
+                placeholderText="Select Media Type"
+                variant={SelectVariant.typeahead}
+                aria-label={property.name}
+                onToggle={isExpanded => {
+                    this.openSelect(property.name, isExpanded)
+                }}
+                onSelect={(e, value, isPlaceholder) => 
this.propertyChanged(property.name, (!isPlaceholder ? value : undefined))}
+                selections={value}
+                isOpen={this.isSelectOpen(property.name)}
+                isCreatable={false}
+                isInputFilterPersisted={false}
+                onFilter={(e, text) => this.getMediaTypeSelectOptions(text)}
+                aria-labelledby={property.name}
+                direction={SelectDirection.down}
+            >
+                {this.getMediaTypeSelectOptions()}
+            </Select>
+        )
+    }
+
+    canBeInternalUri = (property: PropertyMeta, element?: CamelElement): 
boolean => {
+        if (element?.dslName === 'WireTapDefinition' && property.name === 
'uri') {
+            return true;
+        } else if (element?.dslName === 'SagaDefinition' && ['compensation', 
'completion'].includes(property.name)) {
+            return true;
+        } else if (element && ['GetDefinition', 'PostDefinition', 
'PutDefinition', 'PatchDefinition', 'DeleteDefinition', 
'HeadDefinition'].includes(element?.dslName) && property.name === 'to') {
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    canBeMediaType = (property: PropertyMeta, element?: CamelElement): boolean 
=> {
+        if (element
+            && ['RestDefinition', 'GetDefinition', 'PostDefinition', 
'PutDefinition', 'PatchDefinition', 'DeleteDefinition', 
'HeadDefinition'].includes(element?.dslName)
+            && ['consumes', 'produces'].includes(property.name)) {
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    javaTypeGenerated = (property: PropertyMeta): boolean => {
+        return property.javaType.length !== 0;
+    }
+
+    getInternalUriSelect = (property: PropertyMeta, value: any) => {
+        const selectOptions: JSX.Element[] = [];
+        const urls = CamelUi.getInternalRouteUris(this.props.integration, 
"direct");
+        urls.push(...CamelUi.getInternalRouteUris(this.props.integration, 
"seda"));
+        if (urls && urls.length > 0) {
+            selectOptions.push(...urls.map((value: string) =>
+                <SelectOption key={value} value={value.trim()}/>));
+        }
+        return (
+            <Select
+                placeholderText="Select or type an URI"
+                variant={SelectVariant.typeahead}
+                aria-label={property.name}
+                onClear={event => this.clearSelection(property.name)}
+                onToggle={isExpanded => {
+                    this.openSelect(property.name, isExpanded)
+                }}
+                onSelect={(e, value, isPlaceholder) => {
+                    const url = value.toString().split(":");
+                    const newRoute = !urls.includes(value.toString()) && 
(['direct', 'seda'].includes(url[0])) ? new RouteToCreate(url[0], url[1]) : 
undefined;
+                    this.propertyChanged(property.name, (!isPlaceholder ? 
value : undefined), newRoute)
+                }}
+                selections={value}
+                isOpen={this.isSelectOpen(property.name)}
+                isCreatable={true}
+                isInputFilterPersisted={true}
+                aria-labelledby={property.name}
+                direction={SelectDirection.down}
+            >
+                {selectOptions}
+            </Select>
+        )
+    }
+
+    onMultiValueObjectUpdate = (index: number, fieldId: string, value: 
CamelElement) => {
+        const mValue = [...this.props.value];
+        mValue[index] = value;
+        this.props.onChange?.call(this, fieldId, mValue);
+    }
+
+    isKeyValueObject(property: PropertyMeta) {
+        const props = 
CamelDefinitionApiExt.getElementProperties(property.type);
+        return props.length === 2 && props.filter(p => p.name === 
'key').length === 1 && props.filter(p => p.name === 'value').length === 1;
+    }
+
+    getMultiObjectFieldProps(property: PropertyMeta, value: any, v: any, 
index: number, hideLabel: boolean = false) {
+        return (<>
+            <div className="object">
+                {value && <ObjectField property={property}
+                                       hideLabel={hideLabel}
+                                       value={v}
+                                       onPropertyUpdate={(f, v) => 
this.onMultiValueObjectUpdate(index, f, v)}
+                                       integration={this.props.integration}
+                                       dark={this.props.dark}/>}
+            </div>
+            <Button variant="link" className="delete-button" onClick={e => {
+                const v = Array.from(value);
+                v.splice(index, 1);
+                this.propertyChanged(property.name, v);
+            }}><DeleteIcon/></Button>
+        </>)
+    }
+
+    getMultiValueObjectField = (property: PropertyMeta, value: any) => {
+        const isKeyValue = this.isKeyValueObject(property);
+        return (
+            <div>
+                {value && Array.from(value).map((v: any, index: number) => {
+                    if (isKeyValue)
+                        return <div key={property + "-" + index} 
className="object-key-value">
+                            {this.getMultiObjectFieldProps(property, value, v, 
index, index > 0)}
+                        </div>
+                    else
+                        return <Card key={property + "-" + index} 
className="object-value">
+                            {this.getMultiObjectFieldProps(property, value, v, 
index)}
+                        </Card>
+                })}
+                <Button variant="link" className="add-button"
+                        onClick={e => this.propertyChanged(property.name, 
[...value, CamelDefinitionApi.createStep(property.type, {})])}><AddIcon/>{"Add 
" + property.displayName}
+                </Button>
+            </div>
+        )
+    }
+
+    getMultiValueField = (property: PropertyMeta, value: any) => {
+        return (
+            <div>
+                <TextInputGroup className="input-group">
+                    <TextInputGroupMain 
value={this.state.arrayValues.get(property.name)} onChange={e => 
this.arrayChanged(property.name, e)} onKeyUp={e => {
+                        if (e.key === 'Enter') this.arraySave(property.name)
+                    }}>
+                        <ChipGroup>
+                            {value && Array.from(value).map((v: any, index: 
number) => (
+                                <Chip key={"chip-" + index} className="chip" 
onClick={() => this.arrayDeleteValue(property.name, v)}>{v.toString()}</Chip>))}
+                        </ChipGroup>
+                    </TextInputGroupMain>
+                    <TextInputGroupUtilities>
+                        <Button variant="plain" onClick={e => 
this.arraySave(property.name)} aria-label="Add element">
+                            <PlusIcon/>
+                        </Button>
+                    </TextInputGroupUtilities>
+                </TextInputGroup>
+            </div>
+        )
+    }
+
+    getKameletParameters = () => {
+        const element = this.props.element;
+        const requiredParameters = 
CamelUtil.getKameletRequiredParameters(element);
+        return (
+            <div className="parameters">
+                {CamelUtil.getKameletProperties(element).map(property =>
+                    <KameletPropertyField
+                        key={property.id}
+                        property={property}
+                        
value={CamelDefinitionApiExt.getParametersValue(element, property.id)}
+                        required={requiredParameters?.includes(property.id)}
+                        onParameterChange={this.props.onParameterChange}
+                    />)}
+            </div>
+        )
+    }
+
+    getMainComponentParameters = (properties: ComponentProperty[]) => {
+        return (
+            <div className="parameters">
+                {properties.map(kp => {
+                    // console.log(kp);
+                    // 
console.log(CamelDefinitionApiExt.getParametersValue(this.props.element, 
kp.name, kp.kind === 'path'));
+                    return (<ComponentParameterField
+                        key={kp.name}
+                        property={kp}
+                        element={this.props.element}
+                        integration={this.props.integration}
+                        
value={CamelDefinitionApiExt.getParametersValue(this.props.element, kp.name, 
kp.kind === 'path')}
+                        onParameterChange={this.props.onParameterChange}
+                    />)
+                })}
+            </div>
+        )
+    }
+
+    getExpandableComponentParameters = (properties: ComponentProperty[], 
label: string) => {
+        return (
+            <ExpandableSection
+                toggleText={label}
+                onToggle={isExpanded => {
+                    this.setState(state => {
+                        state.isShowAdvanced.set(label, 
!state.isShowAdvanced.get(label));
+                        return {isShowAdvanced: state.isShowAdvanced};
+                    })
+                }}
+                isExpanded={this.state.isShowAdvanced.has(label) && 
this.state.isShowAdvanced.get(label)}>
+                <div className="parameters">
+                    {properties.map(kp =>
+                        <ComponentParameterField
+                            key={kp.name}
+                            property={kp}
+                            integration={this.props.integration}
+                            
value={CamelDefinitionApiExt.getParametersValue(this.props.element, kp.name, 
kp.kind === 'path')}
+                            onParameterChange={this.props.onParameterChange}
+                        />
+                    )}
+                </div>
+            </ExpandableSection>
+        )
+    }
+
+    getLabelIcon = (property: PropertyMeta) => {
+        return (
+            property.description
+                ? <Popover
+                    position={"left"}
+                    headerContent={property.displayName}
+                    bodyContent={property.description}
+                    footerContent={
+                        <div>
+                            {property.defaultValue !== undefined && 
property.defaultValue.toString().trim().length > 0 && <div>{"Default: " + 
property.defaultValue}</div>}
+                            {property.required && <b>Required</b>}
+                        </div>
+                    }>
+                    <button type="button" aria-label="More info" onClick={e => 
{
+                        e.preventDefault();
+                        e.stopPropagation();
+                    }} className="pf-c-form__group-label-help">
+                        <HelpIcon noVerticalAlign/>
+                    </button>
+                </Popover>
+                : <div></div>
+        )
+    }
+
+
+    isMultiValueField = (property: PropertyMeta): boolean => {
+        return ['string'].includes(property.type) && property.name !== 
'expression' && property.isArray && !property.enumVals;
+    }
+
+    getComponentParameters(property: PropertyMeta) {
+        const properties = 
CamelUtil.getComponentProperties(this.props.element);
+        const propertiesMain = properties.filter(p => 
!p.label.includes("advanced") && !p.label.includes("security") && 
!p.label.includes("scheduler"));
+        const propertiesAdvanced = properties.filter(p => 
p.label.includes("advanced"));
+        const propertiesScheduler = properties.filter(p => 
p.label.includes("scheduler"));
+        const propertiesSecurity = properties.filter(p => 
p.label.includes("security"));
+        return (
+            <>
+                {property.name === 'parameters' && 
this.getMainComponentParameters(propertiesMain)}
+                {property.name === 'parameters' && this.props.element && 
propertiesScheduler.length > 0
+                    && 
this.getExpandableComponentParameters(propertiesScheduler, "Scheduler 
parameters")}
+                {property.name === 'parameters' && this.props.element && 
propertiesSecurity.length > 0
+                    && 
this.getExpandableComponentParameters(propertiesSecurity, "Security 
parameters")}
+                {property.name === 'parameters' && this.props.element && 
propertiesAdvanced.length > 0
+                    && 
this.getExpandableComponentParameters(propertiesAdvanced, "Advanced 
parameters")}
+            </>
+        )
+    }
+
+    render() {
+        const isKamelet = CamelUtil.isKameletComponent(this.props.element);
+        const property: PropertyMeta = this.props.property;
+        const value = this.props.value;
+        return (
+            <div>
+                <FormGroup
+                    label={this.props.hideLabel ? undefined : 
this.getLabel(property, value)}
+                    isRequired={property.required}
+                    fieldId={property.name}
+                    labelIcon={this.getLabelIcon(property)}>
+                    {value && ["ExpressionDefinition", 
"ExpressionSubElementDefinition"].includes(property.type)
+                        && this.getExpressionField(property, value)}
+                    {property.isObject && !property.isArray && 
!["ExpressionDefinition", 
"ExpressionSubElementDefinition"].includes(property.type)
+                        && this.getObjectField(property, value)}
+                    {property.isObject && property.isArray && 
!this.isMultiValueField(property)
+                        && this.getMultiValueObjectField(property, value)}
+                    {property.name === 'expression' && property.type === 
"string" && !property.isArray
+                        && this.getTextArea(property, value)}
+                    {this.canBeInternalUri(property, this.props.element)
+                        && this.getInternalUriSelect(property, value)}
+                    {this.canBeMediaType(property, this.props.element)
+                        && this.getMediaTypeSelect(property, value)}
+                    {this.javaTypeGenerated(property)
+                        && this.getJavaTypeGeneratedInput(property, value)}
+                    {['string', 'duration', 'integer', 
'number'].includes(property.type) && property.name !== 'expression' && 
!property.name.endsWith("Ref")
+                        && !property.isArray && !property.enumVals
+                        && !this.canBeInternalUri(property, this.props.element)
+                        && !this.canBeMediaType(property, this.props.element)
+                        && !this.javaTypeGenerated(property)
+                        && this.getStringInput(property, value)}
+                    {['string'].includes(property.type) && 
property.name.endsWith("Ref") && !property.isArray && !property.enumVals
+                        && this.getSelectBean(property, value)}
+                    {this.isMultiValueField(property)
+                        && this.getMultiValueField(property, value)}
+                    {property.type === 'boolean'
+                        && this.getSwitch(property, value)}
+                    {property.enumVals
+                        && this.getSelect(property, value)}
+                    {isKamelet && property.name === 'parameters' && 
this.getKameletParameters()}
+                    {!isKamelet && property.name === 'parameters' && 
this.getComponentParameters(property)}
+                </FormGroup>
+                {this.getKubernetesSelectorModal()}
+            </div>
+        )
+    }
+}
diff --git a/karavan-core/src/core/api/CamelDefinitionApiExt.ts 
b/karavan-core/src/core/api/CamelDefinitionApiExt.ts
index d41c954..ea080d1 100644
--- a/karavan-core/src/core/api/CamelDefinitionApiExt.ts
+++ b/karavan-core/src/core/api/CamelDefinitionApiExt.ts
@@ -100,7 +100,7 @@ export class CamelDefinitionApiExt {
 
     static findElementMetaInIntegration = (integration: Integration, uuid: 
string): CamelElementMeta => {
         const i = CamelUtil.cloneIntegration(integration);
-        const routes = i.spec.flows?.filter(f => f.dslName === 
'RouteDefinition')
+        const routes = i.spec.flows?.filter(f => 
['RouteConfigurationDefinition', 'RouteDefinition'].includes(f.dslName));
         return CamelDefinitionApiExt.findElementInElements(routes, uuid);
     }
 
@@ -177,8 +177,9 @@ export class CamelDefinitionApiExt {
 
     static deleteStepFromIntegration = (integration: Integration, 
uuidToDelete: string): Integration => {
         const flows: any[] = [];
-        integration.spec.flows?.filter(flow => flow.dslName !== 
'RouteDefinition').forEach(x => flows.push(x));
-        const routes = 
CamelDefinitionApiExt.deleteStepFromSteps(integration.spec.flows?.filter(flow 
=> flow.dslName === 'RouteDefinition'), uuidToDelete);
+        integration.spec.flows?.filter(flow => 
!['RouteConfigurationDefinition', 'RouteDefinition'].includes(flow.dslName))
+            .forEach(x => flows.push(x));
+        const routes = 
CamelDefinitionApiExt.deleteStepFromSteps(integration.spec.flows?.filter(flow 
=> ['RouteConfigurationDefinition', 'RouteDefinition'].includes(flow.dslName)), 
uuidToDelete);
         flows.push(...routes);
         integration.spec.flows = flows;
         return integration;
@@ -472,11 +473,17 @@ export class CamelDefinitionApiExt {
         const elementClone = CamelUtil.cloneStep(e);
         const int: Integration = CamelUtil.cloneIntegration(integration);
         const flows: CamelElement[] = [];
-        integration.spec.flows?.filter(f => f.dslName !== 
'RouteDefinition').forEach(f => flows.push(f));
+        integration.spec.flows?.filter(f => !['RouteConfigurationDefinition', 
'RouteDefinition'].includes(f.dslName))
+            .forEach(f => flows.push(f));
+
+        integration.spec.flows?.filter(f => f.dslName === 
'RouteConfigurationDefinition').forEach(f => {
+            const routeConfiguration = CamelDefinitionApiExt.updateElement(f, 
elementClone) as RouteConfigurationDefinition;
+            
flows.push(CamelDefinitionApi.createRouteConfigurationDefinition(routeConfiguration));
+        });
         integration.spec.flows?.filter(f => f.dslName === 
'RouteDefinition').forEach(f => {
             const route = CamelDefinitionApiExt.updateElement(f, elementClone) 
as RouteDefinition;
             flows.push(CamelDefinitionApi.createRouteDefinition(route));
-        })
+        });
         int.spec.flows = flows
         return int;
     }


Reply via email to