"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.FieldsControls = exports.FieldsConfig = exports.ChangePointResults = void 0; var _react = _interopRequireWildcard(require("react")); var _eui = require("@elastic/eui"); var _i18nReact = require("@kbn/i18n-react"); var _i18n = require("@kbn/i18n"); var _mlDatePicker = require("@kbn/ml-date-picker"); var _public = require("@kbn/presentation-util-plugin/public"); var _mlIsDefined = require("@kbn/ml-is-defined"); var _max_series_control = require("./max_series_control"); var _constants = require("../../../common/constants"); var _use_cases_modal = require("../../hooks/use_cases_modal"); var _use_data_source = require("../../hooks/use_data_source"); var _use_aiops_app_context = require("../../hooks/use_aiops_app_context"); var _change_points_table = require("./change_points_table"); var _constants2 = require("./constants"); var _function_picker = require("./function_picker"); var _metric_field_selector = require("./metric_field_selector"); var _split_field_selector = require("./split_field_selector"); var _change_point_detection_context = require("./change_point_detection_context"); var _use_change_point_agg_request = require("./use_change_point_agg_request"); var _use_split_field_cardinality = require("./use_split_field_cardinality"); 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 selectControlCss = { width: '350px' }; const SavedObjectSaveModalDashboard = (0, _public.withSuspense)(_public.LazySavedObjectSaveModalDashboard); /** * Contains panels with controls and change point results. */ const FieldsConfig = () => { const { requestParams: { fieldConfigs }, updateRequestParams, selectedChangePoints, setSelectedChangePoints } = (0, _change_point_detection_context.useChangePointDetectionContext)(); const onChange = (0, _react.useCallback)((update, index) => { fieldConfigs.splice(index, 1, update); updateRequestParams({ fieldConfigs }); }, [updateRequestParams, fieldConfigs]); const onAdd = (0, _react.useCallback)(() => { const update = [...fieldConfigs]; update.push(update[update.length - 1]); updateRequestParams({ fieldConfigs: update }); }, [updateRequestParams, fieldConfigs]); const onRemove = (0, _react.useCallback)(index => { fieldConfigs.splice(index, 1); updateRequestParams({ fieldConfigs }); delete selectedChangePoints[index]; setSelectedChangePoints({ ...selectedChangePoints }); }, [updateRequestParams, fieldConfigs, setSelectedChangePoints, selectedChangePoints]); const onSelectionChange = (0, _react.useCallback)((update, index) => { setSelectedChangePoints({ ...selectedChangePoints, [index]: update }); }, [setSelectedChangePoints, selectedChangePoints]); return /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, fieldConfigs.map((fieldConfig, index) => { const key = index; return /*#__PURE__*/_react.default.createElement(_react.default.Fragment, { key: key }, /*#__PURE__*/_react.default.createElement(FieldPanel, { panelIndex: index, "data-test-subj": `aiopsChangePointPanel_${index}`, fieldConfig: fieldConfig, onChange: value => onChange(value, index), onRemove: onRemove.bind(null, index), removeDisabled: fieldConfigs.length === 1, onSelectionChange: update => { onSelectionChange(update, index); } }), /*#__PURE__*/_react.default.createElement(_eui.EuiSpacer, { size: "s" })); }), /*#__PURE__*/_react.default.createElement(_eui.EuiButton, { onClick: onAdd, disabled: fieldConfigs.length >= _constants2.MAX_CHANGE_POINT_CONFIGS, "data-test-subj": 'aiopsChangePointAddConfig' }, /*#__PURE__*/_react.default.createElement(_i18nReact.FormattedMessage, { id: "xpack.aiops.changePointDetection.addButtonLabel", defaultMessage: "Add" }))); }; exports.FieldsConfig = FieldsConfig; /** * Components that combines field config and state for change point response. * @param fieldConfig * @param onChange * @param onRemove * @param removeDisabled * @constructor */ const FieldPanel = ({ panelIndex, fieldConfig, onChange, onRemove, removeDisabled, onSelectionChange, 'data-test-subj': dataTestSubj }) => { var _capabilities$dashboa, _capabilities$dashboa2, _cases$helpers$canUse, _cases$helpers; const { embeddable, application: { capabilities }, cases } = (0, _use_aiops_app_context.useAiopsAppContext)(); const { dataView } = (0, _use_data_source.useDataSource)(); const { combinedQuery, requestParams, selectedChangePoints } = (0, _change_point_detection_context.useChangePointDetectionContext)(); const splitFieldCardinality = (0, _use_split_field_cardinality.useSplitFieldCardinality)(fieldConfig.splitField, combinedQuery); const [isExpanded, setIsExpanded] = (0, _react.useState)(true); const [isActionMenuOpen, setIsActionMenuOpen] = (0, _react.useState)(false); const [isDashboardFormValid, setIsDashboardFormValid] = (0, _react.useState)(true); const canEditDashboards = (_capabilities$dashboa = (_capabilities$dashboa2 = capabilities.dashboard) === null || _capabilities$dashboa2 === void 0 ? void 0 : _capabilities$dashboa2.createNew) !== null && _capabilities$dashboa !== void 0 ? _capabilities$dashboa : false; const { create: canCreateCase, update: canUpdateCase } = (_cases$helpers$canUse = cases === null || cases === void 0 ? void 0 : (_cases$helpers = cases.helpers) === null || _cases$helpers === void 0 ? void 0 : _cases$helpers.canUseCases()) !== null && _cases$helpers$canUse !== void 0 ? _cases$helpers$canUse : { create: false, update: false }; const [dashboardAttachment, setDashboardAttachment] = (0, _react.useState)({ applyTimeRange: false, maxSeriesToPlot: 6 }); const [dashboardAttachmentReady, setDashboardAttachmentReady] = (0, _react.useState)(false); const { results: annotations, isLoading: annotationsLoading, progress } = (0, _use_change_point_agg_request.useChangePointResults)(fieldConfig, requestParams, combinedQuery, splitFieldCardinality); const openCasesModalCallback = (0, _use_cases_modal.useCasesModal)(_constants.EMBEDDABLE_CHANGE_POINT_CHART_TYPE); const selectedPartitions = (0, _react.useMemo)(() => { var _selectedChangePoints; return ((_selectedChangePoints = selectedChangePoints[panelIndex]) !== null && _selectedChangePoints !== void 0 ? _selectedChangePoints : []).map(v => { var _v$group; return (_v$group = v.group) === null || _v$group === void 0 ? void 0 : _v$group.value; }); }, [selectedChangePoints, panelIndex]); const caseAttachmentButtonDisabled = (0, _mlIsDefined.isDefined)(fieldConfig.splitField) && selectedPartitions.length === 0; const timeRange = (0, _mlDatePicker.useTimeRangeUpdates)(); const panels = (0, _react.useMemo)(() => { return [{ id: 'panelActions', size: 's', items: [...(canEditDashboards || canUpdateCase || canCreateCase ? [{ name: selectedPartitions.length > 0 ? _i18n.i18n.translate('xpack.aiops.changePointDetection.attachSelectedChartsLabel', { defaultMessage: 'Attach selected charts' }) : _i18n.i18n.translate('xpack.aiops.changePointDetection.attachChartsLabel', { defaultMessage: 'Attach charts' }), icon: 'plusInCircle', panel: 'attachMainPanel' }] : []), { name: _i18n.i18n.translate('xpack.aiops.changePointDetection.removeConfigLabel', { defaultMessage: 'Remove configuration' }), icon: 'trash', onClick: onRemove, disabled: removeDisabled }] }, { id: 'attachMainPanel', size: 's', initialFocusedItemIndex: 0, title: selectedPartitions.length > 0 ? _i18n.i18n.translate('xpack.aiops.changePointDetection.attachSelectedChartsLabel', { defaultMessage: 'Attach selected charts' }) : _i18n.i18n.translate('xpack.aiops.changePointDetection.attachChartsLabel', { defaultMessage: 'Attach charts' }), items: [...(canEditDashboards ? [{ name: _i18n.i18n.translate('xpack.aiops.changePointDetection.attachToDashboardLabel', { defaultMessage: 'To dashboard' }), panel: 'attachToDashboardPanel' }] : []), ...(canUpdateCase || canCreateCase ? [{ name: _i18n.i18n.translate('xpack.aiops.changePointDetection.attachToCaseLabel', { defaultMessage: 'To case' }), disabled: caseAttachmentButtonDisabled, ...(caseAttachmentButtonDisabled ? { toolTipPosition: 'left', toolTipContent: _i18n.i18n.translate('xpack.aiops.changePointDetection.attachToCaseTooltipContent', { defaultMessage: 'Select change points to attach' }) } : {}), onClick: () => { openCasesModalCallback({ timeRange, fn: fieldConfig.fn, metricField: fieldConfig.metricField, dataViewId: dataView.id, ...(fieldConfig.splitField ? { splitField: fieldConfig.splitField, partitions: selectedPartitions } : {}) }); } }] : [])] }, { id: 'attachToDashboardPanel', title: _i18n.i18n.translate('xpack.aiops.changePointDetection.attachToDashboardTitle', { defaultMessage: 'Attach to dashboard' }), size: 's', content: /*#__PURE__*/_react.default.createElement(_eui.EuiPanel, { paddingSize: 's' }, /*#__PURE__*/_react.default.createElement(_eui.EuiSpacer, { size: 's' }), /*#__PURE__*/_react.default.createElement(_eui.EuiForm, null, /*#__PURE__*/_react.default.createElement(_eui.EuiFormRow, { fullWidth: true }, /*#__PURE__*/_react.default.createElement(_eui.EuiSwitch, { label: _i18n.i18n.translate('xpack.aiops.changePointDetection.applyTimeRangeLabel', { defaultMessage: 'Apply time range' }), checked: dashboardAttachment.applyTimeRange, onChange: e => setDashboardAttachment(prevState => { return { ...prevState, applyTimeRange: e.target.checked }; }), compressed: true })), (0, _mlIsDefined.isDefined)(fieldConfig.splitField) && selectedPartitions.length === 0 ? /*#__PURE__*/_react.default.createElement(_max_series_control.MaxSeriesControl, { value: dashboardAttachment.maxSeriesToPlot, onChange: v => { setDashboardAttachment(prevState => { return { ...prevState, maxSeriesToPlot: v }; }); }, onValidationChange: result => { setIsDashboardFormValid(result === null); } }) : null, /*#__PURE__*/_react.default.createElement(_eui.EuiSpacer, { size: 'm' }), /*#__PURE__*/_react.default.createElement(_eui.EuiButton, { fill: true, type: 'submit', fullWidth: true, onClick: setDashboardAttachmentReady.bind(null, true), disabled: !isDashboardFormValid }, /*#__PURE__*/_react.default.createElement(_i18nReact.FormattedMessage, { id: "xpack.aiops.changePointDetection.submitDashboardAttachButtonLabel", defaultMessage: "Attach" })))) }]; }, [canCreateCase, canEditDashboards, canUpdateCase, caseAttachmentButtonDisabled, dashboardAttachment.applyTimeRange, dashboardAttachment.maxSeriesToPlot, dataView.id, fieldConfig.fn, fieldConfig.metricField, fieldConfig.splitField, isDashboardFormValid, onRemove, openCasesModalCallback, removeDisabled, selectedPartitions, timeRange]); const onSaveCallback = (0, _react.useCallback)(({ dashboardId, newTitle, newDescription }) => { var _selectedChangePoints2; const stateTransfer = embeddable.getStateTransfer(); const embeddableInput = { title: newTitle, description: newDescription, dataViewId: dataView.id, metricField: fieldConfig.metricField, splitField: fieldConfig.splitField, fn: fieldConfig.fn, ...(dashboardAttachment.applyTimeRange ? { timeRange } : {}), maxSeriesToPlot: dashboardAttachment.maxSeriesToPlot, ...((_selectedChangePoints2 = selectedChangePoints[panelIndex]) !== null && _selectedChangePoints2 !== void 0 && _selectedChangePoints2.length ? { partitions: selectedPartitions } : {}) }; const state = { input: embeddableInput, type: _constants.EMBEDDABLE_CHANGE_POINT_CHART_TYPE }; const path = dashboardId === 'new' ? '#/create' : `#/view/${dashboardId}`; stateTransfer.navigateToWithEmbeddablePackage('dashboards', { state, path }); }, [embeddable, dataView.id, fieldConfig.metricField, fieldConfig.splitField, fieldConfig.fn, dashboardAttachment.applyTimeRange, dashboardAttachment.maxSeriesToPlot, timeRange, selectedChangePoints, panelIndex, selectedPartitions]); return /*#__PURE__*/_react.default.createElement(_eui.EuiPanel, { paddingSize: "s", hasBorder: true, hasShadow: false, "data-test-subj": dataTestSubj }, /*#__PURE__*/_react.default.createElement(_eui.EuiFlexGroup, { alignItems: 'center', justifyContent: 'spaceBetween', gutterSize: 's' }, /*#__PURE__*/_react.default.createElement(_eui.EuiFlexItem, { grow: false }, /*#__PURE__*/_react.default.createElement(_eui.EuiFlexGroup, { alignItems: 'center', gutterSize: 's' }, /*#__PURE__*/_react.default.createElement(_eui.EuiFlexItem, { grow: false }, /*#__PURE__*/_react.default.createElement(_eui.EuiButtonIcon, { iconType: isExpanded ? 'arrowDown' : 'arrowRight', onClick: setIsExpanded.bind(null, prevState => !prevState), "aria-label": _i18n.i18n.translate('xpack.aiops.changePointDetection.expandConfigLabel', { defaultMessage: 'Expand configuration' }) })), /*#__PURE__*/_react.default.createElement(_eui.EuiFlexItem, { grow: false }, /*#__PURE__*/_react.default.createElement(FieldsControls, { fieldConfig: fieldConfig, onChange: onChange }, /*#__PURE__*/_react.default.createElement(_eui.EuiFlexItem, { css: { visibility: progress === null ? 'hidden' : 'visible' }, grow: true }, /*#__PURE__*/_react.default.createElement(_eui.EuiProgress, { label: /*#__PURE__*/_react.default.createElement(_i18nReact.FormattedMessage, { id: "xpack.aiops.changePointDetection.progressBarLabel", defaultMessage: "Fetching change points" }), value: progress !== null && progress !== void 0 ? progress : 0, max: 100, valueText: true, size: "m" }), /*#__PURE__*/_react.default.createElement(_eui.EuiSpacer, { size: "s" })))))), /*#__PURE__*/_react.default.createElement(_eui.EuiFlexItem, { grow: false }, /*#__PURE__*/_react.default.createElement(_eui.EuiFlexGroup, { alignItems: 'center', justifyContent: 'spaceBetween', gutterSize: 's' }, /*#__PURE__*/_react.default.createElement(_eui.EuiFlexItem, { grow: false }, /*#__PURE__*/_react.default.createElement(_eui.EuiPopover, { id: `panelContextMenu_${panelIndex}`, button: /*#__PURE__*/_react.default.createElement(_eui.EuiButtonIcon, { "aria-label": _i18n.i18n.translate('xpack.aiops.changePointDetection.configActionsLabel', { defaultMessage: 'Context menu' }), iconType: "boxesHorizontal", color: "text", onClick: setIsActionMenuOpen.bind(null, true) }), isOpen: isActionMenuOpen, closePopover: setIsActionMenuOpen.bind(null, false), panelPaddingSize: "none", anchorPosition: "downLeft" }, /*#__PURE__*/_react.default.createElement(_eui.EuiContextMenu, { panels: panels, initialPanelId: 'panelActions' })))))), isExpanded ? /*#__PURE__*/_react.default.createElement(ChangePointResults, { fieldConfig: fieldConfig, isLoading: annotationsLoading, annotations: annotations, splitFieldCardinality: splitFieldCardinality, onSelectionChange: onSelectionChange }) : null, dashboardAttachmentReady ? /*#__PURE__*/_react.default.createElement(SavedObjectSaveModalDashboard, { canSaveByReference: false, objectType: _i18n.i18n.translate('xpack.aiops.changePointDetection.objectTypeLabel', { defaultMessage: 'Change point chart' }), documentInfo: { title: _i18n.i18n.translate('xpack.aiops.changePointDetection.attachmentTitle', { defaultMessage: 'Change point: {function}({metric}){splitBy}', values: { function: fieldConfig.fn, metric: fieldConfig.metricField, splitBy: fieldConfig.splitField ? _i18n.i18n.translate('xpack.aiops.changePointDetection.splitByTitle', { defaultMessage: ' split by "{splitField}"', values: { splitField: fieldConfig.splitField } }) : '' } }) }, onClose: () => { setDashboardAttachmentReady(false); }, onSave: onSaveCallback }) : null); }; /** * Renders controls for fields selection and emits updates on change. */ const FieldsControls = ({ fieldConfig, onChange, children }) => { const { splitFieldsOptions, combinedQuery } = (0, _change_point_detection_context.useChangePointDetectionContext)(); const { dataView } = (0, _use_data_source.useDataSource)(); const { data, uiSettings, fieldFormats, charts, fieldStats } = (0, _use_aiops_app_context.useAiopsAppContext)(); const timefilter = (0, _mlDatePicker.useTimefilter)(); // required in order to trigger state updates (0, _mlDatePicker.useTimeRangeUpdates)(); const timefilterActiveBounds = timefilter.getActiveBounds(); const fieldStatsServices = (0, _react.useMemo)(() => { return { uiSettings, dataViews: data.dataViews, data, fieldFormats, charts }; }, [uiSettings, data, fieldFormats, charts]); const FieldStatsFlyoutProvider = fieldStats.FieldStatsFlyoutProvider; const onChangeFn = (0, _react.useCallback)((field, value) => { const result = { ...fieldConfig, [field]: value }; onChange(result); }, [onChange, fieldConfig]); return /*#__PURE__*/_react.default.createElement(FieldStatsFlyoutProvider, { fieldStatsServices: fieldStatsServices, dataView: dataView, dslQuery: combinedQuery, timeRangeMs: timefilterActiveBounds ? { from: timefilterActiveBounds.min.valueOf(), to: timefilterActiveBounds.max.valueOf() } : undefined }, /*#__PURE__*/_react.default.createElement(_eui.EuiFlexGroup, { alignItems: 'center', responsive: true, wrap: true, gutterSize: 'm' }, /*#__PURE__*/_react.default.createElement(_eui.EuiFlexItem, { grow: false, css: { width: '200px' } }, /*#__PURE__*/_react.default.createElement(_function_picker.FunctionPicker, { value: fieldConfig.fn, onChange: v => onChangeFn('fn', v) })), /*#__PURE__*/_react.default.createElement(_eui.EuiFlexItem, { grow: false, css: selectControlCss }, /*#__PURE__*/_react.default.createElement(_metric_field_selector.MetricFieldSelector, { value: fieldConfig.metricField, onChange: v => onChangeFn('metricField', v) })), splitFieldsOptions.length > 0 ? /*#__PURE__*/_react.default.createElement(_eui.EuiFlexItem, { grow: false, css: selectControlCss }, /*#__PURE__*/_react.default.createElement(_split_field_selector.SplitFieldSelector, { value: fieldConfig.splitField, onChange: v => onChangeFn('splitField', v) })) : null, children)); }; exports.FieldsControls = FieldsControls; /** * Handles request and rendering results of the change point with provided config. */ const ChangePointResults = ({ fieldConfig, splitFieldCardinality, isLoading, annotations, onSelectionChange }) => { const cardinalityExceeded = splitFieldCardinality && splitFieldCardinality > _constants2.SPLIT_FIELD_CARDINALITY_LIMIT; return /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, /*#__PURE__*/_react.default.createElement(_eui.EuiSpacer, { size: "s" }), cardinalityExceeded ? /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, /*#__PURE__*/_react.default.createElement(_eui.EuiCallOut, { title: _i18n.i18n.translate('xpack.aiops.changePointDetection.cardinalityWarningTitle', { defaultMessage: 'Analysis has been limited' }), color: "warning", iconType: "warning" }, /*#__PURE__*/_react.default.createElement("p", null, _i18n.i18n.translate('xpack.aiops.changePointDetection.cardinalityWarningMessage', { defaultMessage: 'The "{splitField}" field cardinality is {cardinality} which exceeds the limit of {cardinalityLimit}. Only the first {cardinalityLimit} partitions, sorted by document count, are analyzed.', values: { cardinality: splitFieldCardinality, cardinalityLimit: _constants2.SPLIT_FIELD_CARDINALITY_LIMIT, splitField: fieldConfig.splitField } }))), /*#__PURE__*/_react.default.createElement(_eui.EuiSpacer, { size: "m" })) : null, /*#__PURE__*/_react.default.createElement(_change_points_table.ChangePointsTable, { annotations: annotations, fieldConfig: fieldConfig, isLoading: isLoading, onSelectionChange: onSelectionChange })); }; exports.ChangePointResults = ChangePointResults;