mirror of
https://github.com/open-metadata/OpenMetadata.git
synced 2025-09-22 07:19:59 +00:00
FIX: Api version endpoint page breaking due to STRING changeDescription updated (#22060)
* fix the api version endpoint page breaking due to STRING changeDescription updated * change the logic and use same function which displayName use * added unit test for schemaVersionUtils * change function name to be generic * added support for dataDisplayType diff supported for other supporting entity as well * adde playwright test
This commit is contained in:
parent
cec7dd31ad
commit
307e883b10
@ -22,7 +22,11 @@ import {
|
|||||||
redirectToHomePage,
|
redirectToHomePage,
|
||||||
toastNotification,
|
toastNotification,
|
||||||
} from '../../utils/common';
|
} from '../../utils/common';
|
||||||
import { addMultiOwner, assignTier } from '../../utils/entity';
|
import {
|
||||||
|
addMultiOwner,
|
||||||
|
assignTier,
|
||||||
|
getEntityDataTypeDisplayPatch,
|
||||||
|
} from '../../utils/entity';
|
||||||
|
|
||||||
const entityCreationConfig: EntityDataClassCreationConfig = {
|
const entityCreationConfig: EntityDataClassCreationConfig = {
|
||||||
apiEndpoint: true,
|
apiEndpoint: true,
|
||||||
@ -78,6 +82,7 @@ test.describe('Entity Version pages', () => {
|
|||||||
const domain = EntityDataClass.domain1.responseData;
|
const domain = EntityDataClass.domain1.responseData;
|
||||||
|
|
||||||
for (const entity of entities) {
|
for (const entity of entities) {
|
||||||
|
const dataTypeDisplayPath = getEntityDataTypeDisplayPatch(entity);
|
||||||
await entity.patch({
|
await entity.patch({
|
||||||
apiContext,
|
apiContext,
|
||||||
patchData: [
|
patchData: [
|
||||||
@ -116,6 +121,15 @@ test.describe('Entity Version pages', () => {
|
|||||||
description: domain.description,
|
description: domain.description,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
...(dataTypeDisplayPath
|
||||||
|
? [
|
||||||
|
{
|
||||||
|
op: 'add' as const,
|
||||||
|
path: dataTypeDisplayPath,
|
||||||
|
value: 'OBJECT',
|
||||||
|
},
|
||||||
|
]
|
||||||
|
: []),
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -1728,3 +1728,22 @@ export const checkExploreSearchFilter = async (
|
|||||||
|
|
||||||
await entity?.visitEntityPage(page);
|
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;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
@ -90,6 +90,7 @@ export enum EntityField {
|
|||||||
EXPERTS = 'experts',
|
EXPERTS = 'experts',
|
||||||
FIELDS = 'fields',
|
FIELDS = 'fields',
|
||||||
PARAMETER_VALUES = 'parameterValues',
|
PARAMETER_VALUES = 'parameterValues',
|
||||||
|
DATA_TYPE_DISPLAY = 'dataTypeDisplay',
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ANNOUNCEMENT_BG = '#FFFDF8';
|
export const ANNOUNCEMENT_BG = '#FFFDF8';
|
||||||
|
@ -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');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@ -70,6 +70,8 @@ import { t } from './i18next/LocalUtil';
|
|||||||
import { getJSONFromString, isValidJSONString } from './StringsUtils';
|
import { getJSONFromString, isValidJSONString } from './StringsUtils';
|
||||||
import { getTagsWithoutTier, getTierTags } from './TableUtils';
|
import { getTagsWithoutTier, getTierTags } from './TableUtils';
|
||||||
|
|
||||||
|
type EntityColumn = TableColumn | ContainerColumn | Field;
|
||||||
|
|
||||||
export const getChangedEntityName = (diffObject?: EntityDiffProps) =>
|
export const getChangedEntityName = (diffObject?: EntityDiffProps) =>
|
||||||
diffObject?.added?.name ??
|
diffObject?.added?.name ??
|
||||||
diffObject?.deleted?.name ??
|
diffObject?.deleted?.name ??
|
||||||
@ -506,10 +508,9 @@ export function getEntityDescriptionDiff<A extends AssetsChildForVersionPages>(
|
|||||||
return entityList;
|
return entityList;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getEntityDisplayNameDiff<
|
export function getStringEntityDiff<A extends EntityColumn>(
|
||||||
A extends TableColumn | ContainerColumn | Field
|
|
||||||
>(
|
|
||||||
entityDiff: EntityDiffProps,
|
entityDiff: EntityDiffProps,
|
||||||
|
key: EntityField,
|
||||||
changedEntityName?: string,
|
changedEntityName?: string,
|
||||||
entityList: A[] = []
|
entityList: A[] = []
|
||||||
) {
|
) {
|
||||||
@ -519,11 +520,11 @@ export function getEntityDisplayNameDiff<
|
|||||||
const formatEntityData = (arr: Array<A>) => {
|
const formatEntityData = (arr: Array<A>) => {
|
||||||
arr?.forEach((i) => {
|
arr?.forEach((i) => {
|
||||||
if (isEqual(i.name, changedEntityName)) {
|
if (isEqual(i.name, changedEntityName)) {
|
||||||
i.displayName = getTextDiff(
|
i[key as keyof A] = getTextDiff(
|
||||||
oldDisplayName ?? '',
|
oldDisplayName ?? '',
|
||||||
newDisplayName ?? '',
|
newDisplayName ?? '',
|
||||||
i.displayName
|
i[key as keyof typeof i] as unknown as string
|
||||||
);
|
) as unknown as A[keyof A];
|
||||||
} else {
|
} else {
|
||||||
formatEntityData(i?.children as Array<A>);
|
formatEntityData(i?.children as Array<A>);
|
||||||
}
|
}
|
||||||
@ -535,9 +536,11 @@ export function getEntityDisplayNameDiff<
|
|||||||
return entityList;
|
return entityList;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getEntityTagDiff<
|
export function getEntityTagDiff<A extends EntityColumn>(
|
||||||
A extends TableColumn | ContainerColumn | Field
|
entityDiff: EntityDiffProps,
|
||||||
>(entityDiff: EntityDiffProps, changedEntityName?: string, entityList?: A[]) {
|
changedEntityName?: string,
|
||||||
|
entityList?: A[]
|
||||||
|
) {
|
||||||
const oldTags: TagLabel[] = JSON.parse(
|
const oldTags: TagLabel[] = JSON.parse(
|
||||||
getChangedEntityOldValue(entityDiff) ?? '[]'
|
getChangedEntityOldValue(entityDiff) ?? '[]'
|
||||||
);
|
);
|
||||||
@ -822,7 +825,23 @@ export function getColumnsDataWithVersionChanges<
|
|||||||
];
|
];
|
||||||
} else if (isEndsWithField(EntityField.DISPLAYNAME, changedEntityName)) {
|
} else if (isEndsWithField(EntityField.DISPLAYNAME, changedEntityName)) {
|
||||||
newColumnsList = [
|
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)) {
|
} else if (!isEndsWithField(EntityField.CONSTRAINT, changedEntityName)) {
|
||||||
const changedEntity = changedEntityName
|
const changedEntity = changedEntityName
|
||||||
|
@ -626,6 +626,7 @@ export const getFeedChangeFieldLabel = (fieldName?: EntityField) => {
|
|||||||
[EntityField.EXPERTS]: t('label.expert-plural'),
|
[EntityField.EXPERTS]: t('label.expert-plural'),
|
||||||
[EntityField.FIELDS]: t('label.field-plural'),
|
[EntityField.FIELDS]: t('label.field-plural'),
|
||||||
[EntityField.PARAMETER_VALUES]: t('label.parameter-plural'),
|
[EntityField.PARAMETER_VALUES]: t('label.parameter-plural'),
|
||||||
|
[EntityField.DATA_TYPE_DISPLAY]: t('label.data-type-display'),
|
||||||
};
|
};
|
||||||
|
|
||||||
return isUndefined(fieldName) ? '' : fieldNameLabelMapping[fieldName];
|
return isUndefined(fieldName) ? '' : fieldNameLabelMapping[fieldName];
|
||||||
|
@ -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<id:INT>';
|
||||||
|
const newDataTypeDisplay = 'STRUCT<id:BIGINT>';
|
||||||
|
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)');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@ -30,6 +30,7 @@ import {
|
|||||||
getDiffByFieldName,
|
getDiffByFieldName,
|
||||||
getEntityDescriptionDiff,
|
getEntityDescriptionDiff,
|
||||||
getEntityTagDiff,
|
getEntityTagDiff,
|
||||||
|
getStringEntityDiff,
|
||||||
getTextDiff,
|
getTextDiff,
|
||||||
isEndsWithField,
|
isEndsWithField,
|
||||||
} from './EntityVersionUtils';
|
} from './EntityVersionUtils';
|
||||||
@ -83,6 +84,7 @@ export function createAddedSchemasDiff(
|
|||||||
const newField: Array<Field> = JSON.parse(
|
const newField: Array<Field> = JSON.parse(
|
||||||
schemaFieldsDiff.added?.newValue ?? '[]'
|
schemaFieldsDiff.added?.newValue ?? '[]'
|
||||||
);
|
);
|
||||||
|
|
||||||
newField.forEach((field) => {
|
newField.forEach((field) => {
|
||||||
const formatSchemaFieldsData = (arr: Array<Field>, updateAll?: boolean) => {
|
const formatSchemaFieldsData = (arr: Array<Field>, updateAll?: boolean) => {
|
||||||
arr?.forEach((i) => {
|
arr?.forEach((i) => {
|
||||||
@ -217,6 +219,32 @@ export const getVersionedSchema = (
|
|||||||
schemaFields
|
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 = {
|
||||||
...clonedMessageSchema,
|
...clonedMessageSchema,
|
||||||
schemaFields: formattedSchema,
|
schemaFields: formattedSchema,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user