-
Notifications
You must be signed in to change notification settings - Fork 1.7k
feat: Add data dimensions, health, incident metrics, and test case status translations in multiple languages #26765
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
ShaileshParmar11
wants to merge
13
commits into
main
Choose a base branch
from
dq-dashboard-migrate
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+6,412
−76
Open
Changes from 3 commits
Commits
Show all changes
13 commits
Select commit
Hold shift + click to select a range
1a90fe4
feat: Add data dimensions, health, incident metrics, and test case st…
ShaileshParmar11 c92454d
refactor: Simplify query formatting and clean up JSX structure in Dat…
ShaileshParmar11 8c7bf92
feat: Add DATA_OBSERVABILITY tab to Domain and Glossary detail pages,…
ShaileshParmar11 5db2caf
Merge remote-tracking branch 'origin/main' into dq-dashboard-migrate
ShaileshParmar11 0713ebd
Enhance localization and UI components
ShaileshParmar11 80071ca
Refactor TagPage layout by wrapping DataQualityDashboard in a div for…
ShaileshParmar11 ca28e1b
Update localization for "data-dimensions" in multiple languages and r…
ShaileshParmar11 adcf4fa
Refactor GlossaryTermClassBase tests to use React types for childProps
ShaileshParmar11 ce121ee
Remove unnecessary newline in DQ_FILTER_KEYS export
ShaileshParmar11 615907c
Enhance DataQualityDashboard and TagPage tests with new query logic a…
ShaileshParmar11 d68b8f1
Format expect assertion in TagPage test for improved readability
ShaileshParmar11 859a5a3
Merge remote-tracking branch 'origin/main' into dq-dashboard-migrate
ShaileshParmar11 4f2bfd7
address review comment
ShaileshParmar11 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
31 changes: 31 additions & 0 deletions
31
...i/src/main/resources/ui/src/components/DataQuality/ChartWidgets/ChartWidgets.constants.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,31 @@ | ||
| /* | ||
| * Copyright 2024 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 { TestCaseStatus } from '../../../generated/entity/feed/testCaseResult'; | ||
|
|
||
| /** Segment order for TestCaseStatusPieChartWidget: Success, Failed, Aborted */ | ||
| export const TEST_CASE_STATUS_PIE_SEGMENT_ORDER: TestCaseStatus[] = [ | ||
| TestCaseStatus.Success, | ||
| TestCaseStatus.Failed, | ||
| TestCaseStatus.Aborted, | ||
| ]; | ||
|
|
||
| /** | ||
| * Shared segment order for binary-status pie charts (Success, Failed). | ||
| * Used by EntityHealthStatusPieChartWidget (Healthy → Success, Unhealthy → Failed) | ||
| * and DataAssetsCoveragePieChartWidget (Covered → Success, Not covered → Failed). | ||
| */ | ||
| export const BINARY_STATUS_PIE_SEGMENT_ORDER: TestCaseStatus[] = [ | ||
| TestCaseStatus.Success, | ||
| TestCaseStatus.Failed, | ||
| ]; |
137 changes: 137 additions & 0 deletions
137
...rtWidgets/DataAssetsCoveragePieChartWidget/DataAssetsCoveragePieChartWidget.component.tsx
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,137 @@ | ||
| /* | ||
| * Copyright 2024 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 { Card, Typography } from 'antd'; | ||
| import { ReactComponent as DataAssetsCoverageIcon } from '../../../../assets/svg/ic-data-assets-coverage.svg'; | ||
| import CustomPieChart from '../../../Visualisations/Chart/CustomPieChart.component'; | ||
| import type { CustomPieChartData } from '../../../Visualisations/Chart/Chart.interface'; | ||
| import { GREEN_3, RED_3 } from '../../../../constants/Color.constants'; | ||
| import { ROUTES } from '../../../../constants/constants'; | ||
| import { INITIAL_DATA_ASSETS_COVERAGE_STATES } from '../../../../constants/profiler.constant'; | ||
| import { DataQualityPageTabs } from '../../../../pages/DataQuality/DataQualityPage.interface'; | ||
| import { | ||
| fetchEntityCoveredWithDQ, | ||
| fetchTotalEntityCount, | ||
| } from '../../../../rest/dataQualityDashboardAPI'; | ||
| import { getDataQualityPagePath } from '../../../../utils/RouterUtils'; | ||
| import { useCallback, useEffect, useMemo, useState } from 'react'; | ||
| import { useTranslation } from 'react-i18next'; | ||
| import { useNavigate } from 'react-router-dom'; | ||
| import { getPieChartLabel } from '../../../../utils/DataQuality/DataQualityUtils'; | ||
| import { PieChartWidgetCommonProps } from '../../DataQuality.interface'; | ||
| import '../chart-widgets.less'; | ||
|
|
||
| const DataAssetsCoveragePieChartWidget = ({ | ||
| className = '', | ||
| chartFilter, | ||
| }: PieChartWidgetCommonProps) => { | ||
| const { t } = useTranslation(); | ||
| const navigate = useNavigate(); | ||
| const [isLoading, setIsLoading] = useState(true); | ||
| const [dataAssetsCoverageStates, setDataAssetsCoverageStates] = useState<{ | ||
| covered: number; | ||
| notCovered: number; | ||
| total: number; | ||
| }>(INITIAL_DATA_ASSETS_COVERAGE_STATES); | ||
|
|
||
| const handleSegmentClick = useCallback( | ||
| (_entry: CustomPieChartData, index: number) => { | ||
| if (index === 0) { | ||
| navigate(getDataQualityPagePath(DataQualityPageTabs.TEST_SUITES)); | ||
| } else if (index === 1) { | ||
| navigate(ROUTES.EXPLORE); | ||
| } | ||
| }, | ||
| [navigate] | ||
| ); | ||
|
|
||
| const { data, chartLabel } = useMemo( | ||
| () => ({ | ||
| data: [ | ||
| { | ||
| name: t('label.covered'), | ||
| value: dataAssetsCoverageStates.covered, | ||
| color: GREEN_3, | ||
| }, | ||
| { | ||
| name: t('label.not-covered'), | ||
| value: dataAssetsCoverageStates.notCovered, | ||
| color: RED_3, | ||
| }, | ||
| ], | ||
| chartLabel: getPieChartLabel( | ||
| t('label.table-plural'), | ||
| dataAssetsCoverageStates.total | ||
| ), | ||
| }), | ||
| [dataAssetsCoverageStates] | ||
| ); | ||
|
|
||
| const fetchDataAssetsCoverage = async () => { | ||
| setIsLoading(true); | ||
| try { | ||
| const { data: coverageData } = await fetchEntityCoveredWithDQ( | ||
| chartFilter, | ||
| false | ||
| ); | ||
| const { data: totalData } = await fetchTotalEntityCount(chartFilter); | ||
| if (coverageData.length === 0 || totalData.length === 0) { | ||
| setDataAssetsCoverageStates(INITIAL_DATA_ASSETS_COVERAGE_STATES); | ||
| } | ||
|
|
||
| const covered = parseInt(coverageData[0].originEntityFQN); | ||
| let total = parseInt(totalData[0].fullyQualifiedName); | ||
|
|
||
| if (covered > total) { | ||
| total = covered; | ||
| } | ||
|
|
||
| setDataAssetsCoverageStates({ | ||
| covered, | ||
| notCovered: total - covered, | ||
| total: total, | ||
| }); | ||
| } catch { | ||
| setDataAssetsCoverageStates(INITIAL_DATA_ASSETS_COVERAGE_STATES); | ||
| } finally { | ||
| setIsLoading(false); | ||
| } | ||
| }; | ||
|
|
||
| useEffect(() => { | ||
| fetchDataAssetsCoverage(); | ||
| }, [chartFilter]); | ||
|
|
||
| return ( | ||
| <Card className={className} loading={isLoading}> | ||
| <div className="d-flex flex-column items-center"> | ||
| <div className="d-flex items-center gap-2"> | ||
| <div className="custom-chart-icon-background data-assets-coverage-icon icon-container"> | ||
| <DataAssetsCoverageIcon /> | ||
| </div> | ||
| <Typography.Text className="font-medium text-md"> | ||
| {t('label.data-asset-plural-coverage')} | ||
| </Typography.Text> | ||
| </div> | ||
| <CustomPieChart | ||
| showLegends | ||
| data={data} | ||
| label={chartLabel} | ||
| name="data-assets-coverage" | ||
| onSegmentClick={handleSegmentClick} | ||
| /> | ||
| </div> | ||
| </Card> | ||
| ); | ||
| }; | ||
|
|
||
| export default DataAssetsCoveragePieChartWidget; | ||
163 changes: 163 additions & 0 deletions
163
...y/ChartWidgets/DataAssetsCoveragePieChartWidget/DataAssetsCoveragePieChartWidget.test.tsx
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,163 @@ | ||
| /* | ||
| * Copyright 2024 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, render, screen } from '@testing-library/react'; | ||
| import { | ||
| fetchEntityCoveredWithDQ, | ||
| fetchTotalEntityCount, | ||
| } from '../../../../rest/dataQualityDashboardAPI'; | ||
| import CustomPieChart from '../../../Visualisations/Chart/CustomPieChart.component'; | ||
| import DataAssetsCoveragePieChartWidget from './DataAssetsCoveragePieChartWidget.component'; | ||
|
|
||
| jest.mock('react-router-dom', () => { | ||
| const actual = | ||
| jest.requireActual<typeof import('react-router-dom')>('react-router-dom'); | ||
| const mockNavigate = jest.fn(); | ||
|
|
||
| return { | ||
| ...actual, | ||
| useNavigate: () => mockNavigate, | ||
| __getMockNavigate: () => mockNavigate, | ||
| }; | ||
| }); | ||
|
|
||
| jest.mock('../../../../rest/dataQualityDashboardAPI', () => ({ | ||
| fetchEntityCoveredWithDQ: jest.fn().mockResolvedValue({ data: [] }), | ||
| fetchTotalEntityCount: jest.fn().mockResolvedValue({ data: [] }), | ||
| })); | ||
|
|
||
| jest.mock('../../../../utils/RouterUtils', () => ({ | ||
| getDataQualityPagePath: jest.fn((tab: string) => `/data-quality/${tab}`), | ||
| })); | ||
|
|
||
| jest.mock( | ||
| '../../../Visualisations/Chart/CustomPieChart.component', | ||
| () => | ||
| jest | ||
| .fn() | ||
| .mockImplementation( | ||
| (props: { onSegmentClick?: (e: unknown, i: number) => void }) => ( | ||
| <div> | ||
| CustomPieChart.component | ||
| <button | ||
| data-testid="segment-covered" | ||
| onClick={() => | ||
| props.onSegmentClick?.({ name: 'Covered', value: 1 }, 0) | ||
| } | ||
| /> | ||
| <button | ||
| data-testid="segment-not-covered" | ||
| onClick={() => | ||
| props.onSegmentClick?.({ name: 'Not covered', value: 0 }, 1) | ||
| } | ||
| /> | ||
| </div> | ||
| ) | ||
| ) | ||
| ); | ||
|
|
||
| describe('DataAssetsCoveragePieChartWidget', () => { | ||
| beforeEach(() => { | ||
| jest.clearAllMocks(); | ||
| }); | ||
|
|
||
| it('should render the component', async () => { | ||
| render(<DataAssetsCoveragePieChartWidget />); | ||
|
|
||
| expect( | ||
| await screen.findByText('label.data-asset-plural-coverage') | ||
| ).toBeInTheDocument(); | ||
| expect( | ||
| await screen.findByText('CustomPieChart.component') | ||
| ).toBeInTheDocument(); | ||
| }); | ||
|
|
||
| it('fetchEntityCoveredWithDQ & fetchTotalEntityCount should be called', async () => { | ||
| render(<DataAssetsCoveragePieChartWidget />); | ||
|
|
||
| await act(async () => { | ||
| await Promise.resolve(); | ||
| }); | ||
|
|
||
| expect(fetchEntityCoveredWithDQ).toHaveBeenCalledTimes(1); | ||
| expect(fetchTotalEntityCount).toHaveBeenCalledTimes(1); | ||
| }); | ||
|
|
||
| it('fetchEntityCoveredWithDQ & fetchTotalEntityCount should be called with filter if provided via props', async () => { | ||
| const filters = { | ||
| tier: ['tier'], | ||
| tags: ['tag1', 'tag2'], | ||
| ownerFqn: 'ownerFqn', | ||
| }; | ||
| render(<DataAssetsCoveragePieChartWidget chartFilter={filters} />); | ||
|
|
||
| await act(async () => { | ||
| await Promise.resolve(); | ||
| }); | ||
|
|
||
| expect(fetchEntityCoveredWithDQ).toHaveBeenCalledWith(filters, false); | ||
| expect(fetchTotalEntityCount).toHaveBeenCalledWith(filters); | ||
| }); | ||
|
|
||
| it('should pass onSegmentClick to CustomPieChart and navigate to Test Suites on Covered click', async () => { | ||
| const { getDataQualityPagePath } = jest.requireMock( | ||
| '../../../../utils/RouterUtils' | ||
| ) as { getDataQualityPagePath: jest.Mock }; | ||
| const mockNavigate = ( | ||
| jest.requireMock('react-router-dom') as { | ||
| __getMockNavigate: () => jest.Mock; | ||
| } | ||
| ).__getMockNavigate(); | ||
|
|
||
| render(<DataAssetsCoveragePieChartWidget />); | ||
|
|
||
| await act(async () => { | ||
| await Promise.resolve(); | ||
| }); | ||
|
|
||
| expect(CustomPieChart).toHaveBeenCalledWith( | ||
| expect.objectContaining({ | ||
| onSegmentClick: expect.any(Function), | ||
| }), | ||
| expect.anything() | ||
| ); | ||
|
|
||
| const segmentCovered = await screen.findByTestId('segment-covered'); | ||
| await act(async () => { | ||
| segmentCovered.click(); | ||
| }); | ||
|
|
||
| expect(getDataQualityPagePath).toHaveBeenCalledWith('test-suites'); | ||
| expect(mockNavigate).toHaveBeenCalledWith('/data-quality/test-suites'); | ||
| }); | ||
|
|
||
| it('should navigate to Explore on Not covered segment click', async () => { | ||
| const mockNavigate = ( | ||
| jest.requireMock('react-router-dom') as { | ||
| __getMockNavigate: () => jest.Mock; | ||
| } | ||
| ).__getMockNavigate(); | ||
|
|
||
| render(<DataAssetsCoveragePieChartWidget />); | ||
|
|
||
| await act(async () => { | ||
| await Promise.resolve(); | ||
| }); | ||
|
|
||
| const segmentNotCovered = await screen.findByTestId('segment-not-covered'); | ||
| await act(async () => { | ||
| segmentNotCovered.click(); | ||
| }); | ||
|
|
||
| expect(mockNavigate).toHaveBeenCalledWith('/explore'); | ||
| }); | ||
| }); |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When
coverageDataortotalDatais empty, the code setsINITIAL_DATA_ASSETS_COVERAGE_STATESbut then still accessescoverageData[0]/totalData[0], resulting inNaNvalues (or runtime errors). Return immediately after setting the initial state (or guard the parsing logic) so the state remains valid when the API returns no buckets.