From 7c74388503bd355172535573fd275fa2d2ee858d Mon Sep 17 00:00:00 2001 From: Shenoy Pratik Date: Fri, 12 Jan 2024 09:41:41 -0800 Subject: [PATCH] Revert "Merge feature/explorer-query-assistant to main" --- common/constants/data_sources.ts | 2 - common/constants/explorer.ts | 1 - common/constants/query_assist.ts | 12 - common/constants/shared.ts | 3 - opensearch_dashboards.json | 8 +- public/.DS_Store | Bin 0 -> 6148 bytes .../common/live_tail/live_tail_button.tsx | 12 +- .../components/common/search/date_picker.tsx | 58 +- .../components/common/search/query_area.tsx | 81 --- .../search/query_assist_summarization.tsx | 111 ---- public/components/common/search/search.tsx | 492 +++++----------- .../icons/query-assistant-logo.svg | 18 - .../__snapshots__/no_results.test.tsx.snap | 236 ++++---- .../__tests__/no_results.test.tsx | 16 +- .../datasources/datasources_selection.tsx | 28 +- .../explorer/events_views/data_grid.tsx | 122 ++-- .../event_analytics/explorer/explorer.tsx | 227 +++---- .../event_analytics/explorer/no_results.tsx | 129 ++-- .../query_assist/__tests__/hooks.test.ts | 127 ---- .../query_assist/__tests__/input.test.tsx | 107 ---- .../explorer/query_assist/hooks.ts | 89 --- .../explorer/query_assist/input.tsx | 367 ------------ .../__snapshots__/sidebar.test.tsx.snap | 555 +++++++++--------- .../sidebar/__tests__/sidebar.test.tsx | 18 +- .../explorer/sidebar/field.tsx | 9 +- .../explorer/sidebar/sidebar.tsx | 2 + .../components/event_analytics/home/home.tsx | 6 +- .../event_analytics/hooks/use_fetch_events.ts | 71 +-- .../hooks/use_fetch_patterns.ts | 8 +- .../hooks/use_fetch_visualizations.ts | 13 +- .../redux/reducers/fetch_reducers.ts | 4 - .../event_analytics/redux/reducers/index.ts | 2 +- .../query_assistant_summarization_slice.ts | 50 -- .../redux/slices/query_result_slice.ts | 12 +- .../redux/slices/query_slice.ts | 8 +- .../redux/slices/search_meta_data_slice.ts | 8 +- public/framework/core_refs.ts | 11 +- public/framework/redux/reducers/index.ts | 2 - public/index.ts | 14 +- public/plugin.ts | 57 +- .../data_fetchers/ppl/ppl_data_fetcher.ts | 7 +- .../__tests__/generate_field_context.test.ts | 95 --- .../query_assist/generate_field_context.ts | 71 --- server/index.ts | 23 +- server/plugin.ts | 21 +- server/routes/index.ts | 41 +- server/routes/query_assist/routes.ts | 180 ------ 47 files changed, 850 insertions(+), 2684 deletions(-) delete mode 100644 common/constants/query_assist.ts create mode 100644 public/.DS_Store delete mode 100644 public/components/common/search/query_area.tsx delete mode 100644 public/components/common/search/query_assist_summarization.tsx delete mode 100644 public/components/datasources/icons/query-assistant-logo.svg delete mode 100644 public/components/event_analytics/explorer/query_assist/__tests__/hooks.test.ts delete mode 100644 public/components/event_analytics/explorer/query_assist/__tests__/input.test.tsx delete mode 100644 public/components/event_analytics/explorer/query_assist/hooks.ts delete mode 100644 public/components/event_analytics/explorer/query_assist/input.tsx delete mode 100644 public/components/event_analytics/redux/slices/query_assistant_summarization_slice.ts delete mode 100644 server/common/helpers/query_assist/__tests__/generate_field_context.test.ts delete mode 100644 server/common/helpers/query_assist/generate_field_context.ts delete mode 100644 server/routes/query_assist/routes.ts diff --git a/common/constants/data_sources.ts b/common/constants/data_sources.ts index 7918516628..931f1f7290 100644 --- a/common/constants/data_sources.ts +++ b/common/constants/data_sources.ts @@ -5,8 +5,6 @@ export const DATA_SOURCE_NAME_URL_PARAM_KEY = 'datasourceName'; export const DATA_SOURCE_TYPE_URL_PARAM_KEY = 'datasourceType'; -export const OLLY_QUESTION_URL_PARAM_KEY = 'olly_q'; -export const INDEX_URL_PARAM_KEY = 'indexPattern'; export const DEFAULT_DATA_SOURCE_TYPE = 'DEFAULT_INDEX_PATTERNS'; export const DEFAULT_DATA_SOURCE_NAME = 'Default cluster'; export const DEFAULT_DATA_SOURCE_OBSERVABILITY_DISPLAY_NAME = 'OpenSearch'; diff --git a/common/constants/explorer.ts b/common/constants/explorer.ts index d94957c3e8..187d5d40c3 100644 --- a/common/constants/explorer.ts +++ b/common/constants/explorer.ts @@ -18,7 +18,6 @@ export const RAW_QUERY = 'rawQuery'; export const FINAL_QUERY = 'finalQuery'; export const SELECTED_DATE_RANGE = 'selectedDateRange'; export const INDEX = 'index'; -export const OLLY_QUERY_ASSISTANT = 'ollyQueryAssistant'; export const SELECTED_PATTERN_FIELD = 'selectedPatternField'; export const PATTERN_REGEX = 'patternRegex'; export const FILTERED_PATTERN = 'filteredPattern'; diff --git a/common/constants/query_assist.ts b/common/constants/query_assist.ts deleted file mode 100644 index 04b38c1299..0000000000 --- a/common/constants/query_assist.ts +++ /dev/null @@ -1,12 +0,0 @@ -/* - * Copyright OpenSearch Contributors - * SPDX-License-Identifier: Apache-2.0 - */ - -const QUERY_ASSIST_API_PREFIX = '/api/observability/query_assist'; -export const QUERY_ASSIST_API = { - GENERATE_PPL: `${QUERY_ASSIST_API_PREFIX}/generate_ppl`, - SUMMARIZE: `${QUERY_ASSIST_API_PREFIX}/summarize`, -}; - -export const ML_COMMONS_API_PREFIX = '/_plugins/_ml'; diff --git a/common/constants/shared.ts b/common/constants/shared.ts index e6e84b37bd..9baffcf101 100644 --- a/common/constants/shared.ts +++ b/common/constants/shared.ts @@ -256,6 +256,3 @@ export const S3_DATASOURCE_TYPE = 'S3_DATASOURCE'; export const ASYNC_QUERY_SESSION_ID = 'async-query-session-id'; export const DIRECT_DUMMY_QUERY = 'select 1'; - -export const QUERY_ASSISTANT_FIXED_START_TIME = 'now-40y'; -export const QUERY_ASSISTANT_FIXED_END_TIME = 'now'; diff --git a/opensearch_dashboards.json b/opensearch_dashboards.json index efaf215872..c05e9a9023 100644 --- a/opensearch_dashboards.json +++ b/opensearch_dashboards.json @@ -18,6 +18,8 @@ "urlForwarding", "visualizations" ], - "optionalPlugins": ["managementOverview", "assistantDashboards"], - "configPath": ["observability"] -} + "optionalPlugins": [ + "managementOverview", + "assistantDashboards" + ] +} \ No newline at end of file diff --git a/public/.DS_Store b/public/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..dbd07c1a1c1d289c1b490405a326e8a55ae29325 GIT binary patch literal 6148 zcmeHKJ8r^25S>XVP-t9I?iIMf%5qNN3m_#C4KXMr)UI+aj>emhq98+&f+l(+&Ai>& zd29I<9*>A<`}MjKX+&fWH%Fk~B9M^^Pys6Nqkw%M3f!hP6`=yd49* i9b;qdc*nm7hJ9eJk%`7>a;(5S$@6*vQa;1$mR literal 0 HcmV?d00001 diff --git a/public/components/common/live_tail/live_tail_button.tsx b/public/components/common/live_tail/live_tail_button.tsx index 66bb91591a..0e53152fa3 100644 --- a/public/components/common/live_tail/live_tail_button.tsx +++ b/public/components/common/live_tail/live_tail_button.tsx @@ -3,13 +3,13 @@ * SPDX-License-Identifier: Apache-2.0 */ -// Define pop over interval options for live tail button in your plugin +//Define pop over interval options for live tail button in your plugin -import { EuiButton } from '@elastic/eui'; -import React, { useMemo } from 'react'; -import { LiveTailProps } from 'common/types/explorer'; +import { EuiButton } from "@elastic/eui"; +import React, { useMemo } from "react"; +import { LiveTailProps } from "common/types/explorer"; -// Live Tail Button +//Live Tail Button export const LiveTailButton = ({ isLiveTailOn, isLiveTailPopoverOpen, @@ -20,7 +20,7 @@ export const LiveTailButton = ({ const liveButton = useMemo(() => { return ( setIsLiveTailPopoverOpen(!isLiveTailPopoverOpen)} data-test-subj={dataTestSubj} diff --git a/public/components/common/search/date_picker.tsx b/public/components/common/search/date_picker.tsx index 5f9a463750..75087c9f13 100644 --- a/public/components/common/search/date_picker.tsx +++ b/public/components/common/search/date_picker.tsx @@ -3,57 +3,25 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { EuiSuperDatePicker, EuiToolTip } from '@elastic/eui'; -import React, { useEffect } from 'react'; -import { uiSettingsService } from '../../../../common/utils'; -import { coreRefs } from '../../../framework/core_refs'; +import React from 'react'; +import { EuiSuperDatePicker } from '@elastic/eui'; import { IDatePickerProps } from './search'; -import { - QUERY_ASSISTANT_FIXED_END_TIME, - QUERY_ASSISTANT_FIXED_START_TIME, -} from '../../../../common/constants/shared'; +import { uiSettingsService } from '../../../../common/utils'; export function DatePicker(props: IDatePickerProps) { - const { - startTime, - endTime, - handleTimePickerChange, - handleTimeRangePickerRefresh, - isAppAnalytics, - } = props; + const { startTime, endTime, handleTimePickerChange, handleTimeRangePickerRefresh } = props; const handleTimeChange = (e: any) => handleTimePickerChange([e.start, e.end]); - const allowTimeChanging = !coreRefs.queryAssistEnabled || isAppAnalytics; - - // set the time range to be 40 years rather than the standard 15 minutes if using query assistant - useEffect(() => { - if (!allowTimeChanging) { - handleTimePickerChange([QUERY_ASSISTANT_FIXED_START_TIME, QUERY_ASSISTANT_FIXED_END_TIME]); - } - }, []); return ( - <> - - - - + ); } diff --git a/public/components/common/search/query_area.tsx b/public/components/common/search/query_area.tsx deleted file mode 100644 index ffa486a366..0000000000 --- a/public/components/common/search/query_area.tsx +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright OpenSearch Contributors - * SPDX-License-Identifier: Apache-2.0 - */ - -import { EuiCodeEditor, EuiFlexGroup, EuiFlexItem, EuiPanel } from '@elastic/eui'; -import React, { useEffect, useMemo } from 'react'; -import { coreRefs } from '../../../framework/core_refs'; -import { QueryAssistInput } from '../../event_analytics/explorer/query_assist/input'; -import { useFetchEvents } from '../../event_analytics/hooks/use_fetch_events'; - -export function QueryArea({ - tabId, - handleQueryChange, - handleTimeRangePickerRefresh, - runQuery, - tempQuery, - setNeedsUpdate, - setFillRun, - selectedIndex, - nlqInput, - setNlqInput, - pplService, -}: any) { - const requestParams = { tabId }; - const { getAvailableFields } = useFetchEvents({ - pplService, - requestParams, - }); - - // use effect that sets the editor text and populates sidebar field for a particular index upon initialization - const memoizedGetAvailableFields = useMemo(() => getAvailableFields, []); - const memoizedHandleQueryChange = useMemo(() => handleQueryChange, []); - useEffect(() => { - const indexQuery = `source = ${selectedIndex[0].label}`; - memoizedHandleQueryChange(indexQuery); - memoizedGetAvailableFields(indexQuery); - }, [selectedIndex, memoizedGetAvailableFields, memoizedHandleQueryChange]); - - return ( - - - - { - handleQueryChange(query); - // query is considered updated when the last run query is not the same as whats in the editor - // setUpdatedQuery(runQuery !== query); - setNeedsUpdate(runQuery !== query); - }} - onFocus={() => setFillRun(true)} - onBlur={() => setFillRun(false)} - value={tempQuery} - wrapEnabled={true} - /> - - {coreRefs.queryAssistEnabled && ( - - - - )} - - - ); -} diff --git a/public/components/common/search/query_assist_summarization.tsx b/public/components/common/search/query_assist_summarization.tsx deleted file mode 100644 index bb7d92e051..0000000000 --- a/public/components/common/search/query_assist_summarization.tsx +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright OpenSearch Contributors - * SPDX-License-Identifier: Apache-2.0 - */ - -import { - EuiAccordion, - EuiBadge, - EuiCallOut, - EuiFlexGroup, - EuiFlexItem, - EuiIcon, - EuiLink, - EuiMarkdownFormat, - EuiPanel, - EuiSpacer, - EuiText, -} from '@elastic/eui'; -import chatLogo from '../../datasources/icons/query-assistant-logo.svg'; -import React from 'react'; - -export function QueryAssistSummarization({ - queryAssistantSummarization, - setNlqInput, - showFlyout, -}: any) { - return ( - - - - - Generated by Opensearch Assistant - - - - - - - } - > - {queryAssistantSummarization?.summary?.length > 0 && ( - <> - - {queryAssistantSummarization?.isPPLError ? ( - <> - - {queryAssistantSummarization.summary} - - - - - Suggestions: - - {queryAssistantSummarization.suggestedQuestions.map((question) => ( - - setNlqInput(question)} - onClickAriaLabel="Set input to the suggested question" - > - {question} - - - ))} - - - PPL Documentation - - - - - ) : ( - - {queryAssistantSummarization.summary} - - )} - - - - The OpenSearch Assistant may produce inaccurate information. Verify all information - before using it in any environment or workload. Share feedback via{' '} - - Forum - {' '} - or{' '} - - Slack - - - - - )} - - - ); -} diff --git a/public/components/common/search/search.tsx b/public/components/common/search/search.tsx index 278919533d..c849c25cbd 100644 --- a/public/components/common/search/search.tsx +++ b/public/components/common/search/search.tsx @@ -3,71 +3,38 @@ * SPDX-License-Identifier: Apache-2.0 */ +import './search.scss'; + import '@algolia/autocomplete-theme-classic'; import { - EuiAccordion, EuiBadge, EuiButton, EuiButtonEmpty, - EuiCallOut, - EuiComboBox, - EuiComboBoxOptionOption, EuiContextMenuItem, EuiContextMenuPanel, EuiFlexGroup, EuiFlexItem, - EuiIcon, - EuiLink, - EuiMarkdownFormat, - EuiPanel, EuiPopover, EuiPopoverFooter, - EuiSpacer, - EuiSuperSelect, - EuiSuperSelectOption, - EuiText, EuiToolTip, } from '@elastic/eui'; import { isEqual } from 'lodash'; import React, { useEffect, useState } from 'react'; -import { batch, useDispatch, useSelector } from 'react-redux'; -import { QUERY_LANGUAGE } from '../../../../common/constants/data_sources'; -import { - APP_ANALYTICS_TAB_ID_REGEX, - INDEX, - OLLY_QUERY_ASSISTANT, - RAW_QUERY, -} from '../../../../common/constants/explorer'; +import { useDispatch } from 'react-redux'; +import { APP_ANALYTICS_TAB_ID_REGEX } from '../../../../common/constants/explorer'; import { PPL_SPAN_REGEX } from '../../../../common/constants/shared'; import { uiSettingsService } from '../../../../common/utils'; import { useFetchEvents } from '../../../components/event_analytics/hooks'; import { usePolling } from '../../../components/hooks/use_polling'; import { coreRefs } from '../../../framework/core_refs'; import { SQLService } from '../../../services/requests/sql'; -import { - useCatIndices, - useGetIndexPatterns, -} from '../../event_analytics/explorer/query_assist/hooks'; import { SavePanel } from '../../event_analytics/explorer/save_panel'; -import { - resetSummary, - selectQueryAssistantSummarization, -} from '../../event_analytics/redux/slices/query_assistant_summarization_slice'; -import { reset } from '../../event_analytics/redux/slices/query_result_slice'; -import { - changeData, - changeQuery, - selectQueries, -} from '../../event_analytics/redux/slices/query_slice'; import { update as updateSearchMetaData } from '../../event_analytics/redux/slices/search_meta_data_slice'; import { PPLReferenceFlyout } from '../helpers'; import { LiveTailButton, StopLiveButton } from '../live_tail/live_tail_button'; -import { DatePicker } from './date_picker'; -import { QueryArea } from './query_area'; -import './search.scss'; -import { QueryAssistSummarization } from './query_assist_summarization'; import { Autocomplete } from './autocomplete'; - +import { DatePicker } from './date_picker'; +import { QUERY_LANGUAGE } from '../../../../common/constants/data_sources'; export interface IQueryBarProps { query: string; tempQuery: string; @@ -79,13 +46,12 @@ export interface IQueryBarProps { export interface IDatePickerProps { startTime: string; endTime: string; - setStartTime: (start: string) => void; - setEndTime: (end: string) => void; + setStartTime: () => void; + setEndTime: () => void; setTimeRange: () => void; setIsOutputStale: () => void; handleTimePickerChange: (timeRange: string[]) => any; handleTimeRangePickerRefresh: () => any; - isAppAnalytics: boolean; } export const Search = (props: any) => { @@ -93,7 +59,6 @@ export const Search = (props: any) => { query, tempQuery, handleQueryChange, - handleQuerySearch, handleTimePickerChange, dslService, startTime, @@ -127,26 +92,16 @@ export const Search = (props: any) => { curVisId, setSubType, setIsQueryRunning, - isAppAnalytics, - pplService, } = props; - const queryRedux = useSelector(selectQueries)[tabId]; - const queryAssistantSummarization = useSelector(selectQueryAssistantSummarization)[tabId]; const dispatch = useDispatch(); const appLogEvents = tabId.match(APP_ANALYTICS_TAB_ID_REGEX); const [isSavePanelOpen, setIsSavePanelOpen] = useState(false); const [isLanguagePopoverOpen, setLanguagePopoverOpen] = useState(false); const [isFlyoutVisible, setIsFlyoutVisible] = useState(false); const [queryLang, setQueryLang] = useState(QUERY_LANGUAGE.PPL); - const [timeRange, setTimeRange] = useState(['now-15m', 'now']); // default time range - const [needsUpdate, setNeedsUpdate] = useState(false); - const [fillRun, setFillRun] = useState(false); const sqlService = new SQLService(coreRefs.http); const { application } = coreRefs; - const [nlqInput, setNlqInput] = useState(''); - - const showQueryArea = !appLogEvents && coreRefs.queryAssistEnabled; const { data: pollingResult, @@ -163,10 +118,6 @@ export const Search = (props: any) => { pplService: new SQLService(coreRefs.http), requestParams, }); - const { getAvailableFields } = useFetchEvents({ - pplService, - requestParams, - }); const closeFlyout = () => { setIsFlyoutVisible(false); @@ -229,11 +180,6 @@ export const Search = (props: any) => { setLanguagePopoverOpen(false); }; - const languageOptions: Array> = [ - { value: QUERY_LANGUAGE.PPL, inputDisplay: PPL }, - { value: QUERY_LANGUAGE.DQL, inputDisplay: DQL }, - ]; - const languagePopOverItems = [ { } }, [pollingResult, pollingError]); - useEffect(() => { - // set index and olly query assistant question if changed elsewhere - if (!queryRedux.ollyQueryAssistant) return; - if (queryRedux.index.length > 0) { - const reduxIndex = [{ label: queryRedux.index }]; - setSelectedIndex(reduxIndex); - // sets the editor text and populates sidebar field for a particular index upon initialization - const indexQuery = `source = ${reduxIndex[0].label}`; - handleQueryChange(indexQuery); - getAvailableFields(indexQuery); - } - if (queryRedux.ollyQueryAssistant.length > 0) { - setNlqInput(queryRedux.ollyQueryAssistant); - // remove index and olly query assistant - dispatch( - changeData({ - tabId: props.tabId, - data: { - [INDEX]: '', - [OLLY_QUERY_ASSISTANT]: '', - }, - }) - ); - } - }, [queryRedux.index, queryRedux.ollyQueryAssistant]); - - const runChanges = () => { - batch(() => { - dispatch(reset({ tabId })); - dispatch(resetSummary({ tabId })); - dispatch(changeQuery({ tabId, query: { [RAW_QUERY]: tempQuery } })); - }); - onQuerySearch(queryLang); - handleTimePickerChange(timeRange); - setNeedsUpdate(false); - }; - - // STATE FOR LANG PICKER AND INDEX PICKER - const [selectedIndex, setSelectedIndex] = useState([ - { label: 'opensearch_dashboards_sample_data_logs' }, - ]); - const { data: indices, loading: indicesLoading } = useCatIndices(); - const { data: indexPatterns, loading: indexPatternsLoading } = useGetIndexPatterns(); - const indicesAndIndexPatterns = - indexPatterns && indices - ? [...indexPatterns, ...indices].filter( - (v1, index, array) => array.findIndex((v2) => v1.label === v2.label) === index - ) - : undefined; - const loading = indicesLoading || indexPatternsLoading; - return (
- - - - {appLogEvents && ( - - - - Base Query - - - - )} - {!appLogEvents && ( - <> - - { - handleQueryLanguageChange(lang); - setQueryLang(lang); - }} - /> - - - showFlyout()} - color="#159D8D" - // onClickAriaLabel={'pplLinkShowFlyout'} - /> - - {coreRefs.queryAssistEnabled && ( - - Index} - singleSelection={true} - isLoading={loading} - options={indicesAndIndexPatterns} - selectedOptions={selectedIndex} - onChange={(index) => { - // clear previous state - batch(() => { - dispatch(reset({ tabId })); - dispatch(resetSummary({ tabId })); - }); - // change the query in the editor to be just source= - const indexQuery = `source = ${index[0].label}`; - handleQueryChange(indexQuery); - // get the fields into the sidebar - getAvailableFields(indexQuery); - setSelectedIndex(index); - }} - /> - - )} - - )} - {!showQueryArea && ( - + {appLogEvents && ( + + + + Base Query + + + + )} + {!appLogEvents && ( + + + + + + )} + + { + onQuerySearch(queryLang); + }} + dslService={dslService} + getSuggestions={getSuggestions} + onItemSelect={onItemSelect} + tabId={tabId} + /> + showFlyout()} + onClickAriaLabel={'pplLinkShowFlyout'} + > + PPL + + + + + {!isLiveTailOn && ( + handleTimePickerChange(timeRange)} + handleTimeRangePickerRefresh={() => { + onQuerySearch(queryLang); + }} + /> + )} + + {showSaveButton && !showSavePanelOptionsList && ( + + + + + + )} + {isLiveTailOn && ( + + + + )} + {showSaveButton && searchBarConfigs[selectedSubTabId]?.showSaveButton && ( + <> + + setIsSavePanelOpen(false)} > - { - onQuerySearch(queryLang); - }} - dslService={dslService} - getSuggestions={getSuggestions} - onItemSelect={onItemSelect} - tabId={tabId} - /> - showFlyout()} - onClickAriaLabel={'pplLinkShowFlyout'} - > - PPL - - - )} - - - {!isLiveTailOn && ( - { - // modifies run button to look like the update button, if there is a time change, disables timepicker setting update if timepicker is disabled - setNeedsUpdate( - !showQueryArea && // keeps statement false if using query assistant ui, timepicker shouldn't change run button - !(tRange[0] === startTime && tRange[1] === endTime) // checks to see if the time given is different from prev + { + return ( + isEqual(curVisId, 'line') && + tempQuery && + tempQuery.match(PPL_SPAN_REGEX) !== null ); - // keeps the time range change local, to be used when update pressed - setTimeRange(tRange); - setStartTime(tRange[0]); - setEndTime(tRange[1]); }} - handleTimeRangePickerRefresh={() => { - onQuerySearch(queryLang); - }} - isAppAnalytics={isAppAnalytics} /> - )} - - - - - {needsUpdate ? 'Update' : 'Run'} - - + + + + setIsSavePanelOpen(false)} + data-test-subj="eventExplorer__querySaveCancel" + > + Cancel + + + + { + handleSavingObject(); + setIsSavePanelOpen(false); + }} + data-test-subj="eventExplorer__querySaveConfirm" + > + Save + + + + + - {!showQueryArea && showSaveButton && !showSavePanelOptionsList && ( - - - - - - )} - {!showQueryArea && isLiveTailOn && ( - - - - )} - {showSaveButton && searchBarConfigs[selectedSubTabId]?.showSaveButton && ( - <> - - setIsSavePanelOpen(false)} - > - - - - - setIsSavePanelOpen(false)} - data-test-subj="eventExplorer__querySaveCancel" - > - Cancel - - - - { - handleSavingObject(); - setIsSavePanelOpen(false); - }} - data-test-subj="eventExplorer__querySaveConfirm" - > - Save - - - - - - - - )} - - - {showQueryArea && ( - <> - - - - {(queryAssistantSummarization?.summary?.length > 0 || - queryAssistantSummarization?.summaryLoading) && ( - - - - )} )} diff --git a/public/components/datasources/icons/query-assistant-logo.svg b/public/components/datasources/icons/query-assistant-logo.svg deleted file mode 100644 index d21737d822..0000000000 --- a/public/components/datasources/icons/query-assistant-logo.svg +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/public/components/event_analytics/__tests__/__snapshots__/no_results.test.tsx.snap b/public/components/event_analytics/__tests__/__snapshots__/no_results.test.tsx.snap index 0fe3612fb8..9092d4be2c 100644 --- a/public/components/event_analytics/__tests__/__snapshots__/no_results.test.tsx.snap +++ b/public/components/event_analytics/__tests__/__snapshots__/no_results.test.tsx.snap @@ -1,156 +1,142 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`No result component Renders No result component 1`] = ` - - + - -
- -
- -
+ } > - - } >
- -
-
-
-
- -
- -
- - -
-

