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:
Ashish Gupta 2024-07-28 17:38:49 +05:30 committed by GitHub
parent e93bf7024f
commit 464d6cab72
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 127 additions and 11 deletions

View File

@ -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'),
})} })}

View File

@ -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"

View File

@ -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;

View File

@ -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', () => {

View File

@ -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)
); );

View File

@ -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);
});
}); });

View File

@ -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[]);
};