From 6c4056d2c50cd6e32d5507ac71adfdf4f04858bc Mon Sep 17 00:00:00 2001 From: Ainur Date: Fri, 8 Mar 2024 14:40:53 +0100 Subject: [PATCH 01/16] CB-4810 add new client event for updating session info --- .../cloudbeaver/model/session/WebSession.java | 17 ++++++++--------- .../server/websockets/CBEventsWebSocket.java | 7 +++++++ .../service/session/WebSessionManager.java | 3 ++- 3 files changed, 17 insertions(+), 10 deletions(-) diff --git a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/session/WebSession.java b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/session/WebSession.java index 178cff195f..7eac22aa8a 100644 --- a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/session/WebSession.java +++ b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/session/WebSession.java @@ -524,17 +524,10 @@ public List getSessionMessages() { } } - public synchronized void updateInfo( - HttpServletRequest request, - HttpServletResponse response - ) throws DBWebException { + public synchronized void updateInfo(boolean isOldHttpSessionUsed) { log.debug("Update session lifetime " + getSessionId() + " for user " + getUserId()); touchSession(); - HttpSession httpSession = request.getSession(); - this.lastRemoteAddr = request.getRemoteAddr(); - this.lastRemoteUserAgent = request.getHeader("User-Agent"); - this.cacheExpired = false; - if (!httpSession.isNew()) { + if (isOldHttpSessionUsed) { try { // Persist session if (!isAuthorizedInSecurityManager()) { @@ -554,6 +547,12 @@ public synchronized void updateInfo( } } + public synchronized void updateSessionParameters(HttpServletRequest request) { + this.lastRemoteAddr = request.getRemoteAddr(); + this.lastRemoteUserAgent = request.getHeader("User-Agent"); + this.cacheExpired = false; + } + @Association public List getConnections() { synchronized (connections) { diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/websockets/CBEventsWebSocket.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/websockets/CBEventsWebSocket.java index ddf70ca3cb..c3fac62c5c 100644 --- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/websockets/CBEventsWebSocket.java +++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/websockets/CBEventsWebSocket.java @@ -18,6 +18,7 @@ import io.cloudbeaver.DBWebException; import io.cloudbeaver.model.session.BaseWebSession; +import io.cloudbeaver.model.session.WebSession; import io.cloudbeaver.websocket.CBWebSessionEventHandler; import org.eclipse.jetty.websocket.api.Session; import org.eclipse.jetty.websocket.api.WriteCallback; @@ -76,6 +77,12 @@ public void onWebSocketText(String message) { this.webSession.getEventsFilter().setSubscribedProjects(projectEvent.getProjectIds()); break; } + case TOUCH_SESSION: { + if (webSession instanceof WebSession session) { + session.updateInfo(true); + } + break; + } default: var e = new DBWebException("Unknown websocket client event: " + clientEvent.getId()); log.error(e.getMessage(), e); diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/session/WebSessionManager.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/session/WebSessionManager.java index 6704c92ffd..bf08b7f518 100644 --- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/session/WebSessionManager.java +++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/session/WebSessionManager.java @@ -82,7 +82,8 @@ protected CBApplication getApplication() { public boolean touchSession(@NotNull HttpServletRequest request, @NotNull HttpServletResponse response) throws DBWebException { WebSession webSession = getWebSession(request, response, false); - webSession.updateInfo(request, response); + webSession.updateSessionParameters(request); + webSession.updateInfo(!request.getSession().isNew()); return true; } From 7caa874a4bc21c674d68cb9d6c5d6711833e053a Mon Sep 17 00:00:00 2001 From: Ainur Date: Tue, 12 Mar 2024 11:12:22 +0100 Subject: [PATCH 02/16] CB-4810 update request parameters on creating web session --- .../src/io/cloudbeaver/model/session/WebSession.java | 7 ++++--- .../server/websockets/CBJettyWebSocketManager.java | 1 + .../cloudbeaver/service/core/impl/WebServiceCore.java | 1 + .../service/session/WebSessionManager.java | 11 ++++++----- 4 files changed, 12 insertions(+), 8 deletions(-) diff --git a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/session/WebSession.java b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/session/WebSession.java index 7eac22aa8a..916e92afaa 100644 --- a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/session/WebSession.java +++ b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/session/WebSession.java @@ -124,13 +124,13 @@ public class WebSession extends BaseWebSession private final Map sessionHandlers; public WebSession( - @NotNull HttpSession httpSession, + @NotNull HttpServletRequest request, @NotNull WebAuthApplication application, @NotNull Map sessionHandlers ) throws DBException { - super(httpSession.getId(), application); + super(request.getSession().getId(), application); this.lastAccessTime = this.createTime; - setLocale(CommonUtils.toString(httpSession.getAttribute(ATTR_LOCALE), this.locale)); + setLocale(CommonUtils.toString(request.getSession().getAttribute(ATTR_LOCALE), this.locale)); this.sessionHandlers = sessionHandlers; //force authorization of anonymous session to avoid access error, //because before authorization could be called by any request, @@ -138,6 +138,7 @@ public WebSession( //and the order of requests is not guaranteed. //look at CB-4747 refreshSessionAuth(); + updateSessionParameters(request); } @Nullable diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/websockets/CBJettyWebSocketManager.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/websockets/CBJettyWebSocketManager.java index 28295b75eb..8d372cdb4d 100644 --- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/websockets/CBJettyWebSocketManager.java +++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/websockets/CBJettyWebSocketManager.java @@ -54,6 +54,7 @@ public Object createWebSocket(@NotNull JettyServerUpgradeRequest request, JettyS var httpRequest = request.getHttpServletRequest(); var webSession = webSessionManager.getOrRestoreSession(httpRequest); if (webSession != null) { + webSession.updateSessionParameters(httpRequest); // web client session return createNewEventsWebSocket(webSession); } diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/core/impl/WebServiceCore.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/core/impl/WebServiceCore.java index ad7eec7b06..c31162fbf7 100644 --- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/core/impl/WebServiceCore.java +++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/core/impl/WebServiceCore.java @@ -282,6 +282,7 @@ public boolean closeSession(HttpServletRequest request) throws DBWebException { } @Override + @Deprecated public boolean touchSession(@NotNull HttpServletRequest request, @NotNull HttpServletResponse response) throws DBWebException { return CBPlatform.getInstance().getSessionManager().touchSession(request, response); } diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/session/WebSessionManager.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/session/WebSessionManager.java index bf08b7f518..3f322e4e0c 100644 --- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/session/WebSessionManager.java +++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/session/WebSessionManager.java @@ -79,6 +79,7 @@ protected CBApplication getApplication() { return application; } + @Deprecated public boolean touchSession(@NotNull HttpServletRequest request, @NotNull HttpServletResponse response) throws DBWebException { WebSession webSession = getWebSession(request, response, false); @@ -106,14 +107,14 @@ public WebSession getWebSession( var baseWebSession = sessionMap.get(sessionId); if (baseWebSession == null && CBApplication.getInstance().isConfigurationMode()) { try { - webSession = createWebSessionImpl(httpSession); + webSession = createWebSessionImpl(request); } catch (DBException e) { throw new DBWebException("Failed to create web session", e); } sessionMap.put(sessionId, webSession); } else if (baseWebSession == null) { try { - webSession = createWebSessionImpl(httpSession); + webSession = createWebSessionImpl(request); } catch (DBException e) { throw new DBWebException("Failed to create web session", e); } @@ -175,7 +176,7 @@ public WebSession getOrRestoreSession(@NotNull HttpServletRequest request) { return null; } - webSession = createWebSessionImpl(httpSession); + webSession = createWebSessionImpl(request); restorePreviousUserSession(webSession, oldAuthInfo); sessionMap.put(sessionId, webSession); @@ -209,8 +210,8 @@ private void restorePreviousUserSession( } @NotNull - protected WebSession createWebSessionImpl(@NotNull HttpSession httpSession) throws DBException { - return new WebSession(httpSession, application, getSessionHandlers()); + protected WebSession createWebSessionImpl(@NotNull HttpServletRequest request) throws DBException { + return new WebSession(request, application, getSessionHandlers()); } @NotNull From 1cf0d9e86d632c59c31c583b7841f4f9715a7abf Mon Sep 17 00:00:00 2001 From: Ainur Date: Tue, 12 Mar 2024 12:18:24 +0100 Subject: [PATCH 03/16] CB-4810 deprecate update session gql functions --- .../io.cloudbeaver.server/schema/service.core.graphqls | 4 ++-- .../io.cloudbeaver.server/schema/service.events.graphqls | 3 ++- .../server/websockets/CBJettyWebSocketManager.java | 4 ++++ .../src/io/cloudbeaver/service/core/DBWServiceCore.java | 1 + .../src/io/cloudbeaver/service/core/impl/WebServiceCore.java | 1 + 5 files changed, 10 insertions(+), 3 deletions(-) diff --git a/server/bundles/io.cloudbeaver.server/schema/service.core.graphqls b/server/bundles/io.cloudbeaver.server/schema/service.core.graphqls index b393c48824..faf8c4a166 100644 --- a/server/bundles/io.cloudbeaver.server/schema/service.core.graphqls +++ b/server/bundles/io.cloudbeaver.server/schema/service.core.graphqls @@ -566,9 +566,9 @@ extend type Mutation { closeSession: Boolean # Refreshes session on server and returns its state - touchSession: Boolean @deprecated(reason: "use updateSession instead") + touchSession: Boolean @deprecated(reason: "use events to update session") # Refreshes session on server and returns session state - updateSession: SessionInfo! @since(version: "24.0.0") + updateSession: SessionInfo! @since(version: "24.0.0") @deprecated(reason: "use events to update session") # Refresh session connection list refreshSessionConnections: Boolean diff --git a/server/bundles/io.cloudbeaver.server/schema/service.events.graphqls b/server/bundles/io.cloudbeaver.server/schema/service.events.graphqls index 515b89b30d..567b9b0c7b 100644 --- a/server/bundles/io.cloudbeaver.server/schema/service.events.graphqls +++ b/server/bundles/io.cloudbeaver.server/schema/service.events.graphqls @@ -37,7 +37,8 @@ enum CBServerEventId { enum CBClientEventId { cb_client_topic_subscribe, cb_client_topic_unsubscribe, - cb_client_projects_active + cb_client_projects_active, + cb_client_touch_session } # Client subscribes on topic to receive only related events diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/websockets/CBJettyWebSocketManager.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/websockets/CBJettyWebSocketManager.java index 8d372cdb4d..70fbe04fc1 100644 --- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/websockets/CBJettyWebSocketManager.java +++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/websockets/CBJettyWebSocketManager.java @@ -29,6 +29,7 @@ import org.jkiss.dbeaver.DBException; import org.jkiss.dbeaver.Log; import org.jkiss.dbeaver.model.security.exception.SMAccessTokenExpiredException; +import org.jkiss.dbeaver.runtime.DBWorkbench; import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; @@ -59,6 +60,9 @@ public Object createWebSocket(@NotNull JettyServerUpgradeRequest request, JettyS return createNewEventsWebSocket(webSession); } // possible desktop client session + if (!DBWorkbench.isDistributed()) { + return null; + } try { var headlessSession = createHeadlessSession(httpRequest); if (headlessSession == null) { diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/core/DBWServiceCore.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/core/DBWServiceCore.java index e684f44669..c8b8eb1b39 100644 --- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/core/DBWServiceCore.java +++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/core/DBWServiceCore.java @@ -94,6 +94,7 @@ WebSession openSession( @WebAction(authRequired = false) boolean touchSession(@NotNull HttpServletRequest request, @NotNull HttpServletResponse servletResponse) throws DBWebException; + @Deprecated @WebAction(authRequired = false) WebSession updateSession(@NotNull HttpServletRequest request, @NotNull HttpServletResponse response) throws DBWebException; diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/core/impl/WebServiceCore.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/core/impl/WebServiceCore.java index c31162fbf7..cd1ba5ca20 100644 --- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/core/impl/WebServiceCore.java +++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/core/impl/WebServiceCore.java @@ -288,6 +288,7 @@ public boolean touchSession(@NotNull HttpServletRequest request, @NotNull HttpSe } @Override + @Deprecated public WebSession updateSession(@NotNull HttpServletRequest request, @NotNull HttpServletResponse response) throws DBWebException { WebSessionManager sessionManager = CBPlatform.getInstance().getSessionManager(); From 754639a6c250542903415ce38cfa8744df100f27 Mon Sep 17 00:00:00 2001 From: Ainur Date: Fri, 15 Mar 2024 10:01:55 +0100 Subject: [PATCH 04/16] CB-4810 fix tests --- .../cloudbeaver/server/websockets/CBJettyWebSocketManager.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/websockets/CBJettyWebSocketManager.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/websockets/CBJettyWebSocketManager.java index 70fbe04fc1..02c730dd10 100644 --- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/websockets/CBJettyWebSocketManager.java +++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/websockets/CBJettyWebSocketManager.java @@ -60,9 +60,6 @@ public Object createWebSocket(@NotNull JettyServerUpgradeRequest request, JettyS return createNewEventsWebSocket(webSession); } // possible desktop client session - if (!DBWorkbench.isDistributed()) { - return null; - } try { var headlessSession = createHeadlessSession(httpRequest); if (headlessSession == null) { From df951b2c144aa542914b255f9fbd992e31bcb140 Mon Sep 17 00:00:00 2001 From: Ainur Date: Fri, 15 Mar 2024 14:26:53 +0100 Subject: [PATCH 05/16] CB-4810 add params to session state event --- .../io/cloudbeaver/service/session/WebSessionManager.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/session/WebSessionManager.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/session/WebSessionManager.java index 3f322e4e0c..d77bf2a154 100644 --- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/session/WebSessionManager.java +++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/session/WebSessionManager.java @@ -332,7 +332,13 @@ public void sendSessionsStates() { }) .forEach(session -> { try { - session.addSessionEvent(new WSSessionStateEvent(session.getRemainingTime(), session.isValid())); + session.addSessionEvent(new WSSessionStateEvent( + session.getLastAccessTimeMillis(), + session.getRemainingTime(), + session.isValid(), + ((WebSession) session).isCacheExpired(), + ((WebSession) session).getLocale(), + ((WebSession) session).getActionParameters())); } catch (Exception e) { log.error("Failed to refresh session state: " + session.getSessionId(), e); } From 4a2eb8c5f6163512b84c3f023aef5b800e5a15d9 Mon Sep 17 00:00:00 2001 From: Ainur Date: Fri, 15 Mar 2024 16:33:01 +0100 Subject: [PATCH 06/16] CB-4810 add params to gql schema --- .../io.cloudbeaver.server/schema/service.events.graphqls | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/server/bundles/io.cloudbeaver.server/schema/service.events.graphqls b/server/bundles/io.cloudbeaver.server/schema/service.events.graphqls index 567b9b0c7b..2842bbf4ca 100644 --- a/server/bundles/io.cloudbeaver.server/schema/service.events.graphqls +++ b/server/bundles/io.cloudbeaver.server/schema/service.events.graphqls @@ -115,8 +115,12 @@ type WSSocketConnectedEvent implements CBServerEvent { type WSSessionStateEvent implements CBServerEvent { id: CBServerEventId! topicId: CBEventTopic + lastAccessTime: Int! remainingTime: Int! isValid: Boolean + isCacheExpired: Boolean + locale: String! + actionParameters: Object } # Session expired event From ad5177288b93d021b7c637e6d69bbe8be8d23bc6 Mon Sep 17 00:00:00 2001 From: "s.teleshev" Date: Sat, 16 Mar 2024 11:05:01 +0100 Subject: [PATCH 07/16] CB-4686 feat: migrates touch session from REST to WebSockets --- .../core-root/src/SessionActivityService.ts | 13 +++-- .../core-root/src/SessionInfoEventHandler.ts | 8 ++- .../packages/core-root/src/SessionResource.ts | 54 +++++++++---------- .../SessionExpireWarningDialogBootstrap.ts | 10 +--- .../src/locales/en.ts | 2 +- .../src/locales/it.ts | 2 +- .../src/locales/ru.ts | 2 +- .../src/locales/zh.ts | 2 +- 8 files changed, 45 insertions(+), 48 deletions(-) diff --git a/webapp/packages/core-root/src/SessionActivityService.ts b/webapp/packages/core-root/src/SessionActivityService.ts index cd6a5dc144..e8c9712cce 100644 --- a/webapp/packages/core-root/src/SessionActivityService.ts +++ b/webapp/packages/core-root/src/SessionActivityService.ts @@ -16,22 +16,21 @@ export const SESSION_TOUCH_TIME_PERIOD = 1000 * 60; export class SessionActivityService extends Dependency { private touchSessionTimer: ReturnType | null = null; - constructor(private readonly clientActivityService: ClientActivityService, private readonly sessionResource: SessionResource) { + constructor( + private readonly clientActivityService: ClientActivityService, + private readonly sessionResource: SessionResource, + ) { super(); this.notifyClientActivity = this.notifyClientActivity.bind(this); this.clientActivityService.onActiveStateChange.addHandler(this.notifyClientActivity); } - private async notifyClientActivity() { + private notifyClientActivity() { if (this.touchSessionTimer || !this.clientActivityService.isActive) { return; } - try { - await this.sessionResource.updateSession(); - } catch (e) { - console.error('Session update error', e); - } + this.sessionResource.updateSession(); this.touchSessionTimer = setTimeout(() => { if (this.touchSessionTimer) { diff --git a/webapp/packages/core-root/src/SessionInfoEventHandler.ts b/webapp/packages/core-root/src/SessionInfoEventHandler.ts index b376a326f7..83b5d340f8 100644 --- a/webapp/packages/core-root/src/SessionInfoEventHandler.ts +++ b/webapp/packages/core-root/src/SessionInfoEventHandler.ts @@ -6,10 +6,10 @@ * you may not use this file except in compliance with the License. */ import { injectable } from '@cloudbeaver/core-di'; -import type { WsSessionStateEvent as ISessionStateEvent } from '@cloudbeaver/core-sdk'; +import { type CbClientEvent, CbEventTopic, type WsSessionStateEvent as ISessionStateEvent } from '@cloudbeaver/core-sdk'; import { TopicEventHandler } from './ServerEventEmitter/TopicEventHandler'; -import { ISessionEvent, SessionEventId, SessionEventSource, SessionEventTopic } from './SessionEventSource'; +import { ClientEventId, ISessionEvent, SessionEventId, SessionEventSource, SessionEventTopic } from './SessionEventSource'; export { type ISessionStateEvent }; @@ -19,6 +19,10 @@ export class SessionInfoEventHandler extends TopicEventHandler({ id: ClientEventId.CbClientTouchSession, topicId: CbEventTopic.CbSession }); + } + map(event: any): ISessionStateEvent { return event; } diff --git a/webapp/packages/core-root/src/SessionResource.ts b/webapp/packages/core-root/src/SessionResource.ts index b9479333a5..2030feb662 100644 --- a/webapp/packages/core-root/src/SessionResource.ts +++ b/webapp/packages/core-root/src/SessionResource.ts @@ -12,7 +12,7 @@ import { GraphQLService, SessionStateFragment } from '@cloudbeaver/core-sdk'; import { ServerConfigResource } from './ServerConfigResource'; import { ServerEventId } from './SessionEventSource'; -import { SessionInfoEventHandler } from './SessionInfoEventHandler'; +import { type ISessionStateEvent, SessionInfoEventHandler } from './SessionInfoEventHandler'; export type SessionState = SessionStateFragment; export interface ISessionAction { @@ -20,38 +20,23 @@ export interface ISessionAction { [key: string]: any; } -interface SessionStateData { - isValid?: boolean; - remainingTime: number; -} - @injectable() export class SessionResource extends CachedDataResource { private action: ISessionAction | null; private defaultLocale: string | undefined; - readonly onStatusUpdate: ISyncExecutor; + readonly onStatusUpdate: ISyncExecutor; constructor( private readonly graphQLService: GraphQLService, - sessionInfoEventHandler: SessionInfoEventHandler, + private readonly sessionInfoEventHandler: SessionInfoEventHandler, serverConfigResource: ServerConfigResource, ) { super(() => null); + this.handleSessionStateEvent = this.handleSessionStateEvent.bind(this); + this.onStatusUpdate = new SyncExecutor(); - sessionInfoEventHandler.onEvent( - ServerEventId.CbSessionState, - event => { - if (this.data) { - this.data.valid = event.isValid ?? this.data.valid; - this.data.remainingTime = event.remainingTime; - // TODO: probably we want to call here this.dataUpdate - } - this.onStatusUpdate.execute(event); - }, - undefined, - this, - ); + sessionInfoEventHandler.onEvent(ServerEventId.CbSessionState, this.handleSessionStateEvent, undefined, this); this.action = null; this.sync( @@ -73,6 +58,25 @@ export class SessionResource extends CachedDataResource { this.defaultLocale = defaultLocale; } + private handleSessionStateEvent(event: ISessionStateEvent) { + if (!this.data) { + return; + } + + const sessionState: SessionState = { + ...this.data, + valid: event?.isValid ?? this.data?.valid, + remainingTime: event.remainingTime, + actionParameters: event.actionParameters, + cacheExpired: event?.isCacheExpired ?? this.data.cacheExpired, + lastAccessTime: String(event.lastAccessTime), + locale: event.locale, + }; + + this.setData(sessionState); + this.onStatusUpdate.execute(sessionState); + } + async changeLanguage(locale: string): Promise { if (this.data?.locale === locale) { return; @@ -93,16 +97,12 @@ export class SessionResource extends CachedDataResource { return session; } - async updateSession() { + updateSession() { if (!this.data?.valid) { return; } - const { updateSession } = await this.graphQLService.sdk.updateSession(); - - this.setData(updateSession); - - return updateSession; + this.sessionInfoEventHandler.updateSession(); } protected setData(data: SessionState | null) { diff --git a/webapp/packages/plugin-session-expiration/src/SessionExpireWarningDialog/SessionExpireWarningDialogBootstrap.ts b/webapp/packages/plugin-session-expiration/src/SessionExpireWarningDialog/SessionExpireWarningDialogBootstrap.ts index 7567820fb4..b870daf183 100644 --- a/webapp/packages/plugin-session-expiration/src/SessionExpireWarningDialog/SessionExpireWarningDialogBootstrap.ts +++ b/webapp/packages/plugin-session-expiration/src/SessionExpireWarningDialog/SessionExpireWarningDialogBootstrap.ts @@ -9,7 +9,6 @@ import { UserInfoResource } from '@cloudbeaver/core-authentication'; import { importLazyComponent } from '@cloudbeaver/core-blocks'; import { Bootstrap, injectable } from '@cloudbeaver/core-di'; import { CommonDialogService, DialogueStateResult } from '@cloudbeaver/core-dialogs'; -import { NotificationService } from '@cloudbeaver/core-events'; import { ServerConfigResource, SESSION_EXPIRE_MIN_TIME, SessionExpireService, SessionResource } from '@cloudbeaver/core-root'; import { GraphQLService } from '@cloudbeaver/core-sdk'; @@ -24,7 +23,6 @@ export class SessionExpireWarningDialogBootstrap extends Bootstrap { private readonly sessionResource: SessionResource, private readonly userInfoResource: UserInfoResource, private readonly graphQLService: GraphQLService, - private readonly notificationService: NotificationService, ) { super(); this.dialogInternalPromise = null; @@ -33,7 +31,7 @@ export class SessionExpireWarningDialogBootstrap extends Bootstrap { register(): void { this.sessionExpireService.onSessionExpire.addHandler(this.close.bind(this)); this.sessionResource.onStatusUpdate.addHandler((data, contexts) => { - this.handleStateChange(data.isValid, data.remainingTime); + this.handleStateChange(data.valid, data.remainingTime); }); } @@ -74,11 +72,7 @@ export class SessionExpireWarningDialogBootstrap extends Bootstrap { const { sessionState } = await this.graphQLService.sdk.sessionState(); if (sessionState.valid) { - try { - await this.sessionResource.updateSession(); - } catch (e: any) { - this.notificationService.logException(e, 'plugin_session_expiration_session_update_error'); - } + this.sessionResource.updateSession(); } else { this.sessionExpireService.sessionExpired(); } diff --git a/webapp/packages/plugin-session-expiration/src/locales/en.ts b/webapp/packages/plugin-session-expiration/src/locales/en.ts index 8c94cf3aff..d6d1738de6 100644 --- a/webapp/packages/plugin-session-expiration/src/locales/en.ts +++ b/webapp/packages/plugin-session-expiration/src/locales/en.ts @@ -1 +1 @@ -export default [['plugin_session_expiration_session_update_error', 'Session update failed']]; +export default []; diff --git a/webapp/packages/plugin-session-expiration/src/locales/it.ts b/webapp/packages/plugin-session-expiration/src/locales/it.ts index 8c94cf3aff..d6d1738de6 100644 --- a/webapp/packages/plugin-session-expiration/src/locales/it.ts +++ b/webapp/packages/plugin-session-expiration/src/locales/it.ts @@ -1 +1 @@ -export default [['plugin_session_expiration_session_update_error', 'Session update failed']]; +export default []; diff --git a/webapp/packages/plugin-session-expiration/src/locales/ru.ts b/webapp/packages/plugin-session-expiration/src/locales/ru.ts index 4404022cc8..d6d1738de6 100644 --- a/webapp/packages/plugin-session-expiration/src/locales/ru.ts +++ b/webapp/packages/plugin-session-expiration/src/locales/ru.ts @@ -1 +1 @@ -export default [['plugin_session_expiration_session_update_error', 'Не удалось обновить сессию']]; +export default []; diff --git a/webapp/packages/plugin-session-expiration/src/locales/zh.ts b/webapp/packages/plugin-session-expiration/src/locales/zh.ts index 8c94cf3aff..d6d1738de6 100644 --- a/webapp/packages/plugin-session-expiration/src/locales/zh.ts +++ b/webapp/packages/plugin-session-expiration/src/locales/zh.ts @@ -1 +1 @@ -export default [['plugin_session_expiration_session_update_error', 'Session update failed']]; +export default []; From 1f6ba779986aac9fba4886c37150bc99f1cfd198 Mon Sep 17 00:00:00 2001 From: "s.teleshev" Date: Mon, 18 Mar 2024 19:03:27 +0100 Subject: [PATCH 08/16] CB-4686 fix: do not open websocket if session is expired --- .../core-root/src/SessionEventSource.ts | 26 +++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/webapp/packages/core-root/src/SessionEventSource.ts b/webapp/packages/core-root/src/SessionEventSource.ts index 24c375a708..f46abcff3f 100644 --- a/webapp/packages/core-root/src/SessionEventSource.ts +++ b/webapp/packages/core-root/src/SessionEventSource.ts @@ -76,10 +76,12 @@ export class SessionEventSource implements IServerEventEmitter { this.onInit.execute(); + this.disconnected = false; }); this.closeSubject.subscribe(event => { console.info(`Websocket closed: ${event.reason}`); + this.disconnect(); }); this.eventsSubject = merge(this.oldEventsSubject, this.subject); @@ -120,8 +122,8 @@ export class SessionEventSource implements IServerEventEmitter(topicId: SessionEventTopic, mapTo: (event: ISessionEvent) => T = e => e as T): Observable { return merge( this.subject.multiplex( - () => ({ id: ClientEventId.CbClientTopicSubscribe, topicId } as ITopicSubEvent), - () => ({ id: ClientEventId.CbClientTopicUnsubscribe, topicId } as ITopicSubEvent), + () => ({ id: ClientEventId.CbClientTopicSubscribe, topicId }) as ITopicSubEvent, + () => ({ id: ClientEventId.CbClientTopicUnsubscribe, topicId }) as ITopicSubEvent, event => event.topicId === topicId, ), this.oldEventsSubject, @@ -135,6 +137,26 @@ export class SessionEventSource implements IServerEventEmitter Date: Tue, 19 Mar 2024 11:01:26 +0100 Subject: [PATCH 09/16] Revert "CB-4686 fix: do not open websocket if session is expired" This reverts commit 1f6ba779986aac9fba4886c37150bc99f1cfd198. --- .../core-root/src/SessionEventSource.ts | 26 ++----------------- 1 file changed, 2 insertions(+), 24 deletions(-) diff --git a/webapp/packages/core-root/src/SessionEventSource.ts b/webapp/packages/core-root/src/SessionEventSource.ts index f46abcff3f..24c375a708 100644 --- a/webapp/packages/core-root/src/SessionEventSource.ts +++ b/webapp/packages/core-root/src/SessionEventSource.ts @@ -76,12 +76,10 @@ export class SessionEventSource implements IServerEventEmitter { this.onInit.execute(); - this.disconnected = false; }); this.closeSubject.subscribe(event => { console.info(`Websocket closed: ${event.reason}`); - this.disconnect(); }); this.eventsSubject = merge(this.oldEventsSubject, this.subject); @@ -122,8 +120,8 @@ export class SessionEventSource implements IServerEventEmitter(topicId: SessionEventTopic, mapTo: (event: ISessionEvent) => T = e => e as T): Observable { return merge( this.subject.multiplex( - () => ({ id: ClientEventId.CbClientTopicSubscribe, topicId }) as ITopicSubEvent, - () => ({ id: ClientEventId.CbClientTopicUnsubscribe, topicId }) as ITopicSubEvent, + () => ({ id: ClientEventId.CbClientTopicSubscribe, topicId } as ITopicSubEvent), + () => ({ id: ClientEventId.CbClientTopicUnsubscribe, topicId } as ITopicSubEvent), event => event.topicId === topicId, ), this.oldEventsSubject, @@ -137,26 +135,6 @@ export class SessionEventSource implements IServerEventEmitter Date: Tue, 19 Mar 2024 11:15:24 +0100 Subject: [PATCH 10/16] CB-4686 setData uses performUpdate for the SessionResource --- .../src/Resource/CachedResource.ts | 22 +++++++++++++------ .../packages/core-root/src/SessionResource.ts | 6 +++-- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/webapp/packages/core-resource/src/Resource/CachedResource.ts b/webapp/packages/core-resource/src/Resource/CachedResource.ts index 25e2e720b0..811526aced 100644 --- a/webapp/packages/core-resource/src/Resource/CachedResource.ts +++ b/webapp/packages/core-resource/src/Resource/CachedResource.ts @@ -110,13 +110,16 @@ export abstract class CachedResource< clear: action, }); - setInterval(() => { - // mark resource outdate when it's not used - if (!this.useTracker.isResourceInUse && !this.isOutdated()) { - this.logger.log('not in use'); - this.markOutdated(); - } - }, 5 * 60 * 1000); + setInterval( + () => { + // mark resource outdate when it's not used + if (!this.useTracker.isResourceInUse && !this.isOutdated()) { + this.logger.log('not in use'); + this.markOutdated(); + } + }, + 5 * 60 * 1000, + ); } /** @@ -517,6 +520,11 @@ export abstract class CachedResource< this.setData(this.defaultValue()); } + /** + * Sets data to the resource. Forbidden to use outside of the loader or performUpdate functions! + * @param data - new data + * @returns {void} + */ protected setData(data: TData): void { this.data = data; } diff --git a/webapp/packages/core-root/src/SessionResource.ts b/webapp/packages/core-root/src/SessionResource.ts index 2030feb662..3474af9f33 100644 --- a/webapp/packages/core-root/src/SessionResource.ts +++ b/webapp/packages/core-root/src/SessionResource.ts @@ -73,8 +73,10 @@ export class SessionResource extends CachedDataResource { locale: event.locale, }; - this.setData(sessionState); - this.onStatusUpdate.execute(sessionState); + this.performUpdate(undefined, [], async () => { + this.setData(sessionState); + this.onStatusUpdate.execute(sessionState); + }); } async changeLanguage(locale: string): Promise { From 7aeffffc6705abb89eca3638cb3e9c7443f4404c Mon Sep 17 00:00:00 2001 From: "s.teleshev" Date: Tue, 19 Mar 2024 11:48:55 +0100 Subject: [PATCH 11/16] CB-4686 unsubscribes from the websocket when session expired event arrived --- .../core-root/src/SessionEventSource.ts | 48 ++++++++++++++++--- .../src/SessionExpireEventService.ts | 6 ++- 2 files changed, 47 insertions(+), 7 deletions(-) diff --git a/webapp/packages/core-root/src/SessionEventSource.ts b/webapp/packages/core-root/src/SessionEventSource.ts index 24c375a708..ad54c1578c 100644 --- a/webapp/packages/core-root/src/SessionEventSource.ts +++ b/webapp/packages/core-root/src/SessionEventSource.ts @@ -5,7 +5,21 @@ * Licensed under the Apache License, Version 2.0. * you may not use this file except in compliance with the License. */ -import { catchError, debounceTime, filter, interval, map, merge, Observable, repeat, retry, share, Subject, throwError } from 'rxjs'; +import { + catchError, + debounceTime, + filter, + interval, + map, + merge, + Observable, + repeat, + retry, + share, + Subject, + SubscriptionLike, + throwError, +} from 'rxjs'; import { webSocket, WebSocketSubject } from 'rxjs/webSocket'; import { injectable } from '@cloudbeaver/core-di'; @@ -53,6 +67,10 @@ export class SessionEventSource implements IServerEventEmitter; private disconnected: boolean; + private readonly closeSubjectSubscription: SubscriptionLike; + private readonly openSubjectSubscription: SubscriptionLike; + private readonly errorSubjectSubscription: SubscriptionLike; + constructor( private readonly networkStateService: NetworkStateService, private readonly sessionExpireService: SessionExpireService, @@ -74,23 +92,41 @@ export class SessionEventSource implements IServerEventEmitter { + this.openSubjectSubscription = this.openSubject.subscribe(() => { this.onInit.execute(); }); - this.closeSubject.subscribe(event => { + this.closeSubjectSubscription = this.closeSubject.subscribe(event => { console.info(`Websocket closed: ${event.reason}`); }); this.eventsSubject = merge(this.oldEventsSubject, this.subject); - this.errorSubject.pipe(debounceTime(1000)).subscribe(error => { + this.errorSubjectSubscription = this.errorSubject.pipe(debounceTime(1000)).subscribe(error => { console.error(error); }); this.errorHandler = this.errorHandler.bind(this); } + unsubscribe() { + if (!this.subject.closed) { + this.subject.unsubscribe(); + } + + if (!this.closeSubjectSubscription.closed) { + this.closeSubjectSubscription.unsubscribe(); + } + + if (!this.openSubjectSubscription.closed) { + this.openSubjectSubscription.unsubscribe(); + } + + if (!this.errorSubjectSubscription.closed) { + this.errorSubjectSubscription.unsubscribe(); + } + } + onEvent(id: SessionEventId, callback: IServerEventCallback, mapTo: (event: ISessionEvent) => T = e => e as T): Subscription { const sub = this.eventsSubject .pipe( @@ -120,8 +156,8 @@ export class SessionEventSource implements IServerEventEmitter(topicId: SessionEventTopic, mapTo: (event: ISessionEvent) => T = e => e as T): Observable { return merge( this.subject.multiplex( - () => ({ id: ClientEventId.CbClientTopicSubscribe, topicId } as ITopicSubEvent), - () => ({ id: ClientEventId.CbClientTopicUnsubscribe, topicId } as ITopicSubEvent), + () => ({ id: ClientEventId.CbClientTopicSubscribe, topicId }) as ITopicSubEvent, + () => ({ id: ClientEventId.CbClientTopicUnsubscribe, topicId }) as ITopicSubEvent, event => event.topicId === topicId, ), this.oldEventsSubject, diff --git a/webapp/packages/core-root/src/SessionExpireEventService.ts b/webapp/packages/core-root/src/SessionExpireEventService.ts index b2ea73f0d3..77665a19dc 100644 --- a/webapp/packages/core-root/src/SessionExpireEventService.ts +++ b/webapp/packages/core-root/src/SessionExpireEventService.ts @@ -13,7 +13,10 @@ import { SessionExpireService } from './SessionExpireService'; @injectable() export class SessionExpireEventService extends Dependency { - constructor(private readonly sessionEventSource: SessionEventSource, private readonly sessionExpireService: SessionExpireService) { + constructor( + private readonly sessionEventSource: SessionEventSource, + private readonly sessionExpireService: SessionExpireService, + ) { super(); this.sessionEventSource.onEvent(ServerEventId.CbSessionExpired, () => { this.onSessionExpireEvent(); @@ -22,5 +25,6 @@ export class SessionExpireEventService extends Dependency { private onSessionExpireEvent(): void { this.sessionExpireService.sessionExpired(); + this.sessionEventSource.unsubscribe(); } } From 9b57c04eb9f1c63e545ca4ed1235ef602e97a13e Mon Sep 17 00:00:00 2001 From: "s.teleshev" Date: Tue, 19 Mar 2024 11:59:57 +0100 Subject: [PATCH 12/16] Revert "CB-4686 unsubscribes from the websocket when session expired event arrived" This reverts commit 7aeffffc6705abb89eca3638cb3e9c7443f4404c. --- .../core-root/src/SessionEventSource.ts | 48 +++---------------- .../src/SessionExpireEventService.ts | 6 +-- 2 files changed, 7 insertions(+), 47 deletions(-) diff --git a/webapp/packages/core-root/src/SessionEventSource.ts b/webapp/packages/core-root/src/SessionEventSource.ts index ad54c1578c..24c375a708 100644 --- a/webapp/packages/core-root/src/SessionEventSource.ts +++ b/webapp/packages/core-root/src/SessionEventSource.ts @@ -5,21 +5,7 @@ * Licensed under the Apache License, Version 2.0. * you may not use this file except in compliance with the License. */ -import { - catchError, - debounceTime, - filter, - interval, - map, - merge, - Observable, - repeat, - retry, - share, - Subject, - SubscriptionLike, - throwError, -} from 'rxjs'; +import { catchError, debounceTime, filter, interval, map, merge, Observable, repeat, retry, share, Subject, throwError } from 'rxjs'; import { webSocket, WebSocketSubject } from 'rxjs/webSocket'; import { injectable } from '@cloudbeaver/core-di'; @@ -67,10 +53,6 @@ export class SessionEventSource implements IServerEventEmitter; private disconnected: boolean; - private readonly closeSubjectSubscription: SubscriptionLike; - private readonly openSubjectSubscription: SubscriptionLike; - private readonly errorSubjectSubscription: SubscriptionLike; - constructor( private readonly networkStateService: NetworkStateService, private readonly sessionExpireService: SessionExpireService, @@ -92,41 +74,23 @@ export class SessionEventSource implements IServerEventEmitter { + this.openSubject.subscribe(() => { this.onInit.execute(); }); - this.closeSubjectSubscription = this.closeSubject.subscribe(event => { + this.closeSubject.subscribe(event => { console.info(`Websocket closed: ${event.reason}`); }); this.eventsSubject = merge(this.oldEventsSubject, this.subject); - this.errorSubjectSubscription = this.errorSubject.pipe(debounceTime(1000)).subscribe(error => { + this.errorSubject.pipe(debounceTime(1000)).subscribe(error => { console.error(error); }); this.errorHandler = this.errorHandler.bind(this); } - unsubscribe() { - if (!this.subject.closed) { - this.subject.unsubscribe(); - } - - if (!this.closeSubjectSubscription.closed) { - this.closeSubjectSubscription.unsubscribe(); - } - - if (!this.openSubjectSubscription.closed) { - this.openSubjectSubscription.unsubscribe(); - } - - if (!this.errorSubjectSubscription.closed) { - this.errorSubjectSubscription.unsubscribe(); - } - } - onEvent(id: SessionEventId, callback: IServerEventCallback, mapTo: (event: ISessionEvent) => T = e => e as T): Subscription { const sub = this.eventsSubject .pipe( @@ -156,8 +120,8 @@ export class SessionEventSource implements IServerEventEmitter(topicId: SessionEventTopic, mapTo: (event: ISessionEvent) => T = e => e as T): Observable { return merge( this.subject.multiplex( - () => ({ id: ClientEventId.CbClientTopicSubscribe, topicId }) as ITopicSubEvent, - () => ({ id: ClientEventId.CbClientTopicUnsubscribe, topicId }) as ITopicSubEvent, + () => ({ id: ClientEventId.CbClientTopicSubscribe, topicId } as ITopicSubEvent), + () => ({ id: ClientEventId.CbClientTopicUnsubscribe, topicId } as ITopicSubEvent), event => event.topicId === topicId, ), this.oldEventsSubject, diff --git a/webapp/packages/core-root/src/SessionExpireEventService.ts b/webapp/packages/core-root/src/SessionExpireEventService.ts index 77665a19dc..b2ea73f0d3 100644 --- a/webapp/packages/core-root/src/SessionExpireEventService.ts +++ b/webapp/packages/core-root/src/SessionExpireEventService.ts @@ -13,10 +13,7 @@ import { SessionExpireService } from './SessionExpireService'; @injectable() export class SessionExpireEventService extends Dependency { - constructor( - private readonly sessionEventSource: SessionEventSource, - private readonly sessionExpireService: SessionExpireService, - ) { + constructor(private readonly sessionEventSource: SessionEventSource, private readonly sessionExpireService: SessionExpireService) { super(); this.sessionEventSource.onEvent(ServerEventId.CbSessionExpired, () => { this.onSessionExpireEvent(); @@ -25,6 +22,5 @@ export class SessionExpireEventService extends Dependency { private onSessionExpireEvent(): void { this.sessionExpireService.sessionExpired(); - this.sessionEventSource.unsubscribe(); } } From f3c65a157dee1b21c332a10903cf9ea24d2b22bd Mon Sep 17 00:00:00 2001 From: Ainur Date: Tue, 19 Mar 2024 15:52:23 +0100 Subject: [PATCH 13/16] CB-4810 rename client event --- .../io.cloudbeaver.server/schema/service.events.graphqls | 2 +- .../src/io/cloudbeaver/server/websockets/CBEventsWebSocket.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/server/bundles/io.cloudbeaver.server/schema/service.events.graphqls b/server/bundles/io.cloudbeaver.server/schema/service.events.graphqls index 2842bbf4ca..3c506edd11 100644 --- a/server/bundles/io.cloudbeaver.server/schema/service.events.graphqls +++ b/server/bundles/io.cloudbeaver.server/schema/service.events.graphqls @@ -38,7 +38,7 @@ enum CBClientEventId { cb_client_topic_subscribe, cb_client_topic_unsubscribe, cb_client_projects_active, - cb_client_touch_session + cb_client_session_ping } # Client subscribes on topic to receive only related events diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/websockets/CBEventsWebSocket.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/websockets/CBEventsWebSocket.java index c3fac62c5c..697011c5d4 100644 --- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/websockets/CBEventsWebSocket.java +++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/websockets/CBEventsWebSocket.java @@ -77,7 +77,7 @@ public void onWebSocketText(String message) { this.webSession.getEventsFilter().setSubscribedProjects(projectEvent.getProjectIds()); break; } - case TOUCH_SESSION: { + case SESSION_PING: { if (webSession instanceof WebSession session) { session.updateInfo(true); } From 09d7a0dc22636782b69aa234e1e6a029c266565d Mon Sep 17 00:00:00 2001 From: "s.teleshev" Date: Tue, 19 Mar 2024 16:20:21 +0100 Subject: [PATCH 14/16] CB-4686 pr fixes --- .../core-root/src/SessionActivityService.ts | 2 +- .../core-root/src/SessionInfoEventHandler.ts | 4 +-- .../packages/core-root/src/SessionResource.ts | 35 +++++++++---------- .../SessionExpireWarningDialogBootstrap.ts | 10 +++--- 4 files changed, 25 insertions(+), 26 deletions(-) diff --git a/webapp/packages/core-root/src/SessionActivityService.ts b/webapp/packages/core-root/src/SessionActivityService.ts index e8c9712cce..ab24782851 100644 --- a/webapp/packages/core-root/src/SessionActivityService.ts +++ b/webapp/packages/core-root/src/SessionActivityService.ts @@ -30,7 +30,7 @@ export class SessionActivityService extends Dependency { return; } - this.sessionResource.updateSession(); + this.sessionResource.pingSession(); this.touchSessionTimer = setTimeout(() => { if (this.touchSessionTimer) { diff --git a/webapp/packages/core-root/src/SessionInfoEventHandler.ts b/webapp/packages/core-root/src/SessionInfoEventHandler.ts index 83b5d340f8..fe80326f87 100644 --- a/webapp/packages/core-root/src/SessionInfoEventHandler.ts +++ b/webapp/packages/core-root/src/SessionInfoEventHandler.ts @@ -19,8 +19,8 @@ export class SessionInfoEventHandler extends TopicEventHandler({ id: ClientEventId.CbClientTouchSession, topicId: CbEventTopic.CbSession }); + pingSession(): void { + this.emit({ id: ClientEventId.CbClientSessionPing, topicId: CbEventTopic.CbSession }); } map(event: any): ISessionStateEvent { diff --git a/webapp/packages/core-root/src/SessionResource.ts b/webapp/packages/core-root/src/SessionResource.ts index 3474af9f33..314150b773 100644 --- a/webapp/packages/core-root/src/SessionResource.ts +++ b/webapp/packages/core-root/src/SessionResource.ts @@ -24,7 +24,6 @@ export interface ISessionAction { export class SessionResource extends CachedDataResource { private action: ISessionAction | null; private defaultLocale: string | undefined; - readonly onStatusUpdate: ISyncExecutor; constructor( private readonly graphQLService: GraphQLService, @@ -35,7 +34,6 @@ export class SessionResource extends CachedDataResource { this.handleSessionStateEvent = this.handleSessionStateEvent.bind(this); - this.onStatusUpdate = new SyncExecutor(); sessionInfoEventHandler.onEvent(ServerEventId.CbSessionState, this.handleSessionStateEvent, undefined, this); this.action = null; @@ -59,23 +57,22 @@ export class SessionResource extends CachedDataResource { } private handleSessionStateEvent(event: ISessionStateEvent) { - if (!this.data) { - return; - } - - const sessionState: SessionState = { - ...this.data, - valid: event?.isValid ?? this.data?.valid, - remainingTime: event.remainingTime, - actionParameters: event.actionParameters, - cacheExpired: event?.isCacheExpired ?? this.data.cacheExpired, - lastAccessTime: String(event.lastAccessTime), - locale: event.locale, - }; - this.performUpdate(undefined, [], async () => { + if (!this.data) { + return; + } + + const sessionState: SessionState = { + ...this.data, + valid: event?.isValid ?? this.data.valid, + remainingTime: event.remainingTime, + actionParameters: event.actionParameters, + cacheExpired: event?.isCacheExpired ?? this.data.cacheExpired, + lastAccessTime: String(event.lastAccessTime), + locale: event.locale, + }; + this.setData(sessionState); - this.onStatusUpdate.execute(sessionState); }); } @@ -99,12 +96,12 @@ export class SessionResource extends CachedDataResource { return session; } - updateSession() { + pingSession() { if (!this.data?.valid) { return; } - this.sessionInfoEventHandler.updateSession(); + this.sessionInfoEventHandler.pingSession(); } protected setData(data: SessionState | null) { diff --git a/webapp/packages/plugin-session-expiration/src/SessionExpireWarningDialog/SessionExpireWarningDialogBootstrap.ts b/webapp/packages/plugin-session-expiration/src/SessionExpireWarningDialog/SessionExpireWarningDialogBootstrap.ts index b870daf183..06ec2a584a 100644 --- a/webapp/packages/plugin-session-expiration/src/SessionExpireWarningDialog/SessionExpireWarningDialogBootstrap.ts +++ b/webapp/packages/plugin-session-expiration/src/SessionExpireWarningDialog/SessionExpireWarningDialogBootstrap.ts @@ -30,14 +30,16 @@ export class SessionExpireWarningDialogBootstrap extends Bootstrap { register(): void { this.sessionExpireService.onSessionExpire.addHandler(this.close.bind(this)); - this.sessionResource.onStatusUpdate.addHandler((data, contexts) => { - this.handleStateChange(data.valid, data.remainingTime); + this.sessionResource.onDataUpdate.addHandler(() => { + const { valid, remainingTime } = this.sessionResource.data || {}; + + this.handleSessionResourceDataUpdate(valid, remainingTime); }); } load(): void {} - private handleStateChange(isValid?: boolean, remainingTime?: number) { + private handleSessionResourceDataUpdate(isValid?: boolean, remainingTime?: number) { if (!this.serverConfigResource.anonymousAccessEnabled && !this.userInfoResource.data && !this.serverConfigResource.configurationMode) { return; } @@ -72,7 +74,7 @@ export class SessionExpireWarningDialogBootstrap extends Bootstrap { const { sessionState } = await this.graphQLService.sdk.sessionState(); if (sessionState.valid) { - this.sessionResource.updateSession(); + this.sessionResource.pingSession(); } else { this.sessionExpireService.sessionExpired(); } From 0530eb44928c04bb9915b571c41c75582f3f2ad7 Mon Sep 17 00:00:00 2001 From: "s.teleshev" Date: Wed, 20 Mar 2024 15:23:40 +0100 Subject: [PATCH 15/16] CB-4686 makes onDataOutdated execution manual --- .../core-authentication/src/AuthConfigurationsResource.ts | 6 +++++- .../packages/core-authentication/src/UserInfoResource.ts | 4 ++++ webapp/packages/core-authentication/src/UsersResource.ts | 2 ++ .../ConnectionExecutionContextResource.ts | 7 ++++++- .../core-connections/src/ConnectionFolderResource.ts | 7 ++++++- .../core-connections/src/ConnectionInfoResource.ts | 7 +++++++ .../src/NodesManager/NavTreeResource.ts | 6 +++++- .../core-resource-manager/src/ResourceManagerResource.ts | 3 +++ .../core-resource-manager/src/SharedProjectsResource.ts | 2 ++ .../packages/core-resource/src/Resource/CachedResource.ts | 1 - webapp/packages/core-root/src/ServerConfigResource.ts | 4 ++++ 11 files changed, 44 insertions(+), 5 deletions(-) diff --git a/webapp/packages/core-authentication/src/AuthConfigurationsResource.ts b/webapp/packages/core-authentication/src/AuthConfigurationsResource.ts index be4acb05d7..14960db42b 100644 --- a/webapp/packages/core-authentication/src/AuthConfigurationsResource.ts +++ b/webapp/packages/core-authentication/src/AuthConfigurationsResource.ts @@ -30,7 +30,10 @@ type NewConfiguration = AuthConfiguration & { [NEW_CONFIGURATION_SYMBOL]: boolea @injectable() export class AuthConfigurationsResource extends CachedMapResource { - constructor(private readonly graphQLService: GraphQLService, permissionsResource: SessionPermissionsResource) { + constructor( + private readonly graphQLService: GraphQLService, + permissionsResource: SessionPermissionsResource, + ) { super(() => new Map(), []); permissionsResource.require(this, EAdminPermission.admin).outdateResource(this); @@ -51,6 +54,7 @@ export class AuthConfigurationsResource extends CachedMapResource { - return await this.performUpdate(getContextBaseId(key, ''), [], async () => { + const contextKey = getContextBaseId(key, ''); + return await this.performUpdate(contextKey, [], async () => { const { context } = await this.graphQLService.sdk.executionContextCreate({ ...key, defaultCatalog, @@ -85,6 +86,8 @@ export class ConnectionExecutionContextResource extends CachedMapResource { diff --git a/webapp/packages/core-connections/src/ConnectionFolderResource.ts b/webapp/packages/core-connections/src/ConnectionFolderResource.ts index 5b25873085..63bbab8571 100644 --- a/webapp/packages/core-connections/src/ConnectionFolderResource.ts +++ b/webapp/packages/core-connections/src/ConnectionFolderResource.ts @@ -37,7 +37,11 @@ export const ConnectionFolderProjectKey = resourceKeyAliasFactory('@connection-f @injectable() export class ConnectionFolderResource extends CachedMapResource { - constructor(private readonly graphQLService: GraphQLService, sessionDataResource: SessionDataResource, appAuthService: AppAuthService) { + constructor( + private readonly graphQLService: GraphQLService, + sessionDataResource: SessionDataResource, + appAuthService: AppAuthService, + ) { super(); appAuthService.requireAuthentication(this); @@ -70,6 +74,7 @@ export class ConnectionFolderResource extends CachedMapResource { await this.performUpdate(key, [], async () => { await this.graphQLService.sdk.deleteConnection({ projectId: key.projectId, connectionId: key.connectionId }); + this.onDataOutdated.execute(key); }); this.delete(key); }); diff --git a/webapp/packages/core-navigation-tree/src/NodesManager/NavTreeResource.ts b/webapp/packages/core-navigation-tree/src/NodesManager/NavTreeResource.ts index dfad4051f6..92edcdb250 100644 --- a/webapp/packages/core-navigation-tree/src/NodesManager/NavTreeResource.ts +++ b/webapp/packages/core-navigation-tree/src/NodesManager/NavTreeResource.ts @@ -193,6 +193,7 @@ export class NavTreeResource extends CachedMapResource { const deletedNodes: string[] = []; @@ -223,8 +224,9 @@ export class NavTreeResource extends CachedMapResource, target: string): Promise { const parents = Array.from(new Set(ResourceKeyUtils.mapArray(key, key => this.navNodeInfoResource.get(key)?.parentId).filter(isDefined))); + const parentsKey = resourceKeyList(parents); - await this.performUpdate(resourceKeyList(parents), [], async () => { + await this.performUpdate(parentsKey, [], async () => { this.markLoading(target, true); try { @@ -235,6 +237,7 @@ export class NavTreeResource extends CachedMapResource 0) { diff --git a/webapp/packages/core-resource/src/Resource/CachedResource.ts b/webapp/packages/core-resource/src/Resource/CachedResource.ts index 811526aced..3310a353fd 100644 --- a/webapp/packages/core-resource/src/Resource/CachedResource.ts +++ b/webapp/packages/core-resource/src/Resource/CachedResource.ts @@ -659,7 +659,6 @@ export abstract class CachedResource< { success: () => { if (loaded) { - this.onDataOutdated.execute(key); // TODO: probably need to remove, we need to notify any related resources that subscribed to .onOutdate, to recursively outdate them this.dataUpdate(key); } }, diff --git a/webapp/packages/core-root/src/ServerConfigResource.ts b/webapp/packages/core-root/src/ServerConfigResource.ts index c4f1dd2fc3..840a5781d6 100644 --- a/webapp/packages/core-root/src/ServerConfigResource.ts +++ b/webapp/packages/core-root/src/ServerConfigResource.ts @@ -232,6 +232,7 @@ export class ServerConfigResource extends CachedDataResource { await this.graphQLService.sdk.updateProductConfiguration({ configuration }); this.setData(await this.loader()); + this.onDataOutdated.execute(); }); } @@ -256,6 +257,8 @@ export class ServerConfigResource extends CachedDataResource !this.isNavigatorSettingsChanged() && (!this.isChanged() || skipConfigUpdate), ); @@ -271,6 +274,7 @@ export class ServerConfigResource extends CachedDataResource !this.isChanged() && !onlyRestart, ); From d247b23061682da240a9ba80d33cabbf734dc22a Mon Sep 17 00:00:00 2001 From: "s.teleshev" Date: Thu, 21 Mar 2024 11:10:35 +0100 Subject: [PATCH 16/16] CB-4686 fix: onDataOutdated.execute executes in the end of the performUpdate --- .../ConnectionExecutionContextResource.ts | 3 ++- webapp/packages/core-connections/src/ConnectionInfoResource.ts | 2 +- .../core-navigation-tree/src/NodesManager/NavTreeResource.ts | 3 ++- webapp/packages/core-resource/src/Resource/CachedResource.ts | 1 + webapp/packages/core-root/src/SessionResource.ts | 1 - 5 files changed, 6 insertions(+), 4 deletions(-) diff --git a/webapp/packages/core-connections/src/ConnectionExecutionContext/ConnectionExecutionContextResource.ts b/webapp/packages/core-connections/src/ConnectionExecutionContext/ConnectionExecutionContextResource.ts index 59e6b772e7..ad12575d22 100644 --- a/webapp/packages/core-connections/src/ConnectionExecutionContext/ConnectionExecutionContextResource.ts +++ b/webapp/packages/core-connections/src/ConnectionExecutionContext/ConnectionExecutionContextResource.ts @@ -86,9 +86,10 @@ export class ConnectionExecutionContextResource extends CachedMapResource { await this.performUpdate(key, [], async () => { await this.graphQLService.sdk.deleteConnection({ projectId: key.projectId, connectionId: key.connectionId }); - this.onDataOutdated.execute(key); }); this.delete(key); }); + this.onDataOutdated.execute(key); } // async updateSessionConnections(): Promise { diff --git a/webapp/packages/core-navigation-tree/src/NodesManager/NavTreeResource.ts b/webapp/packages/core-navigation-tree/src/NodesManager/NavTreeResource.ts index 92edcdb250..566b9f30f0 100644 --- a/webapp/packages/core-navigation-tree/src/NodesManager/NavTreeResource.ts +++ b/webapp/packages/core-navigation-tree/src/NodesManager/NavTreeResource.ts @@ -272,11 +272,12 @@ export class NavTreeResource extends CachedMapResource