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 6375aae  Fix #254 (#281)
6375aae is described below

commit 6375aaedc20d75adcebede05ecd500a52d2a2ae6
Author: Marat Gubaidullin <[email protected]>
AuthorDate: Tue Apr 5 17:55:43 2022 -0400

    Fix #254 (#281)
---
 karavan-core/src/core/api/CamelUtil.ts             | 98 +++++++++++++++++++++-
 karavan-core/src/core/api/ComponentApi.ts          |  1 +
 karavan-core/test/checkRequired.spec.ts            | 41 +++++++++
 karavan-designer/src/designer/karavan.css          |  5 ++
 karavan-designer/src/designer/route/DslElement.tsx | 17 +++-
 .../src/designer/route/DslProperties.tsx           |  2 +-
 .../route/property/ComponentParameterField.tsx     |  7 +-
 .../designer/route/property/DslPropertyField.tsx   |  6 +-
 karavan-designer/src/designer/utils/CamelUi.ts     | 66 ++-------------
 9 files changed, 179 insertions(+), 64 deletions(-)

diff --git a/karavan-core/src/core/api/CamelUtil.ts 
b/karavan-core/src/core/api/CamelUtil.ts
index f78edff..bf948c1 100644
--- a/karavan-core/src/core/api/CamelUtil.ts
+++ b/karavan-core/src/core/api/CamelUtil.ts
@@ -19,8 +19,14 @@ import {
     CamelElement, Beans, Dependency,
 } from "../model/IntegrationDefinition";
 import {CamelDefinitionApi} from "./CamelDefinitionApi";
-import {NamedBeanDefinition} from "../model/CamelDefinition";
+import {KameletDefinition, NamedBeanDefinition, ToDefinition} from 
"../model/CamelDefinition";
 import {TraitApi} from "../model/TraitDefinition";
+import {KameletApi} from "./KameletApi";
+import {KameletModel, Property} from "../model/KameletModels";
+import {ComponentProperty} from "../model/ComponentModels";
+import {ComponentApi} from "./ComponentApi";
+import {CamelMetadataApi} from "../model/CamelMetadata";
+import {CamelDefinitionApiExt} from "./CamelDefinitionApiExt";
 
 export class CamelUtil {
 
@@ -117,4 +123,94 @@ export class CamelUtil {
             return result;
         }
     }
