mirror of
https://github.com/dbeaver/cloudbeaver.git
synced 2026-09-24 16:04:36 +08:00
Merge pull request #114 from dbeaver/refactor-authentication
feat: connection behavior & error message fix
This commit is contained in:
+3
-1
@@ -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);
|
||||
}
|
||||
|
||||
+3
-1
@@ -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({
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
+8
-8
@@ -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>
|
||||
)}
|
||||
|
||||
+12
-10
@@ -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>
|
||||
);
|
||||
});
|
||||
+1
-3
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
},
|
||||
|
||||
+11
-3
@@ -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>
|
||||
);
|
||||
});
|
||||
|
||||
-55
@@ -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>
|
||||
</>
|
||||
);
|
||||
});
|
||||
+2
-1
@@ -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;
|
||||
|
||||
+2
@@ -10,6 +10,8 @@ import { css } from 'reshadow';
|
||||
|
||||
export const formStyles = css`
|
||||
connection-form {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 18px 24px;
|
||||
}
|
||||
|
||||
+18
-16
@@ -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}
|
||||
|
||||
+32
-14
@@ -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
-12
@@ -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[]> {
|
||||
|
||||
+8
-9
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
+11
-9
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user