feat(ui): add Asset Health widget to the table detail page#29469
feat(ui): add Asset Health widget to the table detail page#29469siddhant1 wants to merge 7 commits into
Conversation
Surfaces an at-a-glance health summary in the table right rail covering pipeline runs, data quality tests, data observability incidents, and data contracts. Each row shows a status badge with a severity tone and renders a setup CTA (link pipeline, add tests, enable observability, create contract) when the category isn't configured yet. An overall header badge rolls the rows up to All clear / Running / Attention / Critical / Not set up. Wired in as a customizable detail-page widget (KnowledgePanel.AssetHealth) via TableClassBase and TableTabsUtils, with React Query-backed data fetching in useAssetHealth and pure mapping logic plus unit tests in the utils module. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
❌ PR checklist incompleteThis PR cannot be merged until the following are addressed on its linked issue:
The fields live on the linked issue in the Shipping project (open the issue → right sidebar → Projects). After you set them, re-run this check (or push a commit) — issue/project changes do not re-trigger it automatically. Maintainers can bypass this check by adding the |
|
Hi there 👋 Thanks for your contribution! The OpenMetadata team will review the PR shortly! Once it has been labeled as Let us know if you need any help! |
Replaces the raw tw:bg-white palette class on the widget's loading and main card surfaces with the semantic tw:bg-primary token, which remaps to the dark surface color under .dark-mode. All other color classes in the widget already use auto-inverting tokens from the color handbook. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The card border and header divider used utility-gray-blue-100, which inverts to gray-blue-900 (~#101323) in dark mode — nearly identical to the bg-primary card surface (~#0c0e12) — so the card edge and divider disappeared. Switch to the semantic border-secondary token, which stays subtle in light mode and is visible in dark mode. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… query errors) - getAssetHealthHeader: a warning-only roll-up now uses the amber Warning tone instead of the red Error tone; Error is reserved for actual error rows. - getDataQualityHealthRow: treat success < total (with no failed/queued/aborted bucket) as a Warning rather than green "Passing". - useAssetHealth: expose isError (aggregating the pipeline, summary and incident queries; the contract query is excluded because its 404 is the expected "no contract" case) and render an error state so a failed fetch no longer shows a falsely healthy "No anomalies"/setup CTAs. - Add header tone assertions and a success<total case to the unit tests. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
| let tone = AssetHealthTone.Success; | ||
| let badgeLabel = t('label.passing'); | ||
| if (failed > 0) { | ||
| tone = AssetHealthTone.Error; | ||
| badgeLabel = t('message.count-failed-test', { count: failed }); | ||
| } else if (queued > 0) { | ||
| tone = AssetHealthTone.Info; | ||
| badgeLabel = t('label.queued'); | ||
| } else if (aborted > 0) { | ||
| tone = AssetHealthTone.Warning; | ||
| badgeLabel = t('label.aborted'); | ||
| } else if (success < total) { | ||
| tone = AssetHealthTone.Warning; | ||
| } | ||
|
|
There was a problem hiding this comment.
💡 Quality: Warning-tone DQ row still shows 'Passing' badge label
In getDataQualityHealthRow, the new branch else if (success < total) { tone = AssetHealthTone.Warning; } (utils.ts:173-174) changes only the tone but leaves badgeLabel = t('label.passing') from line 163 unchanged. As a result, when some tests are neither failed, queued, nor aborted but success < total (e.g. total: 16, success: 14), the row renders a yellow Warning-tone badge that still reads "Passing". The badge text contradicts its tone. The added unit test only asserts row.tone and does not catch the mislabeled badge. Consider setting a more accurate label in this branch (e.g. a partial/incomplete label) so the badge text matches the Warning tone.
Use a distinct label for the partial-success Warning case so the badge text no longer reads 'Passing'.:
} else if (success < total) {
tone = AssetHealthTone.Warning;
badgeLabel = t('label.partial');
}
- Apply fix
Check the box to apply the fix or reply for a change | Was this helpful? React with 👍 / 👎
| // The contract query is excluded: getContractByEntityId returns a 404 for the | ||
| // common "no contract" case, which is already handled as the Create CTA row. | ||
| const isError = | ||
| pipelineQuery.isError || summaryQuery.isError || incidentsQuery.isError; | ||
|
|
||
| return { rows, isLoading, isError }; |
There was a problem hiding this comment.
💡 Edge Case: Single query failure hides all healthy Asset Health rows
isError is the OR of pipelineQuery.isError || summaryQuery.isError || incidentsQuery.isError (useAssetHealth.ts:105-106), and the component returns a full-widget error state when isError is true (AssetHealthWidget.component.tsx:99-110). This means a failure in any single category query (e.g. the incidents endpoint) replaces the entire widget with a generic error message, hiding the other three categories whose data loaded successfully. Since the four categories are independent, consider rendering the per-row data that did resolve and only showing an error/empty state for the category that failed, rather than collapsing the whole widget.
Was this helpful? React with 👍 / 👎
…ata) Drives the real app against demo sample_data tables (no seeded fixtures, no response mocking): asserts the widget renders all four category rows, that an unconfigured table (fact_sale) shows the Add tests / Enable / Create setup CTAs and each navigates to its tab, and that a table with a real test suite (dim_address) renders the data observability row as a status instead of a CTA. Exhaustive per-tone/transient/loading/error states remain covered by the AssetHealthWidget.utils unit tests, as noted in the spec header. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
🔴 Playwright Results — 30 failure(s), 83 flaky✅ 4379 passed · ❌ 30 failed · 🟡 83 flaky · ⏭️ 37 skipped
Genuine Failures (failed on all attempts)❌
|
Address design review on the Asset Health widget row: top-align the icon and the status badge/CTA to the text (items-start), bump the subtitle to 12px (text-xs), and cap the content column width (max-w-[220px]) so the long Contract description wraps before the right-aligned badge column instead of running under it. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Code Review 👍 Approved with suggestions 2 resolved / 4 findingsAdds a new Asset Health widget to the table detail page with comprehensive test coverage, though the pipeline query lacks specific ordering and error handling is overly broad. Please address the potential for masking failing pipelines and ensure partial failure states correctly affect individual row visibility. 💡 Quality: Warning-tone DQ row still shows 'Passing' badge labelIn Use a distinct label for the partial-success Warning case so the badge text no longer reads 'Passing'.💡 Edge Case: Single query failure hides all healthy Asset Health rows📄 openmetadata-ui/src/main/resources/ui/src/components/DataAssets/AssetHealthWidget/useAssetHealth.ts:103-108 📄 openmetadata-ui/src/main/resources/ui/src/components/DataAssets/AssetHealthWidget/AssetHealthWidget.component.tsx:99-110
✅ 2 resolved✅ Quality: Warning-only health rolls up to red Error tone in header
✅ Edge Case: Data quality shows green 'Passing' when not all tests succeeded
🤖 Prompt for agentsOptionsDisplay: compact → Showing less information. Comment with these commands to change the behavior for this request:
Was this helpful? React with 👍 / 👎 | Gitar |
| } else if (success < total) { | ||
| tone = AssetHealthTone.Warning; | ||
| } |
| it('should be a warning tone when success is below total without a known failure bucket', () => { | ||
| const row = getDataQualityHealthRow(true, { total: 16, success: 14 }); | ||
|
|
||
| expect(row.tone).toBe(AssetHealthTone.Warning); | ||
| }); |
| originEntityFQN: tableFqn, | ||
| }), | ||
| enabled: Boolean(testSuiteId), | ||
| }); |
| getDataObservabilityHealthRow( | ||
| Boolean(testSuiteId), | ||
| incidentsQuery.data?.data ?? [] | ||
| ), |
|



