diff --git a/src/plugins/data/common/index_patterns/index_patterns/ensure_default_index_pattern.ts b/src/plugins/data/common/index_patterns/index_patterns/ensure_default_index_pattern.ts index 9fed0e1d0519..94df6995186a 100644 --- a/src/plugins/data/common/index_patterns/index_patterns/ensure_default_index_pattern.ts +++ b/src/plugins/data/common/index_patterns/index_patterns/ensure_default_index_pattern.ts @@ -32,9 +32,7 @@ import { includes } from 'lodash'; import { IndexPatternsContract } from './index_patterns'; import { UiSettingsCommon } from '../types'; -export type EnsureDefaultIndexPattern = ( - shouldRedirect?: boolean -) => Promise | undefined; +export type EnsureDefaultIndexPattern = () => Promise | undefined; export const createEnsureDefaultIndexPattern = ( uiSettings: UiSettingsCommon, @@ -44,10 +42,7 @@ export const createEnsureDefaultIndexPattern = ( * Checks whether a default index pattern is set and exists and defines * one otherwise. */ - return async function ensureDefaultIndexPattern( - this: IndexPatternsContract, - shouldRedirect: boolean = true - ) { + return async function ensureDefaultIndexPattern(this: IndexPatternsContract) { const patterns = await this.getIds(); let defaultId = await uiSettings.get('defaultIndex'); let defined = !!defaultId; @@ -67,6 +62,8 @@ export const createEnsureDefaultIndexPattern = ( defaultId = patterns[0]; await uiSettings.set('defaultIndex', defaultId); } else { + const isEnhancementsEnabled = await uiSettings.get('query:enhancements:enabled'); + const shouldRedirect = !isEnhancementsEnabled; if (shouldRedirect) return onRedirectNoIndexPattern(); else return; } diff --git a/src/plugins/data/public/query/query_string/language_service/lib/dql_language.ts b/src/plugins/data/public/query/query_string/language_service/lib/dql_language.ts index 6816bf0d7121..402af7482d08 100644 --- a/src/plugins/data/public/query/query_string/language_service/lib/dql_language.ts +++ b/src/plugins/data/public/query/query_string/language_service/lib/dql_language.ts @@ -24,6 +24,12 @@ export const getDQLLanguageConfig = ( visualizable: true, }, showDocLinks: true, + docLink: { + title: i18n.translate('data.dqlLanguage.docLink', { + defaultMessage: 'DQL documentation', + }), + url: 'https://opensearch.org/docs/latest/query-dsl/full-text/query-string/', + }, editorSupportedAppNames: ['discover'], supportedAppNames: ['discover', 'dashboards', 'visualize', 'data-explorer', 'vis-builder', '*'], sampleQueries: [ diff --git a/src/plugins/data/public/query/query_string/language_service/lib/lucene_language.ts b/src/plugins/data/public/query/query_string/language_service/lib/lucene_language.ts index c42b14543633..4da92f868654 100644 --- a/src/plugins/data/public/query/query_string/language_service/lib/lucene_language.ts +++ b/src/plugins/data/public/query/query_string/language_service/lib/lucene_language.ts @@ -24,6 +24,12 @@ export const getLuceneLanguageConfig = ( visualizable: true, }, showDocLinks: true, + docLink: { + title: i18n.translate('data.luceneLanguage.docLink', { + defaultMessage: 'Lucene documentation', + }), + url: 'https://opensearch.org/docs/latest/query-dsl/full-text/query-string/', + }, editorSupportedAppNames: ['discover'], supportedAppNames: ['discover', 'dashboards', 'visualize', 'data-explorer', 'vis-builder', '*'], sampleQueries: [ diff --git a/src/plugins/data/public/query/query_string/language_service/types.ts b/src/plugins/data/public/query/query_string/language_service/types.ts index 0889f7e63950..c80858d67102 100644 --- a/src/plugins/data/public/query/query_string/language_service/types.ts +++ b/src/plugins/data/public/query/query_string/language_service/types.ts @@ -55,6 +55,10 @@ export interface LanguageConfig { visualizable?: boolean; }; showDocLinks?: boolean; + docLink?: { + title: string; + url: string; + }; editorSupportedAppNames?: string[]; supportedAppNames?: string[]; hideDatePicker?: boolean; diff --git a/src/plugins/data/public/ui/_index.scss b/src/plugins/data/public/ui/_index.scss index cdbe539a2e19..9ab4ca672b38 100644 --- a/src/plugins/data/public/ui/_index.scss +++ b/src/plugins/data/public/ui/_index.scss @@ -1,4 +1,3 @@ -@import "./common"; @import "./filter_bar/index"; @import "./typeahead/index"; @import "./saved_query_management/index"; diff --git a/src/plugins/data/public/ui/dataset_selector/advanced_selector.tsx b/src/plugins/data/public/ui/dataset_selector/advanced_selector.tsx index 25434062de8e..734153452eea 100644 --- a/src/plugins/data/public/ui/dataset_selector/advanced_selector.tsx +++ b/src/plugins/data/public/ui/dataset_selector/advanced_selector.tsx @@ -13,27 +13,19 @@ import { } from '../../../common'; import { DatasetExplorer } from './dataset_explorer'; import { Configurator } from './configurator'; +import { getQueryService } from '../../services'; import { IDataPluginServices } from '../../types'; export const AdvancedSelector = ({ services, onSelect, onCancel, - selectedDataset, - setSelectedDataset, - setIndexPattern, - direct = false, }: { services: IDataPluginServices; onSelect: (dataset: Dataset) => void; onCancel: () => void; - selectedDataset?: Dataset; - setSelectedDataset: (data: Dataset | undefined) => void; - setIndexPattern: (id: string | undefined) => void; - direct?: boolean; }) => { - const queryService = services.data.query; - const queryString = queryService.queryString; + const queryString = getQueryService().queryString; const [path, setPath] = useState([ { @@ -56,21 +48,14 @@ export const AdvancedSelector = ({ }), }, ]); + const [selectedDataset, setSelectedDataset] = useState(); - const [currentSelectedDataset, setCurrentSelectedDataset] = useState( - selectedDataset - ); - - return currentSelectedDataset ? ( + return selectedDataset ? ( { - setSelectedDataset(undefined); - setCurrentSelectedDataset(undefined); - }} - queryService={queryService} + onPrevious={() => setSelectedDataset(undefined)} /> ) : ( { - setSelectedDataset(dataset); - setIndexPattern(dataset.id); - setCurrentSelectedDataset(dataset); - if (direct) { - const query = queryString.getInitialQueryByDataset(dataset); - queryString.setQuery(query); - queryString.getDatasetService().addRecentDataset(dataset); - } - }} + onNext={(dataset) => setSelectedDataset(dataset)} onCancel={onCancel} /> ); diff --git a/src/plugins/data/public/ui/dataset_selector/configurator.tsx b/src/plugins/data/public/ui/dataset_selector/configurator.tsx index b8a74a9353e0..db1cb80dd6e3 100644 --- a/src/plugins/data/public/ui/dataset_selector/configurator.tsx +++ b/src/plugins/data/public/ui/dataset_selector/configurator.tsx @@ -20,21 +20,20 @@ import { i18n } from '@osd/i18n'; import { FormattedMessage } from '@osd/i18n/react'; import React, { useEffect, useMemo, useState } from 'react'; import { BaseDataset, DEFAULT_DATA, Dataset, DatasetField } from '../../../common'; -import { getIndexPatterns } from '../../services'; +import { getIndexPatterns, getQueryService } from '../../services'; export const Configurator = ({ baseDataset, onConfirm, onCancel, onPrevious, - queryService, }: { baseDataset: BaseDataset; onConfirm: (dataset: Dataset) => void; onCancel: () => void; onPrevious: () => void; - queryService: any; }) => { + const queryService = getQueryService(); const queryString = queryService.queryString; const languageService = queryService.queryString.getLanguageService(); const indexPatternsService = getIndexPatterns(); diff --git a/src/plugins/data/public/ui/dataset_selector/dataset_selector.tsx b/src/plugins/data/public/ui/dataset_selector/dataset_selector.tsx index 6755645020d1..691f477a5818 100644 --- a/src/plugins/data/public/ui/dataset_selector/dataset_selector.tsx +++ b/src/plugins/data/public/ui/dataset_selector/dataset_selector.tsx @@ -33,9 +33,7 @@ type EuiSmallButtonEmptyProps = React.ComponentProps interface DatasetSelectorProps { selectedDataset?: Dataset; - setSelectedDataset: (data: Dataset | undefined) => void; - setIndexPattern: (id: string | undefined) => void; - handleDatasetChange: (dataset: Dataset) => void; + setSelectedDataset: (dataset: Dataset) => void; services: IDataPluginServices; } @@ -73,8 +71,6 @@ const RootComponent: React.FC< export const DatasetSelector = ({ selectedDataset, setSelectedDataset, - setIndexPattern, - handleDatasetChange, services, appearance, buttonProps, @@ -106,7 +102,7 @@ export const DatasetSelector = ({ // If no dataset is selected, select the first one if (!selectedDataset && fetchedDatasets.length > 0) { - handleDatasetChange(fetchedDatasets[0]); + setSelectedDataset(fetchedDatasets[0]); } }; @@ -183,11 +179,11 @@ export const DatasetSelector = ({ indexPatterns.find((dataset) => dataset.id === selectedOption.key); if (foundDataset) { closePopover(); - handleDatasetChange(foundDataset); + setSelectedDataset(foundDataset); } } }, - [recentDatasets, indexPatterns, handleDatasetChange, closePopover] + [recentDatasets, indexPatterns, setSelectedDataset, closePopover] ); const datasetTitle = useMemo(() => { @@ -270,14 +266,10 @@ export const DatasetSelector = ({ onSelect={(dataset?: Dataset) => { overlay?.close(); if (dataset) { - handleDatasetChange(dataset); + setSelectedDataset(dataset); } }} onCancel={() => overlay?.close()} - selectedDataset={undefined} - setSelectedDataset={setSelectedDataset} - setIndexPattern={setIndexPattern} - direct={true} /> ), { diff --git a/src/plugins/data/public/ui/dataset_selector/index.test.tsx b/src/plugins/data/public/ui/dataset_selector/index.test.tsx index 9e486beb5310..77528eaea532 100644 --- a/src/plugins/data/public/ui/dataset_selector/index.test.tsx +++ b/src/plugins/data/public/ui/dataset_selector/index.test.tsx @@ -5,7 +5,6 @@ import React from 'react'; import { mount } from 'enzyme'; -import { act } from 'react-dom/test-utils'; import { DatasetSelector as ConnectedDatasetSelector } from './index'; import { DatasetSelector } from './dataset_selector'; import { useOpenSearchDashboards } from '../../../../opensearch_dashboards_react/public'; @@ -38,6 +37,7 @@ describe('ConnectedDatasetSelector', () => { const mockServices = { data: { query: { + // @ts-ignore queryString: mockQueryString, }, }, @@ -45,87 +45,36 @@ describe('ConnectedDatasetSelector', () => { beforeEach(() => { (useOpenSearchDashboards as jest.Mock).mockReturnValue({ services: mockServices }); - jest.clearAllMocks(); }); it('should render DatasetSelector with correct props', () => { - const wrapper = mount( - - ); + const wrapper = mount(); expect(wrapper.find(DatasetSelector).props()).toEqual({ selectedDataset: undefined, setSelectedDataset: expect.any(Function), - setIndexPattern: expect.any(Function), - handleDatasetChange: expect.any(Function), services: mockServices, }); }); it('should initialize selectedDataset correctly', () => { const mockDataset: Dataset = { id: 'initial', title: 'Initial Dataset', type: 'test' }; + mockQueryString.getQuery.mockReturnValueOnce({ dataset: mockDataset }); - const wrapper = mount( - - ); + const wrapper = mount(); expect(wrapper.find(DatasetSelector).prop('selectedDataset')).toEqual(mockDataset); }); it('should call handleDatasetChange only once when dataset changes', () => { - const setSelectedDataset = jest.fn(); - const setIndexPattern = jest.fn(); - const wrapper = mount( - - ); - const handleDatasetChange = wrapper.find(DatasetSelector).prop('handleDatasetChange') as ( + const wrapper = mount(); + const setSelectedDataset = wrapper.find(DatasetSelector).prop('setSelectedDataset') as ( dataset?: Dataset ) => void; const newDataset: Dataset = { id: 'test', title: 'Test Dataset', type: 'test' }; - act(() => { - handleDatasetChange(newDataset); - }); + setSelectedDataset(newDataset); expect(mockQueryString.getInitialQueryByDataset).toHaveBeenCalledTimes(1); expect(mockQueryString.setQuery).toHaveBeenCalledTimes(1); expect(mockOnSubmit).toHaveBeenCalledTimes(1); - expect(setSelectedDataset).toHaveBeenCalledWith(newDataset); - expect(setIndexPattern).toHaveBeenCalledWith(newDataset.id); - }); - - it('should subscribe to queryString.getUpdates$ and unsubscribe on unmount', () => { - const wrapper = mount( - - ); - - expect(mockQueryString.getUpdates$).toHaveBeenCalledTimes(1); - expect(mockSubscribe).toHaveBeenCalledTimes(1); - - wrapper.unmount(); - - expect(mockUnsubscribe).toHaveBeenCalledTimes(1); }); }); diff --git a/src/plugins/data/public/ui/dataset_selector/index.tsx b/src/plugins/data/public/ui/dataset_selector/index.tsx index 48cd2926de22..53966ed6375f 100644 --- a/src/plugins/data/public/ui/dataset_selector/index.tsx +++ b/src/plugins/data/public/ui/dataset_selector/index.tsx @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { useCallback, useEffect } from 'react'; +import { useCallback, useState, useEffect } from 'react'; import React from 'react'; import { Dataset, Query, TimeRange } from '../../../common'; import { @@ -12,42 +12,37 @@ import { DatasetSelectorUsingButtonProps, DatasetSelectorAppearance, } from './dataset_selector'; -import { AdvancedSelector } from './advanced_selector'; +import { useOpenSearchDashboards } from '../../../../opensearch_dashboards_react/public'; +import { IDataPluginServices } from '../../types'; interface ConnectedDatasetSelectorProps { onSubmit: ((query: Query, dateRange?: TimeRange | undefined) => void) | undefined; - selectedDataset?: Dataset; - setSelectedDataset: (data: Dataset | undefined) => void; - setIndexPattern: (id: string | undefined) => void; - services?: any; } const ConnectedDatasetSelector = ({ onSubmit, - selectedDataset, - setSelectedDataset, - setIndexPattern, - services, ...datasetSelectorProps }: ConnectedDatasetSelectorProps & (DatasetSelectorUsingButtonProps | DatasetSelectorUsingButtonEmptyProps)) => { + const { services } = useOpenSearchDashboards(); const queryString = services.data.query.queryString; + const [selectedDataset, setSelectedDataset] = useState( + () => queryString.getQuery().dataset || queryString.getDefaultQuery().dataset + ); useEffect(() => { const subscription = queryString.getUpdates$().subscribe((query) => { setSelectedDataset(query.dataset); - setIndexPattern(query.dataset?.id); }); return () => { subscription.unsubscribe(); }; - }, [queryString, setSelectedDataset, setIndexPattern]); + }, [queryString]); const handleDatasetChange = useCallback( (dataset?: Dataset) => { setSelectedDataset(dataset); - setIndexPattern(dataset?.id); if (dataset) { const query = queryString.getInitialQueryByDataset(dataset); queryString.setQuery(query); @@ -55,19 +50,21 @@ const ConnectedDatasetSelector = ({ queryString.getDatasetService().addRecentDataset(dataset); } }, - [onSubmit, queryString, setSelectedDataset, setIndexPattern] + [onSubmit, queryString] ); return ( ); }; -export { ConnectedDatasetSelector as DatasetSelector, AdvancedSelector, DatasetSelectorAppearance }; +export { + ConnectedDatasetSelector as DatasetSelector, + ConnectedDatasetSelectorProps as DatasetSelectorProps, + DatasetSelectorAppearance, +}; diff --git a/src/plugins/data/public/ui/index.ts b/src/plugins/data/public/ui/index.ts index 00ac361bb26e..bdde4cc42a39 100644 --- a/src/plugins/data/public/ui/index.ts +++ b/src/plugins/data/public/ui/index.ts @@ -28,7 +28,7 @@ * under the License. */ -export { UiEnhancements, IUiStart, IUiSetup } from './types'; +export { IUiStart, IUiSetup } from './types'; export { IndexPatternSelectProps } from './index_pattern_select'; export { FilterLabel } from './filter_bar'; export { QueryStringInput, QueryStringInputProps } from './query_string_input'; @@ -51,5 +51,4 @@ export { useQueryStringManager, } from './search_bar'; export { SuggestionsComponent } from './typeahead'; -export { DatasetSelector, AdvancedSelector, DatasetSelectorAppearance } from './dataset_selector'; -export { NoIndexPatternsPanel } from './no_index_patterns'; +export { DatasetSelector, DatasetSelectorAppearance } from './dataset_selector'; diff --git a/src/plugins/data/public/ui/no_index_patterns/index.ts b/src/plugins/data/public/ui/no_index_patterns/index.ts deleted file mode 100644 index 4f7a76b5baa2..000000000000 --- a/src/plugins/data/public/ui/no_index_patterns/index.ts +++ /dev/null @@ -1,6 +0,0 @@ -/* - * Copyright OpenSearch Contributors - * SPDX-License-Identifier: Apache-2.0 - */ - -export * from './no_index_patterns_panel'; diff --git a/src/plugins/data/public/ui/no_index_patterns/no_index_patterns_panel.tsx b/src/plugins/data/public/ui/no_index_patterns/no_index_patterns_panel.tsx deleted file mode 100644 index 6dd7d2a8614c..000000000000 --- a/src/plugins/data/public/ui/no_index_patterns/no_index_patterns_panel.tsx +++ /dev/null @@ -1,145 +0,0 @@ -/* - * Copyright OpenSearch Contributors - * SPDX-License-Identifier: Apache-2.0 - */ - -import React from 'react'; -import { i18n } from '@osd/i18n'; -import { - EuiPanel, - EuiFlexGroup, - EuiFlexItem, - EuiIcon, - EuiText, - EuiSmallButton, - EuiSpacer, - EuiTitle, - EuiButtonEmpty, -} from '@elastic/eui'; - -interface NoIndexPatternsPanelProps { - onOpenDataSelector: () => void; -} - -export const NoIndexPatternsPanel: React.FC = ({ - onOpenDataSelector, -}) => ( - - - - - - - - - -

- {i18n.translate('data.noIndexPatterns.selectDataTitle', { - defaultMessage: 'Select data', - })} -

-
-
- - - {i18n.translate('data.noIndexPatterns.selectDataDescription', { - defaultMessage: - 'Select an available data source and choose a query language to use for running queries. You can use the data dropdown or use the enhanced data selector to select data.', - })} - - - - - {i18n.translate('data.noIndexPatterns.openDataSelectorButton', { - defaultMessage: 'Open data selector', - })} - - - - - -

- {i18n.translate('data.noIndexPatterns.learnMoreAboutQueryLanguages', { - defaultMessage: 'Learn more about query languages', - })} -

-
-
- - - - - - {i18n.translate('data.noIndexPatterns.pplDocumentation', { - defaultMessage: 'PPL documentation', - })} - - - - - - - {i18n.translate('data.noIndexPatterns.sqlDocumentation', { - defaultMessage: 'SQL documentation', - })} - - - - - - - {i18n.translate('data.noIndexPatterns.luceneDocumentation', { - defaultMessage: 'Lucene documentation', - })} - - - - - - - {i18n.translate('data.noIndexPatterns.dqlDocumentation', { - defaultMessage: 'DQL documentation', - })} - - - - - -
-
-
-
-); diff --git a/src/plugins/data/public/ui/query_editor/query_editor_top_row.tsx b/src/plugins/data/public/ui/query_editor/query_editor_top_row.tsx index 62ba24527cd5..ab9b8c50e038 100644 --- a/src/plugins/data/public/ui/query_editor/query_editor_top_row.tsx +++ b/src/plugins/data/public/ui/query_editor/query_editor_top_row.tsx @@ -15,7 +15,15 @@ import { import classNames from 'classnames'; import React, { useState } from 'react'; import { createPortal } from 'react-dom'; -import { IDataPluginServices, IIndexPattern, Query, TimeHistoryContract, TimeRange } from '../..'; +import { + DatasetSelector, + DatasetSelectorAppearance, + IDataPluginServices, + IIndexPattern, + Query, + TimeHistoryContract, + TimeRange, +} from '../..'; import { useOpenSearchDashboards, withOpenSearchDashboards, @@ -52,6 +60,7 @@ export interface QueryEditorTopRowProps { isDirty: boolean; timeHistory?: TimeHistoryContract; indicateNoData?: boolean; + datasetSelectorRef?: React.RefObject; datePickerRef?: React.RefObject; savedQueryManagement?: any; queryStatus?: QueryStatus; @@ -63,14 +72,7 @@ export default function QueryEditorTopRow(props: QueryEditorTopRowProps) { const [isDateRangeInvalid, setIsDateRangeInvalid] = useState(false); const [isQueryEditorFocused, setIsQueryEditorFocused] = useState(false); const opensearchDashboards = useOpenSearchDashboards(); - const { - uiSettings, - storage, - appName, - data: { - query: { queryString }, - }, - } = opensearchDashboards.services; + const { uiSettings, storage, appName } = opensearchDashboards.services; const queryLanguage = props.query && props.query.language; const persistedLog: PersistedLog | undefined = React.useMemo( @@ -170,6 +172,19 @@ export default function QueryEditorTopRow(props: QueryEditorTopRowProps) { return valueAsMoment.toISOString(); } + function renderDatasetSelector() { + return ( + + ); + } + function renderQueryEditor() { if (!shouldRenderQueryEditor()) return; return ( @@ -315,6 +330,8 @@ export default function QueryEditorTopRow(props: QueryEditorTopRowProps) { ); + const datasetSelector = <>{renderDatasetSelector()}; + return ( + {props?.datasetSelectorRef?.current && + createPortal(datasetSelector, props.datasetSelectorRef.current)} {props?.datePickerRef?.current && uiSettings.get(UI_SETTINGS.QUERY_ENHANCEMENTS_ENABLED) ? createPortal(datePicker, props.datePickerRef.current) : datePicker} diff --git a/src/plugins/data/public/ui/search_bar/create_search_bar.tsx b/src/plugins/data/public/ui/search_bar/create_search_bar.tsx index 03bb2abfa508..f8b9694caabc 100644 --- a/src/plugins/data/public/ui/search_bar/create_search_bar.tsx +++ b/src/plugins/data/public/ui/search_bar/create_search_bar.tsx @@ -209,6 +209,7 @@ export function createSearchBar({ core, storage, data }: StatefulSearchBarDeps) onClearSavedQuery={defaultOnClearSavedQuery(props, clearSavedQuery)} onSavedQueryUpdated={defaultOnSavedQueryUpdated(props, setSavedQuery)} onSaved={defaultOnSavedQueryUpdated(props, setSavedQuery)} + datasetSelectorRef={props.datasetSelectorRef} datePickerRef={props.datePickerRef} isFilterBarPortable={props.isFilterBarPortable} {...overrideDefaultBehaviors(props)} diff --git a/src/plugins/data/public/ui/search_bar/search_bar.tsx b/src/plugins/data/public/ui/search_bar/search_bar.tsx index 975b0535c9d8..28f39bf7a23f 100644 --- a/src/plugins/data/public/ui/search_bar/search_bar.tsx +++ b/src/plugins/data/public/ui/search_bar/search_bar.tsx @@ -75,6 +75,7 @@ export interface SearchBarOwnProps { refreshInterval?: number; dateRangeFrom?: string; dateRangeTo?: string; + datasetSelectorRef?: React.RefObject; datePickerRef?: React.RefObject; // Query bar - should be in SearchBarInjectedDeps query?: Query; @@ -549,6 +550,7 @@ class SearchBarUI extends Component { filterBar={filterBar} dataTestSubj={this.props.dataTestSubj} indicateNoData={this.props.indicateNoData} + datasetSelectorRef={this.props.datasetSelectorRef} datePickerRef={this.props.datePickerRef} savedQueryManagement={searchBarMenu(false, true)} queryStatus={this.props.queryStatus} diff --git a/src/plugins/data/public/ui/ui_service.ts b/src/plugins/data/public/ui/ui_service.ts index 4eb45b1a67f2..87cfcf630965 100644 --- a/src/plugins/data/public/ui/ui_service.ts +++ b/src/plugins/data/public/ui/ui_service.ts @@ -4,7 +4,6 @@ */ import { CoreSetup, CoreStart, Plugin, PluginInitializerContext } from 'src/core/public'; -import { BehaviorSubject } from 'rxjs'; import { ConfigSchema } from '../../config'; import { DataPublicPluginStart } from '../types'; import { createIndexPatternSelect } from './index_pattern_select'; @@ -12,8 +11,6 @@ import { createSearchBar } from './search_bar/create_search_bar'; import { SuggestionsComponent } from './typeahead'; import { IUiSetup, IUiStart } from './types'; import { DataStorage } from '../../common'; -import { QueryStatus } from '../query'; -import { ResultStatus } from '../query/query_string/language_service/lib'; /** @internal */ // eslint-disable-next-line @typescript-eslint/no-empty-interface diff --git a/src/plugins/data_explorer/public/components/app_container.tsx b/src/plugins/data_explorer/public/components/app_container.tsx index abd1328bafeb..a1fa4a2ce381 100644 --- a/src/plugins/data_explorer/public/components/app_container.tsx +++ b/src/plugins/data_explorer/public/components/app_container.tsx @@ -41,6 +41,7 @@ export const AppContainer = React.memo( const showActionsInGroup = uiSettings?.get('home:useNewHomePage'); const topLinkRef = useRef(null); + const datasetSelectorRef = useRef(null); const datePickerRef = useRef(null); if (!view) { @@ -59,6 +60,7 @@ export const AppContainer = React.memo( params.optionalRef = { topLinkRef, + datasetSelectorRef, datePickerRef, }; // Render the application DOM. @@ -103,7 +105,7 @@ export const AppContainer = React.memo( mode={['collapsible', { position: 'top' }]} paddingSize="none" > - + diff --git a/src/plugins/data_explorer/public/components/sidebar/index.tsx b/src/plugins/data_explorer/public/components/sidebar/index.tsx index 3c5975d9452e..6a4ef0585e33 100644 --- a/src/plugins/data_explorer/public/components/sidebar/index.tsx +++ b/src/plugins/data_explorer/public/components/sidebar/index.tsx @@ -12,50 +12,32 @@ import { DataSourceSelectable, UI_SETTINGS, } from '../../../../data/public'; -import { - DataSourceOption, - DatasetSelector, - DatasetSelectorAppearance, -} from '../../../../data/public/'; -import { Dataset } from '../../../../data/common'; +import { DataSourceOption } from '../../../../data/public/'; import { useOpenSearchDashboards } from '../../../../opensearch_dashboards_react/public'; import { DataExplorerServices } from '../../types'; -import { - setIndexPattern, - useTypedDispatch, - useTypedSelector, - setSelectedDataset, -} from '../../utils/state_management'; +import { setIndexPattern, useTypedDispatch, useTypedSelector } from '../../utils/state_management'; import './index.scss'; -type HandleSetIndexPattern = (id: string | undefined) => void; -type HandleSelectedDataset = (data: Dataset | undefined) => void; +interface SidebarProps { + children: React.ReactNode; + datasetSelectorRef: React.RefObject; +} -export const Sidebar: FC = ({ children }) => { - const { indexPattern: indexPatternId, selectedDataset } = useTypedSelector( - (state) => state.metadata - ); +export const Sidebar: FC = ({ children, datasetSelectorRef }) => { + const { indexPattern: indexPatternId } = useTypedSelector((state) => state.metadata); const dispatch = useTypedDispatch(); const [selectedSources, setSelectedSources] = useState([]); const [dataSourceOptionList, setDataSourceOptionList] = useState([]); const [activeDataSources, setActiveDataSources] = useState([]); - const { services } = useOpenSearchDashboards(); + const { - data: { indexPatterns, dataSources }, - notifications: { toasts }, - application, - uiSettings, - } = services; - - const handleDatasetSubmit = useCallback( - (query: any) => { - // Update the index pattern - if (query.dataset) { - dispatch(setIndexPattern(query.dataset.id)); - } + services: { + data: { indexPatterns, dataSources }, + notifications: { toasts }, + application, + uiSettings, }, - [dispatch] - ); + } = useOpenSearchDashboards(); const [isEnhancementEnabled, setIsEnhancementEnabled] = useState(false); @@ -137,14 +119,6 @@ export const Sidebar: FC = ({ children }) => { dataSources.dataSourceService.reload(); }, [dataSources.dataSourceService]); - const handleSetIndexPattern: HandleSetIndexPattern = (id: string | undefined) => { - dispatch(setIndexPattern(id)); - }; - - const handleSelectedDataset: HandleSelectedDataset = (data: Dataset | undefined) => { - dispatch(setSelectedDataset(data)); - }; - return ( { hasBorder={true} borderRadius="l" > - - {isEnhancementEnabled ? ( - - ) : ( + + {isEnhancementEnabled &&
} + {!isEnhancementEnabled && ( { /> )} + {children} diff --git a/src/plugins/data_explorer/public/index.ts b/src/plugins/data_explorer/public/index.ts index 9d9ae2f46d2b..f8adda434ced 100644 --- a/src/plugins/data_explorer/public/index.ts +++ b/src/plugins/data_explorer/public/index.ts @@ -18,6 +18,4 @@ export { useTypedSelector, useTypedDispatch, setIndexPattern, - setSelectedDataset, - setDataSet, } from './utils/state_management'; diff --git a/src/plugins/data_explorer/public/utils/state_management/metadata_slice.ts b/src/plugins/data_explorer/public/utils/state_management/metadata_slice.ts index c62e550b0073..1fca4a659244 100644 --- a/src/plugins/data_explorer/public/utils/state_management/metadata_slice.ts +++ b/src/plugins/data_explorer/public/utils/state_management/metadata_slice.ts @@ -6,13 +6,11 @@ import { createSlice, PayloadAction } from '@reduxjs/toolkit'; import { DataExplorerServices } from '../../types'; import { QUERY_ENHANCEMENT_ENABLED_SETTING } from '../../components/constants'; -import { Dataset } from '../../../../data/common'; export interface MetadataState { indexPattern?: string; originatingApp?: string; view?: string; - selectedDataset?: Dataset; } const initialState: MetadataState = {}; @@ -28,16 +26,13 @@ export const getPreloadedState = async ({ .getStateTransfer(scopedHistory) .getIncomingEditorState({ keysToRemoveAfterFetch: ['id', 'input'] }) || {}; const isQueryEnhancementEnabled = uiSettings.get(QUERY_ENHANCEMENT_ENABLED_SETTING); - const defaultIndexPattern = await data.indexPatterns.getDefault(); - const selectedDataset = - data.query.queryString.getQuery().dataset || - data.query.queryString.getDefaultQuery().dataset || - undefined; + const defaultIndexPattern = isQueryEnhancementEnabled + ? undefined + : await data.indexPatterns.getDefault(); const preloadedState: MetadataState = { ...initialState, originatingApp, indexPattern: defaultIndexPattern?.id, - selectedDataset, }; return preloadedState; @@ -56,9 +51,6 @@ export const slice = createSlice({ setView: (state, action: PayloadAction) => { state.view = action.payload; }, - setSelectedDataset: (state, action: PayloadAction) => { - state.selectedDataset = action.payload; - }, setState: (_state, action: PayloadAction) => { return action.payload; }, @@ -66,10 +58,4 @@ export const slice = createSlice({ }); export const { reducer } = slice; -export const { - setIndexPattern, - setOriginatingApp, - setView, - setState, - setSelectedDataset, -} = slice.actions; +export const { setIndexPattern, setOriginatingApp, setView, setState } = slice.actions; diff --git a/src/plugins/data_explorer/public/utils/state_management/redux_persistence.test.tsx b/src/plugins/data_explorer/public/utils/state_management/redux_persistence.test.tsx index 9af3bcf3d491..62159558a0c4 100644 --- a/src/plugins/data_explorer/public/utils/state_management/redux_persistence.test.tsx +++ b/src/plugins/data_explorer/public/utils/state_management/redux_persistence.test.tsx @@ -26,7 +26,6 @@ describe('test redux state persistence', () => { "metadata": Object { "indexPattern": "id", "originatingApp": undefined, - "selectedDataset": undefined, }, } `); diff --git a/src/plugins/data_explorer/public/utils/state_management/store.ts b/src/plugins/data_explorer/public/utils/state_management/store.ts index 1ac3564e34ce..daf0b3d7e369 100644 --- a/src/plugins/data_explorer/public/utils/state_management/store.ts +++ b/src/plugins/data_explorer/public/utils/state_management/store.ts @@ -116,9 +116,4 @@ export type RenderState = Omit; // Remaining state after export type Store = ReturnType; export type AppDispatch = Store['dispatch']; -export { - MetadataState, - setIndexPattern, - setOriginatingApp, - setSelectedDataset, -} from './metadata_slice'; +export { MetadataState, setIndexPattern, setOriginatingApp } from './metadata_slice'; diff --git a/src/plugins/data/public/ui/_common.scss b/src/plugins/discover/public/application/components/no_index_patterns/no_index_patterns.scss similarity index 86% rename from src/plugins/data/public/ui/_common.scss rename to src/plugins/discover/public/application/components/no_index_patterns/no_index_patterns.scss index fcd98b9c7b31..fb2d8ef915aa 100644 --- a/src/plugins/data/public/ui/_common.scss +++ b/src/plugins/discover/public/application/components/no_index_patterns/no_index_patterns.scss @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -.dataUI-centerPanel { +.discoverNoIndexPatterns-centerPanel { height: 100%; width: 100%; diff --git a/src/plugins/discover/public/application/components/no_index_patterns/no_index_patterns.tsx b/src/plugins/discover/public/application/components/no_index_patterns/no_index_patterns.tsx new file mode 100644 index 000000000000..7906a2f06041 --- /dev/null +++ b/src/plugins/discover/public/application/components/no_index_patterns/no_index_patterns.tsx @@ -0,0 +1,89 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import './no_index_patterns.scss'; +import React from 'react'; +import { i18n } from '@osd/i18n'; +import { + EuiPanel, + EuiFlexGroup, + EuiFlexItem, + EuiIcon, + EuiText, + EuiTitle, + EuiButtonEmpty, +} from '@elastic/eui'; +import { getServices } from '../../../opensearch_dashboards_services'; + +export const DiscoverNoIndexPatterns: React.FC = () => { + const languageService = getServices().data.query.queryString.getLanguageService(); + const registeredLanguages = languageService.getLanguages(); + + return ( + + + + + + + + + +

+ {i18n.translate('discover.noIndexPatterns.selectDataTitle', { + defaultMessage: 'Select data', + })} +

+
+
+ + + {i18n.translate('discover.noIndexPatterns.selectDataDescription', { + defaultMessage: + 'Select an available data source and choose a query language to use for running queries. You can use the data dropdown or use the enhanced data selector to select data.', + })} + + + + +

+ {i18n.translate('discover.noIndexPatterns.learnMoreAboutQueryLanguages', { + defaultMessage: 'Learn more about query languages', + })} +

+
+
+ + + {registeredLanguages.map( + (language) => + language.docLink && ( + + + {language.docLink.title} + + + ) + )} + + +
+
+
+
+ ); +}; diff --git a/src/plugins/discover/public/application/components/no_results/no_results.tsx b/src/plugins/discover/public/application/components/no_results/no_results.tsx index 6c8421535fde..57d34b64ef64 100644 --- a/src/plugins/discover/public/application/components/no_results/no_results.tsx +++ b/src/plugins/discover/public/application/components/no_results/no_results.tsx @@ -41,31 +41,21 @@ import { EuiPanel, } from '@elastic/eui'; import { i18n } from '@osd/i18n'; -import { Query } from '../../../../../data/common'; import { - DatasetServiceContract, - LanguageServiceContract, + Query, + QueryStringContract, SavedQuery, SavedQueryService, } from '../../../../../data/public/'; interface Props { - datasetService: DatasetServiceContract; + queryString: QueryStringContract; savedQuery: SavedQueryService; - languageService: LanguageServiceContract; query: Query | undefined; timeFieldName?: string; - queryLanguage?: string; } -export const DiscoverNoResults = ({ - datasetService, - savedQuery, - languageService, - query, - timeFieldName, - queryLanguage, -}: Props) => { +export const DiscoverNoResults = ({ queryString, query, savedQuery, timeFieldName }: Props) => { // Commented out due to no usage in code // See: https://github.com/opensearch-project/OpenSearch-Dashboards/issues/8149 // @@ -212,15 +202,17 @@ export const DiscoverNoResults = ({ // Samples for the dataset type if (query?.dataset?.type) { - const datasetSampleQueries = datasetService - .getType(query.dataset.type) + const datasetSampleQueries = queryString + .getDatasetService() + ?.getType(query.dataset.type) ?.getSampleQueries?.(query.dataset, query.language); if (Array.isArray(datasetSampleQueries)) sampleQueries.push(...datasetSampleQueries); } // Samples for the language if (query?.language) { - const languageSampleQueries = languageService.getLanguage(query.language)?.sampleQueries; + const languageSampleQueries = queryString.getLanguageService()?.getLanguage(query.language) + ?.sampleQueries; if (Array.isArray(languageSampleQueries)) sampleQueries.push(...languageSampleQueries); } @@ -264,7 +256,7 @@ export const DiscoverNoResults = ({ ] : []), ]; - }, [datasetService, languageService, query, savedQueries]); + }, [queryString, query, savedQueries]); return ( diff --git a/src/plugins/discover/public/application/view_components/canvas/index.tsx b/src/plugins/discover/public/application/view_components/canvas/index.tsx index af94e0e6f542..b118df817ab0 100644 --- a/src/plugins/discover/public/application/view_components/canvas/index.tsx +++ b/src/plugins/discover/public/application/view_components/canvas/index.tsx @@ -12,6 +12,7 @@ import { DiscoverChartContainer } from './discover_chart_container'; import { useDiscoverContext } from '../context'; import { ResultStatus, SearchData } from '../utils/use_search'; import { DiscoverNoResults } from '../../components/no_results/no_results'; +import { DiscoverNoIndexPatterns } from '../../components/no_index_patterns/no_index_patterns'; import { DiscoverUninitialized } from '../../components/uninitialized/uninitialized'; import { LoadingSpinner } from '../../components/loading_spinner/loading_spinner'; import { setColumns, useDispatch, useSelector } from '../../utils/state_management'; @@ -27,31 +28,19 @@ import { OpenSearchSearchHit } from '../../../application/doc_views/doc_views_ty import { buildColumns } from '../../utils/columns'; import './discover_canvas.scss'; import { HeaderVariant } from '../../../../../../core/public'; -import { Query } from '../../../../../../../src/plugins/data/common/types'; -import { setIndexPattern, setSelectedDataset } from '../../../../../data_explorer/public'; -import { NoIndexPatternsPanel, AdvancedSelector } from '../../../../../data/public'; -import { Dataset } from '../../../../../data/common'; -import { toMountPoint } from '../../../../../opensearch_dashboards_react/public'; // eslint-disable-next-line import/no-default-export export default function DiscoverCanvas({ setHeaderActionMenu, history, optionalRef }: ViewProps) { - const { indexPattern: currentIndexPattern, selectedDataset } = useSelector( - (state) => state.metadata - ); - const [loadedIndexPattern, setLoadedIndexPattern] = useState(selectedDataset?.id); const panelRef = useRef(null); const { data$, refetch$, indexPattern } = useDiscoverContext(); - const { services } = useOpenSearchDashboards(); const { - uiSettings, - capabilities, - chrome: { setHeaderVariant }, - data, - overlays, - } = services; - const datasetService = data.query.queryString.getDatasetService(); - const savedQuery = data.query.savedQueries; - const languageService = data.query.queryString.getLanguageService(); + services: { + uiSettings, + capabilities, + chrome: { setHeaderVariant }, + data, + }, + } = useOpenSearchDashboards(); const { columns } = useSelector((state) => { const stateColumns = state.discover.columns; @@ -69,7 +58,6 @@ export default function DiscoverCanvas({ setHeaderActionMenu, history, optionalR ); const dispatch = useDispatch(); const prevIndexPattern = useRef(indexPattern); - const [query, setQuery] = useState(); const [fetchState, setFetchState] = useState({ status: data$.getValue().status, @@ -79,9 +67,6 @@ export default function DiscoverCanvas({ setHeaderActionMenu, history, optionalR const onQuerySubmit = useCallback( (payload, isUpdate) => { - if (payload?.query) { - setQuery(payload?.query); - } if (isUpdate === false) { refetch$.next(); } @@ -139,48 +124,10 @@ export default function DiscoverCanvas({ setHeaderActionMenu, history, optionalR }; const showSaveQuery = !!capabilities.discover?.saveQuery; - const handleDatasetChange = (dataset: Dataset) => { - dispatch(setSelectedDataset(dataset)); - - // Update query and other necessary state - const queryString = data.query.queryString; - const initialQuery = queryString.getInitialQueryByDataset(dataset); - queryString.setQuery(initialQuery); - queryString.getDatasetService().addRecentDataset(dataset); - }; - - const handleOpenDataSelector = () => { - const overlay = overlays?.openModal( - toMountPoint( - { - overlay?.close(); - if (dataset) { - handleDatasetChange(dataset); - } - }} - onCancel={() => overlay?.close()} - selectedDataset={undefined} - setSelectedDataset={setSelectedDataset} - setIndexPattern={setIndexPattern} - dispatch={dispatch} - /> - ), - { - maxWidth: false, - className: 'datasetSelector__advancedModal', - } - ); - }; - - const hasNoDataset = !currentIndexPattern && !loadedIndexPattern && isEnhancementsEnabled; - return ( - {hasNoDataset ? ( - - ) : ( - <> - {fetchState.status === ResultStatus.NO_RESULTS && ( - - )} - {fetchState.status === ResultStatus.ERROR && ( - - )} - {fetchState.status === ResultStatus.UNINITIALIZED && ( - refetch$.next()} /> - )} - {fetchState.status === ResultStatus.LOADING && } - {fetchState.status === ResultStatus.READY && isEnhancementsEnabled && ( - <> - - - - )} - {fetchState.status === ResultStatus.READY && !isEnhancementsEnabled && ( - - - - - )} - - )} + + {!indexPattern && } + <> + {fetchState.status === ResultStatus.NO_RESULTS && ( + + )} + {fetchState.status === ResultStatus.ERROR && ( + + )} + {fetchState.status === ResultStatus.UNINITIALIZED && ( + refetch$.next()} /> + )} + {fetchState.status === ResultStatus.LOADING && } + {fetchState.status === ResultStatus.READY && isEnhancementsEnabled && ( + <> + + + + )} + {fetchState.status === ResultStatus.READY && !isEnhancementsEnabled && ( + + + + + )} + ); } diff --git a/src/plugins/discover/public/application/view_components/canvas/top_nav.tsx b/src/plugins/discover/public/application/view_components/canvas/top_nav.tsx index 0b3f1275d7ff..a4054535ddf5 100644 --- a/src/plugins/discover/public/application/view_components/canvas/top_nav.tsx +++ b/src/plugins/discover/public/application/view_components/canvas/top_nav.tsx @@ -35,15 +35,9 @@ export interface TopNavProps { }; showSaveQuery: boolean; isEnhancementsEnabled?: boolean; - useNoIndexPatternsTopNav?: boolean; } -export const TopNav = ({ - opts, - showSaveQuery, - isEnhancementsEnabled, - useNoIndexPatternsTopNav = false, -}: TopNavProps) => { +export const TopNav = ({ opts, showSaveQuery, isEnhancementsEnabled }: TopNavProps) => { const { services } = useOpenSearchDashboards(); const { data$, inspectorAdapters, savedSearch, indexPattern } = useDiscoverContext(); const [indexPatterns, setIndexPatterns] = useState(undefined); @@ -68,13 +62,7 @@ export const TopNav = ({ const showActionsInGroup = uiSettings.get('home:useNewHomePage'); const topNavLinks = savedSearch - ? getTopNavLinks( - services, - inspectorAdapters, - savedSearch, - isEnhancementsEnabled, - useNoIndexPatternsTopNav - ) + ? getTopNavLinks(services, inspectorAdapters, savedSearch, isEnhancementsEnabled) : []; connectStorageToQueryState( @@ -100,18 +88,11 @@ export const TopNav = ({ useEffect(() => { let isMounted = true; const initializeDataset = async () => { - await data.indexPatterns.ensureDefaultIndexPattern(isEnhancementsEnabled ? false : true); + await data.indexPatterns.ensureDefaultIndexPattern(); const defaultIndexPattern = await data.indexPatterns.getDefault(); - // TODO: ROCKY do we need this? - // const queryString = data.query.queryString; - // const defaultDataset = queryString.getDatasetService().getDefault(); - if (!isMounted) return; setIndexPatterns(defaultIndexPattern ? [defaultIndexPattern] : undefined); - // if (defaultDataset) { - // datasetManager.setDataset(defaultDataset); - // } }; initializeDataset(); @@ -119,7 +100,7 @@ export const TopNav = ({ return () => { isMounted = false; }; - }, [data.indexPatterns, data.query, isEnhancementsEnabled]); + }, [data.indexPatterns, data.query]); useEffect(() => { const pageTitleSuffix = savedSearch?.id && savedSearch.title ? `: ${savedSearch.title}` : ''; @@ -176,30 +157,19 @@ export const TopNav = ({ {} : opts.onQuerySubmit} - savedQueryId={useNoIndexPatternsTopNav ? undefined : state.savedQuery} - onSavedQueryIdChange={useNoIndexPatternsTopNav ? () => {} : updateSavedQueryId} - datePickerRef={useNoIndexPatternsTopNav ? undefined : opts?.optionalRef?.datePickerRef} + indexPatterns={indexPattern ? [indexPattern] : indexPatterns} + onQuerySubmit={opts.onQuerySubmit} + savedQueryId={state.savedQuery} + onSavedQueryIdChange={updateSavedQueryId} + datasetSelectorRef={opts?.optionalRef?.datasetSelectorRef} + datePickerRef={opts?.optionalRef?.datePickerRef} groupActions={showActionsInGroup} - screenTitle={ - useNoIndexPatternsTopNav - ? i18n.translate('discover.noIndexPatterns.screenTitle', { - defaultMessage: 'Select data', - }) - : screenTitle - } + screenTitle={screenTitle} queryStatus={queryStatus} /> diff --git a/src/plugins/query_enhancements/public/plugin.tsx b/src/plugins/query_enhancements/public/plugin.tsx index ff3df7e9ce1c..cbd40cb60b60 100644 --- a/src/plugins/query_enhancements/public/plugin.tsx +++ b/src/plugins/query_enhancements/public/plugin.tsx @@ -77,6 +77,12 @@ export class QueryEnhancementsPlugin filterable: false, visualizable: false, }, + docLink: { + title: i18n.translate('queryEnhancements.pplLanguage.docLink', { + defaultMessage: 'PPL documentation', + }), + url: 'https://opensearch.org/docs/latest/search-plugins/sql/ppl/syntax/', + }, showDocLinks: false, editor: enhancedPPLQueryEditor, editorSupportedAppNames: ['discover'], @@ -96,6 +102,12 @@ export class QueryEnhancementsPlugin filterable: false, visualizable: false, }, + docLink: { + title: i18n.translate('queryEnhancements.sqlLanguage.docLink', { + defaultMessage: 'SQL documentation', + }), + url: 'https://opensearch.org/docs/latest/search-plugins/sql/sql/basic/', + }, showDocLinks: false, editor: enhancedSQLQueryEditor, editorSupportedAppNames: ['discover'],