/* * Copyright OpenSearch Contributors * SPDX-License-Identifier: Apache-2.0 */ import { EuiButton, EuiContextMenuItem, EuiContextMenuPanel, EuiFlexGroup, EuiFlexItem, EuiHorizontalRule, EuiLink, EuiOverlayMask, EuiPage, EuiPageBody, EuiPageContent, EuiPageContentHeader, EuiPageContentHeaderSection, EuiPageHeader, EuiPageHeaderSection, EuiPopover, EuiSpacer, EuiText, EuiTitle, htmlIdGenerator, } from '@elastic/eui'; import React, { ReactElement, useEffect, useRef, useState } from 'react'; import { batch, connect, useDispatch } from 'react-redux'; import { useHistory } from 'react-router-dom'; import { HttpStart } from '../../../../../../src/core/public'; import { CUSTOM_PANELS_API_PREFIX } from '../../../../common/constants/custom_panels'; import { EVENT_ANALYTICS_DOCUMENTATION_URL, NEW_TAB, RAW_QUERY, REDIRECT_TAB, SELECTED_DATE_RANGE, TAB_CREATED_TYPE, TAB_ID_TXT_PFX, } from '../../../../common/constants/explorer'; import { EVENT_ANALYTICS, OBSERVABILITY_BASE, SAVED_OBJECTS, } from '../../../../common/constants/shared'; import { EmptyTabParams, ExplorerData as IExplorerData, IQuery, } from '../../../../common/types/explorer'; import SavedObjects from '../../../services/saved_objects/event_analytics/saved_objects'; import { SavedObjectsActions } from '../../../services/saved_objects/saved_object_client/saved_objects_actions'; import { ObservabilitySavedObject } from '../../../services/saved_objects/saved_object_client/types'; import { getSampleDataModal } from '../../common/helpers/add_sample_modal'; import { DeleteModal } from '../../common/helpers/delete_modal'; import { onItemSelect, parseGetSuggestions } from '../../common/search/autocomplete_logic'; import { Search } from '../../common/search/search'; import { init as initFields } from '../redux/slices/field_slice'; import { init as initPatterns } from '../redux/slices/patterns_slice'; import { init as initQueryResult, selectQueryResult } from '../redux/slices/query_result_slice'; import { changeQuery, init as initQuery, selectQueries } from '../redux/slices/query_slice'; import { addTab, selectQueryTabs, setSelectedQueryTab } from '../redux/slices/query_tab_slice'; import { SavedQueryTable } from './saved_objects_table'; interface IHomeProps { pplService: any; dslService: any; savedObjects: SavedObjects; setToast: ( title: string, color?: string, text?: React.ReactChild | undefined, side?: string | undefined ) => void; getExistingEmptyTab: (params: EmptyTabParams) => string; http: HttpStart; queries: IQuery; explorerData: IExplorerData; tabsState: any; } const EventAnalyticsHome = (props: IHomeProps) => { const { pplService, dslService, savedObjects, setToast, getExistingEmptyTab, http, queries, explorerData, tabsState, } = props; const history = useHistory(); const dispatch = useDispatch(); const [searchQuery, setSearchQuery] = useState(''); const [selectedDateRange, setSelectedDateRange] = useState(['now-15m', 'now']); const [savedHistories, setSavedHistories] = useState([]); const [selectedHistories, setSelectedHistories] = useState([]); const [isActionsPopoverOpen, setIsActionsPopoverOpen] = useState(false); const [isTableLoading, setIsTableLoading] = useState(false); const [modalLayout, setModalLayout] = useState(); const [isModalVisible, setIsModalVisible] = useState(false); const selectedDateRangeRef = useRef(); selectedDateRangeRef.current = selectedDateRange; const closeModal = () => { setIsModalVisible(false); }; const showModal = () => { setIsModalVisible(true); }; const fetchHistories = async () => { const observabilityObjects = await SavedObjectsActions.getBulk({ objectType: ['savedQuery', 'savedVisualization'], sortOrder: 'desc', fromIndex: 0, }); const nonAppObjects = observabilityObjects.observabilityObjectList.filter( (object: ObservabilitySavedObject) => (object.savedVisualization && !object.savedVisualization.application_id) || object.savedQuery ); setSavedHistories(nonAppObjects); }; const deleteHistoryList = async () => { const objectIdsToDelete = selectedHistories.map((hstry) => hstry.data.objectId); await SavedObjectsActions.deleteBulk({ objectIdList: objectIdsToDelete }) .then(async () => { setSavedHistories((staleHistories) => { return staleHistories.filter((his) => { return !objectIdsToDelete.includes(his.objectId); }); }); setToast(`Histories has been successfully deleted.`, 'success'); }) .catch((error) => { setToast(`Cannot delete Histories, error: ${error.message}`, 'danger'); }) .finally(() => { closeModal(); }); }; const addNewTab = async () => { // get a new tabId const tabId = htmlIdGenerator(TAB_ID_TXT_PFX)(); // create a new tab await batch(() => { dispatch(initQuery({ tabId })); dispatch(initQueryResult({ tabId })); dispatch(initFields({ tabId })); dispatch(addTab({ tabId })); dispatch(initPatterns({ tabId })); }); return tabId; }; useEffect(() => { fetchHistories(); }, []); const dispatchInitialData = (tabId: string) => { batch(() => { dispatch( changeQuery({ tabId, query: { [RAW_QUERY]: searchQuery, [SELECTED_DATE_RANGE]: selectedDateRangeRef.current, [TAB_CREATED_TYPE]: NEW_TAB, }, }) ); dispatch(setSelectedQueryTab({ tabId })); }); }; const handleQuerySearch = async () => { const emptyTabId = getExistingEmptyTab({ tabIds: tabsState.queryTabIds, queries, explorerData, }); const newTabId = emptyTabId ? emptyTabId : await addNewTab(); // update this new tab with data await dispatchInitialData(newTabId); // redirect to explorer history.push('/explorer'); }; const handleQueryChange = async (query: string) => setSearchQuery(query); const handleTimePickerChange = async (timeRange: string[]) => setSelectedDateRange(timeRange); const handleHistoryClick = async (objectId: string) => { const emptyTabId = getExistingEmptyTab({ tabIds: tabsState.queryTabIds, queries, explorerData, }); const newTabId = emptyTabId ? emptyTabId : await addNewTab(); batch(() => { dispatch( changeQuery({ tabId: newTabId, query: { [TAB_CREATED_TYPE]: REDIRECT_TAB, }, }) ); dispatch(setSelectedQueryTab({ tabId: newTabId })); }); // redirect to explorer history.push(`/explorer/${objectId}`); }; const addSampledata = async () => { setModalLayout( getSampleDataModal(closeModal, async () => { closeModal(); await addSampleEvents(); }) ); showModal(); }; const addSampleEvents = async () => { try { setIsTableLoading(true); const flights = await http .get('../api/saved_objects/_find', { query: { type: 'index-pattern', search_fields: 'title', search: 'opensearch_dashboards_sample_data_flights', }, }) .then((resp: any) => resp.total === 0); const logs = await http .get('../api/saved_objects/_find', { query: { type: 'index-pattern', search_fields: 'title', search: 'opensearch_dashboards_sample_data_logs', }, }) .then((resp: any) => resp.total === 0); if (flights || logs) setToast('Adding sample data. This can take some time.'); await Promise.all([ flights ? http.post('../api/sample_data/flights') : Promise.resolve(), logs ? http.post('../api/sample_data/logs') : Promise.resolve(), ]); await http .get( `${OBSERVABILITY_BASE}${EVENT_ANALYTICS}${SAVED_OBJECTS}/addSampleSavedObjects/event_analytics` ) .then(async (resp: any) => { await http.post(`${CUSTOM_PANELS_API_PREFIX}/panels/addSamplePanels`, { body: JSON.stringify({ savedVisualizationIds: [...resp?.savedVizIds], }), }); // wait for sample data to flush to index await new Promise((resolve) => setTimeout(resolve, 1000)); const res = await SavedObjectsActions.getBulk({ objectType: ['savedQuery', 'savedVisualization'], sortOrder: 'desc', fromIndex: 0, }); setSavedHistories((staleHistoryList) => { return [...res.observabilityObjectList, ...staleHistoryList]; }); }); setToast(`Sample events added successfully.`); } catch (error: any) { setToast(`Cannot add sample events data, error: ${error}`, 'danger'); } finally { setIsTableLoading(false); } }; const popoverButton = ( setIsActionsPopoverOpen(!isActionsPopoverOpen)} data-test-subj="eventHomeAction" > Actions ); const deleteHistory = () => { const customPanelString = `${selectedHistories.length > 1 ? 'histories' : 'history'}`; setModalLayout( ); showModal(); }; const popoverItems: ReactElement[] = [ { setIsActionsPopoverOpen(false); deleteHistory(); }} data-test-subj="eventHomeAction__delete" > Delete , { setIsActionsPopoverOpen(false); history.push(`/explorer`); }} data-test-subj="eventHomeAction__explorer" > Event Explorer , { setIsActionsPopoverOpen(false); addSampledata(); }} data-test-subj="eventHomeAction__addSamples" > Add samples , ]; return ( <>

Logs

{}} setEndTime={() => {}} showSaveButton={false} runButtonText="New Query" getSuggestions={parseGetSuggestions} onItemSelect={onItemSelect} />

Queries and Visualizations ({savedHistories.length})

Use Events Analytics to monitor, correlate, analyze and visualize machine generated data through Piped Processing Language. Save frequently searched queries and visualizations for quick access{' '} Learn more
setIsActionsPopoverOpen(false)} >
{savedHistories.length > 0 ? ( ) : ( <>

No Queries or Visualizations

Use events analytics to create and save frequently searched
queries and visualizations, using PPL.
history.push(`/explorer`)} data-test-subj="actionEventExplorer" > Event Explorer addSampledata()} data-test-subj="actionAddSamples" > Add samples )}
{isModalVisible && modalLayout} ); }; const mapStateToProps = (state) => { return { queries: selectQueries(state), explorerData: selectQueryResult(state), tabsState: selectQueryTabs(state), }; }; export const Home = connect(mapStateToProps)(EventAnalyticsHome);