[CB-6198] Migrate secrets ui (public) from backend to frontend part (#3701)

* dbeaver/pro#5170 adds refactoring boilerplate

* dbeaver/pro#6198 remove redundant code about secret configuration

* moves secrets into provider properties

* renames optionsExtendedForm to ProviderPropertiesExtendedForm

* build fix

* dbeaver/pro#5170 set external params fix

* moves providerProperties container to ConnectionFormService

* cleanup

* adds empty arrays to connection secret reset state

* truncates select items and select itself

* Revert "truncates select items and select itself"

This reverts commit 56c943b5a0.

---------

Co-authored-by: Ainur <ainur.iagudin@dbeaver.com>
Co-authored-by: Evgenia <139753579+EvgeniaBzzz@users.noreply.github.com>
Co-authored-by: Ainur <59531286+yagudin10@users.noreply.github.com>
This commit is contained in:
sergeyteleshev
2025-09-16 15:05:28 +02:00
committed by GitHub
co-authored by Ainur Evgenia Ainur
parent e466462269
commit 5a394e648d
9 changed files with 35 additions and 47 deletions
@@ -57,6 +57,7 @@ public class WebConnectionConfig {
private boolean sharedCredentials;
private Map<String, Object> mainPropertyValues;
private Map<String, Object> providerProperties;
private Map<String, Object> externalParameters;
private List<WebNetworkHandlerConfigInput> networkHandlersConfig;
private DBPDriverConfigurationType configurationType;
private String selectedSecretId;
@@ -103,6 +104,7 @@ public class WebConnectionConfig {
String configType = JSONUtils.getString(params, "configurationType");
configurationType = configType == null ? null : DBPDriverConfigurationType.valueOf(configType);
externalParameters = JSONUtils.getObjectOrNull(params, "externalParameters");
networkHandlersConfig = new ArrayList<>();
for (Map<String, Object> nhc : JSONUtils.getObjectList(params, "networkHandlersConfig")) {
@@ -248,4 +250,8 @@ public class WebConnectionConfig {
public String getDefaultSchemaName() {
return defaultSchemaName;
}
public Map<String, Object> getExternalParameters() {
return externalParameters;
}
}
@@ -18,10 +18,7 @@ package io.cloudbeaver.model;
import io.cloudbeaver.DBWebException;
import io.cloudbeaver.WebServiceUtils;
import io.cloudbeaver.model.app.WebAppConfiguration;
import io.cloudbeaver.model.session.WebSession;
import io.cloudbeaver.model.utils.ConfigurationUtils;
import io.cloudbeaver.server.WebAppUtils;
import org.jkiss.dbeaver.DBException;
import org.jkiss.dbeaver.Log;
import org.jkiss.dbeaver.model.DBConstants;
@@ -39,8 +36,6 @@ import org.jkiss.utils.CommonUtils;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Stream;
/**
* Web driver configuration
@@ -257,29 +252,14 @@ public class WebDatabaseDriverInfo {
@Property
public WebPropertyInfo[] getProviderProperties() {
WebPropertyInfo[] additionalWebProperty = Optional.of(WebAppUtils.getWebApplication())
.filter(app -> app.getAppConfiguration().isSecretManagerEnabled())
.map(app -> app.getConnectionController().getExternalInfo(webSession))
.orElse(new WebPropertyInfo[0]);
WebPropertyInfo[] providerProperties = Arrays.stream(driver.getProviderPropertyDescriptors())
return Arrays.stream(driver.getProviderPropertyDescriptors())
.map(p -> new WebPropertyInfo(webSession, p, null))
.toArray(WebPropertyInfo[]::new);
return Stream.concat(
Arrays.stream(additionalWebProperty),
Arrays.stream(providerProperties)
).toArray(WebPropertyInfo[]::new);
}
@Property
public boolean isEnabled() {
WebAppConfiguration config = WebAppUtils.getWebApplication().getAppConfiguration();
return ConfigurationUtils.isDriverEnabled(
driver,
config.getEnabledDrivers(),
config.getDisabledDrivers()
);
return WebServiceUtils.isDriverEnabled(driver);
}
@Property
@@ -21,11 +21,9 @@ import io.cloudbeaver.WebObjectId;
import io.cloudbeaver.model.WebConnectionConfig;
import io.cloudbeaver.model.WebConnectionInfo;
import io.cloudbeaver.model.WebNetworkHandlerConfigInput;
import io.cloudbeaver.model.WebPropertyInfo;
import io.cloudbeaver.model.session.WebSession;
import org.jkiss.code.NotNull;
import org.jkiss.code.Nullable;
import org.jkiss.dbeaver.DBException;
import org.jkiss.dbeaver.model.DBPDataSourceContainer;
import org.jkiss.dbeaver.model.app.DBPDataSourceRegistry;
import org.jkiss.dbeaver.registry.DataSourceDescriptor;
@@ -98,5 +96,4 @@ public interface ConnectionController {
void validateConnection(DBPDataSourceContainer dataSourceContainer) throws DBWebException;
WebPropertyInfo[] getExternalInfo(WebSession webSession);
}
@@ -20,7 +20,6 @@ import io.cloudbeaver.*;
import io.cloudbeaver.model.WebConnectionConfig;
import io.cloudbeaver.model.WebConnectionInfo;
import io.cloudbeaver.model.WebNetworkHandlerConfigInput;
import io.cloudbeaver.model.WebPropertyInfo;
import io.cloudbeaver.model.session.WebSession;
import io.cloudbeaver.utils.ServletAppUtils;
import io.cloudbeaver.utils.WebDataSourceUtils;
@@ -482,10 +481,6 @@ public class ConnectionControllerCE implements ConnectionController {
public void validateConnection(DBPDataSourceContainer dataSourceContainer) throws DBWebException {
}
public WebPropertyInfo[] getExternalInfo(WebSession session) {
return null;
}
private void validateDriverLibrariesPresence(@NotNull DBPDataSourceContainer container) throws DBWebException {
if (!DBWorkbench.isDistributed() && container.getDriver().getDriverLoader(container).needsExternalDependencies()) {
@@ -62,5 +62,6 @@ export * from './NetworkHandlerResource.js';
export * from './useConnectionInfo.js';
export * from './useDBDriver.js';
export * from './USER_NAME_PROPERTY_ID.js';
export * from './parseConnectionKey.js';
export { manifest as coreConnectionsManifest } from './manifest.js';
@@ -1,10 +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.
*/
export type EmptyObject = Record<any, never>;
export const EMPTY_ARRAY: ReadonlyArray<any> = Object.freeze([]);
export const EMPTY_OBJECT: Readonly<Record<any, any>> = Object.freeze({});
export const EMPTY_OBJECT: Readonly<EmptyObject> = Object.freeze({});
@@ -8,17 +8,23 @@
import { injectable } from '@cloudbeaver/core-di';
import { NotificationService } from '@cloudbeaver/core-events';
import { LocalizationService } from '@cloudbeaver/core-localization';
import { FormBaseService } from '@cloudbeaver/core-ui';
import { FormBaseService, type IFormState } from '@cloudbeaver/core-ui';
import type { IConnectionFormProps, IConnectionFormState } from './IConnectionFormState.js';
import { importLazyComponent } from '@cloudbeaver/core-blocks';
import { importLazyComponent, PlaceholderContainer } from '@cloudbeaver/core-blocks';
const ConnectionFormBaseActionsLoader = importLazyComponent(() => import('./ConnectionFormBaseActions.js').then(m => m.ConnectionFormBaseActions));
export type ProviderPropertiesContainerFormProps = {
formState: IFormState<IConnectionFormState>;
};
@injectable(() => [LocalizationService, NotificationService])
export class ConnectionFormService extends FormBaseService<IConnectionFormState, IConnectionFormProps> {
readonly providerPropertiesContainer: PlaceholderContainer<ProviderPropertiesContainerFormProps>;
constructor(localizationService: LocalizationService, notificationService: NotificationService) {
super(localizationService, notificationService, 'Connection form');
this.providerPropertiesContainer = new PlaceholderContainer<ProviderPropertiesContainerFormProps>();
this.actionsContainer.add(ConnectionFormBaseActionsLoader);
}
}
@@ -342,14 +342,7 @@ export const Options: TabContainerPanelComponent<IConnectionFormProps> = observe
)}
</Group>
)}
{driver?.providerProperties && (
<ProviderPropertiesForm
config={optionsPart.state}
properties={driver.providerProperties}
disabled={formState.isDisabled}
readonly={readonly}
/>
)}
{driver?.providerProperties && <ProviderPropertiesForm formState={formState} properties={driver.providerProperties} readonly={readonly} />}
<AdvancedPropertiesForm config={optionsPart.state} disabled={formState.isDisabled} readonly={readonly} />
</Container>
@@ -13,24 +13,31 @@ import {
Group,
GroupTitle,
ObjectPropertyInfoForm,
Placeholder,
useObjectPropertyCategories,
useTranslate,
} from '@cloudbeaver/core-blocks';
import { type DriverPropertyInfoFragment, getObjectPropertyType } from '@cloudbeaver/core-sdk';
import type { IConnectionFormOptionsState } from './IConnectionFormOptionsState.js';
import type { IFormState } from '@cloudbeaver/core-ui';
import type { IConnectionFormState } from '../IConnectionFormState.js';
import { getConnectionFormOptionsPart } from './getConnectionFormOptionsPart.js';
import { useService } from '@cloudbeaver/core-di';
import { ConnectionFormService } from '../ConnectionFormService.js';
type DriverPropertyInfo = DriverPropertyInfoFragment;
interface Props {
config: IConnectionFormOptionsState;
formState: IFormState<IConnectionFormState>;
properties: DriverPropertyInfo[];
disabled?: boolean;
readonly?: boolean;
}
export const ProviderPropertiesForm = observer<Props>(function ProviderPropertiesForm({ config, properties, disabled, readonly }) {
export const ProviderPropertiesForm = observer<Props>(function ProviderPropertiesForm({ properties, readonly, formState }) {
const translate = useTranslate();
const config = getConnectionFormOptionsPart(formState).state;
const disabled = formState.isDisabled;
const supportedProperties = properties.filter(property => property.supportedConfigurationTypes?.some(type => type === config.configurationType));
const connectionFormService = useService(ConnectionFormService);
const { categories, isUncategorizedExists } = useObjectPropertyCategories(supportedProperties);
@@ -43,6 +50,7 @@ export const ProviderPropertiesForm = observer<Props>(function ProviderPropertie
return (
<Group form gap>
<Placeholder container={connectionFormService.providerPropertiesContainer} formState={formState} />
{isUncategorizedExists && (
<>
<GroupTitle>{translate('ui_settings')}</GroupTitle>