CB-4067 removes config from form state

This commit is contained in:
sergeyteleshev
2025-03-19 17:32:54 +01:00
parent da102aafb3
commit 89d89ebc7b
34 changed files with 393 additions and 464 deletions
@@ -1,10 +1,11 @@
/*
* CloudBeaver - Cloud Database Manager
* Copyright (C) 2020-2024 DBeaver Corp and others
* Copyright (C) 2020-2025 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 { action, makeObservable, observable, runInAction, toJS } from 'mobx';
import { AppAuthService, UserInfoResource } from '@cloudbeaver/core-authentication';
@@ -269,13 +270,6 @@ export class ConnectionInfoResource extends CachedMapResource<IConnectionInfoPar
return this.sessionUpdate;
}
getEmptyConfig(): ConnectionConfig {
return {
template: false,
saveCredentials: false,
};
}
isConnecting(key: IConnectionInfoParams): boolean;
isConnecting(key: ResourceKeyList<IConnectionInfoParams>): boolean;
isConnecting(key: ResourceKey<IConnectionInfoParams>): boolean;
@@ -1,11 +1,12 @@
/*
* CloudBeaver - Cloud Database Manager
* Copyright (C) 2020-2024 DBeaver Corp and others
* Copyright (C) 2020-2025 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 { makeObservable, observable } from 'mobx';
import { makeObservable, observable, runInAction } from 'mobx';
import { ConfirmationDialog } from '@cloudbeaver/core-blocks';
import { ConnectionInfoResource, ConnectionsManagerService, createConnectionParam } from '@cloudbeaver/core-connections';
@@ -15,7 +16,7 @@ import { NotificationService } from '@cloudbeaver/core-events';
import { ExecutorInterrupter, type IExecutorHandler } from '@cloudbeaver/core-executor';
import type { AdminConnectionSearchInfo } from '@cloudbeaver/core-sdk';
import { OptionsPanelService } from '@cloudbeaver/core-ui';
import { ConnectionFormService, ConnectionFormState } from '@cloudbeaver/plugin-connections';
import { ConnectionFormService, ConnectionFormState, getConnectionFormOptionsPart } from '@cloudbeaver/plugin-connections';
import { SearchDatabase } from './SearchDatabase.js';
@@ -102,13 +103,17 @@ export class ConnectionSearchService {
this.close();
};
get optionsPart() {
return this.formState ? getConnectionFormOptionsPart(this.formState) : null;
}
private async showUnsavedChangesDialog(): Promise<boolean> {
if (
!this.formState ||
!this.optionsPanelService.isOpen(formGetter) ||
(this.formState.state.config.connectionId &&
(this.optionsPart?.state.connectionId &&
this.formState.state.projectId !== null &&
!this.connectionInfoResource.has(createConnectionParam(this.formState.state.projectId, this.formState.state.config.connectionId)))
!this.connectionInfoResource.has(createConnectionParam(this.formState.state.projectId, this.optionsPart.state.connectionId)))
) {
return true;
}
@@ -148,18 +153,19 @@ export class ConnectionSearchService {
if (!this.formState) {
this.formState = new ConnectionFormState(this.serviceProvider, this.connectionFormService, {
config: {
...this.connectionInfoResource.getEmptyConfig(),
host: database.host,
port: String(database.port),
driverId: database.defaultDriver,
},
submitType: null,
projectId: projects[0]!.id,
availableDrivers: database.possibleDrivers,
type: 'public',
requiredNetworkHandlersIds: [],
});
runInAction(() => {
this.optionsPart!.state.host = database.host;
this.optionsPart!.state.port = String(database.port);
this.optionsPart!.state.driverId = database.defaultDriver;
});
this.formState.disposeTask.addHandler(this.goBack.bind(this));
}
}
@@ -1,15 +1,16 @@
/*
* CloudBeaver - Cloud Database Manager
* Copyright (C) 2020-2024 DBeaver Corp and others
* Copyright (C) 2020-2025 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-lite';
import { Loader, s, useS } from '@cloudbeaver/core-blocks';
import { type IConnectionInfoParams } from '@cloudbeaver/core-connections';
import { ConnectionFormLoader, useConnectionFormState } from '@cloudbeaver/plugin-connections';
import { ConnectionFormLoader, getConnectionFormOptionsPart, useConnectionFormState } from '@cloudbeaver/plugin-connections';
import styles from './ConnectionEdit.module.css';
import { FormMode } from '@cloudbeaver/core-ui';
@@ -22,9 +23,10 @@ interface Props {
export const ConnectionEdit = observer<Props>(function ConnectionEditNew({ item }) {
const data = useConnectionFormState(item, state => {
state.setMode(FormMode.Edit);
const optionsPart = getConnectionFormOptionsPart(state);
runInAction(() => {
state.state.config.connectionId = item.connectionId;
optionsPart.state.connectionId = item.connectionId;
state.state.projectId = item.projectId;
state.state.type = 'admin';
});
@@ -1,10 +1,11 @@
/*
* CloudBeaver - Cloud Database Manager
* Copyright (C) 2020-2024 DBeaver Corp and others
* Copyright (C) 2020-2025 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-lite';
import {
@@ -25,7 +26,7 @@ import {
import { DBDriverResource } from '@cloudbeaver/core-connections';
import { useService } from '@cloudbeaver/core-di';
import { TabListStyles, TabPanelList, TabPanelStyles, TabsState, TabStyles } from '@cloudbeaver/core-ui';
import { ConnectionFormLoader } from '@cloudbeaver/plugin-connections';
import { ConnectionFormLoader, getConnectionFormOptionsPart } from '@cloudbeaver/plugin-connections';
import { CreateConnectionService } from '../CreateConnectionService.js';
import styles from './shared/CreateConnection.module.css';
@@ -48,7 +49,8 @@ export const CreateConnection = observer<Props>(function CreateConnection({ meth
const style = useS(styles);
const createConnectionService = useService(CreateConnectionService);
const translate = useTranslate();
const driver = useResource(CreateConnection, DBDriverResource, createConnectionService.data?.state.config.driverId || null);
const optionsPart = createConnectionService.data ? getConnectionFormOptionsPart(createConnectionService.data) : null;
const driver = useResource(CreateConnection, DBDriverResource, optionsPart?.state.driverId || null);
if (createConnectionService.data) {
return (
@@ -1,11 +1,11 @@
/*
* CloudBeaver - Cloud Database Manager
* Copyright (C) 2020-2024 DBeaver Corp and others
* Copyright (C) 2020-2025 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 { ConnectionInfoResource } from '@cloudbeaver/core-connections';
import { injectable } from '@cloudbeaver/core-di';
import { CreateConnectionService } from '../../CreateConnectionService.js';
@@ -20,7 +20,7 @@ export class ConnectionManualService {
this.createConnectionService.disabled = value;
}
constructor(private readonly connectionInfoResource: ConnectionInfoResource, private readonly createConnectionService: CreateConnectionService) {
constructor(private readonly createConnectionService: CreateConnectionService) {
this.select = this.select.bind(this);
}
@@ -28,7 +28,6 @@ export class ConnectionManualService {
this.createConnectionService.setConnectionTemplate(
projectId,
{
...this.connectionInfoResource.getEmptyConfig(),
template: true,
driverId,
},
@@ -5,13 +5,13 @@
* Licensed under the Apache License, Version 2.0.
* you may not use this file except in compliance with the License.
*/
import { action, makeObservable, observable } from 'mobx';
import { action, makeObservable, observable, runInAction } from 'mobx';
import { AdministrationScreenService } from '@cloudbeaver/core-administration';
import { injectable, IServiceProvider } from '@cloudbeaver/core-di';
import type { ConnectionConfig } from '@cloudbeaver/core-sdk';
import { TabsContainer } from '@cloudbeaver/core-ui';
import { ConnectionFormService, ConnectionFormState } from '@cloudbeaver/plugin-connections';
import { ConnectionFormService, ConnectionFormState, getConnectionFormOptionsPart } from '@cloudbeaver/plugin-connections';
import { ConnectionsAdministrationNavService } from './ConnectionsAdministrationNavService.js';
@@ -104,16 +104,26 @@ export class CreateConnectionService {
this.activateMethod(defaultId);
}
get optionsPart() {
return this.data ? getConnectionFormOptionsPart(this.data) : null;
}
setConnectionTemplate(projectId: string, config: ConnectionConfig, availableDrivers: string[]): void {
this.data?.dispose();
this.data = new ConnectionFormState(this.serviceProvider, this.connectionFormService, {
projectId,
availableDrivers,
config,
submitType: null,
type: 'admin',
requiredNetworkHandlersIds: [],
});
runInAction(() => {
this.optionsPart!.state = {
...this.optionsPart!.state,
...config,
};
});
this.data.disposeTask.addHandler(this.cancelCreate.bind(this));
}
@@ -5,6 +5,7 @@
* Licensed under the Apache License, Version 2.0.
* you may not use this file except in compliance with the License.
*/
import { action, computed, observable } from 'mobx';
import { observer } from 'mobx-react-lite';
import { useMemo, useState } from 'react';
@@ -29,11 +30,10 @@ import type { TLocalizationToken } from '@cloudbeaver/core-localization';
import { CachedMapAllKey, CachedResourceOffsetPageListKey } from '@cloudbeaver/core-resource';
import { FormMode, type TabContainerPanelComponent, useTab } from '@cloudbeaver/core-ui';
import type { IConnectionFormProps } from '@cloudbeaver/plugin-connections';
import { getConnectionFormOptionsPart } from '@cloudbeaver/plugin-connections';
import styles from './ConnectionAccess.module.css';
import { ConnectionAccessGrantedList } from './ConnectionAccessGrantedList.js';
import { ConnectionAccessList } from './ConnectionAccessList.js';
import { useService } from '@cloudbeaver/core-di';
import { getConnectionFormAccessPart } from './getConnectionFormAccessPart.js';
export const ConnectionAccess: TabContainerPanelComponent<IConnectionFormProps> = observer(function ConnectionAccess({ tabId, formState }) {
@@ -74,14 +74,12 @@ export const ConnectionAccess: TabContainerPanelComponent<IConnectionFormProps>
[accessPart.state, teams.resource],
);
if (!selected) {
return null;
}
const connectionInfoService = useService(ConnectionInfoResource);
const originInfoService = useService(ConnectionInfoOriginResource);
const connectionInfo = connectionInfoService.get(createConnectionParam(formState.state.projectId, formState.state.config.connectionId!));
const originInfo = originInfoService.get(createConnectionParam(formState.state.projectId, formState.state.config.connectionId!));
const optionsPart = getConnectionFormOptionsPart(formState);
const key = createConnectionParam(formState.state.projectId, optionsPart.state.connectionId!);
const connectionInfoResource = useResource(ConnectionAccess, ConnectionInfoResource, key);
const originInfoResource = useResource(ConnectionAccess, ConnectionInfoOriginResource, key);
const connectionInfo = connectionInfoResource.data;
const originInfo = originInfoResource.data;
const loading = users.isLoading() || teams.isLoading() || accessPart.isLoading();
const cloud = connectionInfo && originInfo?.origin ? isCloudConnection(originInfo.origin) : false;
const disabled = loading || !accessPart.isLoaded() || formState.isDisabled || cloud;
@@ -93,6 +91,10 @@ export const ConnectionAccess: TabContainerPanelComponent<IConnectionFormProps>
info = 'cloud_connections_access_placeholder';
}
if (!selected) {
return null;
}
return (
<Loader className={s(style, { loader: true })} state={[users, teams, accessPart]}>
{() => (
@@ -1,6 +1,6 @@
/*
* CloudBeaver - Cloud Database Manager
* Copyright (C) 2020-2024 DBeaver Corp and others
* Copyright (C) 2020-2025 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0.
* you may not use this file except in compliance with the License.
@@ -11,7 +11,7 @@ import { AdministrationScreenService } from '@cloudbeaver/core-administration';
import { Bootstrap, injectable } from '@cloudbeaver/core-di';
import { isGlobalProject, ProjectInfoResource } from '@cloudbeaver/core-projects';
import { EAdminPermission, PermissionsService } from '@cloudbeaver/core-root';
import { ConnectionFormService } from '@cloudbeaver/plugin-connections';
import { ConnectionFormService, getConnectionFormOptionsPart } from '@cloudbeaver/plugin-connections';
import { getConnectionFormAccessPart } from './getConnectionFormAccessPart.js';
const ConnectionAccess = React.lazy(async () => {
@@ -41,7 +41,11 @@ export class ConnectionAccessTabService extends Bootstrap {
order: 4,
stateGetter: context => () => getConnectionFormAccessPart(context.formState),
isHidden: (_, context) => !context || !this.isAccessTabActive(context.formState.state.projectId),
isDisabled: (tabId, props) => !props?.formState.state.config.driverId || this.administrationScreenService.isConfigurationMode,
isDisabled: (tabId, props) => {
const optionsPart = props?.formState ? getConnectionFormOptionsPart(props.formState) : null;
return !optionsPart?.state.driverId || this.administrationScreenService.isConfigurationMode;
},
panel: () => ConnectionAccess,
});
}
@@ -9,7 +9,7 @@ import { FormPart, formStatusContext, type IFormState } from '@cloudbeaver/core-
import type { IExecutionContextProvider } from '@cloudbeaver/core-executor';
import { createConnectionParam, type ConnectionInfoResource } from '@cloudbeaver/core-connections';
import { action, makeObservable } from 'mobx';
import type { IConnectionFormState } from '@cloudbeaver/plugin-connections';
import { getConnectionFormOptionsPart, type IConnectionFormState } from '@cloudbeaver/plugin-connections';
import { getSubjectDifferences } from '@cloudbeaver/core-utils';
function getDefaultState(): string[] {
@@ -29,21 +29,9 @@ export class ConnectionFormAccessPart extends FormPart<string[], IConnectionForm
});
}
override isOutdated(): boolean {
const connectionId = this.formState.state.config.connectionId;
const projectId = this.formState.state.projectId;
if (!connectionId || !projectId) {
return false;
}
const key = createConnectionParam(projectId, connectionId);
return this.connectionInfoResource.isOutdated(key);
}
protected override async loader(): Promise<void> {
const connectionId = this.formState.state.config.connectionId;
const optionsPart = getConnectionFormOptionsPart(this.formState);
const connectionId = optionsPart.state.connectionId;
const projectId = this.formState.state.projectId;
if (!connectionId || !projectId) {
@@ -62,7 +50,8 @@ export class ConnectionFormAccessPart extends FormPart<string[], IConnectionForm
contexts: IExecutionContextProvider<IFormState<IConnectionFormState>>,
): Promise<void> {
const status = contexts.getContext(formStatusContext);
const connectionId = this.formState.state.config.connectionId;
const optionsPart = getConnectionFormOptionsPart(this.formState);
const connectionId = optionsPart.state.connectionId;
if (this.formState.state.submitType === 'test' || !data.state.projectId || !status.saved || !connectionId || !this.loaded) {
return;
@@ -19,6 +19,7 @@ import type { ConnectionFormState } from './ConnectionFormState.js';
import { getFirstException } from '@cloudbeaver/core-utils';
import { ConnectionFormService } from './ConnectionFormService.js';
import { ConnectionInfoResource, createConnectionParam } from '@cloudbeaver/core-connections';
import { getConnectionFormOptionsPart } from './Options/getConnectionFormOptionsPart.js';
export interface ConnectionFormProps {
formState: ConnectionFormState;
@@ -41,7 +42,8 @@ export const ConnectionForm = observer<ConnectionFormProps>(function ConnectionF
const initialMode = formState.mode;
const saved = await formState.save();
const info = connectionInfoResource.get(createConnectionParam(formState.state.projectId, formState.state.config.connectionId!));
const optionsPart = getConnectionFormOptionsPart(formState);
const info = connectionInfoResource.get(createConnectionParam(formState.state.projectId, optionsPart.state.connectionId!));
if (saved) {
if (formState.state.submitType === 'submit') {
@@ -75,9 +77,10 @@ export const ConnectionForm = observer<ConnectionFormProps>(function ConnectionF
function save(data, contexts) {
const validation = contexts.getContext(formValidationContext);
const state = contexts.getContext(formStatusContext);
const optionsPart = getConnectionFormOptionsPart(data);
if (validation.valid && state.saved && data.state.submitType === 'submit') {
props.onSave(data.state.config);
props.onSave(optionsPart.state);
}
},
],
@@ -1,10 +1,11 @@
/*
* CloudBeaver - Cloud Database Manager
* Copyright (C) 2020-2024 DBeaver Corp and others
* Copyright (C) 2020-2025 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-lite';
import { useContext } from 'react';
@@ -16,7 +17,7 @@ import { ServerConfigResource } from '@cloudbeaver/core-root';
import { ConnectionFormActionsContext } from './ConnectFormActionsContext.js';
import type { IConnectionFormProps } from './IConnectionFormState.js';
import { useService } from '@cloudbeaver/core-di';
import { getConnectionFormOptionsPart } from './Options/getConnectionFormOptionsPart.js';
export const ConnectionFormBaseActions: PlaceholderComponent<IConnectionFormProps> = observer(function ConnectionFormBaseActions({ formState }) {
const actions = useContext(ConnectionFormActionsContext);
@@ -25,24 +26,25 @@ export const ConnectionFormBaseActions: PlaceholderComponent<IConnectionFormProp
}
const translate = useTranslate();
const driverMap = useResource(ConnectionFormBaseActions, DBDriverResource, formState.state.config.driverId || null);
const optionsPart = getConnectionFormOptionsPart(formState);
const driverMap = useResource(ConnectionFormBaseActions, DBDriverResource, optionsPart.state.driverId || null);
const connectionInfoService = useService(ConnectionInfoResource);
const info = connectionInfoService.get(createConnectionParam(formState.state.projectId, formState.state.config.connectionId!));
const info = connectionInfoService.get(createConnectionParam(formState.state.projectId, optionsPart.state.connectionId!));
const driver = driverMap.data;
const serverConfigResource = useResource(ConnectionFormBaseActions, ServerConfigResource, undefined);
const { data: authModel } = useResource(
ConnectionFormBaseActions,
DatabaseAuthModelsResource,
getComputed(() => formState.state.config.authModelId || info?.authModel || driver?.defaultAuthModel || null),
getComputed(() => optionsPart.state.authModelId || info?.authModel || driver?.defaultAuthModel || null),
);
const authentication = useAuthenticationAction({
providerId: authModel?.requiredAuth ?? info?.requiredAuth ?? AUTH_PROVIDER_LOCAL_ID,
});
const authorized = authentication.providerId === AUTH_PROVIDER_LOCAL_ID || authentication.authorized;
const disableTest = serverConfigResource.data?.distributed && !!formState.state.config.sharedCredentials;
const disableTest = serverConfigResource.data?.distributed && !!optionsPart.state.sharedCredentials;
return (
<>
@@ -10,52 +10,45 @@ import { FormState } from '@cloudbeaver/core-ui';
import { ConnectionFormService } from './ConnectionFormService.js';
import type { IConnectionFormState } from './IConnectionFormState.js';
import { ProjectInfoResource, ProjectsService } from '@cloudbeaver/core-projects';
import { ConnectionInfoResource, createConnectionParam, type IConnectionInfoParams } from '@cloudbeaver/core-connections';
import type { ResourceKeySimple } from '@cloudbeaver/core-resource';
export class ConnectionFormState extends FormState<IConnectionFormState> {
constructor(
private readonly serviceProvider: IServiceProvider,
service: ConnectionFormService,
config: IConnectionFormState,
) {
constructor(serviceProvider: IServiceProvider, service: ConnectionFormService, config: IConnectionFormState) {
super(serviceProvider, service, config);
const projectInfoResource = serviceProvider.getService(ProjectInfoResource);
const projectsService = serviceProvider.getService(ProjectsService);
const resource = serviceProvider.getService(ConnectionInfoResource);
// const projectInfoResource = serviceProvider.getService(ProjectInfoResource);
// const projectsService = serviceProvider.getService(ProjectsService);
// const resource = serviceProvider.getService(ConnectionInfoResource);
resource.onItemUpdate.removeHandler(this.syncInfo.bind(this));
projectInfoResource.onDataUpdate.removeHandler(this.syncProject.bind(this));
projectsService.onActiveProjectChange.removeHandler(this.syncProject.bind(this));
// resource.onItemUpdate.removeHandler(this.syncInfo.bind(this));
// projectInfoResource.onDataUpdate.removeHandler(this.syncProject.bind(this));
// projectsService.onActiveProjectChange.removeHandler(this.syncProject.bind(this));
}
private async syncInfo(key: ResourceKeySimple<IConnectionInfoParams>) {
const resource = this.serviceProvider.getService(ConnectionInfoResource);
// private async syncInfo(key: ResourceKeySimple<IConnectionInfoParams>) {
// const resource = this.serviceProvider.getService(ConnectionInfoResource);
if (
!this.state.config.connectionId ||
this.state.projectId === null ||
!resource.isIntersect(key, createConnectionParam(this.state.projectId, this.state.config.connectionId))
) {
return;
}
// if (
// !this.state.config.connectionId ||
// this.state.projectId === null ||
// !resource.isIntersect(key, createConnectionParam(this.state.projectId, this.state.config.connectionId))
// ) {
// return;
// }
await this.reload();
}
// await this.reload();
// }
private async syncProject() {
if (!this.state.projectId) {
return;
}
// private async syncProject() {
// if (!this.state.projectId) {
// return;
// }
const projectInfoResource = this.serviceProvider.getService(ProjectInfoResource);
const projectsService = this.serviceProvider.getService(ProjectsService);
// const projectInfoResource = this.serviceProvider.getService(ProjectInfoResource);
// const projectsService = this.serviceProvider.getService(ProjectsService);
const project = projectInfoResource.get(this.state.projectId);
if (!project?.canEditDataSources || !projectsService.activeProjects.includes(project)) {
await this.dispose();
}
}
// const project = projectInfoResource.get(this.state.projectId);
// if (!project?.canEditDataSources || !projectsService.activeProjects.includes(project)) {
// await this.dispose();
// }
// }
}
@@ -1,15 +1,15 @@
/*
* CloudBeaver - Cloud Database Manager
* Copyright (C) 2020-2024 DBeaver Corp and others
* Copyright (C) 2020-2025 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 { Bootstrap, injectable } from '@cloudbeaver/core-di';
import { DriverPropertiesLoader } from './DriverPropertiesLoader.js';
import { ConnectionFormService } from '../ConnectionFormService.js';
import { getConnectionFormOptionsPart } from '../Options/getConnectionFormOptionsPart.js';
@injectable()
export class ConnectionDriverPropertiesTabService extends Bootstrap {
@@ -25,9 +25,12 @@ export class ConnectionDriverPropertiesTabService extends Bootstrap {
order: 2,
panel: () => DriverPropertiesLoader,
isDisabled: (tabId, props) => {
if (props?.formState.state.config.driverId) {
return !props.formState.state.config.driverId;
const optionsPart = props?.formState ? getConnectionFormOptionsPart(props.formState) : null;
if (optionsPart?.state.driverId) {
return false;
}
return true;
},
});
@@ -10,6 +10,8 @@ import type { IExecutionContextProvider } from '@cloudbeaver/core-executor';
import { ConnectionInfoResource, createConnectionParam } from '@cloudbeaver/core-connections';
import type { IConnectionFormState } from '../IConnectionFormState.js';
import type { IConnectionProperties } from '../Options/IConnectionConfig.js';
import { getConnectionFormOptionsPart } from '../Options/getConnectionFormOptionsPart.js';
import { runInAction } from 'mobx';
function getDefaultState(): IConnectionProperties {
return {};
@@ -23,19 +25,17 @@ export class ConnectionFormDriverPropertiesPart extends FormPart<IConnectionProp
super(formState, getDefaultState());
}
override isOutdated(): boolean {
return this.connectionInfoResource.isOutdated(createConnectionParam(this.formState.state.projectId, this.formState.state.config.connectionId!));
get optionsPart() {
return getConnectionFormOptionsPart(this.formState);
}
protected override async loader(): Promise<void> {
if (!this.formState.state.config.connectionId || !this.formState.state.projectId) {
if (!this.optionsPart.state.connectionId || !this.formState.state.projectId) {
this.setInitialState(getDefaultState());
return;
}
const connection = this.connectionInfoResource.get(
createConnectionParam(this.formState.state.projectId, this.formState.state.config.connectionId),
);
const connection = this.connectionInfoResource.get(createConnectionParam(this.formState.state.projectId, this.optionsPart.state.connectionId!));
if (connection?.properties) {
this.setInitialState({ ...connection.properties });
@@ -59,5 +59,14 @@ export class ConnectionFormDriverPropertiesPart extends FormPart<IConnectionProp
this.state[key] = this.state[key].trim();
}
}
const optionsPart = getConnectionFormOptionsPart(this.formState);
runInAction(() => {
optionsPart.state.properties = {
...optionsPart.state.properties,
...this.state,
};
});
}
}
@@ -1,6 +1,6 @@
/*
* CloudBeaver - Cloud Database Manager
* Copyright (C) 2020-2024 DBeaver Corp and others
* Copyright (C) 2020-2025 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0.
* you may not use this file except in compliance with the License.
@@ -17,12 +17,13 @@ import { uuid } from '@cloudbeaver/core-utils';
import styles from './DriverProperties.module.css';
import { getConnectionFormDriverPropertiesPart } from './getConnectionFormDriverPropertiesPart.js';
import type { IConnectionFormProps } from '../IConnectionFormState.js';
import { getConnectionFormOptionsPart } from '../Options/getConnectionFormOptionsPart.js';
export const DriverProperties: TabContainerPanelComponent<IConnectionFormProps> = observer(function DriverProperties({ tabId, formState }) {
const { selected } = useTab(tabId);
const style = useS(styles);
const driverPropertiesPart = getConnectionFormDriverPropertiesPart(formState);
const optionsPart = getConnectionFormOptionsPart(formState);
const [state] = useState(() => {
const propertiesList: IProperty[] = observable([]);
@@ -44,7 +45,7 @@ export const DriverProperties: TabContainerPanelComponent<IConnectionFormProps>
});
const driver = useResource(DriverProperties, DBDriverResource, {
key: (selected && formState.state.config.driverId) || null,
key: (selected && optionsPart.state.driverId) || null,
includes: ['includeDriverProperties'] as const,
});
@@ -7,13 +7,11 @@
*/
import { schema } from '@cloudbeaver/core-utils';
import { CONNECTION_CONFIG_SCHEMA } from './Options/IConnectionConfig.js';
import type { IFormProps } from '@cloudbeaver/core-ui';
export const CONNECTION_FORM_STATE_SCHEMA = schema
.object({
projectId: schema.string(),
config: CONNECTION_CONFIG_SCHEMA,
submitType: schema.enum(['submit', 'test']).nullable(),
availableDrivers: schema.array(schema.string()),
requiredNetworkHandlersIds: schema.array(schema.string()),
@@ -6,7 +6,13 @@
* you may not use this file except in compliance with the License.
*/
import { FormMode, FormPart, formStateContext, formValidationContext, type IFormState } from '@cloudbeaver/core-ui';
import { DriverConfigurationType, type ConnectionConfig, type ObjectPropertyInfo, type TestConnectionMutation } from '@cloudbeaver/core-sdk';
import {
DriverConfigurationType,
type ConnectionConfig,
type DatabaseAuthModel,
type ObjectPropertyInfo,
type TestConnectionMutation,
} from '@cloudbeaver/core-sdk';
import { ExecutorInterrupter, type IExecutionContextProvider } from '@cloudbeaver/core-executor';
import {
ConnectionInfoProjectKey,
@@ -16,6 +22,7 @@ import {
DBDriverResource,
isJDBCConnection,
type DatabaseConnection,
type DBDriver,
} from '@cloudbeaver/core-connections';
import type { ProjectInfoResource } from '@cloudbeaver/core-projects';
import { AUTH_PROVIDER_LOCAL_ID, AuthProvidersResource, UserInfoResource } from '@cloudbeaver/core-authentication';
@@ -40,6 +47,7 @@ const defaultStateGetter = () =>
configurationType: DriverConfigurationType.Manual,
keepAliveInterval: 0,
credentials: {},
template: false,
mainPropertyValues: {},
networkHandlersConfig: [],
saveCredentials: false,
@@ -66,19 +74,15 @@ export class ConnectionFormOptionsPart extends FormPart<IConnectionFormOptionsSt
this.formState.loadedTask.addPostHandler(this.formAuthState.bind(this));
}
override isOutdated(): boolean {
return this.connectionInfoResource.isOutdated(createConnectionParam(this.formState.state.projectId, this.formState.state.config.connectionId!));
}
private async formAuthState(data: IFormState<IConnectionFormState>, contexts: IExecutionContextProvider<IFormState<IConnectionFormState>>) {
const stateContext = contexts.getContext(formStateContext);
const info = this.connectionInfoResource.get(createConnectionParam(data.state.projectId, data.state.config.connectionId!));
const info = this.connectionInfoResource.get(createConnectionParam(data.state.projectId, this.state.connectionId!));
if (!this.formState.state.config.driverId || !this.formState.state.projectId) {
if (!this.state.driverId || !this.formState.state.projectId) {
return;
}
const driver = await this.dbDriverResource.load(this.formState.state.config.driverId, ['includeProviderProperties', 'includeMainProperties']);
const driver = await this.dbDriverResource.load(this.state.driverId, ['includeProviderProperties', 'includeMainProperties']);
const [authModel] = await Promise.all([this.databaseAuthModelsResource.load(driver.defaultAuthModel), this.userInfoResource.load()]);
const providerId = authModel.requiredAuth ?? info?.requiredAuth ?? AUTH_PROVIDER_LOCAL_ID;
@@ -93,22 +97,19 @@ export class ConnectionFormOptionsPart extends FormPart<IConnectionFormOptionsSt
}
private async askCredentials(data: IFormState<IConnectionFormState>, contexts: IExecutionContextProvider<IFormState<IConnectionFormState>>) {
if (
this.formState.state.submitType !== 'test' ||
(!this.formState.state.config.authModelId && !this.formState.state.requiredNetworkHandlersIds.length)
) {
if (this.formState.state.submitType !== 'test' || (!this.state.authModelId && !this.formState.state.requiredNetworkHandlersIds.length)) {
return;
}
runInAction(() => {
if (this.formState.state.config.authModelId) {
if (this.state.authModelId) {
this.state.credentials = observable(this.state.credentials!);
}
});
const result = await this.commonDialogService.open(ConnectionAuthenticationDialogLoader, {
config: this.state,
authModelId: this.formState.state.config.authModelId ?? null,
authModelId: this.state.authModelId ?? null,
networkHandlers: this.formState.state.requiredNetworkHandlersIds,
projectId: data.state.projectId,
});
@@ -120,30 +121,28 @@ export class ConnectionFormOptionsPart extends FormPart<IConnectionFormOptionsSt
protected override async loader(): Promise<void> {
if (this.formState.mode === 'create') {
await this.setDefaults();
const defaults = await this.getDefaults();
this.setInitialState({
...defaultStateGetter(),
...this.formState.state.config,
...defaults,
...this.state,
});
return;
}
if (!this.formState.state.config.connectionId || !this.formState.state.projectId) {
if (!this.state.connectionId || !this.formState.state.projectId) {
console.error('Connection id and project id should be defined');
return;
}
const info = await this.connectionInfoResource.load(
createConnectionParam(this.formState.state.projectId, this.formState.state.config.connectionId),
[
'includeAuthProperties',
'includeCredentialsSaved',
'customIncludeOptions',
'includeProperties',
'includeProviderProperties',
'includeNetworkHandlersConfig',
],
);
const info = await this.connectionInfoResource.load(createConnectionParam(this.formState.state.projectId, this.state.connectionId), [
'includeAuthProperties',
'includeCredentialsSaved',
'customIncludeOptions',
'includeProperties',
'includeProviderProperties',
'includeNetworkHandlersConfig',
]);
const config: ConnectionConfig = defaultStateGetter();
@@ -188,55 +187,97 @@ export class ConnectionFormOptionsPart extends FormPart<IConnectionFormOptionsSt
}
this.formState.state.availableDrivers = [info.driverId];
this.formState.state.config = config;
this.setInitialState(config);
this.setInitialState({ ...config });
}
private async setDefaults() {
if (!this.formState.state.config.driverId) {
return;
isNameAutoFill() {
const isAutoFill = this.state.name === this.initialState.name || this.initialState.name === null;
return isAutoFill && this.formState.mode === 'create';
}
updateNameTemplate(driver: DBDriver | undefined) {
runInAction(() => {
const info = this.connectionInfoResource.get(createConnectionParam(this.formState.state.projectId, this.state.connectionId!));
if (isJDBCConnection(driver, info)) {
this.state.name = this.state.url || '';
return;
}
if (!driver) {
this.state.name = 'New connection';
return;
}
const name = getConnectionName(driver.name || '', this.state.host, this.state.port, driver.defaultPort);
this.state.name = name;
});
}
setAuthModel(model: DatabaseAuthModel) {
this.state.credentials = {};
const info = this.connectionInfoResource.get(createConnectionParam(this.formState.state.projectId, this.state.connectionId!));
if (model.id === info?.authModel) {
if (info.authProperties) {
for (const property of info.authProperties) {
if (!property.features.includes('password')) {
this.state.credentials[property.id!] = property.value;
}
}
}
}
const driver = await this.dbDriverResource.load(this.formState.state.config.driverId, ['includeProviderProperties']);
this.state.authModelId = model.id;
}
this.formState.state.config.authModelId = driver?.defaultAuthModel;
this.formState.state.config.configurationType = getDefaultConfigurationType(driver);
private async getDefaults() {
const config = defaultStateGetter();
if (!this.formState.state.config.host) {
this.formState.state.config.host = driver?.defaultServer || 'localhost';
if (!this.state.driverId) {
config.name = 'New connection';
return config;
}
if (!this.formState.state.config.port) {
this.formState.state.config.port = driver?.defaultPort;
const driver = await this.dbDriverResource.load(this.state.driverId, ['includeProviderProperties']);
config.authModelId = driver?.defaultAuthModel;
config.configurationType = getDefaultConfigurationType(driver);
if (!this.state.host) {
config.host = driver?.defaultServer || 'localhost';
}
this.formState.state.config.databaseName = driver?.defaultDatabase;
this.formState.state.config.url = driver?.sampleURL;
if (!this.state.port) {
config.port = driver?.defaultPort;
}
config.databaseName = driver?.defaultDatabase;
config.url = driver?.sampleURL;
if (isJDBCConnection(driver)) {
this.formState.state.config.name = this.formState.state.config.url;
config.name = config.url;
} else {
this.formState.state.config.name = getConnectionName(
driver.name || '',
this.formState.state.config.host,
this.formState.state.config.port,
driver.defaultPort,
);
config.name = getConnectionName(driver.name || '', config.host, config.port, driver.defaultPort);
}
return config;
}
protected override async format(
data: IFormState<IConnectionFormState>,
contexts: IExecutionContextProvider<IFormState<IConnectionFormState>>,
): Promise<void> {
if (!this.formState.state.config.driverId || !this.formState.state.projectId) {
if (!this.state.driverId || !this.formState.state.projectId) {
return;
}
const driver = await this.dbDriverResource.load(this.formState.state.config.driverId, ['includeProviderProperties', 'includeMainProperties']);
const driver = await this.dbDriverResource.load(this.state.driverId, ['includeProviderProperties', 'includeMainProperties']);
if (this.formState.mode === 'edit') {
this.state.connectionId = this.formState.state.config.connectionId;
this.state.connectionId = this.state.connectionId;
}
this.formState.state.requiredNetworkHandlersIds = observable([]);
@@ -278,21 +319,15 @@ export class ConnectionFormOptionsPart extends FormPart<IConnectionFormOptionsSt
}
if ((this.state.authModelId || driver.defaultAuthModel) && !driver.anonymousAccess) {
this.state.authModelId = this.formState.state.config.authModelId || driver.defaultAuthModel;
this.state.authModelId = this.state.authModelId || driver.defaultAuthModel;
this.state.saveCredentials = this.state.saveCredentials || this.state.sharedCredentials;
const info = this.connectionInfoResource.get(createConnectionParam(this.formState.state.projectId, this.formState.state.config.connectionId!));
const info = this.connectionInfoResource.get(createConnectionParam(this.formState.state.projectId, this.state.connectionId!));
const properties = await this.getConnectionAuthModelProperties(this.state.authModelId, info);
if (isCredentialsChanged(properties, this.state.credentials!)) {
this.state.credentials = prepareDynamicProperties(properties, toJS(this.state.credentials!));
}
if (this.state.saveCredentials) {
delete this.formState.state.config.authModelId;
} else {
this.formState.state.config.authModelId = this.state.authModelId;
}
}
if (driver.providerProperties.length > 0) {
@@ -388,13 +423,10 @@ export class ConnectionFormOptionsPart extends FormPart<IConnectionFormOptionsSt
if (this.formState.state.submitType === 'submit') {
if (this.formState.mode === 'edit') {
await this.connectionInfoResource.update(
createConnectionParam(this.formState.state.projectId, this.formState.state.config.connectionId!),
this.state,
);
await this.connectionInfoResource.update(createConnectionParam(this.formState.state.projectId, this.state.connectionId!), this.state);
} else {
const connection = await this.connectionInfoResource.create(this.formState.state.projectId, this.state);
this.formState.state.config.connectionId = connection.id;
this.state.connectionId = connection.id;
this.formState.setMode(FormMode.Edit);
}
} else {
@@ -42,5 +42,4 @@ export const CONNECTION_CONFIG_SCHEMA = schema.object({
userPassword: schema.string().optional(),
});
export type IConnectionConfig = schema.infer<typeof CONNECTION_CONFIG_SCHEMA>;
export type IConnectionProperties = schema.infer<typeof CONNECTION_PROPERTIES_SCHEMA>;
@@ -60,9 +60,9 @@ import { AdvancedPropertiesForm } from './AdvancedPropertiesForm.js';
import styles from './Options.module.css';
import { ParametersForm } from './ParametersForm.js';
import { ProviderPropertiesForm } from './ProviderPropertiesForm.js';
import { useOptions } from './useOptions.js';
import { getConnectionFormOptionsPart } from './getConnectionFormOptionsPart.js';
import type { IConnectionFormProps } from '../IConnectionFormState.js';
import { runInAction } from 'mobx';
const PROFILE_AUTH_MODEL_ID = 'profile';
@@ -97,39 +97,32 @@ export const Options: TabContainerPanelComponent<IConnectionFormProps> = observe
const isSharedProject = projectInfoResource.isProjectShared(formState.state.projectId);
const connectionInfoService = useService(ConnectionInfoResource);
const connectionInfoOriginResource = useService(ConnectionInfoOriginResource);
const info = connectionInfoService.get(createConnectionParam(formState.state.projectId, formState.state.config.connectionId!));
const originInfo = connectionInfoOriginResource.get(createConnectionParam(formState.state.projectId, formState.state.config.connectionId!));
const optionsPart = getConnectionFormOptionsPart(formState);
const config = optionsPart.state;
const info = connectionInfoService.get(createConnectionParam(formState.state.projectId, optionsPart.state.connectionId!));
const key = createConnectionParam(formState.state.projectId, optionsPart.state.connectionId!);
const originInfo = key ? connectionInfoOriginResource.get(key) : null;
//@TODO it's here until the profile implementation in the CloudBeaver
const readonly = formState.isDisabled || info?.authModel === PROFILE_AUTH_MODEL_ID;
useFormValidator(formState.validationTask, formRef.current);
const optionsHook = useOptions(formState, optionsPart.state);
const { credentialsSavingEnabled } = useAdministrationSettings();
const driverMap = useResource(
Options,
DBDriverResource,
{ key: formState.state.config.driverId || config.driverId || null, includes: ['includeProviderProperties', 'includeMainProperties'] as const },
{
onData: data => {
optionsHook.setDefaults(data);
},
},
);
const driverMap = useResource(Options, DBDriverResource, {
key: optionsPart.state.driverId || null,
includes: ['includeProviderProperties', 'includeMainProperties'] as const,
});
const driver = driverMap.data;
const configurationTypes = driverConfiguration.filter(configuration => driver && configuration.isVisible(driver));
function handleFormChange(value?: unknown, name?: string) {
if (name !== 'name' && optionsHook.isNameAutoFill()) {
optionsHook.updateNameTemplate(driver);
if (name !== 'name' && optionsPart.isNameAutoFill()) {
optionsPart.updateNameTemplate(driver);
}
if (config.template) {
config.folder = undefined;
if (optionsPart.state.template) {
optionsPart.state.folder = undefined;
}
}
@@ -138,9 +131,9 @@ export const Options: TabContainerPanelComponent<IConnectionFormProps> = observe
const authModelLoader = useResource(
Options,
DatabaseAuthModelsResource,
getComputed(() => config.authModelId || info?.authModel || driver?.defaultAuthModel || null),
getComputed(() => optionsPart.state.authModelId || info?.authModel || driver?.defaultAuthModel || null),
{
onData: optionsHook.setAuthModel,
onData: optionsPart.setAuthModel.bind(optionsPart),
},
);
@@ -157,7 +150,7 @@ export const Options: TabContainerPanelComponent<IConnectionFormProps> = observe
return;
}
await optionsHook.setAuthModel(model);
optionsPart.setAuthModel(model);
}
const authentication = useAuthenticationAction({
@@ -181,11 +174,11 @@ export const Options: TabContainerPanelComponent<IConnectionFormProps> = observe
let properties = authModel?.properties;
if (info?.authProperties && info.authProperties.length > 0 && config.authModelId === info.authModel) {
if (info?.authProperties && info.authProperties.length > 0 && optionsPart.state.authModelId === info.authModel) {
properties = info.authProperties;
}
const sharedCredentials = config.sharedCredentials && serverConfigResource.data?.distributed;
const sharedCredentials = optionsPart.state.sharedCredentials && serverConfigResource.data?.distributed;
function openCredentialsTab(event: React.MouseEvent<HTMLAnchorElement>) {
event.preventDefault();
@@ -197,7 +190,10 @@ export const Options: TabContainerPanelComponent<IConnectionFormProps> = observe
return;
}
formState.state.config.driverId = driverId;
runInAction(() => {
optionsPart.state.driverId = driverId;
});
await formState.reload();
}
@@ -218,7 +214,7 @@ export const Options: TabContainerPanelComponent<IConnectionFormProps> = observe
<Container wrap gap>
<Combobox
name="driverId"
state={config}
state={optionsPart.state}
items={drivers}
onSelect={reloadForm}
keySelector={driver => driver.id}
@@ -237,7 +233,7 @@ export const Options: TabContainerPanelComponent<IConnectionFormProps> = observe
{configurationTypes.length > 1 && (
<FormFieldDescription label={translate('connections_connection_configuration')} tiny>
<Container gap>
<RadioGroup name="configurationType" state={config}>
<RadioGroup name="configurationType" state={optionsPart.state}>
{configurationTypes.map(conf => (
<Radio
key={conf.value}
@@ -256,29 +252,29 @@ export const Options: TabContainerPanelComponent<IConnectionFormProps> = observe
</FormFieldDescription>
)}
</Container>
{config.configurationType === DriverConfigurationType.Url && (
{optionsPart.state.configurationType === DriverConfigurationType.Url && (
<InputField
type="text"
name="url"
state={config}
state={optionsPart.state}
readOnly={readonly || formState.isDisabled}
autoComplete={`section-${config.driverId || 'driver'} section-jdbc`}
autoComplete={`section-${optionsPart.state.driverId || 'driver'} section-jdbc`}
>
{translate('plugin_connections_connection_form_part_main_url_jdbc')}
</InputField>
)}
{config.configurationType === DriverConfigurationType.Manual &&
{optionsPart.state.configurationType === DriverConfigurationType.Manual &&
(driver?.useCustomPage ? (
<ObjectPropertyInfoForm
state={config.mainPropertyValues}
state={optionsPart.state.mainPropertyValues}
properties={driver.mainProperties ?? EMPTY_ARRAY}
disabled={formState.isDisabled}
readOnly={readonly}
/>
) : (
<ParametersForm
config={config}
config={optionsPart.state}
embedded={driver?.embedded}
requiresServerName={driver?.requiresServerName}
disabled={formState.isDisabled}
@@ -290,10 +286,10 @@ export const Options: TabContainerPanelComponent<IConnectionFormProps> = observe
</Group>
<Group form gap>
<Container wrap gap>
<InputField type="text" name="name" minLength={1} state={config} readOnly={readonly || formState.isDisabled} required fill>
<InputField type="text" name="name" minLength={1} state={optionsPart.state} readOnly={readonly || formState.isDisabled} required fill>
{translate('connections_connection_name')}
</InputField>
{!config.template && (
{!optionsPart.state.template && (
<ProjectSelect
value={formState.state.projectId}
readOnly={readonly || edit}
@@ -302,12 +298,12 @@ export const Options: TabContainerPanelComponent<IConnectionFormProps> = observe
onChange={setProject}
/>
)}
{!config.template && (
{!optionsPart.state.template && (
<InputField
type="text"
name="folder"
state={config}
autoComplete={`section-${config.driverId || 'driver'} section-folder`}
state={optionsPart.state}
autoComplete={`section-${optionsPart.state.driverId || 'driver'} section-folder`}
autoHide
readOnly
tiny
@@ -317,7 +313,7 @@ export const Options: TabContainerPanelComponent<IConnectionFormProps> = observe
</InputField>
)}
</Container>
<Textarea name="description" rows={3} state={config} readOnly={readonly || formState.isDisabled}>
<Textarea name="description" rows={3} state={optionsPart.state} readOnly={readonly || formState.isDisabled}>
{translate('connections_connection_description')}
</Textarea>
</Group>
@@ -328,10 +324,10 @@ export const Options: TabContainerPanelComponent<IConnectionFormProps> = observe
<GroupTitle>{translate('connections_connection_edit_authentication')}</GroupTitle>
{serverConfigResource.resource.distributed && isSharedProject && (
<FieldCheckbox
id={config.connectionId + 'isShared'}
id={optionsPart.state.connectionId + 'isShared'}
name="sharedCredentials"
title={translate('connections_connection_share_credentials_tooltip')}
state={config}
state={optionsPart.state}
disabled={formState.isDisabled || readonly}
keepSize
>
@@ -339,7 +335,7 @@ export const Options: TabContainerPanelComponent<IConnectionFormProps> = observe
</FieldCheckbox>
)}
<ConnectionAuthModelSelector
authModelCredentialsState={config}
authModelCredentialsState={optionsPart.state}
applicableAuthModels={applicableAuthModels}
readonlyAuthModelId={!originLocal}
readonly={readonly}
@@ -350,7 +346,7 @@ export const Options: TabContainerPanelComponent<IConnectionFormProps> = observe
<>
{properties && (
<ConnectionAuthModelCredentialsForm
credentials={config.credentials}
credentials={optionsPart.state.credentials}
properties={properties}
readonly={readonly}
disabled={formState.isDisabled}
@@ -365,12 +361,12 @@ export const Options: TabContainerPanelComponent<IConnectionFormProps> = observe
</Link>
</FormFieldDescription>
)}
{!sharedCredentials && authModel && credentialsSavingEnabled && !config.template && (
{!sharedCredentials && authModel && credentialsSavingEnabled && !optionsPart.state.template && (
<FieldCheckbox
id={config.connectionId + 'authNeeded'}
id={optionsPart.state.connectionId + 'authNeeded'}
name="saveCredentials"
state={config}
disabled={formState.isDisabled || readonly || config.sharedCredentials}
state={optionsPart.state}
disabled={formState.isDisabled || readonly || optionsPart.state.sharedCredentials}
mod={['primary']}
title={translate(
!isSharedProject || serverConfigResource.data?.distributed
@@ -389,10 +385,15 @@ export const Options: TabContainerPanelComponent<IConnectionFormProps> = observe
</Group>
)}
{driver?.providerProperties && (
<ProviderPropertiesForm config={config} properties={driver.providerProperties} disabled={formState.isDisabled} readonly={readonly} />
<ProviderPropertiesForm
config={optionsPart.state}
properties={driver.providerProperties}
disabled={formState.isDisabled}
readonly={readonly}
/>
)}
<AdvancedPropertiesForm config={config} disabled={formState.isDisabled} readonly={readonly} />
<AdvancedPropertiesForm config={optionsPart.state} disabled={formState.isDisabled} readonly={readonly} />
</Container>
</ColoredContainer>
</Form>
@@ -1,133 +0,0 @@
/*
* CloudBeaver - Cloud Database Manager
* Copyright (C) 2020-2024 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0.
* you may not use this file except in compliance with the License.
*/
import { runInAction } from 'mobx';
import { useObjectRef } from '@cloudbeaver/core-blocks';
import { ConnectionInfoResource, createConnectionParam, type DBDriver, DBDriverResource, isJDBCConnection } from '@cloudbeaver/core-connections';
import { useService } from '@cloudbeaver/core-di';
import type { ConnectionConfig, DatabaseAuthModel } from '@cloudbeaver/core-sdk';
import { getConnectionName } from './getConnectionName.js';
import { getDefaultConfigurationType } from './getDefaultConfigurationType.js';
import type { IFormState } from '@cloudbeaver/core-ui';
import type { IConnectionFormState } from '../IConnectionFormState.js';
export function useOptions(formState: IFormState<IConnectionFormState>, config: ConnectionConfig) {
const mode = formState.mode;
const connectionInfoService = useService(ConnectionInfoResource);
const info = connectionInfoService.get(createConnectionParam(formState.state.projectId, formState.state.config.connectionId!));
const dbDriverResource = useService(DBDriverResource);
const refObject = useObjectRef(
() => ({
prevName: null as string | null,
prevDriverId: null as string | null,
}),
{
config,
info,
mode,
formState,
},
);
return useObjectRef({
updateNameTemplate(driver: DBDriver | undefined) {
runInAction(() => {
const { config, info } = refObject;
if (isJDBCConnection(driver, info)) {
refObject.prevName = config.url || '';
config.name = config.url || '';
return;
}
if (!driver) {
config.name = 'New connection';
return;
}
const name = getConnectionName(driver.name || '', config.host, config.port, driver.defaultPort);
refObject.prevName = name;
config.name = name;
});
},
setDefaults(driver: DBDriver | undefined) {
runInAction(() => {
const { config, info, prevDriverId } = refObject;
if (info || driver?.id !== config.driverId) {
return;
}
let prevDriver: DBDriver | undefined;
if (prevDriverId) {
prevDriver = dbDriverResource.get(prevDriverId);
}
refObject.prevDriverId = driver?.id || null;
if (!config.configurationType || !driver?.configurationTypes.includes(config.configurationType)) {
config.configurationType = getDefaultConfigurationType(driver);
}
if ((!prevDriver && config.host === undefined) || config.host === prevDriver?.defaultServer) {
config.host = driver?.defaultServer || 'localhost';
}
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;
}
if (this.isNameAutoFill()) {
this.updateNameTemplate(driver);
}
if (driver?.id !== prevDriver?.id) {
config.credentials = {};
config.providerProperties = {};
config.authModelId = driver?.defaultAuthModel;
}
});
},
async setAuthModel(model: DatabaseAuthModel) {
const { config, info, formState } = refObject;
config.credentials = {};
if (model.id === info?.authModel) {
if (info.authProperties) {
for (const property of info.authProperties) {
if (!property.features.includes('password')) {
config.credentials[property.id!] = property.value;
}
}
}
}
await formState.reload();
},
isNameAutoFill() {
const { prevName, config, mode } = refObject;
const isAutoFill = config.name === prevName || prevName === null;
return isAutoFill && mode === 'create';
},
});
}
@@ -1,6 +1,6 @@
/*
* CloudBeaver - Cloud Database Manager
* Copyright (C) 2020-2024 DBeaver Corp and others
* Copyright (C) 2020-2025 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0.
* you may not use this file except in compliance with the License.
@@ -13,18 +13,20 @@ import { ConnectionInfoResource, createConnectionParam, DatabaseAuthModelsResour
import type { IConnectionFormProps } from '../IConnectionFormState.js';
import { useService } from '@cloudbeaver/core-di';
import { getConnectionFormOptionsPart } from '../Options/getConnectionFormOptionsPart.js';
export const AuthenticationButton: PlaceholderComponent<IConnectionFormProps> = observer(function ConnectionFormAuthenticationAction({ formState }) {
const translate = useTranslate();
const driverMap = useResource(ConnectionFormAuthenticationAction, DBDriverResource, formState.state.config.driverId || null);
const optionsPart = getConnectionFormOptionsPart(formState);
const driverMap = useResource(ConnectionFormAuthenticationAction, DBDriverResource, optionsPart.state.driverId || null);
const connectionInfoService = useService(ConnectionInfoResource);
const info = connectionInfoService.get(createConnectionParam(formState.state.projectId, formState.state.config.connectionId!));
const info = connectionInfoService.get(createConnectionParam(formState.state.projectId, optionsPart.state.connectionId!));
const driver = driverMap.data;
const { data: authModel } = useResource(
ConnectionFormAuthenticationAction,
DatabaseAuthModelsResource,
getComputed(() => formState.state.config.authModelId || info?.authModel || driver?.defaultAuthModel || null),
getComputed(() => optionsPart.state.authModelId || info?.authModel || driver?.defaultAuthModel || null),
);
const authentication = useAuthenticationAction({
@@ -46,15 +48,16 @@ export const AuthenticationButton: PlaceholderComponent<IConnectionFormProps> =
export const ConnectionFormAuthenticationAction: PlaceholderComponent<IConnectionFormProps> = observer(function ConnectionFormAuthenticationAction({
formState,
}) {
const driverMap = useResource(ConnectionFormAuthenticationAction, DBDriverResource, formState.state.config.driverId || null);
const optionsPart = getConnectionFormOptionsPart(formState);
const driverMap = useResource(ConnectionFormAuthenticationAction, DBDriverResource, optionsPart.state.driverId || null);
const connectionInfoService = useService(ConnectionInfoResource);
const info = connectionInfoService.get(createConnectionParam(formState.state.projectId, formState.state.config.connectionId!));
const info = connectionInfoService.get(createConnectionParam(formState.state.projectId, optionsPart.state.connectionId!));
const driver = driverMap.data;
const { data: authModel } = useResource(
ConnectionFormAuthenticationAction,
DatabaseAuthModelsResource,
getComputed(() => formState.state.config.authModelId || info?.authModel || driver?.defaultAuthModel || null),
getComputed(() => optionsPart.state.authModelId || info?.authModel || driver?.defaultAuthModel || null),
);
if (!authModel?.requiredAuth && !info?.requiredAuth) {
@@ -42,22 +42,23 @@ export class ConnectionFormOriginInfoFormPart extends FormPart<IConnectionFormOr
});
}
get optionsPart() {
return getConnectionFormOptionsPart(this.formState);
}
get providerId(): string | null {
if (!this.formState.state.projectId || !this.formState.state.config.driverId) {
if (!this.formState.state.projectId || !this.optionsPart.state.driverId) {
return null;
}
const driver = this.dbDriverResource.get(this.formState.state.config.driverId);
const driver = this.dbDriverResource.get(this.optionsPart.state.driverId);
if (!driver) {
return null;
}
const optionsPart = getConnectionFormOptionsPart(this.formState);
const info = this.connectionInfoResource.get(createConnectionParam(this.formState.state.projectId, optionsPart.state.connectionId!));
const authModel = this.databaseAuthModelsResource.get(
this.formState.state.config.authModelId ?? info?.authModel ?? driver?.defaultAuthModel ?? null,
);
const info = this.connectionInfoResource.get(createConnectionParam(this.formState.state.projectId, this.optionsPart.state.connectionId!));
const authModel = this.databaseAuthModelsResource.get(this.optionsPart.state.authModelId ?? info?.authModel ?? driver?.defaultAuthModel ?? null);
const providerId = authModel?.requiredAuth ?? info?.requiredAuth ?? AUTH_PROVIDER_LOCAL_ID;
return providerId;
@@ -71,29 +72,12 @@ export class ConnectionFormOriginInfoFormPart extends FormPart<IConnectionFormOr
return this.userInfoResource.hasToken(this.providerId);
}
override isOutdated(): boolean {
const isDriverOutdated = this.dbDriverResource.isOutdated(this.formState.state.config.driverId);
const optionsPart = getConnectionFormOptionsPart(this.formState);
const connectionId = optionsPart.state.connectionId;
const isConnectionOutdated = connectionId
? this.connectionInfoResource.isOutdated(createConnectionParam(this.formState.state.projectId, connectionId))
: false;
const isAuthModelOutdated = this.authModelId ? this.databaseAuthModelsResource.isOutdated(this.authModelId) : false;
// TODO why is app goes to infinite loop when this is uncommented?
// const isOriginInfoOutdated = connectionId
// ? this.connectionInfoOriginDetailsResource.isOutdated(createConnectionParam(this.formState.state.projectId, connectionId))
// : false;
return isDriverOutdated || isConnectionOutdated || isAuthModelOutdated || this.userInfoResource.isOutdated() || optionsPart.isOutdated();
}
get authModelId(): string | null {
const driver = this.dbDriverResource.get(this.formState.state.config.driverId!)!;
const optionsPart = getConnectionFormOptionsPart(this.formState);
const driver = this.dbDriverResource.get(optionsPart.state.driverId!)!;
const info = this.connectionInfoResource.get(createConnectionParam(this.formState.state.projectId, optionsPart.state.connectionId!));
return this.formState.state.config.authModelId ?? info?.authModel ?? driver?.defaultAuthModel ?? null;
return optionsPart.state.authModelId ?? info?.authModel ?? driver?.defaultAuthModel ?? null;
}
protected override async loader(): Promise<void> {
@@ -101,12 +85,12 @@ export class ConnectionFormOriginInfoFormPart extends FormPart<IConnectionFormOr
const optionsPart = getConnectionFormOptionsPart(this.formState);
const connectionId = optionsPart.state.connectionId;
if (!connectionId || !this.formState.state.projectId || !this.formState.state.config.driverId) {
if (!connectionId || !this.formState.state.projectId || !optionsPart.state.driverId) {
this.setInitialState(state);
return;
}
await this.dbDriverResource.load(this.formState.state.config.driverId);
await this.dbDriverResource.load(optionsPart.state.driverId);
if (!this.authModelId) {
throw new Error('Auth model is not defined');
@@ -12,7 +12,7 @@ import { Bootstrap, injectable } from '@cloudbeaver/core-di';
import { ConnectionFormService } from '../ConnectionFormService.js';
import { importLazyComponent } from '@cloudbeaver/core-blocks';
import { CachedMapAllKey, getCachedMapResourceLoaderState } from '@cloudbeaver/core-resource';
import { getConnectionFormOptionsPart } from '../Options/getConnectionFormOptionsPart.js';
export const ConnectionFormAuthenticationAction = importLazyComponent(() =>
import('./ConnectionFormAuthenticationAction.js').then(m => m.ConnectionFormAuthenticationAction),
);
@@ -38,7 +38,8 @@ export class ConnectionOriginInfoTabService extends Bootstrap {
getLoader: () => getCachedMapResourceLoaderState(this.connectionInfoOriginResource, () => CachedMapAllKey),
isHidden: (tabId, props) => {
const projectId = props?.formState.state.projectId;
const connectionId = props?.formState.state.config.connectionId;
const optionsPart = props?.formState ? getConnectionFormOptionsPart(props.formState) : null;
const connectionId = optionsPart?.state.connectionId;
if (!projectId || !connectionId) {
return true;
@@ -1,11 +1,10 @@
/*
* CloudBeaver - Cloud Database Manager
* Copyright (C) 2020-2024 DBeaver Corp and others
* Copyright (C) 2020-2025 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-lite';
import { AuthProvidersResource } from '@cloudbeaver/core-authentication';
@@ -30,6 +29,7 @@ import styles from './OriginInfo.module.css';
import type { IConnectionFormProps } from '../IConnectionFormState.js';
import { useService } from '@cloudbeaver/core-di';
import { getConnectionFormOriginInfoFormPart } from './getConnectionFormOriginInfoFormPart.js';
import { getConnectionFormOptionsPart } from '../Options/getConnectionFormOptionsPart.js';
export const OriginInfo: TabContainerPanelComponent<IConnectionFormProps> = observer(function OriginInfo({ tabId, formState }) {
const tab = useTab(tabId);
@@ -37,7 +37,8 @@ export const OriginInfo: TabContainerPanelComponent<IConnectionFormProps> = obse
const originInfoPart = getConnectionFormOriginInfoFormPart(formState);
const style = useS(styles);
const connectionInfoService = useService(ConnectionInfoResource);
const info = connectionInfoService.get(createConnectionParam(formState.state.projectId, formState.state.config.connectionId!));
const optionsPart = getConnectionFormOptionsPart(formState);
const info = connectionInfoService.get(createConnectionParam(formState.state.projectId, optionsPart.state.connectionId!));
const providerLoader = useResource(OriginInfo, AuthProvidersResource, originInfoPart.providerId);
const connectionId = tab.selected && info ? createConnectionParam(info.projectId, info.id) : null;
const isAuthenticated = getComputed(() => originInfoPart.isAuthenticated);
@@ -12,12 +12,13 @@ import { Tab, type TabContainerTabComponent, TabTitle } from '@cloudbeaver/core-
import type { IConnectionFormProps } from '../IConnectionFormState.js';
import { ConnectionInfoOriginResource, createConnectionParam } from '@cloudbeaver/core-connections';
import { getConnectionFormOptionsPart } from '../Options/getConnectionFormOptionsPart.js';
export const OriginInfoTab: TabContainerTabComponent<IConnectionFormProps> = observer(function OriginInfoTab({ formState, ...rest }) {
const optionsPart = getConnectionFormOptionsPart(formState);
const connectionInfoOriginResource = useResource(
OriginInfoTab,
ConnectionInfoOriginResource,
createConnectionParam(formState.state.projectId, formState.state.config.connectionId!),
createConnectionParam(formState.state.projectId, optionsPart.state.connectionId!),
);
return (
@@ -42,18 +42,18 @@ export class ConnectionFormSSHPart extends FormPart<INetworkHandlerConfig, IConn
super(formState, getDefaultState());
}
override isOutdated(): boolean {
return this.connectionInfoResource.isOutdated(createConnectionParam(this.formState.state.projectId, this.formState.state.config.connectionId!));
get optionsPart() {
return getConnectionFormOptionsPart(this.formState);
}
protected override async loader(): Promise<void> {
if (!this.formState.state.config.connectionId || !this.formState.state.projectId) {
if (!this.optionsPart.state.connectionId || !this.formState.state.projectId) {
this.setInitialState(getDefaultState());
return;
}
const connection = await this.connectionInfoResource.load(
createConnectionParam(this.formState.state.projectId, this.formState.state.config.connectionId),
createConnectionParam(this.formState.state.projectId, this.optionsPart.state.connectionId!),
);
this.setInitialState(connection?.networkHandlersConfig?.find(h => h.id === SSH_TUNNEL_ID) ?? getDefaultState());
@@ -68,8 +68,8 @@ export class ConnectionFormSSHPart extends FormPart<INetworkHandlerConfig, IConn
data: IFormState<IConnectionFormState>,
contexts: IExecutionContextProvider<IFormState<IConnectionFormState>>,
): void | Promise<void> {
const urlType = this.formState.state.config.configurationType === DriverConfigurationType.Url;
const optionsPart = getConnectionFormOptionsPart(this.formState);
const urlType = optionsPart.state.configurationType === DriverConfigurationType.Url;
if (urlType) {
return;
@@ -1,6 +1,6 @@
/*
* CloudBeaver - Cloud Database Manager
* Copyright (C) 2020-2024 DBeaver Corp and others
* Copyright (C) 2020-2025 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0.
* you may not use this file except in compliance with the License.
@@ -40,9 +40,10 @@ export class ConnectionSSHTabService extends Bootstrap {
tab: () => SSHTab,
panel: () => SSHPanel,
isHidden: (tabId, props) => {
if (props?.formState.state.config.driverId) {
const driver = this.dbDriverResource.get(props?.formState.state.config.driverId);
const optionsPart = getConnectionFormOptionsPart(props.formState);
const optionsPart = props?.formState ? getConnectionFormOptionsPart(props.formState) : null;
if (optionsPart?.state.driverId) {
const driver = this.dbDriverResource.get(optionsPart.state.driverId);
const urlType = optionsPart.state.configurationType === DriverConfigurationType.Url;
return urlType || !driver?.applicableNetworkHandlers.includes(SSH_TUNNEL_ID);
@@ -44,25 +44,18 @@ export class ConnectionFormSSLPart extends FormPart<INetworkHandlerConfig, IConn
super(formState, getDefaultState());
}
override isOutdated(): boolean {
const isDriverOutdated = this.dbDriverResource.isOutdated(this.formState.state.config.driverId);
const isNetworkHandlerOutdated = this.networkHandlerResource.isOutdated(this.state.id);
const isConnectionOutdated = this.connectionInfoResource.isOutdated(
createConnectionParam(this.formState.state.projectId, this.formState.state.config.connectionId!),
);
return isDriverOutdated || isNetworkHandlerOutdated || isConnectionOutdated;
get optionsPart() {
return getConnectionFormOptionsPart(this.formState);
}
protected override async loader(): Promise<void> {
if (!this.formState.state.config.driverId) {
if (!this.optionsPart.state.driverId) {
this.setInitialState(getDefaultState());
return;
}
const optionsPart = getConnectionFormOptionsPart(this.formState);
const [driver, handlers] = await Promise.all([
this.dbDriverResource.load(this.formState.state.config.driverId),
this.dbDriverResource.load(this.optionsPart.state.driverId),
this.networkHandlerResource.load(CachedMapAllKey),
]);
const handler = getSSLDriverHandler(handlers, driver?.applicableNetworkHandlers ?? []);
@@ -72,10 +65,10 @@ export class ConnectionFormSSLPart extends FormPart<INetworkHandlerConfig, IConn
return;
}
const info = this.connectionInfoResource.get(createConnectionParam(this.formState.state.projectId, this.formState.state.config.connectionId!));
const info = this.connectionInfoResource.get(createConnectionParam(this.formState.state.projectId, this.optionsPart.state.connectionId!));
const initialConfig = info?.networkHandlersConfig?.find(h => h.id === handler.id);
if (!optionsPart.state.networkHandlersConfig?.some(state => state.id === handler.id)) {
if (!this.optionsPart.state.networkHandlersConfig?.some(state => state.id === handler.id)) {
const config: NetworkHandlerConfigInput = initialConfig ? toJS(initialConfig) : getSSLDefaultConfig(handler.id);
if (config.secureProperties) {
@@ -93,11 +86,12 @@ export class ConnectionFormSSLPart extends FormPart<INetworkHandlerConfig, IConn
data: IFormState<IConnectionFormState>,
contexts: IExecutionContextProvider<IFormState<IConnectionFormState>>,
): Promise<void> {
if (!this.formState.state.config.driverId) {
const optionsPart = getConnectionFormOptionsPart(this.formState);
if (!optionsPart.state.driverId) {
return;
}
const optionsPart = getConnectionFormOptionsPart(this.formState);
const handlers = await this.networkHandlerResource.load(CachedMapAllKey);
const descriptor = handlers.find(h => h.id === this.state?.id);
@@ -1,6 +1,6 @@
/*
* CloudBeaver - Cloud Database Manager
* Copyright (C) 2020-2024 DBeaver Corp and others
* Copyright (C) 2020-2025 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0.
* you may not use this file except in compliance with the License.
@@ -9,7 +9,7 @@ import React from 'react';
import { DBDriverResource, NetworkHandlerResource } from '@cloudbeaver/core-connections';
import { Bootstrap, injectable } from '@cloudbeaver/core-di';
import { getConnectionFormOptionsPart } from '../Options/getConnectionFormOptionsPart.js';
import { getSSLDriverHandler } from './getSSLDriverHandler.js';
import { ConnectionFormService } from '../ConnectionFormService.js';
@@ -39,8 +39,10 @@ export class ConnectionSSLTabService extends Bootstrap {
tab: () => SSLTab,
panel: () => SSLPanel,
isHidden: (_, props) => {
if (props?.formState.state.config.driverId) {
const driver = this.dbDriverResource.get(props?.formState.state.config.driverId);
const optionsPart = props?.formState ? getConnectionFormOptionsPart(props.formState) : null;
if (optionsPart?.state.driverId) {
const driver = this.dbDriverResource.get(optionsPart.state.driverId);
const handler = getSSLDriverHandler(this.networkHandlerResource.values, driver?.applicableNetworkHandlers ?? []);
return !handler;
}
@@ -1,10 +1,11 @@
/*
* CloudBeaver - Cloud Database Manager
* Copyright (C) 2020-2024 DBeaver Corp and others
* Copyright (C) 2020-2025 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-lite';
import React from 'react';
@@ -53,12 +54,12 @@ export const SSL: TabContainerPanelComponent<Props> = observer(function SSL({ fo
const disabled = formState.isDisabled;
const enabled = handlerState.enabled || false;
const connectionInfoService = useService(ConnectionInfoResource);
const info = connectionInfoService.get(createConnectionParam(formState.state.projectId, formState.state.config.connectionId!));
const optionsPart = getConnectionFormOptionsPart(formState);
const info = connectionInfoService.get(createConnectionParam(formState.state.projectId, optionsPart.state.connectionId!));
const initialHandler = info?.networkHandlersConfig?.find(h => h.id === handler.id);
const autofillToken = isSafari ? 'section-connection-authentication-ssl section-ssl' : 'new-password';
const projectInfoResource = useService(ProjectInfoResource);
const isSharedProject = projectInfoResource.isProjectShared(formState.state.projectId);
const optionsPart = getConnectionFormOptionsPart(formState);
useAutoLoad(SSL, optionsPart);
@@ -16,10 +16,12 @@ import { getSSLDriverHandler } from './getSSLDriverHandler.js';
import { SSL } from './SSL.js';
import { getConnectionFormSSLPart } from './getConnectionFormSSLPart.js';
import type { IConnectionFormProps } from '../IConnectionFormState.js';
import { getConnectionFormOptionsPart } from '../Options/getConnectionFormOptionsPart.js';
export const SSLPanel: TabContainerTabComponent<IConnectionFormProps> = observer(function SSLPanel(props) {
const networkHandlerResource = useResource(SSLPanel, NetworkHandlerResource, CachedMapAllKey);
const dbDriverResource = useResource(SSLPanel, DBDriverResource, props.formState.state.config.driverId ?? null);
const optionsPart = getConnectionFormOptionsPart(props.formState);
const dbDriverResource = useResource(SSLPanel, DBDriverResource, optionsPart.state.driverId ?? null);
const handler = getSSLDriverHandler(networkHandlerResource.resource.values, dbDriverResource.data?.applicableNetworkHandlers ?? []);
const sslPart = getConnectionFormSSLPart(props.formState);
@@ -1,10 +1,11 @@
/*
* CloudBeaver - Cloud Database Manager
* Copyright (C) 2020-2024 DBeaver Corp and others
* Copyright (C) 2020-2025 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-lite';
import { Translate, useResource } from '@cloudbeaver/core-blocks';
@@ -14,10 +15,12 @@ import { Tab, type TabContainerTabComponent, TabTitle } from '@cloudbeaver/core-
import { getSSLDriverHandler } from './getSSLDriverHandler.js';
import type { IConnectionFormProps } from '../IConnectionFormState.js';
import { getConnectionFormOptionsPart } from '../Options/getConnectionFormOptionsPart.js';
export const SSLTab: TabContainerTabComponent<IConnectionFormProps> = observer(function SSLTab(props) {
const networkHandlerResource = useResource(SSLTab, NetworkHandlerResource, CachedMapAllKey);
const dbDriverResource = useResource(SSLTab, DBDriverResource, props.formState.state.config.driverId ?? null);
const optionsPart = getConnectionFormOptionsPart(props.formState);
const dbDriverResource = useResource(SSLTab, DBDriverResource, optionsPart.state.driverId ?? null);
const handler = getSSLDriverHandler(networkHandlerResource.resource.values, dbDriverResource.data?.applicableNetworkHandlers ?? []);
@@ -11,10 +11,11 @@ import { ConnectionFormState } from './ConnectionFormState.js';
import { ConnectionFormService } from './ConnectionFormService.js';
import type { IConnectionFormState } from './IConnectionFormState.js';
import type { IConnectionInfoParams } from '@cloudbeaver/core-connections';
import { getConnectionFormOptionsPart } from './Options/getConnectionFormOptionsPart.js';
import { runInAction } from 'mobx';
const EMPTY_CONNECTION_INFO_PARAMS: IConnectionFormState = {
projectId: '',
config: {},
submitType: 'submit',
availableDrivers: [],
type: 'admin',
@@ -25,17 +26,21 @@ export function useConnectionFormState(params: IConnectionInfoParams, configure?
const serviceProvider = useService(IServiceProvider);
const service = useService(ConnectionFormService);
const ref = useRef<ConnectionFormState>(null);
let optionsPart = ref.current ? getConnectionFormOptionsPart(ref.current) : null;
if (ref.current?.state.config.connectionId !== params.connectionId || ref.current?.state.projectId !== params.projectId) {
if (optionsPart?.state.connectionId !== params.connectionId || ref.current?.state.projectId !== params.projectId) {
ref.current?.dispose();
ref.current = new ConnectionFormState(serviceProvider, service, {
...EMPTY_CONNECTION_INFO_PARAMS,
projectId: params.projectId,
config: {
...EMPTY_CONNECTION_INFO_PARAMS.config,
connectionId: params.connectionId,
},
});
optionsPart = getConnectionFormOptionsPart(ref.current);
runInAction(() => {
optionsPart!.state.connectionId = params.connectionId;
});
configure?.(ref.current);
}
@@ -1,11 +1,12 @@
/*
* CloudBeaver - Cloud Database Manager
* Copyright (C) 2020-2024 DBeaver Corp and others
* Copyright (C) 2020-2025 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 { action, makeObservable, observable } from 'mobx';
import { action, makeObservable, observable, runInAction } from 'mobx';
import { UserInfoResource } from '@cloudbeaver/core-authentication';
import { ConfirmationDialog, importLazyComponent } from '@cloudbeaver/core-blocks';
@@ -21,6 +22,7 @@ import { AuthenticationService } from '@cloudbeaver/plugin-authentication';
import { ConnectionFormState } from '../ConnectionForm/ConnectionFormState.js';
import { ConnectionFormService } from '../ConnectionForm/ConnectionFormService.js';
import { getConnectionFormOptionsPart } from '../ConnectionForm/Options/getConnectionFormOptionsPart.js';
const PublicConnectionForm = importLazyComponent(() => import('./PublicConnectionForm.js').then(m => m.PublicConnectionForm));
@@ -74,11 +76,18 @@ export class PublicConnectionFormService {
this.formState = new ConnectionFormState(this.serviceProvider, this.connectionFormService, {
projectId,
availableDrivers: availableDrivers ?? [],
config,
submitType: 'submit',
type: 'public',
requiredNetworkHandlersIds: [],
});
runInAction(() => {
this.optionsPart!.state = {
...this.optionsPart!.state,
...config,
};
});
this.formState.disposeTask.addHandler(this.close.bind(this, true));
}
@@ -114,9 +123,11 @@ export class PublicConnectionFormService {
}
async save(): Promise<void> {
const optionsPart = this.formState ? getConnectionFormOptionsPart(this.formState) : null;
const key =
this.formState && this.formState.state.config.connectionId && this.formState.state.projectId !== null
? createConnectionParam(this.formState.state.projectId, this.formState.state.config.connectionId)
this.formState && optionsPart?.state.connectionId && this.formState.state.projectId !== null
? createConnectionParam(this.formState.state.projectId, optionsPart.state.connectionId)
: null;
await this.close(true);
@@ -126,24 +137,26 @@ export class PublicConnectionFormService {
}
}
get optionsPart() {
return this.formState ? getConnectionFormOptionsPart(this.formState) : null;
}
private readonly closeRemoved: IExecutorHandler<ResourceKey<IConnectionInfoParams>> = (data, contexts) => {
if (!this.formState || !this.formState.state.config.connectionId || this.formState.state.projectId === null) {
if (!this.formState || !this.optionsPart?.state.connectionId || this.formState.state.projectId === null) {
return;
}
if (!this.connectionInfoResource.has(createConnectionParam(this.formState.state.projectId, this.formState.state.config.connectionId))) {
if (!this.connectionInfoResource.has(createConnectionParam(this.formState.state.projectId, this.optionsPart.state.connectionId!))) {
this.close(true);
}
};
private readonly closeDeleted: IExecutorHandler<ResourceKeySimple<IConnectionInfoParams>> = (data, contexts) => {
if (!this.formState || !this.formState.state.config.connectionId || this.formState.state.projectId === null) {
if (!this.formState || !this.optionsPart?.state.connectionId || this.formState.state.projectId === null) {
return;
}
if (
this.connectionInfoResource.isIntersect(data, createConnectionParam(this.formState.state.projectId, this.formState.state.config.connectionId))
) {
if (this.connectionInfoResource.isIntersect(data, createConnectionParam(this.formState.state.projectId, this.optionsPart.state.connectionId!))) {
this.close(true);
}
};
@@ -157,12 +170,14 @@ export class PublicConnectionFormService {
};
private async showUnsavedChangesDialog(): Promise<boolean> {
const optionsPart = this.formState ? getConnectionFormOptionsPart(this.formState) : null;
if (
!this.formState ||
!this.optionsPanelService.isOpen(formGetter) ||
(this.formState.state.config.connectionId &&
(optionsPart?.state.connectionId &&
this.formState.state.projectId !== null &&
!this.connectionInfoResource.has(createConnectionParam(this.formState.state.projectId, this.formState.state.config.connectionId)))
!this.connectionInfoResource.has(createConnectionParam(this.formState.state.projectId, optionsPart.state.connectionId)))
) {
return true;
}