CB-5933 Connection state is not synchronized with other resources triggering connection state (#3119)

* 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 <denis.sinelnikov@dbeaver.com>
Co-authored-by: Evgenia <139753579+EvgeniaBzzz@users.noreply.github.com>
This commit is contained in:
sergeyteleshev
2024-12-12 11:27:12 +01:00
committed by GitHub
co-authored by denis.sinelnikov Evgenia
parent 02e6b9c6de
commit 6830e34b2a
6 changed files with 103 additions and 9 deletions
@@ -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
}
@@ -23,6 +23,7 @@ import org.jkiss.code.NotNull;
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 @@ public class WebDataSourceMonitorJob extends DataSourceMonitorJob {
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()
)
);
}
}
}
@@ -55,6 +55,7 @@ import org.jkiss.dbeaver.model.runtime.DBRProgressMonitor;
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 class WebServiceCore implements DBWServiceCore {
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 {
@@ -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<IConnectionInfoPar
sessionDataResource: SessionDataResource,
appAuthService: AppAuthService,
connectionInfoEventHandler: ConnectionInfoEventHandler,
connectionStateEventHandler: ConnectionStateEventHandler,
userInfoResource: UserInfoResource,
) {
super();
@@ -163,6 +165,38 @@ export class ConnectionInfoResource extends CachedMapResource<IConnectionInfoPar
this,
);
connectionStateEventHandler.onEvent<IWsDataSourceDisconnectEvent>(
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<IWsDataSourceConnectEvent>(
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<ResourceKeyList<IConnectionInfoParams>>(
ServerEventId.CbDatasourceUpdated,
key => {
@@ -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<ConnectionStateEvent, ISessionEvent, SessionEventId, SessionEventTopic> {
constructor(sessionEventSource: SessionEventSource) {
super(SessionEventTopic.CbDatasourceConnection, sessionEventSource);
}
map(event: any): ConnectionStateEvent {
return event;
}
}
@@ -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),
],
};