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

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

commit a784ba5c940d70787eb679412efa027cce97aa3d
Author: Marat Gubaidullin <[email protected]>
AuthorDate: Mon Aug 24 15:35:46 2026 -0400

    Karavan Webapp UI Services
---
 .../main/webui/src/services/NotificationService.ts |  73 +++++
 .../src/main/webui/src/services/ProjectService.ts  | 310 +++++++++++++++++++++
 .../src/main/webui/src/services/SystemService.ts   |  37 +++
 3 files changed, 420 insertions(+)

diff --git a/karavan-app/src/main/webui/src/services/NotificationService.ts 
b/karavan-app/src/main/webui/src/services/NotificationService.ts
new file mode 100644
index 00000000..27265bf5
--- /dev/null
+++ b/karavan-app/src/main/webui/src/services/NotificationService.ts
@@ -0,0 +1,73 @@
+/*
+ * 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";
+import {unstable_batchedUpdates} from "react-dom";
+import {useProjectStore} from "@stores/ProjectStore";
+import {ProjectService} from "./ProjectService";
+import {EventBus} from "@designer/utils/EventBus";
+
+export class KaravanEvent {
+    id: string = '';
+    type: 'system' | 'user' = 'system';
+    event: string = '';
+    className: string = '';
+    data: any = {};
+
+    public constructor(init?: Partial<KaravanEvent>) {
+        Object.assign(this, init);
+    }
+}
+
+const karavanEvents = new Subject<KaravanEvent>();
+
+export const NotificationEventBus = {
+    sendEvent: (event: KaravanEvent) =>  karavanEvents.next(event),
+    onEvent: () => karavanEvents.asObservable(),
+}
+
+const sub = NotificationEventBus.onEvent()?.subscribe((event: KaravanEvent) => 
{
+    if (event.event === 'commit' && event.className === "ProjectFolder") {
+        const projectId = event.data?.projectId;
+        const statuses = event.data?.statuses;
+        const messages = event.data?.messages;
+        const variant = statuses?.filter((s: any) => s !== "OK")?.length > 0 ? 
'danger' : 'success';
+        const title = variant === 'success' ? 'Commited' : 'Error';
+        EventBus.sendAlert(title, messages, variant);
+        if (useProjectStore.getState().project?.projectId === projectId) {
+            unstable_batchedUpdates(() => {
+                useProjectStore.setState({isPushing: false});
+                ProjectService.refreshProject(projectId);
+                ProjectService.refreshProjectData(projectId);
+            });
+        }
+    } else if (event.event === 'imagesLoaded') {
+        const projectId = event.data?.projectId;
+        unstable_batchedUpdates(() => {
+            ProjectService.refreshImages(projectId);
+        });
+    } else if (event.event === 'error') {
+        const error = event.data?.error;
+        EventBus.sendAlert('Error', error, "danger");
+    } else if (event.event === 'ping') {
+        // do nothing
+    } else {
+        const message = event.data?.message ?  event.data?.message : 
JSON.stringify(event.data);
+        // EventBus.sendAlert('Success', message);
+    }
+});
+
diff --git a/karavan-app/src/main/webui/src/services/ProjectService.ts 
b/karavan-app/src/main/webui/src/services/ProjectService.ts
new file mode 100644
index 00000000..e732d047
--- /dev/null
+++ b/karavan-app/src/main/webui/src/services/ProjectService.ts
@@ -0,0 +1,310 @@
+/*
+ * 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 {KaravanApi} from '@api/KaravanApi';
+import {CamelStatus, ContainerImage, ContainerStatus, Project, ProjectFile, 
ProjectType} from '@models/ProjectModels';
+import {TemplateApi} from '@core/api/TemplateApi';
+import {InfrastructureAPI} from '@designer/utils/InfrastructureAPI';
+import {unstable_batchedUpdates} from 'react-dom'
+import {useDevModeStore, useFilesStore, useFileStore, useProjectsStore, 
useProjectStore, useStatusesStore} from '@stores/ProjectStore';
+import {LogsEventBus} from '@bus/LogsEventBus';
+import {EventBus} from "@designer/utils/EventBus";
+import {KameletApi} from "@core/api/KameletApi";
+import {getCurrentUser} from "@api/auth/AuthApi";
+import {AxiosResponse} from "axios";
+import {JSON_SCHEMA_EXTENSION, KARAVAN_DOT_EXTENSION, 
LANDSCAPE_FILE_NAME_JSON, OPENAPI_FILE_NAME_JSON} from "@core/contants";
+import {useLogStore} from "@stores/LogStore";
+import {useContainerStatusesStore} from "@stores/ContainerStatusesStore";
+import {ComponentApi} from "@core/api/ComponentApi";
+
+const COMPONENTS_BLACK_LIST = 'component-blocklist.txt';
+
+export class ProjectService {
+
+    public static startDevModeContainer(projectId: string, verbose: boolean, 
compile: boolean = false, showLog: boolean = true) {
+        useDevModeStore.setState({status: 'wip'})
+        KaravanApi.startDevModeContainer(projectId, verbose, compile, res => {
+            useDevModeStore.setState({status: 'none'})
+            if (res.status === 200 || res.status === 201) {
+                LogsEventBus.sendLog('set', '');
+                useLogStore.setState({podName: res.data})
+                useProjectStore.setState({tabIndex: 'log'})
+            } else {
+                const resData = (res as any)?.response?.data;
+                const error = resData?.message ? resData?.message : 
res.statusText;
+                EventBus.sendAlert('Error Starting DevMode container', error, 
'warning')
+            }
+        });
+    }
+
+    public static reloadDevModeCode(project: Project) {
+        useDevModeStore.setState({status: 'wip'})
+        KaravanApi.reloadDevModeCode(project.projectId, res => {
+            useDevModeStore.setState({status: 'none'})
+            if (res.status === 200 || res.status === 201) {
+                // setIsReloadingPod(false);
+            } else {
+                EventBus.sendAlert('Error Reloading DevMode container', 
res.statusText, 'warning')
+            }
+        });
+    }
+
+    public static stopDevModeContainer(projectId: string) {
+        useDevModeStore.setState({status: 'wip'})
+        KaravanApi.manageContainer(projectId, 'devmode', projectId, 'stop', 
'never', res => {
+            useDevModeStore.setState({status: 'none'})
+            if (res.status === 200) {
+            } else {
+                EventBus.sendAlert('Error stopping DevMode container', 
res.statusText, 'warning')
+            }
+        });
+    }
+
+    public static pauseDevModeContainer(project: Project) {
+        useDevModeStore.setState({status: 'wip'})
+        KaravanApi.manageContainer(project.projectId, 'devmode', 
project.projectId, 'pause', 'never', res => {
+            useDevModeStore.setState({status: 'none'})
+            if (res.status === 200) {
+            } else {
+                EventBus.sendAlert('Error stopping DevMode container', 
res.statusText, 'warning')
+            }
+        });
+    }
+
+    public static deleteDevModeContainer(projectId: string) {
+        useDevModeStore.setState({status: 'wip'})
+        LogsEventBus.sendLog('set', '');
+        KaravanApi.deleteDevModeContainer(projectId, false, res => {
+            useDevModeStore.setState({status: 'none'})
+            if (res.status === 202) {
+            } else {
+                EventBus.sendAlert('Error delete runner', res.statusText, 
'warning')
+            }
+        });
+    }
+
+    public static pushProject(project: Project, commitMessage: string, 
selectedFileNames: string[]) {
+        const params = {
+            'projectId': project.projectId,
+            'message': commitMessage,
+            'userId': getCurrentUser()?.username,
+            'fileNames': selectedFileNames.join(","),
+        };
+        KaravanApi.push(params, res => {
+            if (res.status === 200 || res.status === 201) {
+                // ProjectService.refreshProject(project.projectId);
+                // ProjectService.refreshProjectData(project.projectId);
+            } else {
+                EventBus.sendAlert("Error pushing", (res as 
any)?.response?.data, 'danger')
+            }
+        });
+    }
+
+    public static pullProject(projectId: string) {
+        useProjectStore.setState({isPulling: true})
+        KaravanApi.pull(projectId, res => {
+            if (res.status === 200 || res.status === 201) {
+                useProjectStore.setState({isPulling: false})
+                ProjectService.refreshProject(projectId);
+                ProjectService.refreshProjectData(projectId);
+            } else {
+                EventBus.sendAlert("Error pulling", (res as 
any)?.response?.data, 'danger')
+            }
+            useProjectStore.setState({isPulling: false})
+        });
+    }
+
+    public static pullAllProjects() {
+        useProjectStore.setState({isPulling: true})
+        KaravanApi.pull(undefined, res => {
+            if (res.status === 200 || res.status === 201) {
+                useProjectStore.setState({isPulling: false})
+                ProjectService.refreshProjects();
+            } else {
+                EventBus.sendAlert("Error pulling", (res as 
any)?.response?.data, 'danger')
+            }
+            useProjectStore.setState({isPulling: false})
+        });
+    }
+
+    static afterKameletsLoad(yamls: string, saveCamelKamelets: (kameletYamls: 
string[], clean?: boolean) => void): void {
+        try {
+            const kamelets: string[] = [];
+            yamls.split(/\n?---\n?/).map(c => c.trim()).forEach(z => 
kamelets.push(z));
+            saveCamelKamelets(kamelets, true);
+        } catch (e: any) {
+            console.error(e);
+            EventBus.sendAlert("Error updating kamelets", e?.message, 
'danger');
+        }
+    }
+
+    public static loadCustomKamelets() {
+        KaravanApi.getCustomKamelets(yaml => 
ProjectService.afterKameletsLoad(yaml, KameletApi.saveCustomKamelets));
+    }
+
+    public static loadBlockedComponentAndKamelets() {
+        KaravanApi.getFiles(ProjectType.configuration, (files: ProjectFile[]) 
=> {
+            const c = files.find(f => f.name === COMPONENTS_BLACK_LIST);
+            const listC = c?.code?.split('\n') || [];
+            ComponentApi.saveBlockedComponentNames(listC);
+        });
+    }
+
+    public static updateFile(file: ProjectFile, active: boolean, 
updateFilesStore: boolean = true, after?: (res: AxiosResponse<any>) => void) {
+        KaravanApi.putProjectFile(file, res => {
+            if (res.status === 200) {
+                const newFile = res.data;
+                if (updateFilesStore) {
+                    useFilesStore.getState().upsertFile(newFile);
+                }
+                if (active) {
+                    useFileStore.setState({file: newFile});
+                }
+                after?.(res)
+            } else {
+                // console.log(res) //TODO show notification
+            }
+        })
+    }
+
+    public static renameFile(projectId: string, oldName: string, newName: 
string,  after: (res: boolean) => void) {
+        KaravanApi.renameProjectFile(projectId, oldName, newName, (result: 
boolean, err?: Error | undefined) => {
+            if (result) {
+                after(result);
+            } else {
+                EventBus.sendAlert("Error", err?.message ?? "Error copying 
file!", "warning");
+            }
+        })
+    }
+
+    public static refreshProject(projectId: string) {
+        KaravanApi.getProject(projectId, (project: Project) => {
+            useProjectStore.setState({project: project});
+            unstable_batchedUpdates(() => {
+                useProjectsStore.getState().upsertProject(project);
+            })
+        });
+    }
+
+    public static refreshProjects() {
+        KaravanApi.getProjects((projects: Project[]) => {
+            useProjectsStore.setState({projects: projects});
+        });
+    }
+
+    public static refreshAllContainerStatuses() {
+        KaravanApi.getAllContainerStatuses((statuses: ContainerStatus[]) => {
+            useContainerStatusesStore.setState({containers: statuses});
+        });
+    }
+
+    public static refreshAllCamelContextStatuses() {
+        KaravanApi.getAllCamelStatuses("context",(statuses: CamelStatus[]) => {
+            useStatusesStore.setState({camelContexts: statuses});
+        });
+    }
+
+    public static refreshAllCamelRouteStatuses() {
+        KaravanApi.getAllCamelStatuses("route",(statuses: CamelStatus[]) => {
+            useStatusesStore.setState({routes: statuses});
+        });
+    }
+
+    public static refreshAllCamelProcessorStatuses() {
+        KaravanApi.getAllCamelStatuses("processor",(statuses: CamelStatus[]) 
=> {
+            useStatusesStore.setState({processors: statuses});
+        });
+    }
+    public static refreshAllCamelConsumerStatuses() {
+        KaravanApi.getAllCamelStatuses("consumer",(statuses: CamelStatus[]) => 
{
+            useStatusesStore.setState({consumers: statuses});
+        });
+    }
+
+    public static refreshCamelTraces(projectId: string) {
+        KaravanApi.getProjectCamelTraces(projectId, res => {
+            if (res.status === 200) {
+                useProjectStore.setState({camelTraces: res.data})
+            } else {
+                useProjectStore.setState({camelTraces: []})
+            }
+        })
+    }
+
+    public static refreshImages(projectId: string) {
+        KaravanApi.getImages(projectId, (res: ContainerImage[]) => {
+            useProjectStore.setState({images: res});
+        });
+    }
+
+    public static deleteProject(project: Project, deleteContainers?: boolean) {
+        KaravanApi.deleteProject(project, deleteContainers === true, res => {
+            if (res.status === 204) {
+                EventBus.sendAlert('Success', 'Project deleted', 'success');
+                ProjectService.refreshProjects();
+            } else {
+                EventBus.sendAlert('Warning', 'Error when deleting project:' + 
res.statusText, 'warning');
+            }
+        });
+    }
+
+    public static deleteFile(file: ProjectFile) {
+        KaravanApi.deleteProjectFile(file, res => {
+            if (res.status === 204) {
+                ProjectService.refreshProjectData(file.projectId);
+            } else {
+            }
+        });
+    }
+
+    public static refreshProjectFiles(projectId: string) {
+        KaravanApi.getFiles(projectId, (files: ProjectFile[]) => {
+            const kameletsYamls = files.filter(f => 
f.name.endsWith('.kamelet.yaml') && f.code?.length > 0).map(f => 
f.code).join("\n---\n")
+            if (kameletsYamls && kameletsYamls.length > 0) {
+                ProjectService.afterKameletsLoad(kameletsYamls, 
KameletApi.saveProjectKamelets);
+            }
+            useFilesStore.setState({files: files});
+        });
+
+        useFilesStore.getState().fetchCommitedFiles(projectId);
+
+        KaravanApi.getFilesDiff(projectId, (diff: any) => {
+            useFilesStore.setState({diff: diff});
+        });
+    }
+
+    public static refreshProjectData(projectId: string) {
+        KaravanApi.getProject(projectId, (project: Project) => {
+            // ProjectEventBus.selectProject(project);
+            KaravanApi.getFiles(ProjectType.templates, (files: ProjectFile[]) 
=> {
+                files.filter(f => f.name.endsWith('java'))
+                    .forEach(f => {
+                        const name = f.name.replace(".java", '');
+                        TemplateApi.saveTemplate(name, f.code);
+                    })
+            });
+        });
+        KaravanApi.getCustomKamelets(yaml => 
ProjectService.afterKameletsLoad(yaml, KameletApi.saveCustomKamelets));
+        ProjectService.refreshProjectFiles(projectId);
+        ProjectService.loadBlockedComponentAndKamelets();
+        KaravanApi.getConfigMaps((any: []) => 
InfrastructureAPI.setConfigMaps(any));
+        KaravanApi.getSecrets((any: []) => InfrastructureAPI.setSecrets(any));
+        KaravanApi.getServices((any: []) => 
InfrastructureAPI.setServices(any));
+        KaravanApi.getImages(projectId, (images: []) => 
useProjectStore.setState({images: images}));
+        useFilesStore.getState().fetchCommitedFiles(projectId);
+    }
+}
\ No newline at end of file
diff --git a/karavan-app/src/main/webui/src/services/SystemService.ts 
b/karavan-app/src/main/webui/src/services/SystemService.ts
new file mode 100644
index 00000000..76e27114
--- /dev/null
+++ b/karavan-app/src/main/webui/src/services/SystemService.ts
@@ -0,0 +1,37 @@
+/*
+ * 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 {useSystemStore} from "@stores/SystemStore";
+import {SystemApi} from "@api/SystemApi";
+import {KubernetesConfigMap, KubernetesSecret} from "@models/SystemModels";
+
+export class SystemService {
+
+    public static refresh() {
+        SystemApi.getSecrets((secrets: KubernetesSecret[]) => {
+            useSystemStore.setState({secrets: [...secrets].sort((a, b) => 
a.name.localeCompare(b.name))});
+        });
+        SystemApi.getConfigMaps((configmaps: KubernetesConfigMap[]) => {
+            useSystemStore.setState({configmaps: [...configmaps].sort((a, b) 
=> a.name.localeCompare(b.name))});
+        });
+        SystemApi.getEnvVars((envVars: string[]) => {
+            useSystemStore.setState({envVars: [...envVars].sort()});
+        });
+        SystemApi.getAppProps((appProps: string[]) => {
+            useSystemStore.setState({appProps: [...appProps].sort()});
+        });
+    }
+}
\ No newline at end of file

Reply via email to