123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181 |
- import {useCallback, useEffect, useMemo} from 'react';
- import {
- getDiffInMinutes,
- GranularityLadder,
- ONE_HOUR,
- ONE_WEEK,
- SIXTY_DAYS,
- THIRTY_DAYS,
- TWENTY_FOUR_HOURS,
- TWO_WEEKS,
- } from 'sentry/components/charts/utils';
- import {parseStatsPeriod} from 'sentry/components/organizations/pageFilters/parse';
- import {t} from 'sentry/locale';
- import type {PageFilters} from 'sentry/types/core';
- import {parsePeriodToHours} from 'sentry/utils/duration/parsePeriodToHours';
- import {useUpdateQuery} from 'sentry/utils/metrics';
- import {parseMRI} from 'sentry/utils/metrics/mri';
- import {isMetricsQueryWidget} from 'sentry/utils/metrics/types';
- import {decodeScalar} from 'sentry/utils/queryString';
- import useLocationQuery from 'sentry/utils/url/useLocationQuery';
- import usePageFilters from 'sentry/utils/usePageFilters';
- import {useMetricsContext} from 'sentry/views/metrics/context';
- const ALL_INTERVAL_OPTIONS = [
- {value: '1m', label: t('1 minute')},
- {value: '5m', label: t('5 minutes')},
- {value: '15m', label: t('15 minutes')},
- {value: '30m', label: t('30 minutes')},
- {value: '1h', label: t('1 hour')},
- {value: '4h', label: t('4 hours')},
- {value: '1d', label: t('1 day')},
- {value: '1w', label: t('1 week')},
- {value: '4w', label: t('1 month')},
- ];
- const minimumInterval = new GranularityLadder([
- [SIXTY_DAYS, '1d'],
- [THIRTY_DAYS, '2h'],
- [TWO_WEEKS, '1h'],
- [ONE_WEEK, '30m'],
- [TWENTY_FOUR_HOURS, '5m'],
- [ONE_HOUR, '1m'],
- [0, '1m'],
- ]);
- const maximumInterval = new GranularityLadder([
- [SIXTY_DAYS, '4w'],
- [THIRTY_DAYS, '1w'],
- [TWO_WEEKS, '1w'],
- [ONE_WEEK, '1d'],
- [TWENTY_FOUR_HOURS, '6h'],
- [ONE_HOUR, '15m'],
- [0, '1m'],
- ]);
- export function getIntervalOptionsForStatsPeriod(
- datetime: PageFilters['datetime'],
- maxInterval?: string
- ) {
- const diffInMinutes = getDiffInMinutes(datetime);
- const maxIntervalInHours = maxInterval && parsePeriodToHours(maxInterval);
- const minimumOption = minimumInterval.getInterval(diffInMinutes);
- const minimumOptionInHours = parsePeriodToHours(minimumOption);
- const defaultMaximumOption = maximumInterval.getInterval(diffInMinutes);
- const maximumOption =
- maxIntervalInHours && maxIntervalInHours > parsePeriodToHours(defaultMaximumOption)
- ? maxInterval
- : defaultMaximumOption;
- const maximumOptionInHours = parsePeriodToHours(maximumOption);
- return ALL_INTERVAL_OPTIONS.filter(option => {
- const optionInHours = parsePeriodToHours(option.value);
- return optionInHours >= minimumOptionInHours && optionInHours <= maximumOptionInHours;
- });
- }
- export function validateInterval(
- interval: string,
- options: {label: string; value: string; disabled?: boolean}[]
- ) {
- const isPeriod = !!parseStatsPeriod(interval);
- const enabledOptions = options.filter(option => !option.disabled);
- const currentIntervalValues = enabledOptions.map(option => option.value);
- return isPeriod && currentIntervalValues.includes(interval)
- ? interval
- : // Take the 2nd most granular option if available
- enabledOptions[1]?.value ?? enabledOptions[0].value;
- }
- export function useMetricsIntervalParam() {
- const {datetime} = usePageFilters().selection;
- const {interval} = useLocationQuery({fields: {interval: decodeScalar}});
- const {widgets} = useMetricsContext();
- const updateQuery = useUpdateQuery();
- const hasSetMetric = useMemo(
- () =>
- widgets.some(
- widget => isMetricsQueryWidget(widget) && parseMRI(widget.mri)!.type === 's'
- ),
- [widgets]
- );
- const handleIntervalChange = useCallback(
- (newInterval: string) => {
- updateQuery({interval: newInterval}, {replace: true});
- },
- [updateQuery]
- );
- const metricsIntervalOptions = useMetricsIntervalOptions({
- interval,
- hasSetMetric,
- datetime,
- onIntervalChange: handleIntervalChange,
- });
- useEffect(() => {
- if (interval !== metricsIntervalOptions.interval) {
- handleIntervalChange(metricsIntervalOptions.interval);
- }
- }, [interval, metricsIntervalOptions.interval, handleIntervalChange]);
- return metricsIntervalOptions;
- }
- export interface MetricsIntervalParamProps {
- datetime: PageFilters['datetime'];
- hasSetMetric: boolean;
- interval: string;
- onIntervalChange: (interval: string) => void;
- }
- export function useMetricsIntervalOptions({
- interval,
- hasSetMetric,
- datetime,
- onIntervalChange,
- }: MetricsIntervalParamProps) {
- const currentIntervalOptions = useMemo(() => {
- const minInterval = hasSetMetric ? '1h' : undefined;
- const options = getIntervalOptionsForStatsPeriod(datetime, minInterval);
- if (!hasSetMetric) {
- return options;
- }
- // Disable all intervals that are smaller than one hour for set metrics
- return options.map(option => {
- return parsePeriodToHours(option.value) < 1
- ? {
- ...option,
- disabled: true,
- tooltip: t(
- 'Intervals smaller than 1 hour are not available for set metrics.'
- ),
- }
- : option;
- });
- }, [datetime, hasSetMetric]);
- const setInterval = useCallback(
- (newInterval: string) => {
- onIntervalChange(newInterval);
- },
- [onIntervalChange]
- );
- const validatedInterval = useMemo(
- () => validateInterval(interval, currentIntervalOptions),
- [interval, currentIntervalOptions]
- );
- return {
- interval: validatedInterval,
- setInterval,
- currentIntervalOptions,
- };
- }
|