From 464d6cab721f64fbd9afc87c0dc9509b37ea92b9 Mon Sep 17 00:00:00 2001 From: Ashish Gupta Date: Sun, 28 Jul 2024 17:38:49 +0530 Subject: [PATCH] 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 --- .../GlossaryTerms/tabs/RelatedTerms.tsx | 1 + .../TagsSelectForm.component.tsx | 3 + .../TagsSelectForm.interface.ts | 1 + .../TreeAsyncSelectList.test.tsx | 1 + .../AsyncSelectList/TreeAsyncSelectList.tsx | 27 +++++---- .../ui/src/utils/GlossaryUtils.test.ts | 59 +++++++++++++++++++ .../resources/ui/src/utils/GlossaryUtils.tsx | 46 ++++++++++++++- 7 files changed, 127 insertions(+), 11 deletions(-) diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryTerms/tabs/RelatedTerms.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryTerms/tabs/RelatedTerms.tsx index dfa3cc64832..d0221548db3 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryTerms/tabs/RelatedTerms.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryTerms/tabs/RelatedTerms.tsx @@ -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'), })} diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Tag/TagsSelectForm/TagsSelectForm.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Tag/TagsSelectForm/TagsSelectForm.component.tsx index 9dcf637d4e9..393bb42f1ed 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Tag/TagsSelectForm/TagsSelectForm.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Tag/TagsSelectForm/TagsSelectForm.component.tsx @@ -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 = ({ ) : ( void; onSubmit: (option: DefaultOptionType | DefaultOptionType[]) => Promise; onCancel: () => void; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/common/AsyncSelectList/TreeAsyncSelectList.test.tsx b/openmetadata-ui/src/main/resources/ui/src/components/common/AsyncSelectList/TreeAsyncSelectList.test.tsx index 888f7b71289..1093729a8f7 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/common/AsyncSelectList/TreeAsyncSelectList.test.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/common/AsyncSelectList/TreeAsyncSelectList.test.tsx @@ -32,6 +32,7 @@ jest.mock('../../../utils/GlossaryUtils', () => ({ ]), convertGlossaryTermsToTreeOptions: jest.fn(), findGlossaryTermByFqn: jest.fn(), + filterTreeNodeOptions: jest.fn().mockReturnValue([]), })); describe('TreeAsyncSelectList', () => { diff --git a/openmetadata-ui/src/main/resources/ui/src/components/common/AsyncSelectList/TreeAsyncSelectList.tsx b/openmetadata-ui/src/main/resources/ui/src/components/common/AsyncSelectList/TreeAsyncSelectList.tsx index 7223100839c..57adb0e744e 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/common/AsyncSelectList/TreeAsyncSelectList.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/common/AsyncSelectList/TreeAsyncSelectList.tsx @@ -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> = ({ initialOptions, tagType, isSubmitLoading, + filterOptions = [], onCancel, ...props }) => { @@ -82,7 +84,9 @@ const TreeAsyncSelectList: FC> = ({ 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> = ({ 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> = ({ 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) ); diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/GlossaryUtils.test.ts b/openmetadata-ui/src/main/resources/ui/src/utils/GlossaryUtils.test.ts index c6cb6006e23..bea6c81ae09 100644 --- a/openmetadata-ui/src/main/resources/ui/src/utils/GlossaryUtils.test.ts +++ b/openmetadata-ui/src/main/resources/ui/src/utils/GlossaryUtils.test.ts @@ -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); + }); }); diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/GlossaryUtils.tsx b/openmetadata-ui/src/main/resources/ui/src/utils/GlossaryUtils.tsx index fef037b108d..3522c20bb83 100644 --- a/openmetadata-ui/src/main/resources/ui/src/utils/GlossaryUtils.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/utils/GlossaryUtils.tsx @@ -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[]); +};