|
@@ -8,7 +8,6 @@ import minBy from 'lodash/minBy';
|
|
|
import {fetchTotalCount} from 'app/actionCreators/events';
|
|
|
import {Client} from 'app/api';
|
|
|
import Feature from 'app/components/acl/feature';
|
|
|
-import MarkLine from 'app/components/charts/components/markLine';
|
|
|
import EventsRequest from 'app/components/charts/eventsRequest';
|
|
|
import {LineChartSeries} from 'app/components/charts/lineChart';
|
|
|
import OptionSelector from 'app/components/charts/optionSelector';
|
|
@@ -25,13 +24,12 @@ import {t} from 'app/locale';
|
|
|
import space from 'app/styles/space';
|
|
|
import {Organization, Project} from 'app/types';
|
|
|
import {Series, SeriesDataUnit} from 'app/types/echarts';
|
|
|
-import {MINUTE} from 'app/utils/formatters';
|
|
|
import {
|
|
|
getCrashFreeRateSeries,
|
|
|
MINUTES_THRESHOLD_TO_DISPLAY_SECONDS,
|
|
|
} from 'app/utils/sessions';
|
|
|
-import theme from 'app/utils/theme';
|
|
|
import withApi from 'app/utils/withApi';
|
|
|
+import {getComparisonMarkLines} from 'app/views/alerts/changeAlerts/comparisonMarklines';
|
|
|
import {COMPARISON_DELTA_OPTIONS} from 'app/views/alerts/incidentRules/constants';
|
|
|
import {isSessionAggregate, SESSION_AGGREGATE_TO_FIELD} from 'app/views/alerts/utils';
|
|
|
import {AlertWizardAlertNames} from 'app/views/alerts/wizard/options';
|
|
@@ -39,7 +37,6 @@ import {getAlertTypeFromAggregateDataset} from 'app/views/alerts/wizard/utils';
|
|
|
|
|
|
import {
|
|
|
AlertRuleComparisonType,
|
|
|
- AlertRuleThresholdType,
|
|
|
Dataset,
|
|
|
IncidentRule,
|
|
|
SessionsAggregate,
|
|
@@ -227,80 +224,6 @@ class TriggersChart extends React.PureComponent<Props, State> {
|
|
|
);
|
|
|
}
|
|
|
|
|
|
- getComparisonMarkLines(
|
|
|
- timeseriesData: Series[] = [],
|
|
|
- comparisonTimeseriesData: Series[] = []
|
|
|
- ): LineChartSeries[] {
|
|
|
- const {timeWindow} = this.props;
|
|
|
- const changeStatuses: {name: number | string; status: string}[] = [];
|
|
|
-
|
|
|
- if (
|
|
|
- timeseriesData?.[0]?.data !== undefined &&
|
|
|
- timeseriesData[0].data.length > 1 &&
|
|
|
- comparisonTimeseriesData?.[0]?.data !== undefined &&
|
|
|
- comparisonTimeseriesData[0].data.length > 1
|
|
|
- ) {
|
|
|
- const changeData = comparisonTimeseriesData[0].data;
|
|
|
- const baseData = timeseriesData[0].data;
|
|
|
-
|
|
|
- if (
|
|
|
- this.props.triggers.some(({alertThreshold}) => typeof alertThreshold === 'number')
|
|
|
- ) {
|
|
|
- const lastPointLimit =
|
|
|
- (baseData[changeData.length - 1].name as number) - timeWindow * MINUTE;
|
|
|
- changeData.forEach(({name, value: comparisonValue}, idx) => {
|
|
|
- const baseValue = baseData[idx].value;
|
|
|
- const comparisonPercentage =
|
|
|
- comparisonValue === 0
|
|
|
- ? baseValue === 0
|
|
|
- ? 0
|
|
|
- : Infinity
|
|
|
- : ((baseValue - comparisonValue) / comparisonValue) * 100;
|
|
|
- const status = this.checkChangeStatus(comparisonPercentage);
|
|
|
- if (
|
|
|
- idx === 0 ||
|
|
|
- idx === changeData.length - 1 ||
|
|
|
- status !== changeStatuses[changeStatuses.length - 1].status
|
|
|
- ) {
|
|
|
- changeStatuses.push({name, status});
|
|
|
- }
|
|
|
- });
|
|
|
-
|
|
|
- return changeStatuses.slice(0, -1).map(({name, status}, idx) => ({
|
|
|
- seriesName: t('status'),
|
|
|
- type: 'line',
|
|
|
- markLine: MarkLine({
|
|
|
- silent: true,
|
|
|
- lineStyle: {
|
|
|
- color:
|
|
|
- status === 'critical'
|
|
|
- ? theme.red300
|
|
|
- : status === 'warning'
|
|
|
- ? theme.yellow300
|
|
|
- : theme.green300,
|
|
|
- type: 'solid',
|
|
|
- width: 4,
|
|
|
- },
|
|
|
- data: [
|
|
|
- [
|
|
|
- {coord: [name, 0]},
|
|
|
- {
|
|
|
- coord: [
|
|
|
- Math.min(changeStatuses[idx + 1].name as number, lastPointLimit),
|
|
|
- 0,
|
|
|
- ],
|
|
|
- },
|
|
|
- ] as any,
|
|
|
- ],
|
|
|
- }),
|
|
|
- data: [],
|
|
|
- }));
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- return [];
|
|
|
- }
|
|
|
-
|
|
|
async fetchTotalCount() {
|
|
|
const {api, organization, environment, projects, query} = this.props;
|
|
|
const statsPeriod = this.getStatsPeriod();
|
|
@@ -318,55 +241,6 @@ class TriggersChart extends React.PureComponent<Props, State> {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- checkChangeStatus(value: number): string {
|
|
|
- const {thresholdType, triggers} = this.props;
|
|
|
- const criticalTrigger = triggers?.find(trig => trig.label === 'critical');
|
|
|
- const warningTrigger = triggers?.find(trig => trig.label === 'warning');
|
|
|
- const criticalTriggerAlertThreshold =
|
|
|
- typeof criticalTrigger?.alertThreshold === 'number'
|
|
|
- ? criticalTrigger.alertThreshold
|
|
|
- : undefined;
|
|
|
- const warningTriggerAlertThreshold =
|
|
|
- typeof warningTrigger?.alertThreshold === 'number'
|
|
|
- ? warningTrigger.alertThreshold
|
|
|
- : undefined;
|
|
|
-
|
|
|
- // Need to catch the critical threshold cases before warning threshold cases
|
|
|
- if (
|
|
|
- thresholdType === AlertRuleThresholdType.ABOVE &&
|
|
|
- criticalTriggerAlertThreshold &&
|
|
|
- value >= criticalTriggerAlertThreshold
|
|
|
- ) {
|
|
|
- return 'critical';
|
|
|
- }
|
|
|
- if (
|
|
|
- thresholdType === AlertRuleThresholdType.ABOVE &&
|
|
|
- warningTriggerAlertThreshold &&
|
|
|
- value >= warningTriggerAlertThreshold
|
|
|
- ) {
|
|
|
- return 'warning';
|
|
|
- }
|
|
|
- // When threshold is below(lower than in comparison alerts) the % diff value is negative
|
|
|
- // It crosses the threshold if its abs value is greater than threshold
|
|
|
- // -80% change crosses below 60% threshold -1 * (-80) > 60
|
|
|
- if (
|
|
|
- thresholdType === AlertRuleThresholdType.BELOW &&
|
|
|
- criticalTriggerAlertThreshold &&
|
|
|
- -1 * value >= criticalTriggerAlertThreshold
|
|
|
- ) {
|
|
|
- return 'critical';
|
|
|
- }
|
|
|
- if (
|
|
|
- thresholdType === AlertRuleThresholdType.BELOW &&
|
|
|
- warningTriggerAlertThreshold &&
|
|
|
- -1 * value >= warningTriggerAlertThreshold
|
|
|
- ) {
|
|
|
- return 'warning';
|
|
|
- }
|
|
|
-
|
|
|
- return '';
|
|
|
- }
|
|
|
-
|
|
|
renderChart(
|
|
|
timeseriesData: Series[] = [],
|
|
|
isLoading: boolean,
|
|
@@ -448,11 +322,14 @@ class TriggersChart extends React.PureComponent<Props, State> {
|
|
|
aggregate,
|
|
|
environment,
|
|
|
comparisonDelta,
|
|
|
+ triggers,
|
|
|
+ thresholdType,
|
|
|
} = this.props;
|
|
|
|
|
|
const period = this.getStatsPeriod();
|
|
|
- const renderComparisonStats =
|
|
|
- organization.features.includes('change-alerts') && comparisonDelta;
|
|
|
+ const renderComparisonStats = Boolean(
|
|
|
+ organization.features.includes('change-alerts') && comparisonDelta
|
|
|
+ );
|
|
|
|
|
|
return isSessionAggregate(aggregate) ? (
|
|
|
<SessionsRequest
|
|
@@ -513,9 +390,12 @@ class TriggersChart extends React.PureComponent<Props, State> {
|
|
|
{({loading, reloading, timeseriesData, comparisonTimeseriesData}) => {
|
|
|
let comparisonMarkLines: LineChartSeries[] = [];
|
|
|
if (renderComparisonStats && comparisonTimeseriesData) {
|
|
|
- comparisonMarkLines = this.getComparisonMarkLines(
|
|
|
+ comparisonMarkLines = getComparisonMarkLines(
|
|
|
timeseriesData,
|
|
|
- comparisonTimeseriesData
|
|
|
+ comparisonTimeseriesData,
|
|
|
+ timeWindow,
|
|
|
+ triggers,
|
|
|
+ thresholdType
|
|
|
);
|
|
|
}
|
|
|
|