diff --git a/openmetadata-ui/src/main/resources/ui/playwright/e2e/VersionPages/EntityVersionPages.spec.ts b/openmetadata-ui/src/main/resources/ui/playwright/e2e/VersionPages/EntityVersionPages.spec.ts
index d7537fe8f5f..5ae091aad1a 100644
--- a/openmetadata-ui/src/main/resources/ui/playwright/e2e/VersionPages/EntityVersionPages.spec.ts
+++ b/openmetadata-ui/src/main/resources/ui/playwright/e2e/VersionPages/EntityVersionPages.spec.ts
@@ -22,7 +22,11 @@ import {
redirectToHomePage,
toastNotification,
} from '../../utils/common';
-import { addMultiOwner, assignTier } from '../../utils/entity';
+import {
+ addMultiOwner,
+ assignTier,
+ getEntityDataTypeDisplayPatch,
+} from '../../utils/entity';
const entityCreationConfig: EntityDataClassCreationConfig = {
apiEndpoint: true,
@@ -78,6 +82,7 @@ test.describe('Entity Version pages', () => {
const domain = EntityDataClass.domain1.responseData;
for (const entity of entities) {
+ const dataTypeDisplayPath = getEntityDataTypeDisplayPatch(entity);
await entity.patch({
apiContext,
patchData: [
@@ -116,6 +121,15 @@ test.describe('Entity Version pages', () => {
description: domain.description,
},
},
+ ...(dataTypeDisplayPath
+ ? [
+ {
+ op: 'add' as const,
+ path: dataTypeDisplayPath,
+ value: 'OBJECT',
+ },
+ ]
+ : []),
],
});
}
diff --git a/openmetadata-ui/src/main/resources/ui/playwright/utils/entity.ts b/openmetadata-ui/src/main/resources/ui/playwright/utils/entity.ts
index 4cd3c61ddba..9aa1b5f00c3 100644
--- a/openmetadata-ui/src/main/resources/ui/playwright/utils/entity.ts
+++ b/openmetadata-ui/src/main/resources/ui/playwright/utils/entity.ts
@@ -1728,3 +1728,22 @@ export const checkExploreSearchFilter = async (
await entity?.visitEntityPage(page);
};
+
+export const getEntityDataTypeDisplayPatch = (entity: EntityClass) => {
+ switch (entity.getType()) {
+ case 'Table':
+ case 'Dashboard Data Model':
+ return '/columns/0/dataTypeDisplay';
+ case 'ApiEndpoint':
+ return '/requestSchema/schemaFields/0/dataTypeDisplay';
+ case 'Topic':
+ return '/messageSchema/schemaFields/0/dataTypeDisplay';
+ case 'Container':
+ return '/dataModel/columns/0/dataTypeDisplay';
+ case 'SearchIndex':
+ return '/fields/0/dataTypeDisplay';
+
+ default:
+ return undefined;
+ }
+};
diff --git a/openmetadata-ui/src/main/resources/ui/src/constants/Feeds.constants.ts b/openmetadata-ui/src/main/resources/ui/src/constants/Feeds.constants.ts
index d6d40952d3b..8d1b163d2ec 100644
--- a/openmetadata-ui/src/main/resources/ui/src/constants/Feeds.constants.ts
+++ b/openmetadata-ui/src/main/resources/ui/src/constants/Feeds.constants.ts
@@ -90,6 +90,7 @@ export enum EntityField {
EXPERTS = 'experts',
FIELDS = 'fields',
PARAMETER_VALUES = 'parameterValues',
+ DATA_TYPE_DISPLAY = 'dataTypeDisplay',
}
export const ANNOUNCEMENT_BG = '#FFFDF8';
diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/EntityVersionUtils.test.tsx b/openmetadata-ui/src/main/resources/ui/src/utils/EntityVersionUtils.test.tsx
new file mode 100644
index 00000000000..6afaaff69ee
--- /dev/null
+++ b/openmetadata-ui/src/main/resources/ui/src/utils/EntityVersionUtils.test.tsx
@@ -0,0 +1,426 @@
+/*
+ * Copyright 2025 Collate.
+ * 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.
+ */
+
+import { EntityField } from '../constants/Feeds.constants';
+import {
+ Column as ContainerColumn,
+ DataType as ContainerDataType,
+} from '../generated/entity/data/container';
+import {
+ Column as TableColumn,
+ DataType as TableDataType,
+} from '../generated/entity/data/table';
+import { DataTypeTopic, Field } from '../generated/entity/data/topic';
+import { FieldChange } from '../generated/entity/services/databaseService';
+import { getStringEntityDiff } from './EntityVersionUtils';
+
+// Mock data for testing
+const createMockTableColumn = (
+ name: string,
+ displayName?: string
+): TableColumn => ({
+ name,
+ displayName,
+ dataType: TableDataType.String,
+ dataTypeDisplay: 'string',
+ fullyQualifiedName: `test.table.${name}`,
+ tags: [],
+ children: [],
+});
+
+const createMockContainerColumn = (
+ name: string,
+ displayName?: string
+): ContainerColumn => ({
+ name,
+ displayName,
+ dataType: ContainerDataType.String,
+ dataTypeDisplay: 'string',
+ fullyQualifiedName: `test.container.${name}`,
+ tags: [],
+ children: [],
+});
+
+const createMockField = (name: string, displayName?: string): Field => ({
+ name,
+ displayName,
+ dataType: DataTypeTopic.String,
+ dataTypeDisplay: 'string',
+ fullyQualifiedName: `test.topic.${name}`,
+ tags: [],
+ children: [],
+});
+
+const createMockFieldChange = (
+ name: string,
+ oldValue: string,
+ newValue: string
+): FieldChange => ({
+ name,
+ oldValue,
+ newValue,
+});
+
+const createMockEntityDiff = (
+ added?: FieldChange,
+ deleted?: FieldChange,
+ updated?: FieldChange
+) => ({
+ added,
+ deleted,
+ updated,
+});
+
+describe('getStringEntityDiff', () => {
+ describe('TableColumn entity', () => {
+ it('should update displayName with diff when entity name matches', () => {
+ const oldDisplayName = 'Old Display Name';
+ const newDisplayName = 'New Display Name';
+ const entityName = 'testColumn';
+
+ const entityDiff = createMockEntityDiff(
+ undefined,
+ undefined,
+ createMockFieldChange('displayName', oldDisplayName, newDisplayName)
+ );
+
+ const columns = [
+ createMockTableColumn(entityName, oldDisplayName),
+ createMockTableColumn('otherColumn', 'Other Display Name'),
+ ];
+
+ const result = getStringEntityDiff(
+ entityDiff,
+ EntityField.DISPLAYNAME,
+ entityName,
+ columns
+ );
+
+ expect(result).toHaveLength(2);
+ expect(result[0].displayName).toContain('diff-removed');
+ expect(result[0].displayName).toContain('diff-added');
+ expect(result[1].displayName).toBe('Other Display Name');
+ });
+
+ it('should update description field when DESCRIPTION field is passed', () => {
+ const oldDescription = 'Old description';
+ const newDescription = 'New description';
+ const entityName = 'testColumn';
+
+ const entityDiff = createMockEntityDiff(
+ undefined,
+ undefined,
+ createMockFieldChange('description', oldDescription, newDescription)
+ );
+
+ const columns = [
+ { ...createMockTableColumn(entityName), description: oldDescription },
+ createMockTableColumn('otherColumn', 'Other Display Name'),
+ ];
+
+ const result = getStringEntityDiff(
+ entityDiff,
+ EntityField.DESCRIPTION,
+ entityName,
+ columns
+ );
+
+ expect(result).toHaveLength(2);
+ expect(result[0].description).toContain('diff-removed');
+ expect(result[0].description).toContain('diff-added');
+ expect(result[1].description).toBeUndefined();
+ });
+
+ it('should handle nested children entities', () => {
+ const oldDisplayName = 'Old Child Display Name';
+ const newDisplayName = 'New Child Display Name';
+ const childEntityName = 'childColumn';
+
+ const entityDiff = createMockEntityDiff(
+ undefined,
+ undefined,
+ createMockFieldChange('displayName', oldDisplayName, newDisplayName)
+ );
+
+ const childColumn = createMockTableColumn(
+ childEntityName,
+ oldDisplayName
+ );
+ const parentColumn = {
+ ...createMockTableColumn('parentColumn', 'Parent Display Name'),
+ children: [childColumn],
+ };
+
+ const columns = [parentColumn];
+
+ const result = getStringEntityDiff(
+ entityDiff,
+ EntityField.DISPLAYNAME,
+ childEntityName,
+ columns
+ );
+
+ expect(result).toHaveLength(1);
+ expect(result[0].children?.[0].displayName).toContain('diff-removed');
+ expect(result[0].children?.[0].displayName).toContain('diff-added');
+ expect(result[0].displayName).toBe('Parent Display Name');
+ });
+
+ it('should not modify entities when name does not match', () => {
+ const entityDiff = createMockEntityDiff(
+ undefined,
+ undefined,
+ createMockFieldChange('displayName', 'Old Name', 'New Name')
+ );
+
+ const columns = [
+ createMockTableColumn('differentColumn', 'Original Display Name'),
+ ];
+
+ const result = getStringEntityDiff(
+ entityDiff,
+ EntityField.DISPLAYNAME,
+ 'nonExistentColumn',
+ columns
+ );
+
+ expect(result).toHaveLength(1);
+ expect(result[0].displayName).toBe('Original Display Name');
+ });
+ });
+
+ describe('ContainerColumn entity', () => {
+ it('should update displayName for ContainerColumn', () => {
+ const oldDisplayName = 'Old Container Display Name';
+ const newDisplayName = 'New Container Display Name';
+ const entityName = 'containerColumn';
+
+ const entityDiff = createMockEntityDiff(
+ undefined,
+ undefined,
+ createMockFieldChange('displayName', oldDisplayName, newDisplayName)
+ );
+
+ const columns = [createMockContainerColumn(entityName, oldDisplayName)];
+
+ const result = getStringEntityDiff(
+ entityDiff,
+ EntityField.DISPLAYNAME,
+ entityName,
+ columns
+ );
+
+ expect(result).toHaveLength(1);
+ expect(result[0].displayName).toContain('diff-removed');
+ expect(result[0].displayName).toContain('diff-added');
+ });
+ });
+
+ describe('Field entity', () => {
+ it('should update displayName for Field', () => {
+ const oldDisplayName = 'Old Field Display Name';
+ const newDisplayName = 'New Field Display Name';
+ const entityName = 'fieldName';
+
+ const entityDiff = createMockEntityDiff(
+ undefined,
+ undefined,
+ createMockFieldChange('displayName', oldDisplayName, newDisplayName)
+ );
+
+ const fields = [createMockField(entityName, oldDisplayName)];
+
+ const result = getStringEntityDiff(
+ entityDiff,
+ EntityField.DISPLAYNAME,
+ entityName,
+ fields
+ );
+
+ expect(result).toHaveLength(1);
+ expect(result[0].displayName).toContain('diff-removed');
+ expect(result[0].displayName).toContain('diff-added');
+ });
+ });
+
+ describe('Edge cases', () => {
+ it('should handle empty entity list', () => {
+ const entityDiff = createMockEntityDiff(
+ undefined,
+ undefined,
+ createMockFieldChange('displayName', 'Old', 'New')
+ );
+
+ const result = getStringEntityDiff(
+ entityDiff,
+ EntityField.DISPLAYNAME,
+ 'anyEntity',
+ []
+ );
+
+ expect(result).toHaveLength(0);
+ });
+
+ it('should handle undefined changedEntityName', () => {
+ const entityDiff = createMockEntityDiff(
+ undefined,
+ undefined,
+ createMockFieldChange('displayName', 'Old', 'New')
+ );
+
+ const columns = [
+ createMockTableColumn('testColumn', 'Test Display Name'),
+ ];
+
+ const result = getStringEntityDiff(
+ entityDiff,
+ EntityField.DISPLAYNAME,
+ undefined,
+ columns
+ );
+
+ expect(result).toHaveLength(1);
+ expect(result[0].displayName).toBe('Test Display Name');
+ });
+
+ it('should handle empty old and new values', () => {
+ const entityDiff = createMockEntityDiff(
+ undefined,
+ undefined,
+ createMockFieldChange('displayName', '', '')
+ );
+
+ const columns = [
+ createMockTableColumn('testColumn', 'Existing Display Name'),
+ ];
+
+ const result = getStringEntityDiff(
+ entityDiff,
+ EntityField.DISPLAYNAME,
+ 'testColumn',
+ columns
+ );
+
+ expect(result).toHaveLength(1);
+ // Should preserve the existing display name when both old and new are empty
+ expect(result[0].displayName).toBe('Existing Display Name');
+ });
+
+ it('should handle added field change', () => {
+ const newDisplayName = 'New Added Display Name';
+ const entityName = 'testColumn';
+
+ const entityDiff = createMockEntityDiff(
+ createMockFieldChange('displayName', '', newDisplayName),
+ undefined,
+ undefined
+ );
+
+ const columns = [createMockTableColumn(entityName, '')];
+
+ const result = getStringEntityDiff(
+ entityDiff,
+ EntityField.DISPLAYNAME,
+ entityName,
+ columns
+ );
+
+ expect(result).toHaveLength(1);
+ expect(result[0].displayName).toContain('diff-added');
+ });
+
+ it('should handle deleted field change', () => {
+ const oldDisplayName = 'Deleted Display Name';
+ const entityName = 'testColumn';
+
+ const entityDiff = createMockEntityDiff(
+ undefined,
+ createMockFieldChange('displayName', oldDisplayName, ''),
+ undefined
+ );
+
+ const columns = [createMockTableColumn(entityName, oldDisplayName)];
+
+ const result = getStringEntityDiff(
+ entityDiff,
+ EntityField.DISPLAYNAME,
+ entityName,
+ columns
+ );
+
+ expect(result).toHaveLength(1);
+ expect(result[0].displayName).toContain('diff-removed');
+ });
+ });
+
+ describe('Different EntityField values', () => {
+ it('should handle DATA_TYPE_DISPLAY field', () => {
+ const oldDataTypeDisplay = 'VARCHAR(255)';
+ const newDataTypeDisplay = 'TEXT';
+ const entityName = 'testColumn';
+
+ const entityDiff = createMockEntityDiff(
+ undefined,
+ undefined,
+ createMockFieldChange(
+ EntityField.DATA_TYPE_DISPLAY,
+ oldDataTypeDisplay,
+ newDataTypeDisplay
+ )
+ );
+
+ const columns = [
+ {
+ ...createMockTableColumn(entityName),
+ dataTypeDisplay: oldDataTypeDisplay,
+ },
+ ];
+
+ const result = getStringEntityDiff(
+ entityDiff,
+ EntityField.DATA_TYPE_DISPLAY,
+ entityName,
+ columns
+ );
+
+ expect(result).toHaveLength(1);
+ expect(result[0].dataTypeDisplay).toContain('diff-removed');
+ expect(result[0].dataTypeDisplay).toContain('diff-added');
+ });
+
+ it('should handle NAME field', () => {
+ const oldName = 'oldColumnName';
+ const newName = 'newColumnName';
+ const entityName = 'oldColumnName';
+
+ const entityDiff = createMockEntityDiff(
+ undefined,
+ undefined,
+ createMockFieldChange('name', oldName, newName)
+ );
+
+ const columns = [{ ...createMockTableColumn(entityName), name: oldName }];
+
+ const result = getStringEntityDiff(
+ entityDiff,
+ EntityField.NAME,
+ entityName,
+ columns
+ );
+
+ expect(result).toHaveLength(1);
+ expect(result[0].name).toContain('diff-removed');
+ expect(result[0].name).toContain('diff-added');
+ });
+ });
+});
diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/EntityVersionUtils.tsx b/openmetadata-ui/src/main/resources/ui/src/utils/EntityVersionUtils.tsx
index fe237601f0b..f7d545efbcf 100644
--- a/openmetadata-ui/src/main/resources/ui/src/utils/EntityVersionUtils.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/utils/EntityVersionUtils.tsx
@@ -70,6 +70,8 @@ import { t } from './i18next/LocalUtil';
import { getJSONFromString, isValidJSONString } from './StringsUtils';
import { getTagsWithoutTier, getTierTags } from './TableUtils';
+type EntityColumn = TableColumn | ContainerColumn | Field;
+
export const getChangedEntityName = (diffObject?: EntityDiffProps) =>
diffObject?.added?.name ??
diffObject?.deleted?.name ??
@@ -506,10 +508,9 @@ export function getEntityDescriptionDiff(
return entityList;
}
-export function getEntityDisplayNameDiff<
- A extends TableColumn | ContainerColumn | Field
->(
+export function getStringEntityDiff(
entityDiff: EntityDiffProps,
+ key: EntityField,
changedEntityName?: string,
entityList: A[] = []
) {
@@ -519,11 +520,11 @@ export function getEntityDisplayNameDiff<
const formatEntityData = (arr: Array) => {
arr?.forEach((i) => {
if (isEqual(i.name, changedEntityName)) {
- i.displayName = getTextDiff(
+ i[key as keyof A] = getTextDiff(
oldDisplayName ?? '',
newDisplayName ?? '',
- i.displayName
- );
+ i[key as keyof typeof i] as unknown as string
+ ) as unknown as A[keyof A];
} else {
formatEntityData(i?.children as Array);
}
@@ -535,9 +536,11 @@ export function getEntityDisplayNameDiff<
return entityList;
}
-export function getEntityTagDiff<
- A extends TableColumn | ContainerColumn | Field
->(entityDiff: EntityDiffProps, changedEntityName?: string, entityList?: A[]) {
+export function getEntityTagDiff(
+ entityDiff: EntityDiffProps,
+ changedEntityName?: string,
+ entityList?: A[]
+) {
const oldTags: TagLabel[] = JSON.parse(
getChangedEntityOldValue(entityDiff) ?? '[]'
);
@@ -822,7 +825,23 @@ export function getColumnsDataWithVersionChanges<
];
} else if (isEndsWithField(EntityField.DISPLAYNAME, changedEntityName)) {
newColumnsList = [
- ...getEntityDisplayNameDiff(columnDiff, changedColName, colList),
+ ...getStringEntityDiff(
+ columnDiff,
+ EntityField.DISPLAYNAME,
+ changedColName,
+ colList
+ ),
+ ];
+ } else if (
+ isEndsWithField(EntityField.DATA_TYPE_DISPLAY, changedEntityName)
+ ) {
+ newColumnsList = [
+ ...getStringEntityDiff(
+ columnDiff,
+ EntityField.DATA_TYPE_DISPLAY,
+ changedColName,
+ colList
+ ),
];
} else if (!isEndsWithField(EntityField.CONSTRAINT, changedEntityName)) {
const changedEntity = changedEntityName
diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/FeedUtils.tsx b/openmetadata-ui/src/main/resources/ui/src/utils/FeedUtils.tsx
index c7eb49a1922..36376fb2891 100644
--- a/openmetadata-ui/src/main/resources/ui/src/utils/FeedUtils.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/utils/FeedUtils.tsx
@@ -626,6 +626,7 @@ export const getFeedChangeFieldLabel = (fieldName?: EntityField) => {
[EntityField.EXPERTS]: t('label.expert-plural'),
[EntityField.FIELDS]: t('label.field-plural'),
[EntityField.PARAMETER_VALUES]: t('label.parameter-plural'),
+ [EntityField.DATA_TYPE_DISPLAY]: t('label.data-type-display'),
};
return isUndefined(fieldName) ? '' : fieldNameLabelMapping[fieldName];
diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/SchemaVersionUtils.test.ts b/openmetadata-ui/src/main/resources/ui/src/utils/SchemaVersionUtils.test.ts
new file mode 100644
index 00000000000..b2596d62114
--- /dev/null
+++ b/openmetadata-ui/src/main/resources/ui/src/utils/SchemaVersionUtils.test.ts
@@ -0,0 +1,313 @@
+/*
+ * Copyright 2023 Collate.
+ * 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.
+ */
+
+import {
+ ChangeDescription,
+ DataTypeTopic,
+ Field,
+ MessageSchemaObject,
+ SchemaType,
+} from '../generated/entity/data/topic';
+import { FieldChange } from '../generated/entity/services/databaseService';
+import { getVersionedSchema } from './SchemaVersionUtils';
+
+// Mock data for testing
+const createMockField = (
+ name: string,
+ dataTypeDisplay?: string,
+ dataType: DataTypeTopic = DataTypeTopic.String
+): Field => ({
+ name,
+ dataType,
+ dataTypeDisplay,
+ fullyQualifiedName: `test.topic.schema.${name}`,
+ tags: [],
+ children: [],
+});
+
+const createMockMessageSchema = (fields: Field[]): MessageSchemaObject => ({
+ schemaFields: fields,
+ schemaType: SchemaType.Avro,
+});
+
+const createMockFieldChange = (
+ name: string,
+ oldValue: string,
+ newValue: string
+): FieldChange => ({
+ name,
+ oldValue,
+ newValue,
+});
+
+const createMockChangeDescription = (
+ fieldsAdded?: FieldChange[],
+ fieldsDeleted?: FieldChange[],
+ fieldsUpdated?: FieldChange[]
+): ChangeDescription => ({
+ fieldsAdded: fieldsAdded || [],
+ fieldsDeleted: fieldsDeleted || [],
+ fieldsUpdated: fieldsUpdated || [],
+});
+
+describe('SchemaVersionUtils', () => {
+ describe('getVersionedSchema', () => {
+ describe('DATA_TYPE_DISPLAY changes', () => {
+ it('should update dataTypeDisplay with diff highlighting when field is changed', () => {
+ const oldDataTypeDisplay = 'VARCHAR(255)';
+ const newDataTypeDisplay = 'TEXT';
+ const fieldName = 'testField';
+
+ // Create mock schema with a field that has dataTypeDisplay
+ const originalSchema = createMockMessageSchema([
+ createMockField(fieldName, oldDataTypeDisplay),
+ createMockField('otherField', 'INT'),
+ ]);
+
+ // Create change description for dataTypeDisplay update
+ const changeDescription = createMockChangeDescription(
+ undefined,
+ undefined,
+ [
+ createMockFieldChange(
+ `schemaFields.${fieldName}.dataTypeDisplay`,
+ oldDataTypeDisplay,
+ newDataTypeDisplay
+ ),
+ ]
+ );
+
+ // Execute the function
+ const result = getVersionedSchema(originalSchema, changeDescription);
+
+ // Verify the result
+ expect(result.schemaFields).toHaveLength(2);
+ expect(result.schemaFields?.[0].name).toBe(fieldName);
+ expect(result.schemaFields?.[0].dataTypeDisplay).toContain(
+ 'diff-removed'
+ );
+ expect(result.schemaFields?.[0].dataTypeDisplay).toContain(
+ 'diff-added'
+ );
+ expect(result.schemaFields?.[0].dataTypeDisplay).toContain(
+ oldDataTypeDisplay
+ );
+ expect(result.schemaFields?.[0].dataTypeDisplay).toContain(
+ newDataTypeDisplay
+ );
+
+ // Other field should remain unchanged
+ expect(result.schemaFields?.[1].dataTypeDisplay).toBe('INT');
+ });
+
+ it('should handle nested field dataTypeDisplay changes', () => {
+ const oldDataTypeDisplay = 'STRUCT';
+ const newDataTypeDisplay = 'STRUCT';
+ const parentFieldName = 'parentField';
+ const childFieldName = 'childField';
+
+ // Create mock schema with nested fields
+ const childField = createMockField(childFieldName, oldDataTypeDisplay);
+ const parentField = {
+ ...createMockField(parentFieldName, 'STRUCT'),
+ children: [childField],
+ };
+ const originalSchema = createMockMessageSchema([parentField]);
+
+ // Create change description for nested field dataTypeDisplay update
+ const changeDescription = createMockChangeDescription(
+ undefined,
+ undefined,
+ [
+ createMockFieldChange(
+ `schemaFields.${parentFieldName}.children.${childFieldName}.dataTypeDisplay`,
+ oldDataTypeDisplay,
+ newDataTypeDisplay
+ ),
+ ]
+ );
+
+ // Execute the function
+ const result = getVersionedSchema(originalSchema, changeDescription);
+
+ // Verify the result
+ expect(result.schemaFields).toHaveLength(1);
+ expect(result.schemaFields?.[0].children).toHaveLength(1);
+ expect(
+ result.schemaFields?.[0].children?.[0].dataTypeDisplay
+ ).toContain('diff-removed');
+ expect(
+ result.schemaFields?.[0].children?.[0].dataTypeDisplay
+ ).toContain('diff-added');
+ });
+
+ it('should not modify fields when dataTypeDisplay change does not match', () => {
+ const fieldName = 'testField';
+ const originalDataTypeDisplay = 'VARCHAR(255)';
+
+ // Create mock schema
+ const originalSchema = createMockMessageSchema([
+ createMockField(fieldName, originalDataTypeDisplay),
+ ]);
+
+ // Create change description for a different field
+ const changeDescription = createMockChangeDescription(
+ undefined,
+ undefined,
+ [
+ createMockFieldChange(
+ `schemaFields.differentField.dataTypeDisplay`,
+ 'OLD_VALUE',
+ 'NEW_VALUE'
+ ),
+ ]
+ );
+
+ // Execute the function
+ const result = getVersionedSchema(originalSchema, changeDescription);
+
+ // Verify the result - should remain unchanged
+ expect(result.schemaFields).toHaveLength(1);
+ expect(result.schemaFields?.[0].dataTypeDisplay).toBe(
+ originalDataTypeDisplay
+ );
+ });
+
+ it('should handle multiple dataTypeDisplay changes', () => {
+ const field1Name = 'field1';
+ const field2Name = 'field2';
+ const oldDataType1 = 'INT';
+ const newDataType1 = 'BIGINT';
+ const oldDataType2 = 'STRING';
+ const newDataType2 = 'TEXT';
+
+ // Create mock schema with multiple fields
+ const originalSchema = createMockMessageSchema([
+ createMockField(field1Name, oldDataType1),
+ createMockField(field2Name, oldDataType2),
+ ]);
+
+ // Create change description for multiple dataTypeDisplay updates
+ const changeDescription = createMockChangeDescription(
+ undefined,
+ undefined,
+ [
+ createMockFieldChange(
+ `schemaFields.${field1Name}.dataTypeDisplay`,
+ oldDataType1,
+ newDataType1
+ ),
+ createMockFieldChange(
+ `schemaFields.${field2Name}.dataTypeDisplay`,
+ oldDataType2,
+ newDataType2
+ ),
+ ]
+ );
+
+ // Execute the function
+ const result = getVersionedSchema(originalSchema, changeDescription);
+
+ // Verify both fields are updated
+ expect(result.schemaFields).toHaveLength(2);
+ expect(result.schemaFields?.[0].dataTypeDisplay).toContain(
+ 'diff-removed'
+ );
+ expect(result.schemaFields?.[0].dataTypeDisplay).toContain(
+ 'diff-added'
+ );
+ expect(result.schemaFields?.[1].dataTypeDisplay).toContain(
+ 'diff-removed'
+ );
+ expect(result.schemaFields?.[1].dataTypeDisplay).toContain(
+ 'diff-added'
+ );
+ });
+
+ it('should preserve other schema properties when updating dataTypeDisplay', () => {
+ const fieldName = 'testField';
+ const originalSchema = createMockMessageSchema([
+ createMockField(fieldName, 'VARCHAR(255)'),
+ ]);
+
+ // Add additional schema properties
+ const schemaWithExtraProps = {
+ ...originalSchema,
+ schemaType: SchemaType.JSON,
+ schemaText: 'original schema text',
+ };
+
+ const changeDescription = createMockChangeDescription(
+ undefined,
+ undefined,
+ [
+ createMockFieldChange(
+ `schemaFields.${fieldName}.dataTypeDisplay`,
+ 'VARCHAR(255)',
+ 'TEXT'
+ ),
+ ]
+ );
+
+ // Execute the function
+ const result = getVersionedSchema(
+ schemaWithExtraProps,
+ changeDescription
+ );
+
+ // Verify schema properties are preserved
+ expect(result.schemaType).toBe(SchemaType.JSON);
+ expect(result.schemaText).toBe('original schema text');
+ expect(result.schemaFields?.[0].dataTypeDisplay).toContain(
+ 'diff-removed'
+ );
+ expect(result.schemaFields?.[0].dataTypeDisplay).toContain(
+ 'diff-added'
+ );
+ });
+ });
+
+ describe('Edge cases', () => {
+ it('should handle empty schema fields', () => {
+ const originalSchema = createMockMessageSchema([]);
+ const changeDescription = createMockChangeDescription(
+ undefined,
+ undefined,
+ [
+ createMockFieldChange(
+ 'schemaFields.nonExistentField.dataTypeDisplay',
+ 'OLD',
+ 'NEW'
+ ),
+ ]
+ );
+
+ const result = getVersionedSchema(originalSchema, changeDescription);
+
+ expect(result.schemaFields).toHaveLength(0);
+ });
+
+ it('should handle missing changeDescription fields', () => {
+ const originalSchema = createMockMessageSchema([
+ createMockField('testField', 'VARCHAR(255)'),
+ ]);
+ const changeDescription = createMockChangeDescription();
+
+ const result = getVersionedSchema(originalSchema, changeDescription);
+
+ expect(result.schemaFields).toHaveLength(1);
+ expect(result.schemaFields?.[0].dataTypeDisplay).toBe('VARCHAR(255)');
+ });
+ });
+ });
+});
diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/SchemaVersionUtils.ts b/openmetadata-ui/src/main/resources/ui/src/utils/SchemaVersionUtils.ts
index a8d47e59bb1..d8308fa26cc 100644
--- a/openmetadata-ui/src/main/resources/ui/src/utils/SchemaVersionUtils.ts
+++ b/openmetadata-ui/src/main/resources/ui/src/utils/SchemaVersionUtils.ts
@@ -30,6 +30,7 @@ import {
getDiffByFieldName,
getEntityDescriptionDiff,
getEntityTagDiff,
+ getStringEntityDiff,
getTextDiff,
isEndsWithField,
} from './EntityVersionUtils';
@@ -83,6 +84,7 @@ export function createAddedSchemasDiff(
const newField: Array = JSON.parse(
schemaFieldsDiff.added?.newValue ?? '[]'
);
+
newField.forEach((field) => {
const formatSchemaFieldsData = (arr: Array, updateAll?: boolean) => {
arr?.forEach((i) => {
@@ -217,6 +219,32 @@ export const getVersionedSchema = (
schemaFields
);
+ clonedMessageSchema = {
+ ...clonedMessageSchema,
+ schemaFields: formattedSchema,
+ };
+ } else if (isEndsWithField(EntityField.DISPLAYNAME, changedEntityName)) {
+ const formattedSchema = getStringEntityDiff(
+ schemaFieldDiff,
+ EntityField.DISPLAYNAME,
+ changedSchemaFieldName,
+ schemaFields
+ );
+
+ clonedMessageSchema = {
+ ...clonedMessageSchema,
+ schemaFields: formattedSchema,
+ };
+ } else if (
+ isEndsWithField(EntityField.DATA_TYPE_DISPLAY, changedEntityName)
+ ) {
+ const formattedSchema = getStringEntityDiff(
+ schemaFieldDiff,
+ EntityField.DATA_TYPE_DISPLAY,
+ changedSchemaFieldName,
+ schemaFields
+ );
+
clonedMessageSchema = {
...clonedMessageSchema,
schemaFields: formattedSchema,