"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.EQUATION_REGEX = void 0; exports.validateMetricThreshold = validateMetricThreshold; var _esQuery = require("@kbn/es-query"); var _i18n = require("@kbn/i18n"); var _lodash = require("lodash"); var _metrics = require("../../../../common/alerting/metrics"); /* * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one * or more contributor license agreements. Licensed under the Elastic License * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ const EQUATION_REGEX = /[^A-Z|+|\-|\s|\d+|\.|\(|\)|\/|\*|>|<|=|\?|\:|&|\!|\|]+/g; exports.EQUATION_REGEX = EQUATION_REGEX; const isCustomMetricExpressionParams = subject => { return subject.aggType === _metrics.Aggregators.CUSTOM; }; function validateMetricThreshold({ criteria, filterQuery }) { const validationResult = { errors: {} }; const errors = {}; validationResult.errors = errors; if (filterQuery === _metrics.QUERY_INVALID) { errors.filterQuery = [_i18n.i18n.translate('xpack.infra.metrics.alertFlyout.error.invalidFilterQuery', { defaultMessage: 'Filter query is invalid.' })]; } if (!criteria || !criteria.length) { return validationResult; } criteria.forEach((c, idx) => { // Create an id for each criteria, so we can map errors to specific criteria. const id = idx.toString(); errors[id] = errors[id] || { aggField: [], timeSizeUnit: [], timeWindowSize: [], critical: { threshold0: [], threshold1: [] }, warning: { threshold0: [], threshold1: [] }, metric: [], filterQuery: [], customMetrics: {} }; if (!c.aggType) { errors[id].aggField.push(_i18n.i18n.translate('xpack.infra.metrics.alertFlyout.error.aggregationRequired', { defaultMessage: 'Aggregation is required.' })); } if (!c.threshold || !c.threshold.length) { errors[id].critical.threshold0.push(_i18n.i18n.translate('xpack.infra.metrics.alertFlyout.error.thresholdRequired', { defaultMessage: 'Threshold is required.' })); } if (c.warningThreshold && !c.warningThreshold.length) { errors[id].warning.threshold0.push(_i18n.i18n.translate('xpack.infra.metrics.alertFlyout.error.thresholdRequired', { defaultMessage: 'Threshold is required.' })); } for (const props of [{ comparator: c.comparator, threshold: c.threshold, type: 'critical' }, { comparator: c.warningComparator, threshold: c.warningThreshold, type: 'warning' }]) { // The Threshold component returns an empty array with a length ([empty]) because it's using delete newThreshold[i]. // We need to use [...c.threshold] to convert it to an array with an undefined value ([undefined]) so we can test each element. const { comparator, threshold, type } = props; if (threshold && threshold.length && ![...threshold].every(isNumber)) { [...threshold].forEach((v, i) => { if (!isNumber(v)) { const key = i === 0 ? 'threshold0' : 'threshold1'; errors[id][type][key].push(_i18n.i18n.translate('xpack.infra.metrics.alertFlyout.error.thresholdTypeRequired', { defaultMessage: 'Thresholds must contain a valid number.' })); } }); } if (comparator === _metrics.Comparator.BETWEEN && (!threshold || threshold.length < 2)) { errors[id][type].threshold1.push(_i18n.i18n.translate('xpack.infra.metrics.alertFlyout.error.thresholdRequired', { defaultMessage: 'Threshold is required.' })); } } if (!c.timeSize) { errors[id].timeWindowSize.push(_i18n.i18n.translate('xpack.infra.metrics.alertFlyout.error.timeRequred', { defaultMessage: 'Time size is Required.' })); } if (c.aggType !== 'count' && c.aggType !== 'custom' && !c.metric) { errors[id].metric.push(_i18n.i18n.translate('xpack.infra.metrics.alertFlyout.error.metricRequired', { defaultMessage: 'Metric is required.' })); } if (isCustomMetricExpressionParams(c)) { if (!c.customMetrics || c.customMetrics && c.customMetrics.length < 1) { errors[id].customMetricsError = _i18n.i18n.translate('xpack.infra.metrics.alertFlyout.error.customMetricsError', { defaultMessage: 'You must define at least 1 custom metric' }); } else { c.customMetrics.forEach(metric => { const customMetricErrors = {}; if (!metric.aggType) { customMetricErrors.aggType = _i18n.i18n.translate('xpack.infra.metrics.alertFlyout.error.customMetrics.aggTypeRequired', { defaultMessage: 'Aggregation is required' }); } if (metric.aggType !== 'count' && !metric.field) { customMetricErrors.field = _i18n.i18n.translate('xpack.infra.metrics.alertFlyout.error.customMetrics.fieldRequired', { defaultMessage: 'Field is required' }); } if (metric.aggType === 'count' && metric.filter) { try { (0, _esQuery.fromKueryExpression)(metric.filter); } catch (e) { customMetricErrors.filter = e.message; } } if (!(0, _lodash.isEmpty)(customMetricErrors)) { errors[id].customMetrics[metric.name] = customMetricErrors; } }); } if (c.equation && c.equation.match(EQUATION_REGEX)) { errors[id].equation = _i18n.i18n.translate('xpack.infra.metrics.alertFlyout.error.equation.invalidCharacters', { defaultMessage: 'The equation field only supports the following characters: A-Z, +, -, /, *, (, ), ?, !, &, :, |, >, <, =' }); } } }); return validationResult; } const isNumber = value => typeof value === 'number';