Alerting: Remove threshold as default expression for grafana-managed recording rules (#94949)
* remove threshold as default expression for grafana-managed recording rule * fix * remove eslint disable line
This commit is contained in:
@@ -20,6 +20,8 @@ import {
|
||||
import { AlertQuery, PromAlertingRuleState } from 'app/types/unified-alerting-dto';
|
||||
|
||||
import { usePagination } from '../../hooks/usePagination';
|
||||
import { RuleFormValues } from '../../types/rule-form';
|
||||
import { isGrafanaRecordingRuleByType } from '../../utils/rules';
|
||||
import { PopupCard } from '../HoverCard';
|
||||
import { Spacer } from '../Spacer';
|
||||
import { AlertStateTag } from '../rules/AlertStateTag';
|
||||
@@ -58,7 +60,9 @@ export const Expression: FC<ExpressionProps> = ({
|
||||
|
||||
const queryType = query?.type;
|
||||
|
||||
const { setError, clearErrors } = useFormContext();
|
||||
const { setError, clearErrors, watch } = useFormContext<RuleFormValues>();
|
||||
const type = watch('type');
|
||||
const isGrafanaRecordingRule = type ? isGrafanaRecordingRuleByType(type) : false;
|
||||
|
||||
const onQueriesValidationError = useCallback(
|
||||
(errorMsg: string | undefined) => {
|
||||
@@ -158,20 +162,25 @@ export const Expression: FC<ExpressionProps> = ({
|
||||
</div>
|
||||
{hasResults && (
|
||||
<>
|
||||
<ExpressionResult series={series} isAlertCondition={isAlertCondition} />
|
||||
<ExpressionResult
|
||||
series={series}
|
||||
isAlertCondition={isAlertCondition}
|
||||
isRecordingRule={isGrafanaRecordingRule}
|
||||
/>
|
||||
{!isGrafanaRecordingRule && (
|
||||
<div className={styles.footer}>
|
||||
<Stack direction="row" alignItems="center">
|
||||
<Spacer />
|
||||
|
||||
<div className={styles.footer}>
|
||||
<Stack direction="row" alignItems="center">
|
||||
<Spacer />
|
||||
|
||||
<PreviewSummary
|
||||
isCondition={Boolean(isAlertCondition)}
|
||||
firing={groupedByState[PromAlertingRuleState.Firing].length}
|
||||
normal={groupedByState[PromAlertingRuleState.Inactive].length}
|
||||
seriesCount={seriesCount}
|
||||
/>
|
||||
</Stack>
|
||||
</div>
|
||||
<PreviewSummary
|
||||
isCondition={Boolean(isAlertCondition)}
|
||||
firing={groupedByState[PromAlertingRuleState.Firing].length}
|
||||
normal={groupedByState[PromAlertingRuleState.Inactive].length}
|
||||
seriesCount={seriesCount}
|
||||
/>
|
||||
</Stack>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
@@ -182,9 +191,10 @@ export const Expression: FC<ExpressionProps> = ({
|
||||
interface ExpressionResultProps {
|
||||
series: DataFrame[];
|
||||
isAlertCondition?: boolean;
|
||||
isRecordingRule?: boolean;
|
||||
}
|
||||
export const PAGE_SIZE = 20;
|
||||
export const ExpressionResult: FC<ExpressionResultProps> = ({ series, isAlertCondition }) => {
|
||||
export const ExpressionResult: FC<ExpressionResultProps> = ({ series, isAlertCondition, isRecordingRule = false }) => {
|
||||
const { pageItems, previousPage, nextPage, numberOfPages, pageStart, pageEnd } = usePagination(series, 1, PAGE_SIZE);
|
||||
const styles = useStyles2(getStyles);
|
||||
|
||||
@@ -212,7 +222,13 @@ export const ExpressionResult: FC<ExpressionResultProps> = ({ series, isAlertCon
|
||||
!isTimeSeriesResults &&
|
||||
pageItems.map((frame, index) => (
|
||||
// There's no way to uniquely identify a frame that doesn't cause render bugs :/ (Gilles)
|
||||
<FrameRow key={uniqueId()} frame={frame} index={pageStart + index} isAlertCondition={isAlertCondition} />
|
||||
<FrameRow
|
||||
key={uniqueId()}
|
||||
frame={frame}
|
||||
index={pageStart + index}
|
||||
isAlertCondition={isAlertCondition}
|
||||
isRecordingRule={isRecordingRule}
|
||||
/>
|
||||
))}
|
||||
{emptyResults && <div className={cx(styles.expression.noData, styles.mutedText)}>No data</div>}
|
||||
{shouldShowPagination && (
|
||||
@@ -353,6 +369,7 @@ const Header: FC<HeaderProps> = ({
|
||||
interface FrameProps extends Pick<ExpressionProps, 'isAlertCondition'> {
|
||||
frame: DataFrame;
|
||||
index: number;
|
||||
isRecordingRule?: boolean;
|
||||
}
|
||||
|
||||
const OpeningBracket = () => <span>{'{'}</span>;
|
||||
@@ -361,7 +378,7 @@ const ClosingBracket = () => <span>{'}'}</span>;
|
||||
const Quote = () => <span>"</span>;
|
||||
const Equals = () => <span>{'='}</span>;
|
||||
|
||||
const FrameRow: FC<FrameProps> = ({ frame, index, isAlertCondition }) => {
|
||||
function FrameRow({ frame, index, isAlertCondition, isRecordingRule }: FrameProps) {
|
||||
const styles = useStyles2(getStyles);
|
||||
|
||||
const name = getSeriesName(frame) || 'Series ' + index;
|
||||
@@ -374,6 +391,7 @@ const FrameRow: FC<FrameProps> = ({ frame, index, isAlertCondition }) => {
|
||||
const showNormal = isAlertCondition && value === 0;
|
||||
|
||||
const title = `${hasLabels ? '' : name}${hasLabels ? `{${formatLabels(labelsRecord)}}` : ''}`;
|
||||
const shouldRenderSumary = !isRecordingRule;
|
||||
|
||||
return (
|
||||
<div className={styles.expression.resultsRow}>
|
||||
@@ -401,14 +419,18 @@ const FrameRow: FC<FrameProps> = ({ frame, index, isAlertCondition }) => {
|
||||
</Text>
|
||||
</div>
|
||||
<div className={styles.expression.resultValue}>{value}</div>
|
||||
{showFiring && <AlertStateTag state={PromAlertingRuleState.Firing} size="sm" />}
|
||||
{showNormal && <AlertStateTag state={PromAlertingRuleState.Inactive} size="sm" />}
|
||||
{shouldRenderSumary && (
|
||||
<>
|
||||
{showFiring && <AlertStateTag state={PromAlertingRuleState.Firing} size="sm" />}
|
||||
{showNormal && <AlertStateTag state={PromAlertingRuleState.Inactive} size="sm" />}
|
||||
</>
|
||||
)}
|
||||
</Stack>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const TimeseriesRow: FC<FrameProps & { index: number }> = ({ frame, index }) => {
|
||||
}
|
||||
interface TimeseriesRowProps extends Omit<FrameProps, 'isRecordingRule'> {}
|
||||
const TimeseriesRow: FC<TimeseriesRowProps & { index: number }> = ({ frame, index }) => {
|
||||
const styles = useStyles2(getStyles);
|
||||
|
||||
const valueField = frame.fields[1]; // field 0 is "time", field 1 is "value"
|
||||
|
||||
@@ -53,6 +53,7 @@ import {
|
||||
} from '../../../utils/rule-form';
|
||||
import * as ruleId from '../../../utils/rule-id';
|
||||
import { fromRulerRule, fromRulerRuleAndRuleGroupIdentifier, stringifyIdentifier } from '../../../utils/rule-id';
|
||||
import { isGrafanaRecordingRuleByType } from '../../../utils/rules';
|
||||
import { createRelativeUrl } from '../../../utils/url';
|
||||
import { GrafanaRuleExporter } from '../../export/GrafanaRuleExporter';
|
||||
import { AlertRuleNameAndMetric } from '../AlertRuleNameInput';
|
||||
@@ -102,12 +103,13 @@ export const AlertRuleForm = ({ existing, prefill }: Props) => {
|
||||
if (queryParams.has('defaults')) {
|
||||
return formValuesFromQueryParams(queryParams.get('defaults') ?? '', ruleType);
|
||||
}
|
||||
const defaultRuleType = ruleType || RuleFormType.grafana;
|
||||
|
||||
return {
|
||||
...getDefaultFormValues(),
|
||||
condition: 'C',
|
||||
queries: getDefaultQueries(),
|
||||
type: ruleType || RuleFormType.grafana,
|
||||
queries: getDefaultQueries(isGrafanaRecordingRuleByType(defaultRuleType)),
|
||||
type: defaultRuleType,
|
||||
evaluateEvery: evaluateEvery,
|
||||
};
|
||||
}, [existing, prefill, queryParams, evaluateEvery, ruleType]);
|
||||
|
||||
@@ -497,14 +497,16 @@ export function recordingRulerRuleToRuleForm(
|
||||
};
|
||||
}
|
||||
|
||||
export const getDefaultQueries = (): AlertQuery[] => {
|
||||
export const getDefaultQueries = (isRecordingRule = false): AlertQuery[] => {
|
||||
const dataSource = getDefaultOrFirstCompatibleDataSource();
|
||||
|
||||
if (!dataSource) {
|
||||
return [...getDefaultExpressions('A', 'B')];
|
||||
const expressions = isRecordingRule ? getDefaultExpressionsForRecording('A') : getDefaultExpressions('A', 'B');
|
||||
return [...expressions];
|
||||
}
|
||||
const relativeTimeRange = getDefaultRelativeTimeRange();
|
||||
|
||||
const expressions = isRecordingRule ? getDefaultExpressionsForRecording('B') : getDefaultExpressions('B', 'C');
|
||||
return [
|
||||
{
|
||||
refId: 'A',
|
||||
@@ -515,7 +517,7 @@ export const getDefaultQueries = (): AlertQuery[] => {
|
||||
refId: 'A',
|
||||
},
|
||||
},
|
||||
...getDefaultExpressions('B', 'C'),
|
||||
...expressions,
|
||||
];
|
||||
};
|
||||
|
||||
@@ -536,7 +538,6 @@ export const getDefaultRecordingRulesQueries = (
|
||||
},
|
||||
];
|
||||
};
|
||||
|
||||
const getDefaultExpressions = (...refIds: [string, string]): AlertQuery[] => {
|
||||
const refOne = refIds[0];
|
||||
const refTwo = refIds[1];
|
||||
@@ -615,6 +616,46 @@ const getDefaultExpressions = (...refIds: [string, string]): AlertQuery[] => {
|
||||
},
|
||||
];
|
||||
};
|
||||
const getDefaultExpressionsForRecording = (refOne: string): AlertQuery[] => {
|
||||
const reduceExpression: ExpressionQuery = {
|
||||
refId: refOne,
|
||||
type: ExpressionQueryType.reduce,
|
||||
datasource: {
|
||||
uid: ExpressionDatasourceUID,
|
||||
type: ExpressionDatasourceRef.type,
|
||||
},
|
||||
conditions: [
|
||||
{
|
||||
type: 'query',
|
||||
evaluator: {
|
||||
params: [],
|
||||
type: EvalFunction.IsAbove,
|
||||
},
|
||||
operator: {
|
||||
type: 'and',
|
||||
},
|
||||
query: {
|
||||
params: [refOne],
|
||||
},
|
||||
reducer: {
|
||||
params: [],
|
||||
type: 'last',
|
||||
},
|
||||
},
|
||||
],
|
||||
reducer: 'last',
|
||||
expression: 'A',
|
||||
};
|
||||
|
||||
return [
|
||||
{
|
||||
refId: refOne,
|
||||
datasourceUid: ExpressionDatasourceUID,
|
||||
queryType: '',
|
||||
model: reduceExpression,
|
||||
},
|
||||
];
|
||||
};
|
||||
|
||||
const dataQueriesToGrafanaQueries = async (
|
||||
queries: DataQuery[],
|
||||
|
||||
Reference in New Issue
Block a user