mirror of
https://github.com/open-metadata/OpenMetadata.git
synced 2025-09-25 08:50:18 +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(
|
||||
(item) => item.fullyQualifiedName ?? ''
|
||||
)}
|
||||
filterOptions={[glossaryTerm?.fullyQualifiedName ?? '']}
|
||||
placeholder={t('label.add-entity', {
|
||||
entity: t('label.related-term-plural'),
|
||||
})}
|
||||
|
@ -28,6 +28,7 @@ const TagSelectForm = ({
|
||||
onCancel,
|
||||
tagData,
|
||||
tagType,
|
||||
filterOptions,
|
||||
}: TagsSelectFormProps) => {
|
||||
const [form] = useForm();
|
||||
const [isSubmitLoading, setIsSubmitLoading] = useState(false);
|
||||
@ -52,6 +53,7 @@ const TagSelectForm = ({
|
||||
<AsyncSelectList
|
||||
open
|
||||
fetchOptions={fetchApi}
|
||||
filterOptions={filterOptions}
|
||||
initialOptions={tagData}
|
||||
isSubmitLoading={isSubmitLoading}
|
||||
mode="multiple"
|
||||
@ -62,6 +64,7 @@ const TagSelectForm = ({
|
||||
/>
|
||||
) : (
|
||||
<TreeAsyncSelectList
|
||||
filterOptions={filterOptions}
|
||||
initialOptions={tagData}
|
||||
isSubmitLoading={isSubmitLoading}
|
||||
optionClassName="tag-select-box"
|
||||
|
@ -20,6 +20,7 @@ export type TagsSelectFormProps = {
|
||||
placeholder: string;
|
||||
defaultValue: string[];
|
||||
tagData?: SelectOption[];
|
||||
filterOptions?: string[]; // array of fqn
|
||||
onChange?: (value: string[]) => void;
|
||||
onSubmit: (option: DefaultOptionType | DefaultOptionType[]) => Promise<void>;
|
||||
onCancel: () => void;
|
||||
|
@ -32,6 +32,7 @@ jest.mock('../../../utils/GlossaryUtils', () => ({
|
||||
]),
|
||||
convertGlossaryTermsToTreeOptions: jest.fn(),
|
||||
findGlossaryTermByFqn: jest.fn(),
|
||||
filterTreeNodeOptions: jest.fn().mockReturnValue([]),
|
||||
}));
|
||||
|
||||
describe('TreeAsyncSelectList', () => {
|
||||
|
@ -41,6 +41,7 @@ import {
|
||||
import { getEntityName } from '../../../utils/EntityUtils';
|
||||
import {
|
||||
convertGlossaryTermsToTreeOptions,
|
||||
filterTreeNodeOptions,
|
||||
findGlossaryTermByFqn,
|
||||
} from '../../../utils/GlossaryUtils';
|
||||
import {
|
||||
@ -63,6 +64,7 @@ const TreeAsyncSelectList: FC<Omit<AsyncSelectListProps, 'fetchOptions'>> = ({
|
||||
initialOptions,
|
||||
tagType,
|
||||
isSubmitLoading,
|
||||
filterOptions = [],
|
||||
onCancel,
|
||||
...props
|
||||
}) => {
|
||||
@ -82,7 +84,9 @@ const TreeAsyncSelectList: FC<Omit<AsyncSelectListProps, 'fetchOptions'>> = ({
|
||||
const { data } = await getGlossariesList({
|
||||
limit: PAGE_SIZE_LARGE,
|
||||
});
|
||||
setGlossaries((prev) => [...prev, ...data]);
|
||||
setGlossaries((prev) =>
|
||||
filterTreeNodeOptions([...prev, ...data], filterOptions)
|
||||
);
|
||||
} catch (error) {
|
||||
showErrorToast(error as AxiosError);
|
||||
} finally {
|
||||
@ -229,14 +233,17 @@ const TreeAsyncSelectList: FC<Omit<AsyncSelectListProps, 'fetchOptions'>> = ({
|
||||
const activeGlossary = results[0];
|
||||
|
||||
setGlossaries((prev) =>
|
||||
prev.map((glossary) => ({
|
||||
...glossary,
|
||||
children: get(
|
||||
glossary.id === activeGlossary?.id ? activeGlossary : glossary,
|
||||
'children',
|
||||
[]
|
||||
),
|
||||
}))
|
||||
filterTreeNodeOptions(
|
||||
prev.map((glossary) => ({
|
||||
...glossary,
|
||||
children: get(
|
||||
glossary.id === activeGlossary?.id ? activeGlossary : glossary,
|
||||
'children',
|
||||
[]
|
||||
),
|
||||
})),
|
||||
filterOptions
|
||||
)
|
||||
);
|
||||
} catch (error) {
|
||||
showErrorToast(error as AxiosError);
|
||||
@ -248,7 +255,7 @@ const TreeAsyncSelectList: FC<Omit<AsyncSelectListProps, 'fetchOptions'>> = ({
|
||||
const encodedValue = getEncodedFqn(escapeESReservedCharacters(value));
|
||||
const results: Glossary[] = await searchGlossaryTerms(encodedValue);
|
||||
|
||||
setSearchOptions(results);
|
||||
setSearchOptions(filterTreeNodeOptions(results, filterOptions));
|
||||
setExpandedRowKeys(
|
||||
results.map((result) => result.fullyQualifiedName as string)
|
||||
);
|
||||
|
@ -12,6 +12,7 @@
|
||||
*/
|
||||
import { ModifiedGlossaryTerm } from '../components/Glossary/GlossaryTermTab/GlossaryTermTab.interface';
|
||||
import { EntityType } from '../enums/entity.enum';
|
||||
import { Glossary } from '../generated/entity/data/glossary';
|
||||
import {
|
||||
MOCKED_GLOSSARY_TERMS,
|
||||
MOCKED_GLOSSARY_TERMS_1,
|
||||
@ -20,6 +21,7 @@ import {
|
||||
} from '../mocks/Glossary.mock';
|
||||
import {
|
||||
buildTree,
|
||||
filterTreeNodeOptions,
|
||||
findExpandableKeys,
|
||||
findExpandableKeysForArray,
|
||||
getQueryFilterToExcludeTerm,
|
||||
@ -159,4 +161,61 @@ describe('Glossary Utils', () => {
|
||||
|
||||
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 { DefaultOptionType } from 'antd/lib/select';
|
||||
import { isEmpty } from 'lodash';
|
||||
import { isEmpty, isUndefined } from 'lodash';
|
||||
import React from 'react';
|
||||
import { StatusType } from '../components/common/StatusBadge/StatusBadge.interface';
|
||||
import { ModifiedGlossaryTerm } from '../components/Glossary/GlossaryTermTab/GlossaryTermTab.interface';
|
||||
@ -265,3 +265,47 @@ export const findExpandableKeysForArray = (
|
||||
|
||||
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