From 6830e34b2a2efc12c6ea983948bcc53ce63d42cf Mon Sep 17 00:00:00 2001 From: sergeyteleshev Date: Thu, 12 Dec 2024 11:27:12 +0100 Subject: [PATCH] CB-5933 Connection state is not synchronized with other resources triggering connection state (#3119) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * CB-5933. Added event for disconnect datasource * CB-5933. Added to gql * CB-5933 adds handlers for disconnected/connected events * CB-5933. Rename topic * CB-5933. Rename topic * CB-5933. Added event for connect to database * СB-5933 adds ConnectionDisconnectEventHandler * CB-5933. Fixed typo * CB-5933 fixes disconnect handler + adds connect handler for connections * CB-5933 fix for connected event * СB-5933 marks outdated connected/disconnected connections on event handling * CB-5933 cleanup * CB-5933 pr fixes * CB-5933 pr fixes * CB-5933. Refactor after review --------- Co-authored-by: denis.sinelnikov Co-authored-by: Evgenia <139753579+EvgeniaBzzz@users.noreply.github.com> --- .../schema/service.events.graphqls | 21 ++++++++++++ .../server/jobs/WebDataSourceMonitorJob.java | 17 +++++----- .../service/core/impl/WebServiceCore.java | 13 ++++++- .../src/ConnectionInfoResource.ts | 34 +++++++++++++++++++ .../src/ConnectionStateEventHandler.ts | 26 ++++++++++++++ .../packages/core-connections/src/manifest.ts | 1 + 6 files changed, 103 insertions(+), 9 deletions(-) create mode 100644 webapp/packages/core-connections/src/ConnectionStateEventHandler.ts diff --git a/server/bundles/io.cloudbeaver.server/schema/service.events.graphqls b/server/bundles/io.cloudbeaver.server/schema/service.events.graphqls index 3c506edd11..a970eae7f4 100644 --- a/server/bundles/io.cloudbeaver.server/schema/service.events.graphqls +++ b/server/bundles/io.cloudbeaver.server/schema/service.events.graphqls @@ -20,6 +20,9 @@ enum CBServerEventId { cb_datasource_folder_updated, cb_datasource_folder_deleted, + cb_datasource_disconnected, + cb_datasource_connected, + cb_rm_resource_created, cb_rm_resource_updated, cb_rm_resource_deleted, @@ -53,6 +56,7 @@ enum CBEventTopic { cb_object_permissions, cb_subject_permissions, cb_database_output_log, + cb_datasource_connection, cb_delete_temp_folder } @@ -178,6 +182,23 @@ type WSOutputLogInfo { # Add more fields as needed } +# Datasource disconnect event +type WSDataSourceDisconnectEvent implements CBServerEvent { + id: CBServerEventId! + topicId: CBEventTopic + connectionId: String! + projectId: String! + timestamp: Int! +} +# Datasource connect event +type WSDataSourceConnectEvent implements CBServerEvent { + id: CBServerEventId! + topicId: CBEventTopic + connectionId: String! + projectId: String! + timestamp: Int! +} + extend type Query { emptyEvent: Boolean } diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/jobs/WebDataSourceMonitorJob.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/jobs/WebDataSourceMonitorJob.java index 183acc805f..3eb1ab6e1a 100644 --- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/jobs/WebDataSourceMonitorJob.java +++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/jobs/WebDataSourceMonitorJob.java @@ -23,6 +23,7 @@ import org.jkiss.dbeaver.model.DBPDataSource; import org.jkiss.dbeaver.model.app.DBPPlatform; import org.jkiss.dbeaver.model.app.DBPProject; +import org.jkiss.dbeaver.model.websocket.event.datasource.WSDataSourceDisconnectEvent; import org.jkiss.dbeaver.model.websocket.event.datasource.WSDataSourceEvent; import org.jkiss.dbeaver.model.websocket.event.datasource.WSDataSourceProperty; import org.jkiss.dbeaver.runtime.jobs.DataSourceMonitorJob; @@ -57,14 +58,14 @@ protected void doJob() { protected void showNotification(@NotNull DBPDataSource dataSource) { final DBPProject project = dataSource.getContainer().getProject(); if (project.getWorkspaceSession() instanceof WebSession webSession) { - // TODO: Add new event for disconnect datasource - webSession.addSessionEvent(WSDataSourceEvent.update( - webSession.getSessionId(), - webSession.getUserId(), - project.getId(), - List.of(dataSource.getContainer().getId()), - WSDataSourceProperty.CONFIGURATION - )); + webSession.addSessionEvent( + new WSDataSourceDisconnectEvent( + project.getId(), + dataSource.getContainer().getId(), + webSession.getSessionId(), + webSession.getUserId() + ) + ); } } } 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 60f4edbb33..e9e78767bb 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 @@ -55,6 +55,7 @@ import org.jkiss.dbeaver.model.secret.DBSSecretController; import org.jkiss.dbeaver.model.secret.DBSSecretValue; import org.jkiss.dbeaver.model.websocket.WSConstants; +import org.jkiss.dbeaver.model.websocket.event.datasource.WSDataSourceConnectEvent; import org.jkiss.dbeaver.model.websocket.event.datasource.WSDataSourceProperty; import org.jkiss.dbeaver.registry.DataSourceDescriptor; import org.jkiss.dbeaver.registry.DataSourceProviderRegistry; @@ -366,7 +367,17 @@ public WebConnectionInfo initConnection( boolean oldSavePassword = dataSourceContainer.isSavePassword(); try { - dataSourceContainer.connect(webSession.getProgressMonitor(), true, false); + boolean connect = dataSourceContainer.connect(webSession.getProgressMonitor(), true, false); + if (connect) { + webSession.addSessionEvent( + new WSDataSourceConnectEvent( + projectId, + connectionId, + webSession.getSessionId(), + webSession.getUserId() + ) + ); + } } catch (Exception e) { throw new DBWebException("Error connecting to database", e); } finally { diff --git a/webapp/packages/core-connections/src/ConnectionInfoResource.ts b/webapp/packages/core-connections/src/ConnectionInfoResource.ts index fb975d1612..5692c2fc0a 100644 --- a/webapp/packages/core-connections/src/ConnectionInfoResource.ts +++ b/webapp/packages/core-connections/src/ConnectionInfoResource.ts @@ -40,6 +40,7 @@ import { schemaValidationError } from '@cloudbeaver/core-utils'; import { CONNECTION_INFO_PARAM_SCHEMA, type IConnectionInfoParams } from './CONNECTION_INFO_PARAM_SCHEMA.js'; import { ConnectionInfoEventHandler, type IConnectionInfoEvent } from './ConnectionInfoEventHandler.js'; +import { ConnectionStateEventHandler, type IWsDataSourceConnectEvent, type IWsDataSourceDisconnectEvent } from './ConnectionStateEventHandler.js'; import type { DatabaseConnection } from './DatabaseConnection.js'; import { DBDriverResource } from './DBDriverResource.js'; import { parseConnectionKey } from './parseConnectionKey.js'; @@ -97,6 +98,7 @@ export class ConnectionInfoResource extends CachedMapResource( + ServerEventId.CbDatasourceDisconnected, + async data => { + const key: IConnectionInfoParams = { + projectId: data.projectId, + connectionId: data.connectionId, + }; + + if (this.isConnected(key) && !this.isConnecting(key)) { + this.markOutdated(key); + } + }, + undefined, + this, + ); + + connectionStateEventHandler.onEvent( + ServerEventId.CbDatasourceConnected, + async data => { + const key: IConnectionInfoParams = { + projectId: data.projectId, + connectionId: data.connectionId, + }; + + if (!this.isConnected(key) && !this.isConnecting(key)) { + this.markOutdated(key); + } + }, + undefined, + this, + ); + connectionInfoEventHandler.onEvent>( ServerEventId.CbDatasourceUpdated, key => { diff --git a/webapp/packages/core-connections/src/ConnectionStateEventHandler.ts b/webapp/packages/core-connections/src/ConnectionStateEventHandler.ts new file mode 100644 index 0000000000..41da8d3937 --- /dev/null +++ b/webapp/packages/core-connections/src/ConnectionStateEventHandler.ts @@ -0,0 +1,26 @@ +/* + * CloudBeaver - Cloud Database Manager + * Copyright (C) 2020-2024 DBeaver Corp and others + * + * Licensed under the Apache License, Version 2.0. + * you may not use this file except in compliance with the License. + */ +import { injectable } from '@cloudbeaver/core-di'; +import { type ISessionEvent, type SessionEventId, SessionEventSource, SessionEventTopic, TopicEventHandler } from '@cloudbeaver/core-root'; +import type { WsDataSourceConnectEvent, WsDataSourceDisconnectEvent } from '@cloudbeaver/core-sdk'; + +export type IWsDataSourceDisconnectEvent = WsDataSourceDisconnectEvent; +export type IWsDataSourceConnectEvent = WsDataSourceConnectEvent; + +type ConnectionStateEvent = IWsDataSourceConnectEvent | IWsDataSourceDisconnectEvent; + +@injectable() +export class ConnectionStateEventHandler extends TopicEventHandler { + constructor(sessionEventSource: SessionEventSource) { + super(SessionEventTopic.CbDatasourceConnection, sessionEventSource); + } + + map(event: any): ConnectionStateEvent { + return event; + } +} diff --git a/webapp/packages/core-connections/src/manifest.ts b/webapp/packages/core-connections/src/manifest.ts index d381194433..3bb1690a91 100644 --- a/webapp/packages/core-connections/src/manifest.ts +++ b/webapp/packages/core-connections/src/manifest.ts @@ -33,5 +33,6 @@ export const manifest: PluginManifest = { () => import('./ConnectionFolderEventHandler.js').then(m => m.ConnectionFolderEventHandler), () => import('./ConnectionsSettingsService.js').then(m => m.ConnectionsSettingsService), () => import('./ConnectionPublicSecretsResource.js').then(m => m.ConnectionPublicSecretsResource), + () => import('./ConnectionStateEventHandler.js').then(m => m.ConnectionStateEventHandler), ], };