diff --git a/catalog-rest-service/src/main/resources/json/schema/entity/services/connections/connectionBasicType.json b/catalog-rest-service/src/main/resources/json/schema/entity/services/connections/connectionBasicType.json index d969ded3650..d2461c7c197 100644 --- a/catalog-rest-service/src/main/resources/json/schema/entity/services/connections/connectionBasicType.json +++ b/catalog-rest-service/src/main/resources/json/schema/entity/services/connections/connectionBasicType.json @@ -8,7 +8,7 @@ "javaType": "org.openmetadata.catalog.services.connections.database.ConnectionOptions", "description": "Additional connection options that can be sent to service during the connection.", "type": "object", - "patternProperties": { + "additionalProperties": { ".{1,}": { "type": "string" } } }, @@ -16,7 +16,7 @@ "javaType": "org.openmetadata.catalog.services.connections.database.ConnectionArguments", "description": "Additional connection arguments such as security or protocol configs that can be sent to service during connection.", "type": "object", - "patternProperties": { + "additionalProperties": { ".{1,}": { "type": "string" } } }, diff --git a/conf/openmetadata.yaml b/conf/openmetadata.yaml index 410461b5d49..78d1f1e5173 100644 --- a/conf/openmetadata.yaml +++ b/conf/openmetadata.yaml @@ -125,7 +125,6 @@ database: migrationConfiguration: path: "./bootstrap/sql" -# Authorizer Configuration # Authorizer Configuration authorizerConfiguration: className: ${AUTHORIZER_CLASS_NAME:-org.openmetadata.catalog.security.NoopAuthorizer} diff --git a/openmetadata-ui/src/main/resources/ui/src/components/ServiceConfig/ConnectionConfigForm.tsx b/openmetadata-ui/src/main/resources/ui/src/components/ServiceConfig/ConnectionConfigForm.tsx index d9a5b138257..e377e970c2e 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/ServiceConfig/ConnectionConfigForm.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/ServiceConfig/ConnectionConfigForm.tsx @@ -35,7 +35,7 @@ import { ConfigData } from '../../interface/service.interface'; import jsonData from '../../jsons/en'; import { getDashboardConfig } from '../../utils/DashboardServiceUtils'; import { getDatabaseConfig } from '../../utils/DatabaseServiceUtils'; -import { escapeBackwardSlashChar } from '../../utils/JSONSchemaFormUtils'; +import { formatFormDataForSubmit } from '../../utils/JSONSchemaFormUtils'; import { getMessagingConfig } from '../../utils/MessagingServiceUtils'; import { getPipelineConfig } from '../../utils/PipelineServiceUtils'; import { showErrorToast } from '../../utils/ToastUtils'; @@ -71,12 +71,12 @@ const ConnectionConfigForm: FunctionComponent = ({ : ({} as ConfigData); const handleSave = (data: ISubmitEvent) => { - const updatedFormData = escapeBackwardSlashChar(data.formData); + const updatedFormData = formatFormDataForSubmit(data.formData); onSave({ ...data, formData: updatedFormData }); }; const handleTestConnection = (formData: ConfigData) => { - const updatedFormData = escapeBackwardSlashChar(formData); + const updatedFormData = formatFormDataForSubmit(formData); return new Promise((resolve, reject) => { TestConnection(updatedFormData, 'Database') diff --git a/openmetadata-ui/src/main/resources/ui/src/components/common/FormBuilder/FormBuilder.tsx b/openmetadata-ui/src/main/resources/ui/src/components/common/FormBuilder/FormBuilder.tsx index ca5dfc60b94..565552aae7a 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/common/FormBuilder/FormBuilder.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/common/FormBuilder/FormBuilder.tsx @@ -18,6 +18,7 @@ import { debounce, isEmpty } from 'lodash'; import { LoadingState } from 'Models'; import React, { FunctionComponent, useCallback, useState } from 'react'; import { ConfigData } from '../../../interface/service.interface'; +import { formatFormDataForRender } from '../../../utils/JSONSchemaFormUtils'; import SVGIcons, { Icons } from '../../../utils/SvgUtils'; import { Button } from '../../buttons/Button/Button'; import { ArrayFieldTemplate } from '../../JSONSchemaTemplate/ArrayFieldTemplate'; @@ -43,18 +44,19 @@ const FormBuilder: FunctionComponent = ({ onCancel, onSubmit, onTestConnection, + uiSchema, ...props }: Props) => { let oForm: Form | null; const [localFormData, setLocalFormData] = useState( - formData + formatFormDataForRender(formData) ); const [connectionTesting, setConnectionTesting] = useState(false); const [connectionTestingState, setConnectionTestingState] = useState('initial'); const handleCancel = () => { - setLocalFormData(formData); + setLocalFormData(formatFormDataForRender(formData)); if (onCancel) { onCancel(); } @@ -137,6 +139,7 @@ const FormBuilder: FunctionComponent = ({ oForm = form; }} schema={schema} + uiSchema={uiSchema} onChange={(e) => { handleChange(e.formData); props.onChange && props.onChange(e); diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/JSONSchemaFormUtils.ts b/openmetadata-ui/src/main/resources/ui/src/utils/JSONSchemaFormUtils.ts index 86a55117c36..2ca66ef3fd6 100644 --- a/openmetadata-ui/src/main/resources/ui/src/utils/JSONSchemaFormUtils.ts +++ b/openmetadata-ui/src/main/resources/ui/src/utils/JSONSchemaFormUtils.ts @@ -11,7 +11,7 @@ * limitations under the License. */ -import { isString } from 'lodash'; +import { cloneDeep, isString } from 'lodash'; export function escapeBackwardSlashChar(formData: T): T { for (const key in formData) { @@ -30,3 +30,79 @@ export function escapeBackwardSlashChar(formData: T): T { return formData; } + +function formatConnectionFields(formData: T, field: string): T { + if (formData && formData[field as keyof T]) { + // Since connection options support value of type string or object + // try to parse the string value as object + const options = formData[field as keyof T]; + + for (const key in options) { + const value = options[key]; + try { + // eslint-disable-next-line @typescript-eslint/ban-ts-ignore + // @ts-ignore + formData[field as keyof T][key] = JSON.parse(value); + } catch (_) { + // ignore exception + } + } + } + + return formData; +} + +function formatAdditionalProperties(formData: T): T { + for (const key in formData) { + if (typeof formData[key as keyof T] === 'object') { + formatAdditionalProperties(formData[key as keyof T]); + } else { + const data = formData[key as keyof T]; + if ( + key.startsWith('newKey') && + data === ('New Value' as unknown as T[keyof T]) + ) { + delete formData[key]; + } + } + } + + return formData; +} + +export function formatFormDataForSubmit(formData: T): T { + formData = cloneDeep(formData); + formData = escapeBackwardSlashChar(formData); + formData = formatAdditionalProperties(formData); + formData = formatConnectionFields(formData, 'connectionOptions'); + formData = formatConnectionFields(formData, 'connectionArguments'); + + return formData; +} + +function formatConnectionFieldsForRender(formData: T, field: string): T { + if (formData && formData[field as keyof T]) { + // Since connection options support value of type string or object + // convert object into string + const options = formData[field as keyof T]; + + for (const key in options) { + const value = options[key]; + if (typeof value === 'object') { + // eslint-disable-next-line @typescript-eslint/ban-ts-ignore + // @ts-ignore + formData[field as keyof T][key] = JSON.stringify(value); + } + } + } + + return formData; +} + +export function formatFormDataForRender(formData: T): T { + formData = cloneDeep(formData); + formData = formatConnectionFieldsForRender(formData, 'connectionOptions'); + formData = formatConnectionFieldsForRender(formData, 'connectionArguments'); + + return formData; +}