Merge pull request #114 from dbeaver/refactor-authentication

feat: connection behavior & error message fix
This commit is contained in:
Serge Rider
2020-07-09 22:53:43 +03:00
committed by GitHub
29 changed files with 217 additions and 226 deletions
@@ -15,6 +15,7 @@ import { IExtension } from '@cloudbeaver/core-extensions';
import { NavigationTreeService } from '../../NavigationTree/NavigationTreeService';
import { ConnectionsManagerService } from '../../shared/ConnectionsManager/ConnectionsManagerService';
import { ObjectContainer } from '../../shared/ConnectionsManager/ContainerResource';
import { DBDriverResource } from '../../shared/ConnectionsManager/DBDriverResource';
import { isConnectionProvider, IConnectionProvider } from '../../shared/ConnectionsManager/extensions/IConnectionProvider';
import { isConnectionSetter, IConnectionSetter } from '../../shared/ConnectionsManager/extensions/IConnectionSetter';
import { ITab } from '../../shared/NavigationTabs/ITab';
@@ -117,6 +118,7 @@ export class ConnectionSchemaManagerService {
constructor(
private navigationTabsService: NavigationTabsService,
private connectionsManagerService: ConnectionsManagerService,
private dbDriverResource: DBDriverResource,
private notificationService: NotificationService,
private navigationTreeService: NavigationTreeService,
private navNodeExtensionsService: NavNodeExtensionsService,
@@ -185,7 +187,7 @@ export class ConnectionSchemaManagerService {
}
try {
await this.connectionsManagerService.dbDrivers.load('');
await this.dbDriverResource.loadAll();
} catch (exception) {
this.notificationService.logException(exception, 'Can\'t load database drivers', true);
}
@@ -13,6 +13,7 @@ import { ComputedMenuItemModel, ComputedMenuPanelModel, IMenuItem } from '@cloud
import { Connection } from '../../../shared/ConnectionsManager/ConnectionInfoResource';
import { ConnectionsManagerService } from '../../../shared/ConnectionsManager/ConnectionsManagerService';
import { DBDriverResource } from '../../../shared/ConnectionsManager/DBDriverResource';
import { EObjectFeature } from '../../../shared/NodesManager/EObjectFeature';
import { NodeManagerUtils } from '../../../shared/NodesManager/NodeManagerUtils';
import { ConnectionSchemaManagerService } from '../ConnectionSchemaManagerService';
@@ -37,7 +38,7 @@ export class ConnectionSelectorController {
if (!this.currentConnection) {
return;
}
const driverIcon = this.connectionsManagerService.dbDrivers.data.get(this.currentConnection.driverId)?.icon;
const driverIcon = this.dbDriverResource.get(this.currentConnection.driverId)?.icon;
return driverIcon;
}
@@ -75,6 +76,7 @@ export class ConnectionSelectorController {
constructor(
private connectionSelectorService: ConnectionSchemaManagerService,
private connectionsManagerService: ConnectionsManagerService,
private dbDriverResource: DBDriverResource,
) {
this.connectionMenu = new ComputedMenuItemModel({
+1
View File
@@ -30,6 +30,7 @@ export * from './shared/NodesManager/NodeManagerUtils';
export * from './shared/Navigation/NavigationService';
export * from './shared/NavigationTabs/NavigationTabsService';
export * from './shared/NavigationTabs/TabNavigationContext';
export * from './shared/ObjectPropertyInfoForm/ObjectPropertyInfoForm';
export * from './shared/ToolsPanel/LogViewTab/LogViewerMenuService';
export * from './shared/ToolsPanel/LogViewTab/LogViewerService';
export * from './NavigationTree/NavigationTreeService';
@@ -26,11 +26,11 @@ export class ConnectionAuthService {
if (!connection?.connected) {
connection = await this.connectionInfoResource.refresh(connectionId);
} else {
return;
return connection;
}
if (connection.connected) {
return;
return connection;
}
if (connection.authNeeded) {
@@ -38,5 +38,7 @@ export class ConnectionAuthService {
} else {
await this.connectionInfoResource.init(connectionId);
}
return this.connectionInfoResource.get(connectionId)!;
}
}
@@ -19,7 +19,6 @@ import { NavNodeManagerService } from '../NodesManager/NavNodeManagerService';
import { NodeManagerUtils } from '../NodesManager/NodeManagerUtils';
import { ConnectionInfoResource, Connection } from './ConnectionInfoResource';
import { ContainerResource, ObjectContainer } from './ContainerResource';
import { DBDriverResource, DBDriver } from './DBDriverResource';
import { EConnectionFeature } from './EConnectionFeature';
export type DBSource = Pick<DataSourceInfo, 'id' | 'name' | 'driverId' | 'description'>
@@ -37,7 +36,6 @@ export class ConnectionsManagerService {
private graphQLService: GraphQLService,
readonly connectionInfo: ConnectionInfoResource,
readonly connectionObjectContainers: ContainerResource,
readonly dbDrivers: DBDriverResource,
private navNodeManagerService: NavNodeManagerService,
private sessionResource: SessionResource,
private notificationService: NotificationService
@@ -45,10 +43,6 @@ export class ConnectionsManagerService {
this.sessionResource.onDataUpdate.subscribe(this.restoreConnections.bind(this));
}
getDBDrivers(): Map<string, DBDriver> {
return this.dbDrivers.data;
}
async loadConnectionInfoAsync(connectionId: string): Promise<Connection> {
return this.connectionInfo.load(connectionId);
}
@@ -57,11 +51,6 @@ export class ConnectionsManagerService {
return this.connectionInfo.refresh(connectionId);
}
async loadDriversAsync(): Promise<Map<string, DBDriver>> {
await this.dbDrivers.load('');
return this.dbDrivers.data;
}
async addOpenedConnection(connection: Connection) {
this.connectionInfo.set(connection.id, connection);
this.onOpenConnection.next(connection);
@@ -6,8 +6,6 @@
* you may not use this file except in compliance with the License.
*/
import { observable } from 'mobx';
import { injectable } from '@cloudbeaver/core-di';
import {
GraphQLService,
@@ -26,20 +24,22 @@ export type DBDriver = Pick<
| 'embedded'
| 'anonymousAccess'
| 'promotedScore'
| 'defaultAuthModel'
>
@injectable()
export class DBDriverResource extends CachedMapResource<string, DBDriver> {
@observable private loaded = false;
constructor(private graphQLService: GraphQLService) {
super(new Map());
}
isLoaded() {
return this.loaded;
async loadAll() {
await this.load('all');
return this.data;
}
protected async loader(param: string): Promise<Map<string, DBDriver>> {
protected async loader(key: string): Promise<Map<string, DBDriver>> {
const { driverList } = await this.graphQLService.gql.driverList();
this.data.clear();
@@ -47,7 +47,8 @@ export class DBDriverResource extends CachedMapResource<string, DBDriver> {
for (const driver of driverList) {
this.data.set(driver.id, driver);
}
this.loaded = true;
this.data.set('all', {} as any);
this.markUpdated(key);
return this.data;
}
}
@@ -15,8 +15,8 @@ import { CommonDialogWrapper, DialogComponentProps } from '@cloudbeaver/core-dia
import { useTranslate } from '@cloudbeaver/core-localization';
import { composes, useStyles } from '@cloudbeaver/core-theming';
import { ObjectPropertyInfoForm } from '../../ObjectPropertyInfoForm/ObjectPropertyInfoForm';
import { useConnectionInfo } from '../useConnectionInfo';
import { AuthForm } from './AuthForm';
import { DBAuthDialogController } from './DBAuthDialogController';
import { DBAuthDialogFooter } from './DBAuthDialogFooter';
@@ -40,14 +40,14 @@ const styles = composes(
margin-top: 20px;
margin-bottom: 20px;
}
SubmittingForm, AuthForm {
SubmittingForm {
flex: 1;
display: flex;
flex-direction: column;
}
AuthForm {
flex-direction: column;
padding: 18px 24px;
ObjectPropertyInfoForm {
align-items: center;
justify-content: center;
}
ErrorMessage {
position: sticky;
@@ -87,10 +87,10 @@ export const DatabaseAuthDialog = observer(function DatabaseAuthDialog({
? <Loader />
: (
<SubmittingForm onSubmit={controller.login}>
<AuthForm
authProperties={connection.connectionInfo?.authProperties!}
<ObjectPropertyInfoForm
properties={connection.connectionInfo?.authProperties!}
credentials={controller.credentials}
authenticate={controller.isAuthenticating}
processing={controller.isAuthenticating}
/>
</SubmittingForm>
)}
@@ -17,42 +17,44 @@ import { useStyles } from '@cloudbeaver/core-theming';
import { formStyles } from './formStyles';
type Props = {
authProperties: ObjectPropertyInfo[] | undefined;
properties: ObjectPropertyInfo[] | undefined;
credentials: any;
authenticate: boolean;
processing: boolean;
className?: string;
}
export const AuthForm = observer(function AuthForm({
authProperties,
export const ObjectPropertyInfoForm = observer(function ObjectPropertyInfoForm({
properties,
credentials,
authenticate,
processing,
className,
}: Props) {
const handleChange = useCallback((key: string, value: string) => {
credentials[key] = value;
}, [credentials]);
if (!authProperties || authProperties.length === 0) {
if (!properties || properties.length === 0) {
return styled(useStyles(formStyles))(<center as="div">Properties empty</center>);
}
return styled(useStyles(formStyles))(
<InFocus>
<login-form as='div'>
{authProperties.map(property => (
<form-body as='div' className={className}>
{properties.map(property => (
<group as="div" key={property.id}>
<InputField
type={property.features.includes('password') ? 'password' : 'text'}
name={property.id}
value={credentials[property.id!]}
onChange={value => handleChange(property.id!, value)}
disabled={authenticate}
disabled={processing}
mod='surface'
>
{property.displayName}
</InputField>
</group>
))}
</login-form>
</form-body>
</InFocus>
);
});
@@ -9,13 +9,11 @@
import { css } from 'reshadow';
export const formStyles = css`
login-form {
form-body {
flex: 1;
box-sizing: border-box;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
group {
box-sizing: border-box;
@@ -67,11 +67,12 @@ export class NotificationService {
this.notify(notification, ENotificationType.Error);
}
logException(exception: Error, message?: string, silent?: boolean) {
logException(exception: Error | GQLError, message?: string, silent?: boolean) {
const exceptionMessage = hasDetails(exception) ? exception.errorText : exception.message || exception.name;
if (!silent) {
this.logError({
title: message || exception.message || exception.name,
details: this.hasDetails(exception) ? exception : undefined,
title: message || exceptionMessage,
details: hasDetails(exception) ? exception : undefined,
isSilent: silent,
});
}
@@ -88,8 +89,8 @@ export class NotificationService {
showDetails(id: number): void {
// TODO: emit event or something
}
}
private hasDetails(error: Error) {
return error instanceof GQLError || error instanceof ServerInternalError;
}
function hasDetails(error: Error): error is GQLError | ServerInternalError {
return error instanceof GQLError || error instanceof ServerInternalError;
}
@@ -1,8 +0,0 @@
query dataSourceList {
dataSourceList {
id
name
driverId
description
}
}
@@ -1,5 +1,5 @@
mutation openConnection($config: ConnectionConfig!) {
openConnection(config: $config) {
connection: openConnection(config: $config) {
id
name
driverId
@@ -0,0 +1,8 @@
query templateDataSourceList {
sources: templateDataSources {
id
name
driverId
description
}
}
+19 -19
View File
@@ -814,10 +814,6 @@ export type CreateConnectionMutationVariables = Exact<{
export type CreateConnectionMutation = { createConnection: Pick<ConnectionInfo, 'id' | 'name' | 'driverId' | 'connected' | 'features' | 'authNeeded' | 'authModel'> };
export type DataSourceListQueryVariables = Exact<{ [key: string]: never }>;
export type DataSourceListQuery = { dataSourceList: Array<Pick<DataSourceInfo, 'id' | 'name' | 'driverId' | 'description'>> };
export type DeleteConnectionMutationVariables = Exact<{
id: Scalars['ID'];
}>;
@@ -861,7 +857,11 @@ export type OpenConnectionMutationVariables = Exact<{
config: ConnectionConfig;
}>;
export type OpenConnectionMutation = { openConnection: Pick<ConnectionInfo, 'id' | 'name' | 'driverId' | 'connected' | 'features' | 'authNeeded' | 'authModel'> };
export type OpenConnectionMutation = { connection: Pick<ConnectionInfo, 'id' | 'name' | 'driverId' | 'connected' | 'features' | 'authNeeded' | 'authModel'> };
export type TemplateDataSourceListQueryVariables = Exact<{ [key: string]: never }>;
export type TemplateDataSourceListQuery = { sources: Array<Pick<DataSourceInfo, 'id' | 'name' | 'driverId' | 'description'>> };
export type TestConnectionMutationVariables = Exact<{
config: ConnectionConfig;
@@ -1333,16 +1333,6 @@ export const CreateConnectionDocument = `
}
}
`;
export const DataSourceListDocument = `
query dataSourceList {
dataSourceList {
id
name
driverId
description
}
}
`;
export const DeleteConnectionDocument = `
mutation deleteConnection($id: ID!) {
deleteConnection(id: $id)
@@ -1425,7 +1415,7 @@ export const InitConnectionDocument = `
`;
export const OpenConnectionDocument = `
mutation openConnection($config: ConnectionConfig!) {
openConnection(config: $config) {
connection: openConnection(config: $config) {
id
name
driverId
@@ -1436,6 +1426,16 @@ export const OpenConnectionDocument = `
}
}
`;
export const TemplateDataSourceListDocument = `
query templateDataSourceList {
sources: templateDataSources {
id
name
driverId
description
}
}
`;
export const TestConnectionDocument = `
mutation testConnection($config: ConnectionConfig!) {
testConnection(config: $config) {
@@ -2002,9 +2002,6 @@ export function getSdk(client: GraphQLClient, withWrapper: SdkFunctionWrapper =
createConnection(variables: CreateConnectionMutationVariables): Promise<CreateConnectionMutation> {
return withWrapper(() => client.request<CreateConnectionMutation>(CreateConnectionDocument, variables));
},
dataSourceList(variables?: DataSourceListQueryVariables): Promise<DataSourceListQuery> {
return withWrapper(() => client.request<DataSourceListQuery>(DataSourceListDocument, variables));
},
deleteConnection(variables: DeleteConnectionMutationVariables): Promise<DeleteConnectionMutation> {
return withWrapper(() => client.request<DeleteConnectionMutation>(DeleteConnectionDocument, variables));
},
@@ -2026,6 +2023,9 @@ export function getSdk(client: GraphQLClient, withWrapper: SdkFunctionWrapper =
openConnection(variables: OpenConnectionMutationVariables): Promise<OpenConnectionMutation> {
return withWrapper(() => client.request<OpenConnectionMutation>(OpenConnectionDocument, variables));
},
templateDataSourceList(variables?: TemplateDataSourceListQueryVariables): Promise<TemplateDataSourceListQuery> {
return withWrapper(() => client.request<TemplateDataSourceListQuery>(TemplateDataSourceListDocument, variables));
},
testConnection(variables: TestConnectionMutationVariables): Promise<TestConnectionMutation> {
return withWrapper(() => client.request<TestConnectionMutation>(TestConnectionDocument, variables));
},
@@ -9,13 +9,12 @@
import { observer } from 'mobx-react';
import styled from 'reshadow';
import { DBDriver } from '@cloudbeaver/core-app';
import { DBDriver, ObjectPropertyInfoForm } from '@cloudbeaver/core-app';
import { Radio, InputField } from '@cloudbeaver/core-blocks';
import { useTranslate } from '@cloudbeaver/core-localization';
import { useStyles } from '@cloudbeaver/core-theming';
import { ConnectionType } from '../ConnectionFormDialogController';
import { CredentialsForm } from './CredentialsForm';
import { formStyles } from './formStyles';
import { IFormController } from './IFormController';
import { ParametersForm } from './ParametersForm';
@@ -73,7 +72,16 @@ export const ConnectionForm = observer(function ConnectionForm({
</InputField>
</group>
)}
{!driver?.anonymousAccess && <CredentialsForm controller={controller}/>}
{controller.authModel && (
<>
<hr/>
<ObjectPropertyInfoForm
properties={controller.authModel.properties}
credentials={controller.config.credentials}
processing={controller.isConnecting}
/>
</>
)}
</connection-form>
);
});
@@ -1,55 +0,0 @@
/*
* cloudbeaver - Cloud Database Manager
* Copyright (C) 2020 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 { observer } from 'mobx-react';
import styled from 'reshadow';
import { InputField } from '@cloudbeaver/core-blocks';
import { useTranslate } from '@cloudbeaver/core-localization';
import { useStyles } from '@cloudbeaver/core-theming';
import { formStyles } from './formStyles';
import { IFormController } from './IFormController';
type CredentialsFormProps = {
controller: IFormController;
}
export const CredentialsForm = observer(function CredentialsForm({
controller,
}: CredentialsFormProps) {
const translate = useTranslate();
return styled(useStyles(formStyles))(
<>
<hr/>
<group as="div">
<InputField
type="text"
name="userName"
value={controller.config.userName}
onChange={value => controller.onChange('userName', value)}
disabled={controller.isConnecting}
mod='surface'
>
{translate('customConnection_userName')}
</InputField>
<InputField
type="password"
name="userPassword"
value={controller.config.userPassword}
onChange={value => controller.onChange('userPassword', value)}
disabled={controller.isConnecting}
mod='surface'
>
{translate('customConnection_Password')}
</InputField>
</group>
</>
);
});
@@ -6,11 +6,12 @@
* you may not use this file except in compliance with the License.
*/
import { ConnectionConfig } from '@cloudbeaver/core-sdk';
import { ConnectionConfig, DatabaseAuthModel } from '@cloudbeaver/core-sdk';
import { ConnectionType } from '../ConnectionFormDialogController';
export interface IFormController {
authModel?: DatabaseAuthModel;
config: ConnectionConfig;
connectionType: ConnectionType;
isConnecting: boolean;
@@ -10,6 +10,8 @@ import { css } from 'reshadow';
export const formStyles = css`
connection-form {
flex: 1;
display: flex;
flex-direction: column;
padding: 18px 24px;
}
@@ -12,7 +12,7 @@ import styled, { css } from 'reshadow';
import { DBDriver } from '@cloudbeaver/core-app';
import {
SubmittingForm, ErrorMessage, TabsState, TabList, Tab, TabTitle, TabPanel
SubmittingForm, ErrorMessage, TabsState, TabList, Tab, TabTitle, TabPanel, Loader
} from '@cloudbeaver/core-blocks';
import { useController } from '@cloudbeaver/core-di';
import { CommonDialogWrapper } from '@cloudbeaver/core-dialogs';
@@ -43,9 +43,6 @@ const styles = composes(
max-height: 500px;
min-height: 500px;
}
SubmittingForm {
overflow: auto;
}
SubmittingForm, BaseTabPanel {
flex: 1;
display: flex;
@@ -117,18 +114,23 @@ export const ConnectionFormDialog = observer(function ConnectionFormDialog({
)}
onReject={onClose}
>
<SubmittingForm onSubmit={controller.onCreateConnection}>
<TabPanel tabId='options'>
<ConnectionForm driver={driver} controller={controller} />
</TabPanel>
<TabPanel tabId='driver_properties'>
<DriverProperties
driver={driver}
state={controller.config.properties!}
loadProperties={loadProperties}
/>
</TabPanel>
</SubmittingForm>
{controller.isLoading
? <Loader />
: (
<SubmittingForm onSubmit={controller.onCreateConnection}>
<TabPanel tabId='options'>
<ConnectionForm driver={driver} controller={controller} />
</TabPanel>
<TabPanel tabId='driver_properties'>
<DriverProperties
driver={driver}
state={controller.config.properties!}
loadProperties={loadProperties}
/>
</TabPanel>
</SubmittingForm>
)
}
{controller.error.responseMessage && (
<ErrorMessage
text={controller.error.responseMessage}
@@ -8,11 +8,11 @@
import { observable, action } from 'mobx';
import { DBDriver, ErrorDetailsDialog } from '@cloudbeaver/core-app';
import { DBDriver, ErrorDetailsDialog, DatabaseAuthModelsResource } from '@cloudbeaver/core-app';
import { injectable, IInitializableController, IDestructibleController } from '@cloudbeaver/core-di';
import { CommonDialogService } from '@cloudbeaver/core-dialogs';
import { NotificationService } from '@cloudbeaver/core-events';
import { ConnectionConfig, GQLErrorCatcher } from '@cloudbeaver/core-sdk';
import { ConnectionConfig, GQLErrorCatcher, DatabaseAuthModel } from '@cloudbeaver/core-sdk';
import { CustomConnectionService } from '../../CustomConnectionService';
@@ -27,29 +27,34 @@ implements IInitializableController, IDestructibleController {
@observable connectionType = ConnectionType.Attributes
@observable isLoading = true;
@observable isConnecting = false;
@observable driver!: DBDriver
@observable driver!: DBDriver;
@observable authModel?: DatabaseAuthModel;
@observable config: ConnectionConfig = {
name: '',
driverId: '',
host: '',
port: '',
databaseName: '',
userName: '',
userPassword: '',
url: '',
properties: {},
}
credentials: {},
};
readonly error = new GQLErrorCatcher();
private onClose!: () => void
private onClose!: () => void;
private isDistructed = false;
constructor(private customConnectionService: CustomConnectionService,
constructor(
private customConnectionService: CustomConnectionService,
private notificationService: NotificationService,
private commonDialogService: CommonDialogService) { }
private commonDialogService: CommonDialogService,
private dbAuthModelsResource: DatabaseAuthModelsResource
) { }
init(driver: DBDriver, onClose: () => void) {
this.driver = driver;
this.onClose = onClose;
this.loadDatabaseAuthModel();
this.setDriverDefaults();
}
@@ -117,9 +122,9 @@ implements IInitializableController, IDestructibleController {
config.name = this.urlToConnectionName(this.config.name, this.config.url);
config.url = this.config.url;
}
if (!this.driver?.anonymousAccess) {
config.userName = this.config.userName;
config.userPassword = this.config.userPassword;
if (this.authModel) {
config.authModelId = this.config.authModelId;
config.credentials = this.config.credentials;
}
if (Object.keys(this.config.properties).length > 0) {
config.properties = this.config.properties;
@@ -136,9 +141,9 @@ implements IInitializableController, IDestructibleController {
this.config.port = this.driver?.defaultPort || '';
this.config.databaseName = '';
this.config.url = this.driver?.sampleURL || '';
this.config.userName = '';
this.config.userPassword = '';
this.config.properties = {};
this.config.authModelId = this.driver.defaultAuthModel;
this.config.credentials = {};
}
/**
@@ -170,4 +175,17 @@ implements IInitializableController, IDestructibleController {
this.notificationService.logException(exception, message);
}
}
private async loadDatabaseAuthModel() {
if (!this.driver.defaultAuthModel) {
return;
}
try {
this.authModel = await this.dbAuthModelsResource.load(this.driver.defaultAuthModel);
} catch (exception) {
this.notificationService.logException(exception, 'Can\'t load driver auth model');
} finally {
this.isLoading = false;
}
}
}
@@ -8,7 +8,7 @@
import { observable, computed } from 'mobx';
import { DBDriver, ConnectionsManagerService } from '@cloudbeaver/core-app';
import { DBDriver, DBDriverResource } from '@cloudbeaver/core-app';
import { injectable, IInitializableController } from '@cloudbeaver/core-di';
import { NotificationService } from '@cloudbeaver/core-events';
@@ -26,16 +26,14 @@ export class CustomConnectionController implements IInitializableController {
@computed
get drivers(): DBDriver[] {
return Array
.from(
this.connectionsManagerService
.getDBDrivers()
.values()
)
.from(this.dbDriverResource.data.values())
.sort((a, b) => this.sortDrivers(a, b));
}
constructor(private connectionsManagerService: ConnectionsManagerService,
private notificationService: NotificationService) { }
constructor(
private dbDriverResource: DBDriverResource,
private notificationService: NotificationService
) { }
init() {
this.loadDBDrivers();
@@ -46,16 +44,14 @@ export class CustomConnectionController implements IInitializableController {
}
onDriverSelect = (driverId: string) => {
this.driver = this.connectionsManagerService
.getDBDrivers()
.get(driverId)!;
this.driver = this.dbDriverResource.get(driverId)!;
this.step = ConnectionStep.Connection;
}
private async loadDBDrivers() {
try {
await this.connectionsManagerService.loadDriversAsync();
await this.dbDriverResource.loadAll();
} catch (exception) {
this.notificationService.logException(exception, 'Can\'t load database drivers');
} finally {
@@ -13,8 +13,10 @@ import { ConnectionConfig, GraphQLService, ObjectPropertyInfo } from '@cloudbeav
@injectable()
export class CustomConnectionService {
constructor(private graphQLService: GraphQLService,
private connectionsManagerService: ConnectionsManagerService) {
constructor(
private graphQLService: GraphQLService,
private connectionsManagerService: ConnectionsManagerService,
) {
}
async loadDriverProperties(driverId: string): Promise<ObjectPropertyInfo[]> {
@@ -10,10 +10,10 @@ import { MainMenuService, ConnectionDialogsService } from '@cloudbeaver/core-app
import { injectable } from '@cloudbeaver/core-di';
import { CommonDialogService } from '@cloudbeaver/core-dialogs';
import { NotificationService } from '@cloudbeaver/core-events';
import { PermissionsService, EPermission, SessionResource } from '@cloudbeaver/core-root';
import { PermissionsService, EPermission } from '@cloudbeaver/core-root';
import { BasicConnectionService } from './BasicConnectionService';
import { ConnectionDialog } from './ConnectionDialog/ConnectionDialog';
import { TemplateDataSourceListResource } from './DataSourcesResource';
@injectable()
export class BasicConnectionPluginBootstrap {
@@ -21,17 +21,15 @@ export class BasicConnectionPluginBootstrap {
constructor(
private connectionDialogsService: ConnectionDialogsService,
private mainMenuService: MainMenuService,
private basicConnectionService: BasicConnectionService,
private templateDataSourceListResource: TemplateDataSourceListResource,
private commonDialogService: CommonDialogService,
private notificationService: NotificationService,
private permissionsService: PermissionsService,
private sessionResource: SessionResource
private permissionsService: PermissionsService
) {
}
bootstrap() {
this.loadDbSources();
this.sessionResource.onDataUpdate.subscribe(this.loadDbSources.bind(this));
this.mainMenuService.registerMenuItem(
this.connectionDialogsService.newConnectionMenuToken,
{
@@ -40,20 +38,21 @@ export class BasicConnectionPluginBootstrap {
title: 'basicConnection_main_menu_item',
onClick: () => this.openConnectionsDialog(),
isHidden: () => !this.permissionsService.has(EPermission.public),
isDisabled: () => !this.basicConnectionService.dbSources.data.length,
isDisabled: () => !this.templateDataSourceListResource.data.length,
}
);
}
private async openConnectionsDialog() {
this.loadDbSources();
await this.commonDialogService.open(ConnectionDialog, null);
}
private async loadDbSources() {
try {
await this.basicConnectionService.dbSources.refresh(null);
await this.templateDataSourceListResource.loadAll();
} catch (error) {
this.notificationService.logException(error, 'DBSources loading failed');
this.notificationService.logException(error, 'Template Data Sources loading failed');
}
}
}
@@ -6,34 +6,22 @@
* you may not use this file except in compliance with the License.
*/
import {
Connection, DBSource, ConnectionsManagerService
} from '@cloudbeaver/core-app';
import { Connection, ConnectionsManagerService } from '@cloudbeaver/core-app';
import { injectable } from '@cloudbeaver/core-di';
import { PermissionsService } from '@cloudbeaver/core-root';
import { ConnectionConfig, GraphQLService } from '@cloudbeaver/core-sdk';
import { DataSourcesResource } from './DataSourcesResource';
@injectable()
export class BasicConnectionService {
constructor(
private graphQLService: GraphQLService,
private connectionsManagerService: ConnectionsManagerService,
private permissionsService: PermissionsService,
readonly dbSources: DataSourcesResource
private connectionsManagerService: ConnectionsManagerService
) {
this.permissionsService.onUpdate.subscribe(() => this.dbSources.refresh(null));
}
public getDBSources(): DBSource[] {
return this.dbSources.data;
}
async openConnectionAsync(config: ConnectionConfig): Promise<Connection> {
const response = await this.graphQLService.gql.openConnection({ config });
this.connectionsManagerService.addOpenedConnection(response.openConnection);
return response.openConnection;
const { connection } = await this.graphQLService.gql.openConnection({ config });
this.connectionsManagerService.addOpenedConnection(connection);
return connection;
}
}
@@ -8,15 +8,14 @@
import { observable, action } from 'mobx';
import {
ConnectionsManagerService, DBSource, ErrorDetailsDialog,
} from '@cloudbeaver/core-app';
import { DBDriverResource, DBSource, ErrorDetailsDialog } from '@cloudbeaver/core-app';
import { injectable, IInitializableController, IDestructibleController } from '@cloudbeaver/core-di';
import { CommonDialogService } from '@cloudbeaver/core-dialogs';
import { NotificationService } from '@cloudbeaver/core-events';
import { ConnectionConfig, GQLError } from '@cloudbeaver/core-sdk';
import { BasicConnectionService } from '../BasicConnectionService';
import { TemplateDataSourceListResource } from '../DataSourcesResource';
export enum ConnectionStep {
DBSource,
@@ -50,11 +49,11 @@ implements IInitializableController, IDestructibleController, IConnectionControl
private isDistructed = false;
get dbSources() {
return this.basicConnectionService.getDBSources();
return this.templateDataSourceListResource.data;
}
get dbDrivers() {
return this.connectionsManagerService.getDBDrivers();
return this.dbDriverResource.data;
}
get dbDriver() {
@@ -64,10 +63,13 @@ implements IInitializableController, IDestructibleController, IConnectionControl
return this.dbDrivers.get(this.dbSource.driverId);
}
constructor(private connectionsManagerService: ConnectionsManagerService,
constructor(
private dbDriverResource: DBDriverResource,
private templateDataSourceListResource: TemplateDataSourceListResource,
private basicConnectionService: BasicConnectionService,
private notificationService: NotificationService,
private commonDialogService: CommonDialogService) { }
private commonDialogService: CommonDialogService
) { }
init(onClose: () => void) {
this.onClose = onClose;
@@ -153,8 +155,8 @@ implements IInitializableController, IDestructibleController, IConnectionControl
private async loadDBSources() {
try {
await this.basicConnectionService.dbSources.load(null);
await this.connectionsManagerService.loadDriversAsync();
await this.templateDataSourceListResource.loadAll();
await this.dbDriverResource.loadAll();
} catch (exception) {
this.notificationService.logException(exception, 'Can\'t load database sources');
} finally {
@@ -12,25 +12,36 @@ import { EPermission, PermissionsService } from '@cloudbeaver/core-root';
import { GraphQLService, CachedDataResource } from '@cloudbeaver/core-sdk';
@injectable()
export class DataSourcesResource extends CachedDataResource<DBSource[], null> {
export class TemplateDataSourceListResource extends CachedDataResource<DBSource[], null> {
constructor(
private graphQLService: GraphQLService,
private permissionsService: PermissionsService
) {
super([]);
this.permissionsService.onUpdate.subscribe(() => this.markOutdated(null));
}
isLoaded() {
return !!this.data.length;
}
async loadAll() {
await this.load(null);
return this.data;
}
async refreshAll() {
await this.refresh(null);
return this.data;
}
protected async loader(key: null): Promise<DBSource[]> {
if (!await this.permissionsService.hasAsync(EPermission.public)) {
this.markUpdated(key);
return [];
}
const { dataSourceList } = await this.graphQLService.gql.dataSourceList();
const { sources } = await this.graphQLService.gql.templateDataSourceList();
this.markUpdated(key);
return dataSourceList;
return sources;
}
}
@@ -10,7 +10,7 @@ import { PluginManifest } from '@cloudbeaver/core-di';
import { BasicConnectionPluginBootstrap } from './BasicConnectionPluginBootstrap';
import { BasicConnectionService } from './BasicConnectionService';
import { DataSourcesResource } from './DataSourcesResource';
import { TemplateDataSourceListResource } from './DataSourcesResource';
import { LocaleService } from './LocaleService';
export const basicConnectionPluginManifest: PluginManifest = {
@@ -20,7 +20,7 @@ export const basicConnectionPluginManifest: PluginManifest = {
providers: [
BasicConnectionService,
DataSourcesResource,
TemplateDataSourceListResource,
LocaleService,
],
@@ -14,6 +14,7 @@ import {
NavigationService,
IContextProvider,
ITabOptions,
ConnectionAuthService,
} from '@cloudbeaver/core-app';
import { injectable } from '@cloudbeaver/core-di';
import { NotificationService } from '@cloudbeaver/core-events';
@@ -60,7 +61,8 @@ export class SqlEditorNavigatorService {
private notificationService: NotificationService,
private gql: GraphQLService,
private sqlDialectInfoService: SqlDialectInfoService,
private navigationService: NavigationService
private navigationService: NavigationService,
private connectionAuthService: ConnectionAuthService
) {
this.navigator = this.navigationService.createNavigator<SQLCreateAction | SQLEditorAction>(
@@ -164,6 +166,12 @@ export class SqlEditorNavigatorService {
connectionId = this.connectionsManagerService.connections[0].id;
}
const connection = await this.connectionAuthService.auth(connectionId);
if (!connection?.connected) {
return null;
}
await this.sqlDialectInfoService.loadSqlDialectInfo(connectionId);
const context = await this.createSqlContext(connectionId, catalogId, schemaId);
@@ -17,6 +17,7 @@ import {
objectCatalogProvider,
objectCatalogSetter,
objectSchemaSetter,
ConnectionAuthService,
} from '@cloudbeaver/core-app';
import { injectable } from '@cloudbeaver/core-di';
import { NotificationService } from '@cloudbeaver/core-events';
@@ -34,11 +35,14 @@ import { SqlExecutionState } from './SqlExecutionState';
export class SqlEditorTabService {
readonly tabHandler: TabHandler<ISqlEditorTabState>
constructor(private navigationTabsService: NavigationTabsService,
private connectionsManagerService: ConnectionsManagerService,
private notificationService: NotificationService,
private gql: GraphQLService,
private sqlDialectInfoService: SqlDialectInfoService) {
constructor(
private navigationTabsService: NavigationTabsService,
private connectionsManagerService: ConnectionsManagerService,
private notificationService: NotificationService,
private gql: GraphQLService,
private sqlDialectInfoService: SqlDialectInfoService,
private connectionAuthService: ConnectionAuthService
) {
this.tabHandler = this.navigationTabsService
.registerTabHandler<ISqlEditorTabState>({
@@ -102,6 +106,13 @@ export class SqlEditorTabService {
private async setConnectionId(connectionId: string, tab: ITab<ISqlEditorTabState>) {
try {
const connection = await this.connectionAuthService.auth(connectionId);
if (!connection?.connected) {
return false;
}
// try to create new context first
const context = await this.createSqlContext(connectionId);
// when new context created - destroy old one silently