From d911523620b9eeb86991cef0024b2d366ea60c1a Mon Sep 17 00:00:00 2001 From: karanh37 Date: Mon, 4 Dec 2023 21:03:37 +0530 Subject: [PATCH 01/16] assets filter --- .../AssetFilters/AssetFilters.component.tsx | 191 ++++++++ .../AssetFilters/AssetFilters.interface.ts | 27 ++ .../AssetSelectionModal.interface.ts | 3 +- .../AssetSelectionModal.tsx | 264 +++++------ .../asset-selection-model.style.less | 14 + .../DataProductsDetailsPage.component.tsx | 30 +- .../DomainDetailsPage.component.tsx | 18 +- .../tabs/AssetsTabs.component.tsx | 445 +++++++++--------- .../src/constants/AdvancedSearch.constants.ts | 23 + .../ui/src/interface/search.interface.ts | 18 + .../ui/src/locale/languages/en-us.json | 1 + .../ExplorePage/ExplorePage.interface.ts | 1 + .../resources/ui/src/rest/dataProductAPI.ts | 37 +- .../main/resources/ui/src/rest/domainAPI.ts | 34 +- .../ui/src/utils/AdvancedSearchUtils.tsx | 16 +- .../resources/ui/src/utils/DomainUtils.tsx | 23 +- .../resources/ui/src/utils/GlossaryUtils.ts | 25 +- 17 files changed, 730 insertions(+), 440 deletions(-) create mode 100644 openmetadata-ui/src/main/resources/ui/src/components/AssetFilters/AssetFilters.component.tsx create mode 100644 openmetadata-ui/src/main/resources/ui/src/components/AssetFilters/AssetFilters.interface.ts diff --git a/openmetadata-ui/src/main/resources/ui/src/components/AssetFilters/AssetFilters.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/AssetFilters/AssetFilters.component.tsx new file mode 100644 index 000000000000..725c534c8e5c --- /dev/null +++ b/openmetadata-ui/src/main/resources/ui/src/components/AssetFilters/AssetFilters.component.tsx @@ -0,0 +1,191 @@ +/* + * Copyright 2023 Collate. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { PlusOutlined } from '@ant-design/icons'; +import { Button, Divider, Dropdown, Menu, Typography } from 'antd'; +import { isEmpty } from 'lodash'; +import React, { useCallback, useEffect, useMemo, useState } from 'react'; +import { useTranslation } from 'react-i18next'; +import { SearchIndex } from '../../enums/search.enum'; +import { EsQuery } from '../../interface/search.interface'; +import { getAssetsPageQuickFilters } from '../../utils/AdvancedSearchUtils'; +import { getSelectedValuesFromQuickFilter } from '../../utils/Explore.utils'; +import { ExploreQuickFilterField } from '../Explore/ExplorePage.interface'; +import ExploreQuickFilters from '../Explore/ExploreQuickFilters'; +import { AssetFiltersProps } from './AssetFilters.interface'; + +const AssetFilters = ({ + filterData, + type, + aggregations, + onQuickFilterChange, + quickFilterQuery, +}: AssetFiltersProps) => { + const { t } = useTranslation(); + const [filters, setFilters] = useState( + [] as ExploreQuickFilterField[] + ); + const [selectedFilter, setSelectedFilter] = useState([]); + const [selectedQuickFilters, setSelectedQuickFilters] = useState< + ExploreQuickFilterField[] + >([] as ExploreQuickFilterField[]); + + const handleMenuClick = ({ key }: { key: string }) => { + setSelectedFilter((prevSelected) => [...prevSelected, key]); + }; + + const menu = useMemo( + () => ( + + {filters.map((filter) => ( + {filter.label} + ))} + + ), + [selectedFilter, filters] + ); + + const handleQuickFiltersChange = (data: ExploreQuickFilterField[]) => { + const must: EsQuery[] = []; + data.forEach((filter) => { + if (!isEmpty(filter.value)) { + const should: EsQuery[] = []; + if (filter.value) { + filter.value.forEach((filterValue) => { + const term: Record = {}; + term[filter.key] = filterValue.key; + should.push({ term }); + }); + } + + must.push({ + bool: { should }, + }); + } + }); + + const quickFilterQuery = isEmpty(must) + ? undefined + : { + query: { bool: { must } }, + }; + + onQuickFilterChange?.(quickFilterQuery); + }; + + const handleQuickFiltersValueSelect = useCallback( + (field: ExploreQuickFilterField) => { + setSelectedQuickFilters((pre) => { + const data = pre.map((preField) => { + if (preField.key === field.key) { + return field; + } else { + return preField; + } + }); + + handleQuickFiltersChange(data); + + return data; + }); + }, + [setSelectedQuickFilters] + ); + + const clearFilters = useCallback(() => { + setSelectedQuickFilters((pre) => { + const data = pre.map((preField) => { + return { ...preField, value: [] }; + }); + + handleQuickFiltersChange(data); + + return data; + }); + }, [handleQuickFiltersChange, setSelectedQuickFilters]); + + useEffect(() => { + if (filterData?.length === 0) { + const dropdownItems = getAssetsPageQuickFilters(type); + + setFilters( + dropdownItems.map((item) => ({ + ...item, + value: getSelectedValuesFromQuickFilter( + item, + dropdownItems, + undefined // pass in state variable + ), + })) + ); + } else { + setFilters(filterData ?? []); + } + }, [filterData, type]); + + useEffect(() => { + const updatedQuickFilters = filters + .filter((filter) => selectedFilter.includes(filter.key)) + .map((selectedFilterItem) => { + const originalFilterItem = selectedQuickFilters?.find( + (filter) => filter.key === selectedFilterItem.key + ); + + return originalFilterItem || selectedFilterItem; + }); + + const newItems = updatedQuickFilters.filter( + (item) => + !selectedQuickFilters.some( + (existingItem) => item.key === existingItem.key + ) + ); + + if (newItems.length > 0) { + setSelectedQuickFilters((prevSelected) => [...prevSelected, ...newItems]); + } + }, [selectedFilter, selectedQuickFilters, filters]); + + return ( + <> + + - - )} - ); }, [ @@ -790,35 +717,23 @@ const AssetsTabs = forwardRef( } try { - let updatedEntity; + const entities = [...(assetsData?.values() ?? [])].map((item) => { + return getEntityReferenceFromEntity( + item, + (item as EntityDetailUnion).entityType + ); + }); + switch (type) { case AssetsOfEntity.DATA_PRODUCT: - const updatedAssets = ( - (activeEntity as DataProduct)?.assets ?? [] - ).filter( - (asset) => !assetsData.some((item) => item.id === asset.id) - ); - - updatedEntity = { - ...activeEntity, - assets: updatedAssets, - }; - const jsonPatch = compare(activeEntity, updatedEntity); - const res = await patchDataProduct( - (activeEntity as DataProduct).id, - jsonPatch + await removeAssetsFromDataProduct( + getEncodedFqn(activeEntity.fullyQualifiedName ?? ''), + entities ); - setActiveEntity(res); break; case AssetsOfEntity.GLOSSARY: - const entities = [...(assetsData?.values() ?? [])].map((item) => { - return getEntityReferenceFromEntity( - item, - (item as EntityDetailUnion).entityType - ); - }); await removeAssetsFromGlossaryTerm( activeEntity as GlossaryTerm, entities @@ -827,11 +742,10 @@ const AssetsTabs = forwardRef( break; case AssetsOfEntity.DOMAIN: - const selectedItemMap = new Map(); - assetsData.forEach((item) => { - selectedItemMap.set(item.id, item); - }); - await updateDomainAssets(undefined, type, selectedItemMap); + await removeAssetsFromDomain( + getEncodedFqn(activeEntity.fullyQualifiedName ?? ''), + entities + ); break; default: @@ -856,6 +770,23 @@ const AssetsTabs = forwardRef( [type, activeEntity, entityFqn] ); + const clearFilters = useCallback(() => { + setQuickFilterQuery(undefined); + setSelectedQuickFilters((pre) => { + const data = pre.map((preField) => { + return { ...preField, value: [] }; + }); + + handleQuickFiltersChange(data); + + return data; + }); + }, [ + setQuickFilterQuery, + handleQuickFiltersChange, + setSelectedQuickFilters, + ]); + const openNotification = () => { notification.warning({ key: 'asset-tab-notification-key', @@ -880,12 +811,12 @@ const AssetsTabs = forwardRef( index: isEmpty(activeFilter) ? [SearchIndex.ALL] : activeFilter, page: currentPage, }); - }, [activeFilter, currentPage, pageSize, searchValue]); + }, [activeFilter, currentPage, pageSize, searchValue, quickFilterQuery]); useEffect(() => { - const dropdownItems = getAssetsPageQuickFilters(); + const dropdownItems = getAssetsPageQuickFilters(type); - setSelectedQuickFilters( + setFilters( dropdownItems.map((item) => ({ ...item, value: getSelectedValuesFromQuickFilter( @@ -897,6 +828,32 @@ const AssetsTabs = forwardRef( ); }, [type]); + useEffect(() => { + const updatedQuickFilters = filters + .filter((filter) => selectedFilter.includes(filter.key)) + .map((selectedFilterItem) => { + const originalFilterItem = selectedQuickFilters?.find( + (filter) => filter.key === selectedFilterItem.key + ); + + return originalFilterItem || selectedFilterItem; + }); + + const newItems = updatedQuickFilters.filter( + (item) => + !selectedQuickFilters.some( + (existingItem) => item.key === existingItem.key + ) + ); + + if (newItems.length > 0) { + setSelectedQuickFilters((prevSelected) => [ + ...prevSelected, + ...newItems, + ]); + } + }, [selectedFilter, selectedQuickFilters, filters]); + useImperativeHandle(ref, () => ({ refreshAssets() { fetchAssets({ @@ -926,26 +883,44 @@ const AssetsTabs = forwardRef(
- - - + + +
+ +
- - + +
+ + {quickFilterQuery && ( + + {t('label.clear-entity', { + entity: '', + })} + + )} +
diff --git a/openmetadata-ui/src/main/resources/ui/src/constants/AdvancedSearch.constants.ts b/openmetadata-ui/src/main/resources/ui/src/constants/AdvancedSearch.constants.ts index 3c4df47c736f..2deffe430946 100644 --- a/openmetadata-ui/src/main/resources/ui/src/constants/AdvancedSearch.constants.ts +++ b/openmetadata-ui/src/main/resources/ui/src/constants/AdvancedSearch.constants.ts @@ -171,6 +171,29 @@ export const DATA_PRODUCT_DROPDOWN_ITEMS = [ }, ]; +export const DOMAIN_DROPDOWN_ITEMS = [ + { + label: t('label.owner'), + key: 'owner.displayName.keyword', + }, + { + label: t('label.tag'), + key: 'tags.tagFQN', + }, + { + label: t('label.tier'), + key: 'tier.tagFQN', + }, + { + label: t('label.service'), + key: 'service.displayName.keyword', + }, + { + label: t('label.service-type'), + key: 'serviceType', + }, +]; + export const ALL_DROPDOWN_ITEMS = [ ...COMMON_DROPDOWN_ITEMS, ...TABLE_DROPDOWN_ITEMS, diff --git a/openmetadata-ui/src/main/resources/ui/src/interface/search.interface.ts b/openmetadata-ui/src/main/resources/ui/src/interface/search.interface.ts index 9a32036545d8..0f847171efbb 100644 --- a/openmetadata-ui/src/main/resources/ui/src/interface/search.interface.ts +++ b/openmetadata-ui/src/main/resources/ui/src/interface/search.interface.ts @@ -381,3 +381,21 @@ export type RawSuggestResponse< }>; }; }; + +export interface EsQuery { + term: Record; +} + +export interface ElasticSearchQuery { + query: { + bool: { + must?: Array< + EsQuery | { bool: { must?: EsQuery; must_not?: EsQuery[] } } + >; + must_not?: Array< + EsQuery | { bool: { must?: EsQuery; must_not?: EsQuery[] } } + >; + should?: EsQuery[]; + }; + }; +} diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/en-us.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/en-us.json index 9221488ba761..a90f2f12e0f4 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/en-us.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/en-us.json @@ -258,6 +258,7 @@ "data-model-plural": "Data Models", "data-model-type": "Data Model Type", "data-product": "Data Product", + "data-product-lowercase": "data product", "data-product-plural": "Data Products", "data-profiler-metrics": "Data Profiler Metrics", "data-proportion-plural": "Data Proportions", diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/ExplorePage/ExplorePage.interface.ts b/openmetadata-ui/src/main/resources/ui/src/pages/ExplorePage/ExplorePage.interface.ts index aea5180e39ca..a0ec727caf1b 100644 --- a/openmetadata-ui/src/main/resources/ui/src/pages/ExplorePage/ExplorePage.interface.ts +++ b/openmetadata-ui/src/main/resources/ui/src/pages/ExplorePage/ExplorePage.interface.ts @@ -26,6 +26,7 @@ export interface QueryFilterInterface { query: { bool: { must?: QueryFieldInterface[]; + must_not?: QueryFieldInterface[]; should?: QueryFieldInterface[]; }; }; diff --git a/openmetadata-ui/src/main/resources/ui/src/rest/dataProductAPI.ts b/openmetadata-ui/src/main/resources/ui/src/rest/dataProductAPI.ts index 5a3a2c89c44d..e093f5c448e7 100644 --- a/openmetadata-ui/src/main/resources/ui/src/rest/dataProductAPI.ts +++ b/openmetadata-ui/src/main/resources/ui/src/rest/dataProductAPI.ts @@ -17,7 +17,10 @@ import { PagingResponse } from 'Models'; import { PAGE_SIZE } from '../constants/constants'; import { SearchIndex } from '../enums/search.enum'; import { CreateDataProduct } from '../generated/api/domains/createDataProduct'; -import { DataProduct } from '../generated/entity/domains/dataProduct'; +import { + DataProduct, + EntityReference, +} from '../generated/entity/domains/dataProduct'; import { EntityHistory } from '../generated/type/entityHistory'; import { Include } from '../generated/type/include'; import { Paging } from '../generated/type/paging'; @@ -136,3 +139,35 @@ export const fetchDataProductsElasticSearch = async ( }, }; }; + +export const addAssetsToDataProduct = async ( + dataProductFqn: string, + assets: EntityReference[] +) => { + const data: { assets: EntityReference[] } = { + assets: assets, + }; + + const response = await APIClient.put< + { assets: EntityReference[] }, + AxiosResponse + >(`/dataProducts/${dataProductFqn}/assets/add`, data); + + return response.data; +}; + +export const removeAssetsFromDataProduct = async ( + dataProductFqn: string, + assets: EntityReference[] +) => { + const data = { + assets: assets, + }; + + const response = await APIClient.put< + { assets: EntityReference[] }, + AxiosResponse + >(`/dataProducts/${dataProductFqn}/assets/remove`, data); + + return response.data; +}; diff --git a/openmetadata-ui/src/main/resources/ui/src/rest/domainAPI.ts b/openmetadata-ui/src/main/resources/ui/src/rest/domainAPI.ts index 08655d8dc79f..8239d80b4766 100644 --- a/openmetadata-ui/src/main/resources/ui/src/rest/domainAPI.ts +++ b/openmetadata-ui/src/main/resources/ui/src/rest/domainAPI.ts @@ -15,7 +15,7 @@ import { AxiosResponse } from 'axios'; import { Operation } from 'fast-json-patch'; import { PagingResponse } from 'Models'; import { CreateDomain } from '../generated/api/domains/createDomain'; -import { Domain } from '../generated/entity/domains/domain'; +import { Domain, EntityReference } from '../generated/entity/domains/domain'; import { EntityHistory } from '../generated/type/entityHistory'; import { ListParams } from '../interface/API.interface'; import { getURLWithQueryFields } from '../utils/APIUtils'; @@ -81,3 +81,35 @@ export const getDomainVersionData = async (id: string, version: string) => { return response.data; }; + +export const addAssetsToDomain = async ( + domainFqn: string, + assets: EntityReference[] +) => { + const data: { assets: EntityReference[] } = { + assets: assets, + }; + + const response = await APIClient.put< + { assets: EntityReference[] }, + AxiosResponse + >(`/domains/${domainFqn}/assets/add`, data); + + return response.data; +}; + +export const removeAssetsFromDomain = async ( + domainFqn: string, + assets: EntityReference[] +) => { + const data = { + assets: assets, + }; + + const response = await APIClient.put< + { assets: EntityReference[] }, + AxiosResponse + >(`/domains/${domainFqn}/assets/remove`, data); + + return response.data; +}; diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/AdvancedSearchUtils.tsx b/openmetadata-ui/src/main/resources/ui/src/utils/AdvancedSearchUtils.tsx index b42c531201ef..85df4c672a05 100644 --- a/openmetadata-ui/src/main/resources/ui/src/utils/AdvancedSearchUtils.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/utils/AdvancedSearchUtils.tsx @@ -18,6 +18,7 @@ import { isArray, isEmpty } from 'lodash'; import React from 'react'; import { RenderSettings } from 'react-awesome-query-builder'; import ProfilePicture from '../components/common/ProfilePicture/ProfilePicture'; +import { AssetsOfEntity } from '../components/Glossary/GlossaryTerms/tabs/AssetsTabs.interface'; import { SearchDropdownOption } from '../components/SearchDropdown/SearchDropdown.interface'; import { COMMON_DROPDOWN_ITEMS, @@ -25,6 +26,7 @@ import { DASHBOARD_DATA_MODEL_TYPE, DASHBOARD_DROPDOWN_ITEMS, DATA_PRODUCT_DROPDOWN_ITEMS, + DOMAIN_DROPDOWN_ITEMS, GLOSSARY_DROPDOWN_ITEMS, PIPELINE_DROPDOWN_ITEMS, SEARCH_INDEX_DROPDOWN_ITEMS, @@ -88,9 +90,19 @@ export const getDropDownItems = (index: string) => { } }; -export const getAssetsPageQuickFilters = () => { +export const getAssetsPageQuickFilters = (type: AssetsOfEntity) => { + switch (type) { + case AssetsOfEntity.DOMAIN: + return [...DOMAIN_DROPDOWN_ITEMS]; + + case AssetsOfEntity.DATA_PRODUCT: + return [...DATA_PRODUCT_DROPDOWN_ITEMS]; + + case AssetsOfEntity.GLOSSARY: + default: + return [...COMMON_DROPDOWN_ITEMS]; + } // TODO: Add more quick filters - return [...COMMON_DROPDOWN_ITEMS]; }; export const getAdvancedField = (field: string) => { diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/DomainUtils.tsx b/openmetadata-ui/src/main/resources/ui/src/utils/DomainUtils.tsx index 06b104b794c6..0125b094dc91 100644 --- a/openmetadata-ui/src/main/resources/ui/src/utils/DomainUtils.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/utils/DomainUtils.tsx @@ -25,6 +25,7 @@ import { EntityField } from '../constants/Feeds.constants'; import { DataProduct } from '../generated/entity/domains/dataProduct'; import { Domain } from '../generated/entity/domains/domain'; import { ChangeDescription, EntityReference } from '../generated/entity/type'; +import { ElasticSearchQuery } from '../interface/search.interface'; import { getEntityName } from './EntityUtils'; import { getChangedEntityNewValue, @@ -116,7 +117,7 @@ export const getUserNames = ( export const getQueryFilterToIncludeDomain = ( domainFqn: string, dataProductFqn: string -) => ({ +): ElasticSearchQuery => ({ query: { bool: { must: [ @@ -152,23 +153,15 @@ export const getQueryFilterToIncludeDomain = ( }, }); -export const getQueryFilterToExcludeDomainTerms = (fqn: string) => ({ +export const getQueryFilterToExcludeDomainTerms = ( + fqn: string +): ElasticSearchQuery => ({ query: { bool: { - must: [ + must_not: [ { - bool: { - must: [ - { - bool: { - must_not: { - term: { - 'domain.fullyQualifiedName': fqn, - }, - }, - }, - }, - ], + term: { + 'domain.fullyQualifiedName': fqn, }, }, ], diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/GlossaryUtils.ts b/openmetadata-ui/src/main/resources/ui/src/utils/GlossaryUtils.ts index 31b438c3ef28..efe24d39b25c 100644 --- a/openmetadata-ui/src/main/resources/ui/src/utils/GlossaryUtils.ts +++ b/openmetadata-ui/src/main/resources/ui/src/utils/GlossaryUtils.ts @@ -23,7 +23,10 @@ import { SearchIndex } from '../enums/search.enum'; import { Glossary } from '../generated/entity/data/glossary'; import { GlossaryTerm, Status } from '../generated/entity/data/glossaryTerm'; import { EntityReference } from '../generated/type/entityReference'; -import { SearchResponse } from '../interface/search.interface'; +import { + ElasticSearchQuery, + SearchResponse, +} from '../interface/search.interface'; import { ListGlossaryTermsParams } from '../rest/glossaryAPI'; import { searchData } from '../rest/miscAPI'; import { formatSearchGlossaryTermResponse } from './APIUtils'; @@ -189,23 +192,15 @@ export const getSearchedDataFromGlossaryTree = ( }, [] as ModifiedGlossaryTerm[]); }; -export const getQueryFilterToExcludeTerm = (fqn: string) => ({ +export const getQueryFilterToExcludeTerm = ( + fqn: string +): ElasticSearchQuery => ({ query: { bool: { - must: [ + must_not: [ { - bool: { - must: [ - { - bool: { - must_not: { - term: { - 'tags.tagFQN': fqn, - }, - }, - }, - }, - ], + term: { + 'tags.tagFQN': fqn, }, }, ], From f8435cb10652aaa4e2d7f06f87e2cb7e07d4a42f Mon Sep 17 00:00:00 2001 From: karanh37 Date: Mon, 4 Dec 2023 21:05:31 +0530 Subject: [PATCH 02/16] localisation --- .../src/main/resources/ui/src/locale/languages/de-de.json | 1 + .../src/main/resources/ui/src/locale/languages/es-es.json | 1 + .../src/main/resources/ui/src/locale/languages/fr-fr.json | 1 + .../src/main/resources/ui/src/locale/languages/ja-jp.json | 1 + .../src/main/resources/ui/src/locale/languages/pt-br.json | 1 + .../src/main/resources/ui/src/locale/languages/ru-ru.json | 1 + .../src/main/resources/ui/src/locale/languages/zh-cn.json | 1 + 7 files changed, 7 insertions(+) diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/de-de.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/de-de.json index 95451a8fad05..ad130ee08104 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/de-de.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/de-de.json @@ -258,6 +258,7 @@ "data-model-plural": "Datenmodelle", "data-model-type": "Datenmodelltyp", "data-product": "Datenprodukt", + "data-product-lowercase": "data product", "data-product-plural": "Datenprodukte", "data-profiler-metrics": "Data Profiler Metrics", "data-proportion-plural": "Datenverhältnisse", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/es-es.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/es-es.json index c331cf073a7e..6cce1e17c732 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/es-es.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/es-es.json @@ -258,6 +258,7 @@ "data-model-plural": "Data Models", "data-model-type": "Data Model Type", "data-product": "Data Product", + "data-product-lowercase": "data product", "data-product-plural": "Data Products", "data-profiler-metrics": "Data Profiler Metrics", "data-proportion-plural": "Data Proportions", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/fr-fr.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/fr-fr.json index a3be02c3e0f9..10dca5787464 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/fr-fr.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/fr-fr.json @@ -258,6 +258,7 @@ "data-model-plural": "Modèles de Données", "data-model-type": "Type de Modèle de Données", "data-product": "Produit de Données", + "data-product-lowercase": "data product", "data-product-plural": "Produits de Données", "data-profiler-metrics": "Data Profiler Metrics", "data-proportion-plural": "Proportions des Données", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/ja-jp.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/ja-jp.json index 0fb2fbfbd2b0..31283b627276 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/ja-jp.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/ja-jp.json @@ -258,6 +258,7 @@ "data-model-plural": "Data Models", "data-model-type": "Data Model Type", "data-product": "Data Product", + "data-product-lowercase": "data product", "data-product-plural": "Data Products", "data-profiler-metrics": "Data Profiler Metrics", "data-proportion-plural": "Data Proportions", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/pt-br.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/pt-br.json index 4dd9123e9800..02046ae29070 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/pt-br.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/pt-br.json @@ -258,6 +258,7 @@ "data-model-plural": "Modelos de Dados", "data-model-type": "Tipo de Modelo de Dados", "data-product": "Produto de Dados", + "data-product-lowercase": "data product", "data-product-plural": "Produtos de Dados", "data-profiler-metrics": "Métricas do Examinador de Dados", "data-proportion-plural": "Proporções de Dados", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/ru-ru.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/ru-ru.json index c2ce93dc265f..d0fb596c3a12 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/ru-ru.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/ru-ru.json @@ -258,6 +258,7 @@ "data-model-plural": "Модели данных", "data-model-type": "Тип модели данных", "data-product": "Data Product", + "data-product-lowercase": "data product", "data-product-plural": "Data Products", "data-profiler-metrics": "Data Profiler Metrics", "data-proportion-plural": "Распределение данных", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/zh-cn.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/zh-cn.json index a543995d24ec..99c78a331c88 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/zh-cn.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/zh-cn.json @@ -258,6 +258,7 @@ "data-model-plural": "数据模型", "data-model-type": "数据模型类型", "data-product": "数据产品", + "data-product-lowercase": "data product", "data-product-plural": "数据产品", "data-profiler-metrics": "Data Profiler Metrics", "data-proportion-plural": "数据比例", From c1f2098c69d1651cb82be0ed8520efffea7de2a0 Mon Sep 17 00:00:00 2001 From: karanh37 Date: Mon, 4 Dec 2023 22:02:47 +0530 Subject: [PATCH 03/16] update filters --- .../AssetFilters/AssetFilters.component.tsx | 9 ++-- .../AssetFilters/AssetFilters.interface.ts | 10 ++-- .../AssetSelectionModal.interface.ts | 4 +- .../AssetSelectionModal.tsx | 46 +++++-------------- .../DataProductsDetailsPage.component.tsx | 11 +++-- .../GlossaryTermsV1.component.tsx | 9 ++-- .../tabs/AssetsTabs.component.tsx | 17 ++++--- .../resources/ui/src/enums/Explore.enum.ts | 1 + .../ui/src/interface/search.interface.ts | 18 -------- .../ExplorePage/ExplorePage.interface.ts | 1 + .../resources/ui/src/utils/DomainUtils.tsx | 18 +++++--- .../src/utils/ExplorePage/ExplorePageUtils.ts | 11 +++++ .../resources/ui/src/utils/GlossaryUtils.ts | 21 +++++---- 13 files changed, 81 insertions(+), 95 deletions(-) diff --git a/openmetadata-ui/src/main/resources/ui/src/components/AssetFilters/AssetFilters.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/AssetFilters/AssetFilters.component.tsx index 725c534c8e5c..5bc223d122b5 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/AssetFilters/AssetFilters.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/AssetFilters/AssetFilters.component.tsx @@ -16,7 +16,10 @@ import { isEmpty } from 'lodash'; import React, { useCallback, useEffect, useMemo, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { SearchIndex } from '../../enums/search.enum'; -import { EsQuery } from '../../interface/search.interface'; +import { + QueryFieldInterface, + QueryFieldValueInterface, +} from '../../pages/ExplorePage/ExplorePage.interface'; import { getAssetsPageQuickFilters } from '../../utils/AdvancedSearchUtils'; import { getSelectedValuesFromQuickFilter } from '../../utils/Explore.utils'; import { ExploreQuickFilterField } from '../Explore/ExplorePage.interface'; @@ -55,10 +58,10 @@ const AssetFilters = ({ ); const handleQuickFiltersChange = (data: ExploreQuickFilterField[]) => { - const must: EsQuery[] = []; + const must: QueryFieldInterface[] = []; data.forEach((filter) => { if (!isEmpty(filter.value)) { - const should: EsQuery[] = []; + const should: QueryFieldValueInterface[] = []; if (filter.value) { filter.value.forEach((filterValue) => { const term: Record = {}; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/AssetFilters/AssetFilters.interface.ts b/openmetadata-ui/src/main/resources/ui/src/components/AssetFilters/AssetFilters.interface.ts index c81a0bdd4248..688cac483f9d 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/AssetFilters/AssetFilters.interface.ts +++ b/openmetadata-ui/src/main/resources/ui/src/components/AssetFilters/AssetFilters.interface.ts @@ -10,10 +10,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { - Aggregations, - ElasticSearchQuery, -} from '../../interface/search.interface'; +import { Aggregations } from '../../interface/search.interface'; +import { QueryFilterInterface } from '../../pages/ExplorePage/ExplorePage.interface'; import { ExploreQuickFilterField } from '../Explore/ExplorePage.interface'; import { AssetsOfEntity } from '../Glossary/GlossaryTerms/tabs/AssetsTabs.interface'; @@ -21,7 +19,7 @@ export interface AssetFiltersProps { filterData?: ExploreQuickFilterField[]; defaultFilter?: string[]; aggregations?: Aggregations; - onQuickFilterChange?: (query: ElasticSearchQuery | undefined) => void; + onQuickFilterChange?: (query: QueryFilterInterface | undefined) => void; type: AssetsOfEntity; - quickFilterQuery?: ElasticSearchQuery; + quickFilterQuery?: QueryFilterInterface; } diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Assets/AssetsSelectionModal/AssetSelectionModal.interface.ts b/openmetadata-ui/src/main/resources/ui/src/components/Assets/AssetsSelectionModal/AssetSelectionModal.interface.ts index 9aac4a0a570b..83faf82672fb 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Assets/AssetsSelectionModal/AssetSelectionModal.interface.ts +++ b/openmetadata-ui/src/main/resources/ui/src/components/Assets/AssetsSelectionModal/AssetSelectionModal.interface.ts @@ -33,7 +33,7 @@ import { PipelineService } from '../../../generated/entity/services/pipelineServ import { SearchService } from '../../../generated/entity/services/searchService'; import { StorageService } from '../../../generated/entity/services/storageService'; import { Team } from '../../../generated/entity/teams/team'; -import { ElasticSearchQuery } from '../../../interface/search.interface'; +import { QueryFilterInterface } from '../../../pages/ExplorePage/ExplorePage.interface'; export interface AssetSelectionModalProps { entityFqn: string; @@ -41,7 +41,7 @@ export interface AssetSelectionModalProps { type?: AssetsOfEntity; onCancel: () => void; onSave?: () => void; - queryFilter?: ElasticSearchQuery; + queryFilter?: QueryFilterInterface; emptyPlaceHolderText?: string; } diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Assets/AssetsSelectionModal/AssetSelectionModal.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Assets/AssetsSelectionModal/AssetSelectionModal.tsx index 34f5b9d16b2f..19d283b9978d 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Assets/AssetsSelectionModal/AssetSelectionModal.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Assets/AssetsSelectionModal/AssetSelectionModal.tsx @@ -27,10 +27,8 @@ import { SearchIndex } from '../../../enums/search.enum'; import { GlossaryTerm } from '../../../generated/entity/data/glossaryTerm'; import { DataProduct } from '../../../generated/entity/domains/dataProduct'; import { Domain } from '../../../generated/entity/domains/domain'; -import { - Aggregations, - ElasticSearchQuery, -} from '../../../interface/search.interface'; +import { Aggregations } from '../../../interface/search.interface'; +import { QueryFilterInterface } from '../../../pages/ExplorePage/ExplorePage.interface'; import { addAssetsToDataProduct, getDataProductByName, @@ -47,6 +45,7 @@ import { getAggregations, getSelectedValuesFromQuickFilter, } from '../../../utils/Explore.utils'; +import { getCombinedQueryFilterObject } from '../../../utils/ExplorePage/ExplorePageUtils'; import { getDecodedFqn, getEncodedFqn } from '../../../utils/StringsUtils'; import { showErrorToast } from '../../../utils/ToastUtils'; import AssetFilters from '../../AssetFilters/AssetFilters.component'; @@ -66,7 +65,7 @@ export const AssetSelectionModal = ({ onSave, open, type = AssetsOfEntity.GLOSSARY, - queryFilter = {}, + queryFilter, emptyPlaceHolderText, }: AssetSelectionModalProps) => { const { t } = useTranslation(); @@ -89,10 +88,10 @@ export const AssetSelectionModal = ({ ExploreQuickFilterField[] >([] as ExploreQuickFilterField[]); const [quickFilterQuery, setQuickFilterQuery] = useState< - ElasticSearchQuery | undefined + QueryFilterInterface | undefined >(); const [updatedQueryFilter, setUpdatedQueryFilter] = - useState(queryFilter); + useState(); const fetchEntities = useCallback( async ({ @@ -258,33 +257,12 @@ export const AssetSelectionModal = ({ }, [type, handleSave]); const mergeFilters = useCallback(() => { - const mergedFilter: ElasticSearchQuery = { - query: { - bool: { - must: [], - }, - }, - }; - - // Merge must clauses - mergedFilter.query.bool.must = [ - ...(queryFilter?.query?.bool?.must || []), - ...(quickFilterQuery?.query?.bool?.must || []), - ]; - - // Merge must_not clauses - if ( - queryFilter?.query?.bool?.must_not || - quickFilterQuery?.query?.bool?.should - ) { - mergedFilter.query.bool.must_not = [ - ...(queryFilter?.query?.bool?.must_not || []), - ...(quickFilterQuery?.query?.bool?.should || []), - ]; - } - - setUpdatedQueryFilter(mergedFilter); - }, [quickFilterQuery, queryFilter]); + const res = getCombinedQueryFilterObject( + queryFilter as QueryFilterInterface, + quickFilterQuery as QueryFilterInterface + ); + setUpdatedQueryFilter(res); + }, [queryFilter, quickFilterQuery]); useEffect(() => { mergeFilters(); diff --git a/openmetadata-ui/src/main/resources/ui/src/components/DataProducts/DataProductsDetailsPage/DataProductsDetailsPage.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/DataProducts/DataProductsDetailsPage/DataProductsDetailsPage.component.tsx index 74a12e3b1714..95e1a91dc0f4 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/DataProducts/DataProductsDetailsPage/DataProductsDetailsPage.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/DataProducts/DataProductsDetailsPage/DataProductsDetailsPage.component.tsx @@ -63,6 +63,7 @@ import { import { Domain } from '../../../generated/entity/domains/domain'; import { Operation } from '../../../generated/entity/policies/policy'; import { Style } from '../../../generated/type/tagLabel'; +import { QueryFilterInterface } from '../../../pages/ExplorePage/ExplorePage.interface'; import { searchData } from '../../../rest/miscAPI'; import { getEntityDeleteMessage } from '../../../utils/CommonUtils'; import { getQueryFilterToIncludeDomain } from '../../../utils/DomainUtils'; @@ -575,10 +576,12 @@ const DataProductsDetailsPage = ({ })} entityFqn={dataProductFqn} open={assetModalVisible} - queryFilter={getQueryFilterToIncludeDomain( - dataProduct.domain?.fullyQualifiedName ?? '', - dataProduct.fullyQualifiedName ?? '' - )} + queryFilter={ + getQueryFilterToIncludeDomain( + dataProduct.domain?.fullyQualifiedName ?? '', + dataProduct.fullyQualifiedName ?? '' + ) as QueryFilterInterface + } type={AssetsOfEntity.DATA_PRODUCT} onCancel={() => setAssetModelVisible(false)} onSave={handleAssetSave} diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryTerms/GlossaryTermsV1.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryTerms/GlossaryTermsV1.component.tsx index b46716c938c1..4a42c3287e46 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryTerms/GlossaryTermsV1.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryTerms/GlossaryTermsV1.component.tsx @@ -31,6 +31,7 @@ import { } from '../../../generated/entity/data/glossaryTerm'; import { ChangeDescription } from '../../../generated/entity/type'; import { MOCK_GLOSSARY_NO_PERMISSIONS } from '../../../mocks/Glossary.mock'; +import { QueryFilterInterface } from '../../../pages/ExplorePage/ExplorePage.interface'; import { searchData } from '../../../rest/miscAPI'; import { getCountBadge, getFeedCounts } from '../../../utils/CommonUtils'; import { getEntityVersionByField } from '../../../utils/EntityVersionUtils'; @@ -339,9 +340,11 @@ const GlossaryTermsV1 = ({ setAssetModelVisible(false)} onSave={handleAssetSave} diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryTerms/tabs/AssetsTabs.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryTerms/tabs/AssetsTabs.component.tsx index 2008071f08a8..b99c23ed2b9a 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryTerms/tabs/AssetsTabs.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryTerms/tabs/AssetsTabs.component.tsx @@ -61,11 +61,11 @@ import { GlossaryTerm } from '../../../../generated/entity/data/glossaryTerm'; import { DataProduct } from '../../../../generated/entity/domains/dataProduct'; import { Domain } from '../../../../generated/entity/domains/domain'; import { usePaging } from '../../../../hooks/paging/usePaging'; +import { Aggregations } from '../../../../interface/search.interface'; import { - Aggregations, - ElasticSearchQuery, - EsQuery, -} from '../../../../interface/search.interface'; + QueryFieldInterface, + QueryFieldValueInterface, +} from '../../../../pages/ExplorePage/ExplorePage.interface'; import { getDataProductByName, removeAssetsFromDataProduct, @@ -142,9 +142,8 @@ const AssetsTabs = forwardRef( const { fqn } = useParams<{ fqn: string }>(); const [isLoading, setIsLoading] = useState(true); const [data, setData] = useState([]); - const [quickFilterQuery, setQuickFilterQuery] = useState< - ElasticSearchQuery | undefined - >(); + const [quickFilterQuery, setQuickFilterQuery] = + useState>(); const { currentPage, pageSize, @@ -531,10 +530,10 @@ const AssetsTabs = forwardRef( }, []); const handleQuickFiltersChange = (data: ExploreQuickFilterField[]) => { - const must: EsQuery[] = []; + const must: QueryFieldInterface[] = []; data.forEach((filter) => { if (!isEmpty(filter.value)) { - const should: EsQuery[] = []; + const should: QueryFieldValueInterface[] = []; if (filter.value) { filter.value.forEach((filterValue) => { const term: Record = {}; diff --git a/openmetadata-ui/src/main/resources/ui/src/enums/Explore.enum.ts b/openmetadata-ui/src/main/resources/ui/src/enums/Explore.enum.ts index ff3e1035df72..1090619ab7af 100644 --- a/openmetadata-ui/src/main/resources/ui/src/enums/Explore.enum.ts +++ b/openmetadata-ui/src/main/resources/ui/src/enums/Explore.enum.ts @@ -14,6 +14,7 @@ export enum QueryFilterFieldsEnum { MUST = 'must', SHOULD = 'should', + MUST_NOT = 'must_not', } export enum ExplorePageTabs { diff --git a/openmetadata-ui/src/main/resources/ui/src/interface/search.interface.ts b/openmetadata-ui/src/main/resources/ui/src/interface/search.interface.ts index 0f847171efbb..9a32036545d8 100644 --- a/openmetadata-ui/src/main/resources/ui/src/interface/search.interface.ts +++ b/openmetadata-ui/src/main/resources/ui/src/interface/search.interface.ts @@ -381,21 +381,3 @@ export type RawSuggestResponse< }>; }; }; - -export interface EsQuery { - term: Record; -} - -export interface ElasticSearchQuery { - query: { - bool: { - must?: Array< - EsQuery | { bool: { must?: EsQuery; must_not?: EsQuery[] } } - >; - must_not?: Array< - EsQuery | { bool: { must?: EsQuery; must_not?: EsQuery[] } } - >; - should?: EsQuery[]; - }; - }; -} diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/ExplorePage/ExplorePage.interface.ts b/openmetadata-ui/src/main/resources/ui/src/pages/ExplorePage/ExplorePage.interface.ts index a0ec727caf1b..5844cc98ff92 100644 --- a/openmetadata-ui/src/main/resources/ui/src/pages/ExplorePage/ExplorePage.interface.ts +++ b/openmetadata-ui/src/main/resources/ui/src/pages/ExplorePage/ExplorePage.interface.ts @@ -18,6 +18,7 @@ export interface QueryFieldValueInterface { export interface QueryFieldInterface { bool: { must?: Array; + must_not?: Array; should?: Array; }; } diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/DomainUtils.tsx b/openmetadata-ui/src/main/resources/ui/src/utils/DomainUtils.tsx index 0125b094dc91..56e3fabe49b5 100644 --- a/openmetadata-ui/src/main/resources/ui/src/utils/DomainUtils.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/utils/DomainUtils.tsx @@ -25,7 +25,7 @@ import { EntityField } from '../constants/Feeds.constants'; import { DataProduct } from '../generated/entity/domains/dataProduct'; import { Domain } from '../generated/entity/domains/domain'; import { ChangeDescription, EntityReference } from '../generated/entity/type'; -import { ElasticSearchQuery } from '../interface/search.interface'; +import { QueryFilterInterface } from '../pages/ExplorePage/ExplorePage.interface'; import { getEntityName } from './EntityUtils'; import { getChangedEntityNewValue, @@ -117,7 +117,7 @@ export const getUserNames = ( export const getQueryFilterToIncludeDomain = ( domainFqn: string, dataProductFqn: string -): ElasticSearchQuery => ({ +) => ({ query: { bool: { must: [ @@ -155,13 +155,19 @@ export const getQueryFilterToIncludeDomain = ( export const getQueryFilterToExcludeDomainTerms = ( fqn: string -): ElasticSearchQuery => ({ +): QueryFilterInterface => ({ query: { bool: { - must_not: [ + must: [ { - term: { - 'domain.fullyQualifiedName': fqn, + bool: { + must_not: [ + { + term: { + 'domain.fullyQualifiedName': fqn, + }, + }, + ], }, }, ], diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/ExplorePage/ExplorePageUtils.ts b/openmetadata-ui/src/main/resources/ui/src/utils/ExplorePage/ExplorePageUtils.ts index cc688c0b9b1d..ac293627fdaf 100644 --- a/openmetadata-ui/src/main/resources/ui/src/utils/ExplorePage/ExplorePageUtils.ts +++ b/openmetadata-ui/src/main/resources/ui/src/utils/ExplorePage/ExplorePageUtils.ts @@ -30,6 +30,9 @@ export const getQueryFiltersArray = ( case QueryFilterFieldsEnum.MUST: { return queryFiltersObj?.query?.bool?.must ?? []; } + case QueryFilterFieldsEnum.MUST_NOT: { + return queryFiltersObj?.query?.bool?.must_not ?? []; + } } }; @@ -58,6 +61,13 @@ export const getCombinedQueryFilterObject = ( advancesSearchQueryFilter, advancesSearchFilter, ]); + + const mustNotField = getCombinedFields(QueryFilterFieldsEnum.MUST_NOT, [ + elasticsearchQueryFilter, + advancesSearchQueryFilter, + advancesSearchFilter, + ]); + const shouldField = getCombinedFields(QueryFilterFieldsEnum.SHOULD, [ elasticsearchQueryFilter, advancesSearchQueryFilter, @@ -68,6 +78,7 @@ export const getCombinedQueryFilterObject = ( query: { bool: { ...(isEmpty(mustField) ? {} : { must: mustField }), + ...(isEmpty(mustNotField) ? {} : { must_not: mustNotField }), ...(isEmpty(shouldField) ? {} : { should: shouldField }), }, }, diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/GlossaryUtils.ts b/openmetadata-ui/src/main/resources/ui/src/utils/GlossaryUtils.ts index efe24d39b25c..ac31ebf3f339 100644 --- a/openmetadata-ui/src/main/resources/ui/src/utils/GlossaryUtils.ts +++ b/openmetadata-ui/src/main/resources/ui/src/utils/GlossaryUtils.ts @@ -23,10 +23,7 @@ import { SearchIndex } from '../enums/search.enum'; import { Glossary } from '../generated/entity/data/glossary'; import { GlossaryTerm, Status } from '../generated/entity/data/glossaryTerm'; import { EntityReference } from '../generated/type/entityReference'; -import { - ElasticSearchQuery, - SearchResponse, -} from '../interface/search.interface'; +import { SearchResponse } from '../interface/search.interface'; import { ListGlossaryTermsParams } from '../rest/glossaryAPI'; import { searchData } from '../rest/miscAPI'; import { formatSearchGlossaryTermResponse } from './APIUtils'; @@ -192,15 +189,19 @@ export const getSearchedDataFromGlossaryTree = ( }, [] as ModifiedGlossaryTerm[]); }; -export const getQueryFilterToExcludeTerm = ( - fqn: string -): ElasticSearchQuery => ({ +export const getQueryFilterToExcludeTerm = (fqn: string) => ({ query: { bool: { - must_not: [ + must: [ { - term: { - 'tags.tagFQN': fqn, + bool: { + must_not: [ + { + term: { + 'tags.tagFQN': fqn, + }, + }, + ], }, }, ], From 47b8783636edb5d83d185405c4acd7885f3aff9c Mon Sep 17 00:00:00 2001 From: karanh37 Date: Mon, 4 Dec 2023 23:07:19 +0530 Subject: [PATCH 04/16] fix: update filters on teams page --- .../AssetSelectionModal.tsx | 4 +- .../DataProductsDetailsPage.component.tsx | 7 ++- .../DomainDetailsPage.component.tsx | 9 ++-- .../DataProductsTab.component.tsx | 7 +-- .../GlossaryTermsV1.component.tsx | 9 ++-- .../tabs/AssetsTabs.component.tsx | 44 +++++++++---------- .../src/constants/AdvancedSearch.constants.ts | 2 +- .../ui/src/utils/AdvancedSearchUtils.tsx | 6 +-- .../resources/ui/src/utils/DomainUtils.tsx | 3 +- .../ui/src/utils/GlossaryUtils.test.ts | 10 ++--- 10 files changed, 47 insertions(+), 54 deletions(-) diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Assets/AssetsSelectionModal/AssetSelectionModal.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Assets/AssetsSelectionModal/AssetSelectionModal.tsx index 19d283b9978d..7c8ca0a8110b 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Assets/AssetsSelectionModal/AssetSelectionModal.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Assets/AssetsSelectionModal/AssetSelectionModal.tsx @@ -46,7 +46,7 @@ import { getSelectedValuesFromQuickFilter, } from '../../../utils/Explore.utils'; import { getCombinedQueryFilterObject } from '../../../utils/ExplorePage/ExplorePageUtils'; -import { getDecodedFqn, getEncodedFqn } from '../../../utils/StringsUtils'; +import { getEncodedFqn } from '../../../utils/StringsUtils'; import { showErrorToast } from '../../../utils/ToastUtils'; import AssetFilters from '../../AssetFilters/AssetFilters.component'; import ErrorPlaceHolder from '../../common/ErrorWithPlaceholder/ErrorPlaceHolder'; @@ -134,7 +134,7 @@ export const AssetSelectionModal = ({ ); setActiveEntity(data); } else if (type === AssetsOfEntity.GLOSSARY) { - const data = await getGlossaryTermByFQN(getDecodedFqn(entityFqn), 'tags'); + const data = await getGlossaryTermByFQN(entityFqn, 'tags'); setActiveEntity(data); } }, [type, entityFqn]); diff --git a/openmetadata-ui/src/main/resources/ui/src/components/DataProducts/DataProductsDetailsPage/DataProductsDetailsPage.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/DataProducts/DataProductsDetailsPage/DataProductsDetailsPage.component.tsx index 95e1a91dc0f4..bc1617e118b1 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/DataProducts/DataProductsDetailsPage/DataProductsDetailsPage.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/DataProducts/DataProductsDetailsPage/DataProductsDetailsPage.component.tsx @@ -78,7 +78,10 @@ import { getDataProductVersionsPath, getDomainPath, } from '../../../utils/RouterUtils'; -import { escapeESReservedCharacters } from '../../../utils/StringsUtils'; +import { + escapeESReservedCharacters, + getEncodedFqn, +} from '../../../utils/StringsUtils'; import { showErrorToast } from '../../../utils/ToastUtils'; import { EntityDetailsObjectInterface } from '../../Explore/ExplorePage.interface'; import StyleModal from '../../Modals/StyleModal/StyleModal.component'; @@ -211,7 +214,7 @@ const DataProductsDetailsPage = ({ 1, 0, `(dataProducts.fullyQualifiedName:"${escapeESReservedCharacters( - dataProduct.fullyQualifiedName + getEncodedFqn(dataProduct.fullyQualifiedName ?? '') )}")`, '', '', diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Domain/DomainDetailsPage/DomainDetailsPage.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Domain/DomainDetailsPage/DomainDetailsPage.component.tsx index 810d656fa138..ba25d58143af 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Domain/DomainDetailsPage/DomainDetailsPage.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Domain/DomainDetailsPage/DomainDetailsPage.component.tsx @@ -81,7 +81,10 @@ import { getDomainPath, getDomainVersionsPath, } from '../../../utils/RouterUtils'; -import { escapeESReservedCharacters } from '../../../utils/StringsUtils'; +import { + escapeESReservedCharacters, + getEncodedFqn, +} from '../../../utils/StringsUtils'; import { showErrorToast } from '../../../utils/ToastUtils'; import DeleteWidgetModal from '../../common/DeleteWidget/DeleteWidgetModal'; import { EntityDetailsObjectInterface } from '../../Explore/ExplorePage.interface'; @@ -240,7 +243,7 @@ const DomainDetailsPage = ({ 1, 0, `(domain.fullyQualifiedName:${escapeESReservedCharacters( - domain.fullyQualifiedName + getEncodedFqn(domain.fullyQualifiedName ?? '') )})`, '', '', @@ -262,7 +265,7 @@ const DomainDetailsPage = ({ 1, 0, `(domain.fullyQualifiedName:"${escapeESReservedCharacters( - domain.fullyQualifiedName + getEncodedFqn(domain.fullyQualifiedName ?? '') )}") AND !(entityType:"dataProduct")`, '', '', diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Domain/DomainTabs/DataProductsTab/DataProductsTab.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Domain/DomainTabs/DataProductsTab/DataProductsTab.component.tsx index 54c67e194946..c357839aa1cd 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Domain/DomainTabs/DataProductsTab/DataProductsTab.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Domain/DomainTabs/DataProductsTab/DataProductsTab.component.tsx @@ -29,10 +29,7 @@ import { SearchIndex } from '../../../../enums/search.enum'; import { DataProduct } from '../../../../generated/entity/domains/dataProduct'; import { searchData } from '../../../../rest/miscAPI'; import { formatDataProductResponse } from '../../../../utils/APIUtils'; -import { - escapeESReservedCharacters, - getDecodedFqn, -} from '../../../../utils/StringsUtils'; +import { escapeESReservedCharacters } from '../../../../utils/StringsUtils'; import ErrorPlaceHolder from '../../../common/ErrorWithPlaceholder/ErrorPlaceHolder'; import EntitySummaryPanel from '../../../Explore/EntitySummaryPanel/EntitySummaryPanel.component'; import ExploreSearchCard from '../../../ExploreV1/ExploreSearchCard/ExploreSearchCard'; @@ -63,7 +60,7 @@ const DataProductsTab = forwardRef( 1, PAGE_SIZE_LARGE, `(domain.fullyQualifiedName:"${escapeESReservedCharacters( - getDecodedFqn(domainFqn) + domainFqn )}")`, '', '', diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryTerms/GlossaryTermsV1.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryTerms/GlossaryTermsV1.component.tsx index 4a42c3287e46..b46716c938c1 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryTerms/GlossaryTermsV1.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryTerms/GlossaryTermsV1.component.tsx @@ -31,7 +31,6 @@ import { } from '../../../generated/entity/data/glossaryTerm'; import { ChangeDescription } from '../../../generated/entity/type'; import { MOCK_GLOSSARY_NO_PERMISSIONS } from '../../../mocks/Glossary.mock'; -import { QueryFilterInterface } from '../../../pages/ExplorePage/ExplorePage.interface'; import { searchData } from '../../../rest/miscAPI'; import { getCountBadge, getFeedCounts } from '../../../utils/CommonUtils'; import { getEntityVersionByField } from '../../../utils/EntityVersionUtils'; @@ -340,11 +339,9 @@ const GlossaryTermsV1 = ({ setAssetModelVisible(false)} onSave={handleAssetSave} diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryTerms/tabs/AssetsTabs.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryTerms/tabs/AssetsTabs.component.tsx index b99c23ed2b9a..3117555ee243 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryTerms/tabs/AssetsTabs.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryTerms/tabs/AssetsTabs.component.tsx @@ -78,7 +78,6 @@ import { getGlossaryTermByFQN, removeAssetsFromGlossaryTerm, } from '../../../../rest/glossaryAPI'; -import { searchData } from '../../../../rest/miscAPI'; import { searchQuery } from '../../../../rest/searchAPI'; import { getAssetsPageQuickFilters } from '../../../../utils/AdvancedSearchUtils'; import { getCountBadge, Transi18next } from '../../../../utils/CommonUtils'; @@ -90,7 +89,6 @@ import { getAggregations, getSelectedValuesFromQuickFilter, } from '../../../../utils/Explore.utils'; -import { getEntityTypeFromSearchIndex } from '../../../../utils/SearchUtils'; import { escapeESReservedCharacters, getDecodedFqn, @@ -205,12 +203,12 @@ const AssetsTabs = forwardRef( switch (type) { case AssetsOfEntity.DOMAIN: return `(domain.fullyQualifiedName:"${escapeESReservedCharacters( - entityFqn + getEncodedFqn(entityFqn ?? '') )}") AND !(entityType:"dataProduct")`; case AssetsOfEntity.DATA_PRODUCT: return `(dataProducts.fullyQualifiedName:"${escapeESReservedCharacters( - entityFqn + getEncodedFqn(entityFqn ?? '') )}")`; case AssetsOfEntity.TEAM: @@ -223,7 +221,9 @@ const AssetsTabs = forwardRef( return queryFilter ?? ''; default: - return `(tags.tagFQN:"${escapeESReservedCharacters(entityFqn)}")`; + return `(tags.tagFQN:"${escapeESReservedCharacters( + getEncodedFqn(entityFqn ?? '') + )}")`; } }, [type, fqn, entityFqn]); @@ -241,7 +241,7 @@ const AssetsTabs = forwardRef( pageNumber: page, pageSize: pageSize, searchIndex: index, - query: searchValue, + query: `*${searchValue}*`, filters: queryParam, queryFilter: quickFilterQuery, }); @@ -262,7 +262,6 @@ const AssetsTabs = forwardRef( handlePagingChange({ total: res.hits.total.value ?? 0 }); setData(hits); setAggregations(getAggregations(res?.aggregations)); - hits[0] && setSelectedCard(hits[0]._source as SourceType); } catch (_) { // Nothing here @@ -395,26 +394,23 @@ const AssetsTabs = forwardRef( const fetchCountsByEntity = async () => { try { setIsCountLoading(true); - const res = await searchData( - '', - 0, - 0, - queryParam, - '', - '', - SearchIndex.ALL - ); - const buckets = res.data.aggregations[`sterms#index_count`].buckets; + const res = await searchQuery({ + query: `*${searchValue}*`, + pageNumber: 0, + pageSize: 0, + queryFilter: quickFilterQuery, + searchIndex: SearchIndex.ALL, + filters: queryParam, + }); + + const buckets = res.aggregations[`index_count`].buckets; const counts: Record = {}; buckets.forEach((item) => { if (item) { - counts[ - getEntityTypeFromSearchIndex(item?.key) ?? EntityType.TABLE - ] = item.doc_count; + counts[item.key ?? ''] = item.doc_count; } }); - setItemCount(counts as Record); } catch (err) { showErrorToast(err as AxiosError); @@ -582,7 +578,6 @@ const AssetsTabs = forwardRef(
{data.map(({ _source, _id = '' }) => ( @@ -638,6 +634,7 @@ const AssetsTabs = forwardRef( [ type, data, + activeEntity, permissions, paging, currentPage, @@ -675,7 +672,7 @@ const AssetsTabs = forwardRef( const assetsHeader = useMemo(() => { return (
- {data.length > 0 && ( + {activeEntity && data.length > 0 && ( onSelectAll(e.target.checked)}> @@ -688,6 +685,7 @@ const AssetsTabs = forwardRef( ); }, [ activeFilter, + activeEntity, isLoading, data, openKeys, diff --git a/openmetadata-ui/src/main/resources/ui/src/constants/AdvancedSearch.constants.ts b/openmetadata-ui/src/main/resources/ui/src/constants/AdvancedSearch.constants.ts index 2deffe430946..05820b56ebcb 100644 --- a/openmetadata-ui/src/main/resources/ui/src/constants/AdvancedSearch.constants.ts +++ b/openmetadata-ui/src/main/resources/ui/src/constants/AdvancedSearch.constants.ts @@ -171,7 +171,7 @@ export const DATA_PRODUCT_DROPDOWN_ITEMS = [ }, ]; -export const DOMAIN_DROPDOWN_ITEMS = [ +export const DOMAIN_DATAPRODUCT_DROPDOWN_ITEMS = [ { label: t('label.owner'), key: 'owner.displayName.keyword', diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/AdvancedSearchUtils.tsx b/openmetadata-ui/src/main/resources/ui/src/utils/AdvancedSearchUtils.tsx index 85df4c672a05..639b3f07119e 100644 --- a/openmetadata-ui/src/main/resources/ui/src/utils/AdvancedSearchUtils.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/utils/AdvancedSearchUtils.tsx @@ -26,7 +26,7 @@ import { DASHBOARD_DATA_MODEL_TYPE, DASHBOARD_DROPDOWN_ITEMS, DATA_PRODUCT_DROPDOWN_ITEMS, - DOMAIN_DROPDOWN_ITEMS, + DOMAIN_DATAPRODUCT_DROPDOWN_ITEMS, GLOSSARY_DROPDOWN_ITEMS, PIPELINE_DROPDOWN_ITEMS, SEARCH_INDEX_DROPDOWN_ITEMS, @@ -93,10 +93,8 @@ export const getDropDownItems = (index: string) => { export const getAssetsPageQuickFilters = (type: AssetsOfEntity) => { switch (type) { case AssetsOfEntity.DOMAIN: - return [...DOMAIN_DROPDOWN_ITEMS]; - case AssetsOfEntity.DATA_PRODUCT: - return [...DATA_PRODUCT_DROPDOWN_ITEMS]; + return [...DOMAIN_DATAPRODUCT_DROPDOWN_ITEMS]; case AssetsOfEntity.GLOSSARY: default: diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/DomainUtils.tsx b/openmetadata-ui/src/main/resources/ui/src/utils/DomainUtils.tsx index 56e3fabe49b5..28c2671375ca 100644 --- a/openmetadata-ui/src/main/resources/ui/src/utils/DomainUtils.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/utils/DomainUtils.tsx @@ -33,6 +33,7 @@ import { getDiffByFieldName, getDiffValue, } from './EntityVersionUtils'; +import { getEncodedFqn } from './StringsUtils'; export const getOwner = ( hasPermission: boolean, @@ -164,7 +165,7 @@ export const getQueryFilterToExcludeDomainTerms = ( must_not: [ { term: { - 'domain.fullyQualifiedName': fqn, + 'domain.fullyQualifiedName': getEncodedFqn(fqn), }, }, ], 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 747607250f57..4864cf7b18c7 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 @@ -31,14 +31,10 @@ describe('Glossary Utils', () => { must: [ { bool: { - must: [ + must_not: [ { - bool: { - must_not: { - term: { - 'tags.tagFQN': fqn, - }, - }, + term: { + 'tags.tagFQN': fqn, }, }, ], From 24355eb5fd8632de58a3ca651423ba1d5a200960 Mon Sep 17 00:00:00 2001 From: karanh37 Date: Mon, 4 Dec 2023 23:29:39 +0530 Subject: [PATCH 05/16] hide searchbar & restrict adding assets for draft --- .../tabs/AssetsTabs.component.tsx | 120 ++++++++++-------- 1 file changed, 64 insertions(+), 56 deletions(-) diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryTerms/tabs/AssetsTabs.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryTerms/tabs/AssetsTabs.component.tsx index 3117555ee243..6e8557180feb 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryTerms/tabs/AssetsTabs.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryTerms/tabs/AssetsTabs.component.tsx @@ -129,6 +129,7 @@ const AssetsTabs = forwardRef( type = AssetsOfEntity.GLOSSARY, noDataPlaceholder, entityFqn, + assetCount, }: AssetsTabsProps, ref ) => { @@ -456,7 +457,7 @@ const AssetsTabs = forwardRef( type={ERROR_PLACEHOLDER_TYPE.FILTER} /> ); - } else if (noDataPlaceholder || searchValue) { + } else if (noDataPlaceholder || searchValue || !permissions.Create) { return ( {isObject(noDataPlaceholder) && ( @@ -492,23 +493,28 @@ const AssetsTabs = forwardRef( }} /> - - - + + {permissions.Create && ( + + + + )} ); } @@ -880,46 +886,48 @@ const AssetsTabs = forwardRef(
- - -
- -
+ + +
+ + {quickFilterQuery && ( + + {t('label.clear-entity', { + entity: '', + })} + + )}
-
- - -
- - {quickFilterQuery && ( - - {t('label.clear-entity', { - entity: '', - })} - - )} -
- - + + + )} {isLoading || isCountLoading ? ( From 7672903f7c5df3fc347c65843f3fa4dc489255c6 Mon Sep 17 00:00:00 2001 From: karanh37 Date: Tue, 5 Dec 2023 12:18:08 +0530 Subject: [PATCH 06/16] fix: code smells --- .../AssetFilters/AssetFilters.component.tsx | 12 +++-------- .../AssetFilters/AssetFilters.interface.ts | 2 +- .../AssetSelectionModal.tsx | 13 ++++-------- .../tabs/AssetsTabs.component.tsx | 20 +++++++++++-------- .../GlossaryTerms/tabs/assets-tabs.less | 18 +++++++++++++++++ .../ui/src/locale/languages/en-us.json | 1 + 6 files changed, 39 insertions(+), 27 deletions(-) diff --git a/openmetadata-ui/src/main/resources/ui/src/components/AssetFilters/AssetFilters.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/AssetFilters/AssetFilters.component.tsx index 5bc223d122b5..3a8851b9ab1b 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/AssetFilters/AssetFilters.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/AssetFilters/AssetFilters.component.tsx @@ -34,13 +34,11 @@ const AssetFilters = ({ quickFilterQuery, }: AssetFiltersProps) => { const { t } = useTranslation(); - const [filters, setFilters] = useState( - [] as ExploreQuickFilterField[] - ); + const [filters, setFilters] = useState([]); const [selectedFilter, setSelectedFilter] = useState([]); const [selectedQuickFilters, setSelectedQuickFilters] = useState< ExploreQuickFilterField[] - >([] as ExploreQuickFilterField[]); + >([]); const handleMenuClick = ({ key }: { key: string }) => { setSelectedFilter((prevSelected) => [...prevSelected, key]); @@ -123,11 +121,7 @@ const AssetFilters = ({ setFilters( dropdownItems.map((item) => ({ ...item, - value: getSelectedValuesFromQuickFilter( - item, - dropdownItems, - undefined // pass in state variable - ), + value: getSelectedValuesFromQuickFilter(item, dropdownItems), })) ); } else { diff --git a/openmetadata-ui/src/main/resources/ui/src/components/AssetFilters/AssetFilters.interface.ts b/openmetadata-ui/src/main/resources/ui/src/components/AssetFilters/AssetFilters.interface.ts index 688cac483f9d..78b9e472b4c0 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/AssetFilters/AssetFilters.interface.ts +++ b/openmetadata-ui/src/main/resources/ui/src/components/AssetFilters/AssetFilters.interface.ts @@ -19,7 +19,7 @@ export interface AssetFiltersProps { filterData?: ExploreQuickFilterField[]; defaultFilter?: string[]; aggregations?: Aggregations; - onQuickFilterChange?: (query: QueryFilterInterface | undefined) => void; + onQuickFilterChange?: (query?: QueryFilterInterface) => void; type: AssetsOfEntity; quickFilterQuery?: QueryFilterInterface; } diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Assets/AssetsSelectionModal/AssetSelectionModal.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Assets/AssetsSelectionModal/AssetSelectionModal.tsx index 7c8ca0a8110b..027e480218ff 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Assets/AssetsSelectionModal/AssetSelectionModal.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Assets/AssetsSelectionModal/AssetSelectionModal.tsx @@ -86,10 +86,9 @@ export const AssetSelectionModal = ({ const [aggregations, setAggregations] = useState(); const [selectedQuickFilters, setSelectedQuickFilters] = useState< ExploreQuickFilterField[] - >([] as ExploreQuickFilterField[]); - const [quickFilterQuery, setQuickFilterQuery] = useState< - QueryFilterInterface | undefined - >(); + >([]); + const [quickFilterQuery, setQuickFilterQuery] = + useState(); const [updatedQueryFilter, setUpdatedQueryFilter] = useState(); @@ -145,11 +144,7 @@ export const AssetSelectionModal = ({ setSelectedQuickFilters( dropdownItems.map((item) => ({ ...item, - value: getSelectedValuesFromQuickFilter( - item, - dropdownItems, - undefined // pass in state variable - ), + value: getSelectedValuesFromQuickFilter(item, dropdownItems), })) ); }, [type]); diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryTerms/tabs/AssetsTabs.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryTerms/tabs/AssetsTabs.component.tsx index 6e8557180feb..65249fe57739 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryTerms/tabs/AssetsTabs.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryTerms/tabs/AssetsTabs.component.tsx @@ -179,10 +179,8 @@ const AssetsTabs = forwardRef( const [selectedFilter, setSelectedFilter] = useState([]); // Contains menu selection const [selectedQuickFilters, setSelectedQuickFilters] = useState< ExploreQuickFilterField[] - >([] as ExploreQuickFilterField[]); - const [filters, setFilters] = useState( - [] as ExploreQuickFilterField[] - ); + >([]); + const [filters, setFilters] = useState([]); const [searchValue, setSearchValue] = useState(''); const handleMenuClick = ({ key }: { key: string }) => { @@ -794,13 +792,19 @@ const AssetsTabs = forwardRef( notification.warning({ key: 'asset-tab-notification-key', message: ( -
+
{selectedItems && selectedItems.size > 1 && ( - - {selectedItems.size} {t('label.selected-lowercase')} + + {selectedItems.size} {t('label.items-selected-lowercase')} )} - +
), placement: 'bottom', diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryTerms/tabs/assets-tabs.less b/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryTerms/tabs/assets-tabs.less index 489d473ffbff..b58a8d1c515f 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryTerms/tabs/assets-tabs.less +++ b/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryTerms/tabs/assets-tabs.less @@ -79,3 +79,21 @@ } } } + +.asset-tab-delete-notification { + background-color: @text-color !important; + box-shadow: none !important; + .ant-notification-notice-icon { + display: none; + } + .ant-notification-notice-close { + color: @white !important; + left: 24px; + top: 22px; + } + .ant-notification-notice-message { + padding-right: 0 !important; + margin-left: 20px !important; + font-size: 14px !important; + } +} diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/en-us.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/en-us.json index a90f2f12e0f4..2617e9525a78 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/en-us.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/en-us.json @@ -530,6 +530,7 @@ "invalid-condition": "Invalid condition", "invalid-name": "Invalid Name", "is-ready-for-preview": "is ready for preview", + "items-selected-lowercase": "items selected", "january": "January", "job-lowercase": "job", "join": "Join", From bd3a78e1764b280c3434300c0d09b0df3d1b682d Mon Sep 17 00:00:00 2001 From: karanh37 Date: Tue, 5 Dec 2023 14:26:40 +0530 Subject: [PATCH 07/16] fix: encoding decoding issues --- .../AssetsSelectionModal/AssetSelectionModal.tsx | 8 +++++++- .../DataProductsDetailsPage.component.tsx | 7 ++++--- .../DomainDetailsPage.component.tsx | 14 ++++++++------ .../DataProductsTab/DataProductsTab.component.tsx | 14 ++++++++++---- .../GlossaryTerms/tabs/AssetsTabs.component.tsx | 13 ++++--------- .../resources/ui/src/locale/languages/de-de.json | 1 + .../resources/ui/src/locale/languages/es-es.json | 1 + .../resources/ui/src/locale/languages/fr-fr.json | 1 + .../resources/ui/src/locale/languages/ja-jp.json | 1 + .../resources/ui/src/locale/languages/pt-br.json | 1 + .../resources/ui/src/locale/languages/ru-ru.json | 1 + .../resources/ui/src/locale/languages/zh-cn.json | 1 + .../main/resources/ui/src/utils/DomainUtils.tsx | 3 +-- 13 files changed, 41 insertions(+), 25 deletions(-) diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Assets/AssetsSelectionModal/AssetSelectionModal.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Assets/AssetsSelectionModal/AssetSelectionModal.tsx index 027e480218ff..fdd86e5960e0 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Assets/AssetsSelectionModal/AssetSelectionModal.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Assets/AssetsSelectionModal/AssetSelectionModal.tsx @@ -90,7 +90,13 @@ export const AssetSelectionModal = ({ const [quickFilterQuery, setQuickFilterQuery] = useState(); const [updatedQueryFilter, setUpdatedQueryFilter] = - useState(); + useState( + getCombinedQueryFilterObject(queryFilter as QueryFilterInterface, { + query: { + bool: {}, + }, + }) + ); const fetchEntities = useCallback( async ({ diff --git a/openmetadata-ui/src/main/resources/ui/src/components/DataProducts/DataProductsDetailsPage/DataProductsDetailsPage.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/DataProducts/DataProductsDetailsPage/DataProductsDetailsPage.component.tsx index bc1617e118b1..2fb90906f01c 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/DataProducts/DataProductsDetailsPage/DataProductsDetailsPage.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/DataProducts/DataProductsDetailsPage/DataProductsDetailsPage.component.tsx @@ -209,13 +209,14 @@ const DataProductsDetailsPage = ({ const fetchDataProductAssets = async () => { if (dataProduct) { try { + const encodedFqn = getEncodedFqn( + escapeESReservedCharacters(dataProduct.fullyQualifiedName) + ); const res = await searchData( '', 1, 0, - `(dataProducts.fullyQualifiedName:"${escapeESReservedCharacters( - getEncodedFqn(dataProduct.fullyQualifiedName ?? '') - )}")`, + `(dataProducts.fullyQualifiedName:"${encodedFqn}")`, '', '', SearchIndex.ALL diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Domain/DomainDetailsPage/DomainDetailsPage.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Domain/DomainDetailsPage/DomainDetailsPage.component.tsx index ba25d58143af..b76ab6b25e19 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Domain/DomainDetailsPage/DomainDetailsPage.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Domain/DomainDetailsPage/DomainDetailsPage.component.tsx @@ -238,13 +238,14 @@ const DomainDetailsPage = ({ const fetchDataProducts = async () => { if (!isVersionsView) { try { + const encodedFqn = getEncodedFqn( + escapeESReservedCharacters(domain.fullyQualifiedName) + ); const res = await searchData( '', 1, 0, - `(domain.fullyQualifiedName:${escapeESReservedCharacters( - getEncodedFqn(domain.fullyQualifiedName ?? '') - )})`, + `(domain.fullyQualifiedName:"${encodedFqn}")`, '', '', SearchIndex.DATA_PRODUCT @@ -260,13 +261,14 @@ const DomainDetailsPage = ({ const fetchDomainAssets = async () => { if (fqn && !isVersionsView) { try { + const encodedFqn = getEncodedFqn( + escapeESReservedCharacters(domain.fullyQualifiedName) + ); const res = await searchData( '', 1, 0, - `(domain.fullyQualifiedName:"${escapeESReservedCharacters( - getEncodedFqn(domain.fullyQualifiedName ?? '') - )}") AND !(entityType:"dataProduct")`, + `(domain.fullyQualifiedName:"${encodedFqn}") AND !(entityType:"dataProduct")`, '', '', SearchIndex.ALL diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Domain/DomainTabs/DataProductsTab/DataProductsTab.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Domain/DomainTabs/DataProductsTab/DataProductsTab.component.tsx index c357839aa1cd..130c69e243bb 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Domain/DomainTabs/DataProductsTab/DataProductsTab.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Domain/DomainTabs/DataProductsTab/DataProductsTab.component.tsx @@ -29,7 +29,11 @@ import { SearchIndex } from '../../../../enums/search.enum'; import { DataProduct } from '../../../../generated/entity/domains/dataProduct'; import { searchData } from '../../../../rest/miscAPI'; import { formatDataProductResponse } from '../../../../utils/APIUtils'; -import { escapeESReservedCharacters } from '../../../../utils/StringsUtils'; +import { + escapeESReservedCharacters, + getDecodedFqn, + getEncodedFqn, +} from '../../../../utils/StringsUtils'; import ErrorPlaceHolder from '../../../common/ErrorWithPlaceholder/ErrorPlaceHolder'; import EntitySummaryPanel from '../../../Explore/EntitySummaryPanel/EntitySummaryPanel.component'; import ExploreSearchCard from '../../../ExploreV1/ExploreSearchCard/ExploreSearchCard'; @@ -55,13 +59,15 @@ const DataProductsTab = forwardRef( const fetchDataProducts = async () => { try { setLoading(true); + const decodedFqn = getDecodedFqn(domainFqn); + const encodedFqn = getEncodedFqn( + escapeESReservedCharacters(decodedFqn) + ); const res = await searchData( '', 1, PAGE_SIZE_LARGE, - `(domain.fullyQualifiedName:"${escapeESReservedCharacters( - domainFqn - )}")`, + `(domain.fullyQualifiedName:"${encodedFqn}")`, '', '', SearchIndex.DATA_PRODUCT diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryTerms/tabs/AssetsTabs.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryTerms/tabs/AssetsTabs.component.tsx index 65249fe57739..fb0c8cc267b5 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryTerms/tabs/AssetsTabs.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryTerms/tabs/AssetsTabs.component.tsx @@ -199,16 +199,13 @@ const AssetsTabs = forwardRef( ); const queryParam = useMemo(() => { + const encodedFqn = getEncodedFqn(escapeESReservedCharacters(entityFqn)); switch (type) { case AssetsOfEntity.DOMAIN: - return `(domain.fullyQualifiedName:"${escapeESReservedCharacters( - getEncodedFqn(entityFqn ?? '') - )}") AND !(entityType:"dataProduct")`; + return `(domain.fullyQualifiedName:"${encodedFqn}") AND !(entityType:"dataProduct")`; case AssetsOfEntity.DATA_PRODUCT: - return `(dataProducts.fullyQualifiedName:"${escapeESReservedCharacters( - getEncodedFqn(entityFqn ?? '') - )}")`; + return `(dataProducts.fullyQualifiedName:"${encodedFqn}")`; case AssetsOfEntity.TEAM: return `(owner.fullyQualifiedName:"${escapeESReservedCharacters( @@ -220,9 +217,7 @@ const AssetsTabs = forwardRef( return queryFilter ?? ''; default: - return `(tags.tagFQN:"${escapeESReservedCharacters( - getEncodedFqn(entityFqn ?? '') - )}")`; + return `(tags.tagFQN:"${encodedFqn}")`; } }, [type, fqn, entityFqn]); diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/de-de.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/de-de.json index ad130ee08104..125dd64b809d 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/de-de.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/de-de.json @@ -530,6 +530,7 @@ "invalid-condition": "Ungültige Bedingung", "invalid-name": "Ungültiger Name", "is-ready-for-preview": "ist bereit zur Vorschau", + "items-selected-lowercase": "items selected", "january": "Januar", "job-lowercase": "aufgabe", "join": "Beitreten", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/es-es.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/es-es.json index 6cce1e17c732..78b2caec8b01 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/es-es.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/es-es.json @@ -530,6 +530,7 @@ "invalid-condition": "Condición inválida", "invalid-name": "Nombre inválido", "is-ready-for-preview": "está listo para previsualización", + "items-selected-lowercase": "items selected", "january": "Enero", "job-lowercase": "trabajo", "join": "Unirse", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/fr-fr.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/fr-fr.json index 10dca5787464..9ace0494a17d 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/fr-fr.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/fr-fr.json @@ -530,6 +530,7 @@ "invalid-condition": "Condition Invalide", "invalid-name": "Nom Invalide", "is-ready-for-preview": "est prêt à être prévisualisé", + "items-selected-lowercase": "items selected", "january": "Janvier", "job-lowercase": "tâche", "join": "Rejoindre", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/ja-jp.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/ja-jp.json index 31283b627276..72658fc80a2a 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/ja-jp.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/ja-jp.json @@ -530,6 +530,7 @@ "invalid-condition": "不正な条件", "invalid-name": "不正な名称", "is-ready-for-preview": "はプレビューの準備ができました", + "items-selected-lowercase": "items selected", "january": "1月", "job-lowercase": "ジョブ", "join": "参加", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/pt-br.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/pt-br.json index 02046ae29070..01759b37211f 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/pt-br.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/pt-br.json @@ -530,6 +530,7 @@ "invalid-condition": "Condição Inválida", "invalid-name": "Nome Inválido", "is-ready-for-preview": "está pronto para visualização", + "items-selected-lowercase": "items selected", "january": "Janeiro", "job-lowercase": "trabalho", "join": "Juntar", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/ru-ru.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/ru-ru.json index d0fb596c3a12..c6129ffce0a3 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/ru-ru.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/ru-ru.json @@ -530,6 +530,7 @@ "invalid-condition": "Недопустимое условие", "invalid-name": "Неверное имя", "is-ready-for-preview": "готов к предварительному просмотру", + "items-selected-lowercase": "items selected", "january": "Январь", "job-lowercase": "job", "join": "Соединить", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/zh-cn.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/zh-cn.json index 99c78a331c88..a5e337388641 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/zh-cn.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/zh-cn.json @@ -530,6 +530,7 @@ "invalid-condition": "无效条件", "invalid-name": "无效名称", "is-ready-for-preview": "可预览", + "items-selected-lowercase": "items selected", "january": "一月", "job-lowercase": "作业", "join": "加入", diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/DomainUtils.tsx b/openmetadata-ui/src/main/resources/ui/src/utils/DomainUtils.tsx index 28c2671375ca..56e3fabe49b5 100644 --- a/openmetadata-ui/src/main/resources/ui/src/utils/DomainUtils.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/utils/DomainUtils.tsx @@ -33,7 +33,6 @@ import { getDiffByFieldName, getDiffValue, } from './EntityVersionUtils'; -import { getEncodedFqn } from './StringsUtils'; export const getOwner = ( hasPermission: boolean, @@ -165,7 +164,7 @@ export const getQueryFilterToExcludeDomainTerms = ( must_not: [ { term: { - 'domain.fullyQualifiedName': getEncodedFqn(fqn), + 'domain.fullyQualifiedName': fqn, }, }, ], From b8115ba9d0b5cefc8792c174b8ffebfaa9a37769 Mon Sep 17 00:00:00 2001 From: karanh37 Date: Tue, 5 Dec 2023 14:30:36 +0530 Subject: [PATCH 08/16] fix: css --- .../src/components/Glossary/GlossaryTerms/tabs/assets-tabs.less | 1 + 1 file changed, 1 insertion(+) diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryTerms/tabs/assets-tabs.less b/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryTerms/tabs/assets-tabs.less index b58a8d1c515f..e7655be5ed18 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryTerms/tabs/assets-tabs.less +++ b/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryTerms/tabs/assets-tabs.less @@ -90,6 +90,7 @@ color: @white !important; left: 24px; top: 22px; + width: 20px; } .ant-notification-notice-message { padding-right: 0 !important; From 1d52ee04d911089416d3565420ad1d756b82cc5f Mon Sep 17 00:00:00 2001 From: karanh37 Date: Tue, 5 Dec 2023 14:53:49 +0530 Subject: [PATCH 09/16] fix: add permissions check --- .../Glossary/GlossaryTerms/tabs/AssetsTabs.component.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryTerms/tabs/AssetsTabs.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryTerms/tabs/AssetsTabs.component.tsx index fb0c8cc267b5..8dde14a98ee8 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryTerms/tabs/AssetsTabs.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryTerms/tabs/AssetsTabs.component.tsx @@ -606,7 +606,7 @@ const AssetsTabs = forwardRef( handleSummaryPanelDisplay={setSelectedCard} id={_id} key={'assets_' + _id} - showCheckboxes={Boolean(activeEntity)} + showCheckboxes={Boolean(activeEntity) && permissions.Create} showTags={false} source={_source} onCheckboxChange={(selected) => @@ -671,7 +671,7 @@ const AssetsTabs = forwardRef( const assetsHeader = useMemo(() => { return (
- {activeEntity && data.length > 0 && ( + {activeEntity && permissions.Create && data.length > 0 && ( onSelectAll(e.target.checked)}> From 26a02d1e0208a1844b2e4bd3101eb6ae510f821b Mon Sep 17 00:00:00 2001 From: Chirag Madlani <12962843+chirag-madlani@users.noreply.github.com> Date: Tue, 5 Dec 2023 15:33:11 +0530 Subject: [PATCH 10/16] fix(ui): throw validation errors while modifying glossary term (#14236) * fix(ui): throw validation errors while modifying glossary term * update modal layout --- .../src/assets/svg/ic-exclamation-circle.svg | 2 +- .../tabs/GlossaryOverviewTab.component.tsx | 30 ++- ...ssaryUpdateConfirmationModal.interface.tsx | 21 ++ .../GlossaryUpdateConfirmationModal.tsx | 204 ++++++++++++++++++ .../ui/src/locale/languages/de-de.json | 1 + .../ui/src/locale/languages/en-us.json | 1 + .../ui/src/locale/languages/es-es.json | 1 + .../ui/src/locale/languages/fr-fr.json | 1 + .../ui/src/locale/languages/ja-jp.json | 1 + .../ui/src/locale/languages/pt-br.json | 1 + .../ui/src/locale/languages/ru-ru.json | 1 + .../ui/src/locale/languages/zh-cn.json | 1 + .../main/resources/ui/src/rest/glossaryAPI.ts | 18 +- .../main/resources/ui/src/utils/SvgUtils.tsx | 5 - 14 files changed, 271 insertions(+), 17 deletions(-) create mode 100644 openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryUpdateConfirmationModal/GlossaryUpdateConfirmationModal.interface.tsx create mode 100644 openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryUpdateConfirmationModal/GlossaryUpdateConfirmationModal.tsx diff --git a/openmetadata-ui/src/main/resources/ui/src/assets/svg/ic-exclamation-circle.svg b/openmetadata-ui/src/main/resources/ui/src/assets/svg/ic-exclamation-circle.svg index 63c869f07454..a5c29f194139 100644 --- a/openmetadata-ui/src/main/resources/ui/src/assets/svg/ic-exclamation-circle.svg +++ b/openmetadata-ui/src/main/resources/ui/src/assets/svg/ic-exclamation-circle.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryTerms/tabs/GlossaryOverviewTab.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryTerms/tabs/GlossaryOverviewTab.component.tsx index 30a0336e9527..0c37cda95ecd 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryTerms/tabs/GlossaryOverviewTab.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryTerms/tabs/GlossaryOverviewTab.component.tsx @@ -28,6 +28,7 @@ import { OperationPermission } from '../../../PermissionProvider/PermissionProvi import TagsContainerV2 from '../../../Tag/TagsContainerV2/TagsContainerV2'; import { DisplayType } from '../../../Tag/TagsViewer/TagsViewer.interface'; import GlossaryDetailsRightPanel from '../../GlossaryDetailsRightPanel/GlossaryDetailsRightPanel.component'; +import { GlossaryUpdateConfirmationModal } from '../../GlossaryUpdateConfirmationModal/GlossaryUpdateConfirmationModal'; import GlossaryTermReferences from './GlossaryTermReferences'; import GlossaryTermSynonyms from './GlossaryTermSynonyms'; import RelatedTerms from './RelatedTerms'; @@ -51,6 +52,7 @@ const GlossaryOverviewTab = ({ }: Props) => { const [isDescriptionEditable, setIsDescriptionEditable] = useState(false); + const [tagsUpdatating, setTagsUpdating] = useState(); const onDescriptionUpdate = async (updatedHTML: string) => { if (selectedData.description !== updatedHTML) { @@ -82,14 +84,7 @@ const GlossaryOverviewTab = ({ }, [selectedData, isVersionView]); const handleTagsUpdate = async (updatedTags: TagLabel[]) => { - if (updatedTags) { - const updatedData = { - ...selectedData, - tags: updatedTags, - }; - - onUpdate(updatedData); - } + setTagsUpdating(updatedTags); }; const tags = useMemo( @@ -103,6 +98,17 @@ const GlossaryOverviewTab = ({ [isVersionView, selectedData] ); + const handleGlossaryTagUpdateValidationConfirm = async () => { + if (selectedData) { + await onUpdate({ + ...selectedData, + tags: tagsUpdatating, + }); + + setTagsUpdating(undefined); + } + }; + return ( + {tagsUpdatating && ( + setTagsUpdating(undefined)} + onValidationSuccess={handleGlossaryTagUpdateValidationConfirm} + /> + )} ); }; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryUpdateConfirmationModal/GlossaryUpdateConfirmationModal.interface.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryUpdateConfirmationModal/GlossaryUpdateConfirmationModal.interface.tsx new file mode 100644 index 000000000000..ef578c85b533 --- /dev/null +++ b/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryUpdateConfirmationModal/GlossaryUpdateConfirmationModal.interface.tsx @@ -0,0 +1,21 @@ +/* + * Copyright 2023 Collate. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { GlossaryTerm } from '../../../generated/entity/data/glossaryTerm'; +import { TagLabel } from '../../../generated/entity/data/table'; + +export interface GlossaryUpdateConfirmationModalProps { + glossaryTerm: GlossaryTerm; + onValidationSuccess: (() => void) | (() => Promise); + onCancel: () => void; + updatedTags: TagLabel[]; +} diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryUpdateConfirmationModal/GlossaryUpdateConfirmationModal.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryUpdateConfirmationModal/GlossaryUpdateConfirmationModal.tsx new file mode 100644 index 000000000000..e3abb05e29b2 --- /dev/null +++ b/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryUpdateConfirmationModal/GlossaryUpdateConfirmationModal.tsx @@ -0,0 +1,204 @@ +/* + * Copyright 2023 Collate. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* eslint-disable i18next/no-literal-string */ +import Icon from '@ant-design/icons'; +import { Button, Modal, Space, Typography } from 'antd'; +import { AxiosError } from 'axios'; +import { isEmpty } from 'lodash'; +import React, { useState } from 'react'; +import { useTranslation } from 'react-i18next'; +import { Link } from 'react-router-dom'; +import { ReactComponent as ExclamationIcon } from '../../../assets/svg/ic-exclamation-circle.svg'; +import { ClientErrors } from '../../../enums/axios.enum'; +import { EntityType } from '../../../enums/entity.enum'; +import { SearchIndex } from '../../../enums/search.enum'; +import { GlossaryTerm } from '../../../generated/entity/data/glossaryTerm'; +import { EntityReference } from '../../../generated/entity/type'; +import { Status } from '../../../generated/type/bulkOperationResult'; +import { + addAssetsToGlossaryTerm, + GlossaryTermFailure, +} from '../../../rest/glossaryAPI'; +import { searchData } from '../../../rest/miscAPI'; +import { + getEntityLinkFromType, + getEntityName, +} from '../../../utils/EntityUtils'; +import { escapeESReservedCharacters } from '../../../utils/StringsUtils'; +import Table from '../../common/Table/Table'; +import { GlossaryUpdateConfirmationModalProps } from './GlossaryUpdateConfirmationModal.interface'; + +export const GlossaryUpdateConfirmationModal = ({ + glossaryTerm, + onValidationSuccess, + onCancel, + updatedTags, +}: GlossaryUpdateConfirmationModalProps) => { + const [failedStatus, setFailedStatus] = useState(); + const [tagError, setTagError] = useState<{ code: number; message: string }>(); + const [tagAdditionConfirmation, setTagAdditionConfirmation] = useState(false); + const [validating, setValidating] = useState(false); + const { t } = useTranslation(); + + const handleUpdateConfirmation = async () => { + setTagAdditionConfirmation(true); + setValidating(true); + try { + const { data } = await searchData( + '', + 1, + 1000, + `(tags.tagFQN:"${escapeESReservedCharacters( + glossaryTerm.fullyQualifiedName + )}")`, + '', + '', + SearchIndex.ALL + ); + + const assets = data.hits.hits.map(({ _source: { id, entityType } }) => ({ + id, + type: entityType, + })); + + // dryRun validations so that we can list failures if any + const res = await addAssetsToGlossaryTerm( + { ...glossaryTerm, tags: updatedTags } as GlossaryTerm, + assets, + true + ); + + if ( + res.status && + (res as GlossaryTermFailure).status === Status.Success + ) { + await onValidationSuccess(); + } else { + setFailedStatus(res as GlossaryTermFailure); + } + } catch (err) { + // error + setTagError((err as AxiosError).response?.data); + } finally { + setValidating(false); + } + }; + + return ( + + + {failedStatus?.numberOfRowsFailed && + `${failedStatus.numberOfRowsFailed} failed`} + + + + + + +
+ ) + } + title={tagAdditionConfirmation ? 'Following entities failed' : undefined} + width={tagAdditionConfirmation ? 750 : undefined} + onCancel={onCancel}> + {tagAdditionConfirmation ? ( +
+ {!isEmpty(failedStatus?.failedRequest) && !validating && ( + <> + ( + + {getEntityName(record)} + + ), + }, + { + title: t('label.failure-reason'), + dataIndex: 'error', + key: 'error', + render: (error: string) => ( + {error} + ), + }, + ]} + dataSource={failedStatus?.failedRequest} + loading={validating} + pagination={false} + rowKey={(record) => record.request.id} + /> + + + You can either remove this assets or remove conflicting tag from + the asset and try again adding tags! + + + )} + {tagError?.code === ClientErrors.BAD_REQUEST && ( + {tagError.message} + )} + + ) : ( +
+ + + + Would you like to proceed with adding a new tag? + + + This action will apply the tag to all Assets linked to the Glossary + Term{' '} + {getEntityName(glossaryTerm)} + +
+ + + + +
+
+ )} + + ); +}; diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/de-de.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/de-de.json index d9d72b6a8e24..06d18ad2768a 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/de-de.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/de-de.json @@ -414,6 +414,7 @@ "extend-open-meta-data": "Extend OpenMetadata", "failed": "Fehlgeschlagen", "failure-context": "Fehlerkontext", + "failure-reason": "Failure Reason", "favicon-url": "Favicon URL", "feature": "Funktion", "feature-lowercase": "funktion", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/en-us.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/en-us.json index 4552ff17f551..26f96baf9755 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/en-us.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/en-us.json @@ -414,6 +414,7 @@ "extend-open-meta-data": "Extend OpenMetadata", "failed": "Failed", "failure-context": "Failure Context", + "failure-reason": "Failure Reason", "favicon-url": "Favicon URL", "feature": "Feature", "feature-lowercase": "feature", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/es-es.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/es-es.json index eb39dc46555b..af7a0a4ed713 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/es-es.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/es-es.json @@ -414,6 +414,7 @@ "extend-open-meta-data": "Extend OpenMetadata", "failed": "Falló", "failure-context": "Contexto del error", + "failure-reason": "Failure Reason", "favicon-url": "Favicon URL", "feature": "Funcionalidad", "feature-lowercase": "funcionalidad", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/fr-fr.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/fr-fr.json index 03690ac179a4..de2cd5cac6d8 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/fr-fr.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/fr-fr.json @@ -414,6 +414,7 @@ "extend-open-meta-data": "Extend OpenMetadata", "failed": "Échec", "failure-context": "Contexte de l'Échec", + "failure-reason": "Failure Reason", "favicon-url": "Favicon URL", "feature": "Fonctionnalité", "feature-lowercase": "fonctionnalité", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/ja-jp.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/ja-jp.json index 2c44594ce850..956c3aa3a85f 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/ja-jp.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/ja-jp.json @@ -414,6 +414,7 @@ "extend-open-meta-data": "Extend OpenMetadata", "failed": "失敗", "failure-context": "Failure Context", + "failure-reason": "Failure Reason", "favicon-url": "Favicon URL", "feature": "Feature", "feature-lowercase": "feature", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/pt-br.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/pt-br.json index 422c16fb574d..903c411522f6 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/pt-br.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/pt-br.json @@ -414,6 +414,7 @@ "extend-open-meta-data": "Expandir OpenMetadata", "failed": "Falhou", "failure-context": "Contexto de Falha", + "failure-reason": "Failure Reason", "favicon-url": "URL do Favicon", "feature": "Recurso", "feature-lowercase": "recurso", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/ru-ru.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/ru-ru.json index 2ad58af5352e..6ff7d07e16b6 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/ru-ru.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/ru-ru.json @@ -414,6 +414,7 @@ "extend-open-meta-data": "Extend OpenMetadata", "failed": "Неуспешно", "failure-context": "Контекст отказа", + "failure-reason": "Failure Reason", "favicon-url": "Favicon URL", "feature": "Свойство", "feature-lowercase": "свойство", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/zh-cn.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/zh-cn.json index 8a21dff9da13..e04e00e6dd36 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/zh-cn.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/zh-cn.json @@ -414,6 +414,7 @@ "extend-open-meta-data": "Extend OpenMetadata", "failed": "失败", "failure-context": "失败上下文", + "failure-reason": "Failure Reason", "favicon-url": "Favicon URL", "feature": "特点", "feature-lowercase": "特点", diff --git a/openmetadata-ui/src/main/resources/ui/src/rest/glossaryAPI.ts b/openmetadata-ui/src/main/resources/ui/src/rest/glossaryAPI.ts index f78c93c5c18a..460e3b31f5f9 100644 --- a/openmetadata-ui/src/main/resources/ui/src/rest/glossaryAPI.ts +++ b/openmetadata-ui/src/main/resources/ui/src/rest/glossaryAPI.ts @@ -20,6 +20,7 @@ import { CreateGlossary } from '../generated/api/data/createGlossary'; import { CreateGlossaryTerm } from '../generated/api/data/createGlossaryTerm'; import { EntityReference, Glossary } from '../generated/entity/data/glossary'; import { GlossaryTerm } from '../generated/entity/data/glossaryTerm'; +import { Status } from '../generated/type/bulkOperationResult'; import { CSVImportResult } from '../generated/type/csvImportResult'; import { EntityHistory } from '../generated/type/entityHistory'; import { ListParams } from '../interface/API.interface'; @@ -251,19 +252,30 @@ export const updateGlossaryTermVotes = async ( return response.data; }; +export interface GlossaryTermFailure { + dryRun: boolean; + status: Status; + numberOfRowsProcessed: number; + numberOfRowsPassed: number; + numberOfRowsFailed: number; + successRequest: Array; + failedRequest: Array<{ request: EntityReference; error: string }>; +} + export const addAssetsToGlossaryTerm = async ( glossaryTerm: GlossaryTerm, - assets: EntityReference[] + assets: EntityReference[], + dryRun = false ) => { const data = { assets: assets, - dryRun: false, + dryRun: dryRun, glossaryTags: glossaryTerm.tags ?? [], }; const response = await APIClient.put< AddGlossaryToAssetsRequest, - AxiosResponse + AxiosResponse >(`/glossaryTerms/${glossaryTerm.id}/assets/add`, data); return response.data; diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/SvgUtils.tsx b/openmetadata-ui/src/main/resources/ui/src/utils/SvgUtils.tsx index f78651f7e5bb..805085963f81 100644 --- a/openmetadata-ui/src/main/resources/ui/src/utils/SvgUtils.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/utils/SvgUtils.tsx @@ -85,7 +85,6 @@ import IconDownArrow from '../assets/svg/ic-down-arrow.svg'; import IconEditLineageColor from '../assets/svg/ic-edit-lineage-colored.svg'; import IconEditLineage from '../assets/svg/ic-edit-lineage.svg'; import IconEdit from '../assets/svg/ic-edit.svg'; -import IconExclamationCircle from '../assets/svg/ic-exclamation-circle.svg'; import IconExplore from '../assets/svg/ic-explore.svg'; import IconFeed from '../assets/svg/ic-feed.svg'; import IconFilter from '../assets/svg/ic-filter.svg'; @@ -249,7 +248,6 @@ export const Icons = { GROWTH_ARROW: 'icon-growth-arrow', LOSS_ARROW: 'icon-loss-arrow', CHECK_CIRCLE: 'icon-check-circle', - EXCLAMATION_CIRCLE: 'icon-exclamation-circle', TIMES_CIRCLE: 'icon-times-circle', HELP_CIRCLE: 'icon-help-circle', FILTERS: 'icon-filters', @@ -586,10 +584,7 @@ const SVGIcons: FunctionComponent = ({ icon, ...props }: Props) => { IconComponent = IconCheckCircle; break; - case Icons.EXCLAMATION_CIRCLE: - IconComponent = IconExclamationCircle; - break; case Icons.TIMES_CIRCLE: IconComponent = IconTimesCircle; From 3d46d1023d898a337907767cc813234ec43ab6ba Mon Sep 17 00:00:00 2001 From: karanh37 Date: Tue, 5 Dec 2023 16:53:38 +0530 Subject: [PATCH 11/16] fix: domain cypress --- .../ui/cypress/common/DomainUtils.js | 31 +++++------- .../ui/cypress/constants/constants.js | 48 +++++++++++++++++++ .../ui/cypress/e2e/Pages/Domains.spec.js | 20 +++++++- .../tabs/AssetsTabs.component.tsx | 17 +++---- 4 files changed, 87 insertions(+), 29 deletions(-) diff --git a/openmetadata-ui/src/main/resources/ui/cypress/common/DomainUtils.js b/openmetadata-ui/src/main/resources/ui/cypress/common/DomainUtils.js index 14e31cfe1834..269d4ca73d04 100644 --- a/openmetadata-ui/src/main/resources/ui/cypress/common/DomainUtils.js +++ b/openmetadata-ui/src/main/resources/ui/cypress/common/DomainUtils.js @@ -436,14 +436,8 @@ export const removeAssetsFromDomain = (domainObj) => { domainObj.assets.forEach((asset, index) => { interceptURL('GET', '/api/v1/search/query*', 'searchAssets'); - cy.get( - `[data-testid="table-data-card_${asset.fullyQualifiedName}"]` - ).within(() => { - cy.get('.explore-card-actions').invoke('show'); - cy.get('.explore-card-actions').within(() => { - cy.get('[data-testid="delete-tag"]').click(); - }); - }); + cy.get(`[data-testid="manage-button-${asset.fullyQualifiedName}"]`).click(); + cy.get('[data-testid="delete-button"]').click(); cy.get("[data-testid='save-button']").click(); @@ -460,9 +454,13 @@ export const removeAssetsFromDomain = (domainObj) => { export const addAssetsToDataProduct = (dataProductObj, domainObj) => { interceptURL('GET', `/api/v1/search/query**`, 'getDataProductAssets'); + interceptURL('GET', '/api/v1/dataProducts/**', 'getDataProductDetails'); goToDataProductsTab(domainObj); - cy.get(`[data-testid="explore-card-${dataProductObj.name}"]`).click(); + cy.get( + `[data-testid="explore-card-${dataProductObj.name}"] [data-testid="entity-link"]` + ).click(); + verifyResponseStatusCode('@getDataProductDetails', 200); cy.get('[data-testid="assets"]').should('be.visible').click(); cy.get('.ant-tabs-tab-active').contains('Assets').should('be.visible'); @@ -501,7 +499,9 @@ export const addAssetsToDataProduct = (dataProductObj, domainObj) => { export const removeAssetsFromDataProduct = (dataProductObj, domainObj) => { goToDataProductsTab(domainObj); - cy.get(`[data-testid="explore-card-${dataProductObj.name}"]`).click(); + cy.get( + `[data-testid="explore-card-${dataProductObj.name}"] [data-testid="entity-link"]` + ).click(); cy.get('[data-testid="assets"]').should('be.visible').click(); cy.get('.ant-tabs-tab-active').contains('Assets').should('be.visible'); @@ -510,15 +510,8 @@ export const removeAssetsFromDataProduct = (dataProductObj, domainObj) => { dataProductObj.assets.forEach((asset, index) => { interceptURL('GET', '/api/v1/search/query*', 'searchAssets'); - - cy.get( - `[data-testid="table-data-card_${asset.fullyQualifiedName}"]` - ).within(() => { - cy.get('.explore-card-actions').invoke('show'); - cy.get('.explore-card-actions').within(() => { - cy.get('[data-testid="delete-tag"]').click(); - }); - }); + cy.get(`[data-testid="manage-button-${asset.fullyQualifiedName}"]`).click(); + cy.get('[data-testid="delete-button"]').click(); cy.get("[data-testid='save-button']").click(); diff --git a/openmetadata-ui/src/main/resources/ui/cypress/constants/constants.js b/openmetadata-ui/src/main/resources/ui/cypress/constants/constants.js index 3c62dde6f00c..841f25dc93e7 100644 --- a/openmetadata-ui/src/main/resources/ui/cypress/constants/constants.js +++ b/openmetadata-ui/src/main/resources/ui/cypress/constants/constants.js @@ -613,3 +613,51 @@ export const DOMAIN_2 = { }, ], }; + +export const DOMAIN_3 = { + name: 'Cypress Space', + updatedName: 'Cypress Space', + updatedDisplayName: 'Cypress Space', + fullyQualifiedName: 'Cypress Space', + description: 'This is the Cypress for testing domain with space creation', + experts: 'Alex Pollard', + owner: 'Alex Pollard', + domainType: 'Source-aligned', + dataProducts: [ + { + name: 'Cypress%PercentDP', + description: + 'This is the data product description for Cypress DataProduct Assets', + experts: 'Aaron Johnson', + owner: 'Aaron Johnson', + assets: [ + { + name: 'forecast_sales_performance', + fullyQualifiedName: 'sample_superset.forecast_sales_performance', + }, + { + name: 'eta_predictions', + fullyQualifiedName: 'mlflow_svc.eta_predictions', + }, + { + name: 'operations_view', + fullyQualifiedName: 'sample_looker.model.operations_view', + }, + ], + }, + ], + assets: [ + { + name: 'forecast_sales_performance', + fullyQualifiedName: 'sample_superset.forecast_sales_performance', + }, + { + name: 'eta_predictions', + fullyQualifiedName: 'mlflow_svc.eta_predictions', + }, + { + name: 'operations_view', + fullyQualifiedName: 'sample_looker.model.operations_view', + }, + ], +}; diff --git a/openmetadata-ui/src/main/resources/ui/cypress/e2e/Pages/Domains.spec.js b/openmetadata-ui/src/main/resources/ui/cypress/e2e/Pages/Domains.spec.js index 48a23ce5eacb..ab318d57662f 100644 --- a/openmetadata-ui/src/main/resources/ui/cypress/e2e/Pages/Domains.spec.js +++ b/openmetadata-ui/src/main/resources/ui/cypress/e2e/Pages/Domains.spec.js @@ -27,7 +27,7 @@ import { updateDomainDetails, verifyDomain, } from '../../common/DomainUtils'; -import { DOMAIN_1, DOMAIN_2 } from '../../constants/constants'; +import { DOMAIN_1, DOMAIN_2, DOMAIN_3 } from '../../constants/constants'; describe('Domain page should work properly', () => { beforeEach(() => { @@ -52,6 +52,11 @@ describe('Domain page should work properly', () => { addAssetsToDomain(DOMAIN_2); }); + it('Add assets to domain having space using asset selection modal should work properly', () => { + createDomain(DOMAIN_3, false); + addAssetsToDomain(DOMAIN_3); + }); + it('Create new data product should work properly', () => { DOMAIN_1.dataProducts.forEach((dataProduct) => { createDataProducts(dataProduct, DOMAIN_1); @@ -72,6 +77,17 @@ describe('Domain page should work properly', () => { addAssetsToDataProduct(DOMAIN_2.dataProducts[0], DOMAIN_2); }); + it('Add data product assets using asset selection modal with separate domain and dp having space', () => { + DOMAIN_3.dataProducts.forEach((dp) => { + createDataProducts(dp, DOMAIN_3); + cy.get('[data-testid="app-bar-item-domain"]') + .should('be.visible') + .click({ force: true }); + }); + + addAssetsToDataProduct(DOMAIN_3.dataProducts[0], DOMAIN_3); + }); + it('Remove data product assets using asset selection modal should work properly', () => { removeAssetsFromDataProduct(DOMAIN_2.dataProducts[0], DOMAIN_2); }); @@ -97,7 +113,7 @@ describe('Domain page should work properly', () => { }); it('Delete domain flow should work properly', () => { - [DOMAIN_1, DOMAIN_2].forEach((domain) => { + [DOMAIN_1, DOMAIN_2, DOMAIN_3].forEach((domain) => { deleteDomain(domain); }); }); diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryTerms/tabs/AssetsTabs.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryTerms/tabs/AssetsTabs.component.tsx index 8dde14a98ee8..a789c30e40c7 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryTerms/tabs/AssetsTabs.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryTerms/tabs/AssetsTabs.component.tsx @@ -182,6 +182,12 @@ const AssetsTabs = forwardRef( >([]); const [filters, setFilters] = useState([]); const [searchValue, setSearchValue] = useState(''); + const entityTypeString = + type === AssetsOfEntity.GLOSSARY + ? t('label.glossary-term-lowercase') + : type === AssetsOfEntity.DOMAIN + ? t('label.domain-lowercase') + : t('label.data-product-lowercase'); const handleMenuClick = ({ key }: { key: string }) => { setSelectedFilter((prevSelected) => [...prevSelected, key]); @@ -256,7 +262,7 @@ const AssetsTabs = forwardRef( handlePagingChange({ total: res.hits.total.value ?? 0 }); setData(hits); setAggregations(getAggregations(res?.aggregations)); - hits[0] && setSelectedCard(hits[0]._source as SourceType); + hits[0] && setSelectedCard(hits[0]._source); } catch (_) { // Nothing here } finally { @@ -343,12 +349,7 @@ const AssetsTabs = forwardRef( label: ( } id="delete-button" @@ -590,7 +591,7 @@ const AssetsTabs = forwardRef( trigger={['click']}> - - + ) } - title={tagAdditionConfirmation ? 'Following entities failed' : undefined} + title={ + tagAdditionConfirmation + ? t('message.glossary-tag-update-modal-title') + : undefined + } width={tagAdditionConfirmation ? 750 : undefined} onCancel={onCancel}> - {tagAdditionConfirmation ? ( + {tagAdditionConfirmation || validating ? (
{!isEmpty(failedStatus?.failedRequest) && !validating && ( <>
( - - {getEntityName(record)} - - ), - }, - { - title: t('label.failure-reason'), - dataIndex: 'error', - key: 'error', - render: (error: string) => ( - {error} - ), - }, - ]} + columns={tagsColumn} dataSource={failedStatus?.failedRequest} loading={validating} pagination={false} rowKey={(record) => record.request.id} /> - - You can either remove this assets or remove conflicting tag from - the asset and try again adding tags! + {t('message.glossary-tag-assignement-help-message')} )} @@ -171,29 +145,27 @@ export const GlossaryUpdateConfirmationModal = ({ )} ) : ( -
+
- - Would you like to proceed with adding a new tag? + {t('message.tag-update-confirmation')} - This action will apply the tag to all Assets linked to the Glossary - Term{' '} + {t('message.glossary-tag-update-description')}{' '} {getEntityName(glossaryTerm)} -
+
- +
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Tag/TagsV1/TagsV1.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Tag/TagsV1/TagsV1.component.tsx index ff3df3f2ee77..a7ed2805b691 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Tag/TagsV1/TagsV1.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Tag/TagsV1/TagsV1.component.tsx @@ -14,19 +14,18 @@ import { Tag, Tooltip, Typography } from 'antd'; import classNames from 'classnames'; import React, { useCallback, useMemo } from 'react'; import { useHistory } from 'react-router-dom'; -import { FQN_SEPARATOR_CHAR } from '../../../constants/char.constants'; -import { ROUTES } from '../../../constants/constants'; -import { TagSource } from '../../../generated/type/tagLabel'; -import { getTagDisplay, getTagTooltip } from '../../../utils/TagsUtils'; - import { ReactComponent as IconTerm } from '../../../assets/svg/book.svg'; import { ReactComponent as IconTag } from '../../../assets/svg/classification.svg'; import { ReactComponent as PlusIcon } from '../../../assets/svg/plus-primary.svg'; +import { FQN_SEPARATOR_CHAR } from '../../../constants/char.constants'; +import { ROUTES } from '../../../constants/constants'; import { TAG_START_WITH } from '../../../constants/Tag.constants'; +import { TagSource } from '../../../generated/type/tagLabel'; import { reduceColorOpacity } from '../../../utils/CommonUtils'; import { getEntityName } from '../../../utils/EntityUtils'; import Fqn from '../../../utils/Fqn'; import { getEncodedFqn } from '../../../utils/StringsUtils'; +import { getTagDisplay, getTagTooltip } from '../../../utils/TagsUtils'; import { TagsV1Props } from './TagsV1.interface'; import './tagsV1.less'; @@ -37,6 +36,7 @@ const TagsV1 = ({ showOnlyName = false, isVersionPage = false, tagProps, + tooltipOverride, }: TagsV1Props) => { const history = useHistory(); const color = useMemo( @@ -153,7 +153,7 @@ const TagsV1 = ({ const addTagChip = useMemo( () => ( }> {tagChip} diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Tag/TagsV1/TagsV1.interface.ts b/openmetadata-ui/src/main/resources/ui/src/components/Tag/TagsV1/TagsV1.interface.ts index 407ef4520875..cee867a81bee 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Tag/TagsV1/TagsV1.interface.ts +++ b/openmetadata-ui/src/main/resources/ui/src/components/Tag/TagsV1/TagsV1.interface.ts @@ -22,4 +22,6 @@ export type TagsV1Props = { className?: string; isVersionPage?: boolean; tagProps?: TagProps; + disabled?: boolean; + tooltipOverride?: string; }; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Tag/TagsViewer/TagsViewer.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Tag/TagsViewer/TagsViewer.tsx index bdbafd0e9121..25544141c980 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Tag/TagsViewer/TagsViewer.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Tag/TagsViewer/TagsViewer.tsx @@ -46,6 +46,7 @@ const TagsViewer: FunctionComponent = ({ { 'diff-removed': tag?.removed } )} isVersionPage={tag?.added || tag?.removed} + key={tag.name} showOnlyName={tag.source === TagSource.Glossary} startWith={TAG_START_WITH.SOURCE_ICON} tag={tag} diff --git a/openmetadata-ui/src/main/resources/ui/src/components/common/OwnerLabel/OwnerLabel.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/common/OwnerLabel/OwnerLabel.component.tsx index 42bb97740d92..663668fda30c 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/common/OwnerLabel/OwnerLabel.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/common/OwnerLabel/OwnerLabel.component.tsx @@ -63,14 +63,16 @@ export const OwnerLabel = ({ style={{ fontSize: '18px' }} /> ) : ( - +
+ +
); }, [owner]); diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/de-de.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/de-de.json index 06d18ad2768a..1b5f27a6b1b2 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/de-de.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/de-de.json @@ -662,6 +662,7 @@ "new-test-suite": "Neue Test-Suite", "next": "Nächster", "no": "Nein", + "no-comma-cancel": "No, cancel", "no-data-asset-found-for": "Keine Datenanlagen gefunden für", "no-data-found": "Keine Daten gefunden", "no-description": "Keine Beschreibung", @@ -1163,6 +1164,7 @@ "widget-lowercase": "widget", "workflow-plural": "Workflows", "yes": "Ja", + "yes-comma-confirm": "Yes, confirm", "yesterday": "Gestern", "your-entity": "Ihre {{entity}}" }, @@ -1359,6 +1361,9 @@ "find-in-table": "In Tabelle suchen", "fosters-collaboration-among-producers-and-consumers": "Setzen Sie Unternehmensziele und KPIs, um die Datenkultur Ihres Unternehmens proaktiv voranzutreiben. Fördern Sie eine Kultur kontinuierlicher Verbesserung mit rechtzeitigen Berichten zur Überwachung der Datenqualität.", "get-started-with-open-metadata": "Erste Schritte mit OpenMetadata", + "glossary-tag-assignement-help-message": "You can either remove this assets or remove conflicting tag from the asset and try again adding tags!", + "glossary-tag-update-description": "This action will apply the tag to all Assets linked to the Glossary Term", + "glossary-tag-update-modal-title": "Validation failed for following assets", "glossary-term-description": "Jeder Begriff im Glossar hat eine eindeutige Definition. Neben der Definition des Standardbegriffs für ein Konzept können auch Synonyme sowie verwandte Begriffe (z. B. übergeordnete und untergeordnete Begriffe) angegeben werden. Es können Referenzen zu den Assets hinzugefügt werden, die sich auf die Begriffe beziehen. Neue Begriffe können dem Glossar hinzugefügt oder aktualisiert werden. Die Glossarbegriffe können von bestimmten Benutzern überprüft werden, die die Begriffe akzeptieren oder ablehnen können.", "glossary-term-status": "Glossary Term was {{status}}.", "go-back-to-login-page": "Zurück zur Anmeldeseite", @@ -1577,6 +1582,7 @@ "successfully-completed-the-tour": "Sie haben die Tour erfolgreich abgeschlossen.", "synonym-placeholder": "Um ein Synonym hinzuzufügen, geben Sie es einfach ein und drücken Sie Enter", "system-tag-delete-disable-message": "Das Löschen von systemgenerierten Tags ist nicht zulässig. Sie können versuchen, den Tag stattdessen zu deaktivieren.", + "tag-update-confirmation": "Would you like to proceed with adding a new tag?", "take-quick-product-tour": "Machen Sie eine Produkttour, um loszulegen!", "team-moved-success": "Team erfolgreich verschoben!", "team-no-asset": "Ihr Team hat keine Vermögenswerte.", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/en-us.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/en-us.json index 26f96baf9755..bc8399c132ec 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/en-us.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/en-us.json @@ -662,6 +662,7 @@ "new-test-suite": "New Test Suite", "next": "Next", "no": "No", + "no-comma-cancel": "No, cancel", "no-data-asset-found-for": "No data assets found for", "no-data-found": "No data found", "no-description": "No description", @@ -1163,6 +1164,7 @@ "widget-lowercase": "widget", "workflow-plural": "Workflows", "yes": "Yes", + "yes-comma-confirm": "Yes, confirm", "yesterday": "Yesterday", "your-entity": "Your {{entity}}" }, @@ -1359,6 +1361,9 @@ "find-in-table": "Find in table", "fosters-collaboration-among-producers-and-consumers": "Set organizational goals and KPIs to proactively drive the data culture of your company. Foster a culture of continuous improvement with timely reports to monitor data health.", "get-started-with-open-metadata": "Get started with OpenMetadata", + "glossary-tag-assignement-help-message": "You can either remove this assets or remove conflicting tag from the asset and try again adding tags!", + "glossary-tag-update-description": "This action will apply the tag to all Assets linked to the Glossary Term", + "glossary-tag-update-modal-title": "Validation failed for following assets", "glossary-term-description": "Every term in the glossary has a unique definition. Along with defining the standard term for a concept, the synonyms as well as related terms (for e.g., parent and child terms) can be specified. References can be added to the assets related to the terms. New terms can be added or updated to the Glossary. The glossary terms can be reviewed by certain users, who can accept or reject the terms.", "glossary-term-status": "Glossary Term was {{status}}.", "go-back-to-login-page": "Go back to Login page", @@ -1577,6 +1582,7 @@ "successfully-completed-the-tour": "You’ve successfully completed the tour.", "synonym-placeholder": "To add a synonym, simply type it in and press Enter", "system-tag-delete-disable-message": "Deleting a system generated tags is not allowed. You can try disabling the tag instead.", + "tag-update-confirmation": "Would you like to proceed with adding a new tag?", "take-quick-product-tour": "Take a product tour to get started!", "team-moved-success": "Team moved successfully!", "team-no-asset": "Your team does not have any assets.", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/es-es.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/es-es.json index af7a0a4ed713..30435e18636f 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/es-es.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/es-es.json @@ -662,6 +662,7 @@ "new-test-suite": "Nueva Suite de Pruebas", "next": "Siguiente", "no": "No", + "no-comma-cancel": "No, cancel", "no-data-asset-found-for": "No data assets found for", "no-data-found": "No se encontraron datos", "no-description": "Sin descripción", @@ -1163,6 +1164,7 @@ "widget-lowercase": "widget", "workflow-plural": "Workflows", "yes": "Sí", + "yes-comma-confirm": "Yes, confirm", "yesterday": "Yesterday", "your-entity": "Tu {{entity}}" }, @@ -1359,6 +1361,9 @@ "find-in-table": "Buscar en la tabla", "fosters-collaboration-among-producers-and-consumers": "Fomenta la colaboración entre los productores y consumidores de datos.", "get-started-with-open-metadata": "Empezar con OpenMetadata", + "glossary-tag-assignement-help-message": "You can either remove this assets or remove conflicting tag from the asset and try again adding tags!", + "glossary-tag-update-description": "This action will apply the tag to all Assets linked to the Glossary Term", + "glossary-tag-update-modal-title": "Validation failed for following assets", "glossary-term-description": "Cada término en el glosario tiene una definición única. Además de definir el término estándar para un concepto, se pueden especificar sinónimos y términos relacionados (por ejemplo, términos padre e hijo). Se pueden agregar referencias a los activos relacionados con los términos. Se pueden agregar o actualizar nuevos términos al glosario. Los términos del glosario pueden ser revisados por ciertos usuarios, quienes pueden aceptar o rechazar los términos.", "glossary-term-status": "Glossary Term was {{status}}.", "go-back-to-login-page": "Volver a la página de inicio de sesión", @@ -1577,6 +1582,7 @@ "successfully-completed-the-tour": "Has completado el recorrido con éxito.", "synonym-placeholder": "To add a synonym, simply type it in and press Enter", "system-tag-delete-disable-message": "Deleting a system generated tags is not allowed. You can try disabling the tag instead.", + "tag-update-confirmation": "Would you like to proceed with adding a new tag?", "take-quick-product-tour": "Take a product tour to get started!", "team-moved-success": "¡Equipo movido con éxito!", "team-no-asset": "Tu equipo no tiene ningún activo.", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/fr-fr.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/fr-fr.json index de2cd5cac6d8..b9cba7a79f13 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/fr-fr.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/fr-fr.json @@ -662,6 +662,7 @@ "new-test-suite": "Nouvel Ensemble de Tests", "next": "Suivant", "no": "Non", + "no-comma-cancel": "No, cancel", "no-data-asset-found-for": "Aucun actif de données trouvé pour", "no-data-found": "Aucune donnée trouvée", "no-description": "Aucune description", @@ -1163,6 +1164,7 @@ "widget-lowercase": "widget", "workflow-plural": "Workflows", "yes": "Oui", + "yes-comma-confirm": "Yes, confirm", "yesterday": "Hier", "your-entity": "Votre {{entity}}" }, @@ -1359,6 +1361,9 @@ "find-in-table": "Trouver dans la table", "fosters-collaboration-among-producers-and-consumers": "Encouragez la collaborations entre les consommateurs et producteurs de données", "get-started-with-open-metadata": "Commencez votre Journée avec OpenMetadata", + "glossary-tag-assignement-help-message": "You can either remove this assets or remove conflicting tag from the asset and try again adding tags!", + "glossary-tag-update-description": "This action will apply the tag to all Assets linked to the Glossary Term", + "glossary-tag-update-modal-title": "Validation failed for following assets", "glossary-term-description": "Chaque terme du glossaire a une définition unique. En plus de définir le terme standard pour un concept, les synonymes ainsi que les termes associés (par exemple, les termes parent et enfant) peuvent être spécifiés. Des références peuvent être ajoutées aux actifs liés aux termes. De nouveaux termes peuvent être ajoutés ou mis à jour dans le glossaire. Les termes du glossaire peuvent être examinés par certains utilisateurs, qui peuvent accepter ou rejeter les termes.", "glossary-term-status": "Glossary Term was {{status}}.", "go-back-to-login-page": "Retour à la page d'accueil", @@ -1577,6 +1582,7 @@ "successfully-completed-the-tour": "Vous avez fini la visite avec succès.", "synonym-placeholder": "Pour ajouter un synonyme, tapez-le et appuyez sur Entrée", "system-tag-delete-disable-message": "Deleting a system generated tags is not allowed. You can try disabling the tag instead.", + "tag-update-confirmation": "Would you like to proceed with adding a new tag?", "take-quick-product-tour": "Faites une visite guidée pour vous lancer!", "team-moved-success": "L'équipe a été déplacée avec succès !", "team-no-asset": "Votre équipe n'a pas de ressources de données", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/ja-jp.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/ja-jp.json index 956c3aa3a85f..c0711f3ab651 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/ja-jp.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/ja-jp.json @@ -662,6 +662,7 @@ "new-test-suite": "新しいテストスイート", "next": "次へ", "no": "いいえ", + "no-comma-cancel": "No, cancel", "no-data-asset-found-for": "No data assets found for", "no-data-found": "データが見つかりません", "no-description": "説明がありません", @@ -1163,6 +1164,7 @@ "widget-lowercase": "widget", "workflow-plural": "Workflows", "yes": "はい", + "yes-comma-confirm": "Yes, confirm", "yesterday": "Yesterday", "your-entity": "あなたの{{entity}}" }, @@ -1359,6 +1361,9 @@ "find-in-table": "テーブルで探す", "fosters-collaboration-among-producers-and-consumers": "Fosters collaboration among the producers and consumers of data.", "get-started-with-open-metadata": "Get started with OpenMetadata", + "glossary-tag-assignement-help-message": "You can either remove this assets or remove conflicting tag from the asset and try again adding tags!", + "glossary-tag-update-description": "This action will apply the tag to all Assets linked to the Glossary Term", + "glossary-tag-update-modal-title": "Validation failed for following assets", "glossary-term-description": "Every term in the glossary has a unique definition. Along with defining the standard term for a concept, the synonyms as well as related terms (for e.g., parent and child terms) can be specified. References can be added to the assets related to the terms. New terms can be added or updated to the Glossary. The glossary terms can be reviewed by certain users, who can accept or reject the terms.", "glossary-term-status": "Glossary Term was {{status}}.", "go-back-to-login-page": "ログインページに戻る", @@ -1577,6 +1582,7 @@ "successfully-completed-the-tour": "あなたは無事ツアーを終了しました。", "synonym-placeholder": "To add a synonym, simply type it in and press Enter", "system-tag-delete-disable-message": "Deleting a system generated tags is not allowed. You can try disabling the tag instead.", + "tag-update-confirmation": "Would you like to proceed with adding a new tag?", "take-quick-product-tour": "Take a product tour to get started!", "team-moved-success": "チームの移動が成功しました!", "team-no-asset": "あなたのチームはアセットを持っていません。", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/pt-br.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/pt-br.json index 903c411522f6..cf0b62ec514b 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/pt-br.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/pt-br.json @@ -662,6 +662,7 @@ "new-test-suite": "Novo Conjunto de Testes", "next": "Próximo", "no": "Não", + "no-comma-cancel": "No, cancel", "no-data-asset-found-for": "Nenhum ativo de dados encontrado para", "no-data-found": "Nenhum dado encontrado", "no-description": "Sem descrição", @@ -1163,6 +1164,7 @@ "widget-lowercase": "widget", "workflow-plural": "Fluxos de Trabalho", "yes": "Sim", + "yes-comma-confirm": "Yes, confirm", "yesterday": "Ontem", "your-entity": "Sua {{entity}}" }, @@ -1359,6 +1361,9 @@ "find-in-table": "Encontrar na tabela", "fosters-collaboration-among-producers-and-consumers": "Estabeleça metas organizacionais e KPIs para impulsionar proativamente a cultura de dados da sua empresa. Fomente uma cultura de melhoria contínua com relatórios oportunos para monitorar a saúde dos dados.", "get-started-with-open-metadata": "Comece com o OpenMetadata", + "glossary-tag-assignement-help-message": "You can either remove this assets or remove conflicting tag from the asset and try again adding tags!", + "glossary-tag-update-description": "This action will apply the tag to all Assets linked to the Glossary Term", + "glossary-tag-update-modal-title": "Validation failed for following assets", "glossary-term-description": "Cada termo no glossário tem uma definição única. Além de definir o termo padrão para um conceito, os sinônimos e termos relacionados (por exemplo, termos pai e filho) podem ser especificados. Referências podem ser adicionadas aos ativos relacionados aos termos. Novos termos podem ser adicionados ou atualizados no Glossário. Os termos do glossário podem ser revisados por certos usuários, que podem aceitar ou rejeitar os termos.", "glossary-term-status": "Termo do Glossário foi {{status}}.", "go-back-to-login-page": "Voltar para a página de Login", @@ -1577,6 +1582,7 @@ "successfully-completed-the-tour": "Você concluiu com sucesso o tour.", "synonym-placeholder": "Para adicionar um sinônimo, basta digitá-lo e pressionar Enter", "system-tag-delete-disable-message": "Não é permitido excluir tags geradas pelo sistema. Você pode tentar desativar a tag em vez disso.", + "tag-update-confirmation": "Would you like to proceed with adding a new tag?", "take-quick-product-tour": "Faça um tour pelo produto para começar!", "team-moved-success": "Equipe movida com sucesso!", "team-no-asset": "Sua equipe não possui ativos.", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/ru-ru.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/ru-ru.json index 6ff7d07e16b6..cbd71f3418bf 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/ru-ru.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/ru-ru.json @@ -662,6 +662,7 @@ "new-test-suite": "Новый набор тестов", "next": "Следующий", "no": "Нет", + "no-comma-cancel": "No, cancel", "no-data-asset-found-for": "Объекты данных не найдены", "no-data-found": "Данные не найдены", "no-description": "Нет описания", @@ -1163,6 +1164,7 @@ "widget-lowercase": "widget", "workflow-plural": "Workflows", "yes": "Да", + "yes-comma-confirm": "Yes, confirm", "yesterday": "Вчера", "your-entity": "Ваш {{entity}}" }, @@ -1359,6 +1361,9 @@ "find-in-table": "Найти в таблице", "fosters-collaboration-among-producers-and-consumers": "Способствует сотрудничеству между производителями и потребителями данных.", "get-started-with-open-metadata": "Начните работу с OpenMetadata", + "glossary-tag-assignement-help-message": "You can either remove this assets or remove conflicting tag from the asset and try again adding tags!", + "glossary-tag-update-description": "This action will apply the tag to all Assets linked to the Glossary Term", + "glossary-tag-update-modal-title": "Validation failed for following assets", "glossary-term-description": "Каждый термин в глоссарии имеет уникальное определение. Наряду с определением стандартного термина для понятия можно указать синонимы, а также связанные термины (например, родительские и дочерние термины). Ссылки могут быть добавлены к объектам данных, связанным с терминами. Новые термины могут быть добавлены или обновлены в Глоссарий. Термины глоссария могут быть просмотрены определенными пользователями, которые могут принять или отклонить термины.", "glossary-term-status": "Glossary Term was {{status}}.", "go-back-to-login-page": "Вернуться на страницу входа", @@ -1577,6 +1582,7 @@ "successfully-completed-the-tour": "Вы успешно завершили экскурсию.", "synonym-placeholder": "Чтобы добавить синоним, просто введите его и нажмите Enter.", "system-tag-delete-disable-message": "Удаление сгенерированных системой тегов не допускается. Вместо этого вы можете попробовать отключить тег.", + "tag-update-confirmation": "Would you like to proceed with adding a new tag?", "take-quick-product-tour": "Ознакомьтесь с продуктом, чтобы начать работу!", "team-moved-success": "Команда успешно переехала!", "team-no-asset": "У вашей команды нет объектов.", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/zh-cn.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/zh-cn.json index e04e00e6dd36..6fdd26760ec3 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/zh-cn.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/zh-cn.json @@ -662,6 +662,7 @@ "new-test-suite": "新质控测试", "next": "下一步", "no": "否", + "no-comma-cancel": "No, cancel", "no-data-asset-found-for": "没有查询到相关的数据资产", "no-data-found": "未找到数据", "no-description": "无描述", @@ -1163,6 +1164,7 @@ "widget-lowercase": "widget", "workflow-plural": "Workflows", "yes": "是", + "yes-comma-confirm": "Yes, confirm", "yesterday": "昨天", "your-entity": "您的{{entity}}" }, @@ -1359,6 +1361,9 @@ "find-in-table": "在数据表中查找", "fosters-collaboration-among-producers-and-consumers": "促进数据生产者和使用者之间的合作", "get-started-with-open-metadata": "开始使用 OpenMetadata", + "glossary-tag-assignement-help-message": "You can either remove this assets or remove conflicting tag from the asset and try again adding tags!", + "glossary-tag-update-description": "This action will apply the tag to all Assets linked to the Glossary Term", + "glossary-tag-update-modal-title": "Validation failed for following assets", "glossary-term-description": "术语库中的每个术语都有一个唯一的定义。除了为概念定义标准术语之外,还可以指定同义词以及相关术语(例如,父项和子项)。可以向与术语相关的资产添加引用。可以向术语库添加或更新新术语。某些用户可以审查术语,并接受或拒绝这些术语。", "glossary-term-status": "Glossary Term was {{status}}.", "go-back-to-login-page": "返回登录页面", @@ -1577,6 +1582,7 @@ "successfully-completed-the-tour": "您已成功完成导览", "synonym-placeholder": "输入并按回车键以新增一个同义词", "system-tag-delete-disable-message": "无法删除系统默认的标签,您可以尝试禁用标签", + "tag-update-confirmation": "Would you like to proceed with adding a new tag?", "take-quick-product-tour": "快速查看产品导览", "team-moved-success": "团队移动成功", "team-no-asset": "您的团队没有任何资产", diff --git a/openmetadata-ui/src/main/resources/ui/src/rest/glossaryAPI.ts b/openmetadata-ui/src/main/resources/ui/src/rest/glossaryAPI.ts index 460e3b31f5f9..714ed31674b3 100644 --- a/openmetadata-ui/src/main/resources/ui/src/rest/glossaryAPI.ts +++ b/openmetadata-ui/src/main/resources/ui/src/rest/glossaryAPI.ts @@ -262,6 +262,23 @@ export interface GlossaryTermFailure { failedRequest: Array<{ request: EntityReference; error: string }>; } +export const validateTagAddtionToGlossary = async ( + glossaryTerm: GlossaryTerm, + dryRun = false +) => { + const data = { + dryRun: dryRun, + glossaryTags: glossaryTerm.tags ?? [], + }; + + const response = await APIClient.put< + AddGlossaryToAssetsRequest, + AxiosResponse + >(`/glossaryTerms/${glossaryTerm.id}/tags/validate`, data); + + return response.data; +}; + export const addAssetsToGlossaryTerm = async ( glossaryTerm: GlossaryTerm, assets: EntityReference[], @@ -275,7 +292,7 @@ export const addAssetsToGlossaryTerm = async ( const response = await APIClient.put< AddGlossaryToAssetsRequest, - AxiosResponse + AxiosResponse >(`/glossaryTerms/${glossaryTerm.id}/assets/add`, data); return response.data; From c41171b8104e6e494a55d4d056a72f26312a1ca6 Mon Sep 17 00:00:00 2001 From: Chirag Madlani <12962843+chirag-madlani@users.noreply.github.com> Date: Tue, 5 Dec 2023 19:21:25 +0530 Subject: [PATCH 13/16] update modal --- .../GlossaryUpdateConfirmationModal.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryUpdateConfirmationModal/GlossaryUpdateConfirmationModal.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryUpdateConfirmationModal/GlossaryUpdateConfirmationModal.tsx index a948a541290c..95fc13fd4514 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryUpdateConfirmationModal/GlossaryUpdateConfirmationModal.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryUpdateConfirmationModal/GlossaryUpdateConfirmationModal.tsx @@ -132,7 +132,10 @@ export const GlossaryUpdateConfirmationModal = ({ columns={tagsColumn} dataSource={failedStatus?.failedRequest} loading={validating} - pagination={false} + pagination={{ + pageSize: 5, + showSizeChanger: true, + }} rowKey={(record) => record.request.id} /> From 39027dd374ad9727a29013293d17fafefd467658 Mon Sep 17 00:00:00 2001 From: karanh37 Date: Tue, 5 Dec 2023 20:09:37 +0530 Subject: [PATCH 14/16] fix: glossary cypress --- .../ui/cypress/constants/constants.js | 65 +++- .../ui/cypress/e2e/Pages/Glossary.spec.js | 364 ++++++++---------- .../GlossaryHeader.component.tsx | 5 +- 3 files changed, 216 insertions(+), 218 deletions(-) diff --git a/openmetadata-ui/src/main/resources/ui/cypress/constants/constants.js b/openmetadata-ui/src/main/resources/ui/cypress/constants/constants.js index 841f25dc93e7..294f382aca2b 100644 --- a/openmetadata-ui/src/main/resources/ui/cypress/constants/constants.js +++ b/openmetadata-ui/src/main/resources/ui/cypress/constants/constants.js @@ -305,15 +305,63 @@ export const NEW_GLOSSARY = { name: 'Cypress Glossary', description: 'This is the Cypress Glossary', reviewer: 'Aaron Johnson', - tag: 'PII.None', + addReviewer: true, + tag: 'PersonalData.Personal', }; export const NEW_GLOSSARY_1 = { name: 'Cypress Product%Glossary', description: 'This is the Product glossary with percentage', reviewer: 'Brandy Miller', + addReviewer: false, +}; +export const CYPRESS_ASSETS_GLOSSARY = { + name: 'Cypress Assets Glossary', + description: 'This is the Assets Cypress Glossary', + reviewer: '', + addReviewer: false, tag: 'PII.None', }; +const COMMON_ASSETS = [ + { + name: 'dim_customer', + fullyQualifiedName: 'sample_data.ecommerce_db.shopify.dim_address', + }, + { + name: 'raw_order', + fullyQualifiedName: 'sample_data.ecommerce_db.shopify.raw_order', + }, + { + name: 'presto_etl', + fullyQualifiedName: 'sample_airflow.presto_etl', + }, +]; + +export const CYPRESS_ASSETS_GLOSSARY_TERMS = { + term_1: { + name: 'Cypress%PercentTerm', + description: 'This is the Cypress PercentTerm', + synonyms: 'buy,collect,acquire', + fullyQualifiedName: 'Cypress Assets Glossary.Cypress%PercentTerm', + assets: COMMON_ASSETS, + }, + term_2: { + name: 'Cypress Space GTerm', + description: 'This is the Cypress Sales', + synonyms: 'give,disposal,deal', + fullyQualifiedName: 'Cypress Assets Glossary.Cypress Space GTerm', + assets: COMMON_ASSETS, + }, + term_3: { + name: 'Cypress.Dot.GTerm', + description: 'This is the Cypress with space', + synonyms: 'tea,coffee,water', + fullyQualifiedName: 'Cypress Assets Glossary."Cypress.Dot.GTerm"', + displayFqn: 'Cypress Assets Glossary."Cypress.Dot.GTerm"', + assets: COMMON_ASSETS, + }, +}; + export const NEW_GLOSSARY_TERMS = { term_1: { name: 'CypressPurchase', @@ -332,20 +380,7 @@ export const NEW_GLOSSARY_TERMS = { description: 'This is the Cypress with space', synonyms: 'tea,coffee,water', fullyQualifiedName: 'Cypress Glossary.Cypress Space', - assets: [ - { - name: 'dim_customer', - fullyQualifiedName: 'sample_data.ecommerce_db.shopify.dim_address', - }, - { - name: 'raw_order', - fullyQualifiedName: 'sample_data.ecommerce_db.shopify.raw_order', - }, - { - name: 'presto_etl', - fullyQualifiedName: 'sample_airflow.presto_etl', - }, - ], + assets: COMMON_ASSETS, }, }; export const GLOSSARY_TERM_WITH_DETAILS = { diff --git a/openmetadata-ui/src/main/resources/ui/cypress/e2e/Pages/Glossary.spec.js b/openmetadata-ui/src/main/resources/ui/cypress/e2e/Pages/Glossary.spec.js index 990c19557c27..6dca15e83336 100644 --- a/openmetadata-ui/src/main/resources/ui/cypress/e2e/Pages/Glossary.spec.js +++ b/openmetadata-ui/src/main/resources/ui/cypress/e2e/Pages/Glossary.spec.js @@ -28,6 +28,8 @@ import { } from '../../common/common'; import { deleteGlossary } from '../../common/GlossaryUtils'; import { + CYPRESS_ASSETS_GLOSSARY, + CYPRESS_ASSETS_GLOSSARY_TERMS, DELETE_TERM, INVALID_NAMES, NAME_MAX_LENGTH_VALIDATION_ERROR, @@ -63,7 +65,7 @@ const visitGlossaryTermPage = (termName, fqn, fetchPermission) => { 'waitForTermPermission' ); - cy.get(`[data-row-key="${fqn}"]`) + cy.get(`[data-row-key="${Cypress.$.escapeSelector(fqn)}"]`) .scrollIntoView() .should('be.visible') .contains(termName) @@ -78,6 +80,83 @@ const visitGlossaryTermPage = (termName, fqn, fetchPermission) => { cy.get('.ant-tabs .glossary-overview-tab').should('be.visible').click(); }; +const createGlossary = (glossaryData) => { + // Intercept API calls + interceptURL('POST', '/api/v1/glossaries', 'createGlossary'); + interceptURL( + 'GET', + '/api/v1/search/query?q=*disabled:false&index=tag_search_index&from=0&size=10&query_filter=%7B%7D', + 'fetchTags' + ); + + // Click on the "Add Glossary" button + cy.get('[data-testid="add-glossary"]').click(); + + // Validate redirection to the add glossary page + cy.get('[data-testid="form-heading"]') + .contains('Add Glossary') + .should('be.visible'); + + // Perform glossary creation steps + cy.get('[data-testid="save-glossary"]') + .scrollIntoView() + .should('be.visible') + .click(); + + validateForm(); + + cy.get('[data-testid="name"]') + .scrollIntoView() + .should('be.visible') + .clear() + .type(glossaryData.name); + + cy.get(descriptionBox) + .scrollIntoView() + .should('be.visible') + .type(glossaryData.description); + + cy.get('[data-testid="mutually-exclusive-button"]').scrollIntoView().click(); + + if (glossaryData.tag) { + // Add tag + cy.get('[data-testid="tag-selector"] .ant-select-selection-overflow') + .scrollIntoView() + .type(glossaryData.tag); + + verifyResponseStatusCode('@fetchTags', 200); + cy.get(`[data-testid="tag-${glossaryData.tag}"]`).click(); + cy.get('[data-testid="right-panel"]').click(); + } + + if (glossaryData.addReviewer) { + // Add reviewer + cy.get('[data-testid="add-reviewers"]').scrollIntoView().click(); + cy.get('[data-testid="searchbar"]').type(CREDENTIALS.username); + cy.get(`[title="${CREDENTIALS.username}"]`) + .scrollIntoView() + .should('be.visible') + .click(); + cy.get('[data-testid="selectable-list-update-btn"]') + .should('exist') + .and('be.visible') + .click(); + } + + cy.get('[data-testid="save-glossary"]') + .scrollIntoView() + .should('be.visible') + .click(); + + cy.wait('@createGlossary').then(({ request }) => { + expect(request.body.name).equals(glossaryData.name); + expect(request.body.description).equals(glossaryData.description); + }); + + cy.url().should('include', '/glossary/'); + checkDisplayName(glossaryData.name); +}; + const checkDisplayName = (displayName) => { cy.get('[data-testid="entity-header-display-name"]') .scrollIntoView() @@ -89,7 +168,7 @@ const checkDisplayName = (displayName) => { }; const checkAssetsCount = (assetsCount) => { - cy.get('[data-testid="assets"] [data-testid="count"]') + cy.get('[data-testid="assets"] [data-testid="filter-count"]') .scrollIntoView() .should('have.text', assetsCount); }; @@ -183,6 +262,63 @@ const fillGlossaryTermDetails = (term, glossary, isMutually = false) => { } }; +const addAssetToGlossaryTerm = (glossaryTerm, glossary) => { + goToGlossaryPage(); + selectActiveGlossary(glossary.name); + goToAssetsTab(glossaryTerm.name, glossaryTerm.fullyQualifiedName, true); + + checkAssetsCount(0); + cy.contains('Adding a new Asset is easy, just give it a spin!').should( + 'be.visible' + ); + + cy.get('[data-testid="glossary-term-add-button-menu"]').click(); + cy.get('.ant-dropdown-menu .ant-dropdown-menu-title-content') + .contains('Assets') + .click(); + + cy.get('[data-testid="asset-selection-modal"] .ant-modal-title').should( + 'contain', + 'Add Assets' + ); + + glossaryTerm.assets.forEach((asset) => { + interceptURL('GET', '/api/v1/search/query*', 'searchAssets'); + cy.get('[data-testid="asset-selection-modal"] [data-testid="searchbar"]') + .click() + .clear() + .type(asset.name); + + verifyResponseStatusCode('@searchAssets', 200); + + cy.get( + `[data-testid="table-data-card_${asset.fullyQualifiedName}"] input[type="checkbox"]` + ).click(); + }); + + cy.get('[data-testid="save-btn"]').click(); + checkAssetsCount(glossaryTerm.assets.length); +}; + +const removeAssetsFromGlossaryTerm = (glossaryTerm, glossary) => { + goToGlossaryPage(); + selectActiveGlossary(glossary.name); + goToAssetsTab(glossaryTerm.name, glossaryTerm.fullyQualifiedName, true); + checkAssetsCount(glossaryTerm.assets.length); + glossaryTerm.assets.forEach((asset, index) => { + interceptURL('GET', '/api/v1/search/query*', 'searchAssets'); + cy.get(`[data-testid="manage-button-${asset.fullyQualifiedName}"]`).click(); + cy.get('[data-testid="delete-button"]').click(); + cy.get("[data-testid='save-button']").click(); + + interceptURL('GET', '/api/v1/search/query*', 'assetTab'); + // go assets tab + goToAssetsTab(glossaryTerm.name, glossaryTerm.fullyQualifiedName, true); + verifyResponseStatusCode('@assetTab', 200); + checkAssetsCount(glossaryTerm.assets.length - (index + 1)); + }); +}; + const createGlossaryTerm = (term, glossary, status, isMutually = false) => { fillGlossaryTermDetails(term, glossary, isMutually); @@ -194,12 +330,18 @@ const createGlossaryTerm = (term, glossary, status, isMutually = false) => { verifyResponseStatusCode('@createGlossaryTerms', 201); - cy.get(`[data-row-key="${glossary.name}.${term.name}"]`) + cy.get( + `[data-row-key="${Cypress.$.escapeSelector(term.fullyQualifiedName)}"]` + ) .scrollIntoView() .should('be.visible') .contains(term.name); - cy.get(`[data-testid="${glossary.name}.${term.name}-status"]`) + cy.get( + `[data-testid="${Cypress.$.escapeSelector( + term.fullyQualifiedName + )}-status"]` + ) .should('be.visible') .contains(status); }; @@ -501,132 +643,8 @@ describe('Glossary page should work properly', () => { }); it('Create new glossary flow should work properly', () => { - interceptURL('POST', '/api/v1/glossaries', 'createGlossary'); - interceptURL( - 'GET', - '/api/v1/search/query?q=*disabled:false&index=tag_search_index&from=0&size=10&query_filter=%7B%7D', - 'fetchTags' - ); - - cy.get('[data-testid="add-glossary"]').click(); - - // Redirecting to add glossary page - cy.get('[data-testid="form-heading"]') - .contains('Add Glossary') - .should('be.visible'); - - // validation should work - cy.get('[data-testid="save-glossary"]') - .scrollIntoView() - .should('be.visible') - .click(); - - validateForm(); - - cy.get('[data-testid="name"]') - .scrollIntoView() - .should('be.visible') - .clear() - .type(NEW_GLOSSARY.name); - - cy.get(descriptionBox) - .scrollIntoView() - .should('be.visible') - .type(NEW_GLOSSARY.description); - - cy.get('[data-testid="mutually-exclusive-button"]') - .scrollIntoView() - .click(); - - cy.get('[data-testid="tag-selector"] .ant-select-selection-overflow') - .scrollIntoView() - .type('Personal'); - verifyResponseStatusCode('@fetchTags', 200); - cy.get('[data-testid="tag-PersonalData.Personal"]').click(); - cy.get('[data-testid="right-panel"]').click(); - - cy.get('[data-testid="add-reviewers"]').scrollIntoView().click(); - - cy.get('[data-testid="searchbar"]').type(CREDENTIALS.username); - cy.get(`[title="${CREDENTIALS.username}"]`) - .scrollIntoView() - .should('be.visible') - .click(); - - cy.get('[data-testid="selectable-list-update-btn"]') - .should('exist') - .and('be.visible') - .click(); - cy.get('[data-testid="delete-confirmation-modal"]').should('not.exist'); - cy.get('[data-testid="reviewers-container"]') - .children() - .should('have.length', 1); - - cy.get('[data-testid="save-glossary"]') - .scrollIntoView() - .should('be.visible') - .click(); - - cy.wait('@createGlossary').then(({ request }) => { - expect(request.body).to.have.all.keys( - 'description', - 'mutuallyExclusive', - 'name', - 'owner', - 'reviewers', - 'tags' - ); - expect(request.body.name).equals(NEW_GLOSSARY.name); - expect(request.body.description).equals(NEW_GLOSSARY.description); - expect(request.body.mutuallyExclusive).equals(true); - expect(request.body.owner).to.have.all.keys('id', 'type'); - expect(request.body.reviewers).has.length(1); - expect(request.body.tags).has.length(1); - expect(request.body.tags[0].tagFQN).equals('PersonalData.Personal'); - expect(request.body.tags[0].source).equals('Classification'); - - cy.url().should('include', '/glossary/'); - - checkDisplayName(NEW_GLOSSARY.name); - }); - - // Adding another Glossary with mutually exclusive flag off - cy.get('[data-testid="add-glossary"]').should('be.visible').click(); - cy.get('[data-testid="name"]') - .scrollIntoView() - .should('be.visible') - .type(NEW_GLOSSARY_1.name); - - cy.get(descriptionBox) - .scrollIntoView() - .should('be.visible') - .type(NEW_GLOSSARY_1.description); - - cy.get('[data-testid="save-glossary"]') - .scrollIntoView() - .should('be.visible') - .click(); - - cy.wait('@createGlossary').then(({ request }) => { - expect(request.body).to.have.all.keys( - 'description', - 'mutuallyExclusive', - 'name', - 'owner', - 'reviewers', - 'tags' - ); - - expect(request.body.name).equals(NEW_GLOSSARY_1.name); - expect(request.body.description).equals(NEW_GLOSSARY_1.description); - expect(request.body.mutuallyExclusive).equals(false); - expect(request.body.owner).to.have.all.keys('id', 'type'); - expect(request.body.reviewers).has.length(0); - expect(request.body.tags).has.length(0); - - cy.url().should('include', '/glossary/'); - checkDisplayName(NEW_GLOSSARY_1.name); - }); + createGlossary(NEW_GLOSSARY); + createGlossary(NEW_GLOSSARY_1); }); it('Verify and Remove Tags from Glossary', () => { @@ -974,82 +992,22 @@ describe('Glossary page should work properly', () => { }); it('Add asset to glossary term using asset modal', () => { - selectActiveGlossary(NEW_GLOSSARY.name); - goToAssetsTab( - NEW_GLOSSARY_TERMS.term_3.name, - NEW_GLOSSARY_TERMS.term_3.fullyQualifiedName, - true - ); - - checkAssetsCount(0); - cy.contains('Adding a new Asset is easy, just give it a spin!').should( - 'be.visible' - ); - - cy.get('[data-testid="glossary-term-add-button-menu"]').click(); - cy.get('.ant-dropdown-menu .ant-dropdown-menu-title-content') - .contains('Assets') - .click(); - - cy.get('[data-testid="asset-selection-modal"] .ant-modal-title').should( - 'contain', - 'Add Assets' + createGlossary(CYPRESS_ASSETS_GLOSSARY); + const terms = Object.values(CYPRESS_ASSETS_GLOSSARY_TERMS); + selectActiveGlossary(CYPRESS_ASSETS_GLOSSARY.name); + terms.forEach((term) => + createGlossaryTerm(term, CYPRESS_ASSETS_GLOSSARY, 'Approved', true) ); - NEW_GLOSSARY_TERMS.term_3.assets.forEach((asset) => { - interceptURL('GET', '/api/v1/search/query*', 'searchAssets'); - cy.get('[data-testid="asset-selection-modal"] [data-testid="searchbar"]') - .click() - .clear() - .type(asset.name); - - verifyResponseStatusCode('@searchAssets', 200); - - cy.get( - `[data-testid="table-data-card_${asset.fullyQualifiedName}"] input[type="checkbox"]` - ).click(); + terms.forEach((term) => { + addAssetToGlossaryTerm(term, CYPRESS_ASSETS_GLOSSARY); }); - - cy.get('[data-testid="save-btn"]').click(); - - checkAssetsCount(NEW_GLOSSARY_TERMS.term_3.assets); }); it('Remove asset from glossary term using asset modal', () => { - selectActiveGlossary(NEW_GLOSSARY.name); - goToAssetsTab( - NEW_GLOSSARY_TERMS.term_3.name, - NEW_GLOSSARY_TERMS.term_3.fullyQualifiedName, - true - ); - - checkAssetsCount(NEW_GLOSSARY_TERMS.term_3.assets.length); - NEW_GLOSSARY_TERMS.term_3.assets.assets.forEach((asset, index) => { - interceptURL('GET', '/api/v1/search/query*', 'searchAssets'); - - cy.get( - `[data-testid="table-data-card_${asset.fullyQualifiedName}"]` - ).within(() => { - cy.get('.explore-card-actions').invoke('show'); - cy.get('.explore-card-actions').within(() => { - cy.get('[data-testid="delete-tag"]').click(); - }); - }); - - cy.get("[data-testid='save-button']").click(); - - selectActiveGlossary(NEW_GLOSSARY.name); - - interceptURL('GET', '/api/v1/search/query*', 'assetTab'); - // go assets tab - goToAssetsTab( - NEW_GLOSSARY_TERMS.term_3.name, - NEW_GLOSSARY_TERMS.term_3.fullyQualifiedName, - true - ); - verifyResponseStatusCode('@assetTab', 200); - - checkAssetsCount(NEW_GLOSSARY_TERMS.term_3.assets.length - (index + 1)); + const terms = Object.values(CYPRESS_ASSETS_GLOSSARY_TERMS); + terms.forEach((term) => { + removeAssetsFromGlossaryTerm(term, CYPRESS_ASSETS_GLOSSARY); }); }); @@ -1146,7 +1104,11 @@ describe('Cleanup', () => { it('Delete glossary should work properly', () => { goToGlossaryPage(); verifyResponseStatusCode('@fetchGlossaries', 200); - [NEW_GLOSSARY.name, NEW_GLOSSARY_1.name].forEach((glossary) => { + [ + NEW_GLOSSARY.name, + NEW_GLOSSARY_1.name, + CYPRESS_ASSETS_GLOSSARY.name, + ].forEach((glossary) => { deleteGlossary(glossary); }); }); diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryHeader/GlossaryHeader.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryHeader/GlossaryHeader.component.tsx index baaea71d0b81..1b21cad95876 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryHeader/GlossaryHeader.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryHeader/GlossaryHeader.component.tsx @@ -429,13 +429,14 @@ const GlossaryHeader = ({ {glossaryTermStatus && glossaryTermStatus === Status.Approved && ( -
diff --git a/openmetadata-ui/src/main/resources/ui/src/styles/border.less b/openmetadata-ui/src/main/resources/ui/src/styles/border.less index 526d4a6d96d2..c8073f6dc58a 100644 --- a/openmetadata-ui/src/main/resources/ui/src/styles/border.less +++ b/openmetadata-ui/src/main/resources/ui/src/styles/border.less @@ -43,3 +43,7 @@ .border-color-primary { border-color: @primary-color; } + +.border-danger { + border: 1px solid @error-color; +} diff --git a/openmetadata-ui/src/main/resources/ui/src/styles/fonts.less b/openmetadata-ui/src/main/resources/ui/src/styles/fonts.less index 3eec3d3dd67e..0625d540e916 100644 --- a/openmetadata-ui/src/main/resources/ui/src/styles/fonts.less +++ b/openmetadata-ui/src/main/resources/ui/src/styles/fonts.less @@ -89,6 +89,10 @@ pre { } } +.text-danger { + color: @error-color; +} + .text-inherit { font-size: inherit; } From 019d7891f13382f3883b29995d58f43e710ac668 Mon Sep 17 00:00:00 2001 From: Chirag Madlani <12962843+chirag-madlani@users.noreply.github.com> Date: Tue, 5 Dec 2023 23:15:06 +0530 Subject: [PATCH 16/16] fix comments --- .../AsyncSelectList/AsyncSelectList.tsx | 7 +- .../GlossaryUpdateConfirmationModal.test.tsx | 90 +++++++++++++++++++ .../GlossaryUpdateConfirmationModal.tsx | 17 ++-- .../components/Tag/TagsViewer/TagsViewer.tsx | 2 +- .../ui/src/locale/languages/de-de.json | 1 + .../ui/src/locale/languages/en-us.json | 1 + .../ui/src/locale/languages/es-es.json | 1 + .../ui/src/locale/languages/fr-fr.json | 1 + .../ui/src/locale/languages/ja-jp.json | 1 + .../ui/src/locale/languages/pt-br.json | 1 + .../ui/src/locale/languages/ru-ru.json | 1 + .../ui/src/locale/languages/zh-cn.json | 1 + .../main/resources/ui/src/rest/glossaryAPI.ts | 14 +-- 13 files changed, 111 insertions(+), 27 deletions(-) create mode 100644 openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryUpdateConfirmationModal/GlossaryUpdateConfirmationModal.test.tsx diff --git a/openmetadata-ui/src/main/resources/ui/src/components/AsyncSelectList/AsyncSelectList.tsx b/openmetadata-ui/src/main/resources/ui/src/components/AsyncSelectList/AsyncSelectList.tsx index ee7fc078bcce..1d2f54853f00 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/AsyncSelectList/AsyncSelectList.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/AsyncSelectList/AsyncSelectList.tsx @@ -23,6 +23,7 @@ import { AxiosError } from 'axios'; import { debounce, isEmpty, isUndefined, pick } from 'lodash'; import { CustomTagProps } from 'rc-select/lib/BaseSelect'; import React, { FC, useCallback, useMemo, useRef, useState } from 'react'; +import { useTranslation } from 'react-i18next'; import Loader from '../../components/Loader/Loader'; import { FQN_SEPARATOR_CHAR } from '../../constants/char.constants'; import { TAG_START_WITH } from '../../constants/Tag.constants'; @@ -55,7 +56,7 @@ const AsyncSelectList: FC = ({ const [paging, setPaging] = useState({} as Paging); const [currentPage, setCurrentPage] = useState(1); const selectedTagsRef = useRef(initialOptions ?? []); - + const { t } = useTranslation(); const [optionFilteredCount, setOptionFilteredCount] = useState(0); const getFilteredOptions = (data: SelectOption[]) => { @@ -222,9 +223,7 @@ const AsyncSelectList: FC = ({ tag={tag} tagProps={tagProps} tooltipOverride={ - isDerived - ? 'This tag is automatically derived and can only be removed by deleting the related Glossary Term.' - : undefined + isDerived ? t('message.derived-tag-warning') : undefined } /> ); diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryUpdateConfirmationModal/GlossaryUpdateConfirmationModal.test.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryUpdateConfirmationModal/GlossaryUpdateConfirmationModal.test.tsx new file mode 100644 index 000000000000..1cd935adfa3e --- /dev/null +++ b/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryUpdateConfirmationModal/GlossaryUpdateConfirmationModal.test.tsx @@ -0,0 +1,90 @@ +/* + * Copyright 2023 Collate. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { act, fireEvent, render } from '@testing-library/react'; +import React from 'react'; +import { GlossaryTerm } from '../../../generated/entity/data/glossaryTerm'; +import { GlossaryUpdateConfirmationModal } from './GlossaryUpdateConfirmationModal'; + +const mockOnCancel = jest.fn(); +const mockOnValidationSuccess = jest.fn(); +const mockValidateTagAddtionToGlossary = jest.fn().mockResolvedValue({}); + +jest.mock('../../../rest/glossaryAPI', () => ({ + validateTagAddtionToGlossary: mockValidateTagAddtionToGlossary, +})); + +jest.mock('../../../utils/EntityUtils', () => ({ + getEntityLinkFromType: jest.fn(), + getEntityName: jest.fn(), +})); + +jest.mock('../../common/Table/Table', () => { + return jest.fn(); +}); + +describe('GlossaryUpdateConfirmationModal component', () => { + it('should render confirmation screen', async () => { + const { findByText } = render( + + ); + + expect(await findByText('label.no-comma-cancel')).toBeInTheDocument(); + expect(await findByText('label.yes-comma-confirm')).toBeInTheDocument(); + expect( + await findByText('message.tag-update-confirmation') + ).toBeInTheDocument(); + expect( + await findByText('message.glossary-tag-update-description') + ).toBeInTheDocument(); + }); + + it('should call onCancel on clicking on no, cancel button', async () => { + const { findByText } = render( + + ); + + fireEvent.click(await findByText('label.no-comma-cancel')); + + expect(mockOnCancel).toHaveBeenCalled(); + }); + + it.skip('should call validation api on clicking on yes, confirm button', async () => { + const { findByText } = render( + + ); + + await act(async () => { + fireEvent.click(await findByText('label.yes-comma-confirm')); + }); + + expect(mockValidateTagAddtionToGlossary).toHaveBeenCalledWith( + { tags: [] }, + true + ); + }); +}); diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryUpdateConfirmationModal/GlossaryUpdateConfirmationModal.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryUpdateConfirmationModal/GlossaryUpdateConfirmationModal.tsx index 95fc13fd4514..e9e9c9e1cb9d 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryUpdateConfirmationModal/GlossaryUpdateConfirmationModal.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Glossary/GlossaryUpdateConfirmationModal/GlossaryUpdateConfirmationModal.tsx @@ -21,11 +21,11 @@ import { ClientErrors } from '../../../enums/axios.enum'; import { EntityType } from '../../../enums/entity.enum'; import { GlossaryTerm } from '../../../generated/entity/data/glossaryTerm'; import { EntityReference } from '../../../generated/entity/type'; -import { Status } from '../../../generated/type/bulkOperationResult'; import { - GlossaryTermFailure, - validateTagAddtionToGlossary, -} from '../../../rest/glossaryAPI'; + BulkOperationResult, + Status, +} from '../../../generated/type/bulkOperationResult'; +import { validateTagAddtionToGlossary } from '../../../rest/glossaryAPI'; import { getEntityLinkFromType, getEntityName, @@ -39,7 +39,7 @@ export const GlossaryUpdateConfirmationModal = ({ onCancel, updatedTags, }: GlossaryUpdateConfirmationModalProps) => { - const [failedStatus, setFailedStatus] = useState(); + const [failedStatus, setFailedStatus] = useState(); const [tagError, setTagError] = useState<{ code: number; message: string }>(); const [tagAdditionConfirmation, setTagAdditionConfirmation] = useState(false); const [validating, setValidating] = useState(false); @@ -55,13 +55,10 @@ export const GlossaryUpdateConfirmationModal = ({ true ); - if ( - res.status && - (res as GlossaryTermFailure).status === Status.Success - ) { + if (res.status && res.status === Status.Success) { await onValidationSuccess(); } else { - setFailedStatus(res as GlossaryTermFailure); + setFailedStatus(res); } } catch (err) { // error diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Tag/TagsViewer/TagsViewer.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Tag/TagsViewer/TagsViewer.tsx index 25544141c980..0bf07b71bf01 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Tag/TagsViewer/TagsViewer.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Tag/TagsViewer/TagsViewer.tsx @@ -46,7 +46,7 @@ const TagsViewer: FunctionComponent = ({ { 'diff-removed': tag?.removed } )} isVersionPage={tag?.added || tag?.removed} - key={tag.name} + key={tag.tagFQN} showOnlyName={tag.source === TagSource.Glossary} startWith={TAG_START_WITH.SOURCE_ICON} tag={tag} diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/de-de.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/de-de.json index 1b5f27a6b1b2..291610f1d11c 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/de-de.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/de-de.json @@ -1280,6 +1280,7 @@ "delete-message-question-mark": "Nachricht löschen?", "delete-team-message": "Alle Teams unter \"{{teamName}}\" werden ebenfalls {{deleteType}} gelöscht.", "delete-webhook-permanently": "Möchten Sie den Webhook \"{{webhookName}}\" dauerhaft löschen? Diese Aktion kann nicht rückgängig gemacht werden.", + "derived-tag-warning": "This tag is automatically derived and can only be removed by deleting the related Glossary Term.", "disable-app": "This will disable the {{app}} application.", "disable-classification-description": "Durch Deaktivieren der Klassifizierung können Sie nicht mehr nach dieser suchen oder sie mit zugeordneten Tags einer Entität zuweisen.", "disabled-classification-actions-message": "Sie können diese Aktion auf deaktivierten Klassifikationen nicht ausführen.", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/en-us.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/en-us.json index bc8399c132ec..a883b3bb5132 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/en-us.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/en-us.json @@ -1280,6 +1280,7 @@ "delete-message-question-mark": "Delete Message?", "delete-team-message": "Any teams under \"{{teamName}}\" will be {{deleteType}} deleted as well.", "delete-webhook-permanently": "You want to delete webhook {{webhookName}} permanently? This action cannot be reverted.", + "derived-tag-warning": "This tag is automatically derived and can only be removed by deleting the related Glossary Term.", "disable-app": "This will disable the {{app}} application.", "disable-classification-description": "By disabling classification, you will not be able to search by, or assign associated tags to any entity.", "disabled-classification-actions-message": "You can not perform this action on disabled classifications.", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/es-es.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/es-es.json index 30435e18636f..66026b7d0ce1 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/es-es.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/es-es.json @@ -1280,6 +1280,7 @@ "delete-message-question-mark": "¿Eliminar mensaje?", "delete-team-message": "Cualquier equipo bajo \"{{teamName}}\" también será {{deleteType}} eliminado.", "delete-webhook-permanently": "¿Quieres eliminar permanentemente el webhook {{webhookName}}? Esta acción no se puede revertir.", + "derived-tag-warning": "This tag is automatically derived and can only be removed by deleting the related Glossary Term.", "disable-app": "This will disable the {{app}} application.", "disable-classification-description": "Option to disable classifications, You won't be able to see associated tags within application", "disabled-classification-actions-message": "You can not perform this action on disabled classifications.", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/fr-fr.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/fr-fr.json index b9cba7a79f13..ee1ba1746048 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/fr-fr.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/fr-fr.json @@ -1280,6 +1280,7 @@ "delete-message-question-mark": "Supprimer le Message ?", "delete-team-message": "N'importe quelle équipe sous \"{{teamName}}\" sera {{deleteType}} supprimée aussi.", "delete-webhook-permanently": "Vous voulez supprimer le webhook {{webhookName}} de manière permanente ? Cette action ne peut pas être annulée.", + "derived-tag-warning": "This tag is automatically derived and can only be removed by deleting the related Glossary Term.", "disable-app": "This will disable the {{app}} application.", "disable-classification-description": "Option to disable classifications, You won't be able to see associated tags within the application", "disabled-classification-actions-message": "You cannot perform this action on disabled classifications.", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/ja-jp.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/ja-jp.json index c0711f3ab651..19fda7a46d3a 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/ja-jp.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/ja-jp.json @@ -1280,6 +1280,7 @@ "delete-message-question-mark": "このメッセージを削除しますか?", "delete-team-message": "Any teams under \"{{teamName}}\" will be {{deleteType}} deleted as well.", "delete-webhook-permanently": "You want to delete webhook {{webhookName}} permanently? This action cannot be reverted.", + "derived-tag-warning": "This tag is automatically derived and can only be removed by deleting the related Glossary Term.", "disable-app": "This will disable the {{app}} application.", "disable-classification-description": "Option to disable classifications, You won't be able to see associated tags within application", "disabled-classification-actions-message": "You can not perform this action on disabled classifications.", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/pt-br.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/pt-br.json index cf0b62ec514b..ff1c5e1f5364 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/pt-br.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/pt-br.json @@ -1280,6 +1280,7 @@ "delete-message-question-mark": "Excluir Mensagem?", "delete-team-message": "Qualquer equipe sob \"{{teamName}}\" será {{deleteType}} excluída também.", "delete-webhook-permanently": "Você deseja excluir permanentemente o webhook {{webhookName}}? Esta ação não pode ser revertida.", + "derived-tag-warning": "This tag is automatically derived and can only be removed by deleting the related Glossary Term.", "disable-app": "Isso desativará a aplicação {{app}}.", "disable-classification-description": "Ao desativar a classificação, você não poderá buscar por, ou atribuir tags associadas a qualquer entidade.", "disabled-classification-actions-message": "Você não pode realizar esta ação em classificações desativadas.", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/ru-ru.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/ru-ru.json index cbd71f3418bf..eb4205441c6f 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/ru-ru.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/ru-ru.json @@ -1280,6 +1280,7 @@ "delete-message-question-mark": "Удалить сообщение?", "delete-team-message": "Все команды под \"{{teamName}}\" также будут {{deleteType}} удалены.", "delete-webhook-permanently": "Вы хотите навсегда удалить webhook {{webhookName}}? Это действие нельзя отменить.", + "derived-tag-warning": "This tag is automatically derived and can only be removed by deleting the related Glossary Term.", "disable-app": "This will disable the {{app}} application.", "disable-classification-description": "Отключив классификацию, вы не сможете выполнять поиск или назначать связанные теги любому объекту.", "disabled-classification-actions-message": "Вы не можете выполнить это действие с отключенными классификациями.", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/zh-cn.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/zh-cn.json index 6fdd26760ec3..a2feaf2f6d3e 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/zh-cn.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/zh-cn.json @@ -1280,6 +1280,7 @@ "delete-message-question-mark": "删除消息?", "delete-team-message": "任何在\"{{teamName}}\"下的团队也将被{{deleteType}}删除", "delete-webhook-permanently": "您要永久删除 Webhook {{webhookName}} 吗?此操作无法撤消", + "derived-tag-warning": "This tag is automatically derived and can only be removed by deleting the related Glossary Term.", "disable-app": "This will disable the {{app}} application.", "disable-classification-description": "禁用分类选项,您将无法查看到关联的标签", "disabled-classification-actions-message": "您无法在已禁用的分类上执行此操作", diff --git a/openmetadata-ui/src/main/resources/ui/src/rest/glossaryAPI.ts b/openmetadata-ui/src/main/resources/ui/src/rest/glossaryAPI.ts index 714ed31674b3..e143c4aa6874 100644 --- a/openmetadata-ui/src/main/resources/ui/src/rest/glossaryAPI.ts +++ b/openmetadata-ui/src/main/resources/ui/src/rest/glossaryAPI.ts @@ -20,7 +20,7 @@ import { CreateGlossary } from '../generated/api/data/createGlossary'; import { CreateGlossaryTerm } from '../generated/api/data/createGlossaryTerm'; import { EntityReference, Glossary } from '../generated/entity/data/glossary'; import { GlossaryTerm } from '../generated/entity/data/glossaryTerm'; -import { Status } from '../generated/type/bulkOperationResult'; +import { BulkOperationResult } from '../generated/type/bulkOperationResult'; import { CSVImportResult } from '../generated/type/csvImportResult'; import { EntityHistory } from '../generated/type/entityHistory'; import { ListParams } from '../interface/API.interface'; @@ -252,16 +252,6 @@ export const updateGlossaryTermVotes = async ( return response.data; }; -export interface GlossaryTermFailure { - dryRun: boolean; - status: Status; - numberOfRowsProcessed: number; - numberOfRowsPassed: number; - numberOfRowsFailed: number; - successRequest: Array; - failedRequest: Array<{ request: EntityReference; error: string }>; -} - export const validateTagAddtionToGlossary = async ( glossaryTerm: GlossaryTerm, dryRun = false @@ -273,7 +263,7 @@ export const validateTagAddtionToGlossary = async ( const response = await APIClient.put< AddGlossaryToAssetsRequest, - AxiosResponse + AxiosResponse >(`/glossaryTerms/${glossaryTerm.id}/tags/validate`, data); return response.data;