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 2024/06/19 21:32:24 UTC

(camel-karavan) branch main updated (0a215807 -> cf741583)

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

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


    from 0a215807 Custom Kamelet
     new 32cbcc02 Fix #1324
     new 13b60682 Fix #1324
     new cf741583 Cleanup

The 3 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .../api/{AuthResource.java => PublicResource.java} |  42 +--------
 .../api/{ProjectEventBus.ts => ErrorEventBus.ts}   |   8 +-
 karavan-app/src/main/webui/src/api/KaravanApi.tsx  | 104 +++++++++------------
 .../src/main/webui/src/api/ProjectService.ts       |  26 ++++--
 .../main/webui/src/config/ConfigurationPage.tsx    |   2 +-
 .../project/{DevModeToolbar.css => devmode.css}    |   0
 .../src/project/files/CreateIntegrationModal.tsx   |   2 +-
 .../src/project/trace/RunnerInfoTraceNode.tsx      |  76 +++++++++++++++
 .../src/main/webui/src/services/ServicesPage.tsx   |   3 +-
 .../src/main/webui/src/shared/error/Error.ts       |  11 ---
 .../main/webui/src/shared/error/ErrorResponse.ts   |  15 ---
 .../src/shared/error/UseResponseErrorHandler.ts    |  43 ---------
 .../src/main/webui/src/templates/TemplatesPage.tsx |   2 +-
 karavan-app/src/main/webui/src/util/CodeUtils.ts   |   6 +-
 karavan-app/src/main/webui/src/util/StringUtils.ts |  35 ++++++-
 karavan-app/src/main/webui/src/util/form-util.css  |   8 ++
 .../src/main/webui/src/util/useFormUtil.tsx        |  88 ++++++++++++-----
 17 files changed, 261 insertions(+), 210 deletions(-)
 rename karavan-app/src/main/java/org/apache/camel/karavan/api/{AuthResource.java => PublicResource.java} (59%)
 copy karavan-app/src/main/webui/src/api/{ProjectEventBus.ts => ErrorEventBus.ts} (81%)
 copy karavan-app/src/main/webui/src/project/{DevModeToolbar.css => devmode.css} (100%)
 create mode 100644 karavan-app/src/main/webui/src/project/trace/RunnerInfoTraceNode.tsx
 delete mode 100644 karavan-app/src/main/webui/src/shared/error/Error.ts
 delete mode 100644 karavan-app/src/main/webui/src/shared/error/ErrorResponse.ts
 delete mode 100644 karavan-app/src/main/webui/src/shared/error/UseResponseErrorHandler.ts


(camel-karavan) 01/03: Fix #1324

Posted by ma...@apache.org.
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 32cbcc028d7dc433f6cd5222d88ef8bdbbdcc34a
Author: Marat Gubaidullin <ma...@talismancloud.io>
AuthorDate: Wed Jun 19 17:16:54 2024 -0400

    Fix #1324
---
 karavan-app/src/main/webui/src/util/CodeUtils.ts   |  6 +-
 karavan-app/src/main/webui/src/util/form-util.css  |  8 ++
 .../src/main/webui/src/util/useFormUtil.tsx        | 88 ++++++++++++++++------
 3 files changed, 79 insertions(+), 23 deletions(-)

