"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); Object.defineProperty(exports, "__esModule", { value: true }); exports.AnomalyTimeline = void 0; var _react = _interopRequireWildcard(require("react")); var _lodash = require("lodash"); var _eui = require("@elastic/eui"); var _i18n = require("@kbn/i18n"); var _i18nReact = require("@kbn/i18n-react"); var _useDebounce = _interopRequireDefault(require("react-use/lib/useDebounce")); var _useObservable = _interopRequireDefault(require("react-use/lib/useObservable")); var _mlDateUtils = require("@kbn/ml-date-utils"); var _mlIsDefined = require("@kbn/ml-is-defined"); var _mlDatePicker = require("@kbn/ml-date-picker"); var _mlQueryUtils = require("@kbn/ml-query-utils"); var _use_cases_modal = require("../contexts/kibana/use_cases_modal"); var _ = require("../.."); var _explorer_constants = require("./explorer_constants"); var _add_swimlane_to_dashboard_controls = require("./dashboard_controls/add_swimlane_to_dashboard_controls"); var _kibana = require("../contexts/kibana"); var _explorer_no_influencers_found = require("./components/explorer_no_influencers_found"); var _swimlane_container = require("./swimlane_container"); var _no_overall_data = require("./components/no_overall_data"); var _severity_control = require("../components/severity_control"); var _anomaly_timeline_help_popover = require("./anomaly_timeline_help_popover"); var _chart_tooltip = require("../components/chart_tooltip"); var _swimlane_annotation_container = require("./swimlane_annotation_container"); var _anomaly_timeline_service = require("../services/anomaly_timeline_service"); var _anomaly_explorer_context = require("./anomaly_explorer_context"); var _use_time_buckets = require("../components/custom_hooks/use_time_buckets"); var _use_selected_cells = require("./hooks/use_selected_cells"); 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. */ function mapSwimlaneOptionsToEuiOptions(options) { return options.map(option => ({ value: option, text: option })); } const AnomalyTimeline = /*#__PURE__*/_react.default.memo(({ explorerState }) => { var _capabilities$dashboa, _capabilities$dashboa2, _selectedJobs$length; const { services: { application: { capabilities }, charts: chartsService, cases } } = (0, _kibana.useMlKibana)(); const globalTimeRange = (0, _mlDatePicker.useTimeRangeUpdates)(true); const selectCaseModal = cases === null || cases === void 0 ? void 0 : cases.hooks.useCasesAddToExistingCaseModal(); const { anomalyExplorerCommonStateService, anomalyTimelineStateService } = (0, _anomaly_explorer_context.useAnomalyExplorerContext)(); const setSelectedCells = anomalyTimelineStateService.setSelectedCells.bind(anomalyTimelineStateService); const [isMenuOpen, setIsMenuOpen] = (0, _react.useState)(false); const [isAddDashboardsActive, setIsAddDashboardActive] = (0, _react.useState)(false); 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 timeBuckets = (0, _use_time_buckets.useTimeBuckets)(); const { overallAnnotations } = explorerState; const { filterActive, queryString } = (0, _useObservable.default)(anomalyExplorerCommonStateService.getFilterSettings$(), anomalyExplorerCommonStateService.getFilterSettings()); const swimlaneLimit = (0, _useObservable.default)(anomalyTimelineStateService.getSwimLaneCardinality$()); const selectedJobs = (0, _useObservable.default)(anomalyExplorerCommonStateService.getSelectedJobs$(), anomalyExplorerCommonStateService.getSelectedJobs()); const loading = (0, _useObservable.default)(anomalyTimelineStateService.isOverallSwimLaneLoading$(), true); const swimlaneContainerWidth = (0, _useObservable.default)(anomalyTimelineStateService.getContainerWidth$(), anomalyTimelineStateService.getContainerWidth()); const viewBySwimlaneDataLoading = (0, _useObservable.default)(anomalyTimelineStateService.isViewBySwimLaneLoading$(), true); const overallSwimlaneData = (0, _useObservable.default)(anomalyTimelineStateService.getOverallSwimLaneData$()); const viewBySwimlaneData = (0, _useObservable.default)(anomalyTimelineStateService.getViewBySwimLaneData$()); const selectedCells = (0, _useObservable.default)(anomalyTimelineStateService.getSelectedCells$(), anomalyTimelineStateService.getSelectedCells()); const swimLaneSeverity = (0, _useObservable.default)(anomalyTimelineStateService.getSwimLaneSeverity$()); const viewBySwimlaneFieldName = (0, _useObservable.default)(anomalyTimelineStateService.getViewBySwimlaneFieldName$()); const viewBySwimlaneOptions = (0, _useObservable.default)(anomalyTimelineStateService.getViewBySwimLaneOptions$(), anomalyTimelineStateService.getViewBySwimLaneOptions()); const { viewByPerPage, viewByFromPage } = (0, _useObservable.default)(anomalyTimelineStateService.getSwimLanePagination$(), anomalyTimelineStateService.getSwimLanePagination()); const [severityUpdate, setSeverityUpdate] = (0, _react.useState)(anomalyTimelineStateService.getSwimLaneSeverity()); const timeRange = (0, _use_selected_cells.getTimeBoundsFromSelection)(selectedCells); const viewByLoadedForTimeFormatted = timeRange ? `${(0, _mlDateUtils.formatHumanReadableDateTime)(timeRange.earliestMs)} - ${(0, _mlDateUtils.formatHumanReadableDateTime)(timeRange.latestMs)}` : null; (0, _useDebounce.default)(() => { if (severityUpdate === swimLaneSeverity) return; anomalyTimelineStateService.setSeverity(severityUpdate); }, 500, [severityUpdate, swimLaneSeverity]); const openCasesModalCallback = (0, _use_cases_modal.useCasesModal)(_.ANOMALY_SWIMLANE_EMBEDDABLE_TYPE); const openCasesModal = (0, _react.useCallback)(swimLaneType => { openCasesModalCallback({ swimlaneType: swimLaneType, ...(swimLaneType === _explorer_constants.SWIMLANE_TYPE.VIEW_BY ? { viewBy: viewBySwimlaneFieldName } : {}), jobIds: selectedJobs === null || selectedJobs === void 0 ? void 0 : selectedJobs.map(v => v.id), timeRange: globalTimeRange, ...((0, _mlIsDefined.isDefined)(queryString) && queryString !== '' ? { query: { query: queryString, language: _mlQueryUtils.SEARCH_QUERY_LANGUAGE.KUERY } } : {}) }); }, [openCasesModalCallback, selectedJobs, globalTimeRange, viewBySwimlaneFieldName, queryString]); const annotations = (0, _react.useMemo)(() => overallAnnotations.annotationsData, [overallAnnotations]); const closePopoverOnAction = (0, _react.useCallback)(actionCallback => { return () => { setIsMenuOpen(false); actionCallback(); }; }, [setIsMenuOpen]); const menuPanels = (0, _react.useMemo)(() => { const rootItems = []; const panels = [{ id: 0, items: rootItems }]; if (canEditDashboards) { rootItems.push({ name: /*#__PURE__*/_react.default.createElement(_i18nReact.FormattedMessage, { id: "xpack.ml.explorer.addToDashboardLabel", defaultMessage: "Add to dashboard" }), onClick: closePopoverOnAction(setIsAddDashboardActive.bind(null, true)), 'data-test-subj': 'mlAnomalyTimelinePanelAddToDashboardButton' }); } const casesPrivileges = cases === null || cases === void 0 ? void 0 : cases.helpers.canUseCases(); if ((!!(casesPrivileges !== null && casesPrivileges !== void 0 && casesPrivileges.create) || !!(casesPrivileges !== null && casesPrivileges !== void 0 && casesPrivileges.update)) && selectCaseModal) { rootItems.push({ panel: 1, name: /*#__PURE__*/_react.default.createElement(_i18nReact.FormattedMessage, { id: "xpack.ml.explorer.attachToCaseLabel", defaultMessage: "Add to case" }), 'data-test-subj': 'mlAnomalyTimelinePanelAttachToCaseButton' }); panels.push({ id: 1, initialFocusedItemIndex: 0, title: /*#__PURE__*/_react.default.createElement(_i18nReact.FormattedMessage, { id: "xpack.ml.explorer.attachToCaseLabel", defaultMessage: "Add to case" }), items: [{ name: /*#__PURE__*/_react.default.createElement(_i18nReact.FormattedMessage, { id: "xpack.ml.explorer.attachOverallSwimLane", defaultMessage: "Overall" }), onClick: closePopoverOnAction(openCasesModal.bind(null, 'overall')), 'data-test-subj': 'mlAnomalyTimelinePanelAttachOverallButton' }, { name: /*#__PURE__*/_react.default.createElement(_i18nReact.FormattedMessage, { id: "xpack.ml.explorer.attachViewBySwimLane", defaultMessage: "View by {viewByField}", values: { viewByField: viewBySwimlaneFieldName } }), onClick: closePopoverOnAction(openCasesModal.bind(null, 'viewBy')), 'data-test-subj': 'mlAnomalyTimelinePanelAttachViewByButton' }] }); } return panels; // eslint-disable-next-line react-hooks/exhaustive-deps }, [canEditDashboards, openCasesModal, viewBySwimlaneFieldName]); // If selecting a cell in the 'view by' swimlane, indicate the corresponding time in the Overall swimlane. const overallCellSelection = (0, _react.useMemo)(() => { if (!selectedCells) return; if (selectedCells.type === _explorer_constants.SWIMLANE_TYPE.OVERALL) return selectedCells; return { type: _explorer_constants.SWIMLANE_TYPE.OVERALL, lanes: [_explorer_constants.OVERALL_LABEL], times: selectedCells.times }; }, [selectedCells]); const annotationXDomain = (0, _react.useMemo)(() => _anomaly_timeline_service.AnomalyTimelineService.isOverallSwimlaneData(overallSwimlaneData) ? { min: overallSwimlaneData.earliest * 1000, max: overallSwimlaneData.latest * 1000, minInterval: overallSwimlaneData.interval * 1000 } : undefined, [overallSwimlaneData]); const onResize = (0, _react.useCallback)(value => { anomalyTimelineStateService.setContainerWidth(value); // eslint-disable-next-line react-hooks/exhaustive-deps }, []); return /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, /*#__PURE__*/_react.default.createElement(_eui.EuiPanel, { paddingSize: "m", hasShadow: false, hasBorder: true }, /*#__PURE__*/_react.default.createElement(_eui.EuiFlexGroup, { direction: "row", gutterSize: "xs", responsive: false, alignItems: "baseline" }, /*#__PURE__*/_react.default.createElement(_eui.EuiFlexItem, { grow: false }, /*#__PURE__*/_react.default.createElement(_eui.EuiTitle, { size: 'xs' }, /*#__PURE__*/_react.default.createElement("h2", null, /*#__PURE__*/_react.default.createElement(_i18nReact.FormattedMessage, { id: "xpack.ml.explorer.anomalyTimelineTitle", defaultMessage: "Anomaly timeline" })))), /*#__PURE__*/_react.default.createElement(_eui.EuiFlexItem, { grow: false }, /*#__PURE__*/_react.default.createElement(_anomaly_timeline_help_popover.AnomalyTimelineHelpPopover, null)), menuPanels[0].items.length > 0 ? /*#__PURE__*/_react.default.createElement(_eui.EuiFlexItem, { grow: false, css: { 'margin-left': 'auto !important', 'align-self': 'baseline' } }, /*#__PURE__*/_react.default.createElement(_eui.EuiPopover, { button: /*#__PURE__*/_react.default.createElement(_eui.EuiButtonIcon, { size: "s", "aria-label": _i18n.i18n.translate('xpack.ml.explorer.swimlaneActions', { defaultMessage: 'Actions' }), color: "text", iconType: "boxesHorizontal", onClick: setIsMenuOpen.bind(null, !isMenuOpen), "data-test-subj": "mlAnomalyTimelinePanelMenu" }), isOpen: isMenuOpen, closePopover: setIsMenuOpen.bind(null, false), panelPaddingSize: "none", anchorPosition: "downLeft" }, /*#__PURE__*/_react.default.createElement(_eui.EuiContextMenu, { initialPanelId: 0, panels: menuPanels }))) : null), /*#__PURE__*/_react.default.createElement(_eui.EuiSpacer, { size: "s" }), /*#__PURE__*/_react.default.createElement(_eui.EuiFlexGroup, { direction: "row", gutterSize: "m", responsive: false, alignItems: "baseline" }, viewBySwimlaneOptions.length > 0 && /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, /*#__PURE__*/_react.default.createElement(_eui.EuiFlexItem, { grow: false }, /*#__PURE__*/_react.default.createElement(_eui.EuiSelect, { prepend: _i18n.i18n.translate('xpack.ml.explorer.viewByLabel', { defaultMessage: 'View by' }), compressed: true, id: "selectViewBy", options: mapSwimlaneOptionsToEuiOptions(viewBySwimlaneOptions), value: viewBySwimlaneFieldName, onChange: e => { anomalyTimelineStateService.setViewBySwimLaneFieldName(e.target.value); } }))), /*#__PURE__*/_react.default.createElement(_eui.EuiFlexItem, { grow: true, css: { 'max-width': '500px' } }, /*#__PURE__*/_react.default.createElement(_severity_control.SeverityControl, { value: severityUpdate !== null && severityUpdate !== void 0 ? severityUpdate : 0, onChange: (0, _react.useCallback)(update => { setSeverityUpdate(update); }, []) }))), /*#__PURE__*/_react.default.createElement(_eui.EuiSpacer, { size: "m" }), /*#__PURE__*/_react.default.createElement(_eui.EuiFlexGroup, { direction: "row", gutterSize: "m", responsive: false, alignItems: "center" }, /*#__PURE__*/_react.default.createElement(_eui.EuiFlexItem, { grow: false }, /*#__PURE__*/_react.default.createElement(_eui.EuiText, { size: 'xs', color: 'subdued' }, viewByLoadedForTimeFormatted && /*#__PURE__*/_react.default.createElement(_i18nReact.FormattedMessage, { id: "xpack.ml.explorer.sortedByMaxAnomalyScoreForTimeFormattedLabel", defaultMessage: "(Sorted by max anomaly score for {viewByLoadedForTimeFormatted})", values: { viewByLoadedForTimeFormatted } }), (0, _mlIsDefined.isDefined)(viewByLoadedForTimeFormatted) ? null : /*#__PURE__*/_react.default.createElement(_i18nReact.FormattedMessage, { id: "xpack.ml.explorer.sortedByMaxAnomalyScoreLabel", defaultMessage: "(Sorted by max anomaly score)" }), filterActive === true && viewBySwimlaneFieldName === _explorer_constants.VIEW_BY_JOB_LABEL && /*#__PURE__*/_react.default.createElement(_i18nReact.FormattedMessage, { id: "xpack.ml.explorer.jobScoreAcrossAllInfluencersLabel", defaultMessage: "(Job score across all influencers)" }))), /*#__PURE__*/_react.default.createElement(_eui.EuiFlexItem, { grow: false, css: { visibility: selectedCells ? 'visible' : 'hidden' } }, /*#__PURE__*/_react.default.createElement(_eui.EuiButtonEmpty, { size: "xs", onClick: setSelectedCells.bind(anomalyTimelineStateService, undefined), "data-test-subj": "mlAnomalyTimelineClearSelection" }, /*#__PURE__*/_react.default.createElement(_i18nReact.FormattedMessage, { id: "xpack.ml.explorer.clearSelectionLabel", defaultMessage: "Clear selection" })))), /*#__PURE__*/_react.default.createElement(_eui.EuiSpacer, { size: "m" }), annotationXDomain && Array.isArray(annotations) && annotations.length > 0 ? /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, /*#__PURE__*/_react.default.createElement(_chart_tooltip.MlTooltipComponent, null, tooltipService => /*#__PURE__*/_react.default.createElement(_swimlane_annotation_container.SwimlaneAnnotationContainer, { chartWidth: swimlaneContainerWidth, domain: annotationXDomain, annotationsData: annotations, tooltipService: tooltipService })), /*#__PURE__*/_react.default.createElement(_eui.EuiSpacer, { size: "m" })) : null, /*#__PURE__*/_react.default.createElement(_swimlane_container.SwimlaneContainer, { id: "overall", "data-test-subj": "mlAnomalyExplorerSwimlaneOverall", filterActive: filterActive, timeBuckets: timeBuckets, swimlaneData: overallSwimlaneData, swimlaneType: _explorer_constants.SWIMLANE_TYPE.OVERALL, selection: overallCellSelection, onCellsSelection: setSelectedCells, onResize: onResize, isLoading: loading, noDataWarning: /*#__PURE__*/_react.default.createElement(_eui.EuiText, { textAlign: 'center' }, /*#__PURE__*/_react.default.createElement("h5", null, /*#__PURE__*/_react.default.createElement(_no_overall_data.NoOverallData, null))), showTimeline: false, showLegend: false, yAxisWidth: _swimlane_annotation_container.Y_AXIS_LABEL_WIDTH, chartsService: chartsService }), /*#__PURE__*/_react.default.createElement(_eui.EuiSpacer, { size: "m" }), viewBySwimlaneOptions.length > 0 && /*#__PURE__*/_react.default.createElement(_swimlane_container.SwimlaneContainer, { id: "view_by", "data-test-subj": "mlAnomalyExplorerSwimlaneViewBy", filterActive: filterActive, timeBuckets: timeBuckets, showLegend: false, swimlaneData: viewBySwimlaneData, swimlaneType: _explorer_constants.SWIMLANE_TYPE.VIEW_BY, selection: selectedCells, onCellsSelection: setSelectedCells, onResize: onResize, fromPage: viewByFromPage, perPage: viewByPerPage, swimlaneLimit: swimlaneLimit, chartsService: chartsService, onPaginationChange: ({ perPage: perPageUpdate, fromPage: fromPageUpdate }) => { if (perPageUpdate) { anomalyTimelineStateService.setSwimLanePagination({ viewByPerPage: perPageUpdate }); } if (fromPageUpdate) { anomalyTimelineStateService.setSwimLanePagination({ viewByFromPage: fromPageUpdate }); } }, isLoading: loading || viewBySwimlaneDataLoading, yAxisWidth: _swimlane_annotation_container.Y_AXIS_LABEL_WIDTH, noDataWarning: /*#__PURE__*/_react.default.createElement(_eui.EuiText, { textAlign: 'center' }, /*#__PURE__*/_react.default.createElement("h5", null, typeof viewBySwimlaneFieldName === 'string' ? viewBySwimlaneFieldName === _explorer_constants.VIEW_BY_JOB_LABEL ? /*#__PURE__*/_react.default.createElement(_i18nReact.FormattedMessage, { id: "xpack.ml.explorer.noResultForSelectedJobsMessage", defaultMessage: "No results found for selected {jobsCount, plural, one {job} other {jobs}}", values: { jobsCount: (_selectedJobs$length = selectedJobs === null || selectedJobs === void 0 ? void 0 : selectedJobs.length) !== null && _selectedJobs$length !== void 0 ? _selectedJobs$length : 1 } }) : /*#__PURE__*/_react.default.createElement(_explorer_no_influencers_found.ExplorerNoInfluencersFound, { viewBySwimlaneFieldName: viewBySwimlaneFieldName, showFilterMessage: filterActive === true }) : null)) })), isAddDashboardsActive && selectedJobs && /*#__PURE__*/_react.default.createElement(_add_swimlane_to_dashboard_controls.AddSwimlaneToDashboardControl, { onClose: async callback => { setIsAddDashboardActive(false); if (callback) { await callback(); } }, jobIds: selectedJobs.map(({ id }) => id), viewBy: viewBySwimlaneFieldName, queryString: queryString })); }, (prevProps, nextProps) => { return (0, _lodash.isEqual)(prevProps.explorerState, nextProps.explorerState); }); exports.AnomalyTimeline = AnomalyTimeline;