fix: Show error on DBInfraPanel when correlated metric source is missing (#2049)

## Summary

This PR adds an error message on the DB Infra Panel when the selected source is missing a correlated metric source.

Previously, this case would have just resulted in an empty page, with no indication of what was wrong.

### Screenshots or video

<img width="2038" height="367" alt="Screenshot 2026-04-03 at 8 28 22 AM" src="https://github.com/user-attachments/assets/4fde26c6-5ea8-4cf8-bdfa-9028ae48b15e" />

### How to test locally or on Vercel

This can be tested locally by creating a source that points to the demo dataset, without a correlated metric source (I suggest the demo dataset because it has K8s metrics).

### References

- Linear Issue:
- Related PRs:
This commit is contained in:
Drew Davis 2026-04-03 15:18:57 -04:00 committed by GitHub
parent d84237f98b
commit df170d1e40
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 102 additions and 1 deletions

View file

@ -0,0 +1,5 @@
---
"@hyperdx/app": patch
---
fix: Show error on DBInfraPanel when correlated metric source is missing

View file

@ -1,4 +1,5 @@
import { useMemo, useState } from 'react';
import Link from 'next/link';
import { add, min, sub } from 'date-fns';
import {
convertDateRangeToGranularityString,
@ -12,16 +13,23 @@ import {
TSource,
} from '@hyperdx/common-utils/dist/types';
import {
Alert,
Anchor,
Box,
Card,
Group,
Modal,
ScrollArea,
SegmentedControl,
SimpleGrid,
Stack,
Text,
} from '@mantine/core';
import { useDisclosure } from '@mantine/hooks';
import { convertV1ChartConfigToV2 } from '@/ChartUtils';
import { TableSourceForm } from '@/components/Sources/SourceForm';
import { IS_LOCAL_MODE } from '@/config';
import { useSource } from '@/source';
import {
@ -211,11 +219,14 @@ export default ({
rowId: string | undefined | null;
source: TSource;
}) => {
const [editModalOpened, { open: openEditModal, close: closeEditModal }] =
useDisclosure(false);
const metricSourceId =
isLogSource(source) || isTraceSource(source)
? source.metricSourceId
: undefined;
const { data: metricSource } = useSource({
const { data: metricSource, isLoading: isLoadingMetricSource } = useSource({
id: metricSourceId,
kinds: [SourceKind.Metric],
});
@ -227,6 +238,39 @@ export default ({
return (
<Stack my="md" gap={40}>
{!metricSource && !isLoadingMetricSource && (
<>
<Alert color="yellow" title="No correlated metric source">
<Text size="sm">
{metricSourceId
? `The correlated metric source for "${source.name}" could not be found.`
: `Source "${source.name}" does not have a correlated metric source.`}{' '}
Infrastructure metrics can be displayed when a metric source is
configured in{' '}
{IS_LOCAL_MODE ? (
<Anchor component="button" onClick={openEditModal}>
Source Settings
</Anchor>
) : (
<Anchor component={Link} href="/team">
Team Settings
</Anchor>
)}
.
</Text>
</Alert>
{IS_LOCAL_MODE && (
<Modal
size="xl"
opened={editModalOpened}
onClose={closeEditModal}
title="Edit Source"
>
<TableSourceForm sourceId={source.id} />
</Modal>
)}
</>
)}
{podUid && (
<div>
{metricSource && (

View file

@ -0,0 +1,35 @@
import { SearchPage } from '../page-objects/SearchPage';
import { expect, test } from '../utils/base-test';
test.describe('Correlated Metric Source', { tag: ['@full-stack'] }, () => {
let searchPage: SearchPage;
test.beforeEach(async ({ page }) => {
searchPage = new SearchPage(page);
});
test('should show alert when no correlated metric source is configured', async ({
page,
}) => {
// Navigate to search page
await searchPage.goto();
// Select the source without metricSourceId
await searchPage.selectSource('E2E K8s Logs No Metrics');
// Search for K8s events that have k8s.pod.uid resource attribute
await searchPage.performSearch('ResourceAttributes.k8s.pod.uid:*');
// Click on first row to open side panel
await searchPage.table.clickFirstRow();
// Click the Infrastructure tab
await searchPage.sidePanel.clickTab('infrastructure');
// Assert the "No correlated metric source" alert is visible
await expect(page.getByText('No correlated metric source')).toBeVisible();
await expect(
page.getByText('does not have a correlated metric source'),
).toBeVisible();
});
});

View file

@ -92,6 +92,23 @@
"traceIdExpression": "TraceId",
"spanIdExpression": "SpanId",
"implicitColumnExpression": "Body"
},
{
"id": "E2E K8s Logs No Metrics",
"kind": "log",
"name": "E2E K8s Logs No Metrics",
"connection": "local",
"from": { "databaseName": "default", "tableName": "e2e_otel_logs" },
"timestampValueExpression": "TimestampTime",
"defaultTableSelectExpression": "Timestamp, ServiceName, SeverityText, Body",
"serviceNameExpression": "ServiceName",
"severityTextExpression": "SeverityText",
"eventAttributesExpression": "LogAttributes",
"resourceAttributesExpression": "ResourceAttributes",
"traceIdExpression": "TraceId",
"spanIdExpression": "SpanId",
"implicitColumnExpression": "Body",
"displayedTimestampValueExpression": "Timestamp"
}
]
}