@@ -648,196 +648,192 @@ class RuleConditionsForm extends PureComponent<Props, State> {
648648 </ Fragment >
649649 ) : (
650650 < Fragment >
651- < Fragment >
652- { isExtrapolatedChartData && (
653- < OnDemandMetricAlert
654- message = { t (
655- 'The chart data above is an estimate based on the stored transactions that match the filters specified.'
651+ { isExtrapolatedChartData && (
652+ < OnDemandMetricAlert
653+ message = { t (
654+ 'The chart data above is an estimate based on the stored transactions that match the filters specified.'
655+ ) }
656+ />
657+ ) }
658+ { confidenceEnabled && isLowConfidenceChartData && (
659+ < Alert . Container >
660+ < Alert variant = "warning" >
661+ { t (
662+ 'Your low sample count may impact the accuracy of this alert. Edit your query or increase your sampling rate.'
656663 ) }
657- />
658- ) }
659- { confidenceEnabled && isLowConfidenceChartData && (
660- < Alert . Container >
661- < Alert variant = "warning" >
662- { t (
663- 'Your low sample count may impact the accuracy of this alert. Edit your query or increase your sampling rate.'
664- ) }
665- </ Alert >
666- </ Alert . Container >
667- ) }
668- { ! isErrorMigration && this . renderInterval ( ) }
669- < StyledListItem > { t ( 'Filter events' ) } </ StyledListItem >
670- < Tooltip
671- title = { this . transactionAlertDisabledMessage }
672- disabled = { ! this . disableTransactionAlertType }
673- isHoverable
674- >
675- < FormRow noMargin columns = { 1 + ( allowChangeEventTypes ? 1 : 0 ) + 1 } >
676- { this . renderProjectSelector ( ) }
677- < SelectField
678- name = "environment"
679- placeholder = { t ( 'All Environments' ) }
680- style = { {
681- ...this . formElemBaseStyle ,
682- minWidth : 230 ,
683- flex : 1 ,
684- } }
685- styles = { {
686- singleValue : ( base : any ) => ( {
687- ...base ,
688- } ) ,
689- option : ( base : any ) => ( {
690- ...base ,
691- } ) ,
692- } }
693- options = { environmentOptions }
694- isDisabled = {
695- disabled ||
696- this . state . environments === null ||
697- isErrorMigration ||
698- this . disableTransactionAlertType
699- }
700- isClearable
701- inline = { false }
702- flexibleControlStateSize
703- />
704- { allowChangeEventTypes && this . renderEventTypeFilter ( ) }
705- </ FormRow >
706- </ Tooltip >
707- < FormRow noMargin >
708- < FormField
709- name = "query"
710- inline = { false }
664+ </ Alert >
665+ </ Alert . Container >
666+ ) }
667+ { ! isErrorMigration && this . renderInterval ( ) }
668+ < StyledListItem > { t ( 'Filter events' ) } </ StyledListItem >
669+ < Tooltip
670+ title = { this . transactionAlertDisabledMessage }
671+ disabled = { ! this . disableTransactionAlertType }
672+ isHoverable
673+ >
674+ < FormRow noMargin columns = { 1 + ( allowChangeEventTypes ? 1 : 0 ) + 1 } >
675+ { this . renderProjectSelector ( ) }
676+ < SelectField
677+ name = "environment"
678+ placeholder = { t ( 'All Environments' ) }
711679 style = { {
712680 ...this . formElemBaseStyle ,
713- flex : '6 0 500px' ,
681+ minWidth : 230 ,
682+ flex : 1 ,
683+ } }
684+ styles = { {
685+ singleValue : ( base : any ) => ( {
686+ ...base ,
687+ } ) ,
688+ option : ( base : any ) => ( {
689+ ...base ,
690+ } ) ,
714691 } }
692+ options = { environmentOptions }
693+ isDisabled = {
694+ disabled ||
695+ this . state . environments === null ||
696+ isErrorMigration ||
697+ this . disableTransactionAlertType
698+ }
699+ isClearable
700+ inline = { false }
715701 flexibleControlStateSize
716- >
717- { ( { onChange, onBlur, initialData, value} : any ) => {
718- return isEapAlertType ( alertType ) ? (
719- < EAPSearchQueryBuilderWithContext
720- initialQuery = { value ?? '' }
721- onSearch = { ( query , { parsedQuery} ) => {
722- onFilterSearch ( query , parsedQuery ) ;
702+ />
703+ { allowChangeEventTypes && this . renderEventTypeFilter ( ) }
704+ </ FormRow >
705+ </ Tooltip >
706+ < FormRow noMargin >
707+ < FormField
708+ name = "query"
709+ inline = { false }
710+ style = { {
711+ ...this . formElemBaseStyle ,
712+ flex : '6 0 500px' ,
713+ } }
714+ flexibleControlStateSize
715+ >
716+ { ( { onChange, onBlur, initialData, value} : any ) => {
717+ return isEapAlertType ( alertType ) ? (
718+ < EAPSearchQueryBuilderWithContext
719+ initialQuery = { value ?? '' }
720+ onSearch = { ( query , { parsedQuery} ) => {
721+ onFilterSearch ( query , parsedQuery ) ;
722+ onChange ( query , { } ) ;
723+ } }
724+ project = { project }
725+ traceItemType = { traceItemType ?? TraceItemDataset . SPANS }
726+ />
727+ ) : (
728+ < Flex align = "center" gap = "md" >
729+ < SearchQueryBuilder
730+ initialQuery = { initialData ?. query ?? '' }
731+ getTagValues = { this . getEventFieldValues }
732+ placeholder = { this . searchPlaceholder }
733+ searchSource = "alert_builder"
734+ filterKeys = { filterKeys }
735+ disabled = {
736+ disabled || isErrorMigration || this . disableTransactionAlertType
737+ }
738+ onChange = { onChange }
739+ invalidMessages = { {
740+ ...defaultConfig . invalidMessages ,
741+ [ InvalidReason . WILDCARD_NOT_ALLOWED ] : t (
742+ 'The wildcard operator is not supported here.'
743+ ) ,
744+ [ InvalidReason . FREE_TEXT_NOT_ALLOWED ] : t (
745+ 'Free text search is not allowed. If you want to partially match transaction names, use glob patterns like "transaction:*transaction-name*"'
746+ ) ,
747+ } }
748+ onSearch = { query => {
749+ onFilterSearch ( query , true ) ;
723750 onChange ( query , { } ) ;
724751 } }
725- project = { project }
726- traceItemType = { traceItemType ?? TraceItemDataset . SPANS }
752+ onBlur = { ( query , { parsedQuery} ) => {
753+ onFilterSearch ( query , parsedQuery ) ;
754+ onBlur ( query ) ;
755+ } }
756+ // We only need strict validation for Transaction queries, everything else is fine
757+ disallowUnsupportedFilters = {
758+ organization . features . includes ( 'alert-allow-indexed' ) ||
759+ ( hasOnDemandMetricAlertFeature ( organization ) &&
760+ isOnDemandQueryString ( value ) )
761+ ? false
762+ : dataset === Dataset . GENERIC_METRICS
763+ }
764+ />
765+ { isExtrapolatedChartData &&
766+ isOnDemandQueryString ( value ) &&
767+ ( isOnDemandLimitReached ? (
768+ < OnDemandWarningIcon
769+ variant = "danger"
770+ msg = { tct (
771+ 'We don’t routinely collect metrics from [fields] and you’ve already reached the limit of [docLink:alerts with advanced filters] for your organization.' ,
772+ {
773+ fields : (
774+ < strong >
775+ { getOnDemandKeys ( value )
776+ . map ( key => `"${ key } "` )
777+ . join ( ', ' ) }
778+ </ strong >
779+ ) ,
780+ docLink : (
781+ < ExternalLink href = "https://docs.sentry.io/product/alerts/create-alerts/metric-alert-config/#advanced-filters-for-transactions" />
782+ ) ,
783+ }
784+ ) }
785+ isHoverable
786+ />
787+ ) : (
788+ < OnDemandWarningIcon
789+ variant = "primary"
790+ msg = { tct (
791+ 'We don’t routinely collect metrics from [fields]. However, we’ll do so [strong:once this alert has been saved.]' ,
792+ {
793+ fields : (
794+ < strong >
795+ { getOnDemandKeys ( value )
796+ . map ( key => `"${ key } "` )
797+ . join ( ', ' ) }
798+ </ strong >
799+ ) ,
800+ strong : < strong /> ,
801+ }
802+ ) }
803+ />
804+ ) ) }
805+ </ Flex >
806+ ) ;
807+ } }
808+ </ FormField >
809+ </ FormRow >
810+ < FormRow noMargin >
811+ < FormField
812+ name = "query"
813+ inline = { false }
814+ style = { {
815+ ...this . formElemBaseStyle ,
816+ flex : '6 0 500px' ,
817+ } }
818+ flexibleControlStateSize
819+ >
820+ { ( args : any ) => {
821+ if (
822+ args . value ?. includes ( 'is:unresolved' ) &&
823+ comparisonType === AlertRuleComparisonType . DYNAMIC
824+ ) {
825+ return (
826+ < OnDemandMetricAlert
827+ message = { t (
828+ "'is:unresolved' queries are not supported by Anomaly Detection alerts."
829+ ) }
727830 />
728- ) : (
729- < Flex align = "center" gap = "md" >
730- < SearchQueryBuilder
731- initialQuery = { initialData ?. query ?? '' }
732- getTagValues = { this . getEventFieldValues }
733- placeholder = { this . searchPlaceholder }
734- searchSource = "alert_builder"
735- filterKeys = { filterKeys }
736- disabled = {
737- disabled ||
738- isErrorMigration ||
739- this . disableTransactionAlertType
740- }
741- onChange = { onChange }
742- invalidMessages = { {
743- ...defaultConfig . invalidMessages ,
744- [ InvalidReason . WILDCARD_NOT_ALLOWED ] : t (
745- 'The wildcard operator is not supported here.'
746- ) ,
747- [ InvalidReason . FREE_TEXT_NOT_ALLOWED ] : t (
748- 'Free text search is not allowed. If you want to partially match transaction names, use glob patterns like "transaction:*transaction-name*"'
749- ) ,
750- } }
751- onSearch = { query => {
752- onFilterSearch ( query , true ) ;
753- onChange ( query , { } ) ;
754- } }
755- onBlur = { ( query , { parsedQuery} ) => {
756- onFilterSearch ( query , parsedQuery ) ;
757- onBlur ( query ) ;
758- } }
759- // We only need strict validation for Transaction queries, everything else is fine
760- disallowUnsupportedFilters = {
761- organization . features . includes ( 'alert-allow-indexed' ) ||
762- ( hasOnDemandMetricAlertFeature ( organization ) &&
763- isOnDemandQueryString ( value ) )
764- ? false
765- : dataset === Dataset . GENERIC_METRICS
766- }
767- />
768- { isExtrapolatedChartData &&
769- isOnDemandQueryString ( value ) &&
770- ( isOnDemandLimitReached ? (
771- < OnDemandWarningIcon
772- variant = "danger"
773- msg = { tct (
774- 'We don’t routinely collect metrics from [fields] and you’ve already reached the limit of [docLink:alerts with advanced filters] for your organization.' ,
775- {
776- fields : (
777- < strong >
778- { getOnDemandKeys ( value )
779- . map ( key => `"${ key } "` )
780- . join ( ', ' ) }
781- </ strong >
782- ) ,
783- docLink : (
784- < ExternalLink href = "https://docs.sentry.io/product/alerts/create-alerts/metric-alert-config/#advanced-filters-for-transactions" />
785- ) ,
786- }
787- ) }
788- isHoverable
789- />
790- ) : (
791- < OnDemandWarningIcon
792- variant = "primary"
793- msg = { tct (
794- 'We don’t routinely collect metrics from [fields]. However, we’ll do so [strong:once this alert has been saved.]' ,
795- {
796- fields : (
797- < strong >
798- { getOnDemandKeys ( value )
799- . map ( key => `"${ key } "` )
800- . join ( ', ' ) }
801- </ strong >
802- ) ,
803- strong : < strong /> ,
804- }
805- ) }
806- />
807- ) ) }
808- </ Flex >
809831 ) ;
810- } }
811- </ FormField >
812- </ FormRow >
813- < FormRow noMargin >
814- < FormField
815- name = "query"
816- inline = { false }
817- style = { {
818- ...this . formElemBaseStyle ,
819- flex : '6 0 500px' ,
820- } }
821- flexibleControlStateSize
822- >
823- { ( args : any ) => {
824- if (
825- args . value ?. includes ( 'is:unresolved' ) &&
826- comparisonType === AlertRuleComparisonType . DYNAMIC
827- ) {
828- return (
829- < OnDemandMetricAlert
830- message = { t (
831- "'is:unresolved' queries are not supported by Anomaly Detection alerts."
832- ) }
833- />
834- ) ;
835- }
836- return null ;
837- } }
838- </ FormField >
839- </ FormRow >
840- </ Fragment >
832+ }
833+ return null ;
834+ } }
835+ </ FormField >
836+ </ FormRow >
841837 </ Fragment >
842838 ) }
843839 </ Fragment >
0 commit comments