mirror of
https://github.com/datahub-project/datahub.git
synced 2025-09-28 10:35:20 +00:00
fix(ui) Allow empty strings for sibling values (#14502)
This commit is contained in:
parent
6361eb1c59
commit
904d4e85eb
@ -1,4 +1,8 @@
|
|||||||
import { combineEntityDataWithSiblings, shouldEntityBeTreatedAsPrimary } from '@app/entity/shared/siblingUtils';
|
import {
|
||||||
|
cleanHelper,
|
||||||
|
combineEntityDataWithSiblings,
|
||||||
|
shouldEntityBeTreatedAsPrimary,
|
||||||
|
} from '@app/entity/shared/siblingUtils';
|
||||||
import { dataset3WithLineage, dataset3WithSchema, dataset4WithLineage } from '@src/Mocks';
|
import { dataset3WithLineage, dataset3WithSchema, dataset4WithLineage } from '@src/Mocks';
|
||||||
|
|
||||||
import { EntityType, SchemaFieldDataType } from '@types';
|
import { EntityType, SchemaFieldDataType } from '@types';
|
||||||
@ -53,7 +57,7 @@ const datasetPrimary = {
|
|||||||
description: 'primary description',
|
description: 'primary description',
|
||||||
},
|
},
|
||||||
editableProperties: {
|
editableProperties: {
|
||||||
description: '',
|
description: null,
|
||||||
},
|
},
|
||||||
globalTags: {
|
globalTags: {
|
||||||
tags: [
|
tags: [
|
||||||
@ -256,4 +260,226 @@ describe('siblingUtils', () => {
|
|||||||
expect(shouldEntityBeTreatedAsPrimary(datasetUnprimaryWithNoPrimarySiblings)).toBeTruthy();
|
expect(shouldEntityBeTreatedAsPrimary(datasetUnprimaryWithNoPrimarySiblings)).toBeTruthy();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('cleanHelper', () => {
|
||||||
|
let visited: Set<any>;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
visited = new Set();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return the object immediately if it has already been visited', () => {
|
||||||
|
const obj = { test: 'value' };
|
||||||
|
visited.add(obj);
|
||||||
|
const result = cleanHelper(obj, visited);
|
||||||
|
expect(result).toBe(obj);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should remove null properties from objects', () => {
|
||||||
|
const obj = {
|
||||||
|
validProperty: 'test',
|
||||||
|
nullProperty: null,
|
||||||
|
anotherValid: 42,
|
||||||
|
};
|
||||||
|
const result = cleanHelper(obj, visited);
|
||||||
|
expect(result).toEqual({
|
||||||
|
validProperty: 'test',
|
||||||
|
anotherValid: 42,
|
||||||
|
});
|
||||||
|
expect(result).not.toHaveProperty('nullProperty');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should remove undefined properties from objects', () => {
|
||||||
|
const obj = {
|
||||||
|
validProperty: 'test',
|
||||||
|
undefinedProperty: undefined,
|
||||||
|
anotherValid: 42,
|
||||||
|
};
|
||||||
|
const result = cleanHelper(obj, visited);
|
||||||
|
expect(result).toEqual({
|
||||||
|
validProperty: 'test',
|
||||||
|
anotherValid: 42,
|
||||||
|
});
|
||||||
|
expect(result).not.toHaveProperty('undefinedProperty');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should remove empty object properties', () => {
|
||||||
|
const obj = {
|
||||||
|
validProperty: 'test',
|
||||||
|
emptyObject: {},
|
||||||
|
anotherValid: 42,
|
||||||
|
};
|
||||||
|
const result = cleanHelper(obj, visited);
|
||||||
|
expect(result).toEqual({
|
||||||
|
validProperty: 'test',
|
||||||
|
anotherValid: 42,
|
||||||
|
});
|
||||||
|
expect(result).not.toHaveProperty('emptyObject');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should recursively clean nested objects', () => {
|
||||||
|
const obj = {
|
||||||
|
level1: {
|
||||||
|
validProp: 'test',
|
||||||
|
nullProp: null,
|
||||||
|
level2: {
|
||||||
|
nestedValid: 'nested',
|
||||||
|
nestedNull: null,
|
||||||
|
emptyNested: {},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
topLevel: 'value',
|
||||||
|
};
|
||||||
|
const result = cleanHelper(obj, visited);
|
||||||
|
expect(result).toEqual({
|
||||||
|
level1: {
|
||||||
|
validProp: 'test',
|
||||||
|
level2: {
|
||||||
|
nestedValid: 'nested',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
topLevel: 'value',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not modify arrays when they contain null, undefined, or empty objects', () => {
|
||||||
|
const obj = {
|
||||||
|
arrayProp: ['valid', null, undefined, {}, { validNested: 'test' }],
|
||||||
|
regularProp: 'test',
|
||||||
|
};
|
||||||
|
const result = cleanHelper(obj, visited);
|
||||||
|
expect(result.arrayProp).toHaveLength(5);
|
||||||
|
expect(result.arrayProp[0]).toBe('valid');
|
||||||
|
expect(result.arrayProp[1]).toBe(null);
|
||||||
|
expect(result.arrayProp[2]).toBe(undefined);
|
||||||
|
expect(result.arrayProp[3]).toEqual({});
|
||||||
|
expect(result.arrayProp[4]).toEqual({ validNested: 'test' });
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should still clean objects within arrays recursively', () => {
|
||||||
|
const obj = {
|
||||||
|
arrayProp: [
|
||||||
|
{
|
||||||
|
validProp: 'test',
|
||||||
|
nullProp: null,
|
||||||
|
nestedObj: {
|
||||||
|
valid: 'nested',
|
||||||
|
invalid: null,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
const result = cleanHelper(obj, visited);
|
||||||
|
expect(result.arrayProp[0]).toEqual({
|
||||||
|
validProp: 'test',
|
||||||
|
nestedObj: {
|
||||||
|
valid: 'nested',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
expect(result.arrayProp[0]).not.toHaveProperty('nullProp');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle circular references without infinite recursion', () => {
|
||||||
|
const obj: any = {
|
||||||
|
validProp: 'test',
|
||||||
|
nullProp: null,
|
||||||
|
};
|
||||||
|
obj.circular = obj;
|
||||||
|
|
||||||
|
const result = cleanHelper(obj, visited);
|
||||||
|
expect(result.validProp).toBe('test');
|
||||||
|
expect(result).not.toHaveProperty('nullProp');
|
||||||
|
expect(result.circular).toBe(result);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle objects with non-configurable properties gracefully', () => {
|
||||||
|
const obj = {
|
||||||
|
validProp: 'test',
|
||||||
|
nullProp: null,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Make a property non-configurable
|
||||||
|
Object.defineProperty(obj, 'nonConfigurable', {
|
||||||
|
value: null,
|
||||||
|
configurable: false,
|
||||||
|
enumerable: true,
|
||||||
|
writable: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = cleanHelper(obj, visited);
|
||||||
|
expect(result.validProp).toBe('test');
|
||||||
|
expect(result).not.toHaveProperty('nullProp');
|
||||||
|
// Non-configurable null property should remain
|
||||||
|
expect(result).toHaveProperty('nonConfigurable');
|
||||||
|
expect(result.nonConfigurable).toBe(null);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should preserve valid nested structures', () => {
|
||||||
|
const obj = {
|
||||||
|
user: {
|
||||||
|
name: 'John',
|
||||||
|
age: 30,
|
||||||
|
preferences: {
|
||||||
|
theme: 'dark',
|
||||||
|
notifications: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
metadata: {
|
||||||
|
created: '2023-01-01',
|
||||||
|
tags: ['important', 'user'],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = cleanHelper(obj, visited);
|
||||||
|
expect(result).toEqual(obj);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle complex mixed structures', () => {
|
||||||
|
const obj = {
|
||||||
|
validString: 'test',
|
||||||
|
validNumber: 42,
|
||||||
|
validBoolean: true,
|
||||||
|
validArray: [1, 2, 3],
|
||||||
|
nullValue: null,
|
||||||
|
undefinedValue: undefined,
|
||||||
|
emptyObject: {},
|
||||||
|
nestedValid: {
|
||||||
|
innerValid: 'nested',
|
||||||
|
innerNull: null,
|
||||||
|
innerArray: ['a', null, 'b'],
|
||||||
|
innerEmpty: {},
|
||||||
|
},
|
||||||
|
mixedArray: [{ valid: 'item', invalid: null }, null, 'string', 42],
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = cleanHelper(obj, visited);
|
||||||
|
expect(result).toEqual({
|
||||||
|
validString: 'test',
|
||||||
|
validNumber: 42,
|
||||||
|
validBoolean: true,
|
||||||
|
validArray: [1, 2, 3],
|
||||||
|
nestedValid: {
|
||||||
|
innerValid: 'nested',
|
||||||
|
innerArray: ['a', null, 'b'],
|
||||||
|
},
|
||||||
|
mixedArray: [{ valid: 'item' }, null, 'string', 42],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle edge case of empty input object', () => {
|
||||||
|
const obj = {};
|
||||||
|
const result = cleanHelper(obj, visited);
|
||||||
|
expect(result).toEqual({});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle edge case where object becomes empty after cleaning', () => {
|
||||||
|
const obj = {
|
||||||
|
nullProp: null,
|
||||||
|
undefinedProp: undefined,
|
||||||
|
emptyProp: {},
|
||||||
|
};
|
||||||
|
const result = cleanHelper(obj, visited);
|
||||||
|
expect(result).toEqual({});
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -29,7 +29,7 @@ export function stripSiblingsFromEntity(entity: any) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function cleanHelper(obj, visited) {
|
export function cleanHelper(obj, visited) {
|
||||||
if (visited.has(obj)) return obj;
|
if (visited.has(obj)) return obj;
|
||||||
visited.add(obj);
|
visited.add(obj);
|
||||||
|
|
||||||
@ -38,7 +38,7 @@ function cleanHelper(obj, visited) {
|
|||||||
if (v && typeof v === 'object') {
|
if (v && typeof v === 'object') {
|
||||||
cleanHelper(v, visited);
|
cleanHelper(v, visited);
|
||||||
}
|
}
|
||||||
if ((v && typeof v === 'object' && !Object.keys(v).length) || v === null || v === undefined || v === '') {
|
if ((v && typeof v === 'object' && !Object.keys(v).length) || v === null || v === undefined) {
|
||||||
if (Array.isArray(object)) {
|
if (Array.isArray(object)) {
|
||||||
// do nothing
|
// do nothing
|
||||||
} else if (Object.getOwnPropertyDescriptor(object, k)?.configurable) {
|
} else if (Object.getOwnPropertyDescriptor(object, k)?.configurable) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user