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 798b8e2  Internal links (#195)
798b8e2 is described below

commit 798b8e2d5ceca2a60a70c4bad7070a1b1e4d442f
Author: Marat Gubaidullin <marat.gubaidul...@gmail.com>
AuthorDate: Tue Feb 15 17:27:10 2022 -0500

    Internal links (#195)
    
    * Autocreate Direct and Seda routes for Components
    
    * Autocreate Direct and Seda for WireTap
    
    * Autocreate Direct and Seda for Saga
---
 karavan-designer/src/App.tsx                       | 10 +++-
 karavan-designer/src/designer/KaravanDesigner.tsx  |  1 +
 .../src/designer/route/DslProperties.tsx           | 14 ++---
 .../src/designer/route/RouteDesigner.tsx           | 25 ++++++--
 .../route/property/ComponentParameterField.tsx     | 67 +++++++++++++++++++--
 .../designer/route/property/DslPropertyField.tsx   | 69 +++++++++++++++++++---
 karavan-designer/src/designer/utils/CamelUi.ts     | 23 +++++++-
 7 files changed, 181 insertions(+), 28 deletions(-)

diff --git a/karavan-designer/src/App.tsx b/karavan-designer/src/App.tsx
index 519e08e..dacc878 100644
--- a/karavan-designer/src/App.tsx
+++ b/karavan-designer/src/App.tsx
@@ -55,6 +55,10 @@ class App extends React.Component<Props, State> {
             '                      name: http-sink\n' +
             '                  - kamelet:\n' +
             '                      name: kafka-sink\n' +
+            '            - to:\n' +
+            '                uri: direct\n' +
+            '            - to:\n' +
+            '                uri: seda\n' +
             '        id: Main Route\n' +
             '    - route:\n' +
             '        from:\n' +
@@ -64,6 +68,10 @@ class App extends React.Component<Props, State> {
             '        from:\n' +
             '          uri: direct:compensation\n' +
             '        id: Compensation\n' +
+            '    - route:\n' +
+            '        from:\n' +
+            '          uri: seda:demo\n' +
+            '        id: seda\n' +
             // '            - choice:\n' +
             // '                when:\n' +
             // '                  - expression:\n' +
@@ -192,7 +200,7 @@ class App extends React.Component<Props, State> {
 
     save(filename: string, yaml: string) {
         // console.log(filename);
-        // console.log(yaml);
+        console.log(yaml);
     }
 
     public render() {
diff --git a/karavan-designer/src/designer/KaravanDesigner.tsx 
b/karavan-designer/src/designer/KaravanDesigner.tsx
index 6498a50..de59a2b 100644
--- a/karavan-designer/src/designer/KaravanDesigner.tsx
+++ b/karavan-designer/src/designer/KaravanDesigner.tsx
@@ -59,6 +59,7 @@ export class KaravanDesigner extends React.Component<Props, 
State> {
 
     componentDidUpdate = (prevProps: Readonly<Props>, prevState: 
Readonly<State>, snapshot?: any) => {
         if (prevState.key !== this.state.key) {
+
             this.props.onSave?.call(this, 
this.state.integration.metadata.name, this.getCode(this.state.integration));
         }
     }
diff --git a/karavan-designer/src/designer/route/DslProperties.tsx 
b/karavan-designer/src/designer/route/DslProperties.tsx
index 8786278..1c863e6 100644
--- a/karavan-designer/src/designer/route/DslProperties.tsx
+++ b/karavan-designer/src/designer/route/DslProperties.tsx
@@ -35,7 +35,7 @@ import {Integration, CamelElement} from 
"karavan-core/lib/model/IntegrationDefin
 import {CamelDefinitionApiExt} from 
"karavan-core/lib/api/CamelDefinitionApiExt";
 import {ComponentApi} from "karavan-core/lib/api/ComponentApi";
 import {CamelUtil} from "karavan-core/lib/api/CamelUtil";
-import {CamelUi} from "../utils/CamelUi";
+import {CamelUi, RouteToCreate} from "../utils/CamelUi";
 import {CamelMetadataApi, PropertyMeta} from 
"karavan-core/lib/model/CamelMetadata";
 import {IntegrationHeader} from "../utils/KaravanComponents";
 
@@ -43,7 +43,7 @@ interface Props {
     integration: Integration,
     step?: CamelElement,
     onIntegrationUpdate?: any,
-    onPropertyUpdate?: any,
+    onPropertyUpdate?: (element: CamelElement, updatedUuid: string, newRoute?: 
RouteToCreate) => void
 }
 
 interface State {
@@ -58,19 +58,19 @@ export class DslProperties extends React.Component<Props, 
State> {
         selectStatus: new Map<string, boolean>(),
     };
 
-    propertyChanged = (fieldId: string, value: string | number | boolean | 
any) => {
+    propertyChanged = (fieldId: string, value: string | number | boolean | 
any, newRoute?: RouteToCreate) => {
         if (this.state.step) {
             const clone = CamelUtil.cloneStep(this.state.step);
             (clone as any)[fieldId] = value;
             this.setStep(clone)
-            this.props.onPropertyUpdate?.call(this, clone, 
this.state.step.uuid);
+            this.props.onPropertyUpdate?.call(this, clone, 
this.state.step.uuid, newRoute);
         }
     }
 
     dataFormatChanged = (value: DataFormatDefinition) => {
         value.uuid = this.state.step?.uuid ? this.state.step?.uuid : 
value.uuid;
         this.setStep(value);
-        this.props.onPropertyUpdate?.call(this, value, this.state.step?.uuid);
+        this.props.onPropertyUpdate?.call(this, value, value.uuid);
     }
 
     expressionChanged = (exp:ExpressionDefinition) => {
@@ -82,11 +82,11 @@ export class DslProperties extends React.Component<Props, 
State> {
         }
     }
 
-    parametersChanged = (parameter: string, value: string | number | boolean | 
any, pathParameter?: boolean) => {
+    parametersChanged = (parameter: string, value: string | number | boolean | 
any, pathParameter?: boolean, newRoute?: RouteToCreate) => {
         if (this.state.step && this.state.step) {
             if (pathParameter) {
                 const uri = ComponentApi.buildComponentUri((this.state.step as 
any).uri, parameter, value);
-                this.propertyChanged("uri", uri);
+                this.propertyChanged("uri", uri, newRoute);
             } else {
                 const clone = (CamelUtil.cloneStep(this.state.step));
                 const parameters: any = {...(clone as any).parameters};
diff --git a/karavan-designer/src/designer/route/RouteDesigner.tsx 
b/karavan-designer/src/designer/route/RouteDesigner.tsx
index e7c5160..1d59b79 100644
--- a/karavan-designer/src/designer/route/RouteDesigner.tsx
+++ b/karavan-designer/src/designer/route/RouteDesigner.tsx
@@ -32,7 +32,7 @@ import {DslConnections} from "./DslConnections";
 import PlusIcon from "@patternfly/react-icons/dist/esm/icons/plus-icon";
 import {DslElement} from "./DslElement";
 import {EventBus} from "../utils/EventBus";
-import {CamelUi} from "../utils/CamelUi";
+import {CamelUi, RouteToCreate} from "../utils/CamelUi";
 
 interface Props {
     onSave?: (integration: Integration) => void
@@ -97,10 +97,25 @@ export class RouteDesigner extends React.Component<Props, 
State> {
         }
     };
 
-    onPropertyUpdate = (element: CamelElement, updatedUuid: string) => {
-        const clone = CamelUtil.cloneIntegration(this.state.integration);
-        const i = CamelDefinitionApiExt.updateIntegration(clone, element, 
updatedUuid);
-        this.setState({integration: i, key: Math.random().toString()});
+    onPropertyUpdate = (element: CamelElement, updatedUuid: string, newRoute?: 
RouteToCreate) => {
+        if (newRoute) {
+            let i = 
CamelDefinitionApiExt.updateIntegration(this.state.integration, element, 
updatedUuid);
+            const f = CamelDefinitionApi.createFromDefinition({uri: 
newRoute.componentName + ":" + newRoute.name})
+            const r = CamelDefinitionApi.createRouteDefinition({from: f, id: 
newRoute.name})
+            i = CamelDefinitionApiExt.addStepToIntegration(i, r, '');
+            const clone = CamelUtil.cloneIntegration(i);
+            this.setState({
+                integration: clone,
+                key: Math.random().toString(),
+                showSelector: false,
+                selectedStep: element,
+                selectedUuid: element.uuid
+            });
+        } else {
+            const clone = CamelUtil.cloneIntegration(this.state.integration);
+            const i = CamelDefinitionApiExt.updateIntegration(clone, element, 
updatedUuid);
+            this.setState({integration: i, key: Math.random().toString()});
+        }
     }
 
     showDeleteConfirmation = (id: string) => {
diff --git 
a/karavan-designer/src/designer/route/property/ComponentParameterField.tsx 
b/karavan-designer/src/designer/route/property/ComponentParameterField.tsx
index ab5c907..606ea9d 100644
--- a/karavan-designer/src/designer/route/property/ComponentParameterField.tsx
+++ b/karavan-designer/src/designer/route/property/ComponentParameterField.tsx
@@ -29,8 +29,9 @@ import '../../karavan.css';
 import "@patternfly/patternfly/patternfly.css";
 import HelpIcon from "@patternfly/react-icons/dist/js/icons/help-icon";
 import {ComponentProperty} from "karavan-core/lib/model/ComponentModels";
-import {CamelUi} from "../../utils/CamelUi";
-import {Integration} from "karavan-core/lib/model/IntegrationDefinition";
+import {CamelUi, RouteToCreate} from "../../utils/CamelUi";
+import {CamelElement, Integration} from 
"karavan-core/lib/model/IntegrationDefinition";
+import {ToDefinition} from "karavan-core/lib/model/CamelDefinition";
 
 const prefix = "parameters";
 const beanPrefix = "#bean:";
@@ -38,8 +39,9 @@ const beanPrefix = "#bean:";
 interface Props {
     property: ComponentProperty,
     integration: Integration,
+    element?: CamelElement,
     value: any,
-    onParameterChange?: (parameter: string, value: string | number | boolean | 
any, pathParameter?: boolean) => void
+    onParameterChange?: (parameter: string, value: string | number | boolean | 
any, pathParameter?: boolean, newRoute?: RouteToCreate) => void
 }
 
 interface State {
@@ -52,8 +54,8 @@ export class ComponentParameterField extends 
React.Component<Props, State> {
         selectStatus: new Map<string, boolean>(),
     }
 
-    parametersChanged = (parameter: string, value: string | number | boolean | 
any, pathParameter?: boolean) => {
-        this.props.onParameterChange?.call(this, parameter, value, 
pathParameter);
+    parametersChanged = (parameter: string, value: string | number | boolean | 
any, pathParameter?: boolean, newRoute?: RouteToCreate) => {
+        this.props.onParameterChange?.call(this, parameter, value, 
pathParameter, newRoute);
         this.setState({selectStatus: new Map<string, boolean>([[parameter, 
false]])});
     }
 
@@ -90,6 +92,58 @@ export class ComponentParameterField extends 
React.Component<Props, State> {
         )
     }
 
+    canBeInternalUri = (property: ComponentProperty): boolean => {
+        if (this.props.element && this.props.element.dslName === 
'ToDefinition' && property.name === 'name') {
+            const uri:string = (this.props.element as ToDefinition).uri || '';
+            return uri.startsWith("direct") || uri.startsWith("seda");
+        } else {
+            return false;
+        }
+    }
+
+    getInternalComponentName = (property: ComponentProperty): string => {
+        if (this.props.element && this.props.element.dslName === 
'ToDefinition' && property.name === 'name') {
+            const uri:string = (this.props.element as ToDefinition).uri || '';
+            if (uri.startsWith("direct")) return "direct";
+            if (uri.startsWith("seda")) return "seda";
+            return '';
+        } else {
+            return '';
+        }
+    }
+
+    getInternalUriSelect = (property: ComponentProperty, value: any) => {
+        const selectOptions: JSX.Element[] = [];
+        const componentName = this.getInternalComponentName(property);
+        const urls = CamelUi.getInternalRouteUris(this.props.integration, 
componentName, false);
+        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}
+                onToggle={isExpanded => {
+                    this.openSelect(property.name)
+                }}
+                onSelect={(e, value, isPlaceholder) => {
+                    const newRoute = !urls.includes(value.toString()) ? new 
RouteToCreate(componentName, value.toString()) : undefined;
+                    this.parametersChanged(property.name, (!isPlaceholder ? 
value : undefined), property.kind === 'path', newRoute);
+                }}
+                selections={value}
+                isOpen={this.isSelectOpen(property.name)}
+                isCreatable={true}
+                isInputFilterPersisted={true}
+                aria-labelledby={property.name}
+                direction={SelectDirection.down}
+            >
+                {selectOptions}
+            </Select>
+        )
+    }
+
     getTextInput = (property: ComponentProperty, value: any) => {
         const id = prefix + "-" + property.name;
         return (
@@ -160,7 +214,8 @@ export class ComponentParameterField extends 
React.Component<Props, State> {
                         </button>
                     </Popover>
                 }>
-                {['string', 'duration', 'integer', 'int', 
'number'].includes(property.type) && property.enum === undefined
+                {this.canBeInternalUri(property) && 
this.getInternalUriSelect(property, value)}
+                {['string', 'duration', 'integer', 'int', 
'number'].includes(property.type) && property.enum === undefined && 
!this.canBeInternalUri(property)
                     && this.getTextInput(property, value)}
                 {['object'].includes(property.type) && !property.enum
                     && this.getSelectBean(property, value)}
diff --git a/karavan-designer/src/designer/route/property/DslPropertyField.tsx 
b/karavan-designer/src/designer/route/property/DslPropertyField.tsx
index 8e4e63c..051a409 100644
--- a/karavan-designer/src/designer/route/property/DslPropertyField.tsx
+++ b/karavan-designer/src/designer/route/property/DslPropertyField.tsx
@@ -33,9 +33,9 @@ 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} from "../../utils/CamelUi";
+import {CamelUi, RouteToCreate} from "../../utils/CamelUi";
 import {ComponentParameterField} from "./ComponentParameterField";
-import {DataFormatDefinition} from "karavan-core/lib/model/CamelDefinition";
+import {DataFormatDefinition, ToDefinition} 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";
@@ -47,10 +47,10 @@ import AddIcon from 
"@patternfly/react-icons/dist/js/icons/plus-circle-icon";
 interface Props {
     property: PropertyMeta,
     value: any,
-    onChange?: (fieldId: string, value: string | number | boolean | any) => 
void,
+    onChange?: (fieldId: string, value: string | number | boolean | any, 
newRoute?: RouteToCreate) => void,
     onExpressionChange?: (value: ExpressionDefinition) => void,
     onDataFormatChange?: (value: DataFormatDefinition) => void,
-    onParameterChange?: (parameter: string, value: string | number | boolean | 
any, pathParameter?: boolean) => void,
+    onParameterChange?: (parameter: string, value: string | number | boolean | 
any, pathParameter?: boolean, newRoute?: RouteToCreate) => void,
     element?: CamelElement
     integration: Integration,
 }
@@ -77,8 +77,8 @@ export class DslPropertyField extends React.Component<Props, 
State> {
         return this.state.selectStatus.has(propertyName) && 
this.state.selectStatus.get(propertyName) === true;
     }
 
-    propertyChanged = (fieldId: string, value: string | number | boolean | 
any) => {
-        this.props.onChange?.call(this, fieldId, value);
+    propertyChanged = (fieldId: string, value: string | number | boolean | 
any, newRoute?: RouteToCreate) => {
+        this.props.onChange?.call(this, fieldId, value, newRoute);
         this.setState({selectStatus: new Map<string, boolean>([[fieldId, 
false]])});
     }
 
@@ -128,10 +128,15 @@ export class DslPropertyField extends 
React.Component<Props, State> {
         }
     }
 
+    isUriReadOnly = (property: PropertyMeta): boolean => {
+        const dslName:string = this.props.element?.dslName || '';
+        return property.name === 'uri' && !['ToDynamicDefinition', 
'WireTapDefinition'].includes(dslName)
+    }
+
     getTextField = (property: PropertyMeta, value: any) => {
         return (
             <TextInput
-                className="text-field" isRequired
+                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()}
@@ -229,6 +234,49 @@ export class DslPropertyField extends 
React.Component<Props, State> {
         )
     }
 
+     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 {
+            return false;
+        }
+    }
+
+    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}
+                onToggle={isExpanded => {
+                    this.openSelect(property.name)
+                }}
+                onSelect={(e, value, isPlaceholder) => {
+                    const url = value.toString().split(":");
+                    const newRoute = !urls.includes(value.toString()) ? 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>
+        )
+    }
+
     getMultiValueObjectField = (property: PropertyMeta, value: any) => {
         return (
             <div>
@@ -269,6 +317,7 @@ export class DslPropertyField extends 
React.Component<Props, State> {
                     <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}
@@ -341,7 +390,11 @@ export class DslPropertyField extends 
React.Component<Props, State> {
                     && this.getMultiValueObjectField(property, value)}
                 {property.name === 'expression' && property.type === "string" 
&& !property.isArray
                     && this.getTextArea(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.getInternalUriSelect(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.getTextField(property, value)}
                 {['string'].includes(property.type) && 
property.name.endsWith("Ref") && !property.isArray && !property.enumVals
                     && this.getSelectBean(property, value)}
diff --git a/karavan-designer/src/designer/utils/CamelUi.ts 
b/karavan-designer/src/designer/utils/CamelUi.ts
index 3b63f6e..bfbd43d 100644
--- a/karavan-designer/src/designer/utils/CamelUi.ts
+++ b/karavan-designer/src/designer/utils/CamelUi.ts
@@ -19,7 +19,7 @@ import {KameletModel, Property} from 
"karavan-core/lib/model/KameletModels";
 import {DslMetaModel} from "./DslMetaModel";
 import {ComponentApi} from "karavan-core/lib/api/ComponentApi";
 import {ComponentProperty} from "karavan-core/lib/model/ComponentModels";
-import {CamelMetadataApi} from "karavan-core/lib/model/CamelMetadata";
+import {CamelMetadataApi, PropertyMeta} from 
"karavan-core/lib/model/CamelMetadata";
 import {CamelUtil} from "karavan-core/lib/api/CamelUtil";
 import {CamelDefinitionApiExt} from 
"karavan-core/lib/api/CamelDefinitionApiExt";
 import {KameletDefinition, NamedBeanDefinition, RouteDefinition, 
SagaDefinition} from "karavan-core/lib/model/CamelDefinition";
@@ -65,6 +65,16 @@ export const camelIcon =
 export const externalIcon =
     "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='32px' 
height='32px' viewBox='0 0 32 32' 
id='icon'%3E%3Cdefs%3E%3Cstyle%3E.cls-1%7Bfill:none;%7D%3C/style%3E%3C/defs%3E%3Ctitle%3Efog%3C/title%3E%3Cpath
 
d='M25.8289,13.1155A10.02,10.02,0,0,0,16,5.0005V7a8.0233,8.0233,0,0,1,7.8649,6.4934l.2591,1.346,1.3488.2441A5.5019,5.5019,0,0,1,24.5076,26H16v2h8.5076a7.5019,7.5019,0,0,0,1.3213-14.8845Z'/%3E%3Crect
 x='8' y='24' width='6' height='2'/%3E%3Crect x='4' y='24' width='2'  [...]
 
+export class RouteToCreate {
+    componentName: string = ''
+    name: string = ''
+
+    constructor(componentName: string, name: string) {
+        this.componentName = componentName;
+        this.name = name;
+    }
+}
+
 export class CamelUi {
 
     static getSelectorModelTypes = (parentDsl: string | undefined, showSteps: 
boolean = true): string[] => {
@@ -220,6 +230,17 @@ export class CamelUi {
         }
     }
 
+    static getInternalRouteUris = (integration: Integration, componentName: 
string, showComponentName: boolean = true): string[] => {
+        const result:string[] = [];
+        integration.spec.flows?.filter(f => f.dslName === 'RouteDefinition')
+            .filter((r: RouteDefinition) => 
r.from.uri.startsWith(componentName))
+            .forEach((r: RouteDefinition) => {
+                if (showComponentName) result.push(r.from.uri)
+                else result.push(r.from.uri.replace(componentName+":", ""));
+            });
+        return result;
+    }
+
     static getKameletProperties = (element: any): Property[] => {
         const kamelet = CamelUi.getKamelet(element)
         return kamelet

Reply via email to