+ + + + - Select a data source, expand your time range, or modify the query + No results match your search criteria -

-

- - - After selection, check the time range, query filters, fields, and query - - -

+
-
-
- -
- -
- - - +
+ +
+ + +
+ +
+ + +
+

+ + + Select a data source, expand your time range, or modify the query + + +

+

+ + + After selection, check the time range, query filters, fields, and query + + +

+
+
+
+ +
+ + + + `; diff --git a/public/components/event_analytics/__tests__/no_results.test.tsx b/public/components/event_analytics/__tests__/no_results.test.tsx index 7d545ddf48..1bc6c9eb17 100644 --- a/public/components/event_analytics/__tests__/no_results.test.tsx +++ b/public/components/event_analytics/__tests__/no_results.test.tsx @@ -8,28 +8,20 @@ import Adapter from 'enzyme-adapter-react-16'; import React from 'react'; import { waitFor } from '@testing-library/react'; import { NoResults } from '../explorer/no_results'; -import { initialTabId } from '../../../framework/redux/store/shared_state'; -import { Provider } from 'react-redux'; -import { applyMiddleware, createStore } from '@reduxjs/toolkit'; -import { rootReducer } from '../../../framework/redux/reducers'; -import thunk from 'redux-thunk'; describe('No result component', () => { configure({ adapter: new Adapter() }); it('Renders No result component', async () => { - const store = createStore(rootReducer, applyMiddleware(thunk)); - + const wrapper = mount( - - - + ); - + wrapper.update(); await waitFor(() => { expect(wrapper).toMatchSnapshot(); }); }); -}); +}); \ No newline at end of file diff --git a/public/components/event_analytics/explorer/datasources/datasources_selection.tsx b/public/components/event_analytics/explorer/datasources/datasources_selection.tsx index 122e7cc7f8..3c6e34df1f 100644 --- a/public/components/event_analytics/explorer/datasources/datasources_selection.tsx +++ b/public/components/event_analytics/explorer/datasources/datasources_selection.tsx @@ -20,9 +20,9 @@ import { reset as resetCountDistribution } from '../../redux/slices/count_distri import { reset as resetFields } from '../../redux/slices/field_slice'; import { reset as resetPatterns } from '../../redux/slices/patterns_slice'; import { reset as resetQueryResults } from '../../redux/slices/query_result_slice'; -import { changeData, reset as resetQuery } from '../../redux/slices/query_slice'; import { reset as resetVisualization } from '../../redux/slices/visualization_slice'; import { reset as resetVisConfig } from '../../redux/slices/viualization_config_slice'; +import { reset as resetQuery } from '../../redux/slices/query_slice'; import { DirectQueryRequest, SelectedDataSource } from '../../../../../common/types/explorer'; import { ObservabilityDefaultDataSource } from '../../../../framework/datasources/obs_opensearch_datasource'; import { @@ -34,8 +34,6 @@ import { DEFAULT_DATA_SOURCE_OBSERVABILITY_DISPLAY_NAME, DATA_SOURCE_TYPES, QUERY_LANGUAGE, - OLLY_QUESTION_URL_PARAM_KEY, - INDEX_URL_PARAM_KEY, } from '../../../../../common/constants/data_sources'; import { SQLService } from '../../../../services/requests/sql'; import { get as getObjValue } from '../../../../../common/utils/shared'; @@ -44,11 +42,6 @@ import { getAsyncSessionId, } from '../../../../../common/utils/query_session_utils'; import { DIRECT_DUMMY_QUERY } from '../../../../../common/constants/shared'; -import { - INDEX, - OLLY_QUERY_ASSISTANT, - SELECTED_TIMESTAMP, -} from '../../../../../common/constants/explorer'; const getDataSourceState = (selectedSourceState: SelectedDataSource[]) => { if (selectedSourceState.length === 0) return []; @@ -76,9 +69,6 @@ const removeDataSourceFromURLParams = (currURL: string) => { // Remove the data source redirection parameters hashParams.delete(DATA_SOURCE_NAME_URL_PARAM_KEY); hashParams.delete(DATA_SOURCE_TYPE_URL_PARAM_KEY); - hashParams.delete(OLLY_QUESTION_URL_PARAM_KEY); - hashParams.delete(INDEX_URL_PARAM_KEY); - hashParams.delete('timestamp'); // Reconstruct the hash currentURL.hash = hashParams.toString() ? `${hashBase}?${hashParams.toString()}` : hashBase; @@ -188,10 +178,6 @@ export const DataSourceSelection = ({ tabId }: { tabId: string }) => { useEffect(() => { const datasourceName = routerContext?.searchParams.get(DATA_SOURCE_NAME_URL_PARAM_KEY); const datasourceType = routerContext?.searchParams.get(DATA_SOURCE_TYPE_URL_PARAM_KEY); - const idxPattern = routerContext?.searchParams.get(INDEX_URL_PARAM_KEY); - const ollyQuestion = routerContext?.searchParams.get(OLLY_QUESTION_URL_PARAM_KEY) || ''; - const decodedOllyQ = decodeURIComponent(ollyQuestion); - const parsedTimeStamp = routerContext?.searchParams.get('timestamp') || ''; if (datasourceName && datasourceType) { // remove datasourceName and datasourceType from URL for a clean search state removeDataSourceFromURLParams(window.location.href); @@ -204,18 +190,6 @@ export const DataSourceSelection = ({ tabId }: { tabId: string }) => { }) ); }); - if (idxPattern && decodedOllyQ) { - dispatch( - changeData({ - tabId, - data: { - [INDEX]: idxPattern, - [OLLY_QUERY_ASSISTANT]: decodedOllyQ, - [SELECTED_TIMESTAMP]: parsedTimeStamp, - }, - }) - ); - } } }, []); diff --git a/public/components/event_analytics/explorer/events_views/data_grid.tsx b/public/components/event_analytics/explorer/events_views/data_grid.tsx index 25b94eff56..56cac6abb5 100644 --- a/public/components/event_analytics/explorer/events_views/data_grid.tsx +++ b/public/components/event_analytics/explorer/events_views/data_grid.tsx @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import React, { useMemo, useState, useRef, Fragment, useCallback, useEffect } from 'react'; +import React, { useMemo, useState, useRef, Fragment, useCallback } from 'react'; import { EuiDataGrid, EuiDescriptionList, @@ -72,10 +72,6 @@ export function DataGrid(props: DataGridProps) { const [data, setData] = useState(rows); - useEffect(() => { - setData(rows); - }, [rows]); - // setSort and setPage are used to change the query and send a direct request to get data const setSort = (sort: EuiDataGridSorting['columns']) => { sortingFields.current = sort; @@ -107,7 +103,7 @@ export function DataGrid(props: DataGridProps) { }; // creates the header for each column listing what that column is - const dataGridColumns = () => { + const dataGridColumns = useMemo(() => { const columns: EuiDataGridColumn[] = []; selectedColumns.map(({ name, type }) => { if (name === 'timestamp') { @@ -123,10 +119,10 @@ export function DataGrid(props: DataGridProps) { } }); return columns; - }; + }, [explorerFields, totalHits]); // used for which columns are visible and their order - const dataGridColumnVisibility = () => { + const dataGridColumnVisibility = useMemo(() => { if (selectedColumns.length > 0) { const columns: string[] = []; selectedColumns.map(({ name }) => { @@ -141,10 +137,10 @@ export function DataGrid(props: DataGridProps) { } // default shown fields throw new Error('explorer data grid stored columns empty'); - }; + }, [explorerFields, totalHits]); // sets the very first column, which is the button used for the flyout of each row - const dataGridLeadingColumns = () => { + const dataGridLeadingColumns = useMemo(() => { return [ { id: 'inspectCollapseColumn', @@ -175,58 +171,70 @@ export function DataGrid(props: DataGridProps) { width: 40, }, ]; - }; + }, [rows, http, explorerFields, pplService, rawQuery, timeStampField, totalHits]); // renders what is shown in each cell, i.e. the content of each row - const dataGridCellRender = ({ rowIndex, columnId }: { rowIndex: number; columnId: string }) => { - const trueIndex = rowIndex % pageFields.current[1]; - if (trueIndex < data.length) { - if (columnId === '_source') { - return ( - - {Object.keys(data[trueIndex]).map((key) => ( - - - {key} - - - {data[trueIndex][key]} - - - ))} - - ); - } - if (columnId === 'timestamp') { - return `${moment(data[trueIndex][columnId]).format(DATE_DISPLAY_FORMAT)}`; + const dataGridCellRender = useCallback( + ({ rowIndex, columnId }: { rowIndex: number; columnId: string }) => { + const trueIndex = rowIndex % pageFields.current[1]; + if (trueIndex < data.length) { + if (columnId === '_source') { + return ( + + {Object.keys(data[trueIndex]).map((key) => ( + + + {key} + + + {data[trueIndex][key]} + + + ))} + + ); + } + if (columnId === 'timestamp') { + return `${moment(data[trueIndex][columnId]).format(DATE_DISPLAY_FORMAT)}`; + } + return `${data[trueIndex][columnId]}`; } - return `${data[trueIndex][columnId]}`; - } - return null; - }; + return null; + }, + [data, rows, pageFields, explorerFields, totalHits] + ); // ** Pagination config const [pagination, setPagination] = useState({ pageIndex: 0, pageSize: 100 }); // changing the number of items per page, reset index and modify page size - const onChangeItemsPerPage = (pageSize) => - setPagination(() => { - setPage([0, pageSize]); - return { pageIndex: 0, pageSize }; - }); + const onChangeItemsPerPage = useCallback( + (pageSize) => + setPagination(() => { + setPage([0, pageSize]); + return { pageIndex: 0, pageSize }; + }), + [setPagination, setPage, totalHits] + ); // changing the page index, keep page size constant - const onChangePage = (pageIndex) => { - setPagination(({ pageSize }) => { - setPage([pageIndex, pageSize]); - return { pageSize, pageIndex }; - }); - }; - - const rowHeightsOptions = () => ({ - defaultHeight: { - // if source is listed as a column, add extra space - lineCount: selectedColumns.some((obj) => obj.name === '_source') ? 3 : 1, + const onChangePage = useCallback( + (pageIndex) => { + setPagination(({ pageSize }) => { + setPage([pageIndex, pageSize]); + return { pageSize, pageIndex }; + }); }, - }); + [setPagination, setPage, totalHits] + ); + + const rowHeightsOptions = useMemo( + () => ({ + defaultHeight: { + // if source is listed as a column, add extra space + lineCount: selectedColumns.some((obj) => obj.name === '_source') ? 3 : 1, + }, + }), + [explorerFields, totalHits] + ); // TODO: memoize the expensive table below @@ -236,9 +244,9 @@ export function DataGrid(props: DataGridProps) { diff --git a/public/components/event_analytics/explorer/explorer.tsx b/public/components/event_analytics/explorer/explorer.tsx index 5ac6b0591a..fd88697b32 100644 --- a/public/components/event_analytics/explorer/explorer.tsx +++ b/public/components/event_analytics/explorer/explorer.tsx @@ -33,10 +33,6 @@ import React, { } from 'react'; import { batch, useDispatch, useSelector } from 'react-redux'; import { LogExplorerRouterContext } from '..'; -import { - DEFAULT_DATA_SOURCE_TYPE, - QUERY_LANGUAGE, -} from '../../../../common/constants/data_sources'; import { CREATE_TAB_PARAM, CREATE_TAB_PARAM_KEY, @@ -78,13 +74,11 @@ import { getSavingCommonParams, uiSettingsService, } from '../../../../common/utils'; -import { initialTabId } from '../../../framework/redux/store/shared_state'; import { PPLDataFetcher } from '../../../services/data_fetchers/ppl/ppl_data_fetcher'; import { getSavedObjectsClient } from '../../../services/saved_objects/saved_object_client/client_factory'; -import { OSDSavedSearchClient } from '../../../services/saved_objects/saved_object_client/osd_saved_objects/saved_searches'; import { OSDSavedVisualizationClient } from '../../../services/saved_objects/saved_object_client/osd_saved_objects/saved_visualization'; +import { OSDSavedSearchClient } from '../../../services/saved_objects/saved_object_client/osd_saved_objects/saved_searches'; import { PanelSavedObjectClient } from '../../../services/saved_objects/saved_object_client/ppl'; -import { ExplorerSavedObjectLoader } from '../../../services/saved_objects/saved_object_loaders/explorer_saved_object_loader'; import { SaveAsCurrentQuery, SaveAsCurrentVisualization, @@ -92,41 +86,47 @@ import { } from '../../../services/saved_objects/saved_object_savers'; import { SaveAsNewQuery } from '../../../services/saved_objects/saved_object_savers/ppl/save_as_new_query'; import { sleep } from '../../common/live_tail/live_tail_button'; -import { findMinInterval } from '../../common/query_utils'; import { onItemSelect, parseGetSuggestions } from '../../common/search/autocomplete_logic'; import { Search } from '../../common/search/search'; -import { processMetricsData } from '../../custom_panels/helpers/utils'; import { selectSearchMetaData } from '../../event_analytics/redux/slices/search_meta_data_slice'; import { getVizContainerProps } from '../../visualizations/charts/helpers'; import { TabContext, useFetchEvents, useFetchPatterns, useFetchVisualizations } from '../hooks'; import { - selectCountDistribution, render as updateCountDistribution, + selectCountDistribution, } from '../redux/slices/count_distribution_slice'; import { selectFields, updateFields } from '../redux/slices/field_slice'; import { selectQueryResult } from '../redux/slices/query_result_slice'; -import { changeData, changeQuery, selectQueries } from '../redux/slices/query_slice'; +import { changeDateRange, changeQuery, selectQueries } from '../redux/slices/query_slice'; import { updateTabName } from '../redux/slices/query_tab_slice'; import { selectExplorerVisualization } from '../redux/slices/visualization_slice'; import { change as changeVisualizationConfig, change as changeVizConfig, - selectVisualizationConfig, change as updateVizConfig, + selectVisualizationConfig, } from '../redux/slices/viualization_config_slice'; -import { getDefaultVisConfig } from '../utils'; +import { formatError, getDefaultVisConfig } from '../utils'; import { getContentTabTitle, getDateRange } from '../utils/utils'; -import { DataSourceSelection } from './datasources/datasources_selection'; import { DirectQueryRunning } from './direct_query_running'; import { DataGrid } from './events_views/data_grid'; import { HitsCounter } from './hits_counter/hits_counter'; import { LogPatterns } from './log_patterns/log_patterns'; import { NoResults } from './no_results'; -import { ObservabilitySideBar } from './sidebar/observability_sidebar'; import { TimechartHeader } from './timechart_header'; import { ExplorerVisualizations } from './visualizations'; import { CountDistribution } from './visualizations/count_distribution'; import { DirectQueryVisualization } from './visualizations/direct_query_vis'; +import { DataSourceSelection } from './datasources/datasources_selection'; +import { initialTabId } from '../../../framework/redux/store/shared_state'; +import { ObservabilitySideBar } from './sidebar/observability_sidebar'; +import { ExplorerSavedObjectLoader } from '../../../services/saved_objects/saved_object_loaders/explorer_saved_object_loader'; +import { processMetricsData } from '../../custom_panels/helpers/utils'; +import { + DEFAULT_DATA_SOURCE_TYPE, + QUERY_LANGUAGE, +} from '../../../../common/constants/data_sources'; +import { findMinInterval } from '../../common/query_utils'; export const Explorer = ({ pplService, @@ -144,6 +144,10 @@ export const Explorer = ({ appId = '', appBaseQuery = '', addVisualizationToPanel, + startTime, + endTime, + setStartTime, + setEndTime, callback, callbackInApp, queryManager = new QueryManager(), @@ -226,18 +230,12 @@ export const Explorer = ({ const liveTailNameRef = useRef('Live'); const savedObjectLoader = useRef(undefined); const isObjectIdUpdatedFromSave = useRef(false); // Flag to prevent reload when the current search's objectId changes due to a save operation. - const tempQueryRef = useRef(''); queryRef.current = query; selectedPanelNameRef.current = selectedPanelName; explorerFieldsRef.current = explorerFields; isLiveTailOnRef.current = isLiveTailOn; liveTailTabIdRef.current = liveTailTabId; liveTailNameRef.current = liveTailName; - tempQueryRef.current = tempQuery; - - const dateRange = getDateRange(undefined, undefined, query); - const [startTime, setStartTime] = useState(dateRange[0]); - const [endTime, setEndTime] = useState(dateRange[1]); const findAutoInterval = (start: string = '', end: string = '') => { const minInterval = findMinInterval(start, end); @@ -246,16 +244,11 @@ export const Explorer = ({ { text: 'Auto', value: 'auto_' + minInterval }, ...TIME_INTERVAL_OPTIONS, ]); - selectedIntervalRef.current = { - text: 'Auto', - value: 'auto_' + minInterval, - }; + selectedIntervalRef.current = { text: 'Auto', value: 'auto_' + minInterval }; dispatch( updateCountDistribution({ tabId, - data: { - selectedInterval: selectedIntervalRef.current.value.replace(/^auto_/, ''), - }, + data: { selectedInterval: selectedIntervalRef.current.value.replace(/^auto_/, '') }, }) ); }; @@ -273,18 +266,14 @@ export const Explorer = ({ const getErrorHandler = (title: string) => { return (error: any) => { - // const formattedError = formatError(error.name, error.message, error.body.message); - // notifications.toasts.addError(formattedError, { - // title, - // }); + const formattedError = formatError(error.name, error.message, error.body.message); + notifications.toasts.addError(formattedError, { + title, + }); }; }; - const fetchData = async ( - startingTime?: string, - endingTime?: string, - setSummaryStatus?: boolean - ) => { + const fetchData = async (startingTime?: string, endingTime?: string) => { const curQuery: IQuery = queryRef.current!; new PPLDataFetcher( { ...curQuery }, @@ -304,7 +293,6 @@ export const Explorer = ({ queryManager, getDefaultVisConfig, getAvailableFields, - setSummaryStatus, }, { appBaseQuery, @@ -410,9 +398,9 @@ export const Explorer = ({ setEndTime(timeRange[1]); } await dispatch( - changeData({ + changeDateRange({ tabId: requestParams.tabId, - data: { [SELECTED_DATE_RANGE]: timeRange }, + data: { [RAW_QUERY]: queryRef.current![RAW_QUERY], [SELECTED_DATE_RANGE]: timeRange }, }) ); }; @@ -427,11 +415,8 @@ export const Explorer = ({ ); }; - const handleTimeRangePickerRefresh = async ( - availability?: boolean, - setSummaryStatus?: boolean - ) => { - handleQuerySearch(availability, setSummaryStatus); + const handleTimeRangePickerRefresh = async (availability?: boolean) => { + handleQuerySearch(availability); if (availability !== true && query.rawQuery.match(PATTERNS_REGEX)) { let currQuery = query.rawQuery; const currPattern = currQuery.match(PATTERNS_EXTRACTOR_REGEX)!.groups!.pattern; @@ -453,12 +438,7 @@ export const Explorer = ({ const handleOverrideTimestamp = async (timestamp: IField) => { setIsOverridingTimestamp(true); - await dispatch( - changeQuery({ - tabId, - query: { [SELECTED_TIMESTAMP]: timestamp?.name || '' }, - }) - ); + await dispatch(changeQuery({ tabId, query: { [SELECTED_TIMESTAMP]: timestamp?.name || '' } })); setIsOverridingTimestamp(false); handleQuerySearch(); }; @@ -478,59 +458,55 @@ export const Explorer = ({ return 0; }, [countDistribution?.data]); + const dateRange = getDateRange(startTime, endTime, query); const mainContent = useMemo(() => { return (
{explorerData && !isEmpty(explorerData.jsonData) ? ( {(isDefaultDataSourceType || appLogEvents) && ( - <> - - - {countDistribution?.data && !isLiveTailOnRef.current && ( - - {}} - /> - { - const intervalOptionsIndex = timeIntervalOptions.findIndex( - (item) => item.value === selectedIntrv - ); - const intrv = selectedIntrv.replace(/^auto_/, ''); - dispatch( - updateCountDistribution({ - tabId, - data: { selectedInterval: intrv }, - }) - ); - getCountVisualizations(intrv); - selectedIntervalRef.current = timeIntervalOptions[intervalOptionsIndex]; - getPatterns(intrv, getErrorHandler('Error fetching patterns')); - }} - stateInterval={ - countDistribution.selectedInterval || selectedIntervalRef.current?.value - } - startTime={startTime} - endTime={endTime} - /> - - - - )} - - - + + + {countDistribution?.data && !isLiveTailOnRef.current && ( + + {}} + /> + { + const intervalOptionsIndex = timeIntervalOptions.findIndex( + (item) => item.value === selectedIntrv + ); + const intrv = selectedIntrv.replace(/^auto_/, ''); + dispatch( + updateCountDistribution({ tabId, data: { selectedInterval: intrv } }) + ); + getCountVisualizations(intrv); + selectedIntervalRef.current = timeIntervalOptions[intervalOptionsIndex]; + getPatterns(intrv, getErrorHandler('Error fetching patterns')); + }} + stateInterval={ + countDistribution.selectedInterval || selectedIntervalRef.current?.value + } + startTime={appLogEvents ? startTime : dateRange[0]} + endTime={appLogEvents ? endTime : dateRange[1]} + /> + + + + )} + + )} {(isDefaultDataSourceType || appLogEvents) && ( @@ -590,8 +566,8 @@ export const Explorer = ({ : explorerData.datarows.length } requestParams={requestParams} - startTime={startTime} - endTime={endTime} + startTime={appLogEvents ? startTime : dateRange[0]} + endTime={appLogEvents ? endTime : dateRange[1]} /> )} @@ -603,7 +579,7 @@ export const Explorer = ({ ) : ( - + )}
); @@ -695,35 +671,24 @@ export const Explorer = ({ const updateQueryInStore = async (updateQuery: string) => { await dispatch( - changeQuery({ - tabId, - query: { [RAW_QUERY]: updateQuery.replaceAll(PPL_NEWLINE_REGEX, '') }, - }) + changeQuery({ tabId, query: { [RAW_QUERY]: updateQuery.replaceAll(PPL_NEWLINE_REGEX, '') } }) ); }; - const handleQuerySearch = async (availability?: boolean, setSummaryStatus?: boolean) => { - // clear previous selected timestamp when index pattern changes - const searchedQuery = tempQueryRef.current; - if ( - isIndexPatternChanged(searchedQuery, query[RAW_QUERY]) && - query[SELECTED_TIMESTAMP] !== '' - ) { - await dispatch( - changeQuery({ - tabId, - query: { - [SELECTED_TIMESTAMP]: '', - }, - }) - ); - await setDefaultPatternsField('', ''); - } - if (availability !== true) { - await updateQueryInStore(searchedQuery); - } - await fetchData(undefined, undefined, setSummaryStatus); - }; + const handleQuerySearch = useCallback( + async (availability?: boolean) => { + // clear previous selected timestamp when index pattern changes + if (isIndexPatternChanged(tempQuery, query[RAW_QUERY])) { + await dispatch(changeQuery({ tabId, query: { [SELECTED_TIMESTAMP]: '' } })); + await setDefaultPatternsField('', ''); + } + if (availability !== true) { + await updateQueryInStore(tempQuery); + } + await fetchData(startTime, endTime); + }, + [tempQuery, query] + ); const handleQueryChange = async (newQuery: string) => setTempQuery(newQuery); @@ -953,10 +918,8 @@ export const Explorer = ({ handleQueryChange={handleQueryChange} handleQuerySearch={handleQuerySearch} dslService={dslService} - startTime={startTime} - endTime={endTime} - setStartTime={setStartTime} - setEndTime={setEndTime} + startTime={appLogEvents ? startTime : dateRange[0]} + endTime={appLogEvents ? endTime : dateRange[1]} handleTimePickerChange={(timeRange: string[]) => handleTimePickerChange(timeRange) } @@ -986,8 +949,6 @@ export const Explorer = ({ setSubType={setSubType} http={http} setIsQueryRunning={setIsQueryRunning} - isAppAnalytics={appLogEvents} - pplService={pplService} /> {explorerSearchMeta.isPolling ? ( diff --git a/public/components/event_analytics/explorer/no_results.tsx b/public/components/event_analytics/explorer/no_results.tsx index 489a72ace2..d6c96af865 100644 --- a/public/components/event_analytics/explorer/no_results.tsx +++ b/public/components/event_analytics/explorer/no_results.tsx @@ -5,104 +5,43 @@ import React from 'react'; import { FormattedMessage } from '@osd/i18n/react'; -import { - EuiCallOut, - EuiFlexGroup, - EuiFlexItem, - EuiPage, - EuiSpacer, - EuiText, - EuiEmptyPrompt, -} from '@elastic/eui'; -import { coreRefs } from '../../../framework/core_refs'; -import { useSelector } from 'react-redux'; -import { selectQueries } from '../redux/slices/query_slice'; - -export const NoResults = ({ tabId }: any) => { - // get the queries isLoaded, if it exists AND is true = show no res - const queryInfo = useSelector(selectQueries)[tabId]; +import { EuiCallOut, EuiFlexGroup, EuiFlexItem, EuiPage, EuiSpacer, EuiText } from '@elastic/eui'; +export const NoResults = () => { return ( - {coreRefs.queryAssistEnabled ? ( - <> - {/* check to see if the rawQuery is empty or not */} - {queryInfo?.rawQuery ? ( - - - - } - color="warning" - iconType="help" - data-test-subj="observabilityNoResultsCallout" - /> - - - No results} - body={ -

- Try selecting a different data source, expanding your time range or modifying - the query & filters. You may also use the Query Assistant to fine-tune your - query using simple conversational prompts. -

- } - /> -
-
- ) : ( - Get started} - body={ -

- Run a query to view results, or use the Query Assistant to automatically generate - complex queries using simple conversational prompts. -

- } - /> - )} - - ) : ( - - - - } - color="warning" - iconType="help" - data-test-subj="observabilityNoResultsCallout" - /> - - - - -

- -

-

- -

-
-
-
- )} + + + + } + color="warning" + iconType="help" + data-test-subj="observabilityNoResultsCallout" + /> + + + + +

+ +

+

+ +

+
+
+
); }; diff --git a/public/components/event_analytics/explorer/query_assist/__tests__/hooks.test.ts b/public/components/event_analytics/explorer/query_assist/__tests__/hooks.test.ts deleted file mode 100644 index 45e910d027..0000000000 --- a/public/components/event_analytics/explorer/query_assist/__tests__/hooks.test.ts +++ /dev/null @@ -1,127 +0,0 @@ -/* - * Copyright OpenSearch Contributors - * SPDX-License-Identifier: Apache-2.0 - */ - -import { renderHook } from '@testing-library/react-hooks'; -import { SavedObjectsFindResponsePublic } from '../../../../../../../../src/core/public'; -import { coreMock } from '../../../../../../../../src/core/public/mocks'; -import * as coreServices from '../../../../../../common/utils/core_services'; -import { coreRefs } from '../../../../../framework/core_refs'; -import { genericReducer, useCatIndices, useGetIndexPatterns } from '../hooks'; - -const coreStartMock = coreMock.createStart(); - -describe('useCatIndices', () => { - const httpMock = coreStartMock.http; - - beforeEach(() => { - jest.spyOn(coreServices, 'getOSDHttp').mockReturnValue(httpMock); - }); - - afterEach(() => { - jest.clearAllMocks(); - }); - - it('should return indices', async () => { - httpMock.get.mockResolvedValueOnce([{ index: 'test1' }, { index: 'test2' }]); - - const { result, waitForNextUpdate } = renderHook(() => useCatIndices()); - expect(result.current.loading).toBe(true); - await waitForNextUpdate(); - expect(result.current.loading).toBe(false); - expect(result.current.data).toEqual([{ label: 'test1' }, { label: 'test2' }]); - }); - - it('should handle errors', async () => { - httpMock.get.mockRejectedValueOnce('API failed'); - - const { result, waitForNextUpdate } = renderHook(() => useCatIndices()); - expect(result.current.loading).toBe(true); - await waitForNextUpdate(); - expect(result.current.loading).toBe(false); - expect(result.current.data).toBe(undefined); - expect(result.current.error).toEqual('API failed'); - }); -}); - -describe('useGetIndexPatterns', () => { - const savedObjectsClientMock = coreStartMock.savedObjects.client as jest.Mocked< - typeof coreStartMock.savedObjects.client - >; - - beforeAll(() => { - coreRefs.savedObjectsClient = savedObjectsClientMock; - }); - - afterEach(() => { - jest.clearAllMocks(); - }); - - it('should return index patterns', async () => { - savedObjectsClientMock.find.mockResolvedValueOnce({ - savedObjects: [{ attributes: { title: 'test1' } }, { attributes: { title: 'test2' } }], - } as SavedObjectsFindResponsePublic); - - const { result, waitForNextUpdate } = renderHook(() => useGetIndexPatterns()); - expect(result.current.loading).toBe(true); - await waitForNextUpdate(); - expect(result.current.loading).toBe(false); - expect(result.current.data).toEqual([{ label: 'test1' }, { label: 'test2' }]); - }); - - it('should handle errors', async () => { - savedObjectsClientMock.find.mockRejectedValueOnce('API failed'); - - const { result, waitForNextUpdate } = renderHook(() => useGetIndexPatterns()); - expect(result.current.loading).toBe(true); - await waitForNextUpdate(); - expect(result.current.loading).toBe(false); - expect(result.current.data).toBe(undefined); - expect(result.current.error).toEqual('API failed'); - }); -}); - -describe('genericReducer', () => { - it('should return original state', () => { - expect( - genericReducer( - { data: { foo: 'bar' }, loading: false }, - // mock not supported type - { type: ('not-supported-type' as unknown) as 'request' } - ) - ).toEqual({ - data: { foo: 'bar' }, - loading: false, - }); - }); - - it('should return state follow request action', () => { - expect(genericReducer({ data: { foo: 'bar' }, loading: false }, { type: 'request' })).toEqual({ - data: { foo: 'bar' }, - loading: true, - }); - }); - - it('should return state follow success action', () => { - expect( - genericReducer( - { data: { foo: 'bar' }, loading: false }, - { type: 'success', payload: { foo: 'baz' } } - ) - ).toEqual({ - data: { foo: 'baz' }, - loading: false, - }); - }); - - it('should return state follow failure action', () => { - const error = new Error(); - expect( - genericReducer({ data: { foo: 'bar' }, loading: false }, { type: 'failure', error }) - ).toEqual({ - error, - loading: false, - }); - }); -}); diff --git a/public/components/event_analytics/explorer/query_assist/__tests__/input.test.tsx b/public/components/event_analytics/explorer/query_assist/__tests__/input.test.tsx deleted file mode 100644 index f6a9dd02de..0000000000 --- a/public/components/event_analytics/explorer/query_assist/__tests__/input.test.tsx +++ /dev/null @@ -1,107 +0,0 @@ -/* - * Copyright OpenSearch Contributors - * SPDX-License-Identifier: Apache-2.0 - */ - -import { configureStore } from '@reduxjs/toolkit'; -import { fireEvent, render, waitFor } from '@testing-library/react'; -import React, { ComponentProps } from 'react'; -import { Provider } from 'react-redux'; -import { coreMock } from '../../../../../../../../src/core/public/mocks'; -import { QUERY_ASSIST_API } from '../../../../../../common/constants/query_assist'; -import * as coreServices from '../../../../../../common/utils/core_services'; -import { coreRefs } from '../../../../../framework/core_refs'; -import { rootReducer } from '../../../../../framework/redux/reducers'; -import { initialTabId } from '../../../../../framework/redux/store/shared_state'; -import { QueryAssistInput } from '../input'; - -const renderQueryAssistInput = ( - overrideProps: Partial> = {} -) => { - const preloadedState = {}; - const store = configureStore({ reducer: rootReducer, preloadedState }); - const props: ComponentProps = Object.assign( - { - handleQueryChange: jest.fn(), - handleTimeRangePickerRefresh: jest.fn(), - tabId: initialTabId, - setNeedsUpdate: jest.fn(), - selectedIndex: [{ label: 'selected-test-index' }], - nlqInput: 'test-input', - setNlqInput: jest.fn(), - }, - overrideProps - ); - const component = render( - - - - ); - return { component, props, store }; -}; - -describe(' spec', () => { - const coreStartMock = coreMock.createStart(); - coreRefs.toasts = coreStartMock.notifications.toasts; - const httpMock = coreStartMock.http; - - beforeEach(() => { - jest.spyOn(coreServices, 'getOSDHttp').mockReturnValue(httpMock); - }); - - afterEach(() => { - jest.clearAllMocks(); - }); - - it('should call generate ppl based on nlq input value', async () => { - httpMock.post.mockResolvedValueOnce('source = index'); - - const { component, props } = renderQueryAssistInput(); - - await waitFor(() => { - fireEvent.click(component.getByTestId('query-assist-generate-and-run-button')); - }); - - expect(httpMock.post).toBeCalledWith(QUERY_ASSIST_API.GENERATE_PPL, { - body: '{"question":"test-input","index":"selected-test-index"}', - }); - expect(props.handleQueryChange).toBeCalledWith('source = index'); - }); - - it('should display toast for generate errors', async () => { - httpMock.post.mockRejectedValueOnce({ body: { statusCode: 429 } }); - - const { component } = renderQueryAssistInput(); - await waitFor(() => { - fireEvent.click(component.getByTestId('query-assist-generate-button')); - }); - - expect(coreRefs.toasts!.addError).toBeCalledWith( - { - message: 'Request is throttled. Try again later or contact your administrator', - statusCode: 429, - }, - { title: 'Failed to generate results' } - ); - }); - - it('should call summarize for generate and run errors', async () => { - httpMock.post.mockRejectedValueOnce({ body: { statusCode: 429 } }).mockResolvedValueOnce({ - summary: 'too many requests', - suggestedQuestions: ['1', '2'], - }); - - const { component } = renderQueryAssistInput(); - await waitFor(() => { - fireEvent.click(component.getByTestId('query-assist-generate-and-run-button')); - }); - - expect(httpMock.post).toBeCalledWith(QUERY_ASSIST_API.GENERATE_PPL, { - body: '{"question":"test-input","index":"selected-test-index"}', - }); - expect(httpMock.post).toBeCalledWith(QUERY_ASSIST_API.SUMMARIZE, { - body: - '{"question":"test-input","index":"selected-test-index","isError":true,"query":"","response":"{\\"statusCode\\":429}"}', - }); - }); -}); diff --git a/public/components/event_analytics/explorer/query_assist/hooks.ts b/public/components/event_analytics/explorer/query_assist/hooks.ts deleted file mode 100644 index 281f659bac..0000000000 --- a/public/components/event_analytics/explorer/query_assist/hooks.ts +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright OpenSearch Contributors - * SPDX-License-Identifier: Apache-2.0 - */ - -import { EuiComboBoxOptionOption } from '@elastic/eui'; -import { CatIndicesResponse } from '@opensearch-project/opensearch/api/types'; -import { Reducer, useReducer, useState, useEffect } from 'react'; -import { IndexPatternAttributes } from '../../../../../../../src/plugins/data/common'; -import { DSL_BASE, DSL_CAT } from '../../../../../common/constants/shared'; -import { getOSDHttp } from '../../../../../common/utils'; -import { coreRefs } from '../../../../framework/core_refs'; - -interface State { - data?: T; - loading: boolean; - error?: Error; -} - -type Action = - | { type: 'request' } - | { type: 'success'; payload: State['data'] } - | { type: 'failure'; error: NonNullable['error']> }; - -// TODO use instantiation expressions when typescript is upgraded to >= 4.7 -type GenericReducer = Reducer, Action>; -export const genericReducer: GenericReducer = (state, action) => { - switch (action.type) { - case 'request': - return { data: state.data, loading: true }; - case 'success': - return { loading: false, data: action.payload }; - case 'failure': - return { loading: false, error: action.error }; - default: - return state; - } -}; - -export const useCatIndices = () => { - const reducer: GenericReducer = genericReducer; - const [state, dispatch] = useReducer(reducer, { loading: false }); - const [refresh, setRefresh] = useState({}); - - useEffect(() => { - const abortController = new AbortController(); - dispatch({ type: 'request' }); - getOSDHttp() - .get(`${DSL_BASE}${DSL_CAT}`, { query: { format: 'json' }, signal: abortController.signal }) - .then((payload: CatIndicesResponse) => - dispatch({ type: 'success', payload: payload.map((meta) => ({ label: meta.index! })) }) - ) - .catch((error) => dispatch({ type: 'failure', error })); - - return () => abortController.abort(); - }, [refresh]); - - return { ...state, refresh: () => setRefresh({}) }; -}; - -export const useGetIndexPatterns = () => { - const reducer: GenericReducer = genericReducer; - const [state, dispatch] = useReducer(reducer, { loading: false }); - const [refresh, setRefresh] = useState({}); - - useEffect(() => { - let abort = false; - dispatch({ type: 'request' }); - - coreRefs - .savedObjectsClient!.find({ type: 'index-pattern', perPage: 10000 }) - .then((payload) => { - if (!abort) - dispatch({ - type: 'success', - payload: payload.savedObjects.map((meta) => ({ label: meta.attributes.title })), - }); - }) - .catch((error) => { - if (!abort) dispatch({ type: 'failure', error }); - }); - - return () => { - abort = true; - }; - }, [refresh]); - - return { ...state, refresh: () => setRefresh({}) }; -}; diff --git a/public/components/event_analytics/explorer/query_assist/input.tsx b/public/components/event_analytics/explorer/query_assist/input.tsx deleted file mode 100644 index 58008819d7..0000000000 --- a/public/components/event_analytics/explorer/query_assist/input.tsx +++ /dev/null @@ -1,367 +0,0 @@ -/* - * Copyright OpenSearch Contributors - * SPDX-License-Identifier: Apache-2.0 - */ - -import { - EuiBadge, - EuiButton, - EuiComboBoxOptionOption, - EuiFieldText, - EuiFlexGroup, - EuiFlexItem, - EuiForm, - EuiIcon, - EuiInputPopover, - EuiLink, - EuiListGroup, - EuiListGroupItem, - EuiPanel, - EuiText, -} from '@elastic/eui'; -import { ResponseError } from '@opensearch-project/opensearch/lib/errors'; -import React, { useEffect, useState } from 'react'; -import { useDispatch, useSelector } from 'react-redux'; -import { RAW_QUERY } from '../../../../../common/constants/explorer'; -import { getOSDHttp } from '../../../../../common/utils'; -import { coreRefs } from '../../../../framework/core_refs'; -import chatLogo from '../../../datasources/icons/query-assistant-logo.svg'; -import { - changeSummary, - resetSummary, - selectQueryAssistantSummarization, - setResponseForSummaryStatus, -} from '../../redux/slices/query_assistant_summarization_slice'; -import { reset, selectQueryResult } from '../../redux/slices/query_result_slice'; -import { changeQuery, selectQueries } from '../../redux/slices/query_slice'; -import { QUERY_ASSIST_API } from '../../../../../common/constants/query_assist'; - -interface SummarizationContext { - question: string; - query?: string; - response: string; - index: string; - isError: boolean; -} - -interface Props { - handleQueryChange: (query: string) => void; - handleTimeRangePickerRefresh: (availability?: boolean, setSummaryStatus?: boolean) => void; - tabId: string; - setNeedsUpdate: any; - selectedIndex: Array>; - nlqInput: string; - setNlqInput: React.Dispatch>; -} - -const HARDCODED_SUGGESTIONS: Record = { - opensearch_dashboards_sample_data_ecommerce: [ - 'How many unique customers placed orders this week?', - 'Count the number of orders grouped by manufacturer and category', - 'find customers with first names like Eddie', - ], - opensearch_dashboards_sample_data_logs: [ - 'Are there any errors in my logs?', - 'How many requests were there grouped by response code last week?', - "What's the average request size by week?", - ], - opensearch_dashboards_sample_data_flights: [ - 'how many flights were there this week grouped by destination country?', - 'what were the longest flight delays this week?', - 'what carriers have the furthest flights?', - ], - 'sso_logs-*.*': [ - 'show me the most recent 10 logs', - 'how many requests were there grouped by status code', - 'how many request failures were there by week?', - ], -}; - -export const QueryAssistInput: React.FC = (props) => { - // @ts-ignore - const queryRedux = useSelector(selectQueries)[props.tabId]; - // @ts-ignore - const explorerData = useSelector(selectQueryResult)[props.tabId]; - // @ts-ignore - const summaryData = useSelector(selectQueryAssistantSummarization)[props.tabId]; - - useEffect(() => { - if ( - summaryData.responseForSummaryStatus === 'success' || - summaryData.responseForSummaryStatus === 'failure' - ) { - void (async () => { - await dispatch( - changeSummary({ - tabId: props.tabId, - data: { - summaryLoading: false, - }, - }) - ); - if (explorerData.total > 0) generateSummary(); - })(); - } - }, [summaryData.responseForSummaryStatus]); - - const [barSelected, setBarSelected] = useState(false); - - const dispatch = useDispatch(); - - const [isPopoverOpen, setIsPopoverOpen] = useState(false); - const [generating, setGenerating] = useState(false); - const [generatingOrRunning, setGeneratingOrRunning] = useState(false); - // below is only used for url redirection - const [autoRun, setAutoRun] = useState(false); - - useEffect(() => { - if (autoRun) { - setAutoRun(false); - runAndSummarize(); - } else if (queryRedux.ollyQueryAssistant.length > 0) { - setAutoRun(true); - } - }, [queryRedux.ollyQueryAssistant]); - - // hide if not in a tab - if (props.tabId === '') return <>{props.children}; - - // generic method for generating ppl from natural language - const request = async () => { - const generatedPPL = await getOSDHttp().post(QUERY_ASSIST_API.GENERATE_PPL, { - body: JSON.stringify({ - question: props.nlqInput, - index: props.selectedIndex[0].label, - }), - }); - await props.handleQueryChange(generatedPPL); - await dispatch( - changeQuery({ - tabId: props.tabId, - query: { - [RAW_QUERY]: generatedPPL, - }, - }) - ); - return generatedPPL; - }; - const formatError = (error: ResponseError): Error => { - if (error.body) { - if (error.body.statusCode === 429) - return { - ...error.body, - message: 'Request is throttled. Try again later or contact your administrator', - } as Error; - return error.body as Error; - } - return error; - }; - // used by generate query button - const generatePPL = async () => { - dispatch(reset({ tabId: props.tabId })); - dispatch(resetSummary({ tabId: props.tabId })); - if (!props.selectedIndex.length) return; - try { - setGenerating(true); - await request(); - } catch (error) { - coreRefs.toasts?.addError(formatError(error as ResponseError), { - title: 'Failed to generate results', - }); - } finally { - setGenerating(false); - } - }; - const generateSummary = async (context?: Partial) => { - try { - const isError = summaryData.responseForSummaryStatus === 'failure'; - const summarizationContext: SummarizationContext = { - question: props.nlqInput, - index: props.selectedIndex[0].label, - isError, - query: queryRedux.rawQuery, - response: isError - ? String(JSON.parse(explorerData.error.body.message).error.details) - : JSON.stringify({ - datarows: explorerData.datarows, - schema: explorerData.schema, - size: explorerData.size, - total: explorerData.total, - }).slice(0, 7000), - ...context, - }; - await dispatch( - changeSummary({ - tabId: props.tabId, - data: { - summaryLoading: true, - isPPLError: isError, - }, - }) - ); - const summary = await getOSDHttp().post<{ - summary: string; - suggestedQuestions: string[]; - }>(QUERY_ASSIST_API.SUMMARIZE, { - body: JSON.stringify(summarizationContext), - }); - await dispatch( - changeSummary({ - tabId: props.tabId, - data: { - summary: summary.summary, - suggestedQuestions: summary.suggestedQuestions, - }, - }) - ); - } catch (error) { - coreRefs.toasts?.addError(formatError(error as ResponseError), { - title: 'Failed to summarize results', - }); - } finally { - await dispatch( - changeSummary({ - tabId: props.tabId, - data: { - summaryLoading: false, - }, - }) - ); - dispatch( - setResponseForSummaryStatus({ - tabId: props.tabId, - responseForSummaryStatus: 'false', - }) - ); - } - }; - // used by generate and run button - const runAndSummarize = async () => { - dispatch(reset({ tabId: props.tabId })); - dispatch(resetSummary({ tabId: props.tabId })); - if (!props.selectedIndex.length) return; - try { - setGeneratingOrRunning(true); - await request(); - await props.handleTimeRangePickerRefresh(undefined, true); - } catch (error) { - generateSummary({ isError: true, response: JSON.stringify((error as ResponseError).body) }); - } finally { - setGeneratingOrRunning(false); - } - }; - - return ( - <> - - { - e.preventDefault(); - request(); - }} - > - - - - - - - - Query Assistant - - - New! - - - props.setNlqInput(e.target.value)} - fullWidth - onFocus={() => { - setBarSelected(true); - props.setNeedsUpdate(false); - if (props.nlqInput.length === 0) setIsPopoverOpen(true); - }} - onBlur={() => setBarSelected(false)} - /> - } - disableFocusTrap - fullWidth={true} - isOpen={isPopoverOpen} - closePopover={() => { - setIsPopoverOpen(false); - }} - > - - {HARDCODED_SUGGESTIONS[props.selectedIndex[0]?.label]?.map((question) => ( - { - props.setNlqInput(question); - setIsPopoverOpen(false); - }} - label={question} - /> - ))} - - - - - - - - - - - Share feedback via{' '} - - Forum - {' '} - or{' '} - - Slack - - - - - - - Generate query - - - - - Generate and run - - - - - - - - - ); -}; diff --git a/public/components/event_analytics/explorer/sidebar/__tests__/__snapshots__/sidebar.test.tsx.snap b/public/components/event_analytics/explorer/sidebar/__tests__/__snapshots__/sidebar.test.tsx.snap index aff341c85c..59d4966c25 100644 --- a/public/components/event_analytics/explorer/sidebar/__tests__/__snapshots__/sidebar.test.tsx.snap +++ b/public/components/event_analytics/explorer/sidebar/__tests__/__snapshots__/sidebar.test.tsx.snap @@ -22,15 +22,10 @@ exports[`Siderbar component Renders empty sidebar component 1`] = ` "unselectedFields": Array [], } } - handleOverridePattern={[Function]} handleOverrideTimestamp={[MockFunction]} isFieldToggleButtonDisabled={false} - isOverridingPattern={false} isOverridingTimestamp={false} - query="" - selectedPattern="" selectedTimestamp="timestamp" - tabId="OBSERVABILITY_DEFAULT_TAB" > @@ -1222,14 +1323,14 @@ exports[`Siderbar component Renders sidebar component 1`] = ` aria-label="inspect" className="dscSidebarField__actionButton" iconType="inspect" - isDisabled={false} + isDisabled={true} onClick={[Function]} size="xs" >