What this adds
A new Asset Health widget in the right rail of the table detail page that gives a single, scannable summary of an asset's operational health across four categories:
Each row renders a status badge with a severity tone. When a category isn't configured yet, the row shows a setup CTA instead — Link pipeline, Add tests, Enable, or Create — that navigates to the right place to set it up. An overall header badge rolls the rows up into All clear / Running / Attention / Critical / Not set up.
How it's built
KnowledgePanel.AssetHealth) viaTableClassBaseandTableTabsUtils, so it participates in the existing page-customization system.useAssetHealth(React Query); the contract query reuses the shared['contract', id, EntityType.TABLE]key.AssetHealthWidget.utils.ts, covered by unit tests inAssetHealthWidget.utils.test.ts.@openmetadata/ui-core-components(Badge, FeaturedIcon, Skeleton) and Untitled icons; all strings are i18n keys synced across all locales.Testing
yarn test AssetHealthWidget.utils.test.ts— 28/28 pass (pipeline / data-quality / observability / contract mappings + header roll-up + empty-subtitle edge cases)tsc --noEmitclean on the touched files;eslint+prettier --checkclean;yarn check-i18nreports locales in sync🤖 Generated with Claude Code
Greptile Summary
This PR adds an Asset Health widget to the table detail page's right rail, surfacing pipeline, data quality, data observability, and contract health in a single scannable panel. The widget is registered as a customizable
KnowledgePanel.AssetHealthwidget, data is fetched via a dedicateduseAssetHealthReact Query hook, and all status-to-presentation mappings are pure functions with 28 unit tests.AssetHealthWidget,AssetHealthRowItem,useAssetHealth) provides a four-row health summary with setup CTAs for unconfigured categories and an overall header badge rolling up all rows.TableClassBaseandTableTabsUtilswires the widget into the existing page-customization system with a default height of 3 grid units.sample_datatables.Confidence Score: 4/5
Safe to merge after addressing the badge-label mismatch in the data quality row.
In
getDataQualityHealthRow, thesuccess < totalfallback branch sets the tone to Warning but never updatesbadgeLabelfrom its initialt('label.passing')value. Any table whose test summary has more total tests than successful ones — but no recorded failures, queued, or aborted tests — will render a warning-colored badge that reads "Passing." The rest of the widget logic, data fetching, registration, and tests are well-structured.AssetHealthWidget.utils.ts — the
success < totalbranch ingetDataQualityHealthRowleaves an inconsistent badge label.Important Files Changed
success < totalfallback branch leavesbadgeLabelas "Passing" while setting a Warning tone.isError(excluding contract 404s intentionally); pipeline query is scoped to service level which is correct for the use case.success < totalwarning case only asserts tone, missing abadgeLabelassertion that would have caught the label bug.ASSET_HEALTHto the widget-height map andTableWidgetKeysunion; straightforward registration.AssetHealthWidgetunder theKnowledgePanel.AssetHealthwidget key; lazy-loaded correctly.Flowchart
%%{init: {'theme': 'neutral'}}%% flowchart TD AHW[AssetHealthWidget] --> UAH[useAssetHealth hook] UAH --> PQ[pipelineQuery\ngetIngestionPipelines\nby serviceFqn] UAH --> SQ[summaryQuery\ngetTestCaseExecutionSummary\nby testSuiteId] UAH --> IQ[incidentsQuery\ngetListTestCaseIncidentStatus\nby tableFqn] UAH --> CQ[contractQuery\ngetContractByEntityId\nby tableId] PQ --> GPH[getPipelineHealthRow] SQ --> GDQ[getDataQualityHealthRow] IQ --> GDO[getDataObservabilityHealthRow] CQ --> GCH[getContractHealthRow] GPH --> ROWS[rows array] GDQ --> ROWS GDO --> ROWS GCH --> ROWS ROWS --> GAH[getAssetHealthHeader] GAH --> BADGE[Header Badge: All clear / Running / Attention / Critical / Not set up] ROWS --> RI[AssetHealthRowItem x4] RI --> CTA{Has CTA?} CTA -- yes --> BTN[BadgeWithIcon button navigate] CTA -- no --> SBADGE[Status Badge]%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%% flowchart TD AHW[AssetHealthWidget] --> UAH[useAssetHealth hook] UAH --> PQ[pipelineQuery\ngetIngestionPipelines\nby serviceFqn] UAH --> SQ[summaryQuery\ngetTestCaseExecutionSummary\nby testSuiteId] UAH --> IQ[incidentsQuery\ngetListTestCaseIncidentStatus\nby tableFqn] UAH --> CQ[contractQuery\ngetContractByEntityId\nby tableId] PQ --> GPH[getPipelineHealthRow] SQ --> GDQ[getDataQualityHealthRow] IQ --> GDO[getDataObservabilityHealthRow] CQ --> GCH[getContractHealthRow] GPH --> ROWS[rows array] GDQ --> ROWS GDO --> ROWS GCH --> ROWS ROWS --> GAH[getAssetHealthHeader] GAH --> BADGE[Header Badge: All clear / Running / Attention / Critical / Not set up] ROWS --> RI[AssetHealthRowItem x4] RI --> CTA{Has CTA?} CTA -- yes --> BTN[BadgeWithIcon button navigate] CTA -- no --> SBADGE[Status Badge]Reviews (7): Last reviewed commit: "Merge branch 'main' into figma-asset-hea..." | Re-trigger Greptile