+
+    static isKameletComponent = (element: CamelElement | undefined): boolean 
=> {
+        if (element?.dslName === 'KameletDefinition') {
+            return true;
+        } else if (element && ["FromDefinition", 
"ToDefinition"].includes(element.dslName)) {
+            const uri: string = (element as any).uri;
+            return uri !== undefined && uri.startsWith("kamelet:");
+        } else {
+            return false;
+        }
+    }
+
+    static getKamelet = (element: CamelElement): KameletModel | undefined => {
+        if (element.dslName === 'KameletDefinition') {
+            return KameletApi.findKameletByName((element as 
KameletDefinition).name || '');
+        } else if (element.dslName === 'ToDefinition' && (element as 
ToDefinition).uri?.startsWith("kamelet:")) {
+            const kameletName = (element as 
ToDefinition).uri?.replace("kamelet:", "");
+            return KameletApi.findKameletByName(kameletName);
+        } else if (["FromDefinition", "FromDefinition", 
"ToDefinition"].includes(element.dslName)) {
+            const uri: string = (element as any).uri;
+            const k =
+                uri !== undefined ? KameletApi.findKameletByUri(uri) : 
undefined;
+            return k;
+        } else {
+            return undefined;
+        }
+    }
+
+    static getKameletProperties = (element: any): Property[] => {
+        const kamelet = this.getKamelet(element)
+        return kamelet
+            ? KameletApi.getKameletProperties(kamelet?.metadata.name)
+            : [];
+    }
+
+    static getComponentProperties = (element: any): ComponentProperty[] => {
+        const dslName: string = (element as any).dslName;
+        if (dslName === 'ToDynamicDefinition'){
+            const component = ComponentApi.findByName(dslName);
+            return component ? 
ComponentApi.getComponentProperties(component?.component.name,'producer') : [];
+        } else {
+            const uri: string = (element as any).uri;
+            const name = ComponentApi.getComponentNameFromUri(uri);
+            if (name){
+                const component = ComponentApi.findByName(name);
+                return component ? 
ComponentApi.getComponentProperties(component?.component.name, element.dslName 
=== 'FromDefinition' ? 'consumer' : 'producer') : [];
+            } else {
+                return [];
+            }
+        }
+    }
+
+    static checkRequired = (element: CamelElement): [boolean, string []] => {
+        const result: [boolean, string []] = [true, []];
+        const className = element.dslName;
+        let elementMeta =  
CamelMetadataApi.getCamelModelMetadataByClassName(className);
+        if (elementMeta === undefined && className.endsWith("Expression")) 
elementMeta = CamelMetadataApi.getCamelLanguageMetadataByClassName(className);
+        elementMeta?.properties.filter(p => p.required).forEach(p => {
+            const value = (element as any)[p.name];
+            if (p.type === 'string' && (value === undefined || 
value.trim().length === 0)){
+                result[0] = false;
+                result[1].push("Property " + p.displayName + " is required");
+            } else if (p.type === 'ExpressionDefinition'){
+                const expressionMeta =  
CamelMetadataApi.getCamelModelMetadataByClassName('ExpressionDefinition');
+                let expressionCheck = false;
+                expressionMeta?.properties.forEach(ep => {
+                    const expValue = value[ep.name];
+                    if (expValue){
+                        const checkedExpression = this.checkRequired(expValue);
+                        if (checkedExpression[0]) expressionCheck = true;
+                    }
+                })
+                result[0] = expressionCheck;
+                if (!expressionCheck) result[1].push("Expression is not 
defined");
+            }
+        })
+        if (['FromDefinition', 'ToDefinition'].includes(className)){
+            const isKamelet = this.isKameletComponent(element);
+            if (!isKamelet){
+                this.getComponentProperties(element).filter(p => 
p.required).forEach(p => {
+                    const value = 
CamelDefinitionApiExt.getParametersValue(element, p.name, p.kind === 'path');
+                   if (value === undefined || value.trim().length === 0){
+                       result[0] = false;
+                       result[1].push("Property " + p.displayName + " is 
required");
+                   }
+                })
+            }
+        }
+        return result;
+    }
 }
diff --git a/karavan-core/src/core/api/ComponentApi.ts 
b/karavan-core/src/core/api/ComponentApi.ts
index 269a1a7..8cc9e69 100644
--- a/karavan-core/src/core/api/ComponentApi.ts
+++ b/karavan-core/src/core/api/ComponentApi.ts
@@ -196,6 +196,7 @@ export const ComponentApi = {
                     prop.secret = value.secret;
                     prop.enum = value.enum;
                     prop.kind = value.kind;
+                    prop.required = value.required;
                     if (value.defaultValue) prop.defaultValue = 
value.defaultValue
                     if (!value.deprecated) properties.push(prop);
                 })
