"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); Object.defineProperty(exports, "__esModule", { value: true }); exports.nodeTypes = exports.defaultExpression = exports.default = exports.Expressions = exports.ExpressionRow = void 0; var _eui = require("@elastic/eui"); var _i18n = require("@kbn/i18n"); var _react = require("@emotion/react"); var _i18nReact = require("@kbn/i18n-react"); var _public = require("@kbn/triggers-actions-ui-plugin/public"); var _lodash = require("lodash"); var _react2 = _interopRequireWildcard(require("react")); var _useToggle = _interopRequireDefault(require("react-use/lib/useToggle")); var _metrics = require("../../../../common/alerting/metrics"); var _snapshot_api = require("../../../../common/http_api/snapshot_api"); var _inventory_models = require("../../../../common/inventory_models"); var _aws_ec = require("../../../../common/inventory_models/aws_ec2"); var _aws_rds = require("../../../../common/inventory_models/aws_rds"); var _aws_s = require("../../../../common/inventory_models/aws_s3"); var _aws_sqs = require("../../../../common/inventory_models/aws_sqs"); var _container = require("../../../../common/inventory_models/container"); var _host = require("../../../../common/inventory_models/host"); var _pod = require("../../../../common/inventory_models/pod"); var _types = require("../../../../common/inventory_models/types"); var _snapshot_metric_i18n = require("../../../../common/snapshot_metric_i18n"); var _metrics_source = require("../../../containers/metrics_source"); var _kuery_bar = require("../../../pages/metrics/metrics_explorer/components/kuery_bar"); var _kuery = require("../../../utils/kuery"); var _expression_chart = require("./expression_chart"); var _metric = require("./metric"); var _node_type = require("./node_type"); function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); } function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; } /* * 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 FILTER_TYPING_DEBOUNCE_MS = 500; const defaultExpression = { metric: 'cpu', comparator: _metrics.Comparator.GT, threshold: [], timeSize: 1, timeUnit: 'm', customMetric: { type: 'custom', id: 'alert-custom-metric', field: '', aggregation: 'avg' } }; exports.defaultExpression = defaultExpression; const Expressions = props => { const { setRuleParams, ruleParams, errors, metadata } = props; const { source, createDerivedIndexPattern } = (0, _metrics_source.useSourceContext)(); const [timeSize, setTimeSize] = (0, _react2.useState)(1); const [timeUnit, setTimeUnit] = (0, _react2.useState)('m'); const derivedIndexPattern = (0, _react2.useMemo)(() => createDerivedIndexPattern(), [createDerivedIndexPattern]); const updateParams = (0, _react2.useCallback)((id, e) => { const exp = ruleParams.criteria ? ruleParams.criteria.slice() : []; exp[id] = e; setRuleParams('criteria', exp); }, [setRuleParams, ruleParams.criteria]); const addExpression = (0, _react2.useCallback)(() => { var _ruleParams$criteria; const exp = ((_ruleParams$criteria = ruleParams.criteria) === null || _ruleParams$criteria === void 0 ? void 0 : _ruleParams$criteria.slice()) || []; exp.push({ ...defaultExpression, timeSize: timeSize !== null && timeSize !== void 0 ? timeSize : defaultExpression.timeSize, timeUnit: timeUnit !== null && timeUnit !== void 0 ? timeUnit : defaultExpression.timeUnit }); setRuleParams('criteria', exp); }, [setRuleParams, ruleParams.criteria, timeSize, timeUnit]); const removeExpression = (0, _react2.useCallback)(id => { const exp = ruleParams.criteria.slice(); if (exp.length > 1) { exp.splice(id, 1); setRuleParams('criteria', exp); } }, [setRuleParams, ruleParams.criteria]); const onFilterChange = (0, _react2.useCallback)(filter => { setRuleParams('filterQueryText', filter !== null && filter !== void 0 ? filter : ''); try { setRuleParams('filterQuery', (0, _kuery.convertKueryToElasticSearchQuery)(filter, derivedIndexPattern, false) || ''); } catch (e) { setRuleParams('filterQuery', _metrics.QUERY_INVALID); } }, [derivedIndexPattern, setRuleParams]); /* eslint-disable-next-line react-hooks/exhaustive-deps */ const debouncedOnFilterChange = (0, _react2.useCallback)((0, _lodash.debounce)(onFilterChange, FILTER_TYPING_DEBOUNCE_MS), [onFilterChange]); const emptyError = (0, _react2.useMemo)(() => { return { aggField: [], timeSizeUnit: [], timeWindowSize: [] }; }, []); const updateTimeSize = (0, _react2.useCallback)(ts => { const criteria = ruleParams.criteria.map(c => ({ ...c, timeSize: ts })); setTimeSize(ts || undefined); setRuleParams('criteria', criteria); }, [ruleParams.criteria, setRuleParams]); const updateTimeUnit = (0, _react2.useCallback)(tu => { const criteria = ruleParams.criteria.map(c => ({ ...c, timeUnit: tu })); setTimeUnit(tu); setRuleParams('criteria', criteria); }, [ruleParams.criteria, setRuleParams]); const updateNodeType = (0, _react2.useCallback)(nt => { setRuleParams('nodeType', nt); }, [setRuleParams]); const handleFieldSearchChange = (0, _react2.useCallback)(e => onFilterChange(e.target.value), [onFilterChange]); const preFillAlertCriteria = (0, _react2.useCallback)(() => { const md = metadata; if (md && md.options) { setRuleParams('criteria', [{ ...defaultExpression, metric: md.options.metric.type, customMetric: _snapshot_api.SnapshotCustomMetricInputRT.is(md.options.metric) ? md.options.metric : defaultExpression.customMetric }]); } else { setRuleParams('criteria', [defaultExpression]); } }, [metadata, setRuleParams]); const preFillAlertFilter = (0, _react2.useCallback)(() => { const md = metadata; if (md && md.filter) { setRuleParams('filterQueryText', md.filter); setRuleParams('filterQuery', (0, _kuery.convertKueryToElasticSearchQuery)(md.filter, derivedIndexPattern) || ''); } }, [metadata, derivedIndexPattern, setRuleParams]); (0, _react2.useEffect)(() => { const md = metadata; if (!ruleParams.nodeType) { if (md && md.nodeType) { setRuleParams('nodeType', md.nodeType); } else { setRuleParams('nodeType', 'host'); } } if (ruleParams.criteria && ruleParams.criteria.length) { setTimeSize(ruleParams.criteria[0].timeSize); setTimeUnit(ruleParams.criteria[0].timeUnit); } else { preFillAlertCriteria(); } if (ruleParams.filterQuery === undefined) { preFillAlertFilter(); } if (!ruleParams.sourceId) { setRuleParams('sourceId', (source === null || source === void 0 ? void 0 : source.id) || 'default'); } }, [metadata, derivedIndexPattern, defaultExpression, source]); // eslint-disable-line react-hooks/exhaustive-deps return /*#__PURE__*/_react2.default.createElement(_react2.default.Fragment, null, /*#__PURE__*/_react2.default.createElement(_eui.EuiSpacer, { size: "m" }), /*#__PURE__*/_react2.default.createElement(_eui.EuiText, { size: "xs" }, /*#__PURE__*/_react2.default.createElement("h4", null, /*#__PURE__*/_react2.default.createElement(_i18nReact.FormattedMessage, { id: "xpack.infra.metrics.alertFlyout.conditions", defaultMessage: "Conditions" }))), /*#__PURE__*/_react2.default.createElement("div", { css: StyledExpressionCss }, /*#__PURE__*/_react2.default.createElement(_eui.EuiFlexGroup, { css: StyledExpressionRowCss }, /*#__PURE__*/_react2.default.createElement("div", { css: NonCollapsibleExpressionCss }, /*#__PURE__*/_react2.default.createElement(_node_type.NodeTypeExpression, { options: nodeTypes, value: ruleParams.nodeType || 'host', onChange: updateNodeType })))), /*#__PURE__*/_react2.default.createElement(_eui.EuiSpacer, { size: "xs" }), ruleParams.criteria && ruleParams.criteria.map((e, idx) => { return /*#__PURE__*/_react2.default.createElement(ExpressionRow, { nodeType: ruleParams.nodeType, canDelete: ruleParams.criteria.length > 1, remove: removeExpression, addExpression: addExpression, key: idx // idx's don't usually make good key's but here the index has semantic meaning , expressionId: idx, setRuleParams: updateParams, errors: errors[idx] || emptyError, expression: e || {}, fields: derivedIndexPattern.fields }, /*#__PURE__*/_react2.default.createElement(_expression_chart.ExpressionChart, { expression: e, filterQuery: ruleParams.filterQuery, nodeType: ruleParams.nodeType, sourceId: ruleParams.sourceId, "data-test-subj": "preview-chart" })); }), /*#__PURE__*/_react2.default.createElement("div", { css: NonCollapsibleExpressionCss }, /*#__PURE__*/_react2.default.createElement(_public.ForLastExpression, { timeWindowSize: timeSize, timeWindowUnit: timeUnit, errors: emptyError, onChangeWindowSize: updateTimeSize, onChangeWindowUnit: updateTimeUnit })), /*#__PURE__*/_react2.default.createElement("div", null, /*#__PURE__*/_react2.default.createElement(_eui.EuiButtonEmpty, { "data-test-subj": "infraExpressionsAddConditionButton", color: "primary", iconSide: "left", flush: "left", iconType: "plusInCircleFilled", onClick: addExpression }, /*#__PURE__*/_react2.default.createElement(_i18nReact.FormattedMessage, { id: "xpack.infra.metrics.alertFlyout.addCondition", defaultMessage: "Add condition" }))), /*#__PURE__*/_react2.default.createElement(_eui.EuiSpacer, { size: "m" }), /*#__PURE__*/_react2.default.createElement(_eui.EuiCheckbox, { id: "metrics-alert-no-data-toggle", label: /*#__PURE__*/_react2.default.createElement(_react2.default.Fragment, null, _i18n.i18n.translate('xpack.infra.metrics.alertFlyout.alertOnNoData', { defaultMessage: "Alert me if there's no data" }), ' ', /*#__PURE__*/_react2.default.createElement(_eui.EuiToolTip, { content: _i18n.i18n.translate('xpack.infra.metrics.alertFlyout.noDataHelpText', { defaultMessage: 'Enable this to trigger the action if the metric(s) do not report any data over the expected time period, or if the alert fails to query Elasticsearch' }) }, /*#__PURE__*/_react2.default.createElement(_eui.EuiIcon, { type: "questionInCircle", color: "subdued" }))), checked: ruleParams.alertOnNoData, onChange: e => setRuleParams('alertOnNoData', e.target.checked) }), /*#__PURE__*/_react2.default.createElement(_eui.EuiSpacer, { size: "m" }), /*#__PURE__*/_react2.default.createElement(_eui.EuiFormRow, { label: _i18n.i18n.translate('xpack.infra.metrics.alertFlyout.filterLabel', { defaultMessage: 'Filter (optional)' }), helpText: _i18n.i18n.translate('xpack.infra.metrics.alertFlyout.filterHelpText', { defaultMessage: 'Use a KQL expression to limit the scope of your alert trigger.' }), fullWidth: true, display: "rowCompressed" }, metadata ? /*#__PURE__*/_react2.default.createElement(_kuery_bar.MetricsExplorerKueryBar, { derivedIndexPattern: derivedIndexPattern, onSubmit: onFilterChange, onChange: debouncedOnFilterChange, value: ruleParams.filterQueryText }) : /*#__PURE__*/_react2.default.createElement(_eui.EuiFieldSearch, { "data-test-subj": "infraExpressionsFieldSearch", onChange: handleFieldSearchChange, value: ruleParams.filterQueryText, fullWidth: true })), /*#__PURE__*/_react2.default.createElement(_eui.EuiSpacer, { size: "m" })); }; // required for dynamic import // eslint-disable-next-line import/no-default-export exports.Expressions = Expressions; var _default = (0, _metrics_source.withSourceProvider)(Expressions)('default'); exports.default = _default; const NonCollapsibleExpressionCss = (0, _react.css)` margin-left: 28px; `; const StyledExpressionRowCss = (0, _react.css)` display: flex; flex-wrap: wrap; margin: 0 -4px; `; const StyledExpressionCss = (0, _react.css)` padding: 0 4px; `; const StyledHealthCss = (0, _react.css)` margin-left: 4px; `; const ExpressionRow = props => { var _ref, _ref2, _ofFields$find; const [isExpanded, toggle] = (0, _useToggle.default)(true); const { children, setRuleParams, expression, errors, expressionId, remove, canDelete, fields } = props; const { metric, comparator = _metrics.Comparator.GT, threshold = [], customMetric, warningThreshold = [], warningComparator } = expression; const [displayWarningThreshold, setDisplayWarningThreshold] = (0, _react2.useState)(Boolean(warningThreshold === null || warningThreshold === void 0 ? void 0 : warningThreshold.length)); const updateMetric = (0, _react2.useCallback)(m => { const newMetric = _types.SnapshotMetricTypeRT.is(m) ? m : Boolean(m) ? 'custom' : undefined; const newAlertParams = { ...expression, metric: newMetric }; setRuleParams(expressionId, newAlertParams); }, [expressionId, expression, setRuleParams]); const updateCustomMetric = (0, _react2.useCallback)(cm => { if (_snapshot_api.SnapshotCustomMetricInputRT.is(cm)) { setRuleParams(expressionId, { ...expression, customMetric: cm }); } }, [expressionId, expression, setRuleParams]); const updateComparator = (0, _react2.useCallback)(c => { setRuleParams(expressionId, { ...expression, comparator: c }); }, [expressionId, expression, setRuleParams]); const updateWarningComparator = (0, _react2.useCallback)(c => { setRuleParams(expressionId, { ...expression, warningComparator: c }); }, [expressionId, expression, setRuleParams]); const updateThreshold = (0, _react2.useCallback)(t => { if (t.join() !== expression.threshold.join()) { setRuleParams(expressionId, { ...expression, threshold: t }); } }, [expressionId, expression, setRuleParams]); const updateWarningThreshold = (0, _react2.useCallback)(t => { var _expression$warningTh; if (t.join() !== ((_expression$warningTh = expression.warningThreshold) === null || _expression$warningTh === void 0 ? void 0 : _expression$warningTh.join())) { setRuleParams(expressionId, { ...expression, warningThreshold: t }); } }, [expressionId, expression, setRuleParams]); const toggleWarningThreshold = (0, _react2.useCallback)(() => { if (!displayWarningThreshold) { setDisplayWarningThreshold(true); setRuleParams(expressionId, { ...expression, warningComparator: comparator, warningThreshold: [] }); } else { setDisplayWarningThreshold(false); setRuleParams(expressionId, (0, _lodash.omit)(expression, 'warningComparator', 'warningThreshold')); } }, [displayWarningThreshold, setDisplayWarningThreshold, setRuleParams, comparator, expression, expressionId]); const criticalThresholdExpression = /*#__PURE__*/_react2.default.createElement(ThresholdElement, { comparator: comparator, threshold: threshold, updateComparator: updateComparator, updateThreshold: updateThreshold, errors: (_ref = errors.critical) !== null && _ref !== void 0 ? _ref : {}, metric: metric }); const warningThresholdExpression = displayWarningThreshold && /*#__PURE__*/_react2.default.createElement(ThresholdElement, { comparator: warningComparator || comparator, threshold: warningThreshold, updateComparator: updateWarningComparator, updateThreshold: updateWarningThreshold, errors: (_ref2 = errors.warning) !== null && _ref2 !== void 0 ? _ref2 : {}, metric: metric }); const ofFields = (0, _react2.useMemo)(() => { let myMetrics = _host.hostSnapshotMetricTypes; switch (props.nodeType) { case 'awsEC2': myMetrics = _aws_ec.awsEC2SnapshotMetricTypes; break; case 'awsRDS': myMetrics = _aws_rds.awsRDSSnapshotMetricTypes; break; case 'awsS3': myMetrics = _aws_s.awsS3SnapshotMetricTypes; break; case 'awsSQS': myMetrics = _aws_sqs.awsSQSSnapshotMetricTypes; break; case 'host': myMetrics = _host.hostSnapshotMetricTypes; break; case 'pod': myMetrics = _pod.podSnapshotMetricTypes; break; case 'container': myMetrics = _container.containerSnapshotMetricTypes; break; } return myMetrics.map(_snapshot_metric_i18n.toMetricOpt); }, [props.nodeType]); return /*#__PURE__*/_react2.default.createElement(_react2.default.Fragment, null, /*#__PURE__*/_react2.default.createElement(_eui.EuiFlexGroup, { gutterSize: "xs" }, /*#__PURE__*/_react2.default.createElement(_eui.EuiFlexItem, { grow: false }, /*#__PURE__*/_react2.default.createElement(_eui.EuiButtonIcon, { iconType: isExpanded ? 'arrowDown' : 'arrowRight', onClick: toggle, "aria-label": _i18n.i18n.translate('xpack.infra.metrics.alertFlyout.expandRowLabel', { defaultMessage: 'Expand row.' }) })), /*#__PURE__*/_react2.default.createElement(_eui.EuiFlexItem, { grow: true }, /*#__PURE__*/_react2.default.createElement(_eui.EuiFlexGroup, { css: StyledExpressionRowCss }, /*#__PURE__*/_react2.default.createElement("div", { css: StyledExpressionCss }, /*#__PURE__*/_react2.default.createElement(_metric.MetricExpression, { metric: { value: metric, text: ((_ofFields$find = ofFields.find(v => (v === null || v === void 0 ? void 0 : v.value) === metric)) === null || _ofFields$find === void 0 ? void 0 : _ofFields$find.text) || '' }, metrics: ofFields.filter(m => m !== undefined && m.value !== undefined), onChange: updateMetric, onChangeCustom: updateCustomMetric, errors: errors, customMetric: customMetric, fields: fields })), !displayWarningThreshold && criticalThresholdExpression), displayWarningThreshold && /*#__PURE__*/_react2.default.createElement(_react2.default.Fragment, null, /*#__PURE__*/_react2.default.createElement(_eui.EuiFlexGroup, { css: StyledExpressionRowCss }, criticalThresholdExpression, /*#__PURE__*/_react2.default.createElement(_eui.EuiHealth, { css: StyledHealthCss, color: "danger" }, /*#__PURE__*/_react2.default.createElement(_i18nReact.FormattedMessage, { id: "xpack.infra.metrics.alertFlyout.criticalThreshold", defaultMessage: "Alert" }))), /*#__PURE__*/_react2.default.createElement(_eui.EuiFlexGroup, { css: StyledExpressionRowCss }, warningThresholdExpression, /*#__PURE__*/_react2.default.createElement(_eui.EuiHealth, { css: StyledHealthCss, color: "warning" }, /*#__PURE__*/_react2.default.createElement(_i18nReact.FormattedMessage, { id: "xpack.infra.metrics.alertFlyout.warningThreshold", defaultMessage: "Warning" })), /*#__PURE__*/_react2.default.createElement(_eui.EuiButtonIcon, { "aria-label": _i18n.i18n.translate('xpack.infra.metrics.alertFlyout.removeWarningThreshold', { defaultMessage: 'Remove warningThreshold' }), iconSize: "s", color: "text", iconType: "minusInCircleFilled", onClick: toggleWarningThreshold }))), !displayWarningThreshold && /*#__PURE__*/_react2.default.createElement(_react2.default.Fragment, null, ' ', /*#__PURE__*/_react2.default.createElement(_eui.EuiSpacer, { size: "xs" }), /*#__PURE__*/_react2.default.createElement(_eui.EuiFlexGroup, { css: StyledExpressionRowCss }, /*#__PURE__*/_react2.default.createElement(_eui.EuiButtonEmpty, { "data-test-subj": "infraExpressionRowAddWarningThresholdButton", color: "primary", flush: "left", size: "xs", iconType: "plusInCircleFilled", onClick: toggleWarningThreshold }, /*#__PURE__*/_react2.default.createElement(_i18nReact.FormattedMessage, { id: "xpack.infra.metrics.alertFlyout.addWarningThreshold", defaultMessage: "Add warning threshold" }))))), canDelete && /*#__PURE__*/_react2.default.createElement(_eui.EuiFlexItem, { grow: false }, /*#__PURE__*/_react2.default.createElement(_eui.EuiButtonIcon, { "aria-label": _i18n.i18n.translate('xpack.infra.metrics.alertFlyout.removeCondition', { defaultMessage: 'Remove condition' }), color: "danger", iconType: "trash", onClick: () => remove(expressionId) }))), isExpanded ? /*#__PURE__*/_react2.default.createElement("div", { css: (0, _react.css)` padding: 0 0 0 28px; ` }, children) : null, /*#__PURE__*/_react2.default.createElement(_eui.EuiSpacer, { size: "s" })); }; exports.ExpressionRow = ExpressionRow; const ThresholdElement = ({ updateComparator, updateThreshold, threshold, metric, comparator, errors }) => { var _metricUnit$metric; return /*#__PURE__*/_react2.default.createElement(_react2.default.Fragment, null, /*#__PURE__*/_react2.default.createElement("div", { css: StyledExpressionCss }, /*#__PURE__*/_react2.default.createElement(_public.ThresholdExpression, { thresholdComparator: comparator || _metrics.Comparator.GT, threshold: threshold, onChangeSelectedThresholdComparator: updateComparator, onChangeSelectedThreshold: updateThreshold, errors: errors })), metric && /*#__PURE__*/_react2.default.createElement("div", { css: (0, _react.css)` align-self: center; ` }, /*#__PURE__*/_react2.default.createElement(_eui.EuiText, { size: "s" }, ((_metricUnit$metric = metricUnit[metric]) === null || _metricUnit$metric === void 0 ? void 0 : _metricUnit$metric.label) || ''))); }; const getDisplayNameForType = type => { const inventoryModel = (0, _inventory_models.findInventoryModel)(type); return inventoryModel.displayName; }; const nodeTypes = { host: { text: getDisplayNameForType('host'), value: 'host' }, pod: { text: getDisplayNameForType('pod'), value: 'pod' }, container: { text: getDisplayNameForType('container'), value: 'container' }, awsEC2: { text: getDisplayNameForType('awsEC2'), value: 'awsEC2' }, awsS3: { text: getDisplayNameForType('awsS3'), value: 'awsS3' }, awsRDS: { text: getDisplayNameForType('awsRDS'), value: 'awsRDS' }, awsSQS: { text: getDisplayNameForType('awsSQS'), value: 'awsSQS' } }; exports.nodeTypes = nodeTypes; const metricUnit = { count: { label: '' }, cpu: { label: '%' }, memory: { label: '%' }, rx: { label: 'bits/s' }, tx: { label: 'bits/s' }, logRate: { label: '/s' }, diskIOReadBytes: { label: 'bytes/s' }, diskIOWriteBytes: { label: 'bytes/s' }, s3BucketSize: { label: 'bytes' }, s3TotalRequests: { label: '' }, s3NumberOfObjects: { label: '' }, s3UploadBytes: { label: 'bytes' }, s3DownloadBytes: { label: 'bytes' }, sqsOldestMessage: { label: 'seconds' }, rdsLatency: { label: 'ms' }, custom: { label: '' } };