From a2d9782d23238094a91a9169771b929e4f98a62a Mon Sep 17 00:00:00 2001 From: Harshit Shah Date: Thu, 4 Jun 2026 22:10:55 +0530 Subject: [PATCH 01/12] feat(data-product): add data product support in Observability UI - Show inherited domain and data product links on test case details page header - Add data product filter to incident manager (test case list) page - Add Data Observability tab to Data Product details page pre-filtered by data product FQN Closes #27073 Co-Authored-By: Claude Sonnet 4.6 --- .../IncidentManagerPageHeader.component.tsx | 42 ++++++++++++++++++- .../IncidentManager.component.tsx | 36 ++++++++++++++++ .../TestCaseClassBase.ts | 2 + .../ui/src/rest/incidentManagerAPI.ts | 1 + .../utils/DataProduct/DataProductClassBase.ts | 1 + .../ui/src/utils/DataProductUtils.tsx | 27 ++++++++++++ 6 files changed, 108 insertions(+), 1 deletion(-) diff --git a/openmetadata-ui/src/main/resources/ui/src/components/DataQuality/IncidentManager/IncidentManagerPageHeader/IncidentManagerPageHeader.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/DataQuality/IncidentManager/IncidentManagerPageHeader/IncidentManagerPageHeader.component.tsx index 55f7ca063af2..0764d5d829c6 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/DataQuality/IncidentManager/IncidentManagerPageHeader/IncidentManagerPageHeader.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/DataQuality/IncidentManager/IncidentManagerPageHeader/IncidentManagerPageHeader.component.tsx @@ -47,12 +47,16 @@ import { getCommonExtraInfoForVersionDetails } from '../../../../utils/EntityVer import { getEntityFQN } from '../../../../utils/FeedUtils'; import { getNameFromFQN } from '../../../../utils/FqnUtils'; import { getPrioritizedEditPermission } from '../../../../utils/PermissionsUtils'; -import { getEntityDetailsPath } from '../../../../utils/RouterUtils'; +import { + getDataProductDetailsPath, + getEntityDetailsPath, +} from '../../../../utils/RouterUtils'; import { getTaskDisplayId } from '../../../../utils/TasksUtils'; import { getTaskDetailPath as getNewTaskDetailPath } from '../../../../utils/TaskUtils'; import { showErrorToast } from '../../../../utils/ToastUtils'; import { useRequiredParams } from '../../../../utils/useRequiredParams'; import { useActivityFeedProvider } from '../../../ActivityFeed/ActivityFeedProvider/ActivityFeedProvider'; +import { DomainLabel } from '../../../common/DomainLabel/DomainLabel.component'; import { OwnerLabel } from '../../../common/OwnerLabel/OwnerLabel.component'; import { ProfilerTabPath } from '../../../Database/Profiler/ProfilerDashboard/profilerDashboard.interface'; import Severity from '../Severity/Severity.component'; @@ -438,6 +442,42 @@ const IncidentManagerPageHeader = ({ + {testCaseData?.domains && testCaseData.domains.length > 0 && ( + <> + + + + )} + {testCaseData?.dataProducts && testCaseData.dataProducts.length > 0 && ( + <> + + + + {t('label.data-product-plural')} + + {testCaseData.dataProducts.map((dp) => ( + + {getEntityName(dp)} + + + ))} + + + )} ); }; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/IncidentManager/IncidentManager.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/IncidentManager/IncidentManager.component.tsx index 5654b7c3a82a..8d3610de393f 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/IncidentManager/IncidentManager.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/IncidentManager/IncidentManager.component.tsx @@ -540,6 +540,28 @@ const IncidentManager = ({ } }; + const searchDataProducts = async (searchValue = WILD_CARD_CHAR) => { + const query = + searchValue === WILD_CARD_CHAR ? searchValue : `*${searchValue}*`; + try { + const response = await searchQuery({ + pageNumber: 1, + pageSize: PAGE_SIZE_BASE, + searchIndex: SearchIndex.DATA_PRODUCT, + query, + fetchSource: true, + includeFields: ['name', 'displayName', 'fullyQualifiedName'], + }); + + return response.hits.hits.map((hit) => ({ + label: getEntityName(hit._source), + value: hit._source.fullyQualifiedName, + })); + } catch { + return []; + } + }; + useEffect(() => { if ( commonTestCasePermission?.ViewAll || @@ -757,6 +779,20 @@ const IncidentManager = ({ ))} + + updateFilters({ dataProductFqn: value })} + /> + {isDateRangePickerVisible && (
({ id: tab, diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/DataProductUtils.tsx b/openmetadata-ui/src/main/resources/ui/src/utils/DataProductUtils.tsx index f92eeec5c40b..b09edd618178 100644 --- a/openmetadata-ui/src/main/resources/ui/src/utils/DataProductUtils.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/utils/DataProductUtils.tsx @@ -29,6 +29,7 @@ import { CommonWidgets } from '../components/DataAssets/CommonWidgets/CommonWidg import { ContractTab } from '../components/DataContract/ContractTab/ContractTab'; import { DataProductDomainWidget } from '../components/DataProducts/DataProductDomainWidget/DataProductDomainWidget'; import { InputOutputPortsTab } from '../components/DataProducts/InputOutputPortsTab'; +import DataQualityDashboard from '../components/DataQuality/DataQualityDashboard/DataQualityDashboard.component'; import EntitySummaryPanel from '../components/Explore/EntitySummaryPanel/EntitySummaryPanel.component'; import { EntityDetailsObjectInterface } from '../components/Explore/ExplorePage.interface'; import AssetsTabs, { @@ -293,6 +294,32 @@ export const getDataProductDetailTabs = ({ key: EntityTabs.CONTRACT, children: , }, + { + label: ( + + ), + key: EntityTabs.DATA_OBSERVABILITY, + children: ( + + ), + }, ]), { label: ( From a44cfcde6eada5846d98e7c7c280de99550f222f Mon Sep 17 00:00:00 2001 From: Harshit Shah Date: Thu, 4 Jun 2026 22:28:04 +0530 Subject: [PATCH 02/12] revert: remove data product filter from incident manager list page Backend does not index dataProducts in TestCaseResolutionStatus search index and the listFromSearch endpoint has no dataProductFqn param. Reverting until backend support is added. Co-Authored-By: Claude Sonnet 4.6 --- .../IncidentManager.component.tsx | 36 ------------------- .../ui/src/rest/incidentManagerAPI.ts | 1 - 2 files changed, 37 deletions(-) diff --git a/openmetadata-ui/src/main/resources/ui/src/components/IncidentManager/IncidentManager.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/IncidentManager/IncidentManager.component.tsx index 8d3610de393f..5654b7c3a82a 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/IncidentManager/IncidentManager.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/IncidentManager/IncidentManager.component.tsx @@ -540,28 +540,6 @@ const IncidentManager = ({ } }; - const searchDataProducts = async (searchValue = WILD_CARD_CHAR) => { - const query = - searchValue === WILD_CARD_CHAR ? searchValue : `*${searchValue}*`; - try { - const response = await searchQuery({ - pageNumber: 1, - pageSize: PAGE_SIZE_BASE, - searchIndex: SearchIndex.DATA_PRODUCT, - query, - fetchSource: true, - includeFields: ['name', 'displayName', 'fullyQualifiedName'], - }); - - return response.hits.hits.map((hit) => ({ - label: getEntityName(hit._source), - value: hit._source.fullyQualifiedName, - })); - } catch { - return []; - } - }; - useEffect(() => { if ( commonTestCasePermission?.ViewAll || @@ -779,20 +757,6 @@ const IncidentManager = ({ ))} - - updateFilters({ dataProductFqn: value })} - /> - {isDateRangePickerVisible && (
Date: Thu, 4 Jun 2026 22:36:41 +0530 Subject: [PATCH 03/12] feat(data-product): add data product filter to test case list page - Add dataProductFqn param to ListTestCaseParamsBySearch and TestCaseSearchParams - Add dataProduct entry to TEST_CASE_FILTERS and TEST_CASE_FILTERS_LABELS - Wire dataProductFqn in buildTestCaseParams - Add Data Product filter in TestCases advanced filter UI - Revert IncidentManagerPageHeader and TestCaseClassBase (not in scope) Co-Authored-By: Claude Sonnet 4.6 --- .../DataQuality/DataQuality.interface.ts | 1 + .../IncidentManagerPageHeader.component.tsx | 42 +---------- .../TestCases/TestCases.component.tsx | 69 +++++++++++++++++++ .../ui/src/constants/profiler.constant.ts | 2 + .../TestCaseClassBase.ts | 2 - .../src/main/resources/ui/src/rest/testAPI.ts | 1 + .../utils/DataQuality/DataQualityUtils.tsx | 1 + 7 files changed, 75 insertions(+), 43 deletions(-) diff --git a/openmetadata-ui/src/main/resources/ui/src/components/DataQuality/DataQuality.interface.ts b/openmetadata-ui/src/main/resources/ui/src/components/DataQuality/DataQuality.interface.ts index 094f6c590efb..81e0d0bd03f8 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/DataQuality/DataQuality.interface.ts +++ b/openmetadata-ui/src/main/resources/ui/src/components/DataQuality/DataQuality.interface.ts @@ -44,6 +44,7 @@ export type TestCaseSearchParams = { tags?: string; serviceName?: string; dataQualityDimension?: string; + dataProductFqn?: string; }; export type DataQualityPageParams = TestCaseSearchParams & { diff --git a/openmetadata-ui/src/main/resources/ui/src/components/DataQuality/IncidentManager/IncidentManagerPageHeader/IncidentManagerPageHeader.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/DataQuality/IncidentManager/IncidentManagerPageHeader/IncidentManagerPageHeader.component.tsx index 0764d5d829c6..55f7ca063af2 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/DataQuality/IncidentManager/IncidentManagerPageHeader/IncidentManagerPageHeader.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/DataQuality/IncidentManager/IncidentManagerPageHeader/IncidentManagerPageHeader.component.tsx @@ -47,16 +47,12 @@ import { getCommonExtraInfoForVersionDetails } from '../../../../utils/EntityVer import { getEntityFQN } from '../../../../utils/FeedUtils'; import { getNameFromFQN } from '../../../../utils/FqnUtils'; import { getPrioritizedEditPermission } from '../../../../utils/PermissionsUtils'; -import { - getDataProductDetailsPath, - getEntityDetailsPath, -} from '../../../../utils/RouterUtils'; +import { getEntityDetailsPath } from '../../../../utils/RouterUtils'; import { getTaskDisplayId } from '../../../../utils/TasksUtils'; import { getTaskDetailPath as getNewTaskDetailPath } from '../../../../utils/TaskUtils'; import { showErrorToast } from '../../../../utils/ToastUtils'; import { useRequiredParams } from '../../../../utils/useRequiredParams'; import { useActivityFeedProvider } from '../../../ActivityFeed/ActivityFeedProvider/ActivityFeedProvider'; -import { DomainLabel } from '../../../common/DomainLabel/DomainLabel.component'; import { OwnerLabel } from '../../../common/OwnerLabel/OwnerLabel.component'; import { ProfilerTabPath } from '../../../Database/Profiler/ProfilerDashboard/profilerDashboard.interface'; import Severity from '../Severity/Severity.component'; @@ -442,42 +438,6 @@ const IncidentManagerPageHeader = ({ - {testCaseData?.domains && testCaseData.domains.length > 0 && ( - <> - - - - )} - {testCaseData?.dataProducts && testCaseData.dataProducts.length > 0 && ( - <> - - - - {t('label.data-product-plural')} - - {testCaseData.dataProducts.map((dp) => ( - - {getEntityName(dp)} - - - ))} - - - )} ); }; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/DataQuality/TestCases/TestCases.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/DataQuality/TestCases/TestCases.component.tsx index c988df695f78..4814fa19b832 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/DataQuality/TestCases/TestCases.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/DataQuality/TestCases/TestCases.component.tsx @@ -106,6 +106,9 @@ export const TestCases = () => { const [tagOptions, setTagOptions] = useState([]); const [tierOptions, setTierOptions] = useState([]); const [serviceOptions, setServiceOptions] = useState([]); + const [dataProductOptions, setDataProductOptions] = useState< + DefaultOptionType[] + >([]); const [sortOptions, setSortOptions] = useState(DEFAULT_SORT_ORDER); @@ -393,6 +396,44 @@ export const TestCases = () => { } }; + const fetchDataProductOptions = async (search = WILD_CARD_CHAR) => { + setIsOptionsLoading(true); + try { + const response = await searchQuery({ + query: search === WILD_CARD_CHAR ? search : `*${search}*`, + pageNumber: 1, + pageSize: PAGE_SIZE_BASE, + searchIndex: SearchIndex.DATA_PRODUCT, + fetchSource: true, + includeFields: ['name', 'fullyQualifiedName', 'displayName'], + }); + + const options = response.hits.hits.map((hit) => { + return { + label: ( + + + {hit._source.fullyQualifiedName} + + + {getEntityName(hit._source)} + + + ), + value: hit._source.fullyQualifiedName, + }; + }); + setDataProductOptions(options); + } catch { + setDataProductOptions([]); + } finally { + setIsOptionsLoading(false); + } + }; + const getInitialOptions = (key: string, isLengthCheck = false) => { switch (key) { case TEST_CASE_FILTERS.tier: @@ -412,6 +453,12 @@ export const TestCases = () => { break; + case TEST_CASE_FILTERS.dataProduct: + (isEmpty(dataProductOptions) || !isLengthCheck) && + fetchDataProductOptions(); + + break; + default: break; } @@ -512,6 +559,11 @@ export const TestCases = () => { [fetchServiceOptions] ); + const debounceFetchDataProductOptions = useCallback( + debounce(fetchDataProductOptions, 1000), + [fetchDataProductOptions] + ); + const getTestCases = () => { if (!isEmpty(params) || !isEmpty(selectedFilter)) { const updatedValue = uniq([...selectedFilter, ...Object.keys(params)]); @@ -723,6 +775,23 @@ export const TestCases = () => { /> )} + {selectedFilter.includes(TEST_CASE_FILTERS.dataProduct) && ( + +