mirror of
https://github.com/dbeaver/cloudbeaver.git
synced 2026-09-24 16:04:36 +08:00
CB-5197 create main connection fields dynamically (#2693)
* CB-5238 add main properties for connections * CB-5197 render main properties * CB-5197 render main properties * CB-5238 set default values to main properties * CB-5197 fix(ci/cd): respect base and target branches * CB-5238 fixes after review * CB-5197 review fixes * CB-5197 switch to new model * CB-5238 fixes after review * CB-5197 remove custom configuration type * CB-5238 remove custom config type from gql schema * CB-5197 do not check configuration type for creds * CB-5197 make argument optional * CB-5238 fix creating connection with url * CB-5238 rename param * CB-5238 rename param * CB-5197 rename field --------- Co-authored-by: naumov <iamemptyhuh@gmail.com> Co-authored-by: Alexey <wrouds@gmail.com> Co-authored-by: Evgenia Bezborodova <139753579+EvgeniaBzzz@users.noreply.github.com>
This commit is contained in:
co-authored by
naumov
Alexey
Evgenia Bezborodova
parent
4433018ed2
commit
49875e4dc1
@@ -32,6 +32,17 @@ jobs:
|
||||
sudo chmod 777 ../
|
||||
shell: bash
|
||||
|
||||
- name: Determine branches
|
||||
id: determine-branch
|
||||
run: |
|
||||
echo "pr_branch=${{ github.head_ref }}" >> $GITHUB_ENV
|
||||
echo "base_branch=${{ github.event.pull_request.base.ref }}" >> $GITHUB_ENV
|
||||
|
||||
- name: Clone dbeaver/dbeaver
|
||||
id: clone-repo
|
||||
run: |
|
||||
git clone -b ${{ env.pr_branch }} https://github.com/dbeaver/dbeaver.git ../dbeaver || git clone -b ${{ env.base_branch }} https://github.com/dbeaver/dbeaver.git ../dbeaver
|
||||
|
||||
- name: Run build script
|
||||
run: ./build-backend.sh
|
||||
shell: bash
|
||||
|
||||
@@ -384,6 +384,16 @@ public class WebConnectionInfo {
|
||||
}
|
||||
}
|
||||
|
||||
@Property
|
||||
public Map<String, String> getMainPropertyValues() {
|
||||
Map<String, String> mainProperties = new LinkedHashMap<>();
|
||||
mainProperties.put(DBConstants.PROP_HOST, getHost());
|
||||
mainProperties.put(DBConstants.PROP_PORT, getPort());
|
||||
mainProperties.put(DBConstants.PROP_DATABASE, getDatabaseName());
|
||||
mainProperties.put(DBConstants.PROP_SERVER, getServerName());
|
||||
return mainProperties;
|
||||
}
|
||||
|
||||
@Property
|
||||
public Map<String, String> getProviderProperties() {
|
||||
return dataSourceContainer.getConnectionConfiguration().getProviderProperties();
|
||||
|
||||
@@ -229,6 +229,7 @@ type DriverInfo {
|
||||
enabled: Boolean!
|
||||
requiresServerName: Boolean
|
||||
requiresDatabaseName: Boolean
|
||||
useCustomPage: Boolean! @since(version: "24.1.2") # if host, port, database, server name fields are custom
|
||||
|
||||
licenseRequired: Boolean
|
||||
license: String
|
||||
@@ -248,6 +249,10 @@ type DriverInfo {
|
||||
# Driver parameters (map name->value)
|
||||
driverParameters: Object!
|
||||
|
||||
# Main driver properties
|
||||
# Contains info about main fields (host, port, database, server name)
|
||||
mainProperties: [ObjectPropertyInfo!]! @since(version: "24.1.2")
|
||||
|
||||
# Additional driver provider properties
|
||||
# These properties can be configured by user on main connection page
|
||||
# to provide important connection settings
|
||||
@@ -340,6 +345,8 @@ type ConnectionInfo {
|
||||
databaseName: String
|
||||
url: String
|
||||
|
||||
mainPropertyValues: Object @since(version: "24.1.2")
|
||||
|
||||
keepAliveInterval: Int!
|
||||
autocommit: Boolean
|
||||
|
||||
@@ -499,6 +506,10 @@ input ConnectionConfig {
|
||||
port: String
|
||||
serverName: String
|
||||
databaseName: String
|
||||
|
||||
# Host, port, serverName, databaseName are also stored in mainPropertyValues for custom pages
|
||||
mainPropertyValues: Object @since(version: "24.1.2")
|
||||
|
||||
# Connection url jdbc:{driver}://{host}[:{port}]/[{database}]
|
||||
url: String
|
||||
# Properties
|
||||
|
||||
@@ -33,6 +33,7 @@ import io.cloudbeaver.utils.WebDataSourceUtils;
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.code.Nullable;
|
||||
import org.jkiss.dbeaver.Log;
|
||||
import org.jkiss.dbeaver.model.DBConstants;
|
||||
import org.jkiss.dbeaver.model.DBPDataSourceContainer;
|
||||
import org.jkiss.dbeaver.model.access.DBAAuthCredentials;
|
||||
import org.jkiss.dbeaver.model.app.DBPDataSourceRegistry;
|
||||
@@ -150,22 +151,7 @@ public class WebServiceUtils extends WebCommonUtils {
|
||||
}
|
||||
|
||||
public static void setConnectionConfiguration(DBPDriver driver, DBPConnectionConfiguration dsConfig, WebConnectionConfig config) {
|
||||
if (!CommonUtils.isEmpty(config.getUrl())) {
|
||||
dsConfig.setUrl(config.getUrl());
|
||||
} else {
|
||||
if (config.getHost() != null) {
|
||||
dsConfig.setHostName(config.getHost());
|
||||
}
|
||||
if (config.getPort() != null) {
|
||||
dsConfig.setHostPort(config.getPort());
|
||||
}
|
||||
if (config.getDatabaseName() != null) {
|
||||
dsConfig.setDatabaseName(config.getDatabaseName());
|
||||
}
|
||||
if (config.getServerName() != null) {
|
||||
dsConfig.setServerName(config.getServerName());
|
||||
}
|
||||
}
|
||||
setMainProperties(dsConfig, config);
|
||||
if (config.getProperties() != null) {
|
||||
Map<String, String> newProps = new LinkedHashMap<>();
|
||||
for (Map.Entry<String, Object> pe : config.getProperties().entrySet()) {
|
||||
@@ -222,6 +208,37 @@ public class WebServiceUtils extends WebCommonUtils {
|
||||
}
|
||||
}
|
||||
|
||||
private static void setMainProperties(DBPConnectionConfiguration dsConfig, WebConnectionConfig config) {
|
||||
if (CommonUtils.isNotEmpty(config.getUrl())) {
|
||||
dsConfig.setUrl(config.getUrl());
|
||||
return;
|
||||
}
|
||||
if (config.getMainPropertyValues() != null) {
|
||||
for (Map.Entry<String, Object> e : config.getMainPropertyValues().entrySet()) {
|
||||
switch (e.getKey()) {
|
||||
case DBConstants.PROP_HOST -> dsConfig.setHostName(CommonUtils.toString(e.getValue()));
|
||||
case DBConstants.PROP_PORT -> dsConfig.setHostPort(CommonUtils.toString(e.getValue()));
|
||||
case DBConstants.PROP_DATABASE -> dsConfig.setDatabaseName(CommonUtils.toString(e.getValue()));
|
||||
case DBConstants.PROP_SERVER -> dsConfig.setServerName(CommonUtils.toString(e.getValue()));
|
||||
default -> throw new IllegalStateException("Unexpected value: " + e.getKey());
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (config.getHost() != null) {
|
||||
dsConfig.setHostName(config.getHost());
|
||||
}
|
||||
if (config.getPort() != null) {
|
||||
dsConfig.setHostPort(config.getPort());
|
||||
}
|
||||
if (config.getDatabaseName() != null) {
|
||||
dsConfig.setDatabaseName(config.getDatabaseName());
|
||||
}
|
||||
if (config.getServerName() != null) {
|
||||
dsConfig.setServerName(config.getServerName());
|
||||
}
|
||||
}
|
||||
|
||||
public static void saveAuthProperties(
|
||||
@NotNull DBPDataSourceContainer dataSourceContainer,
|
||||
@NotNull DBPConnectionConfiguration configuration,
|
||||
|
||||
@@ -57,6 +57,7 @@ public class WebConnectionConfig {
|
||||
private Map<String, Object> credentials;
|
||||
private boolean saveCredentials;
|
||||
private boolean sharedCredentials;
|
||||
private Map<String, Object> mainPropertyValues;
|
||||
private Map<String, Object> providerProperties;
|
||||
private List<WebNetworkHandlerConfigInput> networkHandlersConfig;
|
||||
private DBPDriverConfigurationType configurationType;
|
||||
@@ -102,6 +103,7 @@ public class WebConnectionConfig {
|
||||
saveCredentials = JSONUtils.getBoolean(params, "saveCredentials");
|
||||
sharedCredentials = JSONUtils.getBoolean(params, "sharedCredentials");
|
||||
|
||||
mainPropertyValues = JSONUtils.getObjectOrNull(params, "mainPropertyValues");
|
||||
providerProperties = JSONUtils.getObjectOrNull(params, "providerProperties");
|
||||
|
||||
String configType = JSONUtils.getString(params, "configurationType");
|
||||
@@ -227,6 +229,11 @@ public class WebConnectionConfig {
|
||||
this.saveCredentials = saveCredentials;
|
||||
}
|
||||
|
||||
@Property
|
||||
public Map<String, Object> getMainPropertyValues() {
|
||||
return mainPropertyValues;
|
||||
}
|
||||
|
||||
@Property
|
||||
public Map<String, Object> getProviderProperties() {
|
||||
return providerProperties;
|
||||
|
||||
+23
@@ -36,9 +36,11 @@ import org.jkiss.dbeaver.registry.DataSourceProviderRegistry;
|
||||
import org.jkiss.dbeaver.registry.network.NetworkHandlerDescriptor;
|
||||
import org.jkiss.dbeaver.registry.network.NetworkHandlerRegistry;
|
||||
import org.jkiss.dbeaver.runtime.properties.PropertySourceCustom;
|
||||
import org.jkiss.utils.ArrayUtils;
|
||||
import org.jkiss.utils.CommonUtils;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
@@ -233,6 +235,22 @@ public class WebDatabaseDriverInfo {
|
||||
return AuthModelDatabaseNative.ID;
|
||||
}
|
||||
|
||||
@Property
|
||||
public WebPropertyInfo[] getMainProperties() {
|
||||
DBPPropertyDescriptor[] properties = driver.getMainPropertyDescriptors();
|
||||
// set default values to main properties
|
||||
Map<String, String> defaultValues = new LinkedHashMap<>();
|
||||
defaultValues.put(DBConstants.PROP_HOST, getDefaultHost());
|
||||
defaultValues.put(DBConstants.PROP_PORT, getDefaultPort());
|
||||
defaultValues.put(DBConstants.PROP_DATABASE, getDefaultDatabase());
|
||||
defaultValues.put(DBConstants.PROP_SERVER, getDefaultServer());
|
||||
PropertySourceCustom propertySource = new PropertySourceCustom(properties, defaultValues);
|
||||
|
||||
return Arrays.stream(properties)
|
||||
.map(p -> new WebPropertyInfo(webSession, p, propertySource))
|
||||
.toArray(WebPropertyInfo[]::new);
|
||||
}
|
||||
|
||||
@Property
|
||||
public WebPropertyInfo[] getProviderProperties() {
|
||||
return Arrays.stream(driver.getProviderPropertyDescriptors())
|
||||
@@ -276,4 +294,9 @@ public class WebDatabaseDriverInfo {
|
||||
.map(dbpDriverLibrary -> new WebDriverLibraryInfo(webSession, dbpDriverLibrary))
|
||||
.toArray(WebDriverLibraryInfo[]::new);
|
||||
}
|
||||
|
||||
@Property
|
||||
public boolean getUseCustomPage() {
|
||||
return !ArrayUtils.isEmpty(driver.getMainPropertyDescriptors());
|
||||
}
|
||||
}
|
||||
|
||||
+8
-7
@@ -7,7 +7,7 @@
|
||||
*/
|
||||
import { observer } from 'mobx-react-lite';
|
||||
|
||||
import { getObjectPropertyType, type ObjectPropertyInfo, type ObjectPropertyType } from '@cloudbeaver/core-sdk';
|
||||
import { getObjectPropertyType, getObjectPropertyValueType, type ObjectPropertyInfo, type ObjectPropertyType } from '@cloudbeaver/core-sdk';
|
||||
import { removeMetadataFromDataURL } from '@cloudbeaver/core-utils';
|
||||
|
||||
import { FieldCheckbox } from '../../FormControls/Checkboxes/FieldCheckbox';
|
||||
@@ -70,7 +70,8 @@ export const RenderField = observer<RenderFieldProps>(function RenderField({
|
||||
const translate = useTranslate();
|
||||
|
||||
const controlType = getObjectPropertyType(property);
|
||||
const password = property.features.includes('password');
|
||||
const type = getObjectPropertyValueType(property);
|
||||
const isPassword = type === 'password';
|
||||
const required = property.required && !readOnly;
|
||||
|
||||
const value = getValue(property.value, controlType);
|
||||
@@ -172,7 +173,7 @@ export const RenderField = observer<RenderFieldProps>(function RenderField({
|
||||
);
|
||||
}
|
||||
|
||||
const passwordSaved = showRememberTip && ((password && !!property.value) || saved);
|
||||
const passwordSaved = showRememberTip && ((isPassword && !!property.value) || saved);
|
||||
const passwordSavedMessage = passwordSaved ? translate('core_blocks_object_property_info_password_saved') : undefined;
|
||||
|
||||
if (controlType === 'file' && state) {
|
||||
@@ -233,8 +234,8 @@ export const RenderField = observer<RenderFieldProps>(function RenderField({
|
||||
return (
|
||||
<InputField
|
||||
required={required}
|
||||
type={password ? 'password' : 'text'}
|
||||
title={password ? property.description || property.displayName : undefined}
|
||||
type={type}
|
||||
title={isPassword ? property.description || property.displayName : undefined}
|
||||
labelTooltip={property.description || property.displayName}
|
||||
name={property.id!}
|
||||
state={state}
|
||||
@@ -257,8 +258,8 @@ export const RenderField = observer<RenderFieldProps>(function RenderField({
|
||||
return (
|
||||
<InputField
|
||||
required={required}
|
||||
type={password ? 'password' : 'text'}
|
||||
title={password ? property.description || property.displayName : undefined}
|
||||
type={type}
|
||||
title={isPassword ? property.description || property.displayName : undefined}
|
||||
labelTooltip={property.description || property.displayName}
|
||||
name={property.id!}
|
||||
value={value}
|
||||
|
||||
@@ -112,6 +112,7 @@ export class DBDriverResource extends CachedMapResource<string, DBDriver, DBDriv
|
||||
includeDriverLibraries: false,
|
||||
includeDriverParameters: false,
|
||||
includeDriverProperties: false,
|
||||
includeMainProperties: false,
|
||||
includeProviderProperties: false,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
*/
|
||||
import type { ObjectPropertyInfo } from './sdk';
|
||||
|
||||
export type ObjectPropertyType = 'checkbox' | 'selector' | 'link' | 'input' | 'textarea' | 'file';
|
||||
export type ObjectPropertyType = 'checkbox' | 'selector' | 'link' | 'textarea' | 'file' | 'input';
|
||||
|
||||
export function getObjectPropertyType(property: ObjectPropertyInfo): ObjectPropertyType {
|
||||
const dataType = property.dataType?.toLowerCase();
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* 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 type { ObjectPropertyInfo } from './sdk';
|
||||
|
||||
export type ObjectPropertyValueType = 'password' | 'text' | 'number';
|
||||
|
||||
export function getObjectPropertyValueType(property: ObjectPropertyInfo): ObjectPropertyValueType | undefined {
|
||||
const dataType = property.dataType?.toLowerCase();
|
||||
|
||||
if (property.features.includes('password')) {
|
||||
return 'password';
|
||||
}
|
||||
|
||||
if (dataType === 'integer') {
|
||||
return 'number';
|
||||
}
|
||||
|
||||
return dataType === 'string' ? 'text' : undefined;
|
||||
}
|
||||
@@ -14,6 +14,7 @@ export * from './EnvironmentService';
|
||||
export * from './EServerErrorCode';
|
||||
export * from './getGQLResponse';
|
||||
export * from './getObjectPropertyType';
|
||||
export * from './getObjectPropertyValueType';
|
||||
export * from './GQLError';
|
||||
export * from './GQLErrorCatcher';
|
||||
export * from './GraphQLService';
|
||||
|
||||
@@ -1,4 +1,11 @@
|
||||
query driverList($driverId: ID, $includeProviderProperties: Boolean!, $includeDriverProperties: Boolean!, $includeDriverParameters: Boolean!, $includeDriverLibraries: Boolean!) {
|
||||
query driverList(
|
||||
$driverId: ID
|
||||
$includeProviderProperties: Boolean!
|
||||
$includeMainProperties: Boolean!
|
||||
$includeDriverProperties: Boolean!
|
||||
$includeDriverParameters: Boolean!
|
||||
$includeDriverLibraries: Boolean!
|
||||
) {
|
||||
drivers: driverList(id: $driverId) {
|
||||
...DatabaseDriver
|
||||
}
|
||||
|
||||
@@ -18,6 +18,8 @@ fragment DatabaseConnection on ConnectionInfo {
|
||||
folder
|
||||
nodePath
|
||||
|
||||
mainPropertyValues
|
||||
|
||||
configurationType @include(if: $customIncludeOptions)
|
||||
useUrl @include(if: $customIncludeOptions)
|
||||
host @include(if: $customIncludeOptions)
|
||||
|
||||
@@ -11,6 +11,7 @@ fragment DatabaseDriver on DriverInfo {
|
||||
embedded
|
||||
enabled
|
||||
requiresServerName
|
||||
useCustomPage
|
||||
anonymousAccess
|
||||
promotedScore
|
||||
providerId
|
||||
@@ -22,8 +23,12 @@ fragment DatabaseDriver on DriverInfo {
|
||||
applicableNetworkHandlers
|
||||
configurationTypes
|
||||
|
||||
mainProperties @include(if: $includeMainProperties) {
|
||||
...DriverPropertyInfo
|
||||
}
|
||||
|
||||
providerProperties @include(if: $includeProviderProperties) {
|
||||
...DriverProviderPropertyInfo
|
||||
...DriverPropertyInfo
|
||||
}
|
||||
|
||||
driverProperties @include(if: $includeDriverProperties) {
|
||||
@@ -42,4 +47,4 @@ fragment DatabaseDriver on DriverInfo {
|
||||
name
|
||||
icon
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
fragment DriverPropertyInfo on ObjectPropertyInfo {
|
||||
...ObjectPropertyInfo
|
||||
supportedConfigurationTypes
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
fragment DriverProviderPropertyInfo on ObjectPropertyInfo {
|
||||
id
|
||||
displayName
|
||||
description
|
||||
category
|
||||
dataType
|
||||
defaultValue
|
||||
validValues
|
||||
length
|
||||
features
|
||||
order
|
||||
supportedConfigurationTypes
|
||||
required
|
||||
}
|
||||
+82
-61
@@ -21,7 +21,6 @@ import { Bootstrap, injectable } from '@cloudbeaver/core-di';
|
||||
import type { IExecutionContextProvider } from '@cloudbeaver/core-executor';
|
||||
import { LocalizationService } from '@cloudbeaver/core-localization';
|
||||
import { isSharedProject, ProjectInfoResource } from '@cloudbeaver/core-projects';
|
||||
import { ServerConfigResource } from '@cloudbeaver/core-root';
|
||||
import { DriverConfigurationType, isObjectPropertyInfoStateEqual, ObjectPropertyInfo } from '@cloudbeaver/core-sdk';
|
||||
import { formStateContext } from '@cloudbeaver/core-ui';
|
||||
import { getUniqueName, isNotNullDefined, isValuesEqual } from '@cloudbeaver/core-utils';
|
||||
@@ -32,16 +31,21 @@ import { connectionConfigContext } from '../Contexts/connectionConfigContext';
|
||||
import { connectionCredentialsStateContext } from '../Contexts/connectionCredentialsStateContext';
|
||||
import type { IConnectionFormFillConfigData, IConnectionFormState, IConnectionFormSubmitData } from '../IConnectionFormProps';
|
||||
import { getConnectionName } from './getConnectionName';
|
||||
import { getDefaultConfigurationType } from './getDefaultConfigurationType';
|
||||
|
||||
export const Options = React.lazy(async () => {
|
||||
const { Options } = await import('./Options');
|
||||
return { default: Options };
|
||||
});
|
||||
|
||||
const MAIN_PROPERTY_DATABASE_KEY = 'database';
|
||||
const MAIN_PROPERTY_HOST_KEY = 'host';
|
||||
const MAIN_PROPERTY_PORT_KEY = 'port';
|
||||
const MAIN_PROPERTY_SERVER_KEY = 'server';
|
||||
|
||||
@injectable()
|
||||
export class ConnectionOptionsTabService extends Bootstrap {
|
||||
constructor(
|
||||
private readonly serverConfigResource: ServerConfigResource,
|
||||
private readonly projectInfoResource: ProjectInfoResource,
|
||||
private readonly connectionFormService: ConnectionFormService,
|
||||
private readonly dbDriverResource: DBDriverResource,
|
||||
@@ -174,10 +178,7 @@ export class ConnectionOptionsTabService extends Bootstrap {
|
||||
const driver = await this.dbDriverResource.load(state.config.driverId, ['includeProviderProperties']);
|
||||
|
||||
state.config.authModelId = driver?.defaultAuthModel;
|
||||
|
||||
state.config.configurationType = driver?.configurationTypes.includes(DriverConfigurationType.Manual)
|
||||
? DriverConfigurationType.Manual
|
||||
: DriverConfigurationType.Url;
|
||||
state.config.configurationType = getDefaultConfigurationType(driver);
|
||||
|
||||
state.config.host = driver?.defaultServer || 'localhost';
|
||||
state.config.port = driver?.defaultPort;
|
||||
@@ -205,6 +206,10 @@ export class ConnectionOptionsTabService extends Bootstrap {
|
||||
state.config.providerProperties = {};
|
||||
}
|
||||
|
||||
if (!state.config.mainPropertyValues || updated) {
|
||||
state.config.mainPropertyValues = {};
|
||||
}
|
||||
|
||||
if (!state.info) {
|
||||
await this.setDefaults(state);
|
||||
return;
|
||||
@@ -218,10 +223,11 @@ export class ConnectionOptionsTabService extends Bootstrap {
|
||||
state.config.template = state.info.template;
|
||||
state.config.driverId = state.info.driverId;
|
||||
|
||||
state.config.host = state.info.host;
|
||||
state.config.port = state.info.port;
|
||||
state.config.serverName = state.info.serverName;
|
||||
state.config.databaseName = state.info.databaseName;
|
||||
state.config.host = state.info.mainPropertyValues[MAIN_PROPERTY_HOST_KEY];
|
||||
state.config.port = state.info.mainPropertyValues[MAIN_PROPERTY_PORT_KEY];
|
||||
state.config.serverName = state.info.mainPropertyValues[MAIN_PROPERTY_SERVER_KEY];
|
||||
state.config.databaseName = state.info.mainPropertyValues[MAIN_PROPERTY_DATABASE_KEY];
|
||||
|
||||
state.config.url = state.info.url;
|
||||
state.config.folder = state.info.folder;
|
||||
|
||||
@@ -243,6 +249,10 @@ export class ConnectionOptionsTabService extends Bootstrap {
|
||||
if (state.info.providerProperties) {
|
||||
state.config.providerProperties = { ...state.info.providerProperties };
|
||||
}
|
||||
|
||||
if (state.info.mainPropertyValues) {
|
||||
state.config.mainPropertyValues = { ...state.info.mainPropertyValues };
|
||||
}
|
||||
}
|
||||
|
||||
private configure(data: IConnectionFormState, contexts: IExecutionContextProvider<IConnectionFormState>) {
|
||||
@@ -251,19 +261,6 @@ export class ConnectionOptionsTabService extends Bootstrap {
|
||||
configuration.include('includeOrigin', 'includeAuthProperties', 'includeCredentialsSaved', 'customIncludeOptions');
|
||||
}
|
||||
|
||||
private getTrimmedPropertiesConfig(authProperties: ObjectPropertyInfo[], credentials: Record<string, any>): Record<string, any> {
|
||||
const trimmedProperties: Record<string, any> = toJS(credentials);
|
||||
for (const property of authProperties) {
|
||||
const value = credentials?.[property.id!];
|
||||
|
||||
if (typeof value === 'string' && value) {
|
||||
trimmedProperties[property.id!] = value?.trim();
|
||||
}
|
||||
}
|
||||
|
||||
return trimmedProperties;
|
||||
}
|
||||
|
||||
private async prepareConfig({ state }: IConnectionFormSubmitData, contexts: IExecutionContextProvider<IConnectionFormSubmitData>) {
|
||||
const config = contexts.getContext(connectionConfigContext);
|
||||
const credentialsState = contexts.getContext(connectionCredentialsStateContext);
|
||||
@@ -272,7 +269,7 @@ export class ConnectionOptionsTabService extends Bootstrap {
|
||||
return;
|
||||
}
|
||||
|
||||
const driver = await this.dbDriverResource.load(state.config.driverId, ['includeProviderProperties']);
|
||||
const driver = await this.dbDriverResource.load(state.config.driverId, ['includeProviderProperties', 'includeMainProperties']);
|
||||
const tempConfig = toJS(config);
|
||||
|
||||
if (state.mode === 'edit') {
|
||||
@@ -280,7 +277,6 @@ export class ConnectionOptionsTabService extends Bootstrap {
|
||||
}
|
||||
|
||||
tempConfig.configurationType = state.config.configurationType;
|
||||
|
||||
tempConfig.name = state.config.name?.trim();
|
||||
|
||||
if (tempConfig.name && state.mode === 'create') {
|
||||
@@ -291,11 +287,8 @@ export class ConnectionOptionsTabService extends Bootstrap {
|
||||
}
|
||||
|
||||
tempConfig.description = state.config.description?.trim();
|
||||
|
||||
tempConfig.template = state.config.template;
|
||||
|
||||
tempConfig.driverId = state.config.driverId;
|
||||
|
||||
tempConfig.keepAliveInterval = Number(state.config.keepAliveInterval);
|
||||
tempConfig.autocommit = state.config.autocommit;
|
||||
|
||||
@@ -305,15 +298,21 @@ export class ConnectionOptionsTabService extends Bootstrap {
|
||||
|
||||
if (tempConfig.configurationType === DriverConfigurationType.Url) {
|
||||
tempConfig.url = state.config.url?.trim();
|
||||
} else {
|
||||
}
|
||||
|
||||
tempConfig.mainPropertyValues = toJS(state.config.mainPropertyValues);
|
||||
|
||||
if (tempConfig.configurationType === DriverConfigurationType.Manual && !driver.useCustomPage) {
|
||||
tempConfig.mainPropertyValues[MAIN_PROPERTY_DATABASE_KEY] = state.config.databaseName?.trim();
|
||||
|
||||
if (!driver.embedded) {
|
||||
tempConfig.host = state.config.host?.trim();
|
||||
tempConfig.port = state.config.port?.trim();
|
||||
tempConfig.mainPropertyValues[MAIN_PROPERTY_HOST_KEY] = state.config.host?.trim();
|
||||
tempConfig.mainPropertyValues[MAIN_PROPERTY_PORT_KEY] = state.config.port?.trim();
|
||||
}
|
||||
|
||||
if (driver.requiresServerName) {
|
||||
tempConfig.serverName = state.config.serverName?.trim();
|
||||
tempConfig.mainPropertyValues[MAIN_PROPERTY_SERVER_KEY] = state.config.serverName?.trim();
|
||||
}
|
||||
tempConfig.databaseName = state.config.databaseName?.trim();
|
||||
}
|
||||
|
||||
if ((state.config.authModelId || driver.defaultAuthModel) && !driver.anonymousAccess) {
|
||||
@@ -324,7 +323,7 @@ export class ConnectionOptionsTabService extends Bootstrap {
|
||||
const properties = await this.getConnectionAuthModelProperties(tempConfig.authModelId, state.info);
|
||||
|
||||
if (this.isCredentialsChanged(properties, state.config.credentials)) {
|
||||
tempConfig.credentials = this.getTrimmedPropertiesConfig(properties, { ...state.config.credentials });
|
||||
tempConfig.credentials = this.prepareDynamicProperties(properties, toJS(state.config.credentials));
|
||||
}
|
||||
|
||||
if (!tempConfig.saveCredentials) {
|
||||
@@ -333,32 +332,19 @@ export class ConnectionOptionsTabService extends Bootstrap {
|
||||
}
|
||||
|
||||
if (driver.providerProperties.length > 0) {
|
||||
const providerProperties: Record<string, any> = { ...state.config.providerProperties };
|
||||
tempConfig.providerProperties = this.prepareDynamicProperties(
|
||||
driver.providerProperties,
|
||||
toJS(state.config.providerProperties),
|
||||
tempConfig.configurationType,
|
||||
);
|
||||
}
|
||||
|
||||
for (const providerProperty of driver.providerProperties) {
|
||||
if (!providerProperty.id) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const supported = providerProperty.supportedConfigurationTypes?.some(t => t === tempConfig.configurationType);
|
||||
|
||||
if (!supported) {
|
||||
delete providerProperties[providerProperty.id];
|
||||
} else {
|
||||
const isDefault = isNotNullDefined(providerProperty.defaultValue);
|
||||
if (!(providerProperty.id in providerProperties) && isDefault) {
|
||||
providerProperties[providerProperty.id] = providerProperty.defaultValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tempConfig.providerProperties = providerProperties;
|
||||
|
||||
for (const key of Object.keys(tempConfig.providerProperties)) {
|
||||
if (typeof tempConfig.providerProperties[key] === 'string') {
|
||||
tempConfig.providerProperties[key] = tempConfig.providerProperties[key]?.trim();
|
||||
}
|
||||
}
|
||||
if (driver.useCustomPage && driver.mainProperties.length > 0) {
|
||||
tempConfig.mainPropertyValues = this.prepareDynamicProperties(
|
||||
driver.mainProperties,
|
||||
tempConfig.mainPropertyValues,
|
||||
tempConfig.configurationType,
|
||||
);
|
||||
}
|
||||
|
||||
runInAction(() => {
|
||||
@@ -366,11 +352,44 @@ export class ConnectionOptionsTabService extends Bootstrap {
|
||||
});
|
||||
}
|
||||
|
||||
private prepareDynamicProperties(
|
||||
propertiesInfo: ObjectPropertyInfo[],
|
||||
properties: Record<string, any>,
|
||||
configurationType?: DriverConfigurationType,
|
||||
) {
|
||||
const result: Record<string, any> = { ...properties };
|
||||
|
||||
for (const propertyInfo of propertiesInfo) {
|
||||
if (!propertyInfo.id) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const supported = configurationType === undefined || propertyInfo.supportedConfigurationTypes?.some(type => type === configurationType);
|
||||
|
||||
if (!supported) {
|
||||
delete result[propertyInfo.id];
|
||||
} else {
|
||||
const isDefault = isNotNullDefined(propertyInfo.defaultValue);
|
||||
if (!(propertyInfo.id in result) && isDefault) {
|
||||
result[propertyInfo.id] = propertyInfo.defaultValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const key of Object.keys(result)) {
|
||||
if (typeof result[key] === 'string') {
|
||||
result[key] = result[key]?.trim();
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private async formAuthState(data: IConnectionFormState, contexts: IExecutionContextProvider<IConnectionFormState>) {
|
||||
const config = contexts.getContext(connectionConfigContext);
|
||||
const stateContext = contexts.getContext(formStateContext);
|
||||
|
||||
const driver = await this.dbDriverResource.load(config.driverId!, ['includeProviderProperties']);
|
||||
const driver = await this.dbDriverResource.load(config.driverId!, ['includeProviderProperties', 'includeMainProperties']);
|
||||
const authModel = await this.databaseAuthModelsResource.load(config.authModelId ?? data.info?.authModel ?? driver.defaultAuthModel);
|
||||
|
||||
const providerId = authModel.requiredAuth ?? data.info?.requiredAuth ?? AUTH_PROVIDER_LOCAL_ID;
|
||||
@@ -394,7 +413,7 @@ export class ConnectionOptionsTabService extends Bootstrap {
|
||||
|
||||
const config = contexts.getContext(connectionConfigContext);
|
||||
const stateContext = contexts.getContext(formStateContext);
|
||||
const driver = await this.dbDriverResource.load(data.config.driverId!, ['includeProviderProperties']);
|
||||
const driver = await this.dbDriverResource.load(data.config.driverId!, ['includeProviderProperties', 'includeMainProperties']);
|
||||
|
||||
if (
|
||||
!isValuesEqual(config.name, data.info.name, '') ||
|
||||
@@ -414,6 +433,8 @@ export class ConnectionOptionsTabService extends Bootstrap {
|
||||
(config.sharedCredentials !== undefined && config.sharedCredentials !== data.info.sharedCredentials) ||
|
||||
(config.providerProperties !== undefined &&
|
||||
!isObjectPropertyInfoStateEqual(driver.providerProperties, config.providerProperties, data.info.providerProperties)) ||
|
||||
(config.mainPropertyValues !== undefined &&
|
||||
!isObjectPropertyInfoStateEqual(driver.mainProperties, config.mainPropertyValues, data.info.mainPropertyValues)) ||
|
||||
(config.keepAliveInterval !== undefined && !isValuesEqual(config.keepAliveInterval, data.info.keepAliveInterval)) ||
|
||||
(config.autocommit !== undefined && !isValuesEqual(config.autocommit, data.info.autocommit))
|
||||
) {
|
||||
|
||||
@@ -21,6 +21,7 @@ import {
|
||||
GroupTitle,
|
||||
InputField,
|
||||
Link,
|
||||
ObjectPropertyInfoForm,
|
||||
Radio,
|
||||
RadioGroup,
|
||||
s,
|
||||
@@ -31,11 +32,12 @@ import {
|
||||
useS,
|
||||
useTranslate,
|
||||
} from '@cloudbeaver/core-blocks';
|
||||
import { DatabaseAuthModelsResource, DBDriverResource, isLocalConnection } from '@cloudbeaver/core-connections';
|
||||
import { DatabaseAuthModelsResource, DBDriver, DBDriverResource, isLocalConnection } from '@cloudbeaver/core-connections';
|
||||
import { useService } from '@cloudbeaver/core-di';
|
||||
import { ServerConfigResource } from '@cloudbeaver/core-root';
|
||||
import { DriverConfigurationType } from '@cloudbeaver/core-sdk';
|
||||
import { type TabContainerPanelComponent, TabsContext, useAuthenticationAction } from '@cloudbeaver/core-ui';
|
||||
import { EMPTY_ARRAY } from '@cloudbeaver/core-utils';
|
||||
import { ProjectSelect } from '@cloudbeaver/plugin-projects';
|
||||
|
||||
import { ConnectionAuthModelCredentialsForm } from '../ConnectionAuthModelCredentials/ConnectionAuthModelCredentialsForm';
|
||||
@@ -57,16 +59,19 @@ interface IDriverConfiguration {
|
||||
value: DriverConfigurationType;
|
||||
description?: string;
|
||||
icon?: string;
|
||||
isVisible: (driver: DBDriver) => boolean;
|
||||
}
|
||||
|
||||
const driverConfiguration: IDriverConfiguration[] = [
|
||||
{
|
||||
name: 'Manual',
|
||||
value: DriverConfigurationType.Manual,
|
||||
isVisible: driver => driver.configurationTypes.includes(DriverConfigurationType.Manual),
|
||||
},
|
||||
{
|
||||
name: 'URL',
|
||||
value: DriverConfigurationType.Url,
|
||||
isVisible: driver => driver.configurationTypes.includes(DriverConfigurationType.Url),
|
||||
},
|
||||
];
|
||||
|
||||
@@ -90,7 +95,7 @@ export const Options: TabContainerPanelComponent<IConnectionFormProps> = observe
|
||||
const driverMap = useResource(
|
||||
Options,
|
||||
DBDriverResource,
|
||||
{ key: config.driverId || null, includes: ['includeProviderProperties'] as const },
|
||||
{ key: config.driverId || null, includes: ['includeProviderProperties', 'includeMainProperties'] as const },
|
||||
{
|
||||
onData: data => {
|
||||
optionsHook.setDefaults(data);
|
||||
@@ -99,7 +104,7 @@ export const Options: TabContainerPanelComponent<IConnectionFormProps> = observe
|
||||
);
|
||||
|
||||
const driver = driverMap.data;
|
||||
const configurationTypes = driverConfiguration.filter(conf => driver?.configurationTypes.includes(conf.value));
|
||||
const configurationTypes = driverConfiguration.filter(configuration => driver && configuration.isVisible(driver));
|
||||
|
||||
function handleFormChange(value?: unknown, name?: string) {
|
||||
if (name !== 'name' && optionsHook.isNameAutoFill()) {
|
||||
@@ -142,7 +147,6 @@ export const Options: TabContainerPanelComponent<IConnectionFormProps> = observe
|
||||
providerId: authModel?.requiredAuth ?? info?.requiredAuth ?? AUTH_PROVIDER_LOCAL_ID,
|
||||
});
|
||||
|
||||
const isURLConfiguration = config.configurationType === DriverConfigurationType.Url;
|
||||
const edit = state.mode === 'edit';
|
||||
const originLocal = !info || isLocalConnection(info);
|
||||
|
||||
@@ -185,44 +189,28 @@ export const Options: TabContainerPanelComponent<IConnectionFormProps> = observe
|
||||
{translate('connections_connection_driver')}
|
||||
</Combobox>
|
||||
{configurationTypes.length > 1 && (
|
||||
<>
|
||||
{/*<Combobox
|
||||
name='configurationType'
|
||||
state={config}
|
||||
items={configurationTypes}
|
||||
keySelector={conf => conf.value}
|
||||
valueSelector={conf => conf.name}
|
||||
titleSelector={conf => conf.description}
|
||||
readOnly={readonly || configurationTypes.length < 2}
|
||||
disabled={disabled}
|
||||
tiny
|
||||
fill
|
||||
>
|
||||
{translate('connections_connection_configuration')}
|
||||
</Combobox>*/}
|
||||
<FormFieldDescription label={translate('connections_connection_configuration')} tiny>
|
||||
<Container gap>
|
||||
<RadioGroup name="configurationType" state={config}>
|
||||
{driverConfiguration.map(conf => (
|
||||
<Radio
|
||||
key={conf.value}
|
||||
id={conf.value}
|
||||
value={conf.value}
|
||||
mod={['primary', 'small']}
|
||||
readOnly={readonly || configurationTypes.length < 2}
|
||||
disabled={readonly}
|
||||
keepSize
|
||||
>
|
||||
{conf.name}
|
||||
</Radio>
|
||||
))}
|
||||
</RadioGroup>
|
||||
</Container>
|
||||
</FormFieldDescription>
|
||||
</>
|
||||
<FormFieldDescription label={translate('connections_connection_configuration')} tiny>
|
||||
<Container gap>
|
||||
<RadioGroup name="configurationType" state={config}>
|
||||
{configurationTypes.map(conf => (
|
||||
<Radio
|
||||
key={conf.value}
|
||||
id={conf.value}
|
||||
value={conf.value}
|
||||
mod={['primary', 'small']}
|
||||
readOnly={readonly || configurationTypes.length < 2}
|
||||
disabled={readonly}
|
||||
keepSize
|
||||
>
|
||||
{conf.name}
|
||||
</Radio>
|
||||
))}
|
||||
</RadioGroup>
|
||||
</Container>
|
||||
</FormFieldDescription>
|
||||
)}
|
||||
</Container>
|
||||
{isURLConfiguration ? (
|
||||
{config.configurationType === DriverConfigurationType.Url && (
|
||||
<InputField
|
||||
type="text"
|
||||
name="url"
|
||||
@@ -233,16 +221,26 @@ export const Options: TabContainerPanelComponent<IConnectionFormProps> = observe
|
||||
>
|
||||
{translate('customConnection_url_JDBC')}
|
||||
</InputField>
|
||||
) : (
|
||||
<ParametersForm
|
||||
config={config}
|
||||
embedded={driver?.embedded}
|
||||
requiresServerName={driver?.requiresServerName}
|
||||
disabled={disabled}
|
||||
readOnly={readonly}
|
||||
originLocal={originLocal}
|
||||
/>
|
||||
)}
|
||||
|
||||
{config.configurationType === DriverConfigurationType.Manual &&
|
||||
(driver?.useCustomPage ? (
|
||||
<ObjectPropertyInfoForm
|
||||
state={config.mainPropertyValues}
|
||||
properties={driver.mainProperties ?? EMPTY_ARRAY}
|
||||
disabled={disabled}
|
||||
readOnly={readonly}
|
||||
/>
|
||||
) : (
|
||||
<ParametersForm
|
||||
config={config}
|
||||
embedded={driver?.embedded}
|
||||
requiresServerName={driver?.requiresServerName}
|
||||
disabled={disabled}
|
||||
readOnly={readonly}
|
||||
originLocal={originLocal}
|
||||
/>
|
||||
))}
|
||||
</Group>
|
||||
<Group form gap>
|
||||
<Container wrap gap>
|
||||
|
||||
+4
-4
@@ -16,13 +16,13 @@ import {
|
||||
useObjectPropertyCategories,
|
||||
useTranslate,
|
||||
} from '@cloudbeaver/core-blocks';
|
||||
import { type ConnectionConfig, type DriverProviderPropertyInfoFragment, getObjectPropertyType } from '@cloudbeaver/core-sdk';
|
||||
import { type ConnectionConfig, type DriverPropertyInfoFragment, getObjectPropertyType } from '@cloudbeaver/core-sdk';
|
||||
|
||||
type DriverProviderPropertyInfo = DriverProviderPropertyInfoFragment;
|
||||
type DriverPropertyInfo = DriverPropertyInfoFragment;
|
||||
|
||||
interface Props {
|
||||
config: ConnectionConfig;
|
||||
properties: DriverProviderPropertyInfo[];
|
||||
properties: DriverPropertyInfo[];
|
||||
disabled?: boolean;
|
||||
readonly?: boolean;
|
||||
}
|
||||
@@ -93,6 +93,6 @@ export const ProviderPropertiesForm = observer<Props>(function ProviderPropertie
|
||||
);
|
||||
});
|
||||
|
||||
function isOnlyBooleans(properties: DriverProviderPropertyInfo[], category?: string): boolean {
|
||||
function isOnlyBooleans(properties: DriverPropertyInfo[], category?: string): boolean {
|
||||
return properties.filter(property => !category || property.category === category).every(property => property.dataType === 'Boolean');
|
||||
}
|
||||
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
* 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 type { DBDriver } from '@cloudbeaver/core-connections';
|
||||
import { DriverConfigurationType } from '@cloudbeaver/core-sdk';
|
||||
|
||||
export function getDefaultConfigurationType(driver: DBDriver | undefined) {
|
||||
if (!driver) {
|
||||
return DriverConfigurationType.Url;
|
||||
}
|
||||
|
||||
const supportManual = driver.configurationTypes.includes(DriverConfigurationType.Manual);
|
||||
return supportManual ? DriverConfigurationType.Manual : DriverConfigurationType.Url;
|
||||
}
|
||||
@@ -10,10 +10,11 @@ import { runInAction } from 'mobx';
|
||||
import { useObjectRef } from '@cloudbeaver/core-blocks';
|
||||
import { DBDriver, DBDriverResource, isJDBCConnection } from '@cloudbeaver/core-connections';
|
||||
import { useService } from '@cloudbeaver/core-di';
|
||||
import { DatabaseAuthModel, DriverConfigurationType } from '@cloudbeaver/core-sdk';
|
||||
import type { DatabaseAuthModel } from '@cloudbeaver/core-sdk';
|
||||
|
||||
import type { IConnectionFormState } from '../IConnectionFormProps';
|
||||
import { getConnectionName } from './getConnectionName';
|
||||
import { getDefaultConfigurationType } from './getDefaultConfigurationType';
|
||||
|
||||
export function useOptions(state: IConnectionFormState) {
|
||||
const dbDriverResource = useService(DBDriverResource);
|
||||
@@ -71,9 +72,7 @@ export function useOptions(state: IConnectionFormState) {
|
||||
refObject.prevDriverId = driver?.id || null;
|
||||
|
||||
if (!config.configurationType || !driver?.configurationTypes.includes(config.configurationType)) {
|
||||
config.configurationType = driver?.configurationTypes.includes(DriverConfigurationType.Manual)
|
||||
? DriverConfigurationType.Manual
|
||||
: DriverConfigurationType.Url;
|
||||
state.config.configurationType = getDefaultConfigurationType(driver);
|
||||
}
|
||||
|
||||
if ((!prevDriver && config.host === undefined) || config.host === prevDriver?.defaultServer) {
|
||||
|
||||
Reference in New Issue
Block a user