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 024e8723a8b241e9cb1eb09ff17a581913a05b01 Author: Marat Gubaidullin <ma...@talismancloud.io> AuthorDate: Sat Feb 10 18:13:42 2024 -0500 Fix #564 --- .../apache/camel/karavan/api/ImagesResource.java | 9 +-- .../camel/karavan/api/NotificationResource.java | 50 ++++++++------ .../camel/karavan/api/ProjectGitResource.java | 13 +++- .../camel/karavan/service/KaravanService.java | 3 - .../camel/karavan/service/NotificationService.java | 50 ++++++++++++++ .../camel/karavan/service/ProjectService.java | 32 +++++++-- .../org/apache/camel/karavan/shared/Constants.java | 7 ++ .../exception/ExceptionToResponseMapper.java | 66 ------------------- .../src/main/webui/src/api/KaravanApi.tsx | 19 +++++- .../src/main/webui/src/api/NotificationApi.tsx | 77 +++++++++++++++------- .../src/main/webui/src/api/NotificationService.ts | 57 ++++++++++++++++ .../src/main/webui/src/api/ProjectEventBus.ts | 34 +--------- .../src/main/webui/src/api/ProjectService.ts | 9 ++- .../karavan-app/src/main/webui/src/main/Main.tsx | 22 +++++-- .../main/webui/src/project/files/FilesToolbar.tsx | 16 ++++- .../main/webui/src/project/log/ProjectLogPanel.tsx | 2 - 16 files changed, 286 insertions(+), 180 deletions(-) diff --git a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/api/ImagesResource.java b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/api/ImagesResource.java index 6bbe60f5..0170918b 100644 --- a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/api/ImagesResource.java +++ b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/api/ImagesResource.java @@ -65,11 +65,8 @@ public class ImagesResource { @Path("/{projectId}") public Response build(JsonObject data, @PathParam("projectId") String projectId) throws Exception { try { - String imageName = data.getString("imageName"); - boolean commit = data.getBoolean("commit"); - String message = data.getString("message"); - projectService.setProjectImage(projectId, imageName, commit, message); - return Response.ok().entity(imageName).build(); + projectService.setProjectImage(projectId, data); + return Response.ok().entity(data.getString("imageName")).build(); } catch (Exception e) { return Response.serverError().entity(e.getMessage()).build(); } @@ -78,7 +75,7 @@ public class ImagesResource { @DELETE @Produces(MediaType.APPLICATION_JSON) @Path("/{imageName}") - public Response deleteImage(@HeaderParam("username") String username, @PathParam("imageName") String imageName) { + public Response deleteImage(@PathParam("imageName") String imageName) { imageName= new String(Base64.decode(imageName)); if (ConfigService.inKubernetes()) { return Response.ok().build(); diff --git a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/api/NotificationResource.java b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/api/NotificationResource.java index 4ad4de25..d7f2deb7 100644 --- a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/api/NotificationResource.java +++ b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/api/NotificationResource.java @@ -16,48 +16,56 @@ */ package org.apache.camel.karavan.api; -import io.quarkus.runtime.StartupEvent; import io.smallrye.mutiny.Multi; import io.vertx.core.json.JsonObject; -import io.vertx.mutiny.core.Vertx; import io.vertx.mutiny.core.eventbus.EventBus; -import jakarta.enterprise.event.Observes; import jakarta.inject.Inject; import jakarta.ws.rs.GET; import jakarta.ws.rs.Path; import jakarta.ws.rs.PathParam; import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.Context; import jakarta.ws.rs.core.MediaType; -import org.jboss.logging.Logger; +import jakarta.ws.rs.sse.OutboundSseEvent; +import jakarta.ws.rs.sse.Sse; import org.jboss.resteasy.reactive.RestStreamElementType; -import java.util.Date; +import static org.apache.camel.karavan.shared.Constants.*; @Path("/api/notification") public class NotificationResource { - private static final Logger LOGGER = Logger.getLogger(NotificationResource.class.getName()); - - void onStart(@Observes StartupEvent ev) throws Exception { - System.out.println("STARTING!!!!!"); - vertx.setPeriodic(1000, - aLong -> { - vertx.eventBus().publish("test0", new JsonObject().put("user", "test0").put("date", new Date().toString())); - vertx.eventBus().publish("test1", new JsonObject().put("user", "test1").put("date", new Date().toString())); - }); - } - - @Inject - Vertx vertx; @Inject EventBus bus; + @GET + @Path("/system") + @Produces(MediaType.SERVER_SENT_EVENTS) + @RestStreamElementType(MediaType.TEXT_PLAIN) + public Multi<OutboundSseEvent> karavanStream( + @Context Sse sse + ) { + return bus.<JsonObject>consumer(NOTIFICATION_ADDRESS_SYSTEM).toMulti() + .map(m -> sse.newEventBuilder() + .id(m.headers().get(NOTIFICATION_HEADER_EVENT_ID)) + .name(m.headers().get(NOTIFICATION_HEADER_EVENT_NAME) + ":" + m.headers().get(NOTIFICATION_HEADER_CLASS_NAME)) + .data(m.body()) + .build()); + } @GET - @Path("{name}") + @Path("/user/{id}") @Produces(MediaType.SERVER_SENT_EVENTS) @RestStreamElementType(MediaType.TEXT_PLAIN) - public Multi<String> greetingStream(@PathParam("name") String name) { - return bus.<String>consumer(name).bodyStream().toMulti(); + public Multi<OutboundSseEvent> userStream( + @PathParam("id") String id, + @Context Sse sse + ) { + return bus.<JsonObject>consumer(id).toMulti() + .map(m -> sse.newEventBuilder() + .id(m.headers().get(NOTIFICATION_HEADER_EVENT_ID)) + .name(m.headers().get(NOTIFICATION_HEADER_EVENT_NAME) + ":" + m.headers().get(NOTIFICATION_HEADER_CLASS_NAME)) + .data(m.body()) + .build()); } } \ No newline at end of file diff --git a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/api/ProjectGitResource.java b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/api/ProjectGitResource.java index aea987ea..b617f7b0 100644 --- a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/api/ProjectGitResource.java +++ b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/api/ProjectGitResource.java @@ -16,6 +16,8 @@ */ package org.apache.camel.karavan.api; +import io.vertx.core.json.JsonObject; +import io.vertx.mutiny.core.eventbus.EventBus; import jakarta.inject.Inject; import jakarta.ws.rs.*; import jakarta.ws.rs.core.MediaType; @@ -25,6 +27,9 @@ import org.apache.camel.karavan.service.ProjectService; import org.jboss.logging.Logger; import java.util.HashMap; +import java.util.Map; + +import static org.apache.camel.karavan.service.ProjectService.PUSH_PROJECT; @Path("/api/git") public class ProjectGitResource { @@ -34,11 +39,15 @@ public class ProjectGitResource { @Inject ProjectService projectService; + @Inject + EventBus eventBus; + @POST @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON) - public Project push(HashMap<String, String> params) throws Exception { - return projectService.commitAndPushProject(params.get("projectId"), params.get("message")); + public HashMap<String, String> push(HashMap<String, String> params) throws Exception { + eventBus.publish(PUSH_PROJECT, JsonObject.mapFrom(params)); + return params; } @PUT diff --git a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/service/KaravanService.java b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/service/KaravanService.java index 9d22d1c1..fe2df760 100644 --- a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/service/KaravanService.java +++ b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/service/KaravanService.java @@ -64,9 +64,6 @@ public class KaravanService implements HealthCheck { @Inject DockerForRegistry dockerForRegistry; - @Inject - KaravanCacheService karavanCacheService; - @Inject EventBus eventBus; diff --git a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/service/NotificationService.java b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/service/NotificationService.java new file mode 100644 index 00000000..b73e9d78 --- /dev/null +++ b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/service/NotificationService.java @@ -0,0 +1,50 @@ +/* + * 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. + */ +package org.apache.camel.karavan.service; + +import io.vertx.core.eventbus.DeliveryOptions; +import io.vertx.core.json.JsonObject; +import io.vertx.mutiny.core.eventbus.EventBus; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; + +import java.util.UUID; + +import static org.apache.camel.karavan.shared.Constants.*; + +@ApplicationScoped +public class NotificationService { + + @Inject + EventBus eventBus; + + public void send(String userId, String eventId, String evenName, String className, JsonObject data) { + eventBus.publish(userId, data, new DeliveryOptions() + .addHeader(NOTIFICATION_HEADER_EVENT_ID, eventId != null ? eventId : UUID.randomUUID().toString()) + .addHeader(NOTIFICATION_HEADER_EVENT_NAME, evenName) + .addHeader(NOTIFICATION_HEADER_CLASS_NAME, className) + ); + } + + public void sendSystem(String eventId, String evenName, String className, JsonObject data) { + eventBus.publish(NOTIFICATION_ADDRESS_SYSTEM, data, new DeliveryOptions() + .addHeader(NOTIFICATION_HEADER_EVENT_ID, eventId != null ? eventId : UUID.randomUUID().toString()) + .addHeader(NOTIFICATION_HEADER_EVENT_NAME, evenName) + .addHeader(NOTIFICATION_HEADER_CLASS_NAME, className) + ); + } +} \ No newline at end of file diff --git a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/service/ProjectService.java b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/service/ProjectService.java index e3f2aa1a..a17044fd 100644 --- a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/service/ProjectService.java +++ b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/service/ProjectService.java @@ -16,6 +16,7 @@ */ package org.apache.camel.karavan.service; +import io.quarkus.vertx.ConsumeEvent; import io.smallrye.mutiny.tuples.Tuple2; import io.vertx.core.json.JsonObject; import io.vertx.mutiny.core.eventbus.EventBus; @@ -53,6 +54,7 @@ import java.util.concurrent.atomic.AtomicBoolean; import java.util.stream.Collectors; import static org.apache.camel.karavan.code.CodeService.*; +import static org.apache.camel.karavan.shared.Constants.NOTIFICATION_EVENT_COMMIT; @Default @Readiness @@ -61,6 +63,8 @@ public class ProjectService implements HealthCheck { private static final Logger LOGGER = Logger.getLogger(ProjectService.class.getName()); + public static final String PUSH_PROJECT = "PUSH_PROJECT"; + @ConfigProperty(name = "karavan.environment") String environment; @@ -71,6 +75,9 @@ public class ProjectService implements HealthCheck { @Inject KaravanCacheService karavanCacheService; + @Inject + NotificationService notificationService; + @Inject KubernetesService kubernetesService; @@ -348,7 +355,13 @@ public class ProjectService implements HealthCheck { } - public Project commitAndPushProject(String projectId, String message) throws Exception { + @ConsumeEvent(value = PUSH_PROJECT, blocking = true, ordered = true) + void commitAndPushProject(JsonObject event) throws Exception { + LOGGER.info("Commit: " + event.encodePrettily()); + String projectId = event.getString("projectId"); + String message = event.getString("message"); + String userId = event.getString("userId"); + String eventId = event.getString("eventId"); Project p = karavanCacheService.getProject(projectId); List<ProjectFile> files = karavanCacheService.getProjectFiles(projectId); RevCommit commit = gitService.commitAndPushProject(p, files, message); @@ -357,7 +370,9 @@ public class ProjectService implements HealthCheck { p.setLastCommit(commitId); p.setLastCommitTimestamp(lastUpdate); karavanCacheService.saveProject(p); - return p; + if (userId != null) { + notificationService.sendSystem(eventId, NOTIFICATION_EVENT_COMMIT, Project.class.getSimpleName(), JsonObject.mapFrom(p)); + } } void addKameletsProject() { @@ -367,7 +382,7 @@ public class ProjectService implements HealthCheck { if (kamelets == null) { kamelets = new Project(Project.Type.kamelets.name(), "Custom Kamelets", "Custom Kamelets", "", Instant.now().toEpochMilli(), Project.Type.kamelets); karavanCacheService.saveProject(kamelets); - commitAndPushProject(Project.Type.kamelets.name(), "Add custom kamelets"); + commitAndPushProject(JsonObject.of("projectId", Project.Type.kamelets.name(), "message", "Add custom kamelets")); } } catch (Exception e) { LOGGER.error("Error during custom kamelets project creation", e); @@ -386,7 +401,7 @@ public class ProjectService implements HealthCheck { ProjectFile file = new ProjectFile(name, value, Project.Type.templates.name(), Instant.now().toEpochMilli()); karavanCacheService.saveProjectFile(file); }); - commitAndPushProject(Project.Type.templates.name(), "Add default templates"); + commitAndPushProject(JsonObject.of("projectId", Project.Type.templates.name(), "message", "Add custom templates")); } else { LOGGER.info("Add new templates if any"); codeService.getTemplates().forEach((name, value) -> { @@ -414,7 +429,7 @@ public class ProjectService implements HealthCheck { ProjectFile file = new ProjectFile(name, value, Project.Type.services.name(), Instant.now().toEpochMilli()); karavanCacheService.saveProjectFile(file); }); - commitAndPushProject(Project.Type.services.name(), "Add services"); + commitAndPushProject(JsonObject.of("projectId", Project.Type.services.name(), "message", "Add services")); } } catch (Exception e) { LOGGER.error("Error during services project creation", e); @@ -427,10 +442,13 @@ public class ProjectService implements HealthCheck { return file.orElse(new ProjectFile()).getCode(); } - public void setProjectImage(String projectId, String imageName, boolean commit, String message) throws Exception { + public void setProjectImage(String projectId, JsonObject data) throws Exception { + String imageName = data.getString("imageName"); + boolean commit = data.getBoolean("commit"); + data.put("projectId", projectId); codeService.updateDockerComposeImage(projectId, imageName); if (commit) { - commitAndPushProject(projectId, message); + eventBus.publish(PUSH_PROJECT, data); } } diff --git a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/shared/Constants.java b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/shared/Constants.java index 28f01ce8..ff6152d9 100644 --- a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/shared/Constants.java +++ b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/shared/Constants.java @@ -42,6 +42,13 @@ public class Constants { public static final String KNOWN_HOSTS_SECRET_KEY = "known-hosts"; public static final String BUILD_SCRIPT_FILENAME_SUFFIX = "-build.sh"; + public static final String NOTIFICATION_ADDRESS_SYSTEM = "karavanSystem"; + public static final String NOTIFICATION_HEADER_EVENT_ID = "id"; + public static final String NOTIFICATION_HEADER_EVENT_NAME = "eventName"; + public static final String NOTIFICATION_HEADER_CLASS_NAME = "className"; + + public static final String NOTIFICATION_EVENT_COMMIT = "commit"; + public enum CamelRuntime { CAMEL_MAIN("camel-main"), QUARKUS("quarkus"), diff --git a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/shared/exception/ExceptionToResponseMapper.java b/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/shared/exception/ExceptionToResponseMapper.java deleted file mode 100644 index b26b49fc..00000000 --- a/karavan-web/karavan-app/src/main/java/org/apache/camel/karavan/shared/exception/ExceptionToResponseMapper.java +++ /dev/null @@ -1,66 +0,0 @@ -package org.apache.camel.karavan.shared.exception; - -import java.util.Collection; -import java.util.List; - -import org.apache.camel.karavan.shared.error.Error; -import org.apache.camel.karavan.shared.error.ErrorResponse; -import org.jboss.logging.Logger; -import org.jboss.resteasy.reactive.RestResponse; -import org.jboss.resteasy.reactive.server.ServerExceptionMapper; - -import jakarta.ws.rs.core.MediaType; -import jakarta.ws.rs.core.Response; - -public class ExceptionToResponseMapper { - private static final Logger LOGGER = Logger.getLogger(ExceptionToResponseMapper.class.getName()); - - @ServerExceptionMapper - public RestResponse<Object> validationException(ValidationException exception) { - List<Error> errors = (exception).getErrors() - .stream() - .map(fieldError -> new Error(fieldError.getField(), fieldError.getMessage())) - .toList(); - - return logAndBuildResponse( - exception, - Response.Status.BAD_REQUEST.getStatusCode(), - Response.Status.BAD_REQUEST.getReasonPhrase(), - errors - ); - } - - private RestResponse<Object> logAndBuildResponse( - Throwable exception, - int status, - String reasonPhrase, - Collection<Error> errors - ) { - LOGGER.error("Error occurred", exception); - - String cause = (exception.getCause() != null) ? exception.getCause().getMessage() : null; - String message = (cause != null) ? exception.getMessage() + ", caused by: " + cause : exception.getMessage(); - - if (message == null) { - message = exception.getClass().toString(); - } - - // Hide errors array if there are no errors and leave just error message - if (errors != null && errors.isEmpty()) { - errors = null; - } - - ErrorResponse responseBody = new ErrorResponse( - status, - reasonPhrase, - message, - errors - ); - - return RestResponse.ResponseBuilder - .create(status) - .entity(responseBody) - .type(MediaType.APPLICATION_JSON) - .build(); - } -} 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 2efbeeb6..78239239 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 @@ -26,9 +26,9 @@ import { } from "./ProjectModels"; import {Buffer} from 'buffer'; import {SsoApi} from "./SsoApi"; -import {EventStreamContentType, fetchEventSource} from "@microsoft/fetch-event-source"; -import {ProjectEventBus} from "./ProjectEventBus"; +import {v4 as uuidv4} from "uuid"; +const USER_ID_KEY = 'KARAVAN_USER_ID'; axios.defaults.headers.common['Accept'] = 'application/json'; axios.defaults.headers.common['Content-Type'] = 'application/json'; const instance = axios.create(); @@ -43,6 +43,21 @@ export class KaravanApi { return instance; } + static getUserId(): string { + if (KaravanApi.me?.userName !== undefined) { + return KaravanApi.me?.userName; + } else { + const userId = localStorage.getItem(USER_ID_KEY); + if (userId !== null && userId !== undefined) { + return userId; + } else { + const newId = uuidv4().toString(); + localStorage.setItem(USER_ID_KEY, newId); + return newId; + } + } + } + static setAuthType(authType: string) { KaravanApi.authType = authType; switch (authType){ diff --git a/karavan-web/karavan-app/src/main/webui/src/api/NotificationApi.tsx b/karavan-web/karavan-app/src/main/webui/src/api/NotificationApi.tsx index 20ea0c1f..cabf1c9e 100644 --- a/karavan-web/karavan-app/src/main/webui/src/api/NotificationApi.tsx +++ b/karavan-web/karavan-app/src/main/webui/src/api/NotificationApi.tsx @@ -18,40 +18,69 @@ import {SsoApi} from "./SsoApi"; import {EventStreamContentType, fetchEventSource} from "@microsoft/fetch-event-source"; import {KaravanApi} from "./KaravanApi"; +import {EventBus} from "../designer/utils/EventBus"; +import {EventSourceMessage} from "@microsoft/fetch-event-source/lib/cjs/parse"; +import {KaravanEvent, NotificationEventBus} from "./NotificationService"; export class NotificationApi { + static getKaravanEvent (ev: EventSourceMessage, type: 'system' | 'user') { + const eventParts = ev.event?.split(':'); + const event = eventParts?.length > 1 ? eventParts[0] : undefined; + const className = eventParts?.length > 1 ? eventParts[1] : undefined; + return new KaravanEvent({id: ev.id, event: event, type: type, className: className, data: JSON.parse(ev.data)}); + } + + static onSystemMessage (ev: EventSourceMessage) { + const ke = NotificationApi.getKaravanEvent(ev, 'system'); + NotificationEventBus.sendEvent(ke); + } + + static onUserMessage (ev: EventSourceMessage) { + const ke = NotificationApi.getKaravanEvent(ev, 'user'); + NotificationEventBus.sendEvent(ke); + } + static async notification(controller: AbortController) { const fetchData = async () => { const headers: any = { Accept: "text/event-stream" }; if (KaravanApi.authType === 'oidc') { headers.Authorization = "Bearer " + SsoApi.keycloak?.token } - await fetchEventSource("/api/notification", { - method: "GET", - headers: headers, - signal: controller.signal, - async onopen(response) { - if (response.ok && response.headers.get('content-type') === EventStreamContentType) { - return; // everything's good - } else if (response.status >= 400 && response.status < 500 && response.status !== 429) { - // client-side errors are usually non-retriable: - console.log("Server side error ", response); - } else { - console.log("Error ", response); - } - }, - onmessage(event) { - console.log(event) - }, - onclose() { - console.log("Connection closed by the server"); - }, - onerror(err) { - console.log("There was an error from server", err); - }, - }); + NotificationApi.fetch('/api/notification/system', controller, headers, + ev => NotificationApi.onSystemMessage(ev)); + NotificationApi.fetch('/api/notification/user/' + KaravanApi.getUserId(), controller, headers, + ev => NotificationApi.onUserMessage(ev)); }; return fetchData(); + }; + + static async fetch(input: string, controller: AbortController, headers: any, onmessage: (ev: EventSourceMessage) => void) { + fetchEventSource(input, { + method: "GET", + headers: headers, + signal: controller.signal, + async onopen(response) { + if (response.ok && response.headers.get('content-type') === EventStreamContentType) { + return; // everything's good + } else if (response.status >= 400 && response.status < 500 && response.status !== 429) { + // client-side errors are usually non-retriable: + console.log("Server side error ", response); + EventBus.sendAlert("Error fetching", `${input} : ${response.statusText}`, "danger"); + } else { + console.log("Error ", response); + EventBus.sendAlert("Error fetching", `${input} : ${response.statusText}`, "danger"); + } + }, + onmessage(event) { + onmessage(event); + }, + onclose() { + console.log("Connection closed by the server"); + }, + onerror(err) { + console.log("There was an error from server", err); + }, + }); } } diff --git a/karavan-web/karavan-app/src/main/webui/src/api/NotificationService.ts b/karavan-web/karavan-app/src/main/webui/src/api/NotificationService.ts new file mode 100644 index 00000000..2008c652 --- /dev/null +++ b/karavan-web/karavan-app/src/main/webui/src/api/NotificationService.ts @@ -0,0 +1,57 @@ +/* + * 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 {ProjectEventBus} from "./ProjectEventBus"; +import {unstable_batchedUpdates} from "react-dom"; +import {useLogStore, useProjectStore} from "./ProjectStore"; +import {ProjectService} from "./ProjectService"; + +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(), +} + +console.log("Start Notification subscriber"); +const sub = NotificationEventBus.onEvent()?.subscribe((event: KaravanEvent) => { + // console.log('KaravanEvent', event); + if (event.event === 'commit' && event.className === 'Project') { + const projectId = event.data?.projectId; + if (useProjectStore.getState().project?.projectId === projectId) { + unstable_batchedUpdates(() => { + useProjectStore.setState({isPushing: false}); + ProjectService.refreshProject(projectId); + ProjectService.refreshProjectData(projectId); + }); + } + } +}); + diff --git a/karavan-web/karavan-app/src/main/webui/src/api/ProjectEventBus.ts b/karavan-web/karavan-app/src/main/webui/src/api/ProjectEventBus.ts index dcbc6c6e..84e458e7 100644 --- a/karavan-web/karavan-app/src/main/webui/src/api/ProjectEventBus.ts +++ b/karavan-web/karavan-app/src/main/webui/src/api/ProjectEventBus.ts @@ -14,44 +14,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import {BehaviorSubject, Subject} from 'rxjs'; -import {Project} from "./ProjectModels"; +import {Subject} from 'rxjs'; -const selectedProject = new BehaviorSubject<Project | undefined>(undefined); -const currentFile = new BehaviorSubject<string | undefined>(undefined); -const mode = new BehaviorSubject<"design" | "code">("design"); const log = new Subject<["add" | "set", string]>(); -export class ShowLogCommand { - name: string - environment: string - show: boolean - - constructor(name: string, environment: string, show: boolean) { - this.name = name; - this.environment = environment; - this.show = show; - } -} - -export class ShowTraceCommand { - name: string - show: boolean - - constructor(name: string, show: boolean) { - this.name = name; - this.show = show; - } -} export const ProjectEventBus = { - selectProject: (project: Project) => selectedProject.next(project), - onSelectProject: () => selectedProject.asObservable(), - - setMode: (m: 'design' | 'code') => mode.next(m), - onSetMode: () => mode.asObservable(), - sendLog: (type: "add" | "set", m: string) => log.next([type, m]), onLog: () => log.asObservable(), - } diff --git a/karavan-web/karavan-app/src/main/webui/src/api/ProjectService.ts b/karavan-web/karavan-app/src/main/webui/src/api/ProjectService.ts index 9f9891f4..9a1b5f21 100644 --- a/karavan-web/karavan-app/src/main/webui/src/api/ProjectService.ts +++ b/karavan-web/karavan-app/src/main/webui/src/api/ProjectService.ts @@ -96,19 +96,18 @@ export class ProjectService { } public static pushProject(project: Project, commitMessage: string) { - useProjectStore.setState({isPushing: true}) const params = { 'projectId': project.projectId, - 'message': commitMessage + 'message': commitMessage, + 'userId': KaravanApi.getUserId() }; KaravanApi.push(params, res => { if (res.status === 200 || res.status === 201) { - ProjectService.refreshProject(project.projectId); - ProjectService.refreshProjectData(project.projectId); + // ProjectService.refreshProject(project.projectId); + // ProjectService.refreshProjectData(project.projectId); } else { EventBus.sendAlert("Error pushing", (res as any)?.response?.data, 'danger') } - useProjectStore.setState({isPushing: false}) }); } 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 00fc6ff5..d08f5dc8 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 @@ -30,16 +30,28 @@ import {useMainHook} from "./useMainHook"; import {Notification} from "../designer/utils/Notification"; import {MainLoader} from "./MainLoader"; import {MainRoutes} from "./MainRoutes"; +import {NotificationApi} from "../api/NotificationApi"; export function Main() { const [readiness, setReadiness] = useAppConfigStore((s) => [s.readiness, s.setReadiness], shallow) - const {getData, getStatuses} = useMainHook(); + const {getData} = useMainHook(); - const initialized = useRef(false) + const initialized = useRef(false); + + useEffect(() => { + if (showMain()) { + console.log("Start Notification fetcher"); + const controller = new AbortController(); + NotificationApi.notification(controller); + return () => { + console.log("Stop Notification fetcher"); + controller.abort(); + }; + } + }, [readiness]); useEffect(() => { - console.log("Main"); if (!initialized.current) { initialized.current = true effect() @@ -55,7 +67,6 @@ export function Main() { }, []) function effect() { - console.log("Main effect start"); KaravanApi.getAuthType((authType: string) => { console.log("authType", authType); if (authType === 'oidc') { @@ -67,9 +78,6 @@ export function Main() { } getData(); }); - return () => { - console.log("Main effect end"); - }; } function showSpinner() { 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 5f4216e3..166bdaaf 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 @@ -41,7 +41,8 @@ import UpdateIcon from "@patternfly/react-icons/dist/esm/icons/cog-icon"; import RefreshIcon from "@patternfly/react-icons/dist/esm/icons/sync-alt-icon"; import {ProjectType} from "../../api/ProjectModels"; import {KaravanApi} from "../../api/KaravanApi"; -import {EventBus} from "../../designer/utils/EventBus"; +import {DslPosition, EventBus} from "../../designer/utils/EventBus"; +import {KaravanEvent, NotificationEventBus} from "../../api/NotificationService"; export function FileToolbar () { @@ -49,17 +50,28 @@ export function FileToolbar () { const [commitMessageIsOpen, setCommitMessageIsOpen] = useState(false); const [pullIsOpen, setPullIsOpen] = useState(false); const [commitMessage, setCommitMessage] = useState(''); - const [project, isPushing, isPulling] = useProjectStore((s) => [s.project, s.isPushing, s.isPulling], shallow ) + const [project, isPushing, isPulling] = + useProjectStore((s) => [s.project, s.isPushing, s.isPulling], shallow ) const {files} = useFilesStore(); const [file, editAdvancedProperties, setEditAdvancedProperties, setAddProperty, setFile] = useFileStore((s) => [s.file, s.editAdvancedProperties, s.setEditAdvancedProperties, s.setAddProperty, s.setFile], shallow ) + // useEffect(() => { + // const sub1 = NotificationEventBus.onEvent()?.subscribe((evt: KaravanEvent) => { + // console.log(evt); + // setIsPushing(false); + // }); + // return () => { + // sub1?.unsubscribe(); + // }; + // }); useEffect(() => { }, [project, file]); function push () { setCommitMessageIsOpen(false); + useProjectStore.setState({isPushing: true}); ProjectService.pushProject(project, commitMessage); } 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 99d62b43..6e7de890 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 @@ -48,11 +48,9 @@ export function ProjectLogPanel () { const f = LogWatchApi.fetchData(type, podName, controller).then(value => { console.log("Fetch Started for: " + podName) }); - console.log("new fetch") setFetch(f); } return () => { - console.log("end"); controller.abort(); }; }, [showLog, type, podName]);