mirror of
https://github.com/open-metadata/OpenMetadata.git
synced 2025-09-25 17:04:54 +00:00
MINOR: filter out glossary term option of same term in the related term (#17213)
* filter out glossary term option of same term in the related term * fix sonar
This commit is contained in:
parent
e93bf7024f
commit
464d6cab72
@ -269,6 +269,7 @@ const RelatedTerms = ({
|
|||||||
defaultValue={selectedOption.map(
|
defaultValue={selectedOption.map(
|
||||||
(item) => item.fullyQualifiedName ?? ''
|
(item) => item.fullyQualifiedName ?? ''
|
||||||
)}
|
)}
|
||||||
|
filterOptions={[glossaryTerm?.fullyQualifiedName ?? '']}
|
||||||
placeholder={t('label.add-entity', {
|
placeholder={t('label.add-entity', {
|
||||||
entity: t('label.related-term-plural'),
|
entity: t('label.related-term-plural'),
|
||||||
})}
|
})}
|
||||||
|
@ -28,6 +28,7 @@ const TagSelectForm = ({
|
|||||||
onCancel,
|
onCancel,
|
||||||
tagData,
|
tagData,
|
||||||
tagType,
|
tagType,
|
||||||
|
filterOptions,
|
||||||
}: TagsSelectFormProps) => {
|
}: TagsSelectFormProps) => {
|
||||||
const [form] = useForm();
|
const [form] = useForm();
|
||||||
const [isSubmitLoading, setIsSubmitLoading] = useState(false);
|
const [isSubmitLoading, setIsSubmitLoading] = useState(false);
|
||||||
@ -52,6 +53,7 @@ const TagSelectForm = ({
|
|||||||
<AsyncSelectList
|
<AsyncSelectList
|
||||||
open
|
open
|
||||||
fetchOptions={fetchApi}
|
fetchOptions={fetchApi}
|
||||||
|
filterOptions={filterOptions}
|
||||||
initialOptions={tagData}
|
initialOptions={tagData}
|
||||||
isSubmitLoading={isSubmitLoading}
|
isSubmitLoading={isSubmitLoading}
|
||||||
mode="multiple"
|
mode="multiple"
|
||||||
@ -62,6 +64,7 @@ const TagSelectForm = ({
|
|||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<TreeAsyncSelectList
|
<TreeAsyncSelectList
|
||||||
|
filterOptions={filterOptions}
|
||||||
initialOptions={tagData}
|
initialOptions={tagData}
|
||||||
isSubmitLoading={isSubmitLoading}
|
isSubmitLoading={isSubmitLoading}
|
||||||
optionClassName="tag-select-box"
|
optionClassName="tag-select-box"
|
||||||
|
@ -20,6 +20,7 @@ export type TagsSelectFormProps = {
|
|||||||
placeholder: string;
|
placeholder: string;
|
||||||
defaultValue: string[];
|
defaultValue: string[];
|
||||||
tagData?: SelectOption[];
|
tagData?: SelectOption[];
|
||||||
|
filterOptions?: string[]; // array of fqn
|
||||||
onChange?: (value: string[]) => void;
|
onChange?: (value: string[]) => void;
|
||||||
onSubmit: (option: DefaultOptionType | DefaultOptionType[]) => Promise<void>;
|
onSubmit: (option: DefaultOptionType | DefaultOptionType[]) => Promise<void>;
|
||||||
onCancel: () => void;
|
onCancel: () => void;
|
||||||
|
@ -32,6 +32,7 @@ jest.mock('../../../utils/GlossaryUtils', () => ({
|
|||||||
]),
|
]),
|
||||||
convertGlossaryTermsToTreeOptions: jest.fn(),
|
convertGlossaryTermsToTreeOptions: jest.fn(),
|
||||||
findGlossaryTermByFqn: jest.fn(),
|
findGlossaryTermByFqn: jest.fn(),
|
||||||
|
filterTreeNodeOptions: jest.fn().mockReturnValue([]),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
describe('TreeAsyncSelectList', () => {
|
describe('TreeAsyncSelectList', () => {
|
||||||
|
@ -41,6 +41,7 @@ import {
|
|||||||
import { getEntityName } from '../../../utils/EntityUtils';
|
import { getEntityName } from '../../../utils/EntityUtils';
|
||||||
import {
|
import {
|
||||||
convertGlossaryTermsToTreeOptions,
|
convertGlossaryTermsToTreeOptions,
|
||||||
|
filterTreeNodeOptions,
|
||||||
findGlossaryTermByFqn,
|
findGlossaryTermByFqn,
|
||||||
} from '../../../utils/GlossaryUtils';
|
} from '../../../utils/GlossaryUtils';
|
||||||
import {
|
import {
|
||||||
@ -63,6 +64,7 @@ const TreeAsyncSelectList: FC<Omit<AsyncSelectListProps, 'fetchOptions'>> = ({
|
|||||||
initialOptions,
|
initialOptions,
|
||||||
tagType,
|
tagType,
|
||||||
isSubmitLoading,
|
isSubmitLoading,
|
||||||
|
filterOptions = [],
|
||||||
onCancel,
|
onCancel,
|
||||||
...props
|
...props
|
||||||
}) => {
|
}) => {
|
||||||
@ -82,7 +84,9 @@ const TreeAsyncSelectList: FC<Omit<AsyncSelectListProps, 'fetchOptions'>> = ({
|
|||||||
const { data } = await getGlossariesList({
|
const { data } = await getGlossariesList({
|
||||||
limit: PAGE_SIZE_LARGE,
|
limit: PAGE_SIZE_LARGE,
|
||||||
});
|
});
|
||||||
setGlossaries((prev) => [...prev, ...data]);
|
setGlossaries((prev) =>
|
||||||
|
filterTreeNodeOptions([...prev, ...data], filterOptions)
|
||||||
|
);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
showErrorToast(error as AxiosError);
|
showErrorToast(error as AxiosError);
|
||||||
} finally {
|
} finally {
|
||||||
@ -229,14 +233,17 @@ const TreeAsyncSelectList: FC<Omit<AsyncSelectListProps, 'fetchOptions'>> = ({
|
|||||||
const activeGlossary = results[0];
|
const activeGlossary = results[0];
|
||||||
|
|
||||||
setGlossaries((prev) =>
|
setGlossaries((prev) =>
|
||||||
prev.map((glossary) => ({
|
filterTreeNodeOptions(
|
||||||
...glossary,
|
prev.map((glossary) => ({
|
||||||
children: get(
|
...glossary,
|
||||||
glossary.id === activeGlossary?.id ? activeGlossary : glossary,
|
children: get(
|
||||||
'children',
|
glossary.id === activeGlossary?.id ? activeGlossary : glossary,
|
||||||
[]
|
'children',
|
||||||
),
|
[]
|
||||||
}))
|
),
|
||||||
|
})),
|
||||||
|
filterOptions
|
||||||
|
)
|
||||||
);
|
);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
showErrorToast(error as AxiosError);
|
showErrorToast(error as AxiosError);
|
||||||
@ -248,7 +255,7 @@ const TreeAsyncSelectList: FC<Omit<AsyncSelectListProps, 'fetchOptions'>> = ({
|
|||||||
const encodedValue = getEncodedFqn(escapeESReservedCharacters(value));
|
const encodedValue = getEncodedFqn(escapeESReservedCharacters(value));
|
||||||
const results: Glossary[] = await searchGlossaryTerms(encodedValue);
|
const results: Glossary[] = await searchGlossaryTerms(encodedValue);
|
||||||
|
|
||||||
setSearchOptions(results);
|
setSearchOptions(filterTreeNodeOptions(results, filterOptions));
|
||||||
setExpandedRowKeys(
|
setExpandedRowKeys(
|
||||||
results.map((result) => result.fullyQualifiedName as string)
|
results.map((result) => result.fullyQualifiedName as string)
|
||||||
);
|
);
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
*/
|
*/
|
||||||
import { ModifiedGlossaryTerm } from '../components/Glossary/GlossaryTermTab/GlossaryTermTab.interface';
|
import { ModifiedGlossaryTerm } from '../components/Glossary/GlossaryTermTab/GlossaryTermTab.interface';
|
||||||
import { EntityType } from '../enums/entity.enum';
|
import { EntityType } from '../enums/entity.enum';
|
||||||
|
import { Glossary } from '../generated/entity/data/glossary';
|
||||||
import {
|
import {
|
||||||
MOCKED_GLOSSARY_TERMS,
|
MOCKED_GLOSSARY_TERMS,
|
||||||
MOCKED_GLOSSARY_TERMS_1,
|
MOCKED_GLOSSARY_TERMS_1,
|
||||||
@ -20,6 +21,7 @@ import {
|
|||||||
} from '../mocks/Glossary.mock';
|
} from '../mocks/Glossary.mock';
|
||||||
import {
|
import {
|
||||||
buildTree,
|
buildTree,
|
||||||
|
filterTreeNodeOptions,
|
||||||
findExpandableKeys,
|
findExpandableKeys,
|
||||||
findExpandableKeysForArray,
|
findExpandableKeysForArray,
|
||||||
getQueryFilterToExcludeTerm,
|
getQueryFilterToExcludeTerm,
|
||||||
@ -159,4 +161,61 @@ describe('Glossary Utils', () => {
|
|||||||
|
|
||||||
expect(expandableKeys).toEqual(['example1', 'example2']);
|
expect(expandableKeys).toEqual(['example1', 'example2']);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('Should return same Glossary when no filterOption is provided', () => {
|
||||||
|
const glossary = [
|
||||||
|
{
|
||||||
|
fullyQualifiedName: 'example1',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
fullyQualifiedName: 'child1',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fullyQualifiedName: 'example2',
|
||||||
|
childrenCount: 2,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fullyQualifiedName: 'example3',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const filteredOptions = filterTreeNodeOptions(glossary as Glossary[], []);
|
||||||
|
|
||||||
|
expect(filteredOptions).toEqual(glossary);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Should return filtered Glossary when filterOption is provided', () => {
|
||||||
|
const glossary = [
|
||||||
|
{
|
||||||
|
fullyQualifiedName: 'example1',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
fullyQualifiedName: 'child1',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fullyQualifiedName: 'example3',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const expected_glossary = [
|
||||||
|
{
|
||||||
|
fullyQualifiedName: 'example1',
|
||||||
|
children: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fullyQualifiedName: 'example3',
|
||||||
|
children: [],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const filteredOptions = filterTreeNodeOptions(glossary as Glossary[], [
|
||||||
|
'child1',
|
||||||
|
]);
|
||||||
|
|
||||||
|
expect(filteredOptions).toEqual(expected_glossary);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -13,7 +13,7 @@
|
|||||||
|
|
||||||
import { Typography } from 'antd';
|
import { Typography } from 'antd';
|
||||||
import { DefaultOptionType } from 'antd/lib/select';
|
import { DefaultOptionType } from 'antd/lib/select';
|
||||||
import { isEmpty } from 'lodash';
|
import { isEmpty, isUndefined } from 'lodash';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { StatusType } from '../components/common/StatusBadge/StatusBadge.interface';
|
import { StatusType } from '../components/common/StatusBadge/StatusBadge.interface';
|
||||||
import { ModifiedGlossaryTerm } from '../components/Glossary/GlossaryTermTab/GlossaryTermTab.interface';
|
import { ModifiedGlossaryTerm } from '../components/Glossary/GlossaryTermTab/GlossaryTermTab.interface';
|
||||||
@ -265,3 +265,47 @@ export const findExpandableKeysForArray = (
|
|||||||
|
|
||||||
return expandableKeys;
|
return expandableKeys;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filter out the tree node options based on the filter options.
|
||||||
|
*
|
||||||
|
* @param options - An array of Glossary objects.
|
||||||
|
* @param filterOptions - An array of FQN string to filter.
|
||||||
|
* @returns An array of filtered Glossary
|
||||||
|
*/
|
||||||
|
export const filterTreeNodeOptions = (
|
||||||
|
options: Glossary[],
|
||||||
|
filterOptions: string[]
|
||||||
|
): Glossary[] => {
|
||||||
|
if (isEmpty(filterOptions)) {
|
||||||
|
return options;
|
||||||
|
}
|
||||||
|
|
||||||
|
const filterNodes = (
|
||||||
|
nodes: ModifiedGlossaryTerm[]
|
||||||
|
): ModifiedGlossaryTerm[] => {
|
||||||
|
return nodes.reduce(
|
||||||
|
(acc: ModifiedGlossaryTerm[], node: ModifiedGlossaryTerm) => {
|
||||||
|
const isMatching = filterOptions.includes(
|
||||||
|
node.fullyQualifiedName ?? ''
|
||||||
|
);
|
||||||
|
|
||||||
|
const filteredChildren = !isUndefined(node.children)
|
||||||
|
? filterNodes(node.children as unknown as ModifiedGlossaryTerm[])
|
||||||
|
: [];
|
||||||
|
|
||||||
|
if (!isMatching) {
|
||||||
|
acc.push({
|
||||||
|
...node,
|
||||||
|
children: filteredChildren as GlossaryTerm[],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return acc;
|
||||||
|
},
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
return filterNodes(options as ModifiedGlossaryTerm[]);
|
||||||
|
};
|
||||||
|
Loading…
x
Reference in New Issue
Block a user