From c92b3e0d4ddec2aa44f737991a1bbcc9f2c8f3a0 Mon Sep 17 00:00:00 2001 From: Karan Hotchandani <33024356+karanh37@users.noreply.github.com> Date: Tue, 18 Nov 2025 21:13:49 +0530 Subject: [PATCH] fix(ui): Domain dp fixes (#24422) * fix data products redirect path * do not show unprocessed status * fix domain issue * fix tests * add playwright tests --- .../ui/playwright/e2e/Pages/Domains.spec.ts | 39 ++++++ .../DataProductsPage.component.tsx | 4 +- .../EntityStatusBadge.component.tsx | 5 + .../ui/src/utils/DomainUtils.test.tsx | 129 +++++++++++++++++- .../resources/ui/src/utils/DomainUtils.tsx | 9 +- 5 files changed, 181 insertions(+), 5 deletions(-) diff --git a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/Domains.spec.ts b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/Domains.spec.ts index eb3df9204e8..91c3549c238 100644 --- a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/Domains.spec.ts +++ b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/Domains.spec.ts @@ -812,6 +812,45 @@ test.describe('Domains', () => { } }); + test('Verify redirect path on data product delete', async ({ page }) => { + const { afterAction, apiContext } = await getApiContext(page); + const domain = new Domain(); + const dataProduct = new DataProduct([domain]); + + await domain.create(apiContext); + await dataProduct.create(apiContext); + + await page.reload(); + await redirectToHomePage(page); + + await sidebarClick(page, SidebarItem.DATA_PRODUCT); + await selectDataProduct(page, dataProduct.data); + + await page.getByTestId('manage-button').click(); + await page.getByTestId('delete-button-title').click(); + + await page.getByTestId('confirmation-text-input').click(); + await page.getByTestId('confirmation-text-input').fill('DELETE'); + + const dpListRes = page.waitForResponse( + '/api/v1/search/query?q=&index=data_product_search_index*' + ); + const deleteRes = page.waitForResponse('/api/v1/dataProducts/*'); + + await page.getByTestId('confirm-button').click(); + + await deleteRes; + await dpListRes; + + await expect( + page.getByText(`"Data Product" deleted successfully!`) + ).toBeVisible(); + + await expect(page.getByTestId('add-entity-button')).toBeVisible(); + + await afterAction(); + }); + test('Verify duplicate domain creation', async ({ page }) => { const { afterAction, apiContext } = await getApiContext(page); const domain = new Domain(); diff --git a/openmetadata-ui/src/main/resources/ui/src/components/DataProducts/DataProductsPage/DataProductsPage.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/DataProducts/DataProductsPage/DataProductsPage.component.tsx index a9634afcfe0..529dd871fc3 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/DataProducts/DataProductsPage/DataProductsPage.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/DataProducts/DataProductsPage/DataProductsPage.component.tsx @@ -18,6 +18,7 @@ import { toString } from 'lodash'; import { useCallback, useEffect, useMemo, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { useNavigate } from 'react-router-dom'; +import { ROUTES } from '../../../constants/constants'; import { ERROR_PLACEHOLDER_TYPE } from '../../../enums/common.enum'; import { EntityType, TabSpecificField } from '../../../enums/entity.enum'; import { DataProduct } from '../../../generated/entity/domains/dataProduct'; @@ -104,8 +105,7 @@ const DataProductsPage = () => { entity: t('label.data-product'), }) ); - const domainPath = getDomainPath(); - navigate(domainPath); + navigate(ROUTES.DATA_PRODUCT); } catch (err) { showErrorToast( err as AxiosError, diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Entity/EntityStatusBadge/EntityStatusBadge.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Entity/EntityStatusBadge/EntityStatusBadge.component.tsx index 94073f63199..9b5ffca315f 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Entity/EntityStatusBadge/EntityStatusBadge.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Entity/EntityStatusBadge/EntityStatusBadge.component.tsx @@ -24,6 +24,11 @@ export const EntityStatusBadge = ({ status, showDivider = true, }: EntityStatusBadgeProps) => { + // Do not show badge for 'Unprocessed' status + if (status === EntityStatus.Unprocessed) { + return null; + } + return ( {showDivider && } diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/DomainUtils.test.tsx b/openmetadata-ui/src/main/resources/ui/src/utils/DomainUtils.test.tsx index 98748ad49cd..eb2c6c83510 100644 --- a/openmetadata-ui/src/main/resources/ui/src/utils/DomainUtils.test.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/utils/DomainUtils.test.tsx @@ -10,8 +10,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +import { EntityType } from '../enums/entity.enum'; import { Domain, DomainType } from '../generated/entity/domains/domain'; -import { getDomainOptions, isDomainExist } from '../utils/DomainUtils'; +import { + getDomainOptions, + getQueryFilterToIncludeDomain, + isDomainExist, +} from '../utils/DomainUtils'; describe('getDomainOptions function', () => { const domains = [ @@ -146,4 +151,126 @@ describe('isDomainExist', () => { expect(isDomainExist(domain, 'parent.child.grandchild')).toBe(true); }); + + describe('getQueryFilterToIncludeDomain', () => { + it('should return correct query filter structure with domain and data product fqns', () => { + const domainFqn = 'testDomain'; + const dataProductFqn = 'testDataProduct'; + + const result = getQueryFilterToIncludeDomain(domainFqn, dataProductFqn); + + expect(result).toEqual({ + query: { + bool: { + must: [ + { + term: { + 'domains.fullyQualifiedName': domainFqn, + }, + }, + { + bool: { + must_not: [ + { + term: { + 'dataProducts.fullyQualifiedName': dataProductFqn, + }, + }, + ], + }, + }, + { + bool: { + must_not: [ + { + terms: { + entityType: [ + EntityType.DATA_PRODUCT, + EntityType.TEST_SUITE, + EntityType.QUERY, + EntityType.TEST_CASE, + ], + }, + }, + ], + }, + }, + ], + }, + }, + }); + }); + + it('should create filter with nested domain fqn', () => { + const domainFqn = 'parent.child.grandchild'; + const dataProductFqn = 'product.subproduct'; + + const result = getQueryFilterToIncludeDomain(domainFqn, dataProductFqn); + + const firstMust = result.query.bool.must[0] as { + term: { 'domains.fullyQualifiedName': string }; + }; + + expect(firstMust.term['domains.fullyQualifiedName']).toBe(domainFqn); + + const secondMust = result.query.bool.must[1] as { + bool: { + must_not: Array<{ + term: { 'dataProducts.fullyQualifiedName': string }; + }>; + }; + }; + + expect( + secondMust.bool.must_not[0].term['dataProducts.fullyQualifiedName'] + ).toBe(dataProductFqn); + }); + + it('should exclude specific entity types', () => { + const domainFqn = 'testDomain'; + const dataProductFqn = 'testDataProduct'; + + const result = getQueryFilterToIncludeDomain(domainFqn, dataProductFqn); + + const thirdMust = result.query.bool.must[2] as { + bool: { + must_not: Array<{ + terms: { entityType: EntityType[] }; + }>; + }; + }; + const excludedEntityTypes = thirdMust.bool.must_not[0].terms.entityType; + + expect(excludedEntityTypes).toContain(EntityType.DATA_PRODUCT); + expect(excludedEntityTypes).toContain(EntityType.TEST_SUITE); + expect(excludedEntityTypes).toContain(EntityType.QUERY); + expect(excludedEntityTypes).toContain(EntityType.TEST_CASE); + expect(excludedEntityTypes).toHaveLength(4); + }); + + it('should handle empty string parameters', () => { + const domainFqn = ''; + const dataProductFqn = ''; + + const result = getQueryFilterToIncludeDomain(domainFqn, dataProductFqn); + + const firstMust = result.query.bool.must[0] as { + term: { 'domains.fullyQualifiedName': string }; + }; + + expect(firstMust.term['domains.fullyQualifiedName']).toBe(''); + + const secondMust = result.query.bool.must[1] as { + bool: { + must_not: Array<{ + term: { 'dataProducts.fullyQualifiedName': string }; + }>; + }; + }; + + expect( + secondMust.bool.must_not[0].term['dataProducts.fullyQualifiedName'] + ).toBe(''); + }); + }); }); 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 c7af4f6efcc..69e33375a9f 100644 --- a/openmetadata-ui/src/main/resources/ui/src/utils/DomainUtils.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/utils/DomainUtils.tsx @@ -103,8 +103,13 @@ export const getQueryFilterToIncludeDomain = ( bool: { must_not: [ { - term: { - entityType: 'dataProduct', + terms: { + entityType: [ + EntityType.DATA_PRODUCT, + EntityType.TEST_SUITE, + EntityType.QUERY, + EntityType.TEST_CASE, + ], }, }, ],