diff --git a/karavan-app/src/main/webui/src/util/CodeUtils.ts b/karavan-app/src/main/webui/src/util/CodeUtils.ts
index fe942a64..33f5a68e 100644
--- a/karavan-app/src/main/webui/src/util/CodeUtils.ts
+++ b/karavan-app/src/main/webui/src/util/CodeUtils.ts
@@ -50,9 +50,11 @@ export class CodeUtils {
         if (type === 'INTEGRATION') {
             return CamelDefinitionYaml.integrationToYaml(Integration.createNew(fileName, 'plain'));
         } else if (type === 'KAMELET') {
-            const type: string | undefined = fileName.replace('.kamelet.yaml', '').split('-').pop();
+            const filenameParts = fileName.replace('.kamelet.yaml', '').split('-');
+            const type: string | undefined = filenameParts.slice(-1)[0]
             const kameletType: KameletTypes | undefined = (type === "sink" || type === "source" || type === "action") ? type : undefined;
-            const integration = Integration.createNew(fileName, 'kamelet');
+            const name = filenameParts.slice(0, -1).join('-');
+            const integration = Integration.createNew(name, 'kamelet');
             const meta: MetadataLabels = new MetadataLabels({"camel.apache.org/kamelet.type": kameletType});
             integration.metadata.labels = meta;
             if (copyFromKamelet !== undefined && copyFromKamelet !== '') {
diff --git a/karavan-app/src/main/webui/src/util/form-util.css b/karavan-app/src/main/webui/src/util/form-util.css
index a5755b2a..da9f82c9 100644
--- a/karavan-app/src/main/webui/src/util/form-util.css
+++ b/karavan-app/src/main/webui/src/util/form-util.css
@@ -17,3 +17,11 @@
     padding-left: 3px;
     padding-right: 3px;
 }
+
+.form-util-text-field {
+    width: 100%;
+}
+.form-util-text-field-suffix,
+.form-util-text-field-suffix input {
+    width: 150px
+}
\ No newline at end of file
diff --git a/karavan-app/src/main/webui/src/util/useFormUtil.tsx b/karavan-app/src/main/webui/src/util/useFormUtil.tsx
index b675c8c9..95424da7 100644
--- a/karavan-app/src/main/webui/src/util/useFormUtil.tsx
+++ b/karavan-app/src/main/webui/src/util/useFormUtil.tsx
@@ -1,10 +1,11 @@
-import React from 'react';
+import React, {useState} from 'react';
 import {
     Controller,
     FieldError,
     UseFormReturn,
 } from "react-hook-form";
 import {
+    Button,
     Flex,
     FormGroup,
     FormHelperText,
@@ -15,9 +16,14 @@ import {
     TextInput, TextInputGroup, TextInputGroupMain, TextVariants
 } from "@patternfly/react-core";
 import "./form-util.css"
+import ShowIcon from "@patternfly/react-icons/dist/js/icons/eye-icon";
+import HideIcon from "@patternfly/react-icons/dist/js/icons/eye-slash-icon";
+import {hasDigit, hasLowercase, hasMinimumLength, hasSpecialCharacter, hasUppercase} from "./StringUtils";
 
 export function useFormUtil(formContext: UseFormReturn<any>) {
 
+    const [showPassword, setShowPassword] = useState<boolean>(false);
+
     function getHelper(error: FieldError | undefined) {
         if (error) {
             return (
@@ -32,7 +38,9 @@ export function useFormUtil(formContext: UseFormReturn<any>) {
         }
     }
 
-    function getTextField(fieldName: string, label: string, validate?: ((value: string, formValues: any) => boolean | string) | Record<string, (value: string, formValues: any) => boolean | string>) {
+    function getTextField(fieldName: string, label: string,
+                          validate?: ((value: string, formValues: any) => boolean | string) | Record<string, (value: string, formValues: any) => boolean | string>,
+                          type: | 'text' | 'date' | 'datetime-local' | 'email' | 'month' | 'number' | 'password' | 'search' | 'tel' | 'time' | 'url' = 'text') {
         const {control, setValue, getValues, formState: {errors}} = formContext;
         return (
             <FormGroup label={label} fieldId={fieldName} isRequired>
@@ -41,7 +49,7 @@ export function useFormUtil(formContext: UseFormReturn<any>) {
                     control={control}
                     name={fieldName}
                     render={() => (
-                        <TextInput className="text-field" type="text" id={fieldName}
+                        <TextInput className="text-field" type={type} id={fieldName}
                                    value={getValues(fieldName)}
                                    validated={!!errors[fieldName] ? 'error' : 'default'}
                                    onChange={(_, v) => {
@@ -81,6 +89,41 @@ export function useFormUtil(formContext: UseFormReturn<any>) {
         )
     }
 
+    function getPasswordField(fieldName: string, label: string, validate?: ((value: string, formValues: any) => boolean | string) | Record<string, (value: string, formValues: any) => boolean | string>) {
+        validate = {
+            length: v => hasMinimumLength(v) || 'Password should be at least 8 characters',
+            lower: v => hasLowercase(v) || 'Password should have at least one lowercase letter',
+            upper: v => hasUppercase(v) || 'Password should have at least one uppercase letter',
+            digit: v => hasDigit(v) || 'Password should have at least one digit',
+            special: v => hasSpecialCharacter(v) || 'Password should have at least one special character',
+        }
+        const {control, setValue, getValues, formState: {errors}} = formContext;
+        return (
+            <FormGroup label={label} fieldId={fieldName} isRequired>
+                <Controller
+                    rules={{required: "Required field", validate: validate}}
+                    control={control}
+                    name={fieldName}
+                    render={() => (
+                        <div style={{display:'flex'}}>
+                            <TextInput className="text-field" type={showPassword ? "text" : "password"} id={fieldName}
+                                       value={getValues(fieldName)}
+                                       validated={!!errors[fieldName] ? 'error' : 'default'}
+                                       onChange={(_, v) => {
+                                           setValue(fieldName, v, {shouldValidate: true});
+                                       }}
+                            />
+                            <Button variant="control" onClick={e => setShowPassword(!showPassword)}>
+                                {showPassword ? <ShowIcon/> : <HideIcon/>}
+                            </Button>
+                        </div>
+                    )}
+                />
+                {getHelper((errors as any)[fieldName])}
+            </FormGroup>
+        )
+    }
+
     function getTextFieldPrefix(fieldName: string, label: string, prefix: string,
                                 required: boolean,
                                 validate?: ((value: string, formValues: any) => boolean | string) | Record<string, (value: string, formValues: any) => boolean | string>) {
@@ -107,25 +150,28 @@ export function useFormUtil(formContext: UseFormReturn<any>) {
     }
 
     function getTextFieldSuffix(fieldName: string, label: string, suffix: string,
-                                required: boolean,
-                                validate?: ((value: string, formValues: any) => boolean | string) | Record<string, (value: string, formValues: any) => boolean | string>) {
-        const {setValue, getValues, register, formState: {errors}} = formContext;
+                          validate?: ((value: string, formValues: any) => boolean | string) | Record<string, (value: string, formValues: any) => boolean | string>,
+                          type: | 'text' | 'date' | 'datetime-local' | 'email' | 'month' | 'number' | 'password' | 'search' | 'tel' | 'time' | 'url' = 'text') {
+        const {control, setValue, getValues, formState: {errors}} = formContext;
         return (
             <FormGroup label={label} fieldId={fieldName} isRequired>
-                <TextInputGroup className="text-field-with-suffix">
-                    <TextInputGroupMain type="text" id={fieldName}
-                                        value={getValues(fieldName)}
-                                        {...register(fieldName, {
-                                            required: (required ? "Required field" : false),
-                                            validate: validate
-                                        })}
-                                        onChange={(e, v) => {
-                                            setValue(fieldName, v, {shouldValidate: true});
-                                        }}
-                    >
-                    </TextInputGroupMain>
-                    <Text className='text-field-suffix' component={TextVariants.p}>{suffix}</Text>
-                </TextInputGroup>
+                <Controller
+                    rules={{required: "Required field", validate: validate}}
+                    control={control}
+                    name={fieldName}
+                    render={() => (
+                        <div style={{display: 'flex'}}>
+                            <TextInput className="form-util-text-field" type={type} id={fieldName}
+                                       value={getValues(fieldName)}
+                                       validated={!!errors[fieldName] ? 'error' : 'default'}
+                                       onChange={(_, v) => {
+                                           setValue(fieldName, v, {shouldValidate: true});
+                                       }}
+                            />
+                            <TextInput className="form-util-text-field-suffix" id={fieldName + ':suffix'} value={suffix} isDisabled/>
+                        </div>
+                    )}
+                />
                 {getHelper((errors as any)[fieldName])}
             </FormGroup>
         )
@@ -188,6 +234,6 @@ export function useFormUtil(formContext: UseFormReturn<any>) {
         )
     }
 
-    return {getFormSelect, getTextField, getSwitches, getTextFieldPrefix, getTextFieldSuffix, getTextArea}
+    return {getFormSelect, getTextField, getSwitches, getTextFieldPrefix, getTextArea, getPasswordField, getTextFieldSuffix}
 }
 


(camel-karavan) 03/03: Cleanup

Posted by ma...@apache.org.
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 cf741583da96e8325622049583df26dca79f8ec6
Author: Marat Gubaidullin <ma...@talismancloud.io>
AuthorDate: Wed Jun 19 17:32:17 2024 -0400

    Cleanup
---
 .../api/{AuthResource.java => PublicResource.java} |  42 +--------
 .../src/main/webui/src/api/ErrorEventBus.ts        |  25 +++++
 karavan-app/src/main/webui/src/api/KaravanApi.tsx  | 104 +++++++++------------
 .../src/main/webui/src/api/ProjectService.ts       |  26 ++++--
 .../main/webui/src/config/ConfigurationPage.tsx    |   2 +-
 karavan-app/src/main/webui/src/project/devmode.css |  39 ++++++++
 .../src/project/trace/RunnerInfoTraceNode.tsx      |  76 +++++++++++++++
 .../src/main/webui/src/services/ServicesPage.tsx   |   3 +-
 .../src/main/webui/src/shared/error/Error.ts       |  11 ---
 .../main/webui/src/shared/error/ErrorResponse.ts   |  15 ---
 .../src/shared/error/UseResponseErrorHandler.ts    |  43 ---------
 .../src/main/webui/src/templates/TemplatesPage.tsx |   2 +-
 karavan-app/src/main/webui/src/util/StringUtils.ts |  35 ++++++-
 13 files changed, 241 insertions(+), 182 deletions(-)

diff --git a/karavan-app/src/main/java/org/apache/camel/karavan/api/AuthResource.java b/karavan-app/src/main/java/org/apache/camel/karavan/api/PublicResource.java
similarity index 59%
rename from karavan-app/src/main/java/org/apache/camel/karavan/api/AuthResource.java
rename to karavan-app/src/main/java/org/apache/camel/karavan/api/PublicResource.java
index 1561486c..aa503c12 100644
--- a/karavan-app/src/main/java/org/apache/camel/karavan/api/AuthResource.java
+++ b/karavan-app/src/main/java/org/apache/camel/karavan/api/PublicResource.java
@@ -23,15 +23,12 @@ import jakarta.ws.rs.core.Response;
 import org.apache.camel.karavan.KaravanStartupLoader;
 import org.apache.camel.karavan.kubernetes.KubernetesStatusService;
 import org.eclipse.microprofile.config.ConfigProvider;
-import org.eclipse.microprofile.config.inject.ConfigProperty;
 import org.eclipse.microprofile.health.HealthCheckResponse;
 
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
 import java.util.*;
 
 @Path("/public")
-public class AuthResource {
+public class PublicResource {
 
     @Inject
     KaravanStartupLoader karavanStartupLoader;
@@ -39,43 +36,6 @@ public class AuthResource {
     @Inject
     KubernetesStatusService kubernetesStatusService;
 
-    @ConfigProperty(name = "quarkus.security.users.embedded.realm-name", defaultValue = "")
-    Optional<String> realm;
-
-    @ConfigProperty(name = "quarkus.security.users.embedded.users")
-    Optional<Map<String,String>> users;
-
-    public static String getMd5Hash(String input) throws NoSuchAlgorithmException {
-        MessageDigest md = MessageDigest.getInstance("MD5");
-        byte[] digest = md.digest(input.getBytes());
-        StringBuilder sb = new StringBuilder();
-        for (byte b : digest) {
-            sb.append(String.format("%02x", b));
-        }
-        return sb.toString();
-    }
-
-    @Path("/auth")
-    @POST
-    @Produces(MediaType.APPLICATION_JSON)
-    @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
-    public Response authenticateUser(@FormParam("username") String username, @FormParam("password") String password) {
-        try {
-            if (users.isPresent() && users.get().containsKey(username)) {
-                var pwdStored = users.get().get(username);
-                var pwdReceived = new String(Base64.getDecoder().decode(password));
-                var pwdString = username + ":" + realm.orElse("") + ":" + pwdReceived;
-                String pwdToCheck = getMd5Hash(pwdString);
-                if (Objects.equals(pwdToCheck, pwdStored)) {
-                    return Response.ok().build();
-                }
-            }
-            return Response.status(Response.Status.FORBIDDEN).entity("Incorrect Username and/or Password!").build();
-        } catch (Exception e) {
-            return Response.status(Response.Status.FORBIDDEN).entity(e.getMessage()).build();
-        }
-    }
-
     @GET
     @Path("/auth")
     @Produces(MediaType.TEXT_PLAIN)
diff --git a/karavan-app/src/main/webui/src/api/ErrorEventBus.ts b/karavan-app/src/main/webui/src/api/ErrorEventBus.ts
new file mode 100644
index 00000000..17df7fb2
--- /dev/null
+++ b/karavan-app/src/main/webui/src/api/ErrorEventBus.ts
@@ -0,0 +1,25 @@
+/*
+ * 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 {Subject} from 'rxjs';
+
+const apiErrors = new Subject<any>();
+
+export const ErrorEventBus = {
+
+    sendApiError: (error: any) =>  apiErrors.next(error),
+    onApiError: () => apiErrors.asObservable(),
+}
diff --git a/karavan-app/src/main/webui/src/api/KaravanApi.tsx b/karavan-app/src/main/webui/src/api/KaravanApi.tsx
index 4cf61d0d..0ca8f6c9 100644
--- a/karavan-app/src/main/webui/src/api/KaravanApi.tsx
+++ b/karavan-app/src/main/webui/src/api/KaravanApi.tsx
@@ -29,6 +29,7 @@ import {SsoApi} from "./SsoApi";
 import {v4 as uuidv4} from "uuid";
 import {useAppConfigStore} from "./ProjectStore";
 import {EventBus} from "../designer/utils/EventBus";
+import {ErrorEventBus} from "./ErrorEventBus";
 
 const USER_ID_KEY = 'KARAVAN_USER_ID';
 axios.defaults.headers.common['Accept'] = 'application/json';
@@ -46,9 +47,7 @@ export class KaravanApi {
     }
 
     static getUserId(): string {
-        if (KaravanApi.me?.userName !== undefined) {
-            return KaravanApi.me?.userName;
-        } else {
+        if (KaravanApi.authType === 'public') {
             const userId = localStorage.getItem(USER_ID_KEY);
             if (userId !== null && userId !== undefined) {
                 return userId;
@@ -57,11 +56,12 @@ export class KaravanApi {
                 localStorage.setItem(USER_ID_KEY, newId);
                 return newId;
             }
+        } else {
+            return KaravanApi.me?.userName;
         }
     }
 
     static setAuthType(authType: string) {
-        console.log("SetAuthType", authType)
         KaravanApi.authType = authType;
         switch (authType) {
             case "public": {
@@ -124,30 +124,6 @@ export class KaravanApi {
         });
     }
 
-    static async auth(username: string, password: string, after: (ok: boolean, res: any) => void) {
-        instance.post('/public/auth',
-            {username: username, password: Buffer.from(password).toString('base64')},
-            {headers: {'content-type': 'application/x-www-form-urlencoded'}})
-            .then(res => {
-                if (res.status === 200) {
-                    KaravanApi.basicToken = Buffer.from(username + ":" + password).toString('base64');
-                    KaravanApi.setBasicAuthentication();
-                    KaravanApi.getMe(user => {
-                        after(true, res);
-                        useAppConfigStore.setState({isAuthorized: true})
-                    })
-                } else if (res.status === 401) {
-                    useAppConfigStore.setState({isAuthorized: false})
-                    KaravanApi.basicToken = '';
-                    after(false, res);
-                }
-            }).catch(err => {
-            KaravanApi.basicToken = '';
-            useAppConfigStore.setState({isAuthorized: false})
-            after(false, err);
-        });
-    }
-
     static async getReadiness(after: (readiness: any) => void) {
         axios.get('/public/readiness', {headers: {'Accept': 'application/json'}})
             .then(res => {
@@ -157,8 +133,7 @@ export class KaravanApi {
                     after(undefined);
                 }
             }).catch(err => {
-            console.log(err.message);
-            after(undefined);
+            ErrorEventBus.sendApiError(err);
         });
     }
 
@@ -169,7 +144,7 @@ export class KaravanApi {
                     after(res.data);
                 }
             }).catch(err => {
-            console.log(err);
+            ErrorEventBus.sendApiError(err);
         });
     }
 
@@ -183,7 +158,7 @@ export class KaravanApi {
                     after(res.data);
                 }
             }).catch(err => {
-            console.log(err);
+            ErrorEventBus.sendApiError(err);
         });
     }
 
@@ -195,7 +170,7 @@ export class KaravanApi {
                     after(res.data);
                 }
             }).catch(err => {
-            console.log(err);
+            ErrorEventBus.sendApiError(err);
         });
     }
 
@@ -206,7 +181,7 @@ export class KaravanApi {
                     after(res.data);
                 }
             }).catch(err => {
-            console.log(err);
+            ErrorEventBus.sendApiError(err);
         });
     }
 
@@ -217,7 +192,7 @@ export class KaravanApi {
                     after(res.data);
                 }
             }).catch(err => {
-            console.log(err);
+            ErrorEventBus.sendApiError(err);
         });
     }
 
@@ -228,7 +203,7 @@ export class KaravanApi {
                     after(res.data);
                 }
             }).catch(err => {
-            console.log(err);
+            ErrorEventBus.sendApiError(err);
         });
     }
 
@@ -241,7 +216,7 @@ export class KaravanApi {
                     after(undefined);
                 }
             }).catch(err => {
-            console.log(err);
+            ErrorEventBus.sendApiError(err);
         });
     }
 
@@ -252,7 +227,7 @@ export class KaravanApi {
                     after(res.data);
                 }
             }).catch(err => {
-            console.log(err);
+            ErrorEventBus.sendApiError(err);
         });
     }
 
@@ -263,7 +238,7 @@ export class KaravanApi {
                     after(res.data);
                 }
             }).catch(err => {
-            console.log(err);
+            ErrorEventBus.sendApiError(err);
         });
     }
 
@@ -274,7 +249,7 @@ export class KaravanApi {
                     after(res.data.map((p: Partial<Project> | undefined) => new Project(p)));
                 }
             }).catch(err => {
-            console.log(err);
+            ErrorEventBus.sendApiError(err);
         });
     }
 
@@ -333,7 +308,7 @@ export class KaravanApi {
                     after(res.data);
                 }
             }).catch((err: any) => {
-            console.log(err);
+            ErrorEventBus.sendApiError(err);
             EventBus.sendAlert("Error", err.message, "danger")
         });
     }
@@ -345,7 +320,7 @@ export class KaravanApi {
                     after(res.data);
                 }
             }).catch(err => {
-            console.log(err);
+            ErrorEventBus.sendApiError(err);
         });
     }
 
@@ -356,7 +331,7 @@ export class KaravanApi {
                     after(res.data);
                 }
             }).catch(err => {
-            console.log(err);
+            ErrorEventBus.sendApiError(err);
         });
     }
 
@@ -367,7 +342,7 @@ export class KaravanApi {
                     after(res.data);
                 }
             }).catch(err => {
-            console.log(err);
+            ErrorEventBus.sendApiError(err);
         });
     }
 
@@ -429,7 +404,7 @@ export class KaravanApi {
                     after(res.data);
                 }
             }).catch(err => {
-            console.log(err);
+            ErrorEventBus.sendApiError(err);
         });
     }
 
@@ -440,7 +415,7 @@ export class KaravanApi {
                     after(res.data);
                 }
             }).catch(err => {
-            console.log(err);
+            ErrorEventBus.sendApiError(err);
         });
     }
 
@@ -514,7 +489,7 @@ export class KaravanApi {
                     after(res.data);
                 }
             }).catch(err => {
-            console.log(err);
+            ErrorEventBus.sendApiError(err);
         });
     }
 
@@ -525,7 +500,7 @@ export class KaravanApi {
                     after(res.data);
                 }
             }).catch(err => {
-            console.log(err);
+            ErrorEventBus.sendApiError(err);
         });
     }
 
@@ -536,7 +511,7 @@ export class KaravanApi {
                     after(res.data);
                 }
             }).catch(err => {
-            console.log(err);
+            ErrorEventBus.sendApiError(err);
         });
     }
 
@@ -547,7 +522,7 @@ export class KaravanApi {
                     after(res.data);
                 }
             }).catch(err => {
-            console.log(err);
+            ErrorEventBus.sendApiError(err);
         });
     }
 
@@ -567,7 +542,7 @@ export class KaravanApi {
                     after(res.data);
                 }
             }).catch(err => {
-            console.log(err);
+            ErrorEventBus.sendApiError(err);
         });
     }
 
@@ -578,7 +553,7 @@ export class KaravanApi {
                     after(res.data);
                 }
             }).catch(err => {
-            console.log(err);
+            ErrorEventBus.sendApiError(err);
         });
     }
 
@@ -630,7 +605,7 @@ export class KaravanApi {
                     after(res.data);
                 }
             }).catch(err => {
-            console.log(err);
+            ErrorEventBus.sendApiError(err);
         });
     }
 
@@ -641,7 +616,7 @@ export class KaravanApi {
                     after(res.data);
                 }
             }).catch(err => {
-            console.log(err);
+            ErrorEventBus.sendApiError(err);
         });
     }
 
@@ -652,7 +627,7 @@ export class KaravanApi {
                     after();
                 }
             }).catch(err => {
-            console.log(err);
+            ErrorEventBus.sendApiError(err);
         });
     }
 
@@ -663,7 +638,7 @@ export class KaravanApi {
                     after(res.data);
                 }
             }).catch(err => {
-            console.log(err);
+            ErrorEventBus.sendApiError(err);
         });
     }
 
@@ -674,7 +649,7 @@ export class KaravanApi {
                     after(res.data);
                 }
             }).catch(err => {
-            console.log(err);
+            ErrorEventBus.sendApiError(err);
         });
     }
 
@@ -703,7 +678,18 @@ export class KaravanApi {
                     after(res.data);
                 }
             }).catch(err => {
-            console.log(err);
+            ErrorEventBus.sendApiError(err);
+        });
+    }
+
+    static async getKameletsForProject(projectId: string, after: (yaml: string) => void) {
+        instance.get('/ui/kamelet/' + projectId, {headers: {'Accept': 'text/plain'}})
+            .then(res => {
+                if (res.status === 200) {
+                    after(res.data);
+                }
+            }).catch(err => {
+            ErrorEventBus.sendApiError(err);
         });
     }
 
@@ -714,7 +700,7 @@ export class KaravanApi {
                     after(JSON.stringify(res.data));
                 }
             }).catch(err => {
-            console.log(err);
+            ErrorEventBus.sendApiError(err);
         });
     }
 }
diff --git a/karavan-app/src/main/webui/src/api/ProjectService.ts b/karavan-app/src/main/webui/src/api/ProjectService.ts
index f32e12fa..bce8a5fa 100644
--- a/karavan-app/src/main/webui/src/api/ProjectService.ts
+++ b/karavan-app/src/main/webui/src/api/ProjectService.ts
@@ -25,7 +25,7 @@ import {
     useStatusesStore,
     useFileStore, useLogStore,
     useProjectsStore,
-    useProjectStore, useDevModeStore, useAppConfigStore
+    useProjectStore, useDevModeStore
 } from './ProjectStore';
 import {ProjectEventBus} from './ProjectEventBus';
 import {EventBus} from "../designer/utils/EventBus";
@@ -98,7 +98,6 @@ export class ProjectService {
     }
 
     public static pushProject(project: Project, commitMessage: string) {
-        useAppConfigStore.setState({notificationFetcherId: Math.random().toString()});
         const params = {
             'projectId': project.projectId,
             'message': commitMessage,
@@ -128,18 +127,28 @@ export class ProjectService {
         });
     }
 
-    public static reloadKamelets() {
-        KaravanApi.getKamelets(yamls => {
-            const kamelets: string[] = [];
-            yamls.split(/\n?---\n?/).map(c => c.trim()).forEach(z => kamelets.push(z));
-            KameletApi.saveKamelets(kamelets, true);
-        })
+    static afterKameletsLoad(yamls: string): void {
+        const kamelets: string[] = [];
+        yamls.split(/\n?---\n?/).map(c => c.trim()).forEach(z => kamelets.push(z));
+        KameletApi.saveKamelets(kamelets, true);
+    }
 
+    public static reloadKamelets(projectId?: string) {
+        if (projectId) {
+            KaravanApi.getKameletsForProject(projectId, ProjectService.afterKameletsLoad);
+            useFilesStore.getState().files
+                ?.filter(f => f.name.endsWith('.kamelet.yaml'))
+                .map(f => f.name.replace('.kamelet.yaml', ''))
+                .forEach(name => KameletApi.saveCustomKameletName(name))
+        } else {
+            KaravanApi.getKamelets(ProjectService.afterKameletsLoad)
+        }
         KaravanApi.getFiles("kamelets", (files: ProjectFile[]) => {
             files.map(f => f.name.replace('.kamelet.yaml', ''))
                 .forEach(name => KameletApi.saveCustomKameletName(name))
         });
     }
+
     public static updateFile(file: ProjectFile, active: boolean) {
         KaravanApi.putProjectFile(file, res => {
             if (res.status === 200) {
@@ -282,6 +291,7 @@ export class ProjectService {
         });
         KaravanApi.getFiles(projectId, (files: ProjectFile[]) => {
             useFilesStore.setState({files: files});
+            ProjectService.reloadKamelets(projectId);
         });
 
         KaravanApi.getFilesDiff(projectId, (diff: any) => {
diff --git a/karavan-app/src/main/webui/src/config/ConfigurationPage.tsx b/karavan-app/src/main/webui/src/config/ConfigurationPage.tsx
index 80fd439b..000b1718 100644
--- a/karavan-app/src/main/webui/src/config/ConfigurationPage.tsx
+++ b/karavan-app/src/main/webui/src/config/ConfigurationPage.tsx
@@ -79,7 +79,7 @@ export const ConfigurationPage = (props: Props) => {
             </PageSection>
             <PageSection className="tools-section" padding={{default: 'noPadding'}}>
                 <Flex direction={{default: "column"}} spaceItems={{default: "spaceItemsNone"}}>
-                    <FlexItem className="knowledge-tabs">
+                    <FlexItem>
                         <Tabs activeKey={tab} onSelect={(event, tabIndex) => setTab(tabIndex)}>
                             <Tab eventKey="statuses" title="Statuses"/>
                             <Tab eventKey="secrets" title="Secrets" isDisabled/>
diff --git a/karavan-app/src/main/webui/src/project/devmode.css b/karavan-app/src/main/webui/src/project/devmode.css
new file mode 100644
index 00000000..fea70e8e
--- /dev/null
+++ b/karavan-app/src/main/webui/src/project/devmode.css
@@ -0,0 +1,39 @@
+/*
+ * 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.
+ */
+
+.karavan .refresher {
+    width: 34px;
+    height: 34px;
+}
+
+.karavan .refresher .spinner {
+    position: absolute;
+    width: 34px;
+    height: 34px;
+}
+
+.karavan .refresher .button {
+    position: absolute;
+    width: 34px;
+    height: 34px;
+    padding: 0;
+    font-size: 16px;
+}
+
+.karavan .refresher .button .pf-v5-c-button__icon {
+    margin: 0;
+}
\ No newline at end of file
diff --git a/karavan-app/src/main/webui/src/project/trace/RunnerInfoTraceNode.tsx b/karavan-app/src/main/webui/src/project/trace/RunnerInfoTraceNode.tsx
new file mode 100644
index 00000000..73181663
--- /dev/null
+++ b/karavan-app/src/main/webui/src/project/trace/RunnerInfoTraceNode.tsx
@@ -0,0 +1,76 @@
+/*
+ * 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 {
+    CodeBlock, CodeBlockCode, DataList, DataListCell, DataListItem, DataListItemCells, DataListItemRow, DataListWrapModifier,
+    DescriptionList,
+    DescriptionListDescription,
+    DescriptionListGroup,
+    DescriptionListTerm, Panel, PanelHeader, PanelMain, PanelMainBody
+} from '@patternfly/react-core';
+import '../../designer/karavan.css';
+
+interface Props {
+    trace: any
+}
+
+export function RunnerInfoTraceNode (props: Props) {
+
+    const type = props.trace?.message?.body?.type;
+    const body = props.trace?.message?.body?.value;
+    const headers: any[] = [...props.trace?.message?.headers];
+    return (
+        <Panel isScrollable>
+                <PanelMain tabIndex={0}>
+                    <PanelHeader>
+                        <DescriptionList isHorizontal>
+                            <DescriptionListGroup>
+                                <DescriptionListTerm>Headers</DescriptionListTerm>
+                            </DescriptionListGroup>
+                            <DataList aria-label="Compact data list example" isCompact>
+                                {headers.map((header: any, index: number) => (
+                                    <DataListItem key={header[0] + "-" + index} aria-labelledby="compact-item1">
+                                        <DataListItemRow>
+                                            <DataListItemCells
+                                                dataListCells={[
+                                                    <DataListCell key="uid" >{header.key}</DataListCell>,
+                                                    <DataListCell key="type">{header.type}</DataListCell>,
+                                                    <DataListCell key="routeId" wrapModifier={DataListWrapModifier.truncate}>
+                                                        {header.value}
+                                                    </DataListCell>,
+                                                ]}
+                                            />
+                                        </DataListItemRow>
+                                    </DataListItem>))}
+                            </DataList>
+                            <DescriptionListGroup>
+                                <DescriptionListTerm>Body</DescriptionListTerm>
+                                <DescriptionListDescription>
+                                    {type}
+                                </DescriptionListDescription>
+                            </DescriptionListGroup>
+                        </DescriptionList>
+                    </PanelHeader>
+                    <PanelMainBody style={{padding: "0"}}>
+                        <CodeBlock title="Body">
+                            <CodeBlockCode id="code-content">{body}</CodeBlockCode>
+                        </CodeBlock>
+                    </PanelMainBody>
+                </PanelMain>
+            </Panel>
+    );
+}
diff --git a/karavan-app/src/main/webui/src/services/ServicesPage.tsx b/karavan-app/src/main/webui/src/services/ServicesPage.tsx
index 725b2124..252fa667 100644
--- a/karavan-app/src/main/webui/src/services/ServicesPage.tsx
+++ b/karavan-app/src/main/webui/src/services/ServicesPage.tsx
@@ -32,7 +32,6 @@ import {
 } from '@patternfly/react-core';
 import '../designer/karavan.css';
 import RefreshIcon from '@patternfly/react-icons/dist/esm/icons/sync-alt-icon';
-import PlusIcon from '@patternfly/react-icons/dist/esm/icons/plus-icon';
 import {
 	Td,
 	Th,
@@ -143,7 +142,7 @@ export function ServicesPage () {
             <PageSection className="tools-section" padding={{default: 'noPadding'}}>
                 <MainToolbar title={title()} tools={getTools()}/>
             </PageSection>
-            <PageSection isFilled className="kamelets-page">
+            <PageSection isFilled className="scrolled-page">
                 {getServicesTable()}
             </PageSection>
             <ProjectLogPanel/>
diff --git a/karavan-app/src/main/webui/src/shared/error/Error.ts b/karavan-app/src/main/webui/src/shared/error/Error.ts
deleted file mode 100644
index b0e0dac3..00000000
--- a/karavan-app/src/main/webui/src/shared/error/Error.ts
+++ /dev/null
@@ -1,11 +0,0 @@
-export class Error {
-    field: string = '';
-    message: string = '';
-    code: string = '';
-
-    constructor(field: string, message: string, code: string) {
-        this.field = field;
-        this.message = message;
-        this.code = code;
-    }
-}
\ No newline at end of file
diff --git a/karavan-app/src/main/webui/src/shared/error/ErrorResponse.ts b/karavan-app/src/main/webui/src/shared/error/ErrorResponse.ts
deleted file mode 100644
index 70728e36..00000000
--- a/karavan-app/src/main/webui/src/shared/error/ErrorResponse.ts
+++ /dev/null
@@ -1,15 +0,0 @@
-import {Error} from "./Error";
-
-export class ErrorResponse {
-    status: number = 0;
-    error: string = '';
-    message: string = '';
-    errors: Array<Error> = [];
-
-    constructor(status: number, error: string, message: string, errors: Array<Error>) {
-        this.status = status;
-        this.error = error;
-        this.message = message;
-        this.errors = errors;
-    }
-}
\ No newline at end of file
diff --git a/karavan-app/src/main/webui/src/shared/error/UseResponseErrorHandler.ts b/karavan-app/src/main/webui/src/shared/error/UseResponseErrorHandler.ts
deleted file mode 100644
index 7e8c557a..00000000
--- a/karavan-app/src/main/webui/src/shared/error/UseResponseErrorHandler.ts
+++ /dev/null
@@ -1,43 +0,0 @@
-import {useState} from 'react';
-import {ErrorResponse} from "./ErrorResponse";
-import {Error} from "./Error";
-import {AxiosError} from "axios";
-
-export function useResponseErrorHandler(errorResponseToFormFields: Map<string, string>, setError: any) {
-    const [globalErrors, setGlobalErrors] = useState<string[]>([]);
-
-    function registerResponseErrors(axiosError: AxiosError) {
-        const errorResponse: ErrorResponse = axiosError.response?.data as ErrorResponse;
-        // Register field errors if field errors were returned
-        if (errorResponse.errors) {
-            // Reset global error
-            setGlobalErrors([]);
-
-            // Register all field errors
-            errorResponse.errors?.forEach((error: Error) => {
-                // If field name was found in mapping table, register it to the form error
-                if (errorResponseToFormFields.get(error.field)) {
-                    setError(errorResponseToFormFields.get(error.field), {
-                        type: 'custom',
-                        message: error.message
-                    });
-                }
-                // If field name was not found in mapping table, register it to the global error
-                else {
-                    // Make copy as state shouldn't be mutated
-                    setGlobalErrors(prevGlobalErrors => [...prevGlobalErrors, error.message]);
-                }
-            });
-        }
-        // Register global error if no field errors were returned
-        else {
-            setGlobalErrors([errorResponse.message]);
-        }
-    }
-
-    function resetGlobalErrors() {
-        setGlobalErrors([]);
-    }
-
-    return [globalErrors, registerResponseErrors, resetGlobalErrors] as const;
-}
diff --git a/karavan-app/src/main/webui/src/templates/TemplatesPage.tsx b/karavan-app/src/main/webui/src/templates/TemplatesPage.tsx
index f13242f1..c63df7b0 100644
--- a/karavan-app/src/main/webui/src/templates/TemplatesPage.tsx
+++ b/karavan-app/src/main/webui/src/templates/TemplatesPage.tsx
@@ -117,7 +117,7 @@ export function TemplatesPage () {
             <PageSection className="tools-section" padding={{default: 'noPadding'}}>
                 <MainToolbar title={title()} tools={getTools()}/>
             </PageSection>
-            <PageSection isFilled className="kamelets-page">
+            <PageSection isFilled className="scrolled-page">
                 {getProjectsTable()}
             </PageSection>
         </PageSection>
diff --git a/karavan-app/src/main/webui/src/util/StringUtils.ts b/karavan-app/src/main/webui/src/util/StringUtils.ts
index ced025d4..ec04e358 100644
--- a/karavan-app/src/main/webui/src/util/StringUtils.ts
+++ b/karavan-app/src/main/webui/src/util/StringUtils.ts
@@ -23,4 +23,37 @@ export function getPathParams(input: string): string[] {
 
 export function getShortCommit(commitId: string): string {
     return commitId ? commitId?.substring(0, 7) : "-";
-}
\ No newline at end of file
+}
+
+export function hasLowercase(password: string): boolean {
+    const pattern = /[a-z]/;
+    return pattern.test(password);
+}
+
+export function hasUppercase(password: string): boolean {
+    const pattern = /[A-Z]/;
+    return pattern.test(password);
+}
+
+export function hasDigit(password: string): boolean {
+    const pattern = /\d/;
+    return pattern.test(password);
+}
+
+export function hasSpecialCharacter(password: string): boolean {
+    const pattern = /[@$!%*?&]/;
+    return pattern.test(password);
+}
+
+export function hasMinimumLength(password: string, minLength: number = 8): boolean {
+    return password.length >= minLength;
+}
+
+
+export function isValidPassword(password: string): boolean {
+    return hasLowercase(password) &&
+        hasUppercase(password) &&
+        hasDigit(password) &&
+        hasSpecialCharacter(password) &&
+        hasMinimumLength(password);
+}


(camel-karavan) 02/03: Fix #1324

Posted by ma...@apache.org.
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 13b6068295b52d4ddcf7647416b1ab28cd9a839b
Author: Marat Gubaidullin <ma...@talismancloud.io>
AuthorDate: Wed Jun 19 17:17:39 2024 -0400

    Fix #1324
---
 karavan-app/src/main/webui/src/project/files/CreateIntegrationModal.tsx | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/karavan-app/src/main/webui/src/project/files/CreateIntegrationModal.tsx b/karavan-app/src/main/webui/src/project/files/CreateIntegrationModal.tsx
index c6a91b0d..8c06de35 100644
--- a/karavan-app/src/main/webui/src/project/files/CreateIntegrationModal.tsx
+++ b/karavan-app/src/main/webui/src/project/files/CreateIntegrationModal.tsx
@@ -156,7 +156,7 @@ export function CreateIntegrationModal() {
                         })}
                     </ToggleGroup>
                 </FormGroup>}
-                {getTextFieldSuffix('name', 'Name', getFileSuffix(), true, {
+                {getTextFieldSuffix('name', 'Name', getFileSuffix(), {
                     regex: v => isValidFileName(v) || 'Only characters, numbers and dashes allowed',
                     length: v => v.length > 3 || 'File name should be longer that 3 characters',
                     name: v => !['templates', 'kamelets', 'karavan'].includes(v) || "'templates', 'kamelets', 'karavan' can't be used as project",