123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148 |
- import {useTheme} from '@emotion/react';
- import type {LineSeriesOption} from 'echarts';
- import type {AreaChartProps} from 'sentry/components/charts/areaChart';
- import {
- transformComparisonTimeseriesData,
- transformTimeseriesData,
- } from 'sentry/components/charts/eventsRequest';
- import LineSeries from 'sentry/components/charts/series/lineSeries';
- import type {Series} from 'sentry/types/echarts';
- import type {EventsStats, SessionApiResponse} from 'sentry/types/organization';
- import type {Project} from 'sentry/types/project';
- import type {UseApiQueryOptions, UseApiQueryResult} from 'sentry/utils/queryClient';
- import type RequestError from 'sentry/utils/requestError/requestError';
- import {MINUTES_THRESHOLD_TO_DISPLAY_SECONDS} from 'sentry/utils/sessions';
- import {capitalize} from 'sentry/utils/string/capitalize';
- import {COMPARISON_DELTA_OPTIONS} from 'sentry/views/alerts/rules/metric/constants';
- import type {TimePeriodType} from 'sentry/views/alerts/rules/metric/details/constants';
- import {
- getMetricChartTooltipFormatter,
- getRuleChangeSeries,
- } from 'sentry/views/alerts/rules/metric/details/metricChart';
- import {
- getMetricAlertChartOption,
- transformSessionResponseToSeries,
- } from 'sentry/views/alerts/rules/metric/details/metricChartOption';
- import {getPeriodInterval} from 'sentry/views/alerts/rules/metric/details/utils';
- import type {MetricRule} from 'sentry/views/alerts/rules/metric/types';
- import {isCrashFreeAlert} from 'sentry/views/alerts/rules/metric/utils/isCrashFreeAlert';
- import type {Anomaly, Incident} from 'sentry/views/alerts/types';
- import {useMetricEventStats} from 'sentry/views/issueDetails/metricIssues/useMetricEventStats';
- import {useMetricSessionStats} from 'sentry/views/issueDetails/metricIssues/useMetricSessionStats';
- interface MetricStatsParams {
- project: Project;
- referrer: string;
- rule: MetricRule;
- timePeriod: TimePeriodType;
- anomalies?: Anomaly[];
- incidents?: Incident[];
- }
- type MetricStatsResponse = SessionApiResponse | EventsStats | undefined;
- interface MetricStatsResult {
- chartProps: Partial<AreaChartProps>;
- data: UseApiQueryResult<MetricStatsResponse, RequestError>['data'];
- resultType: 'sessions' | 'events';
- queryResult?: Partial<UseApiQueryResult<MetricStatsResponse, RequestError>>;
- }
- /**
- * Helper hook to coerce any rule into returning a series data response, whether it is a session or event rule.
- * Returns a similar response to a useApiQuery hook,
- */
- export function useMetricStatsChart(
- {
- project,
- rule,
- timePeriod,
- referrer,
- anomalies = [],
- incidents = [],
- }: MetricStatsParams,
- options: Partial<UseApiQueryOptions<MetricStatsResponse>> = {}
- ): MetricStatsResult {
- const theme = useTheme();
- const shouldUseSessionsStats = isCrashFreeAlert(rule.dataset);
- const interval = getPeriodInterval(timePeriod, rule);
- const {data: sessionStats, ...sessionStatsResults} = useMetricSessionStats(
- {project, rule, timePeriod},
- {
- enabled: shouldUseSessionsStats,
- ...(options as Partial<UseApiQueryOptions<SessionApiResponse>>),
- }
- );
- const {data: eventStats, ...eventStatsResults} = useMetricEventStats(
- {project, rule, timePeriod, referrer},
- {
- enabled: !shouldUseSessionsStats,
- ...(options as Partial<UseApiQueryOptions<EventsStats>>),
- }
- );
- let stats: Series[] = [];
- if (shouldUseSessionsStats && sessionStats) {
- stats = transformSessionResponseToSeries(sessionStats, rule);
- } else if (eventStats) {
- stats = transformTimeseriesData(eventStats.data, eventStats?.meta, rule.aggregate);
- }
- let comparisonData: Series[] = [];
- if (rule.comparisonDelta) {
- comparisonData = transformComparisonTimeseriesData(eventStats?.data ?? []);
- }
- let chartProps: Partial<AreaChartProps> = {};
- if (stats.length > 0) {
- const {chartOption} = getMetricAlertChartOption({
- timeseriesData: stats,
- rule,
- anomalies,
- incidents,
- seriesName: rule.aggregate,
- });
- chartProps = {...chartOption};
- }
- const comparisonSeriesName = capitalize(
- COMPARISON_DELTA_OPTIONS.find(({value}) => value === rule.comparisonDelta)?.label ||
- ''
- );
- const additionalSeries: LineSeriesOption[] = [
- ...(comparisonData || []).map(({data: _data, ...otherSeriesProps}) =>
- LineSeries({
- name: comparisonSeriesName,
- data: _data.map(({name, value}) => [name, value]),
- lineStyle: {color: theme.gray200, type: 'dashed', width: 1},
- itemStyle: {color: theme.gray200},
- animation: false,
- animationThreshold: 1,
- animationDuration: 0,
- ...otherSeriesProps,
- })
- ),
- ...getRuleChangeSeries(rule, stats, theme),
- ];
- return {
- chartProps: {
- minutesThresholdToDisplaySeconds: shouldUseSessionsStats
- ? MINUTES_THRESHOLD_TO_DISPLAY_SECONDS
- : undefined,
- additionalSeries,
- tooltip: getMetricChartTooltipFormatter({
- formattedAggregate: rule.aggregate,
- rule,
- interval,
- comparisonSeriesName,
- theme,
- }),
- ...chartProps,
- },
- resultType: shouldUseSessionsStats ? 'sessions' : 'events',
- data: shouldUseSessionsStats ? sessionStats : eventStats,
- queryResult: shouldUseSessionsStats ? sessionStatsResults : eventStatsResults,
- };
- }
|