From 0bfdcf08869b10d11787a9ecf89df70ee4276d55 Mon Sep 17 00:00:00 2001 From: Ruslan Musaev <43766501+HocKu7@users.noreply.github.com> Date: Tue, 27 Jan 2026 12:53:40 +0100 Subject: [PATCH] dbeaver/pro#7769 fix properties mapping for enums (#4052) * dbeaver/pro#7769 fix properties mapping for enums * dbeaver/pro#7769 fix annotations * dbeaver/pro#7769 use custom extended interface * dbeaver/pro#7769 check explicit value * dbeaver/pro#7769 avoid declaring extra variable * dbeaver/pro#7769 always use isNotNullDefined in terms of backend data --------- Co-authored-by: naumov Co-authored-by: mr-anton-t <42037741+mr-anton-t@users.noreply.github.com> Co-authored-by: Evgenia <139753579+EvgeniaBzzz@users.noreply.github.com> Co-authored-by: Ainur <59531286+yagudin10@users.noreply.github.com> --- .../model/WebBasicEnumObjectInfo.java | 50 +++++++++++++++++++ .../io/cloudbeaver/model/WebPropertyInfo.java | 4 +- .../src/TeamMetaParametersResource.ts | 4 +- .../ObjectPropertyInfoForm.tsx | 10 ++-- .../ObjectPropertyInfoForm/RenderField.tsx | 18 ++++--- .../getObjectPropertyDefaults.ts | 6 +-- .../useObjectPropertyCategories.ts | 4 +- .../src/PropertiesTable/IProperty.ts | 10 ++-- .../src/PropertiesTable/PropertiesTable.tsx | 6 ++- .../src/PropertiesTable/PropertyItem.tsx | 13 +++-- .../Settings/SettingsTransformationService.ts | 9 ++-- .../core-sdk/src/IObjectPropertyInfo.ts | 17 +++++++ .../core-sdk/src/getObjectPropertyType.ts | 4 +- .../core-sdk/src/getObjectPropertyValue.ts | 32 +++++++++--- .../src/getObjectPropertyValueType.ts | 4 +- webapp/packages/core-sdk/src/index.ts | 2 +- .../src/isObjectPropertyInfoStateEqual.ts | 38 -------------- .../ConnectionAuthenticationForm.tsx | 4 +- .../ConnectionAuthModelCredentialsForm.tsx | 6 +-- .../Options/ConnectionFormOptionsPart.ts | 25 +++++++--- .../SSL/ConnectionFormSSLPart.ts | 4 +- .../src/DataTransferProcessorsResource.ts | 10 ++-- .../src/Dialog/useDataExportDialog.ts | 30 ++++++----- .../NavNodeMetadata/ObjectProperties.tsx | 4 +- .../NavNodeMetadata/ObjectProperty.tsx | 4 +- .../ExecutionPlan/NestedNode.tsx | 4 +- .../PropertiesPanelItemsGroup.tsx | 4 +- .../ExecutionPlan/getPropertyValue.ts | 4 +- .../src/SystemInformationResource.ts | 6 +-- 29 files changed, 207 insertions(+), 129 deletions(-) create mode 100644 server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/WebBasicEnumObjectInfo.java create mode 100644 webapp/packages/core-sdk/src/IObjectPropertyInfo.ts delete mode 100644 webapp/packages/core-sdk/src/isObjectPropertyInfoStateEqual.ts diff --git a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/WebBasicEnumObjectInfo.java b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/WebBasicEnumObjectInfo.java new file mode 100644 index 0000000000..8a5d7b8074 --- /dev/null +++ b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/WebBasicEnumObjectInfo.java @@ -0,0 +1,50 @@ +/* + * DBeaver - Universal Database Manager + * Copyright (C) 2010-2025 DBeaver Corp and others + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.cloudbeaver.model; + +import org.jkiss.code.NotNull; + +public class WebBasicEnumObjectInfo { + + @NotNull + private String displayName; + @NotNull + private String value; + + public WebBasicEnumObjectInfo(@NotNull String displayName, @NotNull String value) { + this.displayName = displayName; + this.value = value; + } + + @NotNull + public String getDisplayName() { + return displayName; + } + + public void setDisplayName(@NotNull String displayName) { + this.displayName = displayName; + } + + @NotNull + public String getValue() { + return value; + } + + public void setValue(@NotNull String value) { + this.value = value; + } +} diff --git a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/WebPropertyInfo.java b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/WebPropertyInfo.java index 1a00df3e96..c52676f911 100644 --- a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/WebPropertyInfo.java +++ b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/WebPropertyInfo.java @@ -243,9 +243,11 @@ public class WebPropertyInfo { List result = new ArrayList<>(); int length = Array.getLength(value); for (int i = 0; i < length; i++) { - result.add(Array.get(value, i)); + result.add(makePropertyValue(Array.get(value, i))); } return result; + } else if (value instanceof Enum enumValue) { + return new WebBasicEnumObjectInfo(enumValue.toString(), enumValue.name()); } Class dataType = property.getDataType(); if (dataType == Boolean.class || dataType == Boolean.TYPE) { diff --git a/webapp/packages/core-authentication/src/TeamMetaParametersResource.ts b/webapp/packages/core-authentication/src/TeamMetaParametersResource.ts index f8420fd496..44a13828a9 100644 --- a/webapp/packages/core-authentication/src/TeamMetaParametersResource.ts +++ b/webapp/packages/core-authentication/src/TeamMetaParametersResource.ts @@ -8,9 +8,9 @@ import { injectable } from '@cloudbeaver/core-di'; import { CachedDataResource } from '@cloudbeaver/core-resource'; import { SessionResource } from '@cloudbeaver/core-root'; -import { GraphQLService, type ObjectPropertyInfo } from '@cloudbeaver/core-sdk'; +import { GraphQLService, type IObjectPropertyInfo } from '@cloudbeaver/core-sdk'; -export type TeamMetaParameter = ObjectPropertyInfo; +export type TeamMetaParameter = IObjectPropertyInfo; @injectable(() => [GraphQLService, SessionResource]) export class TeamMetaParametersResource extends CachedDataResource { diff --git a/webapp/packages/core-blocks/src/ObjectPropertyInfo/ObjectPropertyInfoForm/ObjectPropertyInfoForm.tsx b/webapp/packages/core-blocks/src/ObjectPropertyInfo/ObjectPropertyInfoForm/ObjectPropertyInfoForm.tsx index dae45f6b0e..2e74055592 100644 --- a/webapp/packages/core-blocks/src/ObjectPropertyInfo/ObjectPropertyInfoForm/ObjectPropertyInfoForm.tsx +++ b/webapp/packages/core-blocks/src/ObjectPropertyInfo/ObjectPropertyInfoForm/ObjectPropertyInfoForm.tsx @@ -8,7 +8,7 @@ import { observer } from 'mobx-react-lite'; import { useCallback } from 'react'; -import type { ObjectPropertyInfo } from '@cloudbeaver/core-sdk'; +import type { IObjectPropertyInfo } from '@cloudbeaver/core-sdk'; import { getLayoutProps } from '../../Containers/filterLayoutFakeProps.js'; import type { ILayoutSizeProps } from '../../Containers/ILayoutSizeProps.js'; @@ -23,7 +23,7 @@ import { getObjectPropertyDefaults } from '../getObjectPropertyDefaults.js'; type AutocompletePasswordType = 'current-password' | 'new-password' | 'off' | 'on'; export interface ObjectPropertyFormProps extends ILayoutSizeProps { - properties: ReadonlyArray; + properties: ReadonlyArray; state?: Record; context?: Record; defaultState?: Record; @@ -39,12 +39,12 @@ export interface ObjectPropertyFormProps extends ILayoutSizeProps { emptyPlaceholder?: string; canShowPassword?: boolean; autocompletePasswordType?: AutocompletePasswordType; - isSaved?: (property: ObjectPropertyInfo) => boolean; - getLayoutSize?: (property: ObjectPropertyInfo) => ILayoutSizeProps; + isSaved?: (property: IObjectPropertyInfo) => boolean; + getLayoutSize?: (property: IObjectPropertyInfo) => ILayoutSizeProps; onFocus?: (name: string) => void; } -function getAutocompleteParam(property: ObjectPropertyInfo, prefix: string, autocompletePasswordType: AutocompletePasswordType): string { +function getAutocompleteParam(property: IObjectPropertyInfo, prefix: string, autocompletePasswordType: AutocompletePasswordType): string { const isPasswordField = property.features.includes('password'); if (isPasswordField) { diff --git a/webapp/packages/core-blocks/src/ObjectPropertyInfo/ObjectPropertyInfoForm/RenderField.tsx b/webapp/packages/core-blocks/src/ObjectPropertyInfo/ObjectPropertyInfoForm/RenderField.tsx index 49363c833c..eee67ddd30 100644 --- a/webapp/packages/core-blocks/src/ObjectPropertyInfo/ObjectPropertyInfoForm/RenderField.tsx +++ b/webapp/packages/core-blocks/src/ObjectPropertyInfo/ObjectPropertyInfoForm/RenderField.tsx @@ -10,10 +10,12 @@ import { observer } from 'mobx-react-lite'; import { ConditionType, getObjectPropertyDefaultValue, + getObjectPropertyOptionName, + getObjectPropertyOptionValue, getObjectPropertyType, getObjectPropertyValue, getObjectPropertyValueType, - type ObjectPropertyInfo, + type IObjectPropertyInfo, } from '@cloudbeaver/core-sdk'; import { EMPTY_ARRAY, removeMetadataFromDataURL } from '@cloudbeaver/core-utils'; @@ -29,7 +31,7 @@ import { useTranslate } from '../../localization/useTranslate.js'; import { evaluate } from '../evaluate.js'; interface RenderFieldProps { - property: ObjectPropertyInfo; + property: IObjectPropertyInfo; state?: Record; context?: Record; defaultState?: Record; @@ -154,9 +156,9 @@ export const RenderField = observer(function RenderField({ name={property.id!} state={state} items={property.validValues!} - keySelector={value => value} - valueSelector={value => value} - titleSelector={value => value} + keySelector={getObjectPropertyOptionValue} + valueSelector={getObjectPropertyOptionName} + titleSelector={getObjectPropertyOptionName} defaultValue={defaultValue} placeholder={placeholder} title={property.description} @@ -175,9 +177,9 @@ export const RenderField = observer(function RenderField({ required={required} name={property.id!} items={property.validValues!} - keySelector={value => value} - valueSelector={value => value} - titleSelector={value => value} + keySelector={getObjectPropertyOptionValue} + valueSelector={getObjectPropertyOptionName} + titleSelector={getObjectPropertyOptionName} defaultValue={defaultValue} placeholder={placeholder} title={property.description} diff --git a/webapp/packages/core-blocks/src/ObjectPropertyInfo/getObjectPropertyDefaults.ts b/webapp/packages/core-blocks/src/ObjectPropertyInfo/getObjectPropertyDefaults.ts index e637b7c3e9..315163439e 100644 --- a/webapp/packages/core-blocks/src/ObjectPropertyInfo/getObjectPropertyDefaults.ts +++ b/webapp/packages/core-blocks/src/ObjectPropertyInfo/getObjectPropertyDefaults.ts @@ -6,17 +6,17 @@ * you may not use this file except in compliance with the License. */ -import type { ObjectPropertyInfo } from '@cloudbeaver/core-sdk'; +import { getObjectPropertyDefaultValue, type IObjectPropertyInfo } from '@cloudbeaver/core-sdk'; import { isNotNullDefined } from '@dbeaver/js-helpers'; -export function getObjectPropertyDefaults(properties: ReadonlyArray): Record { +export function getObjectPropertyDefaults(properties: ReadonlyArray): Record { const result: Record = {}; for (const property of properties) { const isDefault = isNotNullDefined(property.defaultValue); if (isDefault && property.id) { - result[property.id] = property.defaultValue; + result[property.id] = getObjectPropertyDefaultValue(property); } } diff --git a/webapp/packages/core-blocks/src/ObjectPropertyInfo/useObjectPropertyCategories.ts b/webapp/packages/core-blocks/src/ObjectPropertyInfo/useObjectPropertyCategories.ts index 4aa384980d..534d21f454 100644 --- a/webapp/packages/core-blocks/src/ObjectPropertyInfo/useObjectPropertyCategories.ts +++ b/webapp/packages/core-blocks/src/ObjectPropertyInfo/useObjectPropertyCategories.ts @@ -8,14 +8,14 @@ import { computed, type IComputedValue } from 'mobx'; import { useMemo } from 'react'; -import type { ObjectPropertyInfo } from '@cloudbeaver/core-sdk'; +import type { IObjectPropertyInfo } from '@cloudbeaver/core-sdk'; interface ICategoriesInfo { categories: string[]; isUncategorizedExists: boolean; } -export function useObjectPropertyCategories(properties: ObjectPropertyInfo[]) { +export function useObjectPropertyCategories(properties: IObjectPropertyInfo[]) { const categories: IComputedValue = useMemo( () => computed(() => { diff --git a/webapp/packages/core-blocks/src/PropertiesTable/IProperty.ts b/webapp/packages/core-blocks/src/PropertiesTable/IProperty.ts index bb9b75019e..9dd45ed280 100644 --- a/webapp/packages/core-blocks/src/PropertiesTable/IProperty.ts +++ b/webapp/packages/core-blocks/src/PropertiesTable/IProperty.ts @@ -1,19 +1,17 @@ /* * CloudBeaver - Cloud Database Manager - * Copyright (C) 2020-2024 DBeaver Corp and others + * Copyright (C) 2020-2026 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 interface IProperty { +import type { IObjectPropertyInfo } from '@cloudbeaver/core-sdk'; + +export interface IProperty extends Pick { id: string; key: string; - displayName?: string; - defaultValue?: string; - description?: string; keyPlaceholder?: string; valuePlaceholder?: string; - validValues?: string[]; new?: boolean; } diff --git a/webapp/packages/core-blocks/src/PropertiesTable/PropertiesTable.tsx b/webapp/packages/core-blocks/src/PropertiesTable/PropertiesTable.tsx index 44b6218def..b49a7e0969 100644 --- a/webapp/packages/core-blocks/src/PropertiesTable/PropertiesTable.tsx +++ b/webapp/packages/core-blocks/src/PropertiesTable/PropertiesTable.tsx @@ -9,6 +9,9 @@ import { computed } from 'mobx'; import { observer } from 'mobx-react-lite'; import { useCallback, useMemo, useState } from 'react'; +import { getObjectPropertyOptionValue } from '@cloudbeaver/core-sdk'; +import { isNotNullDefined } from '@dbeaver/js-helpers'; + import { Button } from '../Button.js'; import { Filter } from '../FormControls/Filter.js'; import { useTranslate } from '../localization/useTranslate.js'; @@ -87,7 +90,8 @@ export const PropertiesTable = observer(function PropertiesTable(props) { } if (propertiesState) { - if (value === property.defaultValue && !propsRef.staticProperties) { + const defaultValue = isNotNullDefined(property.defaultValue) ? String(getObjectPropertyOptionValue(property.defaultValue)) : undefined; + if (value === defaultValue && !propsRef.staticProperties) { delete propertiesState[property.key]; } else { propertiesState[property.key] = value; diff --git a/webapp/packages/core-blocks/src/PropertiesTable/PropertyItem.tsx b/webapp/packages/core-blocks/src/PropertiesTable/PropertyItem.tsx index 2319c9b9ce..d0d74e0690 100644 --- a/webapp/packages/core-blocks/src/PropertiesTable/PropertyItem.tsx +++ b/webapp/packages/core-blocks/src/PropertiesTable/PropertyItem.tsx @@ -1,6 +1,6 @@ /* * CloudBeaver - Cloud Database Manager - * Copyright (C) 2020-2025 DBeaver Corp and others + * Copyright (C) 2020-2026 DBeaver Corp and others * * Licensed under the Apache License, Version 2.0. * you may not use this file except in compliance with the License. @@ -8,6 +8,8 @@ import { observer } from 'mobx-react-lite'; import { useCallback, useLayoutEffect, useRef } from 'react'; import { Input } from '@dbeaver/ui-kit'; +import { getObjectPropertyOptionName, getObjectPropertyOptionValue } from '@cloudbeaver/core-sdk'; +import { isNotNullDefined } from '@dbeaver/js-helpers'; import { useTranslate } from '../localization/useTranslate.js'; import { s } from '../s.js'; @@ -41,14 +43,15 @@ export const PropertyItem = observer(function PropertyItem({ const styles = useS(classes); const translate = useTranslate(); const isDeletable = !readOnly && !property.displayName && removable; - const edited = value !== undefined && value !== property.defaultValue; + const defaultValue = isNotNullDefined(property.defaultValue) ? String(getObjectPropertyOptionValue(property.defaultValue)) : undefined; + const edited = value !== undefined && value !== defaultValue; const keyInputRef = useRef(null); const handleKeyChange = useCallback((key: string) => onNameChange(property.id, key), [property]); const handleValueChange = useCallback((value: string | null) => onValueChange(property.id, value), [property]); const handleRemove = useCallback(() => onRemove(property.id), [property]); function handleRevert() { - onValueChange(property.id, property.defaultValue ?? null); + onValueChange(property.id, defaultValue ?? null); } useLayoutEffect(() => { @@ -77,11 +80,13 @@ export const PropertyItem = observer(function PropertyItem({
, property: ObjectPropertyInfo): ISettingDescription | null { + mapSetting(groups: Map, property: IObjectPropertyInfo): ISettingDescription | null { const key = property.id!; const transformer = this.settingTransformers.get(key); @@ -58,7 +58,8 @@ export class SettingsTransformationService { name: property.displayName!, description: property.description!, - options: property.validValues?.map(value => ({ value, name: value })) || [], + options: + property.validValues?.map(value => ({ value: String(getObjectPropertyOptionValue(value)), name: getObjectPropertyOptionName(value) })) || [], }; if (transformer) { @@ -98,7 +99,7 @@ export class SettingsTransformationService { } } -function convertObjectPropertyInfoType(property: ObjectPropertyInfo): ESettingsValueType { +function convertObjectPropertyInfoType(property: IObjectPropertyInfo): ESettingsValueType { switch (getObjectPropertyType(property)) { case 'selector': return ESettingsValueType.Select; diff --git a/webapp/packages/core-sdk/src/IObjectPropertyInfo.ts b/webapp/packages/core-sdk/src/IObjectPropertyInfo.ts new file mode 100644 index 0000000000..8e964e46ee --- /dev/null +++ b/webapp/packages/core-sdk/src/IObjectPropertyInfo.ts @@ -0,0 +1,17 @@ +/* + * CloudBeaver - Cloud Database Manager + * Copyright (C) 2020-2026 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.js'; + +export type ObjectPropertyOptionValue = string | boolean; +export type ObjectPropertyOption = ObjectPropertyOptionValue | { displayName: string; value: ObjectPropertyOptionValue }; + +export interface IObjectPropertyInfo extends ObjectPropertyInfo { + validValues?: ObjectPropertyOption[]; + defaultValue?: ObjectPropertyOption; +} diff --git a/webapp/packages/core-sdk/src/getObjectPropertyType.ts b/webapp/packages/core-sdk/src/getObjectPropertyType.ts index 562b6f2f6d..bdccf8587f 100644 --- a/webapp/packages/core-sdk/src/getObjectPropertyType.ts +++ b/webapp/packages/core-sdk/src/getObjectPropertyType.ts @@ -5,11 +5,11 @@ * 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.js'; +import type { IObjectPropertyInfo } from './IObjectPropertyInfo.js'; export type ObjectPropertyType = 'checkbox' | 'selector' | 'link' | 'textarea' | 'file' | 'input'; -export function getObjectPropertyType(property: ObjectPropertyInfo): ObjectPropertyType { +export function getObjectPropertyType(property: IObjectPropertyInfo): ObjectPropertyType { const dataType = property.dataType?.toLowerCase(); if (dataType === 'boolean') { diff --git a/webapp/packages/core-sdk/src/getObjectPropertyValue.ts b/webapp/packages/core-sdk/src/getObjectPropertyValue.ts index b7a7775dc7..b96e5652aa 100644 --- a/webapp/packages/core-sdk/src/getObjectPropertyValue.ts +++ b/webapp/packages/core-sdk/src/getObjectPropertyValue.ts @@ -1,25 +1,25 @@ /* * CloudBeaver - Cloud Database Manager - * Copyright (C) 2020-2025 DBeaver Corp and others + * Copyright (C) 2020-2026 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 { getObjectPropertyType, type ObjectPropertyType } from './getObjectPropertyType.js'; -import type { ObjectPropertyInfo } from './sdk.js'; +import type { IObjectPropertyInfo, ObjectPropertyOption, ObjectPropertyOptionValue } from './IObjectPropertyInfo.js'; -export function getObjectPropertyValue(property: ObjectPropertyInfo) { +export function getObjectPropertyValue(property: IObjectPropertyInfo) { const controlType = getObjectPropertyType(property); return getValue(property.value, controlType); } -export function getObjectPropertyDefaultValue(property: ObjectPropertyInfo) { +export function getObjectPropertyDefaultValue(property: IObjectPropertyInfo) { const controlType = getObjectPropertyType(property); return getValue(property.defaultValue, controlType); } -export function getObjectPropertyDisplayValue(property: ObjectPropertyInfo) { +export function getObjectPropertyDisplayValue(property: IObjectPropertyInfo) { const controlType = getObjectPropertyType(property); return String(getValue(property.value, controlType)); } @@ -43,5 +43,25 @@ function getValue(value: any, controlType: ObjectPropertyType) { return value.join(', '); } - return value.displayName || value.value || JSON.stringify(value); + return value.value || value.displayName || JSON.stringify(value); +} + +export function getObjectPropertyOptionValue(option: ObjectPropertyOption): ObjectPropertyOptionValue { + if (typeof option === 'object' && option !== null) { + return option.value; + } + + return option; +} + +export function getObjectPropertyOptionName(option: ObjectPropertyOption): string { + if (typeof option === 'object' && option !== null) { + return option.displayName; + } + + if (option === null || option === undefined) { + return ''; + } + + return String(option); } diff --git a/webapp/packages/core-sdk/src/getObjectPropertyValueType.ts b/webapp/packages/core-sdk/src/getObjectPropertyValueType.ts index 3289ab6d9f..ccce140cef 100644 --- a/webapp/packages/core-sdk/src/getObjectPropertyValueType.ts +++ b/webapp/packages/core-sdk/src/getObjectPropertyValueType.ts @@ -5,11 +5,11 @@ * 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.js'; +import type { IObjectPropertyInfo } from './IObjectPropertyInfo.js'; export type ObjectPropertyValueType = 'password' | 'text' | 'number'; -export function getObjectPropertyValueType(property: ObjectPropertyInfo): ObjectPropertyValueType | undefined { +export function getObjectPropertyValueType(property: IObjectPropertyInfo): ObjectPropertyValueType | undefined { const dataType = property.dataType?.toLowerCase(); if (property.features.includes('password')) { diff --git a/webapp/packages/core-sdk/src/index.ts b/webapp/packages/core-sdk/src/index.ts index a5deed93f5..d9a59c7712 100644 --- a/webapp/packages/core-sdk/src/index.ts +++ b/webapp/packages/core-sdk/src/index.ts @@ -18,10 +18,10 @@ export * from './getObjectPropertyValueType.js'; export * from './GQLError.js'; export * from './GQLErrorCatcher.js'; export * from './GraphQLService.js'; -export * from './isObjectPropertyInfoStateEqual.js'; export * from './sdk.js'; export * from './SDKGraphQLErrorExtensions.js'; export * from './ServerInternalError.js'; export * from './ServiceError.js'; export * from './SessionError.js'; export * from './getObjectPropertyValue.js'; +export * from './IObjectPropertyInfo.js'; diff --git a/webapp/packages/core-sdk/src/isObjectPropertyInfoStateEqual.ts b/webapp/packages/core-sdk/src/isObjectPropertyInfoStateEqual.ts deleted file mode 100644 index e8761d5936..0000000000 --- a/webapp/packages/core-sdk/src/isObjectPropertyInfoStateEqual.ts +++ /dev/null @@ -1,38 +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 { isValuesEqual } from '@cloudbeaver/core-utils'; - -import type { ObjectPropertyInfo } from './sdk.js'; - -type RequiredObjectPropertyInfoFields = Pick; - -export function isObjectPropertyInfoStateEqual(properties: RequiredObjectPropertyInfoFields[], first: T, second: T): boolean { - if (first === null || second === null || typeof first !== 'object' || typeof second !== 'object') { - return false; - } - - const customKeys = [...Object.keys(first), ...Object.keys(second)].filter(key => !properties.some(property => key === property.id)); - - for (const property of properties) { - if (!property.id) { - continue; - } - - if (!isValuesEqual(first[property.id as keyof T], second[property.id as keyof T], property.defaultValue)) { - return false; - } - } - - for (const key of customKeys) { - if (first[key as keyof T] !== second[key as keyof T]) { - return false; - } - } - - return true; -} diff --git a/webapp/packages/plugin-connections/src/ConnectionAuthentication/ConnectionAuthenticationForm.tsx b/webapp/packages/plugin-connections/src/ConnectionAuthentication/ConnectionAuthenticationForm.tsx index 67c80ef496..522583da21 100644 --- a/webapp/packages/plugin-connections/src/ConnectionAuthentication/ConnectionAuthenticationForm.tsx +++ b/webapp/packages/plugin-connections/src/ConnectionAuthentication/ConnectionAuthenticationForm.tsx @@ -21,7 +21,7 @@ import { DatabaseAuthModelsResource } from '@cloudbeaver/core-connections'; import { useService } from '@cloudbeaver/core-di'; import { ProjectInfoResource } from '@cloudbeaver/core-projects'; import { ServerConfigResource } from '@cloudbeaver/core-root'; -import type { ObjectPropertyInfo } from '@cloudbeaver/core-sdk'; +import type { IObjectPropertyInfo } from '@cloudbeaver/core-sdk'; import type { IConnectionAuthenticationConfig } from './IConnectionAuthenticationConfig.js'; import { NetworkHandlers } from './NetworkHandlers.js'; @@ -29,7 +29,7 @@ import { NetworkHandlers } from './NetworkHandlers.js'; export interface ConnectionAuthenticationFormProps { config: Partial; authModelId: string | null; - authProperties?: ObjectPropertyInfo[]; + authProperties?: IObjectPropertyInfo[]; networkHandlers?: string[]; formId?: string; allowSaveCredentials?: boolean; diff --git a/webapp/packages/plugin-connections/src/ConnectionForm/ConnectionAuthModelCredentials/ConnectionAuthModelCredentialsForm.tsx b/webapp/packages/plugin-connections/src/ConnectionForm/ConnectionAuthModelCredentials/ConnectionAuthModelCredentialsForm.tsx index d512a14ffe..2666971d0c 100644 --- a/webapp/packages/plugin-connections/src/ConnectionForm/ConnectionAuthModelCredentials/ConnectionAuthModelCredentialsForm.tsx +++ b/webapp/packages/plugin-connections/src/ConnectionForm/ConnectionAuthModelCredentials/ConnectionAuthModelCredentialsForm.tsx @@ -8,12 +8,12 @@ import { observer } from 'mobx-react-lite'; import { Container, ObjectPropertyInfoForm, type ILayoutSizeProps } from '@cloudbeaver/core-blocks'; -import { getObjectPropertyType, type ObjectPropertyInfo } from '@cloudbeaver/core-sdk'; +import { getObjectPropertyType, type IObjectPropertyInfo } from '@cloudbeaver/core-sdk'; interface Props { credentials?: Record; defaultCredentials?: Record; - properties: ReadonlyArray; + properties: ReadonlyArray; readonly?: boolean; disabled?: boolean; } @@ -25,7 +25,7 @@ export const ConnectionAuthModelCredentialsForm = observer(function Conne readonly, disabled, }) { - function getLayoutSize(property: ObjectPropertyInfo): ILayoutSizeProps { + function getLayoutSize(property: IObjectPropertyInfo): ILayoutSizeProps { const type = getObjectPropertyType(property); if (type === 'checkbox') { diff --git a/webapp/packages/plugin-connections/src/ConnectionForm/Options/ConnectionFormOptionsPart.ts b/webapp/packages/plugin-connections/src/ConnectionForm/Options/ConnectionFormOptionsPart.ts index b75c72c35c..91fed24f80 100644 --- a/webapp/packages/plugin-connections/src/ConnectionForm/Options/ConnectionFormOptionsPart.ts +++ b/webapp/packages/plugin-connections/src/ConnectionForm/Options/ConnectionFormOptionsPart.ts @@ -6,7 +6,14 @@ * you may not use this file except in compliance with the License. */ import { FormMode, FormPart, formSubmitContext, formValidationContext, type IFormState } from '@cloudbeaver/core-ui'; -import { DriverConfigurationType, type ConnectionConfig, type ObjectPropertyInfo, type TestConnectionMutation } from '@cloudbeaver/core-sdk'; +import { + DriverConfigurationType, + getObjectPropertyDefaultValue, + getObjectPropertyValue, + type ConnectionConfig, + type IObjectPropertyInfo, + type TestConnectionMutation, +} from '@cloudbeaver/core-sdk'; import { Executor, ExecutorInterrupter, type IExecutionContextProvider, type IExecutor } from '@cloudbeaver/core-executor'; import { ConnectionInfoAuthPropertiesResource, @@ -223,7 +230,7 @@ export class ConnectionFormOptionsPart extends FormPart property.features.includes('password')); const isPasswordEmpty = passwordProperty && - (this.state.credentials?.[passwordProperty.id!] === passwordProperty.defaultValue || !this.state.credentials?.[passwordProperty.id!]); + (this.state.credentials?.[passwordProperty.id!] === getObjectPropertyDefaultValue(passwordProperty) || + !this.state.credentials?.[passwordProperty.id!]); if (isCredentialsChanged(properties, this.state.credentials!)) { this.state.credentials = prepareDynamicProperties(properties, toJS(this.state.credentials!)); @@ -410,7 +418,7 @@ export class ConnectionFormOptionsPart extends FormPart { + private async getConnectionAuthModelProperties(authModelId: string, connectionInfo?: ConnectionInfoAuthProperties): Promise { const authModel = await this.databaseAuthModelsResource.load(authModelId); let properties = authModel.properties; @@ -533,7 +541,7 @@ export class ConnectionFormOptionsPart extends FormPart, configurationType?: DriverConfigurationType, ) { @@ -550,8 +558,9 @@ function prepareDynamicProperties( delete result[propertyInfo.id]; } else { const isDefault = isNotNullDefined(propertyInfo.defaultValue); + if (!(propertyInfo.id in result) && isDefault) { - result[propertyInfo.id] = propertyInfo.defaultValue; + result[propertyInfo.id] = getObjectPropertyDefaultValue(propertyInfo); } } } @@ -565,7 +574,7 @@ function prepareDynamicProperties( return result; } -function isCredentialsChanged(authProperties: ObjectPropertyInfo[], credentials: Record) { +function isCredentialsChanged(authProperties: IObjectPropertyInfo[], credentials: Record) { for (const property of authProperties) { const value = credentials[property.id!]; @@ -573,7 +582,7 @@ function isCredentialsChanged(authProperties: ObjectPropertyInfo[], credentials: if (value !== undefined) { return property.features.includes('file') ? true : !!value; } - } else if (value !== property.value) { + } else if (value !== getObjectPropertyValue(property)) { return true; } } diff --git a/webapp/packages/plugin-connections/src/ConnectionForm/SSL/ConnectionFormSSLPart.ts b/webapp/packages/plugin-connections/src/ConnectionForm/SSL/ConnectionFormSSLPart.ts index f1d93c7e83..3ad3b17d03 100644 --- a/webapp/packages/plugin-connections/src/ConnectionForm/SSL/ConnectionFormSSLPart.ts +++ b/webapp/packages/plugin-connections/src/ConnectionForm/SSL/ConnectionFormSSLPart.ts @@ -9,7 +9,7 @@ import { FormPart, formSubmitContext, type IFormState } from '@cloudbeaver/core- import type { IExecutionContextProvider } from '@cloudbeaver/core-executor'; import type { IConnectionFormState } from '../IConnectionFormState.js'; -import { type NetworkHandlerConfigInput } from '@cloudbeaver/core-sdk'; +import { getObjectPropertyDefaultValue, type NetworkHandlerConfigInput } from '@cloudbeaver/core-sdk'; import { isNotNullDefined } from '@dbeaver/js-helpers'; import { getSSLDriverHandler } from './getSSLDriverHandler.js'; import { ConnectionInfoNetworkHandlersResource, type DBDriverResource, type NetworkHandlerResource } from '@cloudbeaver/core-connections'; @@ -121,7 +121,7 @@ export class ConnectionFormSSLPart extends FormPart [GraphQLService, ServerConfigResource]) -export class DataTransferProcessorsResource extends CachedMapResource { +export class DataTransferProcessorsResource extends CachedMapResource { constructor( private readonly graphQLService: GraphQLService, serverConfigResource: ServerConfigResource, @@ -24,7 +28,7 @@ export class DataTransferProcessorsResource extends CachedMapResource> { + protected async loader(): Promise> { const { processors } = await this.graphQLService.sdk.getDataTransferProcessors(); this.replace(resourceKeyList(processors.map(processor => processor.id)), processors); diff --git a/webapp/packages/plugin-data-export/src/Dialog/useDataExportDialog.ts b/webapp/packages/plugin-data-export/src/Dialog/useDataExportDialog.ts index dfbf66d5b1..724eb44b54 100644 --- a/webapp/packages/plugin-data-export/src/Dialog/useDataExportDialog.ts +++ b/webapp/packages/plugin-data-export/src/Dialog/useDataExportDialog.ts @@ -11,10 +11,10 @@ import { type IProperty, useObservableRef } from '@cloudbeaver/core-blocks'; import { useService } from '@cloudbeaver/core-di'; import { NotificationService } from '@cloudbeaver/core-events'; import { LocalizationService } from '@cloudbeaver/core-localization'; -import type { DataTransferOutputSettings, DataTransferProcessorInfo } from '@cloudbeaver/core-sdk'; +import { getObjectPropertyDefaultValue, type DataTransferOutputSettings } from '@cloudbeaver/core-sdk'; import { DataExportService } from '../DataExportService.js'; -import { DataTransferProcessorsResource } from '../DataTransferProcessorsResource.js'; +import { DataTransferProcessorsResource, type IDataTransferProcessorInfo } from '../DataTransferProcessorsResource.js'; import type { IExportContext } from '../IExportContext.js'; import { DefaultExportOutputSettingsResource } from './DefaultExportOutputSettingsResource.js'; import { EDataExportStep } from './EDataExportStep.js'; @@ -22,7 +22,7 @@ import { EDataExportStep } from './EDataExportStep.js'; interface State { readonly properties: IProperty[]; step: EDataExportStep; - processor: DataTransferProcessorInfo | null; + processor: IDataTransferProcessorInfo | null; processorProperties: Record; outputSettings: Partial; processing: boolean; @@ -46,19 +46,23 @@ export function useDataExportDialog(context: IExportContext, onExport?: () => vo return []; } - return this.processor.properties.map(property => ({ - id: property.id!, - key: property.id!, - displayName: property.displayName, - description: property.description, - validValues: property.validValues, - defaultValue: property.defaultValue, - valuePlaceholder: property.defaultValue, - })); + return this.processor.properties.map(property => { + const defaultValue = getObjectPropertyDefaultValue(property); + + return { + id: property.id!, + key: property.id!, + displayName: property.displayName, + description: property.description, + validValues: property.validValues, + defaultValue, + valuePlaceholder: defaultValue, + }; + }); }, step: EDataExportStep.DataTransferProcessor, processing: false, - processor: null as DataTransferProcessorInfo | null, + processor: null as IDataTransferProcessorInfo | null, processorProperties: {}, outputSettings: {}, exception: null, diff --git a/webapp/packages/plugin-object-viewer/src/ObjectPropertiesPage/NavNodeView/NavNodeMetadata/ObjectProperties.tsx b/webapp/packages/plugin-object-viewer/src/ObjectPropertiesPage/NavNodeView/NavNodeMetadata/ObjectProperties.tsx index fa5ce5449a..5a0f04c0ce 100644 --- a/webapp/packages/plugin-object-viewer/src/ObjectPropertiesPage/NavNodeView/NavNodeMetadata/ObjectProperties.tsx +++ b/webapp/packages/plugin-object-viewer/src/ObjectPropertiesPage/NavNodeView/NavNodeMetadata/ObjectProperties.tsx @@ -20,13 +20,13 @@ import { import { useService } from '@cloudbeaver/core-di'; import { DBObjectResource, NavNodeInfoResource } from '@cloudbeaver/core-navigation-tree'; import { resourceKeyList } from '@cloudbeaver/core-resource'; -import type { ObjectPropertyInfo } from '@cloudbeaver/core-sdk'; +import type { IObjectPropertyInfo } from '@cloudbeaver/core-sdk'; interface Props { objectId: string; } -const emptyArray: ObjectPropertyInfo[] = []; +const emptyArray: IObjectPropertyInfo[] = []; export const ObjectProperties = observer(function ObjectProperties({ objectId }) { const translate = useTranslate(); diff --git a/webapp/packages/plugin-object-viewer/src/ObjectPropertiesPage/NavNodeView/NavNodeMetadata/ObjectProperty.tsx b/webapp/packages/plugin-object-viewer/src/ObjectPropertiesPage/NavNodeView/NavNodeMetadata/ObjectProperty.tsx index f570bc0ec8..dd0b13b5bc 100644 --- a/webapp/packages/plugin-object-viewer/src/ObjectPropertiesPage/NavNodeView/NavNodeMetadata/ObjectProperty.tsx +++ b/webapp/packages/plugin-object-viewer/src/ObjectPropertiesPage/NavNodeView/NavNodeMetadata/ObjectProperty.tsx @@ -8,10 +8,10 @@ import { observer } from 'mobx-react-lite'; import { FieldCheckbox, type ILayoutSizeProps, InputField } from '@cloudbeaver/core-blocks'; -import { getObjectPropertyType, getObjectPropertyValue, type ObjectPropertyInfo } from '@cloudbeaver/core-sdk'; +import { getObjectPropertyType, getObjectPropertyValue, type IObjectPropertyInfo } from '@cloudbeaver/core-sdk'; interface Props extends ILayoutSizeProps { - objectProperty?: ObjectPropertyInfo; + objectProperty?: IObjectPropertyInfo; className?: string; } diff --git a/webapp/packages/plugin-sql-editor/src/SqlResultTabs/ExecutionPlan/NestedNode.tsx b/webapp/packages/plugin-sql-editor/src/SqlResultTabs/ExecutionPlan/NestedNode.tsx index 33d011d792..8ad1be0f36 100644 --- a/webapp/packages/plugin-sql-editor/src/SqlResultTabs/ExecutionPlan/NestedNode.tsx +++ b/webapp/packages/plugin-sql-editor/src/SqlResultTabs/ExecutionPlan/NestedNode.tsx @@ -9,7 +9,7 @@ import { useCallback, useState } from 'react'; import { EventTableItemSelectionFlag, TableColumnValue, TableItem } from '@cloudbeaver/core-blocks'; import { EventContext } from '@cloudbeaver/core-events'; -import type { ObjectPropertyInfo } from '@cloudbeaver/core-sdk'; +import type { IObjectPropertyInfo } from '@cloudbeaver/core-sdk'; import type { IExecutionPlanNode } from './ExecutionPlanTreeContext.js'; import { Expand } from './Expand.js'; @@ -17,7 +17,7 @@ import { getPropertyValue } from './getPropertyValue.js'; import classes from './NestedNode.module.css'; interface Props { - columns: ObjectPropertyInfo[]; + columns: IObjectPropertyInfo[]; node: IExecutionPlanNode; depth: number; className?: string; diff --git a/webapp/packages/plugin-sql-editor/src/SqlResultTabs/ExecutionPlan/PropertiesPanel/PropertiesPanelItemsGroup.tsx b/webapp/packages/plugin-sql-editor/src/SqlResultTabs/ExecutionPlan/PropertiesPanel/PropertiesPanelItemsGroup.tsx index 52274a6235..332e08910a 100644 --- a/webapp/packages/plugin-sql-editor/src/SqlResultTabs/ExecutionPlan/PropertiesPanel/PropertiesPanelItemsGroup.tsx +++ b/webapp/packages/plugin-sql-editor/src/SqlResultTabs/ExecutionPlan/PropertiesPanel/PropertiesPanelItemsGroup.tsx @@ -8,14 +8,14 @@ import { useState } from 'react'; import { TableColumnValue, TableItem } from '@cloudbeaver/core-blocks'; -import type { ObjectPropertyInfo } from '@cloudbeaver/core-sdk'; +import type { IObjectPropertyInfo } from '@cloudbeaver/core-sdk'; import { Expand } from '../Expand.js'; import { getPropertyValue } from '../getPropertyValue.js'; import classes from './PropertiesPanelItemsGroup.module.css'; interface Props { - properties: ObjectPropertyInfo[]; + properties: IObjectPropertyInfo[]; name: string; } diff --git a/webapp/packages/plugin-sql-editor/src/SqlResultTabs/ExecutionPlan/getPropertyValue.ts b/webapp/packages/plugin-sql-editor/src/SqlResultTabs/ExecutionPlan/getPropertyValue.ts index ad2de784cc..186749c14a 100644 --- a/webapp/packages/plugin-sql-editor/src/SqlResultTabs/ExecutionPlan/getPropertyValue.ts +++ b/webapp/packages/plugin-sql-editor/src/SqlResultTabs/ExecutionPlan/getPropertyValue.ts @@ -5,9 +5,9 @@ * Licensed under the Apache License, Version 2.0. * you may not use this file except in compliance with the License. */ -import type { ObjectPropertyInfo } from '@cloudbeaver/core-sdk'; +import type { IObjectPropertyInfo } from '@cloudbeaver/core-sdk'; -export function getPropertyValue(property: ObjectPropertyInfo): string { +export function getPropertyValue(property: IObjectPropertyInfo): string { const value = property.value; if (!value) { diff --git a/webapp/packages/plugin-system-information-administration/src/SystemInformationResource.ts b/webapp/packages/plugin-system-information-administration/src/SystemInformationResource.ts index 946e8bb818..612836062e 100644 --- a/webapp/packages/plugin-system-information-administration/src/SystemInformationResource.ts +++ b/webapp/packages/plugin-system-information-administration/src/SystemInformationResource.ts @@ -8,11 +8,11 @@ import { injectable } from '@cloudbeaver/core-di'; import { CachedDataResource } from '@cloudbeaver/core-resource'; -import { GraphQLService, type ObjectPropertyInfo } from '@cloudbeaver/core-sdk'; +import { GraphQLService, type IObjectPropertyInfo } from '@cloudbeaver/core-sdk'; import { SessionPermissionsResource, EAdminPermission } from '@cloudbeaver/core-root'; @injectable(() => [GraphQLService, SessionPermissionsResource]) -export class SystemInformationResource extends CachedDataResource { +export class SystemInformationResource extends CachedDataResource { constructor( private readonly graphQLService: GraphQLService, private readonly sessionPermissionsResource: SessionPermissionsResource, @@ -21,7 +21,7 @@ export class SystemInformationResource extends CachedDataResource { + protected async loader(): Promise { const { info } = await this.graphQLService.sdk.getSystemInfo(); return info; }