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 2023/08/18 18:34:05 UTC
[camel-karavan] 02/02: Fix #865
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 f91a11255e8a7f95dc63c087c1ad634a9cc19e56
Author: Marat Gubaidullin <ma...@Marats-MacBook-Pro.local>
AuthorDate: Fri Aug 18 14:33:01 2023 -0400
Fix #865
---
.../camel/karavan/api/InfrastructureResource.java | 10 +-
.../apache/camel/karavan/api/StatusResource.java | 17 +-
.../src/main/resources/services/devservices.yaml | 15 +-
.../src/main/webui/src/api/KaravanApi.tsx | 6 +-
.../src/main/webui/src/api/ProjectStore.ts | 67 ++++-
.../src/main/webui/src/dashboard/DashboardPage.tsx | 66 +----
.../karavan-app/src/main/webui/src/index.css | 2 +-
.../src/main/webui/src/main/DataPoller.tsx | 83 ------
.../karavan-app/src/main/webui/src/main/Main.tsx | 30 +--
.../src/main/webui/src/main/MainDataPoller.tsx | 62 +++++
.../src/main/webui/src/main/MainLogin.tsx | 102 ++++----
.../src/main/webui/src/main/PageNavigation.tsx | 16 +-
.../main/webui/src/project/ProjectDataPoller.tsx | 61 +++++
.../src/main/webui/src/project/ProjectPage.tsx | 2 +
.../webui/src/project/dashboard/DashboardTab.tsx | 44 +---
.../main/webui/src/project/files/FilesToolbar.tsx | 1 -
.../main/webui/src/project/log/ProjectLogPanel.tsx | 2 -
.../src/project/pipeline/ProjectPipelineTab.tsx | 4 +-
.../webui/src/project/pipeline/ProjectStatus.tsx | 285 +++++++++------------
.../src/project/trace/RunnerInfoTraceModal.tsx | 4 +-
.../src/project/trace/RunnerInfoTraceNode.tsx | 4 +-
.../src/main/webui/src/project/trace/TraceTab.tsx | 36 +--
.../src/main/webui/src/projects/ProjectsPage.tsx | 38 +--
.../karavan/infinispan/InfinispanService.java | 7 +
24 files changed, 444 insertions(+), 520 deletions(-)
diff --git a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/api/InfrastructureResource.java b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/api/InfrastructureResource.java
index 478205b5..931aa2c5 100644
--- a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/api/InfrastructureResource.java
+++ b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/api/InfrastructureResource.java
@@ -156,9 +156,13 @@ public class InfrastructureResource {
@Produces(MediaType.APPLICATION_JSON)
@Path("/service")
public List<ServiceStatus> getAllServiceStatuses() throws Exception {
- return infinispanService.getServiceStatuses().stream()
- .sorted(Comparator.comparing(ServiceStatus::getProjectId))
- .collect(Collectors.toList());
+ if (infinispanService.isReady()) {
+ return infinispanService.getServiceStatuses().stream()
+ .sorted(Comparator.comparing(ServiceStatus::getProjectId))
+ .collect(Collectors.toList());
+ } else {
+ return List.of();
+ }
}
@GET
diff --git a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/api/StatusResource.java b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/api/StatusResource.java
index 3a584f36..b4dcd21d 100644
--- a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/api/StatusResource.java
+++ b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/api/StatusResource.java
@@ -44,13 +44,12 @@ public class StatusResource {
@GET
@Produces(MediaType.APPLICATION_JSON)
- @Path("/pipeline/{projectId}/{env}")
- public Response getPipelineStatus(@PathParam("projectId") String projectId, @PathParam("env") String env) {
- PipelineStatus status = infinispanService.getPipelineStatus(projectId, env);
- if (status != null) {
- return Response.ok(status).build();
+ @Path("/pipeline/{env}")
+ public List<PipelineStatus> getPipelineStatuses(@PathParam("env") String env) {
+ if (infinispanService.isReady()) {
+ return infinispanService.getPipelineStatuses(env);
} else {
- return Response.noContent().build();
+ return List.of();
}
}
@@ -81,6 +80,10 @@ public class StatusResource {
@Produces(MediaType.APPLICATION_JSON)
@Path("/camel/{env}")
public List<CamelStatus> getCamelStatusByEnv(@PathParam("env") String env) {
- return infinispanService.getCamelStatusesByEnv(env, CamelStatus.Name.context);
+ if (infinispanService.isReady()) {
+ return infinispanService.getCamelStatusesByEnv(env, CamelStatus.Name.context);
+ } else {
+ return List.of();
+ }
}
}
\ No newline at end of file
diff --git a/karavan-web/karavan-app/src/main/resources/services/devservices.yaml b/karavan-web/karavan-app/src/main/resources/services/devservices.yaml
index 1365fd9a..e921cd22 100644
--- a/karavan-web/karavan-app/src/main/resources/services/devservices.yaml
+++ b/karavan-web/karavan-app/src/main/resources/services/devservices.yaml
@@ -41,17 +41,20 @@ services:
KAFKA_INTER_BROKER_LISTENER_NAME: PLAINTEXT
KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1
- mariadb:
- image: mariadb
+ postgres:
+ image: postgres:15.4
restart: always
environment:
- MARIADB_ROOT_PASSWORD: mariadb
+ - POSTGRES_USER=postgres
+ - POSTGRES_PASSWORD=postgres
+ ports:
+ - '5432:5432'
- mariadb-adminer:
- image: adminer
+ adminer:
+ image: adminer:4.8.1-standalone
restart: always
ports:
- - 9080:8080
+ - 8080:8080
greenmail:
container_name: greenmail
diff --git a/karavan-web/karavan-app/src/main/webui/src/api/KaravanApi.tsx b/karavan-web/karavan-app/src/main/webui/src/api/KaravanApi.tsx
index 43919f12..cd2213f2 100644
--- a/karavan-web/karavan-app/src/main/webui/src/api/KaravanApi.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/api/KaravanApi.tsx
@@ -155,13 +155,13 @@ export class KaravanApi {
});
}
- static async getProjectPipelineStatus(projectId: string, env: string, after: (status?: PipelineStatus) => void) {
- instance.get('/api/status/pipeline/' + projectId + "/" + env)
+ static async getPipelineStatuses(env: string, after: (status: PipelineStatus[]) => void) {
+ instance.get('/api/status/pipeline/' + env)
.then(res => {
if (res.status === 200) {
after(res.data);
} else if (res.status === 204) {
- after(undefined);
+ after([]);
}
}).catch(err => {
diff --git a/karavan-web/karavan-app/src/main/webui/src/api/ProjectStore.ts b/karavan-web/karavan-app/src/main/webui/src/api/ProjectStore.ts
index 651c1071..ed7f1acc 100644
--- a/karavan-web/karavan-app/src/main/webui/src/api/ProjectStore.ts
+++ b/karavan-web/karavan-app/src/main/webui/src/api/ProjectStore.ts
@@ -16,16 +16,32 @@
*/
import {create} from 'zustand'
-import {AppConfig, DeploymentStatus, ContainerStatus, Project, ProjectFile, ServiceStatus, CamelStatus} from "./ProjectModels";
+import {
+ AppConfig,
+ DeploymentStatus,
+ ContainerStatus,
+ Project,
+ ProjectFile,
+ ServiceStatus,
+ CamelStatus,
+ PipelineStatus
+} from "./ProjectModels";
import {ProjectEventBus} from "./ProjectEventBus";
import {unstable_batchedUpdates} from "react-dom";
+import {useState} from "react";
interface AppConfigState {
+ loading: boolean;
+ setLoading: (loading: boolean) => void;
config: AppConfig;
setConfig: (config: AppConfig) => void;
}
export const useAppConfigStore = create<AppConfigState>((set) => ({
+ loading: false,
+ setLoading: (loading: boolean) => {
+ set({loading: loading})
+ },
config: new AppConfig(),
setConfig: (config: AppConfig) => {
set({config: config})
@@ -56,12 +72,22 @@ export const useProjectsStore = create<ProjectsState>((set) => ({
}))
interface ProjectState {
- project: Project;
isPushing: boolean,
isRunning: boolean,
- operation: "create" | "select" | "delete" | "none" | "copy";
+ project: Project;
setProject: (project: Project, operation: "create" | "select" | "delete"| "none" | "copy") => void;
+ operation: "create" | "select" | "delete" | "none" | "copy";
setOperation: (o: "create" | "select" | "delete"| "none" | "copy") => void;
+ memory: any,
+ setMemory: (memory: any) => void;
+ jvm: any,
+ setJvm: (jvm: any) => void;
+ context: any,
+ setContext: (context: any) => void;
+ trace: any,
+ setTrace: (trace: any) => void;
+ refreshTrace: boolean
+ setRefreshTrace: (refreshTrace: boolean) => void;
}
export const useProjectStore = create<ProjectState>((set) => ({
@@ -72,7 +98,12 @@ export const useProjectStore = create<ProjectState>((set) => ({
setProject: (project: Project, operation: "create" | "select" | "delete"| "none" | "copy") => {
set((state: ProjectState) => ({
project: project,
- operation: operation
+ operation: operation,
+ refreshTrace: false,
+ jvm: {},
+ context: {},
+ trace: {},
+ memory: {},
}));
},
setOperation: (o: "create" | "select" | "delete"| "none" | "copy") => {
@@ -80,6 +111,26 @@ export const useProjectStore = create<ProjectState>((set) => ({
operation: o
}));
},
+ memory: {},
+ setMemory: (memory: boolean) => {
+ set({memory: memory})
+ },
+ jvm: {},
+ setJvm: (jvm: boolean) => {
+ set({jvm: jvm})
+ },
+ context: {},
+ setContext: (context: boolean) => {
+ set({context: context})
+ },
+ trace: {},
+ setTrace: (trace: boolean) => {
+ set({trace: trace})
+ },
+ refreshTrace: false,
+ setRefreshTrace: (refreshTrace: boolean) => {
+ set({refreshTrace: refreshTrace})
+ },
}))
interface FilesState {
@@ -170,6 +221,8 @@ interface StatusesState {
setServices: (s: ServiceStatus[]) => void;
setContainers: (c: ContainerStatus[]) => void;
setCamels: (c: CamelStatus[]) => void;
+ pipelineStatuses: PipelineStatus[],
+ setPipelineStatuses: (pipelineStatus: PipelineStatus[]) => void;
}
export const useStatusesStore = create<StatusesState>((set) => ({
@@ -196,7 +249,11 @@ export const useStatusesStore = create<StatusesState>((set) => ({
set((state: StatusesState) => ({
camels: c,
}));
- }
+ },
+ pipelineStatuses: [],
+ setPipelineStatuses: (pipelineStatuses: PipelineStatus[]) => {
+ set({pipelineStatuses: pipelineStatuses})
+ },
}))
interface LogState {
diff --git a/karavan-web/karavan-app/src/main/webui/src/dashboard/DashboardPage.tsx b/karavan-web/karavan-app/src/main/webui/src/dashboard/DashboardPage.tsx
index 9b54deba..c032a31b 100644
--- a/karavan-web/karavan-app/src/main/webui/src/dashboard/DashboardPage.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/dashboard/DashboardPage.tsx
@@ -1,4 +1,4 @@
-import React, {useEffect, useState} from 'react';
+import React, {useState} from 'react';
import {
Badge, Bullseye,
Button, EmptyState, EmptyStateIcon, EmptyStateVariant,
@@ -26,11 +26,9 @@ import {
Table
} from '@patternfly/react-table/deprecated';
import {camelIcon, CamelUi} from "../designer/utils/CamelUi";
-import {KaravanApi} from "../api/KaravanApi";
import Icon from "../Logo";
import UpIcon from "@patternfly/react-icons/dist/esm/icons/check-circle-icon";
import DownIcon from "@patternfly/react-icons/dist/esm/icons/error-circle-o-icon";
-import RefreshIcon from "@patternfly/react-icons/dist/esm/icons/sync-alt-icon";
import SearchIcon from "@patternfly/react-icons/dist/esm/icons/search-icon";
import {MainToolbar} from "../designer/MainToolbar";
import {useAppConfigStore, useProjectsStore, useStatusesStore} from "../api/ProjectStore";
@@ -44,51 +42,8 @@ export const DashboardPage = () => {
= useStatusesStore((state) => [state.deployments, state.services, state.containers, state.camels,
state.setDeployments, state.setServices, state.setContainers, state.setCamels], shallow);
const [filter, setFilter] = useState<string>('');
- const [loading, setLoading] = useState<boolean>(true);
const [selectedEnv, setSelectedEnv] = useState<string[]>([config.environment]);
- useEffect(() => {
- const interval = setInterval(() => {
- onGetProjects()
- }, 1300);
- return () => {
- clearInterval(interval)
- };
- }, []);
-
- function onGetProjects() {
- KaravanApi.getConfiguration((config: any) => {
- KaravanApi.getProjects((projects: Project[]) => {
- setProjects(projects);
- });
- KaravanApi.getAllDeploymentStatuses((statuses: DeploymentStatus[]) => {
- setDeployments(statuses);
- });
- KaravanApi.getAllServiceStatuses((statuses: ServiceStatus[]) => {
- setServices(statuses);
- });
- KaravanApi.getAllContainerStatuses((statuses: ContainerStatus[]) => {
- setContainers(statuses);
- });
- selectedEnv.forEach(env => {
- KaravanApi.getAllCamelStatuses(env, (statuses: CamelStatus[]) => {
- setCamels(statuses);
- setLoading(false);
- // setState((state) => {
- // statuses.forEach(newStatus => {
- // const index = state.camelStatuses.findIndex(s => s.projectId === newStatus.projectId && s.env === newStatus.env);
- // if (index !== -1) {
- // state.camelStatuses.splice(index, 1);
- // }
- // state.camelStatuses.push(newStatus);
- // })
- // return state;
- // })
- });
- });
- });
- }
-
function selectEnvironment(name: string, selected: boolean) {
if (selected && !selectedEnv.includes(name)) {
setSelectedEnv((state: string[]) => {
@@ -105,9 +60,9 @@ export const DashboardPage = () => {
function tools() {
return (<Toolbar id="toolbar-group-types">
<ToolbarContent>
- <ToolbarItem>
- <Button variant="link" icon={<RefreshIcon/>} onClick={e => onGetProjects()}/>
- </ToolbarItem>
+ {/*<ToolbarItem>*/}
+ {/* <Button variant="link" icon={<RefreshIcon/>} onClick={e => onGetProjects()}/>*/}
+ {/*</ToolbarItem>*/}
<ToolbarItem>
<ToggleGroup aria-label="Default with single selectable">
{config.environments.map(env => (
@@ -170,9 +125,9 @@ export const DashboardPage = () => {
}
function getCamelStatusByEnvironments(name: string): [string, CamelStatus | undefined] [] {
- return getSelectedEnvironments().map(e => {
+ return selectedEnv.map(e => {
const env: string = e as string;
- const status = camels.find(d => d.projectId === name && d.env === env);
+ const status = camels?.find(d => d.projectId === name && d.env === env);
return [env, status];
});
}
@@ -222,12 +177,9 @@ export const DashboardPage = () => {
<Tr>
<Td colSpan={8}>
<Bullseye>
- {loading && <Spinner className="progress-stepper" diameter="80px" aria-label="Loading..."/>}
- {!loading &&
- <EmptyState variant={EmptyStateVariant.sm}>
- <EmptyStateHeader titleText="No results found" icon={<EmptyStateIcon icon={SearchIcon}/>} headingLevel="h2" />
- </EmptyState>
- }
+ <EmptyState variant={EmptyStateVariant.sm}>
+ <EmptyStateHeader titleText="No results found" icon={<EmptyStateIcon icon={SearchIcon}/>} headingLevel="h2" />
+ </EmptyState>
</Bullseye>
</Td>
</Tr>
diff --git a/karavan-web/karavan-app/src/main/webui/src/index.css b/karavan-web/karavan-app/src/main/webui/src/index.css
index c013e3fd..d7ee69a5 100644
--- a/karavan-web/karavan-app/src/main/webui/src/index.css
+++ b/karavan-web/karavan-app/src/main/webui/src/index.css
@@ -28,7 +28,7 @@
.karavan .nav-buttons .logo {
margin-top: 16px;
- margin-bottom: 10px;
+ margin-bottom: 16px;
width: 32px;
height: 32px;
}
diff --git a/karavan-web/karavan-app/src/main/webui/src/main/DataPoller.tsx b/karavan-web/karavan-app/src/main/webui/src/main/DataPoller.tsx
deleted file mode 100644
index 3b917172..00000000
--- a/karavan-web/karavan-app/src/main/webui/src/main/DataPoller.tsx
+++ /dev/null
@@ -1,83 +0,0 @@
-import React, {useEffect} from 'react';
-
-import {KaravanApi} from "../api/KaravanApi";
-import {KameletApi} from "karavan-core/lib/api/KameletApi";
-import '../designer/karavan.css';
-import {ComponentApi} from "karavan-core/lib/api/ComponentApi";
-import {AppConfig, ContainerStatus} from "../api/ProjectModels";
-import {useAppConfigStore, useStatusesStore} from "../api/ProjectStore";
-import {InfrastructureAPI} from "../designer/utils/InfrastructureAPI";
-import {shallow} from "zustand/shallow";
-
-export const DataPoller = () => {
-
- const [setConfig] = useAppConfigStore((state) => [state.setConfig], shallow)
- const [setContainers] = useStatusesStore((state) => [state.setContainers], shallow);
-
- useEffect(() => {
- console.log("DataPoller Start");
- const interval = setInterval(() => {
- getStatuses();
- }, 1000);
- return () => {
- console.log("DataPoller End");
- clearInterval(interval);
- };
- }, []);
-
- function getStatuses() {
- if (KaravanApi.isAuthorized || KaravanApi.authType === 'public') {
- KaravanApi.getAllContainerStatuses((statuses: ContainerStatus[]) => {
- setContainers(statuses);
- });
- }
- }
-
- function getData() {
- if (KaravanApi.isAuthorized || KaravanApi.authType === 'public') {
- KaravanApi.getConfiguration((config: AppConfig) => {
- setConfig(config);
- InfrastructureAPI.infrastructure = config.infrastructure;
- });
- updateKamelets();
- updateComponents();
- // updateSupportedComponents(); // not implemented yet
- }
- }
-
- async function updateKamelets(): Promise<void> {
- await new Promise(resolve => {
- KaravanApi.getKamelets(yamls => {
- const kamelets: string[] = [];
- yamls.split("\n---\n").map(c => c.trim()).forEach(z => kamelets.push(z));
- KameletApi.saveKamelets(kamelets, true);
- })
- KaravanApi.getCustomKameletNames(names => {
- KameletApi.saveCustomKameletNames(names);
- })
- });
- }
-
- async function updateComponents(): Promise<void> {
- await new Promise(resolve => {
- KaravanApi.getComponents(code => {
- const components: [] = JSON.parse(code);
- const jsons: string[] = [];
- components.forEach(c => jsons.push(JSON.stringify(c)));
- ComponentApi.saveComponents(jsons, true);
- })
- });
- }
-
- async function updateSupportedComponents(): Promise<void> {
- await new Promise(resolve => {
- KaravanApi.getSupportedComponents(jsons => {
- ComponentApi.saveSupportedComponents(jsons);
- })
- });
- }
-
- return (
- <React.Fragment></React.Fragment>
- )
-}
\ No newline at end of file
diff --git a/karavan-web/karavan-app/src/main/webui/src/main/Main.tsx b/karavan-web/karavan-app/src/main/webui/src/main/Main.tsx
index 3cf72154..7a3fa986 100644
--- a/karavan-web/karavan-app/src/main/webui/src/main/Main.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/main/Main.tsx
@@ -1,11 +1,9 @@
-import {Routes, Route, useNavigate, Navigate} from 'react-router-dom';
-import {useLocation} from 'react-router-dom';
-import React, {useEffect, useRef, useState} from "react";
+import {Routes, Route, Navigate} from 'react-router-dom';
+import React, {useEffect, useRef} from "react";
import {KaravanApi} from "../api/KaravanApi";
import {Bullseye, Flex, FlexItem, Page, Spinner} from "@patternfly/react-core";
import Icon from "../Logo";
import {MainLogin} from "./MainLogin";
-import {Notification} from "./Notification";
import {DashboardPage} from "../dashboard/DashboardPage";
import {ProjectsPage} from "../projects/ProjectsPage";
import {ProjectPage} from "../project/ProjectPage";
@@ -13,12 +11,14 @@ import {ServicesPage} from "../services/ServicesPage";
import {ContainersPage} from "../containers/ContainersPage";
import {KnowledgebasePage} from "../knowledgebase/KnowledgebasePage";
import {ProjectEventBus} from "../api/ProjectEventBus";
-import {Project, ToastMessage} from "../api/ProjectModels";
+import {ToastMessage} from "../api/ProjectModels";
import {SsoApi} from "../api/SsoApi";
-import {useAppConfigStore, useStatusesStore} from "../api/ProjectStore";
+import {useAppConfigStore} from "../api/ProjectStore";
import {shallow} from "zustand/shallow";
import {PageNavigation} from "./PageNavigation";
+import {Notification} from "./Notification";
import {useMainHook} from "./useMainHook";
+import {MainDataPoller} from "./MainDataPoller";
export const Main = () => {
@@ -36,9 +36,6 @@ export const Main = () => {
function effect() {
console.log("Main Start");
- const interval = setInterval(() => {
- getStatuses();
- }, 1000);
KaravanApi.getAuthType((authType: string) => {
console.log("authType", authType);
if (authType === 'oidc') {
@@ -52,19 +49,9 @@ export const Main = () => {
});
return () => {
console.log("Main End");
- clearInterval(interval);
};
}
- function onLogin(username: string, password: string) {
- KaravanApi.auth(username, password, (res: any) => {
- if (res?.status === 200) {
- getData();
- } else {
- toast("Error", "Incorrect username and/or password!", "danger");
- }
- });
- }
function toast(title: string, text: string, variant: 'success' | 'danger' | 'warning' | 'info' | 'custom') {
ProjectEventBus.sendAlert(new ToastMessage(title, text, variant))
@@ -97,8 +84,9 @@ export const Main = () => {
</Flex>
}
{!KaravanApi.isAuthorized && KaravanApi.authType === 'basic' &&
- <MainLogin config={config} onLogin={onLogin}/>}
- {/*<Notification/>*/}
+ <MainLogin/>}
+ <Notification/>
+ <MainDataPoller/>
</Page>
);
};
diff --git a/karavan-web/karavan-app/src/main/webui/src/main/MainDataPoller.tsx b/karavan-web/karavan-app/src/main/webui/src/main/MainDataPoller.tsx
new file mode 100644
index 00000000..02f9301c
--- /dev/null
+++ b/karavan-web/karavan-app/src/main/webui/src/main/MainDataPoller.tsx
@@ -0,0 +1,62 @@
+import React, {useEffect, useState} from 'react';
+
+import {KaravanApi} from "../api/KaravanApi";
+import '../designer/karavan.css';
+import {
+ AppConfig,
+ CamelStatus,
+ ContainerStatus,
+ DeploymentStatus,
+ PipelineStatus,
+ Project,
+ ServiceStatus
+} from "../api/ProjectModels";
+import {useAppConfigStore, useProjectsStore, useStatusesStore} from "../api/ProjectStore";
+import {shallow} from "zustand/shallow";
+
+export const MainDataPoller = () => {
+
+ const [config, setLoading] = useAppConfigStore((state) => [state.config, state.setLoading], shallow)
+ const [projects, setProjects] = useProjectsStore((state) => [state.projects, state.setProjects], shallow)
+ const [deployments, services, containers, camels, setDeployments, setServices, setContainers, setCamels, setPipelineStatuses]
+ = useStatusesStore((s) => [s.deployments, s.services, s.containers, s.camels,
+ s.setDeployments, s.setServices, s.setContainers, s.setCamels, s.setPipelineStatuses], shallow);
+
+ useEffect(() => {
+ console.log("MainDataPoller Start");
+ const interval = setInterval(() => {
+ getData();
+ }, 1300);
+ return () => {
+ console.log("MainDataPoller Stop");
+ clearInterval(interval);
+ };
+ }, []);
+
+ function getData() {
+ setLoading(true);
+ KaravanApi.getConfiguration((config: AppConfig) => {
+ KaravanApi.getProjects((projects: Project[]) => {
+ setProjects(projects);
+ });
+ KaravanApi.getAllDeploymentStatuses((statuses: DeploymentStatus[]) => {
+ setDeployments(statuses);
+ });
+ KaravanApi.getAllServiceStatuses((statuses: ServiceStatus[]) => {
+ setServices(statuses);
+ });
+ KaravanApi.getAllContainerStatuses((statuses: ContainerStatus[]) => {
+ setContainers(statuses);
+ });
+ KaravanApi.getAllCamelStatuses(config.environment, (statuses: CamelStatus[]) => {
+ setCamels(statuses);
+ });
+ KaravanApi.getPipelineStatuses(config.environment, (status: PipelineStatus[]) => {
+ setPipelineStatuses(status);
+ });
+ setLoading(false);
+ });
+ }
+
+ return (<></>)
+}
\ No newline at end of file
diff --git a/karavan-web/karavan-app/src/main/webui/src/main/MainLogin.tsx b/karavan-web/karavan-app/src/main/webui/src/main/MainLogin.tsx
index 40426253..a493a963 100644
--- a/karavan-web/karavan-app/src/main/webui/src/main/MainLogin.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/main/MainLogin.tsx
@@ -1,61 +1,63 @@
-import React from 'react';
+import React, {useState} from 'react';
import {
Bullseye, Card, CardBody, CardTitle, LoginForm, Text
} from '@patternfly/react-core';
+import {KaravanApi} from "../api/KaravanApi";
+import {useAppConfigStore} from "../api/ProjectStore";
+import {shallow} from "zustand/shallow";
+import {ProjectEventBus} from "../api/ProjectEventBus";
+import {ToastMessage} from "../api/ProjectModels";
-interface Props {
- config: any,
- onLogin: (username: string, password: string) => void
-}
+export const MainLogin = () => {
-interface State {
- username: string,
- password: string,
- isValidUsername: boolean,
- isValidPassword: boolean,
- isRememberMeChecked: boolean,
-}
+ const [config] = useAppConfigStore((state) => [state.config], shallow)
+ const [username, setUsername] = useState<string>();
+ const [password, setPassword] = useState<string>();
+ const [isValidUsername, setIsValidUsername] = useState<boolean>(true);
+ const [isValidPassword, setIsValidPassword] = useState<boolean>(true);
+ const [isRememberMeChecked, setIsRememberMeChecked] = useState<boolean>(false);
-export class MainLogin extends React.Component<Props, State> {
- public state: State = {
- username: "",
- password: "",
- isValidUsername: true,
- isValidPassword: true,
- isRememberMeChecked: false,
- }
-
- onLoginButtonClick = (event: any) => {
+ function onLoginButtonClick(event: any) {
event.preventDefault();
- this.props.onLogin?.call(this, this.state.username, this.state.password);
+ if (username && password) {
+ onLogin(username, password);
+ }
}
- render() {
- return (
- <Bullseye>
- <Card isFlat isCompact>
- <CardTitle>
- <img alt="karavan-logo" src="karavan-logo-light.png" className="login-logo"/>
- <Text component="h3" style={{width:"fit-content", marginLeft:"auto"}}>{this.props.config.version}</Text>
- </CardTitle>
- <CardBody>
- <LoginForm
- showHelperText={true}
- usernameLabel="Username"
- usernameValue={this.state.username}
- onChangeUsername={(_event, value) => this.setState({username: value})}
- isValidUsername={this.state.isValidUsername}
- passwordLabel="Password"
- passwordValue={this.state.password}
- isShowPasswordEnabled
- onChangePassword={(_event, value) => this.setState({password: value})}
- isValidPassword={this.state.isValidPassword}
- onLoginButtonClick={this.onLoginButtonClick}
- loginButtonLabel="Log in"
- />
- </CardBody>
- </Card>
- </Bullseye>
- );
+ function onLogin(username: string, password: string) {
+ KaravanApi.auth(username, password, (res: any) => {
+ if (res?.status === 200) {
+ } else {
+ ProjectEventBus.sendAlert(new ToastMessage("Error", "Incorrect username and/or password!", "danger"))
+ }
+ });
}
+
+ return (
+ <Bullseye>
+ <Card isFlat isCompact>
+ <CardTitle>
+ <img alt="karavan-logo" src="karavan-logo-light.png" className="login-logo"/>
+ <Text component="h3"
+ style={{width: "fit-content", marginLeft: "auto"}}>{config.version}</Text>
+ </CardTitle>
+ <CardBody>
+ <LoginForm
+ showHelperText={true}
+ usernameLabel="Username"
+ usernameValue={username}
+ onChangeUsername={(_event, value) => setUsername(value)}
+ isValidUsername={isValidUsername}
+ passwordLabel="Password"
+ passwordValue={password}
+ isShowPasswordEnabled
+ onChangePassword={(_event, value) => setPassword(value)}
+ isValidPassword={isValidPassword}
+ onLoginButtonClick={onLoginButtonClick}
+ loginButtonLabel="Log in"
+ />
+ </CardBody>
+ </Card>
+ </Bullseye>
+ );
}
\ No newline at end of file
diff --git a/karavan-web/karavan-app/src/main/webui/src/main/PageNavigation.tsx b/karavan-web/karavan-app/src/main/webui/src/main/PageNavigation.tsx
index 648add20..2c35c44b 100644
--- a/karavan-web/karavan-app/src/main/webui/src/main/PageNavigation.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/main/PageNavigation.tsx
@@ -4,7 +4,7 @@ import {
Flex,
FlexItem,
Tooltip,
- Divider, Popover, Badge
+ Divider, Popover, Badge, Spinner, Bullseye
} from '@patternfly/react-core';
import {KaravanApi} from "../api/KaravanApi";
import '../designer/karavan.css';
@@ -33,7 +33,7 @@ class MenuItem {
export const PageNavigation = () => {
- const [config] = useAppConfigStore((state) => [state.config], shallow)
+ const [config, loading] = useAppConfigStore((state) => [state.config, state.loading], shallow)
const [setFile] = useFileStore((state) => [state.setFile], shallow)
const [setStatus, setPodName] = useDevModeStore((state) => [state.setStatus, state.setPodName], shallow)
const [showUser, setShowUser] = useState<boolean>(false);
@@ -58,10 +58,14 @@ export const PageNavigation = () => {
return (<Flex className="nav-buttons" direction={{default: "column"}} style={{height: "100%"}}
spaceItems={{default: "spaceItemsNone"}}>
<FlexItem alignSelf={{default: "alignSelfCenter"}}>
- <Tooltip className="logo-tooltip" content={"Apache Camel Karavan " + config.version}
- position={"right"}>
- {Icon()}
- </Tooltip>
+ <Bullseye>
+ {loading && <Spinner style={{position: "absolute"}} diameter="40px" aria-label="Loading..."/>}
+ <Tooltip className="logo-tooltip" content={"Apache Camel Karavan " + config.version}
+ position={"right"}>
+ {Icon()}
+ </Tooltip>
+ </Bullseye>
+
</FlexItem>
{getMenu().map(page =>
<FlexItem key={page.pageId} className={pageId === page.pageId ? "nav-button-selected" : ""}>
diff --git a/karavan-web/karavan-app/src/main/webui/src/project/ProjectDataPoller.tsx b/karavan-web/karavan-app/src/main/webui/src/project/ProjectDataPoller.tsx
new file mode 100644
index 00000000..2b86d831
--- /dev/null
+++ b/karavan-web/karavan-app/src/main/webui/src/project/ProjectDataPoller.tsx
@@ -0,0 +1,61 @@
+import React, {useEffect} from 'react';
+
+import {KaravanApi} from "../api/KaravanApi";
+import '../designer/karavan.css';
+import {useAppConfigStore, useProjectStore} from "../api/ProjectStore";
+import {shallow} from "zustand/shallow";
+import {CamelStatus, DeploymentStatus, PipelineStatus} from "../api/ProjectModels";
+
+export const ProjectDataPoller = () => {
+
+ const [config] = useAppConfigStore((state) => [state.config], shallow)
+ const [project, setMemory, setJvm, setContext, refreshTrace, setTrace] = useProjectStore((s) =>
+ [s.project, s.setMemory, s.setJvm, s.setContext, s.refreshTrace, s.setTrace], shallow);
+
+ useEffect(() => {
+ console.log("ProjectDataPoller Start");
+ const interval = setInterval(() => {
+ onRefreshStatus();
+ }, 1000);
+ return () => {
+ console.log("ProjectDataPoller Stop");
+ clearInterval(interval)
+ };
+ }, [project, refreshTrace]);
+
+ function onRefreshStatus() {
+ const projectId = project.projectId;
+ KaravanApi.getDevModeStatus(projectId, "memory", res => {
+ if (res.status === 200) {
+ setMemory(JSON.parse(res.data.status));
+ } else {
+ setMemory({});
+ }
+ })
+ KaravanApi.getDevModeStatus(projectId, "jvm", res => {
+ if (res.status === 200) {
+ setJvm(JSON.parse(res.data.status));
+ } else {
+ setJvm({});
+ }
+ })
+ KaravanApi.getDevModeStatus(projectId, "context", res => {
+ if (res.status === 200) {
+ setContext(JSON.parse(res.data.status));
+ } else {
+ setContext({});
+ }
+ })
+ if (refreshTrace) {
+ KaravanApi.getDevModeStatus(projectId, "trace", res => {
+ if (res.status === 200) {
+ setTrace(JSON.parse(res.data.status));
+ } else {
+ setTrace({});
+ }
+ })
+ }
+ }
+
+ return (<></>)
+}
\ No newline at end of file
diff --git a/karavan-web/karavan-app/src/main/webui/src/project/ProjectPage.tsx b/karavan-web/karavan-app/src/main/webui/src/project/ProjectPage.tsx
index cb6bb96a..a771570c 100644
--- a/karavan-web/karavan-app/src/main/webui/src/project/ProjectPage.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/project/ProjectPage.tsx
@@ -14,6 +14,7 @@ import {FileEditor} from "./file/FileEditor";
import {shallow} from "zustand/shallow";
import {useParams} from "react-router-dom";
import {KaravanApi} from "../api/KaravanApi";
+import {ProjectDataPoller} from "./ProjectDataPoller";
export const ProjectPage = () => {
@@ -53,6 +54,7 @@ export const ProjectPage = () => {
{showFilePanel && <FileEditor/>}
{!showFilePanel && <ProjectPanel/>}
<ProjectLogPanel/>
+ <ProjectDataPoller/>
</PageSection>
)
}
diff --git a/karavan-web/karavan-app/src/main/webui/src/project/dashboard/DashboardTab.tsx b/karavan-web/karavan-app/src/main/webui/src/project/dashboard/DashboardTab.tsx
index beceb5c8..4a21f9ad 100644
--- a/karavan-web/karavan-app/src/main/webui/src/project/dashboard/DashboardTab.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/project/dashboard/DashboardTab.tsx
@@ -14,7 +14,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-import React, {useEffect, useState} from 'react';
+import React from 'react';
import {
Card,
CardBody, Flex, FlexItem, Divider, PageSection
@@ -23,55 +23,19 @@ import '../../designer/karavan.css';
import {InfoContainer} from "./InfoContainer";
import {InfoContext} from "./InfoContext";
import {InfoMemory} from "./InfoMemory";
-import {KaravanApi} from "../../api/KaravanApi";
import {useProjectStore, useStatusesStore} from "../../api/ProjectStore";
import {shallow} from "zustand/shallow";
import {ContainerStatus} from "../../api/ProjectModels";
export const DashboardTab = () => {
- const [project] = useProjectStore((state) => [state.project], shallow);
+ const [project, memory, jvm, context] = useProjectStore((state) =>
+ [state.project, state.memory, state.jvm, state.context], shallow);
const [containers] = useStatusesStore((state) => [state.containers], shallow);
- const [memory, setMemory] = useState({});
- const [jvm, setJvm] = useState({});
- const [context, setContext] = useState({});
-
- useEffect(() => {
- const interval = setInterval(() => {
- onRefreshStatus();
- }, 1000);
- return () => {
- clearInterval(interval)
- };
- }, []);
-
- function onRefreshStatus() {
- const projectId = project.projectId;
- KaravanApi.getDevModeStatus(projectId, "memory", res => {
- if (res.status === 200) {
- setMemory(JSON.parse(res.data.status));
- } else {
- setMemory({});
- }
- })
- KaravanApi.getDevModeStatus(projectId, "jvm", res => {
- if (res.status === 200) {
- setJvm(JSON.parse(res.data.status));
- } else {
- setJvm({});
- }
- })
- KaravanApi.getDevModeStatus(projectId, "context", res => {
- if (res.status === 200) {
- setContext(JSON.parse(res.data.status));
- } else {
- setContext({});
- }
- })
- }
const containerStatus = containers.filter(c => c.containerName === project.projectId).at(0);
const showConsole = containerStatus?.state === 'running'
+
return (
<PageSection className="project-tab-panel" padding={{default: "padding"}}>
<Card className="project-development">
diff --git a/karavan-web/karavan-app/src/main/webui/src/project/files/FilesToolbar.tsx b/karavan-web/karavan-app/src/main/webui/src/project/files/FilesToolbar.tsx
index da56443a..a5ee8418 100644
--- a/karavan-web/karavan-app/src/main/webui/src/project/files/FilesToolbar.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/project/files/FilesToolbar.tsx
@@ -39,7 +39,6 @@ export const FileToolbar = () => {
useEffect(() => {
- console.log("ProjectToolbar useEffect", isPushing, project.lastCommitTimestamp);
}, [project, file]);
function push () {
diff --git a/karavan-web/karavan-app/src/main/webui/src/project/log/ProjectLogPanel.tsx b/karavan-web/karavan-app/src/main/webui/src/project/log/ProjectLogPanel.tsx
index ba5e4d7e..c7eec061 100644
--- a/karavan-web/karavan-app/src/main/webui/src/project/log/ProjectLogPanel.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/project/log/ProjectLogPanel.tsx
@@ -25,8 +25,6 @@ export const ProjectLogPanel = () => {
const [currentPodName, setCurrentPodName] = useState<string | undefined>(undefined);
useEffect(() => {
- const containerStatus = containers.filter(c => c.containerName === podName).at(0);
- console.log("ProjectLogPanel", showLog, type, podName, containerStatus);
const controller = new AbortController();
if (showLog && type !== 'none' && podName !== undefined) {
const f = KaravanApi.fetchData(type, podName, controller).then(value => {
diff --git a/karavan-web/karavan-app/src/main/webui/src/project/pipeline/ProjectPipelineTab.tsx b/karavan-web/karavan-app/src/main/webui/src/project/pipeline/ProjectPipelineTab.tsx
index e2209f89..83b8b990 100644
--- a/karavan-web/karavan-app/src/main/webui/src/project/pipeline/ProjectPipelineTab.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/project/pipeline/ProjectPipelineTab.tsx
@@ -14,8 +14,8 @@ export const ProjectPipelineTab = () => {
<PageSection className="project-tab-panel" padding={{default: "padding"}}>
<div className="project-operations">
{/*{["dev", "test", "prod"].map(env =>*/}
- {["dev"].map(env =>
- <ProjectStatus key={env} project={project} config={config} env={env}/>
+ {config.environments.map(env =>
+ <ProjectStatus env={env}/>
)}
</div>
</PageSection>
diff --git a/karavan-web/karavan-app/src/main/webui/src/project/pipeline/ProjectStatus.tsx b/karavan-web/karavan-app/src/main/webui/src/project/pipeline/ProjectStatus.tsx
index ae022589..698621b8 100644
--- a/karavan-web/karavan-app/src/main/webui/src/project/pipeline/ProjectStatus.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/project/pipeline/ProjectStatus.tsx
@@ -1,4 +1,4 @@
-import React from 'react';
+import React, {useState} from 'react';
import {
Button,
DescriptionList,
@@ -14,116 +14,76 @@ import UpIcon from "@patternfly/react-icons/dist/esm/icons/check-circle-icon";
import DownIcon from "@patternfly/react-icons/dist/esm/icons/error-circle-o-icon";
import ClockIcon from "@patternfly/react-icons/dist/esm/icons/clock-icon";
import DeleteIcon from "@patternfly/react-icons/dist/esm/icons/times-circle-icon";
-import {CamelStatus, DeploymentStatus, PipelineStatus, ContainerStatus, Project} from "../../api/ProjectModels";
-import {useLogStore} from "../../api/ProjectStore";
+import {CamelStatus, DeploymentStatus, ContainerStatus} from "../../api/ProjectModels";
+import {useLogStore, useProjectStore, useStatusesStore} from "../../api/ProjectStore";
+import {shallow} from "zustand/shallow";
interface Props {
- project: Project,
- config: any,
env: string,
}
-interface State {
- pipelineStatus?: PipelineStatus,
- deploymentStatus?: DeploymentStatus,
- podStatuses: ContainerStatus[],
- camelStatus?: CamelStatus,
- isPushing: boolean,
- isBuilding: boolean,
- isRolling: boolean,
- showDeleteConfirmation: boolean,
- deleteEntity?: 'pod' | 'deployment' | 'pipelinerun',
- deleteEntityName?: string,
- deleteEntityEnv?: string,
-}
-
-export class ProjectStatus extends React.Component<Props, State> {
-
- public state: State = {
- podStatuses: [],
- isPushing: false,
- isBuilding: false,
- isRolling: false,
- showDeleteConfirmation: false,
- };
- interval: any;
-
- componentDidMount() {
- this.interval = setInterval(() => this.onRefreshStatus(), 700);
- }
-
- componentWillUnmount() {
- clearInterval(this.interval);
- }
+export const ProjectStatus = (props: Props) => {
- onRefreshStatus = () => {
- const projectId = this.props.project.projectId;
- const env = this.props.env;
- if (this.props.project) {
- KaravanApi.getProjectPipelineStatus(projectId, env, (status?: PipelineStatus) => {
- this.setState({pipelineStatus: status});
- });
- KaravanApi.getProjectDeploymentStatus(projectId, env, (status?: DeploymentStatus) => {
- this.setState({deploymentStatus: status});
- });
- KaravanApi.getProjectPodStatuses(projectId, env, (statuses: ContainerStatus[]) => {
- this.setState({podStatuses: statuses});
- });
- KaravanApi.getProjectCamelStatus(projectId, env, (status: CamelStatus) => {
- this.setState({camelStatus: status});
- });
- }
- }
+ const [project] = useProjectStore((s) => [s.project], shallow);
+ const [containers, deployments, camels, pipelineStatuses] =
+ useStatusesStore((s) => [s.containers, s.deployments, s.camels, s.pipelineStatuses], shallow);
+ const [isPushing, setIsPushing] = useState<boolean>(false);
+ const [isBuilding, setIsBuilding] = useState<boolean>(false);
+ const [isRolling, setIsRolling] = useState<boolean>(false);
+ const [showDeleteConfirmation, setShowDeleteConfirmation] = useState<boolean>(false);
+ const [deleteEntityType, setDeleteEntityType] = useState<'pod' | 'deployment' | 'pipelinerun'>('pod');
+ const [deleteEntityName, setDeleteEntityName] = useState<string>();
+ const [deleteEntityEnv, setDeleteEntityEnv] = useState<string>();
- deleteEntity = (type: 'pod' | 'deployment' | 'pipelinerun', name: string, environment: string) => {
+ function deleteEntity (type: 'pod' | 'deployment' | 'pipelinerun', name: string, environment: string) {
switch (type) {
case "deployment":
KaravanApi.deleteDeployment(environment, name, (res: any) => {
// if (Array.isArray(res) && Array.from(res).length > 0)
- // this.onRefresh();
+ // onRefresh();
});
break;
case "pod":
KaravanApi.deleteContainer(environment, 'project', name, (res: any) => {
// if (Array.isArray(res) && Array.from(res).length > 0)
- // this.onRefresh();
+ // onRefresh();
});
break;
case "pipelinerun":
KaravanApi.stopPipelineRun(environment, name, (res: any) => {
// if (Array.isArray(res) && Array.from(res).length > 0)
- // this.onRefresh();
+ // onRefresh();
});
break;
}
}
- build = () => {
- this.setState({isBuilding: true});
- KaravanApi.pipelineRun(this.props.project, this.props.env, res => {
+ function build () {
+ setIsBuilding(true);
+ KaravanApi.pipelineRun(project, env, res => {
if (res.status === 200 || res.status === 201) {
- this.setState({isBuilding: false});
+ setIsBuilding(false);
} else {
// Todo notification
}
});
}
- rollout = () => {
- this.setState({isRolling: true});
- KaravanApi.rolloutDeployment(this.props.project.projectId, this.props.env, res => {
+ function rollout () {
+ setIsRolling(true);
+ KaravanApi.rolloutDeployment(project.projectId, env, res => {
console.log(res)
if (res.status === 200 || res.status === 201) {
- this.setState({isRolling: false});
+ setIsRolling(false);
} else {
// Todo notification
}
});
}
- buildButton = (env: string) => {
- const {isBuilding, isPushing, pipelineStatus} = this.state;
- const isRunning = pipelineStatus?.result === 'Running';
+ function buildButton (env: string) {
+ const status = pipelineStatuses.filter(p => p.projectId === project.projectId).at(0);
+ const isRunning = status?.result === 'Running';
return (<Tooltip content="Start build pipeline" position={"left"}>
<Button isLoading={isBuilding ? true : undefined}
isDisabled={isBuilding || isRunning || isPushing}
@@ -131,41 +91,41 @@ export class ProjectStatus extends React.Component<Props, State> {
variant="secondary"
className="project-button"
icon={!isBuilding ? <BuildIcon/> : <div></div>}
- onClick={e => this.build()}>
+ onClick={e => build()}>
{isBuilding ? "..." : "Build"}
</Button>
</Tooltip>)
}
- rolloutButton = () => {
- const isRolling = this.state.isRolling;
+ function rolloutButton () {
return (<Tooltip content="Rollout deployment" position={"left"}>
<Button isLoading={isRolling ? true : undefined} size="sm" variant="secondary"
className="project-button"
icon={!isRolling ? <RolloutIcon/> : <div></div>}
- onClick={e => this.rollout()}>
+ onClick={e => rollout()}>
{isRolling ? "..." : "Rollout"}
</Button>
</Tooltip>)
}
- deleteDeploymentButton = (env: string) => {
+ function deleteDeploymentButton (env: string) {
return (<Tooltip content="Delete deployment" position={"left"}>
<Button size="sm" variant="secondary"
className="project-button"
icon={<DeleteIcon/>}
- onClick={e => this.setState({
- showDeleteConfirmation: true,
- deleteEntity: "deployment",
- deleteEntityEnv: env,
- deleteEntityName: this.props.project?.projectId
- })}>
+ onClick={e => {
+ setShowDeleteConfirmation(true);
+ setDeleteEntityType("deployment");
+ setDeleteEntityEnv(env);
+ setDeleteEntityName(project?.projectId);
+ }}>
{"Delete"}
</Button>
</Tooltip>)
}
- getReplicasPanel(env: string, deploymentStatus?: DeploymentStatus) {
+ function getReplicasPanel(env: string) {
+ const deploymentStatus = deployments.find(d => d.name === project?.projectId);
const ok = (deploymentStatus && deploymentStatus?.readyReplicas > 0
&& (deploymentStatus.unavailableReplicas === 0 || deploymentStatus.unavailableReplicas === undefined || deploymentStatus.unavailableReplicas === null)
&& deploymentStatus?.replicas === deploymentStatus?.readyReplicas)
@@ -185,19 +145,20 @@ export class ProjectStatus extends React.Component<Props, State> {
</LabelGroup>}
{deploymentStatus === undefined && <Label icon={<DownIcon/>} color={"grey"}>No deployments</Label>}
</FlexItem>
- <FlexItem>{env === "dev" && this.deleteDeploymentButton(env)}</FlexItem>
+ <FlexItem>{env === "dev" && deleteDeploymentButton(env)}</FlexItem>
</Flex>
)
}
- getPodsPanel(env: string, podStatuses: ContainerStatus[]) {
+ function getPodsPanel(env: string) {
+ const podStatuses = containers.filter(d => d.projectId === project?.projectId);
return (
<Flex justifyContent={{default: "justifyContentSpaceBetween"}}
alignItems={{default: "alignItemsFlexStart"}}>
<FlexItem>
{podStatuses.length === 0 && <Label icon={<DownIcon/>} color={"grey"}>No pods</Label>}
<LabelGroup numLabels={2} isVertical>
- {podStatuses.map(pod => {
+ {podStatuses.map((pod: ContainerStatus) => {
const ready = pod.state === 'running';
return (
<Tooltip key={pod.containerName} content={pod.state}>
@@ -213,12 +174,12 @@ export class ProjectStatus extends React.Component<Props, State> {
{pod.containerName}
</Button>
<Tooltip content={"Delete Pod"}>
- <Button icon={<DeleteIcon/>} variant="link" onClick={e => this.setState({
- showDeleteConfirmation: true,
- deleteEntity: "pod",
- deleteEntityEnv: env,
- deleteEntityName: pod.containerName
- })}></Button>
+ <Button icon={<DeleteIcon/>} variant="link" onClick={e => {
+ setShowDeleteConfirmation(true);
+ setDeleteEntityType("pod");
+ setDeleteEntityEnv(env);
+ setDeleteEntityName(pod.containerName);
+ }}></Button>
</Tooltip>
</Label>
</Tooltip>
@@ -227,23 +188,23 @@ export class ProjectStatus extends React.Component<Props, State> {
)}
</LabelGroup>
</FlexItem>
- <FlexItem>{env === "dev" && this.rolloutButton()}</FlexItem>
+ <FlexItem>{env === "dev" && rolloutButton()}</FlexItem>
</Flex>
)
}
- getStatusColor(status?: string) {
+ function getStatusColor(status?: string) {
if (status === 'UP') return 'green';
if (status === 'DOWN') return 'red';
if (status === 'UNDEFINED') return 'grey';
}
- getStatusIcon(status?: string) {
+ function getStatusIcon(status?: string) {
return (status === 'UP' ? <UpIcon/> : <DownIcon/>)
}
- getHealthPanel(env: string) {
- const status = this.state.camelStatus;
+ function getHealthPanel(env: string) {
+ const status = camels;
// const routesStatus = status?.routesStatus;
// const consumersStatus = status?.consumerStatus;
// const contextStatus = status?.contextStatus;
@@ -251,19 +212,19 @@ export class ProjectStatus extends React.Component<Props, State> {
return (
<LabelGroup numLabels={4}>
{/*{contextVersion &&*/}
- {/* <Label icon={this.getStatusIcon(contextStatus)}*/}
- {/* color={this.getStatusColor(contextStatus)}>{contextVersion}</Label>}*/}
- {/*<Label icon={this.getStatusIcon(contextStatus)}*/}
- {/* color={this.getStatusColor(contextStatus)}>Context</Label>*/}
- {/*<Label icon={this.getStatusIcon(consumersStatus)}*/}
- {/* color={this.getStatusColor(consumersStatus)}>Consumers</Label>*/}
- {/*<Label icon={this.getStatusIcon(routesStatus)} color={this.getStatusColor(routesStatus)}>Routes</Label>*/}
+ {/* <Label icon={getStatusIcon(contextStatus)}*/}
+ {/* color={getStatusColor(contextStatus)}>{contextVersion}</Label>}*/}
+ {/*<Label icon={getStatusIcon(contextStatus)}*/}
+ {/* color={getStatusColor(contextStatus)}>Context</Label>*/}
+ {/*<Label icon={getStatusIcon(consumersStatus)}*/}
+ {/* color={getStatusColor(consumersStatus)}>Consumers</Label>*/}
+ {/*<Label icon={getStatusIcon(routesStatus)} color={getStatusColor(routesStatus)}>Routes</Label>*/}
</LabelGroup>
)
}
- getPipelineState(env: string) {
- const status = this.state.pipelineStatus;
+ function getPipelineState(env: string) {
+ const status = pipelineStatuses.filter(p => p.projectId === project.projectId).at(0);
const pipeline = status?.pipelineName;
const pipelineResult = status?.result;
let lastPipelineRunTime = 0;
@@ -292,14 +253,12 @@ export class ProjectStatus extends React.Component<Props, State> {
</Button>
: "No pipeline"}
{isRunning && <Tooltip content={"Stop PipelineRun"}>
- <Button icon={<DeleteIcon/>} variant="link" onClick={e =>
- this.setState({
- showDeleteConfirmation: true,
- deleteEntity: "pipelinerun",
- deleteEntityEnv: env,
- deleteEntityName: pipeline
- })
- }></Button>
+ <Button icon={<DeleteIcon/>} variant="link" onClick={e => {
+ setShowDeleteConfirmation(true);
+ setDeleteEntityType("pipelinerun");
+ setDeleteEntityEnv(env);
+ setDeleteEntityName(pipeline);
+ }}></Button>
</Tooltip>}
</Label>
{pipeline !== undefined && showTime === true && lastPipelineRunTime !== undefined &&
@@ -307,75 +266,71 @@ export class ProjectStatus extends React.Component<Props, State> {
</LabelGroup>
</Tooltip>
</FlexItem>
- <FlexItem>{env === "dev" && this.buildButton(env)}</FlexItem>
+ <FlexItem>{env === "dev" && buildButton(env)}</FlexItem>
</Flex>
)
}
- getDeleteConfirmation() {
- const {deleteEntity, deleteEntityEnv, deleteEntityName} = this.state;
+ function getDeleteConfirmation() {
return (<Modal
className="modal-delete"
title="Confirmation"
- isOpen={this.state.showDeleteConfirmation}
- onClose={() => this.setState({showDeleteConfirmation: false})}
+ isOpen={showDeleteConfirmation}
+ onClose={() => setShowDeleteConfirmation(false)}
actions={[
<Button key="confirm" variant="primary" onClick={e => {
if (deleteEntityEnv && deleteEntityName && deleteEntity) {
- this.deleteEntity(deleteEntity, deleteEntityName, deleteEntityEnv);
- this.setState({showDeleteConfirmation: false});
+ deleteEntity(deleteEntityType, deleteEntityName, deleteEntityEnv);
+ setShowDeleteConfirmation(false);
}
}}>Delete
</Button>,
<Button key="cancel" variant="link"
- onClick={e => this.setState({showDeleteConfirmation: false})}>Cancel</Button>
+ onClick={e => setShowDeleteConfirmation(false)}>Cancel</Button>
]}
- onEscapePress={e => this.setState({showDeleteConfirmation: false})}>
+ onEscapePress={e => setShowDeleteConfirmation(false)}>
<div>{"Delete " + deleteEntity + " " + deleteEntityName + "?"}</div>
</Modal>)
}
- render() {
- const {deploymentStatus, podStatuses} = this.state;
- const {env} = this.props;
- return (
- <Card className="project-status">
- <CardBody>
- <DescriptionList isHorizontal>
- <DescriptionListGroup>
- <DescriptionListTerm>Environment</DescriptionListTerm>
- <DescriptionListDescription>
- <Badge className="badge">{env}</Badge>
- </DescriptionListDescription>
- </DescriptionListGroup>
- <DescriptionListGroup>
- <DescriptionListTerm>Pipeline</DescriptionListTerm>
- <DescriptionListDescription>
- {this.getPipelineState(env)}
- </DescriptionListDescription>
- </DescriptionListGroup>
- <DescriptionListGroup>
- <DescriptionListTerm>Deployment</DescriptionListTerm>
- <DescriptionListDescription>
- {this.getReplicasPanel(env, deploymentStatus)}
- </DescriptionListDescription>
- </DescriptionListGroup>
- <DescriptionListGroup>
- <DescriptionListTerm>Pods</DescriptionListTerm>
- <DescriptionListDescription>
- {this.getPodsPanel(env, podStatuses)}
- </DescriptionListDescription>
- </DescriptionListGroup>
- <DescriptionListGroup>
- <DescriptionListTerm>Camel health</DescriptionListTerm>
- <DescriptionListDescription>
- {this.getHealthPanel(env)}
- </DescriptionListDescription>
- </DescriptionListGroup>
- </DescriptionList>
- </CardBody>
- {this.state.showDeleteConfirmation && this.getDeleteConfirmation()}
- </Card>
- )
- }
+ const env = props.env;
+ return (
+ <Card className="project-status">
+ <CardBody>
+ <DescriptionList isHorizontal>
+ <DescriptionListGroup>
+ <DescriptionListTerm>Environment</DescriptionListTerm>
+ <DescriptionListDescription>
+ <Badge className="badge">{env}</Badge>
+ </DescriptionListDescription>
+ </DescriptionListGroup>
+ <DescriptionListGroup>
+ <DescriptionListTerm>Pipeline</DescriptionListTerm>
+ <DescriptionListDescription>
+ {getPipelineState(env)}
+ </DescriptionListDescription>
+ </DescriptionListGroup>
+ <DescriptionListGroup>
+ <DescriptionListTerm>Deployment</DescriptionListTerm>
+ <DescriptionListDescription>
+ {getReplicasPanel(env)}
+ </DescriptionListDescription>
+ </DescriptionListGroup>
+ <DescriptionListGroup>
+ <DescriptionListTerm>Pods</DescriptionListTerm>
+ <DescriptionListDescription>
+ {getPodsPanel(env)}
+ </DescriptionListDescription>
+ </DescriptionListGroup>
+ <DescriptionListGroup>
+ <DescriptionListTerm>Camel health</DescriptionListTerm>
+ <DescriptionListDescription>
+ {getHealthPanel(env)}
+ </DescriptionListDescription>
+ </DescriptionListGroup>
+ </DescriptionList>
+ </CardBody>
+ {showDeleteConfirmation && getDeleteConfirmation()}
+ </Card>
+ )
}
diff --git a/karavan-web/karavan-app/src/main/webui/src/project/trace/RunnerInfoTraceModal.tsx b/karavan-web/karavan-app/src/main/webui/src/project/trace/RunnerInfoTraceModal.tsx
index 6940f8a8..d734018d 100644
--- a/karavan-web/karavan-app/src/main/webui/src/project/trace/RunnerInfoTraceModal.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/project/trace/RunnerInfoTraceModal.tsx
@@ -58,8 +58,8 @@ export const RunnerInfoTraceModal = (props: Props) => {
<DescriptionListTerm>Nodes</DescriptionListTerm>
</DescriptionListGroup>
</DescriptionList>
- {props.nodes.map((node: any) => (
- <FlexItem>
+ {props.nodes.map((node: any, index: number) => (
+ <FlexItem key={node.uid + "-" + index}>
<Button variant={node.uid === activeNode.uid ? "secondary" : "link"}
icon={node.nodeId === undefined ? <ArrowRightIcon/> : undefined}
onClick={event => {setActiveNode(node)}}>
diff --git a/karavan-web/karavan-app/src/main/webui/src/project/trace/RunnerInfoTraceNode.tsx b/karavan-web/karavan-app/src/main/webui/src/project/trace/RunnerInfoTraceNode.tsx
index 90d71de6..5a470290 100644
--- a/karavan-web/karavan-app/src/main/webui/src/project/trace/RunnerInfoTraceNode.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/project/trace/RunnerInfoTraceNode.tsx
@@ -42,8 +42,8 @@ export const RunnerInfoTraceNode = (props: Props) => {
<DescriptionListTerm>Headers</DescriptionListTerm>
</DescriptionListGroup>
<DataList aria-label="Compact data list example" isCompact>
- {headers.map((header: any) => (
- <DataListItem key={header[0]} aria-labelledby="compact-item1">
+ {headers.map((header: any, index: number) => (
+ <DataListItem key={header[0] + "-" + index} aria-labelledby="compact-item1">
<DataListItemRow>
<DataListItemCells
dataListCells={[
diff --git a/karavan-web/karavan-app/src/main/webui/src/project/trace/TraceTab.tsx b/karavan-web/karavan-app/src/main/webui/src/project/trace/TraceTab.tsx
index c62aa090..d4420f2f 100644
--- a/karavan-web/karavan-app/src/main/webui/src/project/trace/TraceTab.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/project/trace/TraceTab.tsx
@@ -14,7 +14,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-import React, {useEffect, useState} from 'react';
+import React, {useState} from 'react';
import {
Bullseye,
Button,
@@ -39,40 +39,16 @@ import {
Table
} from '@patternfly/react-table/deprecated';
import SearchIcon from "@patternfly/react-icons/dist/esm/icons/search-icon";
-import {KaravanApi} from "../../api/KaravanApi";
import {useProjectStore} from "../../api/ProjectStore";
+import {shallow} from "zustand/shallow";
export const TraceTab = () => {
- const {project} = useProjectStore();
- const [trace, setTrace] = useState<any>({});
+ const [refreshTrace, setRefreshTrace, trace] = useProjectStore((state) =>
+ [state.refreshTrace, state.setRefreshTrace, state.trace], shallow);
const [nodes, setNodes] = useState([{}]);
- const [isOpen, setIsOpen] = useState(false);
- const [refreshTrace, setRefreshTrace] = useState(false);
-
- useEffect(() => {
- const interval = setInterval(() => {
- onRefreshStatus();
- }, 1000);
- return () => {
- clearInterval(interval)
- };
- }, [refreshTrace]);
-
-
- function onRefreshStatus() {
- const projectId = project.projectId;
- if (refreshTrace) {
- KaravanApi.getDevModeStatus(projectId, "trace", res => {
- if (res.status === 200) {
- setTrace(JSON.parse(res.data.status));
- } else {
- setTrace({});
- }
- })
- }
- }
+ const [isOpen, setIsOpen] = useState<boolean>(false);
function closeModal() {
setIsOpen(false);
@@ -134,7 +110,7 @@ export const TraceTab = () => {
<Td>
<Button style={{padding: '0'}} variant={"link"}
onClick={e => {
- setTrace(trace);
+ // setTrace(trace);
setNodes(getNodes(exchangeId));
setIsOpen(true);
}}>
diff --git a/karavan-web/karavan-app/src/main/webui/src/projects/ProjectsPage.tsx b/karavan-web/karavan-app/src/main/webui/src/projects/ProjectsPage.tsx
index 64c5fd40..ef49c8aa 100644
--- a/karavan-web/karavan-app/src/main/webui/src/projects/ProjectsPage.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/projects/ProjectsPage.tsx
@@ -49,36 +49,10 @@ export const ProjectsPage = () => {
const [projects] = useProjectsStore((state) => [state.projects], shallow)
const [operation] = useProjectStore((state) => [state.operation], shallow)
const [filter, setFilter] = useState<string>('');
- const [loading, setLoading] = useState<boolean>(false);
-
- useEffect(() => {
- const interval = setInterval(() => {
- if (projects.length === 0) setLoading(true);
- if (!["create", "delete", "select", "copy"].includes(operation)) {
- refresh();
- }
- }, 1300);
- return () => {
- clearInterval(interval)
- };
- }, [operation]);
-
- function refresh() {
- ProjectService.refreshProjects();
- ProjectService.refreshAllDeploymentStatuses();
- ProjectService.refreshAllContainerStatuses();
- setLoading(false);
- }
function getTools() {
return <Toolbar id="toolbar-group-types">
<ToolbarContent>
- <ToolbarItem>
- <Button variant="link" icon={<RefreshIcon/>} onClick={e => {
- setLoading(true);
- refresh();
- }}/>
- </ToolbarItem>
<ToolbarItem>
<TextInput className="text-field" type="search" id="search" name="search"
autoComplete="off" placeholder="Search by name"
@@ -106,14 +80,10 @@ export const ProjectsPage = () => {
<Tr>
<Td colSpan={8}>
<Bullseye>
- {loading &&
- <Spinner className="progress-stepper" diameter="80px" aria-label="Loading..."/>}
- {!loading &&
- <EmptyState variant={EmptyStateVariant.sm}>
- <EmptyStateHeader titleText="No results found"
- icon={<EmptyStateIcon icon={SearchIcon}/>} headingLevel="h2"/>
- </EmptyState>
- }
+ <EmptyState variant={EmptyStateVariant.sm}>
+ <EmptyStateHeader titleText="No results found"
+ icon={<EmptyStateIcon icon={SearchIcon}/>} headingLevel="h2"/>
+ </EmptyState>
</Bullseye>
</Td>
</Tr>
diff --git a/karavan-web/karavan-infinispan/src/main/java/org/apache/camel/karavan/infinispan/InfinispanService.java b/karavan-web/karavan-infinispan/src/main/java/org/apache/camel/karavan/infinispan/InfinispanService.java
index 934f3042..e03e9c12 100644
--- a/karavan-web/karavan-infinispan/src/main/java/org/apache/camel/karavan/infinispan/InfinispanService.java
+++ b/karavan-web/karavan-infinispan/src/main/java/org/apache/camel/karavan/infinispan/InfinispanService.java
@@ -210,6 +210,13 @@ public class InfinispanService {
return pipelineStatuses.get(GroupedKey.create(projectId, environment, projectId));
}
+ public List<PipelineStatus> getPipelineStatuses(String environment) {
+ QueryFactory queryFactory = Search.getQueryFactory((RemoteCache<?, ?>) pipelineStatuses);
+ return queryFactory.<PipelineStatus>create("FROM karavan.PipelineStatus WHERE env = :env")
+ .setParameter("env", environment)
+ .execute().list();
+ }
+
public void savePipelineStatus(PipelineStatus status) {
pipelineStatuses.put(GroupedKey.create(status.getProjectId(), status.getEnv(), status.getProjectId()), status);
}