diff --git a/static/app/views/detectors/components/details/metric/sidebar.tsx b/static/app/views/detectors/components/details/metric/sidebar.tsx index 17c80881c45ce6..7e36b783a5c765 100644 --- a/static/app/views/detectors/components/details/metric/sidebar.tsx +++ b/static/app/views/detectors/components/details/metric/sidebar.tsx @@ -90,6 +90,12 @@ function DetectorResolve({detector}: {detector: MetricDetector}) { const mainCondition = conditions.find( condition => condition.conditionResult !== DetectorPriorityLevel.OK ); + + // Get the OK condition (resolution condition) + const okCondition = conditions.find( + condition => condition.conditionResult === DetectorPriorityLevel.OK + ); + const thresholdSuffix = getMetricDetectorSuffix( detector.config?.detectionType || 'static', detector.dataSources[0].queryObj?.snubaQuery?.aggregate || 'count()' @@ -102,6 +108,8 @@ function DetectorResolve({detector}: {detector: MetricDetector}) { typeof mainCondition?.comparison === 'number' ? mainCondition.comparison : undefined, + resolutionThreshold: + typeof okCondition?.comparison === 'number' ? okCondition.comparison : undefined, comparisonDelta: (detector.config as any)?.comparison_delta, thresholdSuffix, }); diff --git a/static/app/views/detectors/components/forms/metric/resolveSection.tsx b/static/app/views/detectors/components/forms/metric/resolveSection.tsx index 2bbfeb6a37c580..b6005b3240c375 100644 --- a/static/app/views/detectors/components/forms/metric/resolveSection.tsx +++ b/static/app/views/detectors/components/forms/metric/resolveSection.tsx @@ -62,6 +62,9 @@ export function ResolveSection() { const highThreshold = useMetricDetectorFormField( METRIC_DETECTOR_FORM_FIELDS.highThreshold ); + const mediumThreshold = useMetricDetectorFormField( + METRIC_DETECTOR_FORM_FIELDS.mediumThreshold + ); const conditionType = useMetricDetectorFormField( METRIC_DETECTOR_FORM_FIELDS.conditionType ); @@ -81,12 +84,17 @@ export function ResolveSection() { const thresholdSuffix = getStaticDetectorThresholdSuffix(aggregate); + // Compute the automatic resolution threshold: medium if present, otherwise high + const resolutionThreshold = + mediumThreshold && mediumThreshold !== '' ? mediumThreshold : highThreshold || 0; + const descriptionContent = getResolutionDescription( detectionType === 'percent' ? { detectionType: 'percent', conditionType, highThreshold: highThreshold || 0, + resolutionThreshold, comparisonDelta: conditionComparisonAgo ?? 3600, // Default to 1 hour if not set thresholdSuffix, } @@ -95,6 +103,7 @@ export function ResolveSection() { detectionType: 'static', conditionType, highThreshold: highThreshold || 0, + resolutionThreshold, thresholdSuffix, } : { diff --git a/static/app/views/detectors/utils/getDetectorResolutionDescription.spec.tsx b/static/app/views/detectors/utils/getDetectorResolutionDescription.spec.tsx index bfd152a92d47d4..199bc7aeab6baa 100644 --- a/static/app/views/detectors/utils/getDetectorResolutionDescription.spec.tsx +++ b/static/app/views/detectors/utils/getDetectorResolutionDescription.spec.tsx @@ -21,6 +21,7 @@ describe('getDetectorResolutionDescription', () => { detectionType: 'static', conditionType: DataConditionType.GREATER, highThreshold: 100, + resolutionThreshold: undefined, thresholdSuffix: '%', }); @@ -34,6 +35,7 @@ describe('getDetectorResolutionDescription', () => { detectionType: 'static', conditionType: DataConditionType.LESS, highThreshold: 50, + resolutionThreshold: undefined, thresholdSuffix: 'ms', }); @@ -47,6 +49,7 @@ describe('getDetectorResolutionDescription', () => { detectionType: 'static', conditionType: DataConditionType.GREATER, highThreshold: 100, + resolutionThreshold: undefined, thresholdSuffix: '', }); @@ -54,6 +57,34 @@ describe('getDetectorResolutionDescription', () => { 'Issue will be resolved when the query value is less than 100.' ); }); + + it('uses resolutionThreshold instead of highThreshold when provided for GREATER condition', () => { + const result = getResolutionDescription({ + detectionType: 'static', + conditionType: DataConditionType.GREATER, + highThreshold: 100, + resolutionThreshold: 75, + thresholdSuffix: '%', + }); + + expect(result).toBe( + 'Issue will be resolved when the query value is less than 75%.' + ); + }); + + it('uses resolutionThreshold instead of highThreshold when provided for LESS condition', () => { + const result = getResolutionDescription({ + detectionType: 'static', + conditionType: DataConditionType.LESS, + highThreshold: 50, + resolutionThreshold: 80, + thresholdSuffix: 'ms', + }); + + expect(result).toBe( + 'Issue will be resolved when the query value is more than 80ms.' + ); + }); }); describe('percent detection type', () => { @@ -62,6 +93,7 @@ describe('getDetectorResolutionDescription', () => { detectionType: 'percent', conditionType: DataConditionType.GREATER, highThreshold: 25, + resolutionThreshold: undefined, comparisonDelta: 3600, // 1 hour thresholdSuffix: '%', }); @@ -76,6 +108,7 @@ describe('getDetectorResolutionDescription', () => { detectionType: 'percent', conditionType: undefined, highThreshold: 25, + resolutionThreshold: undefined, comparisonDelta: 3600, thresholdSuffix: '%', }); @@ -88,6 +121,7 @@ describe('getDetectorResolutionDescription', () => { detectionType: 'percent', conditionType: DataConditionType.GREATER, highThreshold: undefined, + resolutionThreshold: undefined, comparisonDelta: 3600, thresholdSuffix: '%', }); @@ -100,6 +134,7 @@ describe('getDetectorResolutionDescription', () => { detectionType: 'percent', conditionType: DataConditionType.LESS, highThreshold: 15, + resolutionThreshold: undefined, comparisonDelta: 7200, // 2 hours thresholdSuffix: '%', }); @@ -114,6 +149,7 @@ describe('getDetectorResolutionDescription', () => { detectionType: 'percent', conditionType: DataConditionType.GREATER, highThreshold: 20, + resolutionThreshold: undefined, comparisonDelta: 300, // 5 minutes thresholdSuffix: '%', }); @@ -122,5 +158,20 @@ describe('getDetectorResolutionDescription', () => { 'Issue will be resolved when the query value is less than 20% higher than the previous 5 minutes.' ); }); + + it('uses resolutionThreshold instead of highThreshold when provided', () => { + const result = getResolutionDescription({ + detectionType: 'percent', + conditionType: DataConditionType.GREATER, + highThreshold: 25, + resolutionThreshold: 15, + comparisonDelta: 3600, + thresholdSuffix: '%', + }); + + expect(result).toBe( + 'Issue will be resolved when the query value is less than 15% higher than the previous 1 hour.' + ); + }); }); }); diff --git a/static/app/views/detectors/utils/getDetectorResolutionDescription.tsx b/static/app/views/detectors/utils/getDetectorResolutionDescription.tsx index 085d35cdb10071..e7163eb73114b2 100644 --- a/static/app/views/detectors/utils/getDetectorResolutionDescription.tsx +++ b/static/app/views/detectors/utils/getDetectorResolutionDescription.tsx @@ -20,12 +20,14 @@ interface PercentDetectionParams extends BaseDetectionParams { conditionType: DataConditionType | undefined; detectionType: 'percent'; highThreshold: string | number | undefined; + resolutionThreshold: string | number | undefined; } interface StaticDetectionParams extends BaseDetectionParams { conditionType: DataConditionType | undefined; detectionType: 'static'; highThreshold: string | number | undefined; + resolutionThreshold: string | number | undefined; } interface DynamicDetectionParams extends BaseDetectionParams { @@ -45,7 +47,13 @@ export function getResolutionDescription(params: ResolutionDescriptionParams): s ); } - if (!params.conditionType || params.highThreshold === undefined) { + // Use resolutionThreshold if provided, otherwise fall back to highThreshold + const threshold = + params.resolutionThreshold === undefined + ? params.highThreshold + : params.resolutionThreshold; + + if (!params.conditionType || threshold === undefined) { return t('Resolution conditions not configured'); } @@ -53,13 +61,13 @@ export function getResolutionDescription(params: ResolutionDescriptionParams): s if (params.conditionType === DataConditionType.GREATER) { return t( 'Issue will be resolved when the query value is less than %s%s.', - params.highThreshold, + threshold, suffix ); } return t( 'Issue will be resolved when the query value is more than %s%s.', - params.highThreshold, + threshold, suffix ); } @@ -69,13 +77,13 @@ export function getResolutionDescription(params: ResolutionDescriptionParams): s if (params.conditionType === DataConditionType.GREATER) { return t( 'Issue will be resolved when the query value is less than %s%% higher than the previous %s.', - params.highThreshold, + threshold, getDuration(delta) ); } return t( 'Issue will be resolved when the query value is less than %s%% lower than the previous %s.', - params.highThreshold, + threshold, getDuration(delta) ); }