11import { Fragment } from 'react' ;
22import styled from '@emotion/styled' ;
33
4+ import { Grid } from '@sentry/scraps/layout' ;
5+
46import { Flex } from 'sentry/components/core/layout' ;
57import { Heading , Text } from 'sentry/components/core/text' ;
68import { Tooltip } from 'sentry/components/core/tooltip' ;
@@ -10,19 +12,149 @@ import {
1012} from 'sentry/components/searchQueryBuilder/formattedQuery' ;
1113import { Container } from 'sentry/components/workflowEngine/ui/container' ;
1214import { t } from 'sentry/locale' ;
15+ import {
16+ DataConditionType ,
17+ DETECTOR_PRIORITY_LEVEL_TO_PRIORITY_LEVEL ,
18+ DetectorPriorityLevel ,
19+ } from 'sentry/types/workflowEngine/dataConditions' ;
1320import type {
21+ MetricCondition ,
1422 MetricDetector ,
15- SnubaQueryDataSource ,
1623} from 'sentry/types/workflowEngine/detectors' ;
1724import { getExactDuration } from 'sentry/utils/duration/getExactDuration' ;
25+ import { PriorityDot } from 'sentry/views/detectors/components/priorityDot' ;
1826import { getDatasetConfig } from 'sentry/views/detectors/datasetConfig/getDatasetConfig' ;
1927import { getDetectorDataset } from 'sentry/views/detectors/datasetConfig/getDetectorDataset' ;
28+ import { getMetricDetectorSuffix } from 'sentry/views/detectors/utils/metricDetectorSuffix' ;
29+
30+ function getDetectorTypeLabel ( detector : MetricDetector ) {
31+ if ( detector . config . detectionType === 'dynamic' ) {
32+ return t ( 'Dynamic threshold' ) ;
33+ }
34+ if ( detector . config . detectionType === 'percent' ) {
35+ return t ( 'Percent change' ) ;
36+ }
37+ return t ( 'Static threshold' ) ;
38+ }
2039
21- interface MetricDetectorDetectProps {
22- detector : MetricDetector ;
40+ function getConditionLabel ( { condition} : { condition : MetricCondition } ) {
41+ switch ( condition . conditionResult ) {
42+ case DetectorPriorityLevel . OK :
43+ return t ( 'Resolved' ) ;
44+ case DetectorPriorityLevel . LOW :
45+ return t ( 'Low' ) ;
46+ case DetectorPriorityLevel . MEDIUM :
47+ return t ( 'Medium' ) ;
48+ case DetectorPriorityLevel . HIGH :
49+ return t ( 'High' ) ;
50+ default :
51+ return t ( 'Unknown' ) ;
52+ }
2353}
2454
25- function SnubaQueryDetails ( { dataSource} : { dataSource : SnubaQueryDataSource } ) {
55+ function makeDirectionText ( condition : MetricCondition ) {
56+ switch ( condition . type ) {
57+ case DataConditionType . GREATER :
58+ return t ( 'Above' ) ;
59+ case DataConditionType . LESS :
60+ return t ( 'Below' ) ;
61+ case DataConditionType . EQUAL :
62+ return t ( 'Equal to' ) ;
63+ case DataConditionType . NOT_EQUAL :
64+ return t ( 'Not equal to' ) ;
65+ case DataConditionType . GREATER_OR_EQUAL :
66+ return t ( 'Above or equal to' ) ;
67+ case DataConditionType . LESS_OR_EQUAL :
68+ return t ( 'Below or equal to' ) ;
69+ default :
70+ return t ( 'Unknown' ) ;
71+ }
72+ }
73+
74+ function getConditionDescription ( {
75+ aggregate,
76+ config,
77+ condition,
78+ } : {
79+ aggregate : string ;
80+ condition : MetricCondition ;
81+ config : MetricDetector [ 'config' ] ;
82+ } ) {
83+ const comparisonValue =
84+ typeof condition . comparison === 'number' ? String ( condition . comparison ) : '' ;
85+ const unit = getMetricDetectorSuffix ( config . detectionType , aggregate ) ;
86+
87+ if ( config . detectionType === 'percent' ) {
88+ const direction =
89+ condition . type === DataConditionType . GREATER ? t ( 'higher' ) : t ( 'lower' ) ;
90+ const delta = config . comparisonDelta ;
91+ const timeRange = getExactDuration ( delta ) ;
92+
93+ if ( condition . conditionResult === DetectorPriorityLevel . OK ) {
94+ return t (
95+ `Less than %(comparisonValue)s%(unit)s %(direction)s than the previous %(timeRange)s` ,
96+ {
97+ comparisonValue,
98+ unit,
99+ direction,
100+ timeRange,
101+ }
102+ ) ;
103+ }
104+
105+ return t (
106+ `%(comparisonValue)s%(unit)s %(direction)s than the previous %(timeRange)s` ,
107+ {
108+ comparisonValue,
109+ unit,
110+ direction,
111+ timeRange,
112+ }
113+ ) ;
114+ }
115+
116+ return `${ makeDirectionText ( condition ) } ${ comparisonValue } ${ unit } ` ;
117+ }
118+
119+ function DetectorPriorities ( { detector} : { detector : MetricDetector } ) {
120+ if ( detector . config . detectionType === 'dynamic' ) {
121+ return < div > { t ( 'Sentry will automatically update priority.' ) } </ div > ;
122+ }
123+
124+ const conditions = detector . conditionGroup ?. conditions || [ ] ;
125+
126+ return (
127+ < Grid columns = "auto 1fr" gap = "sm lg" align = "start" >
128+ { conditions . map ( ( condition , index ) => (
129+ < Fragment key = { index } >
130+ < Flex align = "center" gap = "sm" >
131+ < PriorityDot
132+ priority = {
133+ condition . conditionResult === DetectorPriorityLevel . OK
134+ ? 'resolved'
135+ : DETECTOR_PRIORITY_LEVEL_TO_PRIORITY_LEVEL [
136+ condition . conditionResult as keyof typeof DETECTOR_PRIORITY_LEVEL_TO_PRIORITY_LEVEL
137+ ]
138+ }
139+ />
140+ < Text > { getConditionLabel ( { condition} ) } </ Text >
141+ </ Flex >
142+ < Text >
143+ { getConditionDescription ( {
144+ aggregate : detector . dataSources [ 0 ] . queryObj . snubaQuery . aggregate ,
145+ condition,
146+ config : detector . config ,
147+ } ) }
148+ </ Text >
149+ </ Fragment >
150+ ) ) }
151+ </ Grid >
152+ ) ;
153+ }
154+
155+ export function MetricDetectorDetailsDetect ( { detector} : { detector : MetricDetector } ) {
156+ const dataSource = detector . dataSources [ 0 ] ;
157+
26158 if ( ! dataSource . queryObj ) {
27159 return < Container > { t ( 'Query not found.' ) } </ Container > ;
28160 }
@@ -75,16 +207,16 @@ function SnubaQueryDetails({dataSource}: {dataSource: SnubaQueryDataSource}) {
75207 < Heading as = "h4" > { t ( 'Interval:' ) } </ Heading >
76208 < Value > { getExactDuration ( dataSource . queryObj . snubaQuery . timeWindow ) } </ Value >
77209 </ Flex >
210+ < Flex gap = "xs" align = "baseline" >
211+ < Heading as = "h4" > { t ( 'Threshold:' ) } </ Heading >
212+ < Value > { getDetectorTypeLabel ( detector ) } </ Value >
213+ </ Flex >
214+ < DetectorPriorities detector = { detector } />
78215 </ Flex >
79216 </ Container >
80217 ) ;
81218}
82219
83- export function MetricDetectorDetailsDetect ( { detector} : MetricDetectorDetectProps ) {
84- const dataSource = detector . dataSources ?. [ 0 ] ;
85- return < SnubaQueryDetails dataSource = { dataSource } /> ;
86- }
87-
88220const Query = styled ( 'dl' ) `
89221 display: grid;
90222 grid-template-columns: auto minmax(0, 1fr);
0 commit comments