Merge remote-tracking branch 'origin/devel' into devel

This commit is contained in:
Serge Rider
2022-02-25 16:18:23 +03:00
11 changed files with 210 additions and 146 deletions
@@ -6,12 +6,12 @@
* you may not use this file except in compliance with the License.
*/
import { observable, makeObservable } from 'mobx';
import { observable, makeObservable, action } from 'mobx';
import { AdministrationScreenService } from '@cloudbeaver/core-administration';
import { TabsContainer } from '@cloudbeaver/core-ui';
import { injectable } from '@cloudbeaver/core-di';
import type { ConnectionConfig } from '@cloudbeaver/core-sdk';
import { TabsContainer } from '@cloudbeaver/core-ui';
import { ConnectionFormService, ConnectionFormState, IConnectionFormState } from '@cloudbeaver/plugin-connections';
import { ConnectionsResource } from '../ConnectionsResource';
@@ -40,16 +40,23 @@ export class CreateConnectionService {
this.data = null;
this.tabsContainer = new TabsContainer();
makeObservable(this, {
data: observable,
disabled: observable,
});
this.setConnectionTemplate = this.setConnectionTemplate.bind(this);
this.clearConnectionTemplate = this.clearConnectionTemplate.bind(this);
this.setCreateMethod = this.setCreateMethod.bind(this);
this.cancelCreate = this.cancelCreate.bind(this);
this.create = this.create.bind(this);
makeObservable<this, 'activateMethod'>(this, {
data: observable,
disabled: observable,
setCreateMethod: action,
cancelCreate: action,
create: action,
setConnectionTemplate: action,
clearConnectionTemplate: action,
close: action,
activateMethod: action,
});
}
getDefault(): string | null {
@@ -6,14 +6,14 @@
* you may not use this file except in compliance with the License.
*/
import { observable, toJS } from 'mobx';
import { observable, runInAction, toJS } from 'mobx';
import { TabsContainer } from '@cloudbeaver/core-ui';
import { PlaceholderContainer } from '@cloudbeaver/core-blocks';
import { injectable } from '@cloudbeaver/core-di';
import { CommonDialogService, DialogueStateResult } from '@cloudbeaver/core-dialogs';
import { ENotificationType, NotificationService } from '@cloudbeaver/core-events';
import { ExecutorHandlersCollection, ExecutorInterrupter, IExecutorHandler, IExecutorHandlersCollection } from '@cloudbeaver/core-executor';
import { TabsContainer } from '@cloudbeaver/core-ui';
import { ConnectionAuthenticationDialog } from '../ConnectionAuthentication/ConnectionAuthenticationDialog';
import { ConnectionFormBaseActions } from './ConnectionFormBaseActions';
@@ -101,7 +101,7 @@ export class ConnectionFormService {
},
});
private showSubmittingStatusMessage: IExecutorHandler<IConnectionFormSubmitData> = (data, contexts) => {
private readonly showSubmittingStatusMessage: IExecutorHandler<IConnectionFormSubmitData> = (data, contexts) => {
const status = contexts.getContext(this.connectionStatusContext);
if (!status.saved) {
@@ -124,7 +124,7 @@ export class ConnectionFormService {
}
};
private askCredentials: IExecutorHandler<IConnectionFormSubmitData> = async (data, contexts) => {
private readonly askCredentials: IExecutorHandler<IConnectionFormSubmitData> = async (data, contexts) => {
const credentialsState = contexts.getContext(connectionCredentialsStateContext);
if (data.submitType !== 'test' || (!credentialsState.authModelId && !credentialsState.networkHandlers.length)) {
@@ -133,21 +133,23 @@ export class ConnectionFormService {
const config = contexts.getContext(connectionConfigContext);
if (credentialsState.authModelId) {
if (!config.credentials) {
config.credentials = { ...data.state.config.credentials };
runInAction(() => {
if (credentialsState.authModelId) {
if (!config.credentials) {
config.credentials = { ...data.state.config.credentials };
}
config.credentials = observable(config.credentials);
}
config.credentials = observable(config.credentials);
}
if (credentialsState.networkHandlers.length > 0) {
if (!config.networkHandlersConfig) {
config.networkHandlersConfig = toJS(data.state.config.networkHandlersConfig) || [];
}
if (credentialsState.networkHandlers.length > 0) {
if (!config.networkHandlersConfig) {
config.networkHandlersConfig = toJS(data.state.config.networkHandlersConfig) || [];
config.networkHandlersConfig = observable(config.networkHandlersConfig);
}
config.networkHandlersConfig = observable(config.networkHandlersConfig);
}
});
const result = await this.commonDialogService.open(ConnectionAuthenticationDialog, {
config,
@@ -161,7 +163,7 @@ export class ConnectionFormService {
}
};
private ensureValidation: IExecutorHandler<IConnectionFormSubmitData> = (data, contexts) => {
private readonly ensureValidation: IExecutorHandler<IConnectionFormSubmitData> = (data, contexts) => {
const validation = contexts.getContext(this.connectionValidationContext);
if (!validation.valid) {
@@ -6,13 +6,13 @@
* you may not use this file except in compliance with the License.
*/
import { computed, makeObservable, observable } from 'mobx';
import { action, computed, makeObservable, observable } from 'mobx';
import type { IFormStateInfo } from '@cloudbeaver/core-blocks';
import { DatabaseConnection, EConnectionFeature, IConnectionsResource } from '@cloudbeaver/core-connections';
import { Executor, IExecutionContextProvider, IExecutor } from '@cloudbeaver/core-executor';
import { ConnectionConfig, ResourceKey, ResourceKeyUtils } from '@cloudbeaver/core-sdk';
import { MetadataMap } from '@cloudbeaver/core-utils';
import { MetadataMap, uuid } from '@cloudbeaver/core-utils';
import { connectionFormConfigureContext } from './connectionFormConfigureContext';
import type { ConnectionFormService } from './ConnectionFormService';
@@ -71,10 +71,19 @@ export class ConnectionFormState implements IConnectionFormState {
return false;
}
get id(): string {
if (this.mode === 'create') {
return 'create';
}
return this.config.connectionId || this._id;
}
readonly resource: IConnectionsResource;
readonly service: ConnectionFormService;
readonly submittingTask: IExecutor<IConnectionFormSubmitData>;
private readonly _id: string;
private stateInfo: IFormStateInfo | null;
private readonly loadConnectionTask: IExecutor<IConnectionFormState>;
private readonly formStateTask: IExecutor<IConnectionFormState>;
@@ -84,22 +93,9 @@ export class ConnectionFormState implements IConnectionFormState {
service: ConnectionFormService,
resource: IConnectionsResource
) {
this._id = uuid();
this.initError = null;
makeObservable<IConnectionFormState, '_availableDrivers' | 'stateInfo'>(this, {
mode: observable,
type: observable,
config: observable,
availableDrivers: computed,
_availableDrivers: observable,
info: computed,
statusMessage: observable,
configured: observable,
readonly: computed,
stateInfo: observable,
initError: observable.ref,
});
this.resource = resource;
this.config = {};
this._availableDrivers = [];
@@ -142,6 +138,26 @@ export class ConnectionFormState implements IConnectionFormState {
};
})
.next(this.formStateTask);
makeObservable<IConnectionFormState, '_availableDrivers' | 'stateInfo' | 'updateFormState'>(this, {
mode: observable,
type: observable,
config: observable,
availableDrivers: computed,
_availableDrivers: observable,
info: computed,
statusMessage: observable,
configured: observable,
readonly: computed,
stateInfo: observable,
initError: observable.ref,
id: computed,
reset: action,
setOptions: action,
setConfig: action,
setAvailableDrivers: action,
updateFormState: action,
});
}
async loadConnectionInfo(): Promise<DatabaseConnection | undefined> {
@@ -6,6 +6,8 @@
* you may not use this file except in compliance with the License.
*/
import { action, makeObservable } from 'mobx';
import { DBDriverResource } from '@cloudbeaver/core-connections';
import { Bootstrap, injectable } from '@cloudbeaver/core-di';
import type { IExecutionContextProvider } from '@cloudbeaver/core-executor';
@@ -24,6 +26,10 @@ export class ConnectionDriverPropertiesTabService extends Bootstrap {
private readonly dbDriverResource: DBDriverResource,
) {
super();
makeObservable<this, 'fillConfig'>(this, {
fillConfig: action,
});
}
register(): void {
@@ -35,7 +41,7 @@ export class ConnectionDriverPropertiesTabService extends Bootstrap {
panel: () => DriverProperties,
isDisabled: (tabId, props) => {
if (props?.state.config.driverId) {
return !props?.state.config.driverId;
return !props.state.config.driverId;
}
return true;
},
@@ -23,6 +23,7 @@ export interface IConnectionFormState {
partsState: MetadataMap<string, any>;
readonly id: string;
readonly initError: Error | null;
readonly statusMessage: string | null;
readonly disabled: boolean;
@@ -6,6 +6,8 @@
* you may not use this file except in compliance with the License.
*/
import { action, makeObservable, runInAction, toJS } from 'mobx';
import { DatabaseAuthModelsResource, DatabaseConnection, DBDriverResource, isJDBCConnection } from '@cloudbeaver/core-connections';
import { Bootstrap, injectable } from '@cloudbeaver/core-di';
import type { IExecutionContextProvider } from '@cloudbeaver/core-executor';
@@ -28,6 +30,10 @@ export class ConnectionOptionsTabService extends Bootstrap {
private readonly databaseAuthModelsResource: DatabaseAuthModelsResource,
) {
super();
makeObservable<this, 'fillConfig'>(this, {
fillConfig: action,
});
}
register(): void {
@@ -179,45 +185,46 @@ export class ConnectionOptionsTabService extends Bootstrap {
}
const driver = await this.dbDriverResource.load(state.config.driverId, ['includeProviderProperties']);
const tempConfig = toJS(config);
if (state.mode === 'edit') {
config.connectionId = state.config.connectionId;
tempConfig.connectionId = state.config.connectionId;
}
config.name = state.config.name?.trim();
tempConfig.name = state.config.name?.trim();
if (config.name && state.mode === 'create') {
if (tempConfig.name && state.mode === 'create') {
const connections = await state.resource.load(CachedMapAllKey);
const connectionNames = connections.map(connection => connection.name);
config.name = getUniqueName(config.name, connectionNames);
tempConfig.name = getUniqueName(tempConfig.name, connectionNames);
}
config.description = state.config.description;
config.template = state.config.template;
config.driverId = state.config.driverId;
tempConfig.description = state.config.description;
tempConfig.template = state.config.template;
tempConfig.driverId = state.config.driverId;
if (isJDBCConnection(driver, state.info)) {
config.url = state.config.url;
tempConfig.url = state.config.url;
} else {
if (!driver.embedded) {
config.host = state.config.host;
config.port = state.config.port;
tempConfig.host = state.config.host;
tempConfig.port = state.config.port;
}
config.databaseName = state.config.databaseName;
tempConfig.databaseName = state.config.databaseName;
}
if (state.config.authModelId || driver.defaultAuthModel) {
config.authModelId = state.config.authModelId || driver.defaultAuthModel;
config.saveCredentials = state.config.saveCredentials;
tempConfig.authModelId = state.config.authModelId || driver.defaultAuthModel;
tempConfig.saveCredentials = state.config.saveCredentials;
const properties = await this.getConnectionAuthModelProperties(config.authModelId, state.info);
const properties = await this.getConnectionAuthModelProperties(tempConfig.authModelId, state.info);
if (this.isCredentialsChanged(properties, state.config.credentials)) {
config.credentials = { ...state.config.credentials };
tempConfig.credentials = { ...state.config.credentials };
}
if (!config.saveCredentials) {
credentialsState.requireAuthModel(config.authModelId);
if (!tempConfig.saveCredentials) {
credentialsState.requireAuthModel(tempConfig.authModelId);
}
}
@@ -235,8 +242,12 @@ export class ConnectionOptionsTabService extends Bootstrap {
providerProperties[providerProperty.id] = providerProperty.defaultValue;
}
config.providerProperties = providerProperties;
tempConfig.providerProperties = providerProperties;
}
runInAction(() => {
Object.assign(config, tempConfig);
});
}
private async formState(
@@ -46,10 +46,9 @@ const styles = css`
}
`;
export const Options: TabContainerPanelComponent<IConnectionFormProps> = observer(function Options(props) {
const {
state,
} = props;
export const Options: TabContainerPanelComponent<IConnectionFormProps> = observer(function Options({
state,
}) {
const service = useService(ConnectionFormService);
const formRef = useRef<HTMLFormElement>(null);
const translate = useTranslate();
@@ -63,11 +62,11 @@ export const Options: TabContainerPanelComponent<IConnectionFormProps> = observe
} = state;
const authentication = useAuthenticationAction({
origin: state.info?.origin ?? { type: AUTH_PROVIDER_LOCAL_ID, displayName: 'Local' },
origin: info?.origin ?? { type: AUTH_PROVIDER_LOCAL_ID, displayName: 'Local' },
});
useFormValidator(submittingHandlers.for(service.formValidationTask), formRef.current);
const optionsHook = useOptions(props.state);
const optionsHook = useOptions(state);
const { credentialsSavingEnabled } = useAdministrationSettings();
const handleDriverSelect = useCallback(async (value?: string, name?: string, prev?: string) => {
@@ -87,8 +86,8 @@ export const Options: TabContainerPanelComponent<IConnectionFormProps> = observe
{ key: config.driverId || null, includes: ['includeProviderProperties'] },
{
onData: (data, resource, prevData) => {
if (!prevData) {
handleDriverSelect(data.id);
if (data.id !== prevData?.id) {
optionsHook.setDefaults(data);
}
},
}
@@ -6,6 +6,8 @@
* you may not use this file except in compliance with the License.
*/
import { runInAction } from 'mobx';
import { useObjectRef } from '@cloudbeaver/core-blocks';
import { DBDriver, isJDBCConnection } from '@cloudbeaver/core-connections';
import type { DatabaseAuthModel } from '@cloudbeaver/core-sdk';
@@ -23,84 +25,87 @@ export function useOptions(state: IConnectionFormState) {
return useObjectRef({
updateNameTemplate(driver: DBDriver | undefined) {
const {
prevName,
state: {
config,
info,
mode,
},
} = refObject;
runInAction(() => {
const {
prevName,
state: {
config,
info,
mode,
},
} = refObject;
const isAutoFill = config.name === prevName || prevName === null;
const isAutoFill = config.name === prevName || prevName === null;
if (mode === 'edit' || !isAutoFill) {
return;
}
if (isJDBCConnection(driver, info)) {
refObject.prevName = config.url || '';
config.name = config.url || '';
return;
}
if (!driver) {
config.name = 'New connection';
return;
}
let name = driver.name || '';
if (config.host) {
name += '@' + config.host.slice(0, MAX_HOST_LENGTH);
if (config.port && config.port !== driver.defaultPort) {
name += ':' + config.port;
if (mode === 'edit' || !isAutoFill) {
return;
}
}
refObject.prevName = name;
config.name = name;
if (isJDBCConnection(driver, info)) {
refObject.prevName = config.url || '';
config.name = config.url || '';
return;
}
if (!driver) {
config.name = 'New connection';
return;
}
let name = driver.name || '';
if (config.host) {
name += '@' + config.host.slice(0, MAX_HOST_LENGTH);
if (config.port && config.port !== driver.defaultPort) {
name += ':' + config.port;
}
}
refObject.prevName = name;
config.name = name;
});
},
setDefaults(driver: DBDriver | undefined, prevDriver: DBDriver | undefined) {
const {
state: {
config,
info,
},
} = refObject;
setDefaults(driver: DBDriver | undefined, prevDriver?: DBDriver) {
runInAction(() => {
const {
state: {
config,
info,
},
} = refObject;
if (info) {
return;
}
if (!prevDriver || config.host === prevDriver.defaultServer) {
config.host = driver?.defaultServer || 'localhost';
}
if (!prevDriver || config.port === prevDriver.defaultPort) {
config.port = driver?.defaultPort;
}
if (!prevDriver || config.databaseName === prevDriver.defaultDatabase) {
config.databaseName = driver?.defaultDatabase;
}
if (!prevDriver || config.url === prevDriver.sampleURL) {
config.url = driver?.sampleURL;
}
this.updateNameTemplate(driver);
if (driver?.id !== prevDriver?.id) {
for (const property of Object.keys(config.credentials)) {
delete config.credentials[property];
if (info || driver?.id !== config.driverId) {
return;
}
for (const property of Object.keys(config.providerProperties)) {
delete config.providerProperties[property];
if ((!prevDriver && config.host === undefined) || config.host === prevDriver?.defaultServer) {
config.host = driver?.defaultServer || 'localhost';
}
config.authModelId = driver?.defaultAuthModel;
}
if ((!prevDriver && config.port === undefined) || config.port === prevDriver?.defaultPort) {
config.port = driver?.defaultPort;
}
if ((!prevDriver && config.databaseName === undefined) || config.databaseName === prevDriver?.defaultDatabase) {
config.databaseName = driver?.defaultDatabase;
}
if ((!prevDriver && config.url === undefined) || config.url === prevDriver?.sampleURL) {
config.url = driver?.sampleURL;
}
this.updateNameTemplate(driver);
if (driver?.id !== prevDriver?.id) {
for (const property of Object.keys(config.credentials)) {
delete config.credentials[property];
}
for (const property of Object.keys(config.providerProperties)) {
delete config.providerProperties[property];
}
config.authModelId = driver?.defaultAuthModel;
}
});
},
setAuthModel(model: DatabaseAuthModel) {
// const {
@@ -6,6 +6,8 @@
* you may not use this file except in compliance with the License.
*/
import { action, makeObservable } from 'mobx';
import { DBDriverResource, SSH_TUNNEL_ID } from '@cloudbeaver/core-connections';
import { Bootstrap, injectable } from '@cloudbeaver/core-di';
import type { IExecutionContextProvider } from '@cloudbeaver/core-executor';
@@ -27,6 +29,11 @@ export class ConnectionSSHTabService extends Bootstrap {
private readonly dbDriverResource: DBDriverResource
) {
super();
makeObservable<this, 'fillConfig' | 'prepareConfig'>(this, {
fillConfig: action,
prepareConfig: action,
});
}
register(): void {
@@ -38,7 +45,7 @@ export class ConnectionSSHTabService extends Bootstrap {
panel: () => SSH,
isHidden: (tabId, props) => {
if (props?.state.config.driverId) {
const driver = this.dbDriverResource.get(props?.state.config.driverId);
const driver = this.dbDriverResource.get(props.state.config.driverId);
return !driver?.applicableNetworkHandlers.includes(SSH_TUNNEL_ID);
}
@@ -149,7 +156,7 @@ export class ConnectionSSHTabService extends Bootstrap {
}
}
private async prepareConfig(
private prepareConfig(
{
state,
}: IConnectionFormSubmitData,
@@ -32,6 +32,7 @@ export const PublicConnectionForm: React.FC = observer(function PublicConnection
<Loader loading={service.formState === null}>
{() => service.formState && (
<ConnectionForm
// key={service.formState.id}
state={service.formState}
onSave={save}
onCancel={close}
@@ -38,12 +38,6 @@ export class PublicConnectionFormService {
private readonly connectionAuthService: ConnectionAuthService,
private readonly sessionDataResource: SessionDataResource
) {
makeObservable(this, {
formState: observable.shallow,
change: action,
open: action,
close: action,
});
this.formState = null;
this.optionsPanelService.closeTask.addHandler(this.closeHandler);
this.connectionInfoResource.onDataUpdate.addPostHandler(this.closeRemoved);
@@ -51,9 +45,20 @@ export class PublicConnectionFormService {
this.sessionDataResource.onDataOutdated.addHandler(() => {
this.close(true);
});
makeObservable(this, {
formState: observable.shallow,
change: action,
open: action,
close: action,
});
}
change(config: ConnectionConfig, availableDrivers?: string[]): void {
// if (this.formState) {
// this.formState.dispose();
// }
if (!this.formState) {
this.formState = new ConnectionFormState(
this.connectionFormService,
@@ -80,6 +85,10 @@ export class PublicConnectionFormService {
}
async close(saved?: boolean): Promise<void> {
if (!this.formState) {
return;
}
if (saved) {
this.clearFormState();
}
@@ -101,7 +110,7 @@ export class PublicConnectionFormService {
}
}
private closeRemoved: IExecutorHandler<ResourceKey<string>> = (data, contexts) => {
private readonly closeRemoved: IExecutorHandler<ResourceKey<string>> = (data, contexts) => {
if (!this.formState || !this.formState.config.connectionId) {
return;
}
@@ -111,7 +120,7 @@ export class PublicConnectionFormService {
}
};
private closeDeleted: IExecutorHandler<ResourceKey<string>> = (data, contexts) => {
private readonly closeDeleted: IExecutorHandler<ResourceKey<string>> = (data, contexts) => {
if (!this.formState || !this.formState.config.connectionId) {
return;
}
@@ -121,7 +130,7 @@ export class PublicConnectionFormService {
}
};
private closeHandler: IExecutorHandler<any> = async (data, contexts) => {
private readonly closeHandler: IExecutorHandler<any> = async (data, contexts) => {
if (
!this.formState
|| !this.optionsPanelService.isOpen(formGetter)