Skip to content

Commit 7884775

Browse files
authored
feat(aci): Use new metric detector charts in issue details (#102996)
1 parent ee244d0 commit 7884775

File tree

15 files changed

+291
-625
lines changed

15 files changed

+291
-625
lines changed

static/app/views/alerts/rules/metric/details/metricChart.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ function formatTooltipDate(date: moment.MomentInput, format: string): string {
9999
return moment(date).format(format);
100100
}
101101

102-
export function getRuleChangeSeries(
102+
function getRuleChangeSeries(
103103
rule: MetricRule,
104104
data: AreaChartSeries[],
105105
theme: Theme
@@ -517,7 +517,7 @@ export default function MetricChart({
517517
);
518518
}
519519

520-
export function getMetricChartTooltipFormatter({
520+
function getMetricChartTooltipFormatter({
521521
interval,
522522
rule,
523523
theme,

static/app/views/detectors/components/details/metric/chart.tsx

Lines changed: 105 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,20 @@ import {type Theme} from '@emotion/react';
33
import styled from '@emotion/styled';
44
import type {YAXisComponentOption} from 'echarts';
55

6-
import {AreaChart} from 'sentry/components/charts/areaChart';
6+
import {AreaChart, type AreaChartProps} from 'sentry/components/charts/areaChart';
77
import {defaultFormatAxisLabel} from 'sentry/components/charts/components/tooltip';
88
import ErrorPanel from 'sentry/components/charts/errorPanel';
99
import {useChartZoom} from 'sentry/components/charts/useChartZoom';
1010
import {Alert} from 'sentry/components/core/alert';
1111
import {Flex} from 'sentry/components/core/layout';
12+
import {normalizeDateTimeParams} from 'sentry/components/organizations/pageFilters/parse';
1213
import Placeholder from 'sentry/components/placeholder';
1314
import {IconWarning} from 'sentry/icons';
1415
import {t} from 'sentry/locale';
1516
import {space} from 'sentry/styles/space';
1617
import type {GroupOpenPeriod} from 'sentry/types/group';
1718
import type {MetricDetector, SnubaQuery} from 'sentry/types/workflowEngine/detectors';
19+
import type RequestError from 'sentry/utils/requestError/requestError';
1820
import {useLocation} from 'sentry/utils/useLocation';
1921
import {useNavigate} from 'sentry/utils/useNavigate';
2022
import {
@@ -79,15 +81,16 @@ interface MetricDetectorDetailsChartProps {
7981
}
8082
const CHART_HEIGHT = 180;
8183

82-
interface MetricDetectorChartProps {
84+
interface UseMetricDetectorChartProps {
8385
detector: MetricDetector;
84-
snubaQuery: SnubaQuery;
86+
openPeriods: GroupOpenPeriod[];
8587
/**
8688
* Relative time period (e.g., '7d'). Use either statsPeriod or absolute start/end.
8789
*/
88-
end?: string;
89-
start?: string;
90-
statsPeriod?: string;
90+
end?: string | null;
91+
height?: number;
92+
start?: string | null;
93+
statsPeriod?: string | null;
9194
}
9295

9396
function createTriggerIntervalMarkerData({
@@ -137,18 +140,25 @@ function createOpenPeriodMarkerData({
137140
}));
138141
}
139142

140-
function MetricDetectorChart({
143+
type UseMetricDetectorChartResult =
144+
| {chartProps: AreaChartProps; error: null; isLoading: false}
145+
| {chartProps: null; error: null; isLoading: true}
146+
| {chartProps: null; error: RequestError; isLoading: false};
147+
148+
export function useMetricDetectorChart({
141149
statsPeriod,
142150
start,
143151
end,
144-
snubaQuery,
145152
detector,
146-
}: MetricDetectorChartProps) {
153+
openPeriods,
154+
height = CHART_HEIGHT,
155+
}: UseMetricDetectorChartProps): UseMetricDetectorChartResult {
147156
const navigate = useNavigate();
148157
const location = useLocation();
149158
const detectionType = detector.config.detectionType;
150159
const comparisonDelta =
151160
detectionType === 'percent' ? detector.config.comparisonDelta : undefined;
161+
const snubaQuery = detector.dataSources[0].queryObj.snubaQuery;
152162
const dataset = getDetectorDataset(snubaQuery.dataset, snubaQuery.eventTypes);
153163
const datasetConfig = getDatasetConfig(dataset);
154164
const {series, comparisonSeries, isLoading, error} = useMetricDetectorSeries({
@@ -173,13 +183,6 @@ function MetricDetectorChart({
173183
comparisonSeries,
174184
});
175185

176-
const {data: openPeriods = []} = useOpenPeriods({
177-
detectorId: detector.id,
178-
start,
179-
end,
180-
statsPeriod,
181-
});
182-
183186
const incidentPeriods = useMemo(() => {
184187
return openPeriods.flatMap<IncidentPeriod>(period => [
185188
createTriggerIntervalMarkerData({
@@ -277,10 +280,10 @@ function MetricDetectorChart({
277280

278281
return axes;
279282
}, [
280-
maxValue,
281-
openPeriodMarkerResult.incidentMarkerYAxis,
282283
detectionType,
283284
snubaQuery.aggregate,
285+
maxValue,
286+
openPeriodMarkerResult.incidentMarkerYAxis,
284287
]);
285288

286289
const grid = useMemo(() => {
@@ -293,19 +296,93 @@ function MetricDetectorChart({
293296
};
294297
}, [openPeriodMarkerResult.incidentMarkerGrid]);
295298

299+
const chartProps = useMemo<AreaChartProps | null>(() => {
300+
if (isLoading || error) {
301+
return null;
302+
}
303+
return {
304+
showTimeInTooltip: true,
305+
height,
306+
stacked: false,
307+
series,
308+
additionalSeries,
309+
yAxes: yAxes.length > 1 ? yAxes : undefined,
310+
yAxis: yAxes.length === 1 ? yAxes[0] : undefined,
311+
grid,
312+
xAxis: openPeriodMarkerResult.incidentMarkerXAxis,
313+
tooltip: {
314+
valueFormatter: getDetectorChartFormatters({
315+
detectionType,
316+
aggregate: snubaQuery.aggregate,
317+
}).formatTooltipValue,
318+
},
319+
...chartZoomProps,
320+
onChartReady: chart => {
321+
chartZoomProps.onChartReady(chart);
322+
openPeriodMarkerResult.onChartReady(chart);
323+
},
324+
};
325+
}, [
326+
additionalSeries,
327+
chartZoomProps,
328+
detectionType,
329+
error,
330+
grid,
331+
height,
332+
isLoading,
333+
openPeriodMarkerResult,
334+
series,
335+
snubaQuery.aggregate,
336+
yAxes,
337+
]);
338+
339+
if (chartProps) {
340+
return {
341+
chartProps,
342+
error: null,
343+
isLoading: false,
344+
};
345+
}
346+
347+
if (error) {
348+
return {
349+
chartProps: null,
350+
error,
351+
isLoading: false,
352+
};
353+
}
354+
355+
return {
356+
isLoading: true,
357+
error: null,
358+
chartProps: null,
359+
};
360+
}
361+
362+
export function MetricDetectorDetailsChart({detector}: MetricDetectorDetailsChartProps) {
363+
const location = useLocation();
364+
const dateParams = normalizeDateTimeParams(location.query);
365+
366+
const {data: openPeriods = []} = useOpenPeriods({
367+
detectorId: detector.id,
368+
...dateParams,
369+
});
370+
371+
const {chartProps, isLoading, error} = useMetricDetectorChart({
372+
detector,
373+
openPeriods,
374+
height: CHART_HEIGHT,
375+
...dateParams,
376+
});
377+
296378
if (isLoading) {
297379
return (
298-
<ChartContainer>
299-
<ChartContainerBody>
300-
<Flex height={CHART_HEIGHT} justify="center" align="center">
301-
<Placeholder height={`${CHART_HEIGHT}px`} />
302-
</Flex>
303-
</ChartContainerBody>
304-
</ChartContainer>
380+
<Flex height={CHART_HEIGHT} justify="center" align="center">
381+
<Placeholder height={`${CHART_HEIGHT}px`} />
382+
</Flex>
305383
);
306384
}
307-
308-
if (error) {
385+
if (error || !chartProps) {
309386
const errorMessage =
310387
typeof error?.responseJSON?.detail === 'string' ? error.responseJSON.detail : null;
311388
return (
@@ -330,53 +407,12 @@ function MetricDetectorChart({
330407
return (
331408
<ChartContainer>
332409
<ChartContainerBody>
333-
<AreaChart
334-
showTimeInTooltip
335-
height={CHART_HEIGHT}
336-
stacked={false}
337-
series={series}
338-
additionalSeries={additionalSeries}
339-
yAxes={yAxes.length > 1 ? yAxes : undefined}
340-
yAxis={yAxes.length === 1 ? yAxes[0] : undefined}
341-
grid={grid}
342-
xAxis={openPeriodMarkerResult.incidentMarkerXAxis}
343-
tooltip={{
344-
valueFormatter: getDetectorChartFormatters({
345-
detectionType,
346-
aggregate: snubaQuery.aggregate,
347-
}).formatTooltipValue,
348-
}}
349-
{...chartZoomProps}
350-
onChartReady={chart => {
351-
chartZoomProps.onChartReady(chart);
352-
openPeriodMarkerResult.onChartReady(chart);
353-
}}
354-
/>
410+
<AreaChart {...chartProps} />
355411
</ChartContainerBody>
356412
</ChartContainer>
357413
);
358414
}
359415

360-
export function MetricDetectorDetailsChart({
361-
detector,
362-
snubaQuery,
363-
}: MetricDetectorDetailsChartProps) {
364-
const location = useLocation();
365-
const statsPeriod = location.query?.statsPeriod as string | undefined;
366-
const start = location.query?.start as string | undefined;
367-
const end = location.query?.end as string | undefined;
368-
const dateParams = start && end ? {start, end} : {statsPeriod};
369-
370-
return (
371-
<MetricDetectorChart
372-
detector={detector}
373-
// Pass snubaQuery separately to avoid checking null in all places
374-
snubaQuery={snubaQuery}
375-
{...dateParams}
376-
/>
377-
);
378-
}
379-
380416
const ChartContainer = styled('div')`
381417
border: 1px solid ${p => p.theme.border};
382418
border-radius: ${p => p.theme.borderRadius};

static/app/views/detectors/datasetConfig/base.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,18 +49,18 @@ interface DetectorSeriesQueryOptions {
4949
* The filter query. eg: `span.op:http`
5050
*/
5151
query: string;
52-
end?: string;
52+
end?: string | null;
5353
/**
5454
* Extra query parameters to pass
5555
*/
5656
extra?: {
5757
useOnDemandMetrics: 'true';
5858
};
59-
start?: string;
59+
start?: string | null;
6060
/**
6161
* Relative time period for the query. Example: '7d'.
6262
*/
63-
statsPeriod?: string;
63+
statsPeriod?: string | null;
6464
}
6565

6666
/**

static/app/views/detectors/datasetConfig/utils/discoverSeries.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -80,18 +80,18 @@ interface DiscoverSeriesQueryOptions {
8080
* The filter query. eg: `span.op:http`
8181
*/
8282
query: string;
83-
end?: string;
83+
end?: string | null;
8484
/**
8585
* Extra query parameters to pass
8686
*/
8787
extra?: {
8888
useOnDemandMetrics: 'true';
8989
};
90-
start?: string;
90+
start?: string | null;
9191
/**
9292
* Relative time period for the query. Example: '7d'.
9393
*/
94-
statsPeriod?: string;
94+
statsPeriod?: string | null;
9595
}
9696

9797
export function getDiscoverSeriesQueryOptions({

static/app/views/detectors/datasetConfig/utils/releasesSeries.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,12 +56,12 @@ interface ReleaseSeriesQueryOptions {
5656
* The filter query. eg: `span.op:http`
5757
*/
5858
query: string;
59-
end?: string;
60-
start?: string;
59+
end?: string | null;
60+
start?: string | null;
6161
/**
6262
* Relative time period for the query. Example: '7d'.
6363
*/
64-
statsPeriod?: string;
64+
statsPeriod?: string | null;
6565
}
6666

6767
export function getReleasesSeriesQueryOptions({

static/app/views/detectors/hooks/useMetricDetectorSeries.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,10 @@ interface UseMetricDetectorSeriesProps {
1818
projectId: string;
1919
query: string;
2020
comparisonDelta?: number;
21-
end?: string;
21+
end?: string | null;
2222
options?: Partial<UseApiQueryOptions<any>>;
23-
start?: string;
24-
statsPeriod?: string;
23+
start?: string | null;
24+
statsPeriod?: string | null;
2525
}
2626

2727
interface UseMetricDetectorSeriesResult {

static/app/views/detectors/hooks/useOpenPeriods.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,10 @@ import useOrganization from 'sentry/utils/useOrganization';
88

99
type CommonParams = {
1010
cursor?: string;
11-
end?: string;
11+
end?: string | null;
1212
limit?: number;
13-
start?: string;
14-
statsPeriod?: string;
13+
start?: string | null;
14+
statsPeriod?: string | null;
1515
};
1616

1717
type UseOpenPeriodsParams =

0 commit comments

Comments
 (0)