diff --git a/karavan-core/test/checkRequired.spec.ts 
b/karavan-core/test/checkRequired.spec.ts
new file mode 100644
index 0000000..cdd7cb6
--- /dev/null
+++ b/karavan-core/test/checkRequired.spec.ts
@@ -0,0 +1,41 @@
+/*
+ * 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 {expect} from 'chai';
+import 'mocha';
+import {
+    LogDefinition, ExpressionDefinition, SplitDefinition, SimpleExpression
+} from "../src/core/model/CamelDefinition";
+import {CamelDefinitionApi} from "../src/core/api/CamelDefinitionApi";
+import {CamelUtil} from "../src/core/api/CamelUtil";
+
+describe('Check required properties', () => {
+
+    it('Check DSL', () => {
+        const log: LogDefinition = CamelDefinitionApi.createLogDefinition({});
+        expect(CamelUtil.checkRequired(log)[0]).to.equal(false);
+        log.message = '${body}'
+        expect(CamelUtil.checkRequired(log)[0]).to.equal(true);
+
+        const split: SplitDefinition = 
CamelDefinitionApi.createSplitDefinition({});
+        expect(CamelUtil.checkRequired(split)[0]).to.equal(false);
+        split.expression = new ExpressionDefinition({simple: new 
SimpleExpression()})
+        expect(CamelUtil.checkRequired(split)[0]).to.equal(false);
+        split.expression = new ExpressionDefinition({simple: new 
SimpleExpression({expression: "${body} !== null"})})
+        expect(CamelUtil.checkRequired(split)[0]).to.equal(true);
+    });
+
+});
\ No newline at end of file
diff --git a/karavan-designer/src/designer/karavan.css 
b/karavan-designer/src/designer/karavan.css
index c9daa62..931297c 100644
--- a/karavan-designer/src/designer/karavan.css
+++ b/karavan-designer/src/designer/karavan.css
@@ -656,6 +656,11 @@
     text-align:start;
 }
 
+.karavan .step-element .header .header-text-required {
+    color: var(--pf-global--danger-color--100);
+    font-weight: bold;
+}
+
 .karavan .step-element .header-icon {
     border-color: var(--pf-global--Color--200);
     border-style: solid;
diff --git a/karavan-designer/src/designer/route/DslElement.tsx 
b/karavan-designer/src/designer/route/DslElement.tsx
index b51cf81..875d928 100644
--- a/karavan-designer/src/designer/route/DslElement.tsx
+++ b/karavan-designer/src/designer/route/DslElement.tsx
@@ -27,6 +27,7 @@ import {CamelUi} from "../utils/CamelUi";
 import {EventBus} from "../utils/EventBus";
 import {ChildElement, CamelDefinitionApiExt} from 
"karavan-core/lib/api/CamelDefinitionApiExt";
 import ReactDOM from "react-dom";
+import {CamelUtil} from "karavan-core/lib/api/CamelUtil";
 
 interface Props {
     step: CamelElement,
@@ -197,7 +198,7 @@ export class DslElement extends React.Component<Props, 
State> {
                 }
                 <div className={this.hasWideChildrenElement() ? "header-text" 
: ""}>
                     {this.hasWideChildrenElement() && <div 
className="spacer"/>}
-                    <Text className={this.hasWideChildrenElement() ? "text 
text-right" : "text 
text-bottom"}>{CamelUi.getElementTitle(this.state.step)}</Text>
+                    {this.getHeaderTextWithTooltip(step)}
                 </div>
                 {showInsertButton && this.getInsertElementButton()}
                 {this.state.step.dslName !== 'FromDefinition' && 
this.getDeleteButton()}
@@ -206,6 +207,20 @@ export class DslElement extends React.Component<Props, 
State> {
         )
     }
 
+    getHeaderTextWithTooltip = (step: CamelElement) => {
+        const checkRequired = CamelUtil.checkRequired(step);
+        const title = CamelUi.getElementTitle(this.state.step);
+        let className = this.hasWideChildrenElement() ? "text text-right" : 
"text text-bottom";
+        if (!checkRequired[0]) className = className + " header-text-required";
+        if (checkRequired[0]) return <Text className={className}>{title}</Text>
+        else return (
+            <Tooltip position={"right"}
+                     content={checkRequired[1]}>
+                <Text className={className}>{title}</Text>
+            </Tooltip>
+        )
+    }
+
     getHeaderWithTooltip = (tooltip: string | undefined) => {
         return (
             <Tooltip position={"left"}
diff --git a/karavan-designer/src/designer/route/DslProperties.tsx 
b/karavan-designer/src/designer/route/DslProperties.tsx
index d7540b5..1c5edc6 100644
--- a/karavan-designer/src/designer/route/DslProperties.tsx
+++ b/karavan-designer/src/designer/route/DslProperties.tsx
@@ -135,7 +135,7 @@ export class DslProperties extends React.Component<Props, 
State> {
 
     getRouteHeader= (): JSX.Element => {
         const title = this.state.step && CamelUi.getTitle(this.state.step)
-        const kamelet = this.state.step && CamelUi.getKamelet(this.state.step)
+        const kamelet = this.state.step && 
CamelUtil.getKamelet(this.state.step)
         const description = this.state.step && kamelet
             ? kamelet.spec.definition.description
             : this.state.step?.dslName ? 
CamelMetadataApi.getCamelModelMetadataByClassName(this.state.step?.dslName)?.description
 : title;
diff --git 
a/karavan-designer/src/designer/route/property/ComponentParameterField.tsx 
b/karavan-designer/src/designer/route/property/ComponentParameterField.tsx
index 6253b83..0b59bb0 100644
--- a/karavan-designer/src/designer/route/property/ComponentParameterField.tsx
+++ b/karavan-designer/src/designer/route/property/ComponentParameterField.tsx
@@ -207,7 +207,12 @@ export class ComponentParameterField extends 
React.Component<Props, State> {
                         position={"left"}
                         headerContent={property.displayName}
                         bodyContent={property.description}
-                        footerContent={property.defaultValue !== undefined ? 
"Default: " + property.defaultValue : undefined}>
+                        footerContent={
+                        <div>
+                            {property.defaultValue !== undefined && 
<div>{"Default: " + property.defaultValue}</div>}
+                            {property.required === true && 
<div>{property.displayName + " is required"}</div>}
+                        </div>
+                        }>
                         <button type="button" aria-label="More info" 
onClick={e => e.preventDefault()}
                                 className="pf-c-form__group-label-help">
                             <HelpIcon noVerticalAlign/>
diff --git a/karavan-designer/src/designer/route/property/DslPropertyField.tsx 
b/karavan-designer/src/designer/route/property/DslPropertyField.tsx
index 3de7bc8..d39ed09 100644
--- a/karavan-designer/src/designer/route/property/DslPropertyField.tsx
+++ b/karavan-designer/src/designer/route/property/DslPropertyField.tsx
@@ -401,7 +401,7 @@ export class DslPropertyField extends 
React.Component<Props, State> {
     getKameletParameters = () => {
         return (
             <div className="parameters">
-                {CamelUi.getKameletProperties(this.props.element).map(property 
=>
+                
{CamelUtil.getKameletProperties(this.props.element).map(property =>
                     <KameletPropertyField
                         key={property.id}
                         property={property}
@@ -484,7 +484,7 @@ export class DslPropertyField extends 
React.Component<Props, State> {
     }
 
     getComponentParameters (property: PropertyMeta) {
-        const properties = CamelUi.getComponentProperties(this.props.element);
+        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"));
@@ -503,7 +503,7 @@ export class DslPropertyField extends 
React.Component<Props, State> {
     }
 
     render() {
-        const isKamelet = CamelUi.isKameletComponent(this.props.element);
+        const isKamelet = CamelUtil.isKameletComponent(this.props.element);
         const property: PropertyMeta = this.props.property;
         const value = this.props.value;
         return (
diff --git a/karavan-designer/src/designer/utils/CamelUi.ts 
b/karavan-designer/src/designer/utils/CamelUi.ts
index 70077e4..ad02ebb 100644
--- a/karavan-designer/src/designer/utils/CamelUi.ts
+++ b/karavan-designer/src/designer/utils/CamelUi.ts
@@ -184,35 +184,8 @@ export class CamelUi {
             : name;
     }
 
-    static isKameletComponent = (element: CamelElement | undefined): boolean 
=> {
-        if (element?.dslName === 'KameletDefinition') {
-            return true;
-        } else if (element && ["FromDefinition", 
"ToDefinition"].includes(element.dslName)) {
-            const uri: string = (element as any).uri;
-            return uri !== undefined && uri.startsWith("kamelet:");
-        } else {
-            return false;
-        }
-    }
-
-    static getKamelet = (element: CamelElement): KameletModel | undefined => {
-        if (element.dslName === 'KameletDefinition') {
-            return KameletApi.findKameletByName((element as 
KameletDefinition).name || '');
-        } else if (element.dslName === 'ToDefinition' && (element as 
ToDefinition).uri?.startsWith("kamelet:")) {
-            const kameletName = (element as 
ToDefinition).uri?.replace("kamelet:", "");
-            return KameletApi.findKameletByName(kameletName);
-        } else if (["FromDefinition", "FromDefinition", 
"ToDefinition"].includes(element.dslName)) {
-            const uri: string = (element as any).uri;
-            const k =
-                uri !== undefined ? KameletApi.findKameletByUri(uri) : 
undefined;
-            return k;
-        } else {
-            return undefined;
-        }
-    }
-
     static isActionKamelet = (element: CamelElement): boolean => {
-        const kamelet = this.getKamelet(element);
+        const kamelet = CamelUtil.getKamelet(element);
         if (kamelet) return kamelet.type() === 'action'
         else return false;
     }
@@ -252,30 +225,6 @@ export class CamelUi {
         return result;
     }
 
-    static getKameletProperties = (element: any): Property[] => {
-        const kamelet = CamelUi.getKamelet(element)
-        return kamelet
-            ? KameletApi.getKameletProperties(kamelet?.metadata.name)
-            : [];
-    }
-
-    static getComponentProperties = (element: any): ComponentProperty[] => {
-        const dslName: string = (element as any).dslName;
-       if (dslName === 'ToDynamicDefinition'){
-           const component = ComponentApi.findByName(dslName);
-           return component ? 
ComponentApi.getComponentProperties(component?.component.name,'producer') : [];
-       } else {
-           const uri: string = (element as any).uri;
-           const name = ComponentApi.getComponentNameFromUri(uri);
-           if (name){
-               const component = ComponentApi.findByName(name);
-               return component ? 
ComponentApi.getComponentProperties(component?.component.name, element.dslName 
=== 'FromDefinition' ? 'consumer' : 'producer') : [];
-           } else {
-               return [];
-           }
-       }
-    }
-
     static getElementTitle = (element: CamelElement): string => {
         if (element.dslName === 'RouteDefinition') {
             const routeId = (element as RouteDefinition).id
@@ -291,7 +240,7 @@ export class CamelUi {
     }
 
     static getTitle = (element: CamelElement): string => {
-        const k: KameletModel | undefined = CamelUi.getKamelet(element);
+        const k: KameletModel | undefined = CamelUtil.getKamelet(element);
         if (k) {
             return k.title();
         } else if (element.dslName === 'RouteDefinition') {
@@ -307,7 +256,7 @@ export class CamelUi {
     }
 
     static getOutgoingTitle = (element: CamelElement): string => {
-        const k: KameletModel | undefined = CamelUi.getKamelet(element);
+        const k: KameletModel | undefined = CamelUtil.getKamelet(element);
         if (k) {
             return k.title();
         } else if (element.dslName === 'RouteDefinition') {
@@ -331,7 +280,10 @@ export class CamelUi {
 
     static isShowUriTooltip = (element: CamelElement): boolean => {
         const uri: string = (element as any).uri;
-        return uri !== undefined && !uri.startsWith("kamelet");
+        if (uri !== undefined && !uri.startsWith("kamelet")){
+            return ComponentApi.getComponentNameFromUri(uri) !== uri;
+        }
+        return false;
     }
 
     static getExpressionTooltip = (element: CamelElement): string => {
@@ -441,7 +393,7 @@ export class CamelUi {
     }
 
     static getIcon = (element: CamelElement): string => {
-        const k: KameletModel | undefined = CamelUi.getKamelet(element);
+        const k: KameletModel | undefined = CamelUtil.getKamelet(element);
         if (["FromDefinition", "KameletDefinition"].includes(element.dslName)) 
{
             return k ? k.icon() : CamelUi.getIconForName(element.dslName);
         } else if (element.dslName === "ToDefinition" && (element as 
ToDefinition).uri?.startsWith("kamelet:")) {
@@ -452,7 +404,7 @@ export class CamelUi {
     }
 
     static getConnectionIcon = (element: CamelElement): string => {
-        const k: KameletModel | undefined = CamelUi.getKamelet(element);
+        const k: KameletModel | undefined = CamelUtil.getKamelet(element);
         if (["FromDefinition", "KameletDefinition"].includes(element.dslName)) 
{
             return k ? k.icon() : externalIcon;
         } else if (element.dslName === "ToDefinition" && (element as 
ToDefinition).uri?.startsWith("kamelet:")) {

Reply via email to