grafana/public/app/features/alerting/unified/components/rules/RuleDetails.tsx
Sonia Aguilar 64ee42d01e
Alerting: Add limits and move state and label matching filters to the BE (#66267)
* WIP

* Add instance totals to combined rule. Use totals to display instances stats in the UI

* WIP

* add global summaries, fix TS errors

* fix useCombined test

* fix test

* use activeAt from rule when available

* Fix NaN in global stats

* Add no data total to global summary

* Add totals recalculation for filtered rules

* Fix instances totals, remove instances filtering from alert list view

* Update tests

* Fetch alerts considering filtering label matchers

* WIP - Fetch alerts appending state filter to endpoint

* Fix multiple values for state in request being applyied

* fix test

* Calculate hidden by for grafana managed alerts

* Use INSTANCES_DISPLAY_LIMIT constant for limiting alert instances instead of 1

* Rename matchers parameter according to API changes

* Fix calculating total number of grafana instances

* Rename matcher prop after previous change

* Display button to remove max instances limit

* Change matcher query param to be an array of strings

* Add test for paramsWithMatcherAndState method

* Refactor matcher to be an string array to be consistent with state

* Use matcher query string as matcher object type (encoded JSON)

* Avoind encoding matcher parameters twice

* fix tests

* Enable toggle for the limit/show all button and restore limit and filters when we come back from custom view

* Move getMatcherListFromString method to utils/alertmanager.ts

* Fix limit toggle button being shown when it's not necessary

* Use filteredTotals from be response to calculate hidden by count

* Fix variables not being replaced correctly

* Fix total shown to be all the instances filtered without limits

* Adress some PR review comments

* Move paramsWithMatcherAndState inside prometheusUrlBuilder method

---------

Co-authored-by: Gilles De Mey <gilles.de.mey@gmail.com>
Co-authored-by: Konrad Lalik <konrad.lalik@grafana.com>
Co-authored-by: Virginia Cepeda <virginia.cepeda@grafana.com>
2023-04-25 11:19:20 +02:00

131 lines
4.2 KiB
TypeScript

import { css } from '@emotion/css';
import React from 'react';
import { GrafanaTheme2, dateTime, dateTimeFormat } from '@grafana/data';
import { useStyles2, Tooltip } from '@grafana/ui';
import { Time } from 'app/features/explore/Time';
import { CombinedRule } from 'app/types/unified-alerting';
import { useCleanAnnotations } from '../../utils/annotations';
import { isRecordingRulerRule } from '../../utils/rules';
import { isNullDate } from '../../utils/time';
import { AlertLabels } from '../AlertLabels';
import { DetailsField } from '../DetailsField';
import { RuleDetailsActionButtons } from './RuleDetailsActionButtons';
import { RuleDetailsAnnotations } from './RuleDetailsAnnotations';
import { RuleDetailsDataSources } from './RuleDetailsDataSources';
import { RuleDetailsExpression } from './RuleDetailsExpression';
import { RuleDetailsMatchingInstances } from './RuleDetailsMatchingInstances';
interface Props {
rule: CombinedRule;
}
// The limit is set to 15 in order to upkeep the good performance
// and to encourage users to go to the rule details page to see the rest of the instances
// We don't want to paginate the instances list on the alert list page
export const INSTANCES_DISPLAY_LIMIT = 15;
export const RuleDetails = ({ rule }: Props) => {
const styles = useStyles2(getStyles);
const {
namespace: { rulesSource },
} = rule;
const annotations = useCleanAnnotations(rule.annotations);
return (
<div>
<RuleDetailsActionButtons rule={rule} rulesSource={rulesSource} isViewMode={false} />
<div className={styles.wrapper}>
<div className={styles.leftSide}>
{<EvaluationBehaviorSummary rule={rule} />}
{!!rule.labels && !!Object.keys(rule.labels).length && (
<DetailsField label="Labels" horizontal={true}>
<AlertLabels labels={rule.labels} />
</DetailsField>
)}
<RuleDetailsExpression rulesSource={rulesSource} rule={rule} annotations={annotations} />
<RuleDetailsAnnotations annotations={annotations} />
</div>
<div className={styles.rightSide}>
<RuleDetailsDataSources rulesSource={rulesSource} rule={rule} />
</div>
</div>
<RuleDetailsMatchingInstances rule={rule} itemsDisplayLimit={INSTANCES_DISPLAY_LIMIT} />
</div>
);
};
interface EvaluationBehaviorSummaryProps {
rule: CombinedRule;
}
const EvaluationBehaviorSummary = ({ rule }: EvaluationBehaviorSummaryProps) => {
let forDuration: string | undefined;
let every = rule.group.interval;
let lastEvaluation = rule.promRule?.lastEvaluation;
let lastEvaluationDuration = rule.promRule?.evaluationTime;
// recording rules don't have a for duration
if (!isRecordingRulerRule(rule.rulerRule)) {
forDuration = rule.rulerRule?.for;
}
return (
<>
{every && (
<DetailsField label="Evaluate" horizontal={true}>
Every {every}
</DetailsField>
)}
{forDuration && (
<DetailsField label="For" horizontal={true}>
{forDuration}
</DetailsField>
)}
{lastEvaluation && !isNullDate(lastEvaluation) && (
<DetailsField label="Last evaluation" horizontal={true}>
<Tooltip
placement="top"
content={`${dateTimeFormat(lastEvaluation, { format: 'YYYY-MM-DD HH:mm:ss' })}`}
theme="info"
>
<span>{`${dateTime(lastEvaluation).locale('en').fromNow(true)} ago`}</span>
</Tooltip>
</DetailsField>
)}
{lastEvaluation && !isNullDate(lastEvaluation) && lastEvaluationDuration !== undefined && (
<DetailsField label="Evaluation time" horizontal={true}>
<Tooltip placement="top" content={`${lastEvaluationDuration}s`} theme="info">
<span>{Time({ timeInMs: lastEvaluationDuration * 1000, humanize: true })}</span>
</Tooltip>
</DetailsField>
)}
</>
);
};
export const getStyles = (theme: GrafanaTheme2) => ({
wrapper: css`
display: flex;
flex-direction: row;
${theme.breakpoints.down('md')} {
flex-direction: column;
}
`,
leftSide: css`
flex: 1;
`,
rightSide: css`
${theme.breakpoints.up('md')} {
padding-left: 90px;
width: 300px;
}
`,
});