From 21ce7acfbead5bb6e3f692cd57d9822cb64a2d6e Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Wed, 15 Jun 2022 11:53:55 +0300 Subject: [PATCH 001/115] Move the add dataview action above the dataview selection panel --- .../dataview_picker/change_dataview.tsx | 80 ++++++++++++------- 1 file changed, 51 insertions(+), 29 deletions(-) diff --git a/src/plugins/unified_search/public/dataview_picker/change_dataview.tsx b/src/plugins/unified_search/public/dataview_picker/change_dataview.tsx index 3e0ed7cc8a266..ead83947c84a2 100644 --- a/src/plugins/unified_search/public/dataview_picker/change_dataview.tsx +++ b/src/plugins/unified_search/public/dataview_picker/change_dataview.tsx @@ -22,6 +22,9 @@ import { EuiText, EuiTourStep, EuiContextMenuPanelProps, + EuiFlexGroup, + EuiFlexItem, + EuiButtonEmpty, } from '@elastic/eui'; import type { DataViewListItem } from '@kbn/data-views-plugin/public'; import { IDataPluginServices } from '@kbn/data-plugin/public'; @@ -162,38 +165,57 @@ export function ChangeDataView({ ); } panelItems.push( - { - onChangeDataView(newId); - setPopoverIsOpen(false); - }} - currentDataViewId={currentDataViewId} - selectableProps={selectableProps} - searchListInputId={searchListInputId} - /> - ); + <> + {onDataViewCreated && ( + + + +
+ {i18n.translate('unifiedSearch.query.queryBar.indexPattern.dataViewsLabel', { + defaultMessage: 'Data views', + })} +
+
+
+ + { + setPopoverIsOpen(false); + onDataViewCreated(); + }} + size="xs" + iconType="plusInCircleFilled" + iconSide="left" + data-test-subj="dataview-create-new" + > + {i18n.translate('unifiedSearch.query.queryBar.indexPattern.addNewDataView', { + defaultMessage: 'Create a data view', + })} + + +
+ )} - if (onDataViewCreated) { - panelItems.push( - , - { + { + onChangeDataView(newId); setPopoverIsOpen(false); - onDataViewCreated(); }} - > - {i18n.translate('unifiedSearch.query.queryBar.indexPattern.addNewDataView', { - defaultMessage: 'Create a data view', - })} - - ); - } + currentDataViewId={currentDataViewId} + selectableProps={selectableProps} + searchListInputId={searchListInputId} + /> + + ); return panelItems; }; From 3364e538b7cb7cca719b6155592786455a54ebcd Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Wed, 15 Jun 2022 13:24:52 +0300 Subject: [PATCH 002/115] Implements a new selectable on the dataview picker for the text based languages --- .../components/top_nav/discover_topnav.tsx | 2 + .../dataview_picker/change_dataview.tsx | 42 +++++++++++++ .../public/dataview_picker/dataview_list.tsx | 4 +- .../public/dataview_picker/index.tsx | 9 +++ .../dataview_picker/text_languages_list.tsx | 60 +++++++++++++++++++ src/plugins/unified_search/public/index.ts | 2 +- 6 files changed, 117 insertions(+), 2 deletions(-) create mode 100644 src/plugins/unified_search/public/dataview_picker/text_languages_list.tsx diff --git a/src/plugins/discover/public/application/main/components/top_nav/discover_topnav.tsx b/src/plugins/discover/public/application/main/components/top_nav/discover_topnav.tsx index e698f3756fb5f..b33348b3862dd 100644 --- a/src/plugins/discover/public/application/main/components/top_nav/discover_topnav.tsx +++ b/src/plugins/discover/public/application/main/components/top_nav/discover_topnav.tsx @@ -9,6 +9,7 @@ import React, { useCallback, useMemo, useRef, useEffect } from 'react'; import { useHistory } from 'react-router-dom'; import { Query, TimeRange } from '@kbn/data-plugin/public'; import { DataViewType } from '@kbn/data-views-plugin/public'; +import type { DataViewPickerProps } from '@kbn/unified-search-plugin/public'; import { useDiscoverServices } from '../../../../utils/use_discover_services'; import { DiscoverLayoutProps } from '../layout/types'; import { getTopNavLinks } from './get_top_nav_links'; @@ -182,6 +183,7 @@ export const DiscoverTopNav = ({ onAddField: addField, onDataViewCreated: createNewDataView, onChangeDataView: (newIndexPatternId: string) => onChangeIndexPattern(newIndexPatternId), + textBasedLanguages: ['SQL'] as DataViewPickerProps['textBasedLanguages'], }; return ( diff --git a/src/plugins/unified_search/public/dataview_picker/change_dataview.tsx b/src/plugins/unified_search/public/dataview_picker/change_dataview.tsx index ead83947c84a2..51e73b441f548 100644 --- a/src/plugins/unified_search/public/dataview_picker/change_dataview.tsx +++ b/src/plugins/unified_search/public/dataview_picker/change_dataview.tsx @@ -31,6 +31,7 @@ import { IDataPluginServices } from '@kbn/data-plugin/public'; import { useKibana } from '@kbn/kibana-react-plugin/public'; import type { DataViewPickerProps } from '.'; import { DataViewsList } from './dataview_list'; +import { TextBasedLanguagesList } from './text_languages_list'; import { changeDataViewStyles } from './change_dataview.styles'; const NEW_DATA_VIEW_MENU_STORAGE_KEY = 'data.newDataViewMenu'; @@ -63,11 +64,13 @@ export function ChangeDataView({ trigger, selectableProps, showNewMenuTour = false, + textBasedLanguages, }: DataViewPickerProps) { const { euiTheme } = useEuiTheme(); const [isPopoverOpen, setPopoverIsOpen] = useState(false); const [dataViewsList, setDataViewsList] = useState([]); const [triggerLabel, setTriggerLabel] = useState(''); + const [isTextBasedLangSelected, setIsTextBasedLangSelected] = useState(false); const kibana = useKibana(); const { application, data, storage } = kibana.services; const styles = changeDataViewStyles({ fullWidth: trigger.fullWidth }); @@ -213,10 +216,49 @@ export function ChangeDataView({ currentDataViewId={currentDataViewId} selectableProps={selectableProps} searchListInputId={searchListInputId} + isTextBasedLangSelected={isTextBasedLangSelected} /> ); + if (textBasedLanguages?.length) { + panelItems.push( + , + + + +
+ {i18n.translate( + 'unifiedSearch.query.queryBar.indexPattern.textBasedLanguagesLabel', + { + defaultMessage: 'Text-based query languages', + } + )} +
+
+
+
, + { + setTriggerLabel(lang); + setPopoverIsOpen(false); + setIsTextBasedLangSelected(true); + // also update the query with the sql query + }} + /> + ); + } + return panelItems; }; diff --git a/src/plugins/unified_search/public/dataview_picker/dataview_list.tsx b/src/plugins/unified_search/public/dataview_picker/dataview_list.tsx index da0919bd0ce8e..508479ed53839 100644 --- a/src/plugins/unified_search/public/dataview_picker/dataview_list.tsx +++ b/src/plugins/unified_search/public/dataview_picker/dataview_list.tsx @@ -15,6 +15,7 @@ import { DataViewListItem } from '@kbn/data-views-plugin/public'; export interface DataViewsListProps { dataViewsList: DataViewListItem[]; onChangeDataView: (newId: string) => void; + isTextBasedLangSelected: boolean; currentDataViewId?: string; selectableProps?: EuiSelectableProps; searchListInputId?: string; @@ -23,6 +24,7 @@ export interface DataViewsListProps { export function DataViewsList({ dataViewsList, onChangeDataView, + isTextBasedLangSelected, currentDataViewId, selectableProps, searchListInputId, @@ -42,7 +44,7 @@ export function DataViewsList({ key: id, label: name ? name : title, value: id, - checked: id === currentDataViewId ? 'on' : undefined, + checked: id === currentDataViewId && !isTextBasedLangSelected ? 'on' : undefined, }))} onChange={(choices) => { const choice = choices.find(({ checked }) => checked) as unknown as { diff --git a/src/plugins/unified_search/public/dataview_picker/index.tsx b/src/plugins/unified_search/public/dataview_picker/index.tsx index bd24aef0498ef..407022254557c 100644 --- a/src/plugins/unified_search/public/dataview_picker/index.tsx +++ b/src/plugins/unified_search/public/dataview_picker/index.tsx @@ -15,6 +15,11 @@ export type ChangeDataViewTriggerProps = EuiButtonProps & { title?: string; }; +export enum TextBasedLanguages { + SQL = 'SQL', + ESQL = 'ESQL', +} + /** @public */ export interface DataViewPickerProps { trigger: ChangeDataViewTriggerProps; @@ -25,6 +30,8 @@ export interface DataViewPickerProps { onAddField?: () => void; onDataViewCreated?: () => void; showNewMenuTour?: boolean; + // list of the supported text-based languages per application + textBasedLanguages?: TextBasedLanguages[]; } export const DataViewPicker = ({ @@ -36,6 +43,7 @@ export const DataViewPicker = ({ trigger, selectableProps, showNewMenuTour, + textBasedLanguages, }: DataViewPickerProps) => { return ( ); }; diff --git a/src/plugins/unified_search/public/dataview_picker/text_languages_list.tsx b/src/plugins/unified_search/public/dataview_picker/text_languages_list.tsx new file mode 100644 index 0000000000000..95f804698f4b1 --- /dev/null +++ b/src/plugins/unified_search/public/dataview_picker/text_languages_list.tsx @@ -0,0 +1,60 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import React from 'react'; +import { EuiSelectable, EuiPanel } from '@elastic/eui'; +import { css } from '@emotion/react'; +import { TextBasedLanguages } from '.'; + +export interface TextBasedLanguagesListProps { + textBasedLanguages: TextBasedLanguages[]; + onChange: (lang: string) => void; + selectedOption: string; +} + +export function TextBasedLanguagesList({ + textBasedLanguages, + onChange, + selectedOption, +}: TextBasedLanguagesListProps) { + return ( + + data-test-subj="text-based-languages-switcher" + singleSelection="always" + options={textBasedLanguages.map((lang) => ({ + key: lang, + label: lang, + value: lang, + checked: lang === selectedOption ? 'on' : undefined, + }))} + onChange={(choices) => { + const choice = choices.find(({ checked }) => checked) as unknown as { + value: string; + }; + onChange(choice.value); + }} + > + {(list) => ( + + {list} + + )} + + ); +} diff --git a/src/plugins/unified_search/public/index.ts b/src/plugins/unified_search/public/index.ts index bc7974b42efb3..4aabdb2b19b6c 100755 --- a/src/plugins/unified_search/public/index.ts +++ b/src/plugins/unified_search/public/index.ts @@ -16,7 +16,7 @@ export type { UnifiedSearchPublicPluginStart, UnifiedSearchPluginSetup } from '. export { SearchBar } from './search_bar'; export { FilterLabel, FilterItem } from './filter_bar'; export { DataViewsList } from './dataview_picker/dataview_list'; -export { DataViewPicker } from './dataview_picker'; +export { DataViewPicker, DataViewPickerProps } from './dataview_picker'; export type { ApplyGlobalFilterActionContext } from './actions'; export { ACTION_GLOBAL_APPLY_FILTER } from './actions'; From f6afba56390ed64551f2b28e7daffac9c685f4e4 Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Wed, 15 Jun 2022 15:01:19 +0300 Subject: [PATCH 003/115] Implementation of the transition modal when on SQL mode and select a dataview --- .../dataview_picker/change_dataview.tsx | 137 +++++++++++++----- .../dataview_picker/dataview_list.test.tsx | 1 + .../public/dataview_picker/dataview_list.tsx | 16 +- .../public/dataview_picker/index.tsx | 4 + .../text_languages_transition_modal.tsx | 120 +++++++++++++++ 5 files changed, 237 insertions(+), 41 deletions(-) create mode 100644 src/plugins/unified_search/public/dataview_picker/text_languages_transition_modal.tsx diff --git a/src/plugins/unified_search/public/dataview_picker/change_dataview.tsx b/src/plugins/unified_search/public/dataview_picker/change_dataview.tsx index 51e73b441f548..1b021bf7fb06a 100644 --- a/src/plugins/unified_search/public/dataview_picker/change_dataview.tsx +++ b/src/plugins/unified_search/public/dataview_picker/change_dataview.tsx @@ -7,7 +7,7 @@ */ import { i18n } from '@kbn/i18n'; -import React, { useState, useEffect } from 'react'; +import React, { useState, useEffect, useCallback } from 'react'; import { css } from '@emotion/react'; import { EuiPopover, @@ -32,9 +32,13 @@ import { useKibana } from '@kbn/kibana-react-plugin/public'; import type { DataViewPickerProps } from '.'; import { DataViewsList } from './dataview_list'; import { TextBasedLanguagesList } from './text_languages_list'; +import { TextBasedLanguagesTransitionModal } from './text_languages_transition_modal'; import { changeDataViewStyles } from './change_dataview.styles'; +// local storage key for the tour component const NEW_DATA_VIEW_MENU_STORAGE_KEY = 'data.newDataViewMenu'; +// local storage key for the text based languages transition modal +const TEXT_LANG_TRANSITION_MODAL_KEY = 'data.textLangTransitionModal'; const newMenuTourTitle = i18n.translate('unifiedSearch.query.dataViewMenu.newMenuTour.title', { defaultMessage: 'A better data view menu', @@ -65,16 +69,21 @@ export function ChangeDataView({ selectableProps, showNewMenuTour = false, textBasedLanguages, + onSaveTextLanguageQuery, }: DataViewPickerProps) { const { euiTheme } = useEuiTheme(); const [isPopoverOpen, setPopoverIsOpen] = useState(false); const [dataViewsList, setDataViewsList] = useState([]); const [triggerLabel, setTriggerLabel] = useState(''); const [isTextBasedLangSelected, setIsTextBasedLangSelected] = useState(false); + const [isTextLangTransitionModalVisible, setIsTextLangTransitionModalVisible] = useState(false); + const kibana = useKibana(); const { application, data, storage } = kibana.services; const styles = changeDataViewStyles({ fullWidth: trigger.fullWidth }); - + const [isTextLangTransitionModalDismissed, setIsTextLangTransitionModalDismissed] = useState(() => + Boolean(storage.get(TEXT_LANG_TRANSITION_MODAL_KEY)) + ); const [isTourDismissed, setIsTourDismissed] = useState(() => Boolean(storage.get(NEW_DATA_VIEW_MENU_STORAGE_KEY)) ); @@ -212,6 +221,9 @@ export function ChangeDataView({ onChangeDataView={(newId) => { onChangeDataView(newId); setPopoverIsOpen(false); + if (isTextBasedLangSelected && !isTextLangTransitionModalDismissed) { + setIsTextLangTransitionModalVisible(true); + } }} currentDataViewId={currentDataViewId} selectableProps={selectableProps} @@ -262,47 +274,92 @@ export function ChangeDataView({ return panelItems; }; - return ( - -   {newMenuTourTitle} - - } - content={ - -

{newMenuTourDescription}

-
+ let modal; + + const onTransitionModalDismiss = useCallback(() => { + storage.set(TEXT_LANG_TRANSITION_MODAL_KEY, true); + setIsTextLangTransitionModalDismissed(true); + }, [storage]); + + const handleCloseTransitionModal = useCallback( + (shouldDismissModal: boolean) => { + setIsTextLangTransitionModalVisible(false); + setIsTextBasedLangSelected(false); + // clean up the text based language query + + if (shouldDismissModal) { + onTransitionModalDismiss(); } - isStepOpen={isTourOpen} - onFinish={onTourDismiss} - step={1} - stepsTotal={1} - footerAction={ - - {newMenuTourDismissLabel} - + }, + [onTransitionModalDismiss] + ); + + const handleSaveTransitionModal = useCallback( + (shouldDismissModal: boolean) => { + setIsTextLangTransitionModalVisible(false); + onSaveTextLanguageQuery?.(); + setIsTextBasedLangSelected(false); + // clean up the text based language query + if (shouldDismissModal) { + onTransitionModalDismiss(); } - repositionOnScroll - display="block" - > - setPopoverIsOpen(false)} - panelPaddingSize="none" - initialFocus={`#${searchListInputId}`} + }, + [onSaveTextLanguageQuery, onTransitionModalDismiss] + ); + + if (isTextLangTransitionModalVisible && !isTextLangTransitionModalDismissed) { + modal = ( + + ); + } + + return ( + <> + +   {newMenuTourTitle} + + } + content={ + +

{newMenuTourDescription}

+
+ } + isStepOpen={isTourOpen} + onFinish={onTourDismiss} + step={1} + stepsTotal={1} + footerAction={ + + {newMenuTourDismissLabel} + + } + repositionOnScroll display="block" - buffer={8} > -
- -
-
-
+ setPopoverIsOpen(false)} + panelPaddingSize="none" + initialFocus={`#${searchListInputId}`} + display="block" + buffer={8} + > +
+ +
+
+ + {modal} + ); } diff --git a/src/plugins/unified_search/public/dataview_picker/dataview_list.test.tsx b/src/plugins/unified_search/public/dataview_picker/dataview_list.test.tsx index 813beae20369c..65ebb0252ae5d 100644 --- a/src/plugins/unified_search/public/dataview_picker/dataview_list.test.tsx +++ b/src/plugins/unified_search/public/dataview_picker/dataview_list.test.tsx @@ -50,6 +50,7 @@ describe('DataView list component', () => { currentDataViewId: 'dataview-1', onChangeDataView: changeDataViewSpy, dataViewsList: list, + isTextBasedLangSelected: false, }; }); it('should trigger the onChangeDataView if a new dataview is selected', async () => { diff --git a/src/plugins/unified_search/public/dataview_picker/dataview_list.tsx b/src/plugins/unified_search/public/dataview_picker/dataview_list.tsx index 508479ed53839..7735ebf30670b 100644 --- a/src/plugins/unified_search/public/dataview_picker/dataview_list.tsx +++ b/src/plugins/unified_search/public/dataview_picker/dataview_list.tsx @@ -7,7 +7,7 @@ */ import React from 'react'; -import { EuiSelectable, EuiSelectableProps, EuiPanel } from '@elastic/eui'; +import { EuiSelectable, EuiSelectableProps, EuiPanel, EuiIcon, EuiToolTip } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { css } from '@emotion/react'; import { DataViewListItem } from '@kbn/data-views-plugin/public'; @@ -45,6 +45,20 @@ export function DataViewsList({ label: name ? name : title, value: id, checked: id === currentDataViewId && !isTextBasedLangSelected ? 'on' : undefined, + append: isTextBasedLangSelected ? ( + + + + ) : null, }))} onChange={(choices) => { const choice = choices.find(({ checked }) => checked) as unknown as { diff --git a/src/plugins/unified_search/public/dataview_picker/index.tsx b/src/plugins/unified_search/public/dataview_picker/index.tsx index 407022254557c..bed8ae2a37d69 100644 --- a/src/plugins/unified_search/public/dataview_picker/index.tsx +++ b/src/plugins/unified_search/public/dataview_picker/index.tsx @@ -32,6 +32,8 @@ export interface DataViewPickerProps { showNewMenuTour?: boolean; // list of the supported text-based languages per application textBasedLanguages?: TextBasedLanguages[]; + // called when the user clicks the Save and switch transition modal button + onSaveTextLanguageQuery?: () => void; } export const DataViewPicker = ({ @@ -44,6 +46,7 @@ export const DataViewPicker = ({ selectableProps, showNewMenuTour, textBasedLanguages, + onSaveTextLanguageQuery, }: DataViewPickerProps) => { return ( ); }; diff --git a/src/plugins/unified_search/public/dataview_picker/text_languages_transition_modal.tsx b/src/plugins/unified_search/public/dataview_picker/text_languages_transition_modal.tsx new file mode 100644 index 0000000000000..7b70022f2d1b7 --- /dev/null +++ b/src/plugins/unified_search/public/dataview_picker/text_languages_transition_modal.tsx @@ -0,0 +1,120 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import React, { useState, useCallback } from 'react'; +import { i18n } from '@kbn/i18n'; +import { css } from '@emotion/react'; + +import { + EuiModal, + EuiModalBody, + EuiModalFooter, + EuiModalHeader, + EuiModalHeaderTitle, + EuiButton, + EuiText, + EuiCheckbox, + EuiFlexItem, + EuiFlexGroup, +} from '@elastic/eui'; + +export interface TextBasedLanguagesTransitionModalProps { + closeModal: (dismissFlag: boolean) => void; + // defined by the application + onSave: (dismissFlag: boolean) => void; +} + +export function TextBasedLanguagesTransitionModal({ + closeModal, + onSave, +}: TextBasedLanguagesTransitionModalProps) { + const [dismissModalChecked, setDismissModalChecked] = useState(false); + const onTransitionModalDismiss = useCallback((e) => { + setDismissModalChecked(e.target.checked); + }, []); + return ( + closeModal(dismissModalChecked)} style={{ width: 700 }}> + + +

+ {i18n.translate( + 'unifiedSearch.query.queryBar.indexPattern.textBasedLanguagesTransitionModalTitle', + { + defaultMessage: 'Current text-based query will be cleared', + } + )} +

+
+
+ + + + {i18n.translate( + 'unifiedSearch.query.queryBar.indexPattern.textBasedLanguagesTransitionModalBody', + { + defaultMessage: + 'The current text-based language query will be cleared when switching to a specific data view. To ensure that no work is inadvertently lost in the transition, it is recommended that you save this search before switching.', + } + )} + + + + + + + + + + closeModal(dismissModalChecked)} + fill + color="warning" + iconType="magnet" + > + {i18n.translate( + 'unifiedSearch.query.queryBar.indexPattern.textBasedLanguagesTransitionModalCloseButton', + { + defaultMessage: 'Switch without saving', + } + )} + + + + onSave(dismissModalChecked)} + fill + color="success" + iconType="save" + > + {i18n.translate( + 'unifiedSearch.query.queryBar.indexPattern.textBasedLanguagesTransitionModalSaveButton', + { + defaultMessage: 'Save and switch', + } + )} + + + + +
+ ); +} From ad59ff004c374f45367bfc5dd5e2c56960562f1f Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Wed, 15 Jun 2022 15:25:22 +0300 Subject: [PATCH 004/115] Fix es lint --- .../unified_search/public/dataview_picker/dataview_list.tsx | 6 +++--- src/plugins/unified_search/public/index.ts | 4 +++- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/plugins/unified_search/public/dataview_picker/dataview_list.tsx b/src/plugins/unified_search/public/dataview_picker/dataview_list.tsx index 7735ebf30670b..8e4ddd5d78d0a 100644 --- a/src/plugins/unified_search/public/dataview_picker/dataview_list.tsx +++ b/src/plugins/unified_search/public/dataview_picker/dataview_list.tsx @@ -15,7 +15,7 @@ import { DataViewListItem } from '@kbn/data-views-plugin/public'; export interface DataViewsListProps { dataViewsList: DataViewListItem[]; onChangeDataView: (newId: string) => void; - isTextBasedLangSelected: boolean; + isTextBasedLangSelected?: boolean; currentDataViewId?: string; selectableProps?: EuiSelectableProps; searchListInputId?: string; @@ -44,8 +44,8 @@ export function DataViewsList({ key: id, label: name ? name : title, value: id, - checked: id === currentDataViewId && !isTextBasedLangSelected ? 'on' : undefined, - append: isTextBasedLangSelected ? ( + checked: id === currentDataViewId && !Boolean(isTextBasedLangSelected) ? 'on' : undefined, + append: Boolean(isTextBasedLangSelected) ? ( Date: Wed, 15 Jun 2022 17:04:55 +0300 Subject: [PATCH 005/115] Change switch modal button modal icon --- .../public/dataview_picker/text_languages_transition_modal.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/unified_search/public/dataview_picker/text_languages_transition_modal.tsx b/src/plugins/unified_search/public/dataview_picker/text_languages_transition_modal.tsx index 7b70022f2d1b7..f58e0eb7f11b1 100644 --- a/src/plugins/unified_search/public/dataview_picker/text_languages_transition_modal.tsx +++ b/src/plugins/unified_search/public/dataview_picker/text_languages_transition_modal.tsx @@ -88,7 +88,7 @@ export function TextBasedLanguagesTransitionModal({ onClick={() => closeModal(dismissModalChecked)} fill color="warning" - iconType="magnet" + iconType="merge" > {i18n.translate( 'unifiedSearch.query.queryBar.indexPattern.textBasedLanguagesTransitionModalCloseButton', From b5f6e43c1555c61eb1293b6e9896404777db14c6 Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Wed, 15 Jun 2022 18:07:30 +0300 Subject: [PATCH 006/115] Lazy load components --- .../dataview_picker/change_dataview.tsx | 24 +++++++++++++++++-- .../dataview_picker/text_languages_list.tsx | 4 +++- .../text_languages_transition_modal.tsx | 5 ++-- 3 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/plugins/unified_search/public/dataview_picker/change_dataview.tsx b/src/plugins/unified_search/public/dataview_picker/change_dataview.tsx index 1b021bf7fb06a..4a918221b8568 100644 --- a/src/plugins/unified_search/public/dataview_picker/change_dataview.tsx +++ b/src/plugins/unified_search/public/dataview_picker/change_dataview.tsx @@ -31,8 +31,8 @@ import { IDataPluginServices } from '@kbn/data-plugin/public'; import { useKibana } from '@kbn/kibana-react-plugin/public'; import type { DataViewPickerProps } from '.'; import { DataViewsList } from './dataview_list'; -import { TextBasedLanguagesList } from './text_languages_list'; -import { TextBasedLanguagesTransitionModal } from './text_languages_transition_modal'; +import type { TextBasedLanguagesListProps } from './text_languages_list'; +import type { TextBasedLanguagesTransitionModalProps } from './text_languages_transition_modal'; import { changeDataViewStyles } from './change_dataview.styles'; // local storage key for the tour component @@ -59,6 +59,26 @@ const newMenuTourDismissLabel = i18n.translate( } ); +const Fallback = () =>
; + +const LazyTextBasedLanguagesTransitionModal = React.lazy( + () => import('./text_languages_transition_modal') +); +export const TextBasedLanguagesTransitionModal = ( + props: TextBasedLanguagesTransitionModalProps +) => ( + }> + + +); + +const LazyTextBasedLanguagesList = React.lazy(() => import('./text_languages_list')); +export const TextBasedLanguagesList = (props: TextBasedLanguagesListProps) => ( + }> + + +); + export function ChangeDataView({ isMissingCurrent, currentDataViewId, diff --git a/src/plugins/unified_search/public/dataview_picker/text_languages_list.tsx b/src/plugins/unified_search/public/dataview_picker/text_languages_list.tsx index 95f804698f4b1..d6d962ff21689 100644 --- a/src/plugins/unified_search/public/dataview_picker/text_languages_list.tsx +++ b/src/plugins/unified_search/public/dataview_picker/text_languages_list.tsx @@ -17,7 +17,9 @@ export interface TextBasedLanguagesListProps { selectedOption: string; } -export function TextBasedLanguagesList({ +// Needed for React.lazy +// eslint-disable-next-line import/no-default-export +export default function TextBasedLanguagesList({ textBasedLanguages, onChange, selectedOption, diff --git a/src/plugins/unified_search/public/dataview_picker/text_languages_transition_modal.tsx b/src/plugins/unified_search/public/dataview_picker/text_languages_transition_modal.tsx index f58e0eb7f11b1..beaee5b3ec171 100644 --- a/src/plugins/unified_search/public/dataview_picker/text_languages_transition_modal.tsx +++ b/src/plugins/unified_search/public/dataview_picker/text_languages_transition_modal.tsx @@ -28,8 +28,9 @@ export interface TextBasedLanguagesTransitionModalProps { // defined by the application onSave: (dismissFlag: boolean) => void; } - -export function TextBasedLanguagesTransitionModal({ +// Needed for React.lazy +// eslint-disable-next-line import/no-default-export +export default function TextBasedLanguagesTransitionModal({ closeModal, onSave, }: TextBasedLanguagesTransitionModalProps) { From 646416630fc727cfd06a6dbf5de4bb4d3f09914c Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Thu, 16 Jun 2022 09:23:54 +0300 Subject: [PATCH 007/115] Small changes on the styling of the switch without saving button --- .../dataview_picker/text_languages_transition_modal.tsx | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/plugins/unified_search/public/dataview_picker/text_languages_transition_modal.tsx b/src/plugins/unified_search/public/dataview_picker/text_languages_transition_modal.tsx index beaee5b3ec171..6d35e74dab492 100644 --- a/src/plugins/unified_search/public/dataview_picker/text_languages_transition_modal.tsx +++ b/src/plugins/unified_search/public/dataview_picker/text_languages_transition_modal.tsx @@ -21,6 +21,7 @@ import { EuiCheckbox, EuiFlexItem, EuiFlexGroup, + useEuiTheme, } from '@elastic/eui'; export interface TextBasedLanguagesTransitionModalProps { @@ -38,6 +39,8 @@ export default function TextBasedLanguagesTransitionModal({ const onTransitionModalDismiss = useCallback((e) => { setDismissModalChecked(e.target.checked); }, []); + + const { euiTheme } = useEuiTheme(); return ( closeModal(dismissModalChecked)} style={{ width: 700 }}> @@ -87,9 +90,13 @@ export default function TextBasedLanguagesTransitionModal({ closeModal(dismissModalChecked)} - fill color="warning" iconType="merge" + css={css` + color: ${euiTheme.colors.warning}; + border: 1px solid ${euiTheme.colors.warning}; + background-color: ${euiTheme.colors.emptyShade}; + `} > {i18n.translate( 'unifiedSearch.query.queryBar.indexPattern.textBasedLanguagesTransitionModalCloseButton', From c376456f2288104ece3b14c5ed8848d7f6f43fa3 Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Fri, 17 Jun 2022 16:42:31 +0300 Subject: [PATCH 008/115] Initialization of mocaco editor --- .../src/filters/build_filters/types.ts | 6 + packages/kbn-es-query/src/filters/index.ts | 1 + packages/kbn-es-query/src/index.ts | 1 + src/plugins/data/common/query/types.ts | 2 +- .../main/services/discover_state.ts | 18 +- .../dataview_picker/change_dataview.tsx | 49 ++-- .../public/dataview_picker/index.tsx | 12 +- .../text_languages_transition_modal.tsx | 7 +- .../query_string_input/query_bar_top_row.tsx | 29 ++- .../text_based_languages_editor/helpers.ts | 32 +++ .../text_based_languages_editor/index.tsx | 212 ++++++++++++++++++ .../text_based_languages_editor.styles.ts | 47 ++++ .../lib/use_query_string_manager.ts | 8 +- .../public/search_bar/search_bar.tsx | 58 +++-- 14 files changed, 425 insertions(+), 57 deletions(-) create mode 100644 src/plugins/unified_search/public/query_string_input/text_based_languages_editor/helpers.ts create mode 100644 src/plugins/unified_search/public/query_string_input/text_based_languages_editor/index.tsx create mode 100644 src/plugins/unified_search/public/query_string_input/text_based_languages_editor/text_based_languages_editor.styles.ts diff --git a/packages/kbn-es-query/src/filters/build_filters/types.ts b/packages/kbn-es-query/src/filters/build_filters/types.ts index 3f6d586feed98..b74f67441c296 100644 --- a/packages/kbn-es-query/src/filters/build_filters/types.ts +++ b/packages/kbn-es-query/src/filters/build_filters/types.ts @@ -82,6 +82,12 @@ export type Query = { language: string; }; +// eslint-disable-next-line @typescript-eslint/consistent-type-definitions +export type AggregateQuery = { + sql?: string; + esql: string; +}; + /** * An interface for a latitude-longitude pair * @public diff --git a/packages/kbn-es-query/src/filters/index.ts b/packages/kbn-es-query/src/filters/index.ts index 765a4a68d2aea..820559d5f9069 100644 --- a/packages/kbn-es-query/src/filters/index.ts +++ b/packages/kbn-es-query/src/filters/index.ts @@ -59,6 +59,7 @@ export { export type { Query, + AggregateQuery, Filter, LatLon, FieldFilter, diff --git a/packages/kbn-es-query/src/index.ts b/packages/kbn-es-query/src/index.ts index 5b0b0a4f1d3a9..1d9ac8ed466e5 100644 --- a/packages/kbn-es-query/src/index.ts +++ b/packages/kbn-es-query/src/index.ts @@ -28,6 +28,7 @@ export type { PhraseFilter, PhrasesFilter, Query, + AggregateQuery, QueryStringFilter, RangeFilter, RangeFilterMeta, diff --git a/src/plugins/data/common/query/types.ts b/src/plugins/data/common/query/types.ts index fea59ea558a35..e10afaf746455 100644 --- a/src/plugins/data/common/query/types.ts +++ b/src/plugins/data/common/query/types.ts @@ -10,7 +10,7 @@ import type { Query, Filter } from '@kbn/es-query'; import type { RefreshInterval, TimeRange } from './timefilter/types'; export type { RefreshInterval, TimeRange, TimeRangeBounds } from './timefilter/types'; -export type { Query } from '@kbn/es-query'; +export type { Query, AggregateQuery } from '@kbn/es-query'; export type SavedQueryTimeFilter = TimeRange & { refreshInterval: RefreshInterval; diff --git a/src/plugins/discover/public/application/main/services/discover_state.ts b/src/plugins/discover/public/application/main/services/discover_state.ts index 81f25e6151c99..b42767eb4235e 100644 --- a/src/plugins/discover/public/application/main/services/discover_state.ts +++ b/src/plugins/discover/public/application/main/services/discover_state.ts @@ -10,7 +10,13 @@ import { isEqual, cloneDeep } from 'lodash'; import { i18n } from '@kbn/i18n'; import { History } from 'history'; import { NotificationsStart, IUiSettingsClient } from '@kbn/core/public'; -import { Filter, FilterStateStore, compareFilters, COMPARE_ALL_OPTIONS } from '@kbn/es-query'; +import { + Filter, + FilterStateStore, + compareFilters, + COMPARE_ALL_OPTIONS, + AggregateQuery, +} from '@kbn/es-query'; import { createKbnUrlStateStorage, createStateContainer, @@ -170,6 +176,10 @@ export interface GetStateReturn { } const APP_STATE_URL_KEY = '_a'; +function isOfAggregateQueryType(arg: AggregateQuery | Query): arg is AggregateQuery { + return Boolean(arg && 'sql' in arg); +} + /** * Builds and returns appState and globalState containers and helper functions * Used to sync URL with UI state @@ -191,7 +201,11 @@ export function getState({ const appStateFromUrl = stateStorage.get(APP_STATE_URL_KEY) as AppState; if (appStateFromUrl && appStateFromUrl.query && !appStateFromUrl.query.language) { - appStateFromUrl.query = migrateLegacyQuery(appStateFromUrl.query); + if (isOfAggregateQueryType(appStateFromUrl.query)) { + appStateFromUrl.query = appStateFromUrl.query; + } else { + appStateFromUrl.query = migrateLegacyQuery(appStateFromUrl.query); + } } if (appStateFromUrl?.sort && !appStateFromUrl.sort.length) { diff --git a/src/plugins/unified_search/public/dataview_picker/change_dataview.tsx b/src/plugins/unified_search/public/dataview_picker/change_dataview.tsx index 4a918221b8568..9a0dfdea07d5f 100644 --- a/src/plugins/unified_search/public/dataview_picker/change_dataview.tsx +++ b/src/plugins/unified_search/public/dataview_picker/change_dataview.tsx @@ -29,7 +29,7 @@ import { import type { DataViewListItem } from '@kbn/data-views-plugin/public'; import { IDataPluginServices } from '@kbn/data-plugin/public'; import { useKibana } from '@kbn/kibana-react-plugin/public'; -import type { DataViewPickerProps } from '.'; +import type { DataViewPickerPropsExtended } from '.'; import { DataViewsList } from './dataview_list'; import type { TextBasedLanguagesListProps } from './text_languages_list'; import type { TextBasedLanguagesTransitionModalProps } from './text_languages_transition_modal'; @@ -90,12 +90,16 @@ export function ChangeDataView({ showNewMenuTour = false, textBasedLanguages, onSaveTextLanguageQuery, -}: DataViewPickerProps) { + onTextLangQuerySubmit, + textBasedLanguage, +}: DataViewPickerPropsExtended) { const { euiTheme } = useEuiTheme(); const [isPopoverOpen, setPopoverIsOpen] = useState(false); const [dataViewsList, setDataViewsList] = useState([]); const [triggerLabel, setTriggerLabel] = useState(''); - const [isTextBasedLangSelected, setIsTextBasedLangSelected] = useState(false); + const [isTextBasedLangSelected, setIsTextBasedLangSelected] = useState( + Boolean(textBasedLanguage) + ); const [isTextLangTransitionModalVisible, setIsTextLangTransitionModalVisible] = useState(false); const kibana = useKibana(); @@ -134,9 +138,13 @@ export function ChangeDataView({ useEffect(() => { if (trigger.label) { - setTriggerLabel(trigger.label); + if (textBasedLanguage) { + setTriggerLabel(textBasedLanguage.toUpperCase()); + } else { + setTriggerLabel(trigger.label); + } } - }, [trigger.label]); + }, [textBasedLanguage, trigger.label]); const createTrigger = function () { const { label, title, 'data-test-subj': dataTestSubj, fullWidth, ...rest } = trigger; @@ -286,6 +294,7 @@ export function ChangeDataView({ setPopoverIsOpen(false); setIsTextBasedLangSelected(true); // also update the query with the sql query + onTextLangQuerySubmit({ sql: `SELECT * FROM ${trigger.title}` }); }} /> ); @@ -301,39 +310,25 @@ export function ChangeDataView({ setIsTextLangTransitionModalDismissed(true); }, [storage]); - const handleCloseTransitionModal = useCallback( - (shouldDismissModal: boolean) => { + const onModalClose = useCallback( + (shouldDismissModal: boolean, needsSave?: boolean) => { setIsTextLangTransitionModalVisible(false); - setIsTextBasedLangSelected(false); - // clean up the text based language query - - if (shouldDismissModal) { - onTransitionModalDismiss(); + if (Boolean(needsSave)) { + onSaveTextLanguageQuery?.(); } - }, - [onTransitionModalDismiss] - ); - - const handleSaveTransitionModal = useCallback( - (shouldDismissModal: boolean) => { - setIsTextLangTransitionModalVisible(false); - onSaveTextLanguageQuery?.(); setIsTextBasedLangSelected(false); // clean up the text based language query + onTextLangQuerySubmit(); + setTriggerLabel(trigger.label); if (shouldDismissModal) { onTransitionModalDismiss(); } }, - [onSaveTextLanguageQuery, onTransitionModalDismiss] + [onSaveTextLanguageQuery, onTextLangQuerySubmit, onTransitionModalDismiss, trigger.label] ); if (isTextLangTransitionModalVisible && !isTextLangTransitionModalDismissed) { - modal = ( - - ); + modal = ; } return ( diff --git a/src/plugins/unified_search/public/dataview_picker/index.tsx b/src/plugins/unified_search/public/dataview_picker/index.tsx index bed8ae2a37d69..97e8e03bcec55 100644 --- a/src/plugins/unified_search/public/dataview_picker/index.tsx +++ b/src/plugins/unified_search/public/dataview_picker/index.tsx @@ -8,6 +8,7 @@ import React from 'react'; import type { EuiButtonProps, EuiSelectableProps } from '@elastic/eui'; +import type { AggregateQuery } from '@kbn/es-query'; import { ChangeDataView } from './change_dataview'; export type ChangeDataViewTriggerProps = EuiButtonProps & { @@ -36,6 +37,11 @@ export interface DataViewPickerProps { onSaveTextLanguageQuery?: () => void; } +export interface DataViewPickerPropsExtended extends DataViewPickerProps { + onTextLangQuerySubmit: (query?: AggregateQuery) => void; + textBasedLanguage?: string; +} + export const DataViewPicker = ({ isMissingCurrent, currentDataViewId, @@ -47,7 +53,9 @@ export const DataViewPicker = ({ showNewMenuTour, textBasedLanguages, onSaveTextLanguageQuery, -}: DataViewPickerProps) => { + onTextLangQuerySubmit, + textBasedLanguage, +}: DataViewPickerPropsExtended) => { return ( ); }; diff --git a/src/plugins/unified_search/public/dataview_picker/text_languages_transition_modal.tsx b/src/plugins/unified_search/public/dataview_picker/text_languages_transition_modal.tsx index 6d35e74dab492..1405662db2bff 100644 --- a/src/plugins/unified_search/public/dataview_picker/text_languages_transition_modal.tsx +++ b/src/plugins/unified_search/public/dataview_picker/text_languages_transition_modal.tsx @@ -25,15 +25,12 @@ import { } from '@elastic/eui'; export interface TextBasedLanguagesTransitionModalProps { - closeModal: (dismissFlag: boolean) => void; - // defined by the application - onSave: (dismissFlag: boolean) => void; + closeModal: (dismissFlag: boolean, needsSave?: boolean) => void; } // Needed for React.lazy // eslint-disable-next-line import/no-default-export export default function TextBasedLanguagesTransitionModal({ closeModal, - onSave, }: TextBasedLanguagesTransitionModalProps) { const [dismissModalChecked, setDismissModalChecked] = useState(false); const onTransitionModalDismiss = useCallback((e) => { @@ -108,7 +105,7 @@ export default function TextBasedLanguagesTransitionModal({ onSave(dismissModalChecked)} + onClick={() => closeModal(dismissModalChecked, true)} fill color="success" iconType="save" diff --git a/src/plugins/unified_search/public/query_string_input/query_bar_top_row.tsx b/src/plugins/unified_search/public/query_string_input/query_bar_top_row.tsx index f982f4cc2280f..3aba3bcacff50 100644 --- a/src/plugins/unified_search/public/query_string_input/query_bar_top_row.tsx +++ b/src/plugins/unified_search/public/query_string_input/query_bar_top_row.tsx @@ -11,7 +11,7 @@ import classNames from 'classnames'; import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import deepEqual from 'fast-deep-equal'; import useObservable from 'react-use/lib/useObservable'; -import type { Filter, TimeRange } from '@kbn/es-query'; +import type { Filter, TimeRange, AggregateQuery } from '@kbn/es-query'; import { EMPTY } from 'rxjs'; import { map } from 'rxjs/operators'; import { @@ -43,12 +43,17 @@ import { AddFilterPopover } from './add_filter_popover'; import { DataViewPicker, DataViewPickerProps } from '../dataview_picker'; import { FilterButtonGroup } from '../filter_bar/filter_button_group/filter_button_group'; import type { SuggestionsListSize } from '../typeahead/suggestions_component'; +import { TextBasedLanguagesEditor } from './text_based_languages_editor'; import './query_bar.scss'; const SuperDatePicker = React.memo( EuiSuperDatePicker as any ) as unknown as typeof EuiSuperDatePicker; +function isOfQueryType(arg: any): arg is Query { + return Boolean(arg && 'query' in arg); +} + const QueryStringInput = withKibana(QueryStringInputUI); // @internal @@ -90,6 +95,7 @@ export interface QueryBarTopRowProps { showSubmitButton?: boolean; suggestionsSize?: SuggestionsListSize; isScreenshotMode?: boolean; + onTextLangQuerySubmit: (query: any) => void; } const SharingMetaFields = React.memo(function SharingMetaFields({ @@ -153,6 +159,7 @@ export const QueryBarTopRow = React.memo( const kibana = useKibana(); const { uiSettings, storage, appName } = kibana.services; + const isQueryLangSelected = props.query && !isOfQueryType(props.query); const queryLanguage = props.query && props.query.language; const queryRef = useRef(props.query); @@ -419,13 +426,19 @@ export const QueryBarTopRow = React.memo( function renderDataViewsPicker() { if (!props.dataViewPickerComponentProps) return; - + let textBasedLanguage; + if (Boolean(isQueryLangSelected)) { + const query = props.query as AggregateQuery; + textBasedLanguage = Object.keys(query)[0]; + } return ( ); @@ -492,6 +505,14 @@ export const QueryBarTopRow = React.memo( ); } + function renderTextLangEditor() { + return ( + + {isQueryLangSelected && props.query && } + + ); + } + const isScreenshotMode = props.isScreenshotMode === true; return ( @@ -514,9 +535,9 @@ export const QueryBarTopRow = React.memo( {renderDataViewsPicker()} - {renderQueryInput()} + {!isQueryLangSelected ? renderQueryInput() : renderTextLangEditor()} {shouldShowDatePickerAsBadge() && props.filterBar} {renderUpdateButton()} diff --git a/src/plugins/unified_search/public/query_string_input/text_based_languages_editor/helpers.ts b/src/plugins/unified_search/public/query_string_input/text_based_languages_editor/helpers.ts new file mode 100644 index 0000000000000..27a1631ce11ec --- /dev/null +++ b/src/plugins/unified_search/public/query_string_input/text_based_languages_editor/helpers.ts @@ -0,0 +1,32 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { useRef } from 'react'; +import useDebounce from 'react-use/lib/useDebounce'; + +export const useDebounceWithOptions = ( + fn: Function, + { skipFirstRender }: { skipFirstRender: boolean } = { skipFirstRender: false }, + ms?: number | undefined, + deps?: React.DependencyList | undefined +) => { + const isFirstRender = useRef(true); + const newDeps = [...(deps || []), isFirstRender]; + + return useDebounce( + () => { + if (skipFirstRender && isFirstRender.current) { + isFirstRender.current = false; + return; + } + return fn(); + }, + ms, + newDeps + ); +}; diff --git a/src/plugins/unified_search/public/query_string_input/text_based_languages_editor/index.tsx b/src/plugins/unified_search/public/query_string_input/text_based_languages_editor/index.tsx new file mode 100644 index 0000000000000..8f104e28dd338 --- /dev/null +++ b/src/plugins/unified_search/public/query_string_input/text_based_languages_editor/index.tsx @@ -0,0 +1,212 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import React, { useRef, memo, useEffect, useState } from 'react'; +import { EsqlLang, monaco } from '@kbn/monaco'; + +import { i18n } from '@kbn/i18n'; +import { + EuiBadge, + useEuiTheme, + EuiText, + EuiFlexGroup, + EuiFlexItem, + EuiResizeObserver, + EuiOutsideClickDetector, +} from '@elastic/eui'; +import { CodeEditor } from '@kbn/kibana-react-plugin/public'; +import { + textBasedLanguagedEditorStyles, + EDITOR_INITIAL_HEIGHT, +} from './text_based_languages_editor.styles'; +import { useDebounceWithOptions } from './helpers'; + +interface TextBasedLanguagesEditorProps { + query: any; +} + +const MAX_COMPACT_VIEW_LENGTH = 250; +const OS_COMMAND_KEY = '⌘'; + +const getTextBasedLanguage = (query: any) => { + return Object.keys(query)[0]; +}; + +const languageId = (language: string) => { + switch (language) { + case 'sql': + default: { + return EsqlLang.ID; + } + } +}; +let clickedOutside = false; + +export const TextBasedLanguagesEditor = memo(function TextBasedLanguagesEditor({ + query, +}: TextBasedLanguagesEditorProps) { + const { euiTheme } = useEuiTheme(); + const language = getTextBasedLanguage(query); + const editorModel = useRef(); + const editor1 = useRef(); + const [lines, setLines] = useState(1); + const [code, setCode] = useState(query.sql); + const [editorHeight, setEditorHeight] = useState(EDITOR_INITIAL_HEIGHT); + const [showLineNumbers, setShowLineNumbers] = useState(false); + const [isCompactFocused, setIsCompactFocused] = useState(false); + const styles = textBasedLanguagedEditorStyles(euiTheme, isCompactFocused, editorHeight); + + const updateHeight = () => { + if (editor1.current) { + const linesCount = editorModel.current?.getLineCount() || 1; + if (linesCount === 1 || clickedOutside) return; + const editorElement = editor1.current.getDomNode(); + const contentHeight = Math.min(MAX_COMPACT_VIEW_LENGTH, editor1.current.getContentHeight()); + + if (editorElement) { + editorElement.style.height = `${contentHeight}px`; + } + const contentWidth = Number(editorElement?.style.width.replace('px', '')); + editor1.current.layout({ width: contentWidth, height: contentHeight }); + setEditorHeight(contentHeight); + } + }; + + const restoreInitialMode = () => { + setEditorHeight(EDITOR_INITIAL_HEIGHT); + setIsCompactFocused(false); + setShowLineNumbers(false); + clickedOutside = true; + if (editor1.current) { + const editorElement = editor1.current.getDomNode(); + if (editorElement) { + editorElement.style.height = `${EDITOR_INITIAL_HEIGHT}px`; + const contentWidth = Number(editorElement?.style.width.replace('px', '')); + editor1.current.layout({ width: contentWidth, height: EDITOR_INITIAL_HEIGHT }); + } + } + }; + + useDebounceWithOptions( + () => { + if (!editorModel.current) return; + editor1.current?.onDidChangeModelContent((e) => { + setLines(editorModel.current?.getLineCount() || 1); + }); + editor1.current?.onDidFocusEditorText(() => { + setIsCompactFocused(true); + clickedOutside = false; + setShowLineNumbers(true); + }); + editor1.current?.onDidContentSizeChange(updateHeight); + }, + { skipFirstRender: false }, + 256, + [] + ); + + // Clean up the monaco editor and DOM on unmount + useEffect(() => { + const model = editorModel; + const editor1ref = editor1; + return () => { + model.current?.dispose(); + editor1ref.current?.dispose(); + }; + }, []); + + const onResize = ({ width }: { width: number }) => { + if (editor1.current) { + editor1.current.layout({ width, height: editorHeight }); + } + }; + + return ( + + {(resizeRef) => ( + { + restoreInitialMode(); + }} + > +
+ +
+ {!isCompactFocused && ( + + {`${lines} lines`} + + )} + { + editor1.current = editor; + const model = editor.getModel(); + if (model) { + editorModel.current = model; + } + setLines(model?.getLineCount() || 1); + }} + /> + {isCompactFocused && ( + + + +

{`${lines} lines`}

+
+
+ + + + +

+ {i18n.translate( + 'unifiedSearch.query.textBasedLanguagesEditor.runQuery', + { + defaultMessage: 'Run query', + } + )} +

+
+
+ + {`${OS_COMMAND_KEY} + Enter`} + +
+
+
+ )} +
+
+
+
+ )} +
+ ); +}); diff --git a/src/plugins/unified_search/public/query_string_input/text_based_languages_editor/text_based_languages_editor.styles.ts b/src/plugins/unified_search/public/query_string_input/text_based_languages_editor/text_based_languages_editor.styles.ts new file mode 100644 index 0000000000000..55ee16ee529d8 --- /dev/null +++ b/src/plugins/unified_search/public/query_string_input/text_based_languages_editor/text_based_languages_editor.styles.ts @@ -0,0 +1,47 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ +import type { EuiThemeComputed } from '@elastic/eui'; + +export const EDITOR_INITIAL_HEIGHT = 40; + +export const textBasedLanguagedEditorStyles = ( + euiTheme: EuiThemeComputed, + isCompactFocused: boolean, + editorHeight: number +) => { + return { + editorContainer: { + position: isCompactFocused ? ('absolute' as 'absolute') : ('relative' as 'relative'), // cast string to type 'relative' | 'absolute' + zIndex: isCompactFocused ? 1 : 0, + height: `${editorHeight}px`, + border: isCompactFocused ? euiTheme.border.thin : 'none', + }, + resizableContainer: { + display: 'flex', + width: '100%', + alignItems: isCompactFocused ? 'flex-start' : 'center', + border: !isCompactFocused ? euiTheme.border.thin : 'none', + }, + linesBadge: { + position: 'absolute' as 'absolute', // cast string to type 'absolute', + zIndex: 1, + right: '16px', + top: '50%', + transform: 'translate(0, -50%)', + }, + bottomContainer: { + borderTop: `1px solid ${euiTheme.colors.primary}`, + borderBottom: euiTheme.border.thin, + backgroundColor: euiTheme.colors.lightestShade, + paddingLeft: euiTheme.size.s, + paddingRight: euiTheme.size.s, + position: 'relative' as 'relative', // cast string to type 'relative', + marginTop: 0, + }, + }; +}; diff --git a/src/plugins/unified_search/public/search_bar/lib/use_query_string_manager.ts b/src/plugins/unified_search/public/search_bar/lib/use_query_string_manager.ts index 10b2c7b64e97c..19a1a6f96fa82 100644 --- a/src/plugins/unified_search/public/search_bar/lib/use_query_string_manager.ts +++ b/src/plugins/unified_search/public/search_bar/lib/use_query_string_manager.ts @@ -15,7 +15,9 @@ interface UseQueryStringProps { query?: Query; queryStringManager: QueryStringContract; } - +function isOfQueryType(arg: any): arg is Query { + return Boolean(arg && 'query' in arg); +} export const useQueryStringManager = (props: UseQueryStringProps) => { // Filters should be either what's passed in the initial state or the current state of the filter manager const [query, setQuery] = useState(props.query || props.queryStringManager.getQuery()); @@ -36,6 +38,7 @@ export const useQueryStringManager = (props: UseQueryStringProps) => { }; }, [props.queryStringManager]); + const isQueryType = isOfQueryType(query); const stableQuery = useMemo( () => ({ language: query.language, @@ -43,6 +46,5 @@ export const useQueryStringManager = (props: UseQueryStringProps) => { }), [query.language, query.query] ); - - return { query: stableQuery }; + return { query: isQueryType ? stableQuery : query }; }; diff --git a/src/plugins/unified_search/public/search_bar/search_bar.tsx b/src/plugins/unified_search/public/search_bar/search_bar.tsx index aada686db3dc8..b8d9a3b840f91 100644 --- a/src/plugins/unified_search/public/search_bar/search_bar.tsx +++ b/src/plugins/unified_search/public/search_bar/search_bar.tsx @@ -105,6 +105,10 @@ interface State { dateRangeTo: string; } +function isOfQueryType(arg: any): arg is Query { + return Boolean(arg && 'query' in arg); +} + class SearchBarUI extends Component { public static defaultProps = { showQueryBar: true, @@ -123,20 +127,24 @@ class SearchBarUI extends Component { } let nextQuery = null; - if (nextProps.query && nextProps.query.query !== get(prevState, 'currentProps.query.query')) { - nextQuery = { - query: nextProps.query.query, - language: nextProps.query.language, - }; - } else if ( - nextProps.query && - prevState.query && - nextProps.query.language !== prevState.query.language - ) { - nextQuery = { - query: '', - language: nextProps.query.language, - }; + if (isOfQueryType(nextProps.query)) { + if (nextProps.query && nextProps.query.query !== get(prevState, 'currentProps.query.query')) { + nextQuery = { + query: nextProps.query.query, + language: nextProps.query.language, + }; + } else if ( + nextProps.query && + prevState.query && + nextProps.query.language !== prevState.query.language + ) { + nextQuery = { + query: '', + language: nextProps.query.language, + }; + } + } else { + nextQuery = nextProps.query; } let nextDateRange = null; @@ -302,6 +310,27 @@ class SearchBarUI extends Component { }); }; + public onTextLangQuerySubmit = (query?: any) => { + // clean up all filters + this.props.onFiltersUpdated?.([]); + this.setState( + { + query, + }, + () => { + if (this.props.onQuerySubmit) { + this.props.onQuerySubmit({ + query: this.state.query, + dateRange: { + from: this.state.dateRangeFrom, + to: this.state.dateRangeTo, + }, + }); + } + } + ); + }; + public onQueryBarSubmit = (queryAndDateRange: { dateRange?: TimeRange; query?: Query }) => { this.setState( { @@ -489,6 +518,7 @@ class SearchBarUI extends Component { filterBar={filterBar} suggestionsSize={this.props.suggestionsSize} isScreenshotMode={this.props.isScreenshotMode} + onTextLangQuerySubmit={this.onTextLangQuerySubmit} />
); From 84007c469cb2a021ed7b3f89cffe96248fb60624 Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Fri, 17 Jun 2022 17:05:11 +0300 Subject: [PATCH 009/115] Change to the type --- packages/kbn-es-query/src/filters/build_filters/types.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/kbn-es-query/src/filters/build_filters/types.ts b/packages/kbn-es-query/src/filters/build_filters/types.ts index b74f67441c296..7e91ff251a5fe 100644 --- a/packages/kbn-es-query/src/filters/build_filters/types.ts +++ b/packages/kbn-es-query/src/filters/build_filters/types.ts @@ -85,7 +85,7 @@ export type Query = { // eslint-disable-next-line @typescript-eslint/consistent-type-definitions export type AggregateQuery = { sql?: string; - esql: string; + esql?: string; }; /** From 6a4f5ed35b84ca141fb81715d0aaa94896b3005e Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Mon, 20 Jun 2022 09:14:06 +0300 Subject: [PATCH 010/115] Fixes types checks --- .../public/dataview_picker/change_dataview.test.tsx | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/plugins/unified_search/public/dataview_picker/change_dataview.test.tsx b/src/plugins/unified_search/public/dataview_picker/change_dataview.test.tsx index d3081561a0c4e..e6426d0f242b2 100644 --- a/src/plugins/unified_search/public/dataview_picker/change_dataview.test.tsx +++ b/src/plugins/unified_search/public/dataview_picker/change_dataview.test.tsx @@ -15,7 +15,7 @@ import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public'; import { dataPluginMock } from '@kbn/data-plugin/public/mocks'; import { ChangeDataView } from './change_dataview'; import { EuiTourStep } from '@elastic/eui'; -import type { DataViewPickerProps } from '.'; +import type { DataViewPickerPropsExtended } from '.'; describe('DataView component', () => { const createMockWebStorage = () => ({ @@ -40,7 +40,10 @@ describe('DataView component', () => { return storage; }; - function wrapDataViewComponentInContext(testProps: DataViewPickerProps, storageValue: boolean) { + function wrapDataViewComponentInContext( + testProps: DataViewPickerPropsExtended, + storageValue: boolean + ) { let dataMock = dataPluginMock.createStartContract(); dataMock = { ...dataMock, @@ -62,7 +65,7 @@ describe('DataView component', () => { ); } - let props: DataViewPickerProps; + let props: DataViewPickerPropsExtended; beforeEach(() => { props = { currentDataViewId: 'dataview-1', @@ -73,6 +76,7 @@ describe('DataView component', () => { 'data-test-subj': 'dataview-trigger', }, onChangeDataView: jest.fn(), + onTextLangQuerySubmit: jest.fn(), }; }); it('should not render the tour component by default', async () => { From e3c437d4afa25127e0cf246794f7ea2a41d51646 Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Mon, 20 Jun 2022 11:41:14 +0300 Subject: [PATCH 011/115] New submit button for query mode --- .../query_string/query_string_manager.ts | 13 ++++++++++++- .../dataview_picker/change_dataview.tsx | 4 ++-- .../public/dataview_picker/index.tsx | 2 +- .../query_string_input/query_bar_top_row.tsx | 19 ++++++++++++++++--- .../text_based_languages_editor/index.tsx | 14 ++++++++++++-- .../public/search_bar/search_bar.tsx | 17 +++++++++++++++++ 6 files changed, 60 insertions(+), 9 deletions(-) diff --git a/src/plugins/data/public/query/query_string/query_string_manager.ts b/src/plugins/data/public/query/query_string/query_string_manager.ts index fb9c2b1a734fa..1ef73ebd0168c 100644 --- a/src/plugins/data/public/query/query_string/query_string_manager.ts +++ b/src/plugins/data/public/query/query_string/query_string_manager.ts @@ -11,8 +11,13 @@ import { skip } from 'rxjs/operators'; import { PublicMethodsOf } from '@kbn/utility-types'; import { CoreStart } from '@kbn/core/public'; import { IStorageWrapper } from '@kbn/kibana-utils-plugin/public'; +import { isEqual } from 'lodash'; import { KIBANA_USER_QUERY_LANGUAGE_KEY, Query, UI_SETTINGS } from '../../../common'; +function isOfQueryType(arg: any): arg is Query { + return Boolean(arg && 'query' in arg); +} + export class QueryStringManager { private query$: BehaviorSubject; @@ -64,7 +69,13 @@ export class QueryStringManager { */ public setQuery = (query: Query) => { const curQuery = this.query$.getValue(); - if (query?.language !== curQuery.language || query?.query !== curQuery.query) { + const isOfTypeQuery = isOfQueryType(query); + if ( + (isOfTypeQuery && query?.language !== curQuery.language) || + query?.query !== curQuery.query + ) { + this.query$.next(query); + } else if (!isOfTypeQuery && !isEqual(query, curQuery)) { this.query$.next(query); } }; diff --git a/src/plugins/unified_search/public/dataview_picker/change_dataview.tsx b/src/plugins/unified_search/public/dataview_picker/change_dataview.tsx index 9a0dfdea07d5f..89181988b8510 100644 --- a/src/plugins/unified_search/public/dataview_picker/change_dataview.tsx +++ b/src/plugins/unified_search/public/dataview_picker/change_dataview.tsx @@ -294,7 +294,7 @@ export function ChangeDataView({ setPopoverIsOpen(false); setIsTextBasedLangSelected(true); // also update the query with the sql query - onTextLangQuerySubmit({ sql: `SELECT * FROM ${trigger.title}` }); + onTextLangQuerySubmit?.({ sql: `SELECT * FROM ${trigger.title}` }); }} /> ); @@ -318,7 +318,7 @@ export function ChangeDataView({ } setIsTextBasedLangSelected(false); // clean up the text based language query - onTextLangQuerySubmit(); + onTextLangQuerySubmit?.(); setTriggerLabel(trigger.label); if (shouldDismissModal) { onTransitionModalDismiss(); diff --git a/src/plugins/unified_search/public/dataview_picker/index.tsx b/src/plugins/unified_search/public/dataview_picker/index.tsx index 97e8e03bcec55..2b2ab7fe2b995 100644 --- a/src/plugins/unified_search/public/dataview_picker/index.tsx +++ b/src/plugins/unified_search/public/dataview_picker/index.tsx @@ -38,7 +38,7 @@ export interface DataViewPickerProps { } export interface DataViewPickerPropsExtended extends DataViewPickerProps { - onTextLangQuerySubmit: (query?: AggregateQuery) => void; + onTextLangQuerySubmit?: (query?: AggregateQuery) => void; textBasedLanguage?: string; } diff --git a/src/plugins/unified_search/public/query_string_input/query_bar_top_row.tsx b/src/plugins/unified_search/public/query_string_input/query_bar_top_row.tsx index 3aba3bcacff50..8fda418bbd24c 100644 --- a/src/plugins/unified_search/public/query_string_input/query_bar_top_row.tsx +++ b/src/plugins/unified_search/public/query_string_input/query_bar_top_row.tsx @@ -96,6 +96,7 @@ export interface QueryBarTopRowProps { suggestionsSize?: SuggestionsListSize; isScreenshotMode?: boolean; onTextLangQuerySubmit: (query: any) => void; + onTextLangQueryChange: (query: any) => void; } const SharingMetaFields = React.memo(function SharingMetaFields({ @@ -383,12 +384,19 @@ export const QueryBarTopRow = React.memo( } ); + const buttonLabelRun = i18n.translate('unifiedSearch.queryBarTopRow.submitButton.run', { + defaultMessage: 'Run query', + }); + + const iconDirty = Boolean(isQueryLangSelected) ? 'playFilled' : 'kqlFunction'; + const tooltipDirty = Boolean(isQueryLangSelected) ? buttonLabelRun : buttonLabelUpdate; + const button = props.customSubmitButton ? ( React.cloneElement(props.customSubmitButton, { onClick: onClickSubmitButton }) ) : ( - {isQueryLangSelected && props.query && } + {isQueryLangSelected && props.query && ( + + )} ); } diff --git a/src/plugins/unified_search/public/query_string_input/text_based_languages_editor/index.tsx b/src/plugins/unified_search/public/query_string_input/text_based_languages_editor/index.tsx index 8f104e28dd338..2006e7f214b90 100644 --- a/src/plugins/unified_search/public/query_string_input/text_based_languages_editor/index.tsx +++ b/src/plugins/unified_search/public/query_string_input/text_based_languages_editor/index.tsx @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import React, { useRef, memo, useEffect, useState } from 'react'; +import React, { useRef, memo, useEffect, useState, useCallback } from 'react'; import { EsqlLang, monaco } from '@kbn/monaco'; import { i18n } from '@kbn/i18n'; @@ -28,6 +28,7 @@ import { useDebounceWithOptions } from './helpers'; interface TextBasedLanguagesEditorProps { query: any; + onTextLangQueryChange: (query: any) => void; } const MAX_COMPACT_VIEW_LENGTH = 250; @@ -49,6 +50,7 @@ let clickedOutside = false; export const TextBasedLanguagesEditor = memo(function TextBasedLanguagesEditor({ query, + onTextLangQueryChange, }: TextBasedLanguagesEditorProps) { const { euiTheme } = useEuiTheme(); const language = getTextBasedLanguage(query); @@ -126,6 +128,14 @@ export const TextBasedLanguagesEditor = memo(function TextBasedLanguagesEditor({ } }; + const onQueryUpdate = useCallback( + (value: string) => { + setCode(value); + onTextLangQueryChange({ sql: value }); + }, + [onTextLangQueryChange] + ); + return ( {(resizeRef) => ( @@ -159,7 +169,7 @@ export const TextBasedLanguagesEditor = memo(function TextBasedLanguagesEditor({ wrappingIndent: 'none', }} width="100%" - onChange={setCode} + onChange={onQueryUpdate} editorDidMount={(editor) => { editor1.current = editor; const model = editor.getModel(); diff --git a/src/plugins/unified_search/public/search_bar/search_bar.tsx b/src/plugins/unified_search/public/search_bar/search_bar.tsx index b8d9a3b840f91..1231fc719e98b 100644 --- a/src/plugins/unified_search/public/search_bar/search_bar.tsx +++ b/src/plugins/unified_search/public/search_bar/search_bar.tsx @@ -200,6 +200,7 @@ class SearchBarUI extends Component { return ( (this.state.query && this.props.query && this.state.query.query !== this.props.query.query) || + (this.state.query && this.props.query && !isEqual(this.state.query, this.props.query)) || this.state.dateRangeFrom !== this.props.dateRangeFrom || this.state.dateRangeTo !== this.props.dateRangeTo ); @@ -304,6 +305,21 @@ class SearchBarUI extends Component { } }; + public onTextLangQueryChange = (query?: any) => { + this.setState({ + query, + }); + if (this.props.onQueryChange) { + this.props.onQueryChange({ + query, + dateRange: { + from: this.state.dateRangeFrom, + to: this.state.dateRangeTo, + }, + }); + } + }; + public toggleFilterBarMenuPopover = (value: boolean) => { this.setState({ openQueryBarMenu: value, @@ -519,6 +535,7 @@ class SearchBarUI extends Component { suggestionsSize={this.props.suggestionsSize} isScreenshotMode={this.props.isScreenshotMode} onTextLangQuerySubmit={this.onTextLangQuerySubmit} + onTextLangQueryChange={this.onTextLangQueryChange} /> ); From edc5277cea627eb393baf8aa43a270935e3737c9 Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Mon, 20 Jun 2022 17:28:40 +0300 Subject: [PATCH 012/115] Implememtation of the expanded mode of the editor --- .../query_string_input/query_bar_top_row.tsx | 25 +- .../text_based_languages_editor/index.tsx | 331 +++++++++++++----- .../text_based_languages_editor.styles.ts | 38 +- 3 files changed, 302 insertions(+), 92 deletions(-) diff --git a/src/plugins/unified_search/public/query_string_input/query_bar_top_row.tsx b/src/plugins/unified_search/public/query_string_input/query_bar_top_row.tsx index 8fda418bbd24c..b7eeb9f5cb12e 100644 --- a/src/plugins/unified_search/public/query_string_input/query_bar_top_row.tsx +++ b/src/plugins/unified_search/public/query_string_input/query_bar_top_row.tsx @@ -135,6 +135,7 @@ export const QueryBarTopRow = React.memo( function QueryBarTopRow(props: QueryBarTopRowProps) { const isMobile = useIsWithinBreakpoints(['xs', 's']); const [isXXLarge, setIsXXLarge] = useState(false); + const [codeEditorIsExpanded, setCodeEditorIsExpanded] = useState(false); useEffect(() => { function handleResize() { @@ -515,14 +516,15 @@ export const QueryBarTopRow = React.memo( function renderTextLangEditor() { return ( - - {isQueryLangSelected && props.query && ( - - )} - + isQueryLangSelected && + props.query && ( + setCodeEditorIsExpanded(status)} + isCodeEditorExpanded={codeEditorIsExpanded} + /> + ) ); } @@ -550,12 +552,17 @@ export const QueryBarTopRow = React.memo( grow={!shouldShowDatePickerAsBadge()} style={{ minWidth: shouldShowDatePickerAsBadge() ? 'auto' : 320, maxWidth: '100%' }} > - {!isQueryLangSelected ? renderQueryInput() : renderTextLangEditor()} + {!isQueryLangSelected + ? renderQueryInput() + : !codeEditorIsExpanded + ? renderTextLangEditor() + : null} {shouldShowDatePickerAsBadge() && props.filterBar} {renderUpdateButton()} {!shouldShowDatePickerAsBadge() && props.filterBar} + {codeEditorIsExpanded && renderTextLangEditor()} )} diff --git a/src/plugins/unified_search/public/query_string_input/text_based_languages_editor/index.tsx b/src/plugins/unified_search/public/query_string_input/text_based_languages_editor/index.tsx index 2006e7f214b90..2895b72596e07 100644 --- a/src/plugins/unified_search/public/query_string_input/text_based_languages_editor/index.tsx +++ b/src/plugins/unified_search/public/query_string_input/text_based_languages_editor/index.tsx @@ -16,19 +16,27 @@ import { EuiText, EuiFlexGroup, EuiFlexItem, + EuiButtonIcon, EuiResizeObserver, EuiOutsideClickDetector, } from '@elastic/eui'; import { CodeEditor } from '@kbn/kibana-react-plugin/public'; +import type { CodeEditorProps } from '@kbn/kibana-react-plugin/public'; + import { textBasedLanguagedEditorStyles, EDITOR_INITIAL_HEIGHT, + EDITOR_INITIAL_HEIGHT_EXPANDED, + EDITOR_MAX_HEIGHT, + EDITOR_MIN_HEIGHT, } from './text_based_languages_editor.styles'; import { useDebounceWithOptions } from './helpers'; interface TextBasedLanguagesEditorProps { query: any; onTextLangQueryChange: (query: any) => void; + expandCodeEditor: (status: boolean) => void; + isCodeEditorExpanded: boolean; } const MAX_COMPACT_VIEW_LENGTH = 250; @@ -47,10 +55,13 @@ const languageId = (language: string) => { } }; let clickedOutside = false; +let initialRender = true; export const TextBasedLanguagesEditor = memo(function TextBasedLanguagesEditor({ query, onTextLangQueryChange, + expandCodeEditor, + isCodeEditorExpanded, }: TextBasedLanguagesEditorProps) { const { euiTheme } = useEuiTheme(); const language = getTextBasedLanguage(query); @@ -58,15 +69,48 @@ export const TextBasedLanguagesEditor = memo(function TextBasedLanguagesEditor({ const editor1 = useRef(); const [lines, setLines] = useState(1); const [code, setCode] = useState(query.sql); - const [editorHeight, setEditorHeight] = useState(EDITOR_INITIAL_HEIGHT); - const [showLineNumbers, setShowLineNumbers] = useState(false); - const [isCompactFocused, setIsCompactFocused] = useState(false); - const styles = textBasedLanguagedEditorStyles(euiTheme, isCompactFocused, editorHeight); + const [editorHeight, setEditorHeight] = useState( + isCodeEditorExpanded ? EDITOR_INITIAL_HEIGHT_EXPANDED : EDITOR_INITIAL_HEIGHT + ); + const [showLineNumbers, setShowLineNumbers] = useState(isCodeEditorExpanded); + const [isCompactFocused, setIsCompactFocused] = useState(isCodeEditorExpanded); + const [isWordWrapped, setIsWordWrapped] = useState(true); + const [userDrags, setUserDrags] = useState(false); + const styles = textBasedLanguagedEditorStyles( + euiTheme, + isCompactFocused, + editorHeight, + isCodeEditorExpanded + ); + + const ref = useRef(null); + + const onMouseDownResizeHandler = useCallback( + (mouseDownEvent: React.MouseEvent) => { + const startSize = editorHeight; + const startPosition = mouseDownEvent.pageY; + + function onMouseMove(mouseMoveEvent: MouseEvent) { + const height = startSize - startPosition + mouseMoveEvent.pageY; + const validatedHeight = Math.min(Math.max(height, EDITOR_MIN_HEIGHT), EDITOR_MAX_HEIGHT); + setEditorHeight(validatedHeight); + setUserDrags(true); + } + function onMouseUp() { + document.body.removeEventListener('mousemove', onMouseMove); + setUserDrags(false); + } + + document.body.addEventListener('mousemove', onMouseMove); + document.body.addEventListener('mouseup', onMouseUp, { once: true }); + }, + [editorHeight] + ); const updateHeight = () => { if (editor1.current) { const linesCount = editorModel.current?.getLineCount() || 1; - if (linesCount === 1 || clickedOutside) return; + if (linesCount === 1 || clickedOutside || initialRender) return; const editorElement = editor1.current.getDomNode(); const contentHeight = Math.min(MAX_COMPACT_VIEW_LENGTH, editor1.current.getContentHeight()); @@ -80,6 +124,7 @@ export const TextBasedLanguagesEditor = memo(function TextBasedLanguagesEditor({ }; const restoreInitialMode = () => { + if (isCodeEditorExpanded) return; setEditorHeight(EDITOR_INITIAL_HEIGHT); setIsCompactFocused(false); setShowLineNumbers(false); @@ -103,9 +148,12 @@ export const TextBasedLanguagesEditor = memo(function TextBasedLanguagesEditor({ editor1.current?.onDidFocusEditorText(() => { setIsCompactFocused(true); clickedOutside = false; + initialRender = false; setShowLineNumbers(true); }); - editor1.current?.onDidContentSizeChange(updateHeight); + if (!isCodeEditorExpanded) { + editor1.current?.onDidContentSizeChange(updateHeight); + } }, { skipFirstRender: false }, 256, @@ -136,87 +184,214 @@ export const TextBasedLanguagesEditor = memo(function TextBasedLanguagesEditor({ [onTextLangQueryChange] ); - return ( - - {(resizeRef) => ( - { - restoreInitialMode(); - }} + const codeEditorOptions: CodeEditorProps['options'] = { + automaticLayout: false, + folding: false, + fontSize: 14, + scrollBeyondLastLine: false, + quickSuggestions: true, + minimap: { enabled: false }, + wordWrap: isWordWrapped ? 'on' : 'off', + lineNumbers: showLineNumbers ? 'on' : 'off', + lineDecorationsWidth: 16, + autoIndent: 'brackets', + wrappingIndent: 'none', + overviewRulerLanes: 0, + hideCursorInOverviewRuler: true, + scrollbar: { + vertical: 'auto', + }, + overviewRulerBorder: false, + }; + + if (isCompactFocused) { + codeEditorOptions.overviewRulerLanes = 4; + codeEditorOptions.hideCursorInOverviewRuler = false; + codeEditorOptions.overviewRulerBorder = true; + } + + const editorPanel = ( + <> + {isCodeEditorExpanded && ( + -
- -
- {!isCompactFocused && ( - - {`${lines} lines`} - - )} - { - editor1.current = editor; - const model = editor.getModel(); - if (model) { - editorModel.current = model; - } - setLines(model?.getLineCount() || 1); - }} - /> - {isCompactFocused && ( - - - -

{`${lines} lines`}

-
-
- - + + { + editor1.current?.updateOptions({ + wordWrap: isWordWrapped ? 'off' : 'on', + }); + setIsWordWrapped(!isWordWrapped); + }} + /> + + + { + expandCodeEditor(false); + }} + /> + + + )} + + + {(resizeRef) => ( + { + restoreInitialMode(); + }} + > +
+ +
+ {!isCompactFocused && ( + + {`${lines} lines`} + + )} + { + editor1.current = editor; + const model = editor.getModel(); + if (model) { + editorModel.current = model; + } + setLines(model?.getLineCount() || 1); + }} + /> + {isCompactFocused && !isCodeEditorExpanded && ( + -

- {i18n.translate( - 'unifiedSearch.query.textBasedLanguagesEditor.runQuery', - { - defaultMessage: 'Run query', - } - )} -

+

{`${lines} lines`}

- {`${OS_COMMAND_KEY} + Enter`} + + + +

+ {i18n.translate( + 'unifiedSearch.query.textBasedLanguagesEditor.runQuery', + { + defaultMessage: 'Run query', + } + )} +

+
+
+ + {`${OS_COMMAND_KEY} + Enter`} + +
- - - )} + )} +
+
-
-
- + + )} + + {!isCodeEditorExpanded && ( + + expandCodeEditor(true)} + /> + + )} + + {isCodeEditorExpanded && ( + + + +

{`${lines} lines`}

+
+
+ + + + +

+ {i18n.translate('unifiedSearch.query.textBasedLanguagesEditor.runQuery', { + defaultMessage: 'Run query', + })} +

+
+
+ + {`${OS_COMMAND_KEY} + Enter`} + +
+
+
)} - + {isCodeEditorExpanded && ( +
+ {!userDrags && ( + + )} + {userDrags &&
} +
+ )} + ); + + return editorPanel; }); diff --git a/src/plugins/unified_search/public/query_string_input/text_based_languages_editor/text_based_languages_editor.styles.ts b/src/plugins/unified_search/public/query_string_input/text_based_languages_editor/text_based_languages_editor.styles.ts index 55ee16ee529d8..92674ce5b3fae 100644 --- a/src/plugins/unified_search/public/query_string_input/text_based_languages_editor/text_based_languages_editor.styles.ts +++ b/src/plugins/unified_search/public/query_string_input/text_based_languages_editor/text_based_languages_editor.styles.ts @@ -8,22 +8,31 @@ import type { EuiThemeComputed } from '@elastic/eui'; export const EDITOR_INITIAL_HEIGHT = 40; +export const EDITOR_INITIAL_HEIGHT_EXPANDED = 140; +export const EDITOR_MIN_HEIGHT = 40; +export const EDITOR_MAX_HEIGHT = 400; export const textBasedLanguagedEditorStyles = ( euiTheme: EuiThemeComputed, isCompactFocused: boolean, - editorHeight: number + editorHeight: number, + isCodeEditorExpanded: boolean ) => { + let position = isCompactFocused ? ('absolute' as 'absolute') : ('relative' as 'relative'); // cast string to type 'relative' | 'absolute' + if (isCodeEditorExpanded) { + position = 'relative' as 'relative'; + } return { editorContainer: { - position: isCompactFocused ? ('absolute' as 'absolute') : ('relative' as 'relative'), // cast string to type 'relative' | 'absolute' + position, zIndex: isCompactFocused ? 1 : 0, height: `${editorHeight}px`, border: isCompactFocused ? euiTheme.border.thin : 'none', + borderRight: euiTheme.border.thin, }, resizableContainer: { display: 'flex', - width: '100%', + width: isCodeEditorExpanded ? '100%' : 'calc(100% - 40px)', alignItems: isCompactFocused ? 'flex-start' : 'center', border: !isCompactFocused ? euiTheme.border.thin : 'none', }, @@ -35,13 +44,32 @@ export const textBasedLanguagedEditorStyles = ( transform: 'translate(0, -50%)', }, bottomContainer: { - borderTop: `1px solid ${euiTheme.colors.primary}`, - borderBottom: euiTheme.border.thin, + border: euiTheme.border.thin, + borderTop: isCodeEditorExpanded ? 'none' : `1px solid ${euiTheme.colors.primary}`, backgroundColor: euiTheme.colors.lightestShade, paddingLeft: euiTheme.size.s, paddingRight: euiTheme.size.s, position: 'relative' as 'relative', // cast string to type 'relative', marginTop: 0, + marginLeft: 0, + }, + topContainer: { + border: euiTheme.border.thin, + backgroundColor: euiTheme.colors.lightestShade, + paddingLeft: euiTheme.size.s, + paddingRight: euiTheme.size.s, + position: 'relative' as 'relative', // cast string to type 'relative', + marginLeft: 0, + marginTop: euiTheme.size.s, + }, + dragResizeContainer: { + width: '100%', + cursor: 'row-resize', + textAlign: 'center' as 'center', + height: euiTheme.size.base, + }, + dragResizeButton: { + cursor: 'row-resize', }, }; }; From dbe7d58d6d55db6fbc9a065da101f6f0803778c7 Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Tue, 21 Jun 2022 14:48:10 +0300 Subject: [PATCH 013/115] Implement documentation --- .../documentation.scss | 151 ++++++ .../documentation.tsx | 236 +++++++++ .../documentation_sections.tsx | 450 ++++++++++++++++++ .../text_based_languages_editor/index.tsx | 99 +++- .../text_based_languages_editor.styles.ts | 2 +- 5 files changed, 915 insertions(+), 23 deletions(-) create mode 100644 src/plugins/unified_search/public/query_string_input/text_based_languages_editor/documentation.scss create mode 100644 src/plugins/unified_search/public/query_string_input/text_based_languages_editor/documentation.tsx create mode 100644 src/plugins/unified_search/public/query_string_input/text_based_languages_editor/documentation_sections.tsx diff --git a/src/plugins/unified_search/public/query_string_input/text_based_languages_editor/documentation.scss b/src/plugins/unified_search/public/query_string_input/text_based_languages_editor/documentation.scss new file mode 100644 index 0000000000000..4cf5fbd08cdcd --- /dev/null +++ b/src/plugins/unified_search/public/query_string_input/text_based_languages_editor/documentation.scss @@ -0,0 +1,151 @@ +.documentation { + display: flex; + flex-direction: column; + + & > * { + flex: 1; + min-height: 0; + } + + & > * + * { + border-top: $euiBorderThin; + } +} + +.documentation__editor { + + & > * + * { + border-top: $euiBorderThin; + } +} + +.documentation__editorHeader, +.documentation__editorFooter { + padding: $euiSizeS $euiSize; +} + +.documentation__editorFooter { + // make sure docs are rendered in front of monaco + z-index: 1; + background-color: $euiColorLightestShade; +} + +.documentation__editorHeaderGroup, +.documentation__editorFooterGroup { + display: block; // Overrides EUI's styling of `display: flex` on `EuiFlexItem` components +} + +.documentation__editorContent { + min-height: 0; + position: relative; +} + +.documentation__editorPlaceholder { + position: absolute; + top: 0; + left: $euiSize; + right: 0; + color: $euiTextSubduedColor; + // Matches monaco editor + font-family: Menlo, Monaco, 'Courier New', monospace; + pointer-events: none; +} + +.documentation__warningText + .documentation__warningText { + margin-top: $euiSizeS; + border-top: $euiBorderThin; + padding-top: $euiSizeS; +} + +.documentation__editorHelp--inline { + align-items: center; + display: flex; + padding: $euiSizeXS; + + & > * + * { + margin-left: $euiSizeXS; + } +} + +.documentation__editorError { + white-space: nowrap; +} + +.documentation__docs { + background: $euiColorEmptyShade; +} + +.documentation__docs--inline { + display: flex; + flex-direction: column; + // make sure docs are rendered in front of monaco + z-index: 1; +} + +.documentation__docsContent { + .documentation__docs--overlay & { + height: 40vh; + width: #{'min(75vh, 90vw)'}; + } + + .documentation__docs--inline & { + flex: 1; + min-height: 0; + } + + & > * + * { + border-left: $euiBorderThin; + } +} + +.documentation__docsSidebar { + background: $euiColorLightestShade; +} + +.documentation__docsSidebarInner { + min-height: 0; + + & > * + * { + border-top: $euiBorderThin; + } +} + +.documentation__docsSearch { + padding: $euiSize; +} + +.documentation__docsNav { + @include euiYScroll; +} + +.documentation__docsNavGroup { + padding: $euiSize; + + & + & { + border-top: $euiBorderThin; + } +} + +.documentation__docsNavGroupLink { + font-weight: inherit; +} + +.documentation__docsText { + @include euiYScroll; + padding: $euiSize; +} + +.documentation__docsTextGroup, +.documentation__docsTextItem { + margin-top: $euiSizeXXL; +} + +.documentation__docsTextGroup { + border-top: $euiBorderThin; + padding-top: $euiSizeXXL; +} + +.documentationOverflow { + // Needs to be higher than the modal and all flyouts + z-index: $euiZLevel9 + 1; +} diff --git a/src/plugins/unified_search/public/query_string_input/text_based_languages_editor/documentation.tsx b/src/plugins/unified_search/public/query_string_input/text_based_languages_editor/documentation.tsx new file mode 100644 index 0000000000000..816a2726d4eb9 --- /dev/null +++ b/src/plugins/unified_search/public/query_string_input/text_based_languages_editor/documentation.tsx @@ -0,0 +1,236 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ +import React, { useEffect, useRef, useState } from 'react'; +import { i18n } from '@kbn/i18n'; +import { + EuiFlexGroup, + EuiFlexItem, + EuiLink, + EuiPopoverTitle, + EuiText, + EuiListGroupItem, + EuiListGroup, + EuiTitle, + EuiFieldSearch, + EuiHighlight, + EuiSpacer, +} from '@elastic/eui'; +import { Markdown } from '@kbn/kibana-react-plugin/public'; +import { comparisonOperators, logicalOperators, mathOperators } from './documentation_sections'; + +import './documentation.scss'; + +function Documentation() { + const [selectedOperator, setSelectedOperator] = useState(); + const scrollTargets = useRef>({}); + + useEffect(() => { + if (selectedOperator && scrollTargets.current[selectedOperator]) { + scrollTargets.current[selectedOperator].scrollIntoView(); + } + }, [selectedOperator]); + + const groups: Array<{ + label: string; + description?: string; + items: Array<{ label: string; description?: JSX.Element }>; + }> = []; + + groups.push({ + label: i18n.translate('unifiedSearch.query.textBasedLanguagesEditor.howItWorks', { + defaultMessage: 'How it works', + }), + items: [], + }); + groups.push(comparisonOperators, logicalOperators, mathOperators); + + const [searchText, setSearchText] = useState(''); + + const normalizedSearchText = searchText.trim().toLocaleLowerCase(); + + const filteredGroups = groups + .map((group) => { + const items = group.items.filter((helpItem) => { + return ( + !normalizedSearchText || helpItem.label.toLocaleLowerCase().includes(normalizedSearchText) + ); + }); + return { ...group, items }; + }) + .filter((group) => { + if (group.items.length > 0 || !normalizedSearchText) { + return true; + } + return group.label.toLocaleLowerCase().includes(normalizedSearchText); + }); + + return ( + <> + + {i18n.translate('unifiedSearch.query.textBasedLanguagesEditor.documentation.header', { + defaultMessage: 'SQL reference', + })} + + + + + + { + setSearchText(e.target.value); + }} + placeholder={i18n.translate( + 'unifiedSearch.query.textBasedLanguagesEditor.documentation.searchPlaceholder', + { + defaultMessage: 'Search operators', + } + )} + /> + + + {filteredGroups.map((helpGroup, index) => { + return ( + + ); + })} + + + + + +
{ + if (el) { + scrollTargets.current[groups[0].label] = el; + } + }} + > + +
+ {groups.slice(1).map((helpGroup, index) => { + return ( +
{ + if (el) { + scrollTargets.current[helpGroup.label] = el; + } + }} + > +

{helpGroup.label}

+ +

{helpGroup.description}

+ + {groups[index + 1].items.map((helpItem) => { + return ( +
{ + if (el) { + scrollTargets.current[helpItem.label] = el; + } + }} + > + {helpItem.description} +
+ ); + })} +
+ ); + })} +
+
+
+ + ); +} + +export const MemoizedDocumentation = React.memo(Documentation); diff --git a/src/plugins/unified_search/public/query_string_input/text_based_languages_editor/documentation_sections.tsx b/src/plugins/unified_search/public/query_string_input/text_based_languages_editor/documentation_sections.tsx new file mode 100644 index 0000000000000..733458e5bb1c9 --- /dev/null +++ b/src/plugins/unified_search/public/query_string_input/text_based_languages_editor/documentation_sections.tsx @@ -0,0 +1,450 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ +import React from 'react'; +import { i18n } from '@kbn/i18n'; +import { Markdown } from '@kbn/kibana-react-plugin/public'; + +export const comparisonOperators = { + label: i18n.translate('unifiedSearch.query.textBasedLanguagesEditor.comparisonOperators', { + defaultMessage: 'Comparison operators', + }), + description: i18n.translate( + 'unifiedSearch.query.textBasedLanguagesEditor.comparisonOperatorsDocumentationDescription', + { + defaultMessage: `Boolean operator for comparing against one or multiple expressions.`, + } + ), + items: [ + { + label: i18n.translate('unifiedSearch.query.textBasedLanguagesEditor.documentation.equality', { + defaultMessage: 'Equality', + }), + description: ( + + ), + }, + { + label: i18n.translate( + 'unifiedSearch.query.textBasedLanguagesEditor.documentation.nullSafeEquality', + { + defaultMessage: 'Null safe equality (<=>)', + } + ), + description: ( + null AS "equals"; + + equals +--------------- +false; +\`\`\` +\`\`\` +SELECT null <=> null AS "equals"; + + equals +--------------- +true; +\`\`\` + `, + description: + 'Text is in markdown. Do not translate function names, special characters, or field names like sum(bytes)', + } + )} + /> + ), + }, + { + label: i18n.translate( + 'unifiedSearch.query.textBasedLanguagesEditor.documentation.inequality', + { + defaultMessage: 'Inequality', + } + ), + description: ( + or !=) +\`\`\` +SELECT last_name l FROM "test_emp" +WHERE emp_no <> 10000 ORDER BY emp_no LIMIT 5; +\`\`\` + `, + description: + 'Text is in markdown. Do not translate function names, special characters, or field names like sum(bytes)', + } + )} + /> + ), + }, + { + label: i18n.translate( + 'unifiedSearch.query.textBasedLanguagesEditor.documentation.comparison', + { + defaultMessage: 'Comparison', + } + ), + description: ( + , >=) +\`\`\` +SELECT last_name l FROM "test_emp" +WHERE emp_no < 10003 ORDER BY emp_no LIMIT 5; +\`\`\` + `, + description: + 'Text is in markdown. Do not translate function names, special characters, or field names like sum(bytes)', + } + )} + /> + ), + }, + { + label: i18n.translate('unifiedSearch.query.textBasedLanguagesEditor.documentation.between', { + defaultMessage: 'Between', + }), + description: ( + + ), + }, + { + label: i18n.translate( + 'unifiedSearch.query.textBasedLanguagesEditor.documentation.nullNotNull', + { + defaultMessage: 'IS NULL and IS NOT NULL', + } + ), + description: ( + + ), + }, + { + label: i18n.translate( + 'unifiedSearch.query.textBasedLanguagesEditor.documentation.inOperator', + { + defaultMessage: 'IN', + } + ), + description: ( + , , ...) +\`\`\` +SELECT last_name l FROM "test_emp" +WHERE emp_no IN (10000, 10001, 10002, 999) ORDER BY emp_no LIMIT 5; +\`\`\` + `, + description: + 'Text is in markdown. Do not translate function names, special characters, or field names like sum(bytes)', + } + )} + /> + ), + }, + ], +}; + +export const logicalOperators = { + label: i18n.translate('unifiedSearch.query.textBasedLanguagesEditor.logicalOperators', { + defaultMessage: 'Logical operators', + }), + description: i18n.translate( + 'unifiedSearch.query.textBasedLanguagesEditor.logicalOperatorsDocumentationDescription', + { + defaultMessage: `Boolean operator for evaluating one or two expressions.`, + } + ), + items: [ + { + label: i18n.translate( + 'unifiedSearch.query.textBasedLanguagesEditor.documentation.AndOperator', + { + defaultMessage: 'AND', + } + ), + description: ( + 10000 AND emp_no < 10005 ORDER BY emp_no LIMIT 5; +\`\`\` + `, + description: + 'Text is in markdown. Do not translate function names, special characters, or field names like sum(bytes)', + } + )} + /> + ), + }, + { + label: i18n.translate( + 'unifiedSearch.query.textBasedLanguagesEditor.documentation.OrOperator', + { + defaultMessage: 'OR', + } + ), + description: ( + + ), + }, + { + label: i18n.translate( + 'unifiedSearch.query.textBasedLanguagesEditor.documentation.NotOperator', + { + defaultMessage: 'NOT', + } + ), + description: ( + + ), + }, + ], +}; + +export const mathOperators = { + label: i18n.translate('unifiedSearch.query.textBasedLanguagesEditor.mathOperators', { + defaultMessage: 'Math operators', + }), + description: i18n.translate( + 'unifiedSearch.query.textBasedLanguagesEditor.mathOperatorsDocumentationDescription', + { + defaultMessage: `Perform mathematical operations affecting one or two values. The result is a value of numeric type..`, + } + ), + items: [ + { + label: i18n.translate( + 'unifiedSearch.query.textBasedLanguagesEditor.documentation.AddOperator', + { + defaultMessage: 'Add', + } + ), + description: ( + + ), + }, + { + label: i18n.translate( + 'unifiedSearch.query.textBasedLanguagesEditor.documentation.SubtractOperator', + { + defaultMessage: 'Subtract', + } + ), + description: ( + + ), + }, + { + label: i18n.translate( + 'unifiedSearch.query.textBasedLanguagesEditor.documentation.negateOperator', + { + defaultMessage: 'Negate', + } + ), + description: ( + + ), + }, + { + label: i18n.translate( + 'unifiedSearch.query.textBasedLanguagesEditor.documentation.multiplyOperator', + { + defaultMessage: 'Multiply', + } + ), + description: ( + + ), + }, + { + label: i18n.translate( + 'unifiedSearch.query.textBasedLanguagesEditor.documentation.divideOperator', + { + defaultMessage: 'Divide', + } + ), + description: ( + + ), + }, + { + label: i18n.translate( + 'unifiedSearch.query.textBasedLanguagesEditor.documentation.moduloOperator', + { + defaultMessage: 'Modulo or remainder', + } + ), + description: ( + + ), + }, + ], +}; diff --git a/src/plugins/unified_search/public/query_string_input/text_based_languages_editor/index.tsx b/src/plugins/unified_search/public/query_string_input/text_based_languages_editor/index.tsx index 2895b72596e07..b9fcef816be1f 100644 --- a/src/plugins/unified_search/public/query_string_input/text_based_languages_editor/index.tsx +++ b/src/plugins/unified_search/public/query_string_input/text_based_languages_editor/index.tsx @@ -17,6 +17,7 @@ import { EuiFlexGroup, EuiFlexItem, EuiButtonIcon, + EuiPopover, EuiResizeObserver, EuiOutsideClickDetector, } from '@elastic/eui'; @@ -30,6 +31,7 @@ import { EDITOR_MAX_HEIGHT, EDITOR_MIN_HEIGHT, } from './text_based_languages_editor.styles'; +import { MemoizedDocumentation } from './documentation'; import { useDebounceWithOptions } from './helpers'; interface TextBasedLanguagesEditorProps { @@ -54,6 +56,7 @@ const languageId = (language: string) => { } } }; + let clickedOutside = false; let initialRender = true; @@ -74,8 +77,10 @@ export const TextBasedLanguagesEditor = memo(function TextBasedLanguagesEditor({ ); const [showLineNumbers, setShowLineNumbers] = useState(isCodeEditorExpanded); const [isCompactFocused, setIsCompactFocused] = useState(isCodeEditorExpanded); - const [isWordWrapped, setIsWordWrapped] = useState(true); + const [isWordWrapped, setIsWordWrapped] = useState(false); const [userDrags, setUserDrags] = useState(false); + const [isHelpOpen, setIsHelpOpen] = useState(false); + const styles = textBasedLanguagedEditorStyles( euiTheme, isCompactFocused, @@ -200,6 +205,7 @@ export const TextBasedLanguagesEditor = memo(function TextBasedLanguagesEditor({ hideCursorInOverviewRuler: true, scrollbar: { vertical: 'auto', + horizontal: 'hidden', }, overviewRulerBorder: false, }; @@ -227,7 +233,7 @@ export const TextBasedLanguagesEditor = memo(function TextBasedLanguagesEditor({ aria-label={ isWordWrapped ? i18n.translate( - 'unifiedSearch.query.textBasedLanguagesEditor..disableWordWrapLabel', + 'unifiedSearch.query.textBasedLanguagesEditor.disableWordWrapLabel', { defaultMessage: 'Disable word wrap', } @@ -249,19 +255,42 @@ export const TextBasedLanguagesEditor = memo(function TextBasedLanguagesEditor({ /> - { - expandCodeEditor(false); - }} - /> + + + { + expandCodeEditor(false); + }} + /> + + + setIsHelpOpen(false)} + ownFocus={false} + button={ + setIsHelpOpen(true)} + /> + } + > + + + + )} @@ -337,13 +366,39 @@ export const TextBasedLanguagesEditor = memo(function TextBasedLanguagesEditor({ {!isCodeEditorExpanded && ( - expandCodeEditor(true)} - /> + + + expandCodeEditor(true)} + css={{ borderRadius: 0 }} + /> + + + setIsHelpOpen(false)} + ownFocus={false} + button={ + setIsHelpOpen(true)} + css={{ borderTopLeftRadius: 0, borderBottomLeftRadius: 0 }} + /> + } + > + + + + )} diff --git a/src/plugins/unified_search/public/query_string_input/text_based_languages_editor/text_based_languages_editor.styles.ts b/src/plugins/unified_search/public/query_string_input/text_based_languages_editor/text_based_languages_editor.styles.ts index 92674ce5b3fae..0c3b6275ef1de 100644 --- a/src/plugins/unified_search/public/query_string_input/text_based_languages_editor/text_based_languages_editor.styles.ts +++ b/src/plugins/unified_search/public/query_string_input/text_based_languages_editor/text_based_languages_editor.styles.ts @@ -32,7 +32,7 @@ export const textBasedLanguagedEditorStyles = ( }, resizableContainer: { display: 'flex', - width: isCodeEditorExpanded ? '100%' : 'calc(100% - 40px)', + width: isCodeEditorExpanded ? '100%' : 'calc(100% - 80px)', alignItems: isCompactFocused ? 'flex-start' : 'center', border: !isCompactFocused ? euiTheme.border.thin : 'none', }, From 69fed5ff0f9ea0ba3c02e3baf2a736b99a55dd88 Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Wed, 22 Jun 2022 12:49:12 +0300 Subject: [PATCH 014/115] Implementation of the oneliner mode with ellipsis --- .../text_based_languages_editor/index.tsx | 50 ++++++++++++++++--- 1 file changed, 43 insertions(+), 7 deletions(-) diff --git a/src/plugins/unified_search/public/query_string_input/text_based_languages_editor/index.tsx b/src/plugins/unified_search/public/query_string_input/text_based_languages_editor/index.tsx index b9fcef816be1f..2d2f6de1c9d8e 100644 --- a/src/plugins/unified_search/public/query_string_input/text_based_languages_editor/index.tsx +++ b/src/plugins/unified_search/public/query_string_input/text_based_languages_editor/index.tsx @@ -43,6 +43,8 @@ interface TextBasedLanguagesEditorProps { const MAX_COMPACT_VIEW_LENGTH = 250; const OS_COMMAND_KEY = '⌘'; +const FONT_WIDTH = 8; +const EDITOR_ONE_LINER_UNUSED_SPACE = 180; const getTextBasedLanguage = (query: any) => { return Object.keys(query)[0]; @@ -59,6 +61,7 @@ const languageId = (language: string) => { let clickedOutside = false; let initialRender = true; +let updateLinesFromModel = false; export const TextBasedLanguagesEditor = memo(function TextBasedLanguagesEditor({ query, @@ -77,7 +80,7 @@ export const TextBasedLanguagesEditor = memo(function TextBasedLanguagesEditor({ ); const [showLineNumbers, setShowLineNumbers] = useState(isCodeEditorExpanded); const [isCompactFocused, setIsCompactFocused] = useState(isCodeEditorExpanded); - const [isWordWrapped, setIsWordWrapped] = useState(false); + const [isWordWrapped, setIsWordWrapped] = useState(true); const [userDrags, setUserDrags] = useState(false); const [isHelpOpen, setIsHelpOpen] = useState(false); @@ -88,7 +91,7 @@ export const TextBasedLanguagesEditor = memo(function TextBasedLanguagesEditor({ isCodeEditorExpanded ); - const ref = useRef(null); + const containerRef = useRef(null); const onMouseDownResizeHandler = useCallback( (mouseDownEvent: React.MouseEvent) => { @@ -133,6 +136,7 @@ export const TextBasedLanguagesEditor = memo(function TextBasedLanguagesEditor({ setEditorHeight(EDITOR_INITIAL_HEIGHT); setIsCompactFocused(false); setShowLineNumbers(false); + updateLinesFromModel = false; clickedOutside = true; if (editor1.current) { const editorElement = editor1.current.getDomNode(); @@ -148,13 +152,16 @@ export const TextBasedLanguagesEditor = memo(function TextBasedLanguagesEditor({ () => { if (!editorModel.current) return; editor1.current?.onDidChangeModelContent((e) => { - setLines(editorModel.current?.getLineCount() || 1); + if (updateLinesFromModel) { + setLines(editorModel.current?.getLineCount() || 1); + } }); editor1.current?.onDidFocusEditorText(() => { setIsCompactFocused(true); + setShowLineNumbers(true); clickedOutside = false; initialRender = false; - setShowLineNumbers(true); + updateLinesFromModel = true; }); if (!isCodeEditorExpanded) { editor1.current?.onDidContentSizeChange(updateHeight); @@ -175,7 +182,33 @@ export const TextBasedLanguagesEditor = memo(function TextBasedLanguagesEditor({ }; }, []); + const calculateVisibleCode = useCallback( + (width: number) => { + const containerWidth = containerRef.current?.offsetWidth; + if (containerWidth && !isCompactFocused) { + const hasLines = /\r|\n/.exec(query.sql); + if (hasLines && !updateLinesFromModel) { + setLines(query.sql.split(/\r|\n/).length); + } + const text = hasLines ? query.sql.split(/\r|\n/)[0] : query.sql; + const queryLength = text.length; + const charactersAlowed = Math.floor((width - EDITOR_ONE_LINER_UNUSED_SPACE) / FONT_WIDTH); + if (queryLength > charactersAlowed) { + const shortedCode = text.substring(0, charactersAlowed) + '...'; + setCode(shortedCode); + } else { + const shortedCode = hasLines ? `${text}...` : text; + setCode(shortedCode); + } + } else { + setCode(query.sql); + } + }, + [isCompactFocused, query.sql] + ); + const onResize = ({ width }: { width: number }) => { + calculateVisibleCode(width); if (editor1.current) { editor1.current.layout({ width, height: editorHeight }); } @@ -268,6 +301,7 @@ export const TextBasedLanguagesEditor = memo(function TextBasedLanguagesEditor({ )} onClick={() => { expandCodeEditor(false); + updateLinesFromModel = false; }} /> @@ -294,7 +328,7 @@ export const TextBasedLanguagesEditor = memo(function TextBasedLanguagesEditor({ )} - + {(resizeRef) => ( {isCompactFocused && !isCodeEditorExpanded && ( @@ -433,7 +469,7 @@ export const TextBasedLanguagesEditor = memo(function TextBasedLanguagesEditor({ )} {isCodeEditorExpanded && ( -
+
{!userDrags && ( Date: Wed, 22 Jun 2022 15:44:41 +0300 Subject: [PATCH 015/115] Some fixes on the resizer --- .../text_based_languages_editor/index.tsx | 36 +++++++++++-------- .../text_based_languages_editor.styles.ts | 10 +++--- 2 files changed, 27 insertions(+), 19 deletions(-) diff --git a/src/plugins/unified_search/public/query_string_input/text_based_languages_editor/index.tsx b/src/plugins/unified_search/public/query_string_input/text_based_languages_editor/index.tsx index 2d2f6de1c9d8e..ff59dd5f12703 100644 --- a/src/plugins/unified_search/public/query_string_input/text_based_languages_editor/index.tsx +++ b/src/plugins/unified_search/public/query_string_input/text_based_languages_editor/index.tsx @@ -34,15 +34,18 @@ import { import { MemoizedDocumentation } from './documentation'; import { useDebounceWithOptions } from './helpers'; +const isMac = navigator.platform.toLowerCase().indexOf('mac') >= 0; + interface TextBasedLanguagesEditorProps { query: any; onTextLangQueryChange: (query: any) => void; expandCodeEditor: (status: boolean) => void; isCodeEditorExpanded: boolean; + errors?: string[]; } const MAX_COMPACT_VIEW_LENGTH = 250; -const OS_COMMAND_KEY = '⌘'; +const COMMAND_KEY = isMac ? '⌘' : '^'; const FONT_WIDTH = 8; const EDITOR_ONE_LINER_UNUSED_SPACE = 180; @@ -68,6 +71,7 @@ export const TextBasedLanguagesEditor = memo(function TextBasedLanguagesEditor({ onTextLangQueryChange, expandCodeEditor, isCodeEditorExpanded, + errors, }: TextBasedLanguagesEditorProps) { const { euiTheme } = useEuiTheme(); const language = getTextBasedLanguage(query); @@ -75,6 +79,7 @@ export const TextBasedLanguagesEditor = memo(function TextBasedLanguagesEditor({ const editor1 = useRef(); const [lines, setLines] = useState(1); const [code, setCode] = useState(query.sql); + const [codeOneLiner, setCodeOneLiner] = useState(''); const [editorHeight, setEditorHeight] = useState( isCodeEditorExpanded ? EDITOR_INITIAL_HEIGHT_EXPANDED : EDITOR_INITIAL_HEIGHT ); @@ -88,7 +93,8 @@ export const TextBasedLanguagesEditor = memo(function TextBasedLanguagesEditor({ euiTheme, isCompactFocused, editorHeight, - isCodeEditorExpanded + isCodeEditorExpanded, + Boolean(errors?.length) ); const containerRef = useRef(null); @@ -143,6 +149,7 @@ export const TextBasedLanguagesEditor = memo(function TextBasedLanguagesEditor({ if (editorElement) { editorElement.style.height = `${EDITOR_INITIAL_HEIGHT}px`; const contentWidth = Number(editorElement?.style.width.replace('px', '')); + calculateVisibleCode(contentWidth, true); editor1.current.layout({ width: contentWidth, height: EDITOR_INITIAL_HEIGHT }); } } @@ -159,6 +166,7 @@ export const TextBasedLanguagesEditor = memo(function TextBasedLanguagesEditor({ editor1.current?.onDidFocusEditorText(() => { setIsCompactFocused(true); setShowLineNumbers(true); + setCodeOneLiner(''); clickedOutside = false; initialRender = false; updateLinesFromModel = true; @@ -183,28 +191,26 @@ export const TextBasedLanguagesEditor = memo(function TextBasedLanguagesEditor({ }, []); const calculateVisibleCode = useCallback( - (width: number) => { + (width: number, force?: boolean) => { const containerWidth = containerRef.current?.offsetWidth; - if (containerWidth && !isCompactFocused) { - const hasLines = /\r|\n/.exec(query.sql); + if (containerWidth && (!isCompactFocused || force)) { + const hasLines = /\r|\n/.exec(code); if (hasLines && !updateLinesFromModel) { - setLines(query.sql.split(/\r|\n/).length); + setLines(code.split(/\r|\n/).length); } - const text = hasLines ? query.sql.split(/\r|\n/)[0] : query.sql; + const text = hasLines ? code.split(/\r|\n/)[0] : code; const queryLength = text.length; const charactersAlowed = Math.floor((width - EDITOR_ONE_LINER_UNUSED_SPACE) / FONT_WIDTH); if (queryLength > charactersAlowed) { const shortedCode = text.substring(0, charactersAlowed) + '...'; - setCode(shortedCode); + setCodeOneLiner(shortedCode); } else { const shortedCode = hasLines ? `${text}...` : text; - setCode(shortedCode); + setCodeOneLiner(shortedCode); } - } else { - setCode(query.sql); } }, - [isCompactFocused, query.sql] + [code, isCompactFocused] ); const onResize = ({ width }: { width: number }) => { @@ -346,7 +352,7 @@ export const TextBasedLanguagesEditor = memo(function TextBasedLanguagesEditor({ )} - {`${OS_COMMAND_KEY} + Enter`} + {`${COMMAND_KEY} + Enter`} @@ -462,7 +468,7 @@ export const TextBasedLanguagesEditor = memo(function TextBasedLanguagesEditor({ - {`${OS_COMMAND_KEY} + Enter`} + {`${COMMAND_KEY} + Enter`} diff --git a/src/plugins/unified_search/public/query_string_input/text_based_languages_editor/text_based_languages_editor.styles.ts b/src/plugins/unified_search/public/query_string_input/text_based_languages_editor/text_based_languages_editor.styles.ts index 0c3b6275ef1de..473f31d652384 100644 --- a/src/plugins/unified_search/public/query_string_input/text_based_languages_editor/text_based_languages_editor.styles.ts +++ b/src/plugins/unified_search/public/query_string_input/text_based_languages_editor/text_based_languages_editor.styles.ts @@ -7,7 +7,7 @@ */ import type { EuiThemeComputed } from '@elastic/eui'; -export const EDITOR_INITIAL_HEIGHT = 40; +export const EDITOR_INITIAL_HEIGHT = 38; export const EDITOR_INITIAL_HEIGHT_EXPANDED = 140; export const EDITOR_MIN_HEIGHT = 40; export const EDITOR_MAX_HEIGHT = 400; @@ -16,25 +16,27 @@ export const textBasedLanguagedEditorStyles = ( euiTheme: EuiThemeComputed, isCompactFocused: boolean, editorHeight: number, - isCodeEditorExpanded: boolean + isCodeEditorExpanded: boolean, + hasErrors: boolean ) => { let position = isCompactFocused ? ('absolute' as 'absolute') : ('relative' as 'relative'); // cast string to type 'relative' | 'absolute' if (isCodeEditorExpanded) { position = 'relative' as 'relative'; } + const bottomContainerBorderColor = hasErrors ? euiTheme.colors.danger : euiTheme.colors.primary; return { editorContainer: { position, zIndex: isCompactFocused ? 1 : 0, height: `${editorHeight}px`, border: isCompactFocused ? euiTheme.border.thin : 'none', - borderRight: euiTheme.border.thin, }, resizableContainer: { display: 'flex', width: isCodeEditorExpanded ? '100%' : 'calc(100% - 80px)', alignItems: isCompactFocused ? 'flex-start' : 'center', border: !isCompactFocused ? euiTheme.border.thin : 'none', + borderBottomColor: hasErrors ? euiTheme.colors.danger : euiTheme.colors.lightShade, }, linesBadge: { position: 'absolute' as 'absolute', // cast string to type 'absolute', @@ -45,7 +47,7 @@ export const textBasedLanguagedEditorStyles = ( }, bottomContainer: { border: euiTheme.border.thin, - borderTop: isCodeEditorExpanded ? 'none' : `1px solid ${euiTheme.colors.primary}`, + borderTop: isCodeEditorExpanded ? 'none' : `1px solid ${bottomContainerBorderColor}`, backgroundColor: euiTheme.colors.lightestShade, paddingLeft: euiTheme.size.s, paddingRight: euiTheme.size.s, From c6091d9cea0f9cff494b7616c3304ad287cd7e69 Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Thu, 23 Jun 2022 12:11:26 +0300 Subject: [PATCH 016/115] Implementation of the errors layout, WIP --- .../query_string_input/query_bar_top_row.tsx | 2 +- .../editor_footer.tsx | 125 ++++++++++++++++++ .../text_based_languages_editor/index.tsx | 89 ++++--------- .../text_based_languages_editor.styles.ts | 7 + 4 files changed, 157 insertions(+), 66 deletions(-) create mode 100644 src/plugins/unified_search/public/query_string_input/text_based_languages_editor/editor_footer.tsx diff --git a/src/plugins/unified_search/public/query_string_input/query_bar_top_row.tsx b/src/plugins/unified_search/public/query_string_input/query_bar_top_row.tsx index 6cf55f02688da..5718fa16f665b 100644 --- a/src/plugins/unified_search/public/query_string_input/query_bar_top_row.tsx +++ b/src/plugins/unified_search/public/query_string_input/query_bar_top_row.tsx @@ -384,7 +384,7 @@ export const QueryBarTopRow = React.memo( defaultMessage: 'Run query', }); - const iconDirty = Boolean(isQueryLangSelected) ? 'playFilled' : 'kqlFunction'; + const iconDirty = Boolean(isQueryLangSelected) ? 'play' : 'kqlFunction'; const tooltipDirty = Boolean(isQueryLangSelected) ? buttonLabelRun : buttonLabelUpdate; const button = props.customSubmitButton ? ( diff --git a/src/plugins/unified_search/public/query_string_input/text_based_languages_editor/editor_footer.tsx b/src/plugins/unified_search/public/query_string_input/text_based_languages_editor/editor_footer.tsx new file mode 100644 index 0000000000000..b4948c1d0b5d6 --- /dev/null +++ b/src/plugins/unified_search/public/query_string_input/text_based_languages_editor/editor_footer.tsx @@ -0,0 +1,125 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import React, { memo, useState } from 'react'; + +import { i18n } from '@kbn/i18n'; +import { + EuiBadge, + EuiText, + EuiFlexGroup, + EuiFlexItem, + EuiIcon, + EuiPopover, + EuiPopoverTitle, + EuiDescriptionList, + EuiDescriptionListDescription, +} from '@elastic/eui'; +import { Interpolation, Theme, css } from '@emotion/react'; + +const isMac = navigator.platform.toLowerCase().indexOf('mac') >= 0; +const COMMAND_KEY = isMac ? '⌘' : '^'; + +interface EditorFooterProps { + lines: number; + containerCSS: Interpolation; + errors?: string[]; +} + +export const EditorFooter = memo(function EditorFooter({ + lines, + containerCSS, + errors, +}: EditorFooterProps) { + const [isPopoverOpen, setIsPopoverOpen] = useState(false); + return ( + + + + + +

{`${lines} lines`}

+
+
+ {errors && errors.length > 0 && ( + + + + + + + setIsPopoverOpen(true)} + > +

{`${errors.length} errors`}

+ + } + ownFocus={false} + isOpen={isPopoverOpen} + closePopover={() => setIsPopoverOpen(false)} + > +
+ + {i18n.translate( + 'unifiedSearch.query.textBasedLanguagesEditor.errorsTitle', + { + defaultMessage: 'Errors', + } + )} + + + {errors.map((error, index) => { + return ( + + {error} + + ); + })} + +
+
+
+
+
+ )} +
+
+ + + + +

+ {i18n.translate('unifiedSearch.query.textBasedLanguagesEditor.runQuery', { + defaultMessage: 'Run query', + })} +

+
+
+ + {`${COMMAND_KEY} + Enter`} + +
+
+
+ ); +}); diff --git a/src/plugins/unified_search/public/query_string_input/text_based_languages_editor/index.tsx b/src/plugins/unified_search/public/query_string_input/text_based_languages_editor/index.tsx index ff59dd5f12703..11f1f286d5817 100644 --- a/src/plugins/unified_search/public/query_string_input/text_based_languages_editor/index.tsx +++ b/src/plugins/unified_search/public/query_string_input/text_based_languages_editor/index.tsx @@ -13,7 +13,6 @@ import { i18n } from '@kbn/i18n'; import { EuiBadge, useEuiTheme, - EuiText, EuiFlexGroup, EuiFlexItem, EuiButtonIcon, @@ -33,8 +32,7 @@ import { } from './text_based_languages_editor.styles'; import { MemoizedDocumentation } from './documentation'; import { useDebounceWithOptions } from './helpers'; - -const isMac = navigator.platform.toLowerCase().indexOf('mac') >= 0; +import { EditorFooter } from './editor_footer'; interface TextBasedLanguagesEditorProps { query: any; @@ -45,9 +43,9 @@ interface TextBasedLanguagesEditorProps { } const MAX_COMPACT_VIEW_LENGTH = 250; -const COMMAND_KEY = isMac ? '⌘' : '^'; const FONT_WIDTH = 8; const EDITOR_ONE_LINER_UNUSED_SPACE = 180; +const EDITOR_ONE_LINER_UNUSED_SPACE_WITH_ERRORS = 220; const getTextBasedLanguage = (query: any) => { return Object.keys(query)[0]; @@ -200,7 +198,11 @@ export const TextBasedLanguagesEditor = memo(function TextBasedLanguagesEditor({ } const text = hasLines ? code.split(/\r|\n/)[0] : code; const queryLength = text.length; - const charactersAlowed = Math.floor((width - EDITOR_ONE_LINER_UNUSED_SPACE) / FONT_WIDTH); + const unusedSpace = + errors && errors.length + ? EDITOR_ONE_LINER_UNUSED_SPACE_WITH_ERRORS + : EDITOR_ONE_LINER_UNUSED_SPACE; + const charactersAlowed = Math.floor((width - unusedSpace) / FONT_WIDTH); if (queryLength > charactersAlowed) { const shortedCode = text.substring(0, charactersAlowed) + '...'; setCodeOneLiner(shortedCode); @@ -210,7 +212,7 @@ export const TextBasedLanguagesEditor = memo(function TextBasedLanguagesEditor({ } } }, - [code, isCompactFocused] + [code, errors, isCompactFocused] ); const onResize = ({ width }: { width: number }) => { @@ -350,6 +352,16 @@ export const TextBasedLanguagesEditor = memo(function TextBasedLanguagesEditor({ {`${lines} lines`} )} + {!isCompactFocused && errors && errors.length > 0 && ( + + {errors.length} + + )} {isCompactFocused && !isCodeEditorExpanded && ( - - - -

{`${lines} lines`}

-
-
- - - - -

- {i18n.translate( - 'unifiedSearch.query.textBasedLanguagesEditor.runQuery', - { - defaultMessage: 'Run query', - } - )} -

-
-
- - {`${COMMAND_KEY} + Enter`} - -
-
-
+ )}
@@ -445,34 +431,7 @@ export const TextBasedLanguagesEditor = memo(function TextBasedLanguagesEditor({ )} {isCodeEditorExpanded && ( - - - -

{`${lines} lines`}

-
-
- - - - -

- {i18n.translate('unifiedSearch.query.textBasedLanguagesEditor.runQuery', { - defaultMessage: 'Run query', - })} -

-
-
- - {`${COMMAND_KEY} + Enter`} - -
-
-
+ )} {isCodeEditorExpanded && (
diff --git a/src/plugins/unified_search/public/query_string_input/text_based_languages_editor/text_based_languages_editor.styles.ts b/src/plugins/unified_search/public/query_string_input/text_based_languages_editor/text_based_languages_editor.styles.ts index 473f31d652384..1b7d775cfab1b 100644 --- a/src/plugins/unified_search/public/query_string_input/text_based_languages_editor/text_based_languages_editor.styles.ts +++ b/src/plugins/unified_search/public/query_string_input/text_based_languages_editor/text_based_languages_editor.styles.ts @@ -39,6 +39,13 @@ export const textBasedLanguagedEditorStyles = ( borderBottomColor: hasErrors ? euiTheme.colors.danger : euiTheme.colors.lightShade, }, linesBadge: { + position: 'absolute' as 'absolute', // cast string to type 'absolute', + zIndex: 1, + right: hasErrors ? '64px' : '16px', + top: '50%', + transform: 'translate(0, -50%)', + }, + errorsBadge: { position: 'absolute' as 'absolute', // cast string to type 'absolute', zIndex: 1, right: '16px', From 9bea48b394b7b261202de0e65f4ff1dd8d63f601 Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Fri, 24 Jun 2022 15:30:16 +0300 Subject: [PATCH 017/115] Fetch SQL data in Discover --- src/plugins/data/common/query/query_state.ts | 4 +- .../common/query/to_expression_ast.test.ts | 70 +++-------------- .../data/common/query/to_expression_ast.ts | 51 +++++++++++-- .../expressions/aggregate_query_to_ast.ts | 20 +++++ .../data/common/search/expressions/essql.ts | 6 +- .../data/common/search/expressions/index.ts | 1 + src/plugins/discover/kibana.json | 3 +- .../components/layout/discover_documents.tsx | 16 ++-- .../components/layout/discover_layout.tsx | 46 +++++++----- .../components/sidebar/discover_field.tsx | 47 ++++++------ .../sidebar/discover_field_bucket.tsx | 4 +- .../sidebar/discover_field_details.tsx | 4 +- .../components/sidebar/discover_sidebar.tsx | 22 +++--- .../sidebar/discover_sidebar_responsive.tsx | 9 ++- .../components/top_nav/discover_topnav.tsx | 5 +- .../components/top_nav/get_top_nav_links.tsx | 5 +- .../main/hooks/use_discover_state.ts | 20 +++++ .../main/services/discover_state.ts | 15 +++- .../application/main/utils/fetch_all.ts | 41 +++++----- .../application/main/utils/fetch_documents.ts | 6 +- .../application/main/utils/fetch_sql.ts | 53 +++++++++++++ src/plugins/discover/public/build_services.ts | 3 + .../discover_grid/discover_grid.tsx | 10 ++- .../discover_grid_cell_actions.tsx | 7 +- .../discover_grid/discover_grid_columns.tsx | 75 +++++++++++-------- .../discover_grid/discover_grid_context.tsx | 4 +- .../discover_grid/discover_grid_flyout.tsx | 22 +++--- src/plugins/discover/public/plugin.tsx | 3 + src/plugins/discover/tsconfig.json | 1 + 29 files changed, 363 insertions(+), 210 deletions(-) create mode 100644 src/plugins/data/common/search/expressions/aggregate_query_to_ast.ts create mode 100644 src/plugins/discover/public/application/main/utils/fetch_sql.ts diff --git a/src/plugins/data/common/query/query_state.ts b/src/plugins/data/common/query/query_state.ts index fbc5626f9a28c..a9ca73d7f0def 100644 --- a/src/plugins/data/common/query/query_state.ts +++ b/src/plugins/data/common/query/query_state.ts @@ -8,7 +8,7 @@ import type { Filter } from '@kbn/es-query'; import type { TimeRange, RefreshInterval } from './timefilter/types'; -import type { Query } from './types'; +import type { Query, AggregateQuery } from './types'; /** * All query state service state @@ -22,5 +22,5 @@ export type QueryState = { time?: TimeRange; refreshInterval?: RefreshInterval; filters?: Filter[]; - query?: Query; + query?: Query | AggregateQuery; }; diff --git a/src/plugins/data/common/query/to_expression_ast.test.ts b/src/plugins/data/common/query/to_expression_ast.test.ts index 9865b70bd491c..72961040febe6 100644 --- a/src/plugins/data/common/query/to_expression_ast.test.ts +++ b/src/plugins/data/common/query/to_expression_ast.test.ts @@ -21,54 +21,18 @@ describe('queryStateToExpressionAst', () => { expect(actual).toMatchInlineSnapshot(` Object { - "findFunction": [Function], - "functions": Array [ + "chain": Array [ Object { - "addArgument": [Function], "arguments": Object {}, - "getArgument": [Function], - "name": "kibana", - "removeArgument": [Function], - "replaceArgument": [Function], - "toAst": [Function], - "toString": [Function], - "type": "expression_function_builder", + "function": "kibana", + "type": "function", }, Object { - "addArgument": [Function], "arguments": Object { - "filters": Array [], - "q": Array [ - Object { - "findFunction": [Function], - "functions": Array [ - Object { - "addArgument": [Function], - "arguments": Object { - "q": Array [ - "\\"\\"", - ], - }, - "getArgument": [Function], - "name": "lucene", - "removeArgument": [Function], - "replaceArgument": [Function], - "toAst": [Function], - "toString": [Function], - "type": "expression_function_builder", - }, - ], - "toAst": [Function], - "toString": [Function], - "type": "expression_builder", - }, - ], "timeRange": Array [ Object { - "findFunction": [Function], - "functions": Array [ + "chain": Array [ Object { - "addArgument": [Function], "arguments": Object { "from": Array [ "now", @@ -77,33 +41,19 @@ describe('queryStateToExpressionAst', () => { "now+7d", ], }, - "getArgument": [Function], - "name": "timerange", - "removeArgument": [Function], - "replaceArgument": [Function], - "toAst": [Function], - "toString": [Function], - "type": "expression_function_builder", + "function": "timerange", + "type": "function", }, ], - "toAst": [Function], - "toString": [Function], - "type": "expression_builder", + "type": "expression", }, ], }, - "getArgument": [Function], - "name": "kibana_context", - "removeArgument": [Function], - "replaceArgument": [Function], - "toAst": [Function], - "toString": [Function], - "type": "expression_function_builder", + "function": "kibana_context", + "type": "function", }, ], - "toAst": [Function], - "toString": [Function], - "type": "expression_builder", + "type": "expression", } `); }); diff --git a/src/plugins/data/common/query/to_expression_ast.ts b/src/plugins/data/common/query/to_expression_ast.ts index 929a7cc928cdd..2bee3f40d1f28 100644 --- a/src/plugins/data/common/query/to_expression_ast.ts +++ b/src/plugins/data/common/query/to_expression_ast.ts @@ -7,29 +7,66 @@ */ import { buildExpression, buildExpressionFunction } from '@kbn/expressions-plugin/common'; +import type { DataViewsContract } from '@kbn/data-views-plugin/common'; import { ExpressionFunctionKibana, ExpressionFunctionKibanaContext, - filtersToAst, QueryState, - queryToAst, + aggregateQueryToAst, timerangeToAst, } from '..'; +import type { Query, AggregateQuery } from './types'; + +function isOfAggregateQueryType(query: AggregateQuery | Query): query is AggregateQuery { + return Boolean(query && 'sql' in query); +} + +function getAggregateQueryMode(query: AggregateQuery): string { + return Object.keys(query)[0]; +} + +function getIndexPatternFromSQLQuery(sqlQuery?: string): string { + const match = sqlQuery?.match(/FROM\s+(\w+)/); + if (match) { + return match[1]; + } + return ''; +} + +interface Args extends QueryState { + dataViewsService: DataViewsContract; +} + /** * Converts QueryState to expression AST * @param filters array of kibana filters - * @param query kibana query + * @param query kibana query or aggregate query * @param time kibana time range */ -export function queryStateToExpressionAst({ filters, query, time }: QueryState) { +export async function queryStateToExpressionAst({ filters, query, time, dataViewsService }: Args) { const kibana = buildExpressionFunction('kibana', {}); const kibanaContext = buildExpressionFunction('kibana_context', { - q: query && queryToAst(query), - filters: filters && filtersToAst(filters), timeRange: time && timerangeToAst(time), }); + const ast = buildExpression([kibana, kibanaContext]).toAst(); + + if (query && isOfAggregateQueryType(query)) { + const mode = getAggregateQueryMode(query); + if (mode === 'sql') { + const idxPattern = getIndexPatternFromSQLQuery(query.sql); + const dataView = await dataViewsService.find(idxPattern); + if (dataView && dataView.length) { + const timeFieldName = dataView[0].timeFieldName; + const essql = aggregateQueryToAst(query, timeFieldName); - const ast = buildExpression([kibana, kibanaContext]); + if (essql) { + ast.chain.push(essql); + } + } else { + throw new Error(`No data view found for index pattern ${idxPattern}`); + } + } + } return ast; } diff --git a/src/plugins/data/common/search/expressions/aggregate_query_to_ast.ts b/src/plugins/data/common/search/expressions/aggregate_query_to_ast.ts new file mode 100644 index 0000000000000..8c62021c16e57 --- /dev/null +++ b/src/plugins/data/common/search/expressions/aggregate_query_to_ast.ts @@ -0,0 +1,20 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { buildExpressionFunction } from '@kbn/expressions-plugin/common'; +import { AggregateQuery } from '../../query'; +import { EssqlExpressionFunctionDefinition } from './essql'; + +export const aggregateQueryToAst = (query: AggregateQuery, timeField?: string) => { + if (query.sql) { + return buildExpressionFunction('essql', { + query: query.sql, + timeField, + }).toAst(); + } +}; diff --git a/src/plugins/data/common/search/expressions/essql.ts b/src/plugins/data/common/search/expressions/essql.ts index 1038bf422fee8..398b92de490d8 100644 --- a/src/plugins/data/common/search/expressions/essql.ts +++ b/src/plugins/data/common/search/expressions/essql.ts @@ -40,9 +40,9 @@ type Output = Observable; interface Arguments { query: string; - parameter: Array; - count: number; - timezone: string; + parameter?: Array; + count?: number; + timezone?: string; timeField?: string; } diff --git a/src/plugins/data/common/search/expressions/index.ts b/src/plugins/data/common/search/expressions/index.ts index 23d3b865b4c05..8c37836e30dea 100644 --- a/src/plugins/data/common/search/expressions/index.ts +++ b/src/plugins/data/common/search/expressions/index.ts @@ -27,6 +27,7 @@ export * from './numerical_range_to_ast'; export * from './query_filter'; export * from './query_filter_to_ast'; export * from './query_to_ast'; +export * from './aggregate_query_to_ast'; export * from './timerange_to_ast'; export * from './kibana_context_type'; export * from './esaggs'; diff --git a/src/plugins/discover/kibana.json b/src/plugins/discover/kibana.json index cb40433b73fa1..7c8376795863a 100644 --- a/src/plugins/discover/kibana.json +++ b/src/plugins/discover/kibana.json @@ -14,7 +14,8 @@ "uiActions", "savedObjects", "dataViewFieldEditor", - "dataViewEditor" + "dataViewEditor", + "expressions" ], "optionalPlugins": ["home", "share", "usageCollection", "spaces", "triggersActionsUi"], "requiredBundles": ["kibanaUtils", "kibanaReact", "dataViews", "unifiedSearch"], diff --git a/src/plugins/discover/public/application/main/components/layout/discover_documents.tsx b/src/plugins/discover/public/application/main/components/layout/discover_documents.tsx index 3dbf313fcc7c7..44fde58660ef9 100644 --- a/src/plugins/discover/public/application/main/components/layout/discover_documents.tsx +++ b/src/plugins/discover/public/application/main/components/layout/discover_documents.tsx @@ -54,7 +54,7 @@ function DiscoverDocumentsComponent({ expandedDoc?: DataTableRecord; indexPattern: DataView; navigateTo: (url: string) => void; - onAddFilter: DocViewFilterFn; + onAddFilter?: DocViewFilterFn; savedSearch: SavedSearch; setExpandedDoc: (doc?: DataTableRecord) => void; state: AppState; @@ -109,8 +109,11 @@ function DiscoverDocumentsComponent({ ); const showTimeCol = useMemo( - () => !uiSettings.get(DOC_HIDE_TIME_COLUMN_SETTING, false) && !!indexPattern.timeFieldName, - [uiSettings, indexPattern.timeFieldName] + () => + !state.textBasedLanguageMode && + !uiSettings.get(DOC_HIDE_TIME_COLUMN_SETTING, false) && + !!indexPattern.timeFieldName, + [uiSettings, indexPattern.timeFieldName, state.textBasedLanguageMode] ); if ( @@ -150,7 +153,7 @@ function DiscoverDocumentsComponent({ onFilter={onAddFilter as DocViewFilterFn} onMoveColumn={onMoveColumn} onRemoveColumn={onRemoveColumn} - onSort={onSort} + onSort={!state.textBasedLanguageMode ? onSort : undefined} useNewFieldsApi={useNewFieldsApi} dataTestSubj="discoverDocTable" /> @@ -173,18 +176,19 @@ function DiscoverDocumentsComponent({ sampleSize={sampleSize} searchDescription={savedSearch.description} searchTitle={savedSearch.title} - setExpandedDoc={setExpandedDoc} + setExpandedDoc={!state.textBasedLanguageMode ? setExpandedDoc : undefined} showTimeCol={showTimeCol} settings={state.grid} onAddColumn={onAddColumn} onFilter={onAddFilter as DocViewFilterFn} onRemoveColumn={onRemoveColumn} onSetColumns={onSetColumns} - onSort={onSort} + onSort={!state.textBasedLanguageMode ? onSort : undefined} onResize={onResize} useNewFieldsApi={useNewFieldsApi} rowHeightState={state.rowHeight} onUpdateRowHeight={onUpdateRowHeight} + isSortEnabled={!state.textBasedLanguageMode} />
diff --git a/src/plugins/discover/public/application/main/components/layout/discover_layout.tsx b/src/plugins/discover/public/application/main/components/layout/discover_layout.tsx index c9032001947ea..d2af050395b69 100644 --- a/src/plugins/discover/public/application/main/components/layout/discover_layout.tsx +++ b/src/plugins/discover/public/application/main/components/layout/discover_layout.tsx @@ -259,7 +259,9 @@ export function DiscoverLayout({ documents$={savedSearchData$.documents$} indexPatternList={indexPatternList} onAddField={onAddColumn} - onAddFilter={onAddFilter} + onAddFilter={ + !state.textBasedLanguageMode ? (onAddFilter as DocViewFilterFn) : undefined + } onRemoveField={onRemoveColumn} onChangeIndexPattern={onChangeIndexPattern} selectedIndexPattern={indexPattern} @@ -325,28 +327,34 @@ export function DiscoverLayout({ gutterSize="none" responsive={false} > - - - - + {!state.textBasedLanguageMode && ( + <> + + + + + + )} {viewMode === VIEW_MODE.DOCUMENT_LEVEL ? ( diff --git a/src/plugins/discover/public/application/main/components/sidebar/discover_field.tsx b/src/plugins/discover/public/application/main/components/sidebar/discover_field.tsx index afea3949bed1c..7bda9268b8621 100644 --- a/src/plugins/discover/public/application/main/components/sidebar/discover_field.tsx +++ b/src/plugins/discover/public/application/main/components/sidebar/discover_field.tsx @@ -222,7 +222,7 @@ export interface DiscoverFieldProps { /** * Callback to add a filter to filter bar */ - onAddFilter: (field: DataViewField | string, value: string, type: '+' | '-') => void; + onAddFilter?: (field: DataViewField | string, value: string, type: '+' | '-') => void; /** * Callback to remove/deselect a the field * @param fieldName @@ -369,6 +369,30 @@ function DiscoverFieldComponent({ ); + const button = ( + } + fieldAction={ + + } + fieldName={} + fieldInfoIcon={field.type === 'conflict' && } + /> + ); + if (!onAddFilter) { + return button; + } + const renderPopover = () => { const details = getDetails(field); return ( @@ -415,26 +439,7 @@ function DiscoverFieldComponent({ return ( } - fieldAction={ - - } - fieldName={} - fieldInfoIcon={field.type === 'conflict' && } - /> - } + button={button} isOpen={infoIsOpen} closePopover={() => setOpen(false)} anchorPosition="rightUp" diff --git a/src/plugins/discover/public/application/main/components/sidebar/discover_field_bucket.tsx b/src/plugins/discover/public/application/main/components/sidebar/discover_field_bucket.tsx index ac0dd3e7f8186..47808d14e1cc3 100644 --- a/src/plugins/discover/public/application/main/components/sidebar/discover_field_bucket.tsx +++ b/src/plugins/discover/public/application/main/components/sidebar/discover_field_bucket.tsx @@ -17,7 +17,7 @@ import './discover_field_bucket.scss'; interface Props { bucket: Bucket; field: DataViewField; - onAddFilter: (field: DataViewField | string, value: string, type: '+' | '-') => void; + onAddFilter?: (field: DataViewField | string, value: string, type: '+' | '-') => void; } export function DiscoverFieldBucket({ field, bucket, onAddFilter }: Props) { @@ -66,7 +66,7 @@ export function DiscoverFieldBucket({ field, bucket, onAddFilter }: Props) { count={bucket.count} /> - {field.filterable && ( + {onAddFilter && field.filterable && (
void; + onAddFilter?: (field: DataViewField | string, value: string, type: '+' | '-') => void; } export function DiscoverFieldDetails({ @@ -43,7 +43,7 @@ export function DiscoverFieldDetails({
- {!indexPattern.metaFields.includes(field.name) && !field.scripted ? ( + {onAddFilter && !indexPattern.metaFields.includes(field.name) && !field.scripted ? ( onAddFilter('_exists_', field.name, '+')} data-test-subj="onAddFilterButton" diff --git a/src/plugins/discover/public/application/main/components/sidebar/discover_sidebar.tsx b/src/plugins/discover/public/application/main/components/sidebar/discover_sidebar.tsx index 5ce3ad6cd147d..f7fdb09d4c6b8 100644 --- a/src/plugins/discover/public/application/main/components/sidebar/discover_sidebar.tsx +++ b/src/plugins/discover/public/application/main/components/sidebar/discover_sidebar.tsx @@ -335,16 +335,18 @@ export function DiscoverSidebarComponent({ }} /> )} - -
- - -
+ {onAddFilter && ( + +
+ + +
+ )}
{ diff --git a/src/plugins/discover/public/application/main/components/sidebar/discover_sidebar_responsive.tsx b/src/plugins/discover/public/application/main/components/sidebar/discover_sidebar_responsive.tsx index e4134184306fa..3b433869e4c5f 100644 --- a/src/plugins/discover/public/application/main/components/sidebar/discover_sidebar_responsive.tsx +++ b/src/plugins/discover/public/application/main/components/sidebar/discover_sidebar_responsive.tsx @@ -62,7 +62,7 @@ export interface DiscoverSidebarResponsiveProps { /** * Callback function when adding a filter from sidebar */ - onAddFilter: (field: DataViewField | string, value: string, type: '+' | '-') => void; + onAddFilter?: (field: DataViewField | string, value: string, type: '+' | '-') => void; /** * Callback function when changing an index pattern */ @@ -210,7 +210,7 @@ export function DiscoverSidebarResponsive(props: DiscoverSidebarResponsiveProps) const editField = useMemo( () => - canEditDataView && selectedIndexPattern + !props.state.textBasedLanguageMode && canEditDataView && selectedIndexPattern ? (fieldName?: string) => { const ref = dataViewFieldEditor.openEditor({ ctx: { @@ -230,11 +230,12 @@ export function DiscoverSidebarResponsive(props: DiscoverSidebarResponsiveProps) } : undefined, [ + props.state.textBasedLanguageMode, canEditDataView, - closeFlyout, - dataViewFieldEditor, selectedIndexPattern, + dataViewFieldEditor, setFieldEditorRef, + closeFlyout, onEditRuntimeField, ] ); diff --git a/src/plugins/discover/public/application/main/components/top_nav/discover_topnav.tsx b/src/plugins/discover/public/application/main/components/top_nav/discover_topnav.tsx index fd4a8b61ff062..ef58b1149fbee 100644 --- a/src/plugins/discover/public/application/main/components/top_nav/discover_topnav.tsx +++ b/src/plugins/discover/public/application/main/components/top_nav/discover_topnav.tsx @@ -177,6 +177,7 @@ export const DiscoverTopNav = ({ onChangeDataView: (newIndexPatternId: string) => onChangeIndexPattern(newIndexPatternId), textBasedLanguages: ['SQL'] as DataViewPickerProps['textBasedLanguages'], }; + const state = stateContainer.appStateContainer.getState(); return ( void; }): TopNavMenuData[] => { + const textBasedLanguageMode = state.appStateContainer.getState().textBasedLanguageMode; const options = { id: 'options', label: i18n.translate('discover.localMenu.localMenu.optionsTitle', { @@ -183,8 +184,8 @@ export const getTopNavLinks = ({ ...(services.capabilities.advancedSettings.save ? [options] : []), newSearch, openSearch, - ...(services.triggersActionsUi ? [alerts] : []), - shareSearch, + ...(services.triggersActionsUi && !textBasedLanguageMode ? [alerts] : []), + ...(!textBasedLanguageMode ? [shareSearch] : []), inspectSearch, ...(services.capabilities.discover.save ? [saveSearch] : []), ]; diff --git a/src/plugins/discover/public/application/main/hooks/use_discover_state.ts b/src/plugins/discover/public/application/main/hooks/use_discover_state.ts index 2d82e12824f04..f71d1a41f3d48 100644 --- a/src/plugins/discover/public/application/main/hooks/use_discover_state.ts +++ b/src/plugins/discover/public/application/main/hooks/use_discover_state.ts @@ -8,6 +8,7 @@ import { useMemo, useEffect, useState, useCallback } from 'react'; import { isEqual } from 'lodash'; import { History } from 'history'; +import { AggregateQuery, Query } from '@kbn/es-query'; import { getState } from '../services/discover_state'; import { getStateDefaults } from '../utils/get_state_defaults'; import { DiscoverServices } from '../../../build_services'; @@ -26,6 +27,14 @@ import { getSwitchIndexPatternAppState } from '../utils/get_switch_index_pattern import { SortPairArr } from '../../../components/doc_table/utils/get_sort'; import { DataTableRecord } from '../../../types'; +function isOfAggregateQueryType(query: AggregateQuery | Query): query is AggregateQuery { + return Boolean(query && 'sql' in query); +} + +function getAggregateQueryMode(query: AggregateQuery): string { + return Object.keys(query)[0]; +} + export function useDiscoverState({ services, history, @@ -226,6 +235,17 @@ export function useDiscoverState({ } }, [initialFetchStatus, refetch$, indexPattern, savedSearch.id]); + useEffect(() => { + let textBasedLanguageMode = ''; + if (state.query && isOfAggregateQueryType(state.query)) { + const aggregatedQuery = state.query as AggregateQuery; + textBasedLanguageMode = getAggregateQueryMode(aggregatedQuery); + } else { + textBasedLanguageMode = ''; + } + stateContainer.setAppState({ textBasedLanguageMode }); + }, [state, stateContainer]); + return { data$, indexPattern, diff --git a/src/plugins/discover/public/application/main/services/discover_state.ts b/src/plugins/discover/public/application/main/services/discover_state.ts index 4a27929849bc0..8717560e71394 100644 --- a/src/plugins/discover/public/application/main/services/discover_state.ts +++ b/src/plugins/discover/public/application/main/services/discover_state.ts @@ -91,6 +91,10 @@ export interface AppState { * Document explorer row height option */ rowHeight?: number; + /** + * Text based languages mode (sql | esql) + */ + textBasedLanguageMode?: string; } interface GetStateParams { @@ -176,8 +180,12 @@ export interface GetStateReturn { } const APP_STATE_URL_KEY = '_a'; -function isOfAggregateQueryType(arg: AggregateQuery | Query): arg is AggregateQuery { - return Boolean(arg && 'sql' in arg); +function isOfAggregateQueryType(query: AggregateQuery | Query): query is AggregateQuery { + return Boolean(query && 'sql' in query); +} + +function getAggregateQueryMode(query: AggregateQuery): string { + return Object.keys(query)[0]; } /** @@ -202,7 +210,8 @@ export function getState({ if (appStateFromUrl && appStateFromUrl.query && !appStateFromUrl.query.language) { if (isOfAggregateQueryType(appStateFromUrl.query)) { - appStateFromUrl.query = appStateFromUrl.query; + const aggregatedQuery = appStateFromUrl.query as AggregateQuery; + appStateFromUrl.textBasedLanguageMode = getAggregateQueryMode(aggregatedQuery); } else { appStateFromUrl.query = migrateLegacyQuery(appStateFromUrl.query); } diff --git a/src/plugins/discover/public/application/main/utils/fetch_all.ts b/src/plugins/discover/public/application/main/utils/fetch_all.ts index 655027dddbf1e..231d8b9369d00 100644 --- a/src/plugins/discover/public/application/main/utils/fetch_all.ts +++ b/src/plugins/discover/public/application/main/utils/fetch_all.ts @@ -9,7 +9,6 @@ import { DataPublicPluginStart, ISearchSource } from '@kbn/data-plugin/public'; import { Adapters } from '@kbn/inspector-plugin'; import { ReduxLikeStateContainer } from '@kbn/kibana-utils-plugin/common'; import { DataViewType } from '@kbn/data-views-plugin/public'; -import { buildDataTableRecord } from '../../../utils/build_data_record'; import { sendCompleteMsg, sendErrorMsg, @@ -33,6 +32,7 @@ import { SavedSearchData, } from '../hooks/use_saved_search'; import { DiscoverServices } from '../../../build_services'; +import { fetchSql } from './fetch_sql'; export interface FetchDeps { abortController: AbortController; @@ -87,15 +87,17 @@ export function fetchAll( sendResetMsg(dataSubjects, initialFetchStatus); } - const { hideChart, sort } = appStateContainer.getState(); + const { hideChart, sort, query, textBasedLanguageMode } = appStateContainer.getState(); // Update the base searchSource, base for all child fetches - updateSearchSource(searchSource, false, { - indexPattern, - services, - sort: sort as SortOrder[], - useNewFieldsApi, - }); + if (!textBasedLanguageMode) { + updateSearchSource(searchSource, false, { + indexPattern, + services, + sort: sort as SortOrder[], + useNewFieldsApi, + }); + } // Mark all subjects as loading sendLoadingMsg(dataSubjects.main$); @@ -107,11 +109,19 @@ export function fetchAll( !hideChart && indexPattern.isTimeBased() && indexPattern.type !== DataViewType.ROLLUP; // Start fetching all required requests - const documents = fetchDocuments(searchSource.createCopy(), fetchDeps); - const charts = isChartVisible ? fetchChart(searchSource.createCopy(), fetchDeps) : undefined; - const totalHits = !isChartVisible - ? fetchTotalHits(searchSource.createCopy(), fetchDeps) - : undefined; + const documents = + textBasedLanguageMode && query + ? fetchSql(query, services.indexPatterns, data, services.expressions) + : fetchDocuments(searchSource.createCopy(), fetchDeps); + + const charts = + isChartVisible && !textBasedLanguageMode + ? fetchChart(searchSource.createCopy(), fetchDeps) + : undefined; + const totalHits = + !isChartVisible && !textBasedLanguageMode + ? fetchTotalHits(searchSource.createCopy(), fetchDeps) + : undefined; /** * This method checks the passed in hit count and will send a PARTIAL message to main$ @@ -140,13 +150,10 @@ export function fetchAll( result: docs.length, }); } - const dataView = searchSource.getField('index')!; - - const resultDocs = docs.map((doc) => buildDataTableRecord(doc, dataView)); dataSubjects.documents$.next({ fetchStatus: FetchStatus.COMPLETE, - result: resultDocs, + result: docs, }); checkHitCount(docs.length); diff --git a/src/plugins/discover/public/application/main/utils/fetch_documents.ts b/src/plugins/discover/public/application/main/utils/fetch_documents.ts index e09875d11deb6..05d3d2f50f9a0 100644 --- a/src/plugins/discover/public/application/main/utils/fetch_documents.ts +++ b/src/plugins/discover/public/application/main/utils/fetch_documents.ts @@ -10,6 +10,7 @@ import { filter, map } from 'rxjs/operators'; import { lastValueFrom } from 'rxjs'; import { isCompleteResponse, ISearchSource } from '@kbn/data-plugin/public'; import { SAMPLE_SIZE_SETTING } from '../../../../common'; +import { buildDataTableRecordList } from '../../../utils/build_data_record'; import { FetchDeps } from './fetch_all'; /** @@ -31,6 +32,7 @@ export const fetchDocuments = ( // not a rollup index pattern. searchSource.setOverwriteDataViewType(undefined); } + const dataView = searchSource.getField('index')!; const executionContext = { description: 'fetch documents', @@ -53,7 +55,9 @@ export const fetchDocuments = ( }) .pipe( filter((res) => isCompleteResponse(res)), - map((res) => res.rawResponse.hits.hits) + map((res) => { + return buildDataTableRecordList(res.rawResponse.hits.hits, dataView); + }) ); return lastValueFrom(fetch$); diff --git a/src/plugins/discover/public/application/main/utils/fetch_sql.ts b/src/plugins/discover/public/application/main/utils/fetch_sql.ts new file mode 100644 index 0000000000000..0a3e2e9205759 --- /dev/null +++ b/src/plugins/discover/public/application/main/utils/fetch_sql.ts @@ -0,0 +1,53 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ +import { pluck } from 'rxjs/operators'; +import { lastValueFrom } from 'rxjs'; +import { Query } from '@kbn/es-query'; +import type { DataPublicPluginStart } from '@kbn/data-plugin/public'; +import type { ExpressionsStart } from '@kbn/expressions-plugin/public'; +import type { Datatable } from '@kbn/expressions-plugin/public'; +import type { DataViewsContract } from '@kbn/data-views-plugin/common'; +import { queryStateToExpressionAst } from '@kbn/data-plugin/common'; +import { DataTableRecord } from '../../../types'; + +export function fetchSql( + query: Query, + dataViewsService: DataViewsContract, + data: DataPublicPluginStart, + expressions: ExpressionsStart +) { + const timeRange = data.query.timefilter.timefilter.getTime(); + return queryStateToExpressionAst({ + query, + time: timeRange, + dataViewsService, + }) + .then((ast) => { + if (ast) { + const execution = expressions.run(ast, null); + let finalData: DataTableRecord[] = []; + execution.pipe(pluck('result')).subscribe((response) => { + const table = response as Datatable; + const rows = table?.rows ?? []; + finalData = rows.map( + (row: Record, idx: number) => + ({ + id: String(idx), + raw: row, + flattened: row, + } as unknown as DataTableRecord) + ); + }); + return lastValueFrom(execution).then(() => finalData || []); + } + return []; + }) + .catch((err) => { + throw new Error(err.message); + }); +} diff --git a/src/plugins/discover/public/build_services.ts b/src/plugins/discover/public/build_services.ts index d77bc5dde2660..23fe49149b4ea 100644 --- a/src/plugins/discover/public/build_services.ts +++ b/src/plugins/discover/public/build_services.ts @@ -27,6 +27,7 @@ import { DataViewsContract, DataPublicPluginStart, } from '@kbn/data-plugin/public'; +import type { ExpressionsStart } from '@kbn/expressions-plugin/public'; import { Start as InspectorPublicPluginStart } from '@kbn/inspector-plugin/public'; import { SharePluginStart } from '@kbn/share-plugin/public'; import { ChartsPluginStart } from '@kbn/charts-plugin/public'; @@ -81,6 +82,7 @@ export interface DiscoverServices { spaces?: SpacesApi; triggersActionsUi: TriggersAndActionsUIPublicPluginStart; locator: DiscoverAppLocator; + expressions: ExpressionsStart; } export const buildServices = memoize(function ( @@ -125,5 +127,6 @@ export const buildServices = memoize(function ( dataViewEditor: plugins.dataViewEditor, triggersActionsUi: plugins.triggersActionsUi, locator, + expressions: plugins.expressions, }; }); diff --git a/src/plugins/discover/public/components/discover_grid/discover_grid.tsx b/src/plugins/discover/public/components/discover_grid/discover_grid.tsx index 5b60a92110320..471f5ed76b177 100644 --- a/src/plugins/discover/public/components/discover_grid/discover_grid.tsx +++ b/src/plugins/discover/public/components/discover_grid/discover_grid.tsx @@ -121,7 +121,7 @@ export interface DiscoverGridProps { /** * Function to set the expanded document, which is displayed in a flyout */ - setExpandedDoc: (doc?: DataTableRecord) => void; + setExpandedDoc?: (doc?: DataTableRecord) => void; /** * Grid display settings persisted in Elasticsearch (e.g. column width) */ @@ -332,8 +332,10 @@ export const DiscoverGrid = ({ isSortEnabled, services, valueToStringConverter, + onFilter, }), [ + onFilter, displayedColumns, displayedRows, indexPattern, @@ -367,8 +369,8 @@ export const DiscoverGrid = ({ return { columns: sortingColumns, onSort: () => {} }; }, [sortingColumns, onTableSort, isSortEnabled]); const lead = useMemo( - () => getLeadControlColumns().filter(({ id }) => controlColumnIds.includes(id)), - [controlColumnIds] + () => getLeadControlColumns(setExpandedDoc).filter(({ id }) => controlColumnIds.includes(id)), + [controlColumnIds, setExpandedDoc] ); const additionalControls = useMemo( @@ -542,7 +544,7 @@ export const DiscoverGrid = ({

)} - {expandedDoc && ( + {setExpandedDoc && expandedDoc && ( ( - - - {i18n.translate('discover.controlColumnHeader', { - defaultMessage: 'Control column', - })} - - - ), - rowCellRender: ExpandButton, - }, - { - id: 'select', - width: 24, - rowCellRender: SelectButton, - headerCellRender: () => ( - - - {i18n.translate('discover.selectColumnHeader', { - defaultMessage: 'Select column', - })} - - - ), - }, - ]; +const openDetails = { + id: 'openDetails', + width: 24, + headerCellRender: () => ( + + + {i18n.translate('discover.controlColumnHeader', { + defaultMessage: 'Control column', + })} + + + ), + rowCellRender: ExpandButton, +}; + +const select = { + id: 'select', + width: 24, + rowCellRender: SelectButton, + headerCellRender: () => ( + + + {i18n.translate('discover.selectColumnHeader', { + defaultMessage: 'Select column', + })} + + + ), +}; + +export function getLeadControlColumns(setExpandedDoc?: (doc?: DataTableRecord) => void) { + if (!setExpandedDoc) { + return [select]; + } + return [openDetails, select]; } export function buildEuiGridColumn({ @@ -62,6 +68,7 @@ export function buildEuiGridColumn({ services, valueToStringConverter, rowsCount, + onFilter, }: { columnName: string; columnWidth: number | undefined; @@ -71,6 +78,7 @@ export function buildEuiGridColumn({ services: DiscoverServices; valueToStringConverter: ValueToStringConverter; rowsCount: number; + onFilter?: DocViewFilterFn; }) { const indexPatternField = indexPattern.getFieldByName(columnName); const column: EuiDataGridColumn = { @@ -107,7 +115,7 @@ export function buildEuiGridColumn({ }), ], }, - cellActions: indexPatternField ? buildCellActions(indexPatternField) : [], + cellActions: indexPatternField ? buildCellActions(indexPatternField, onFilter) : [], }; if (column.id === indexPattern.timeFieldName) { @@ -153,6 +161,7 @@ export function getEuiGridColumns({ isSortEnabled, services, valueToStringConverter, + onFilter, }: { columns: string[]; rowsCount: number; @@ -163,6 +172,7 @@ export function getEuiGridColumns({ isSortEnabled: boolean; services: DiscoverServices; valueToStringConverter: ValueToStringConverter; + onFilter: DocViewFilterFn; }) { const timeFieldName = indexPattern.timeFieldName; const getColWidth = (column: string) => settings?.columns?.[column]?.width ?? 0; @@ -182,6 +192,7 @@ export function getEuiGridColumns({ services, valueToStringConverter, rowsCount, + onFilter, }) ); } diff --git a/src/plugins/discover/public/components/discover_grid/discover_grid_context.tsx b/src/plugins/discover/public/components/discover_grid/discover_grid_context.tsx index 0761e4c40376e..24b2275f11763 100644 --- a/src/plugins/discover/public/components/discover_grid/discover_grid_context.tsx +++ b/src/plugins/discover/public/components/discover_grid/discover_grid_context.tsx @@ -13,9 +13,9 @@ import type { DataTableRecord, ValueToStringConverter } from '../../types'; export interface GridContext { expanded?: DataTableRecord | undefined; - setExpanded: (hit?: DataTableRecord) => void; + setExpanded?: (hit?: DataTableRecord) => void; rows: DataTableRecord[]; - onFilter: DocViewFilterFn; + onFilter?: DocViewFilterFn; indexPattern: DataView; isDarkMode: boolean; selectedDocs: string[]; diff --git a/src/plugins/discover/public/components/discover_grid/discover_grid_flyout.tsx b/src/plugins/discover/public/components/discover_grid/discover_grid_flyout.tsx index c2165fc27ee2a..af16fba24556e 100644 --- a/src/plugins/discover/public/components/discover_grid/discover_grid_flyout.tsx +++ b/src/plugins/discover/public/components/discover_grid/discover_grid_flyout.tsx @@ -38,7 +38,7 @@ export interface DiscoverGridFlyoutProps { indexPattern: DataView; onAddColumn: (column: string) => void; onClose: () => void; - onFilter: DocViewFilterFn; + onFilter?: DocViewFilterFn; onRemoveColumn: (column: string) => void; setExpandedDoc: (doc: DataTableRecord) => void; } @@ -213,14 +213,18 @@ export function DiscoverGridFlyout({ hit={actualHit} columns={columns} indexPattern={indexPattern} - filter={(mapping, value, mode) => { - onFilter(mapping, value, mode); - services.toastNotifications.addSuccess( - i18n.translate('discover.grid.flyout.toastFilterAdded', { - defaultMessage: `Filter was added`, - }) - ); - }} + filter={ + onFilter + ? (mapping, value, mode) => { + onFilter(mapping, value, mode); + services.toastNotifications.addSuccess( + i18n.translate('discover.grid.flyout.toastFilterAdded', { + defaultMessage: `Filter was added`, + }) + ); + } + : undefined + } onRemoveColumn={(columnName: string) => { onRemoveColumn(columnName); services.toastNotifications.addSuccess( diff --git a/src/plugins/discover/public/plugin.tsx b/src/plugins/discover/public/plugin.tsx index e9eaba11d048c..4fd5bdf09827a 100644 --- a/src/plugins/discover/public/plugin.tsx +++ b/src/plugins/discover/public/plugin.tsx @@ -18,6 +18,7 @@ import { PluginInitializerContext, } from '@kbn/core/public'; import { UiActionsSetup, UiActionsStart } from '@kbn/ui-actions-plugin/public'; +import { ExpressionsSetup, ExpressionsStart } from '@kbn/expressions-plugin/public'; import { EmbeddableSetup, EmbeddableStart } from '@kbn/embeddable-plugin/public'; import { ChartsPluginStart } from '@kbn/charts-plugin/public'; import { NavigationPublicPluginStart as NavigationStart } from '@kbn/navigation-plugin/public'; @@ -152,6 +153,7 @@ export interface DiscoverSetupPlugins { urlForwarding: UrlForwardingSetup; home?: HomePublicPluginSetup; data: DataPublicPluginSetup; + expressions: ExpressionsSetup; } /** @@ -173,6 +175,7 @@ export interface DiscoverStartPlugins { dataViewFieldEditor: IndexPatternFieldEditorStart; spaces?: SpacesPluginStart; triggersActionsUi: TriggersAndActionsUIPublicPluginStart; + expressions: ExpressionsStart; } /** diff --git a/src/plugins/discover/tsconfig.json b/src/plugins/discover/tsconfig.json index 9915680ada26e..5cdc21844c8a1 100644 --- a/src/plugins/discover/tsconfig.json +++ b/src/plugins/discover/tsconfig.json @@ -11,6 +11,7 @@ { "path": "../../core/tsconfig.json" }, { "path": "../charts/tsconfig.json" }, { "path": "../data/tsconfig.json" }, + { "path": "../expressions/tsconfig.json" }, { "path": "../embeddable/tsconfig.json" }, { "path": "../inspector/tsconfig.json" }, { "path": "../url_forwarding/tsconfig.json" }, From cbbd12961740876fd5031a1bfebe7c4d9ea41bdb Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Fri, 24 Jun 2022 15:37:31 +0300 Subject: [PATCH 018/115] Fix expression test --- src/plugins/data/common/query/to_expression_ast.test.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/plugins/data/common/query/to_expression_ast.test.ts b/src/plugins/data/common/query/to_expression_ast.test.ts index 72961040febe6..0f376e98256a8 100644 --- a/src/plugins/data/common/query/to_expression_ast.test.ts +++ b/src/plugins/data/common/query/to_expression_ast.test.ts @@ -5,18 +5,20 @@ * in compliance with, at your election, the Elastic License 2.0 or the Server * Side Public License, v 1. */ - +import { DataViewsContract } from '@kbn/data-views-plugin/common'; import { queryStateToExpressionAst } from './to_expression_ast'; describe('queryStateToExpressionAst', () => { - it('returns an object with the correct structure', () => { - const actual = queryStateToExpressionAst({ + it('returns an object with the correct structure', async () => { + const dataViewsService = {} as unknown as DataViewsContract; + const actual = await queryStateToExpressionAst({ filters: [], query: { language: 'lucene', query: '' }, time: { from: 'now', to: 'now+7d', }, + dataViewsService, }); expect(actual).toMatchInlineSnapshot(` From 617bf18710c1dd9985861d86808829049a2e7540 Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Fri, 24 Jun 2022 15:41:39 +0300 Subject: [PATCH 019/115] Fix editor zIndex --- .../text_based_languages_editor.styles.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/unified_search/public/query_string_input/text_based_languages_editor/text_based_languages_editor.styles.ts b/src/plugins/unified_search/public/query_string_input/text_based_languages_editor/text_based_languages_editor.styles.ts index 1b7d775cfab1b..65569a4eff904 100644 --- a/src/plugins/unified_search/public/query_string_input/text_based_languages_editor/text_based_languages_editor.styles.ts +++ b/src/plugins/unified_search/public/query_string_input/text_based_languages_editor/text_based_languages_editor.styles.ts @@ -27,7 +27,7 @@ export const textBasedLanguagedEditorStyles = ( return { editorContainer: { position, - zIndex: isCompactFocused ? 1 : 0, + zIndex: isCompactFocused ? 4 : 0, height: `${editorHeight}px`, border: isCompactFocused ? euiTheme.border.thin : 'none', }, From fa0295e810ba4ad672b8f2c40cd82c15a10161b9 Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Fri, 24 Jun 2022 16:16:33 +0300 Subject: [PATCH 020/115] Fix types error --- src/plugins/data/common/query/query_state.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/plugins/data/common/query/query_state.ts b/src/plugins/data/common/query/query_state.ts index a9ca73d7f0def..fbc5626f9a28c 100644 --- a/src/plugins/data/common/query/query_state.ts +++ b/src/plugins/data/common/query/query_state.ts @@ -8,7 +8,7 @@ import type { Filter } from '@kbn/es-query'; import type { TimeRange, RefreshInterval } from './timefilter/types'; -import type { Query, AggregateQuery } from './types'; +import type { Query } from './types'; /** * All query state service state @@ -22,5 +22,5 @@ export type QueryState = { time?: TimeRange; refreshInterval?: RefreshInterval; filters?: Filter[]; - query?: Query | AggregateQuery; + query?: Query; }; From fb459498d901f3a93e33a462ef410f2c364fcb1d Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Fri, 24 Jun 2022 16:33:28 +0300 Subject: [PATCH 021/115] Fix type check in Discover --- .../components/discover_grid/discover_grid_expand_button.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/discover/public/components/discover_grid/discover_grid_expand_button.tsx b/src/plugins/discover/public/components/discover_grid/discover_grid_expand_button.tsx index e00e722f9c2e9..ff0d84435fcd0 100644 --- a/src/plugins/discover/public/components/discover_grid/discover_grid_expand_button.tsx +++ b/src/plugins/discover/public/components/discover_grid/discover_grid_expand_button.tsx @@ -52,7 +52,7 @@ export const ExpandButton = ({ rowIndex, setCellProps }: EuiDataGridCellValueEle iconSize="s" aria-label={buttonLabel} data-test-subj={testSubj} - onClick={() => setExpanded(isCurrentRowExpanded ? undefined : current)} + onClick={() => setExpanded?.(isCurrentRowExpanded ? undefined : current)} color={isCurrentRowExpanded ? 'primary' : 'text'} iconType={isCurrentRowExpanded ? 'minimize' : 'expand'} isSelected={isCurrentRowExpanded} From d1ef05d6fc6c8c273727e2ab1da15e8e16646292 Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Fri, 24 Jun 2022 17:06:40 +0300 Subject: [PATCH 022/115] Fix more types --- .../public/application/main/utils/fetch_all.test.ts | 13 +++++++++---- .../discover_grid/discover_grid_columns.test.tsx | 3 +++ 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/plugins/discover/public/application/main/utils/fetch_all.test.ts b/src/plugins/discover/public/application/main/utils/fetch_all.test.ts index e33d931c571da..813b25e5be692 100644 --- a/src/plugins/discover/public/application/main/utils/fetch_all.test.ts +++ b/src/plugins/discover/public/application/main/utils/fetch_all.test.ts @@ -116,14 +116,15 @@ describe('test fetchAll', () => { { _id: '1', _index: 'logs' }, { _id: '2', _index: 'logs' }, ]; - mockFetchDocuments.mockResolvedValue(hits); + const documents = hits.map((hit) => buildDataTableRecord(hit, indexPatternMock)); + mockFetchDocuments.mockResolvedValue(documents); await fetchAll(subjects, searchSource, false, deps); expect(await collect()).toEqual([ { fetchStatus: FetchStatus.UNINITIALIZED }, { fetchStatus: FetchStatus.LOADING }, { fetchStatus: FetchStatus.COMPLETE, - result: hits.map((hit) => buildDataTableRecord(hit, indexPatternMock)), + result: documents, }, ]); }); @@ -135,7 +136,9 @@ describe('test fetchAll', () => { { _id: '2', _index: 'logs' }, ]; searchSource.getField('index')!.isTimeBased = () => false; - mockFetchDocuments.mockResolvedValue(hits); + const documents = hits.map((hit) => buildDataTableRecord(hit, indexPatternMock)); + mockFetchDocuments.mockResolvedValue(documents); + mockFetchTotalHits.mockResolvedValue(42); await fetchAll(subjects, searchSource, false, deps); expect(await collect()).toEqual([ @@ -177,7 +180,9 @@ describe('test fetchAll', () => { const collectMain = subjectCollector(subjects.main$); searchSource.getField('index')!.isTimeBased = () => false; mockFetchTotalHits.mockRejectedValue({ msg: 'Oh noes!' }); - mockFetchDocuments.mockResolvedValue([{ _id: '1', _index: 'logs' }]); + const hits = [{ _id: '1', _index: 'logs' }]; + const documents = hits.map((hit) => buildDataTableRecord(hit, indexPatternMock)); + mockFetchDocuments.mockResolvedValue(documents); await fetchAll(subjects, searchSource, false, deps); expect(await collectTotalHits()).toEqual([ { fetchStatus: FetchStatus.UNINITIALIZED }, diff --git a/src/plugins/discover/public/components/discover_grid/discover_grid_columns.test.tsx b/src/plugins/discover/public/components/discover_grid/discover_grid_columns.test.tsx index 0f32075cc4e66..199abbb3362d2 100644 --- a/src/plugins/discover/public/components/discover_grid/discover_grid_columns.test.tsx +++ b/src/plugins/discover/public/components/discover_grid/discover_grid_columns.test.tsx @@ -24,6 +24,7 @@ describe('Discover grid columns', function () { valueToStringConverter: discoverGridContextMock.valueToStringConverter, rowsCount: 100, services: discoverServiceMock, + onFilter: () => {}, }); expect(actual).toMatchInlineSnapshot(` Array [ @@ -134,6 +135,7 @@ describe('Discover grid columns', function () { valueToStringConverter: discoverGridContextMock.valueToStringConverter, rowsCount: 100, services: discoverServiceMock, + onFilter: () => {}, }); expect(actual).toMatchInlineSnapshot(` Array [ @@ -238,6 +240,7 @@ describe('Discover grid columns', function () { valueToStringConverter: discoverGridContextMock.valueToStringConverter, rowsCount: 100, services: discoverServiceMock, + onFilter: () => {}, }); expect(actual).toMatchInlineSnapshot(` Array [ From 160eb054eb6f589381f3ab1796ce21bcc7508a01 Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Fri, 24 Jun 2022 19:09:22 +0300 Subject: [PATCH 023/115] some CI fixes --- .../main/components/top_nav/discover_topnav.test.tsx | 10 +++++++++- .../public/application/main/services/discover_state.ts | 9 +-------- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/src/plugins/discover/public/application/main/components/top_nav/discover_topnav.test.tsx b/src/plugins/discover/public/application/main/components/top_nav/discover_topnav.test.tsx index f228c17341ac1..170956750be6d 100644 --- a/src/plugins/discover/public/application/main/components/top_nav/discover_topnav.test.tsx +++ b/src/plugins/discover/public/application/main/components/top_nav/discover_topnav.test.tsx @@ -31,7 +31,15 @@ function getProps(savePermissions = true): DiscoverTopNavProps { discoverServiceMock.capabilities.discover!.save = savePermissions; return { - stateContainer: {} as GetStateReturn, + stateContainer: { + appStateContainer: { + getState: jest.fn(() => { + return { + textBasedLanguageMode: undefined, + }; + }), + }, + } as unknown as GetStateReturn, indexPattern: indexPatternMock, savedSearch: savedSearchMock, navigateTo: jest.fn(), diff --git a/src/plugins/discover/public/application/main/services/discover_state.ts b/src/plugins/discover/public/application/main/services/discover_state.ts index 8717560e71394..d8032630a9af7 100644 --- a/src/plugins/discover/public/application/main/services/discover_state.ts +++ b/src/plugins/discover/public/application/main/services/discover_state.ts @@ -184,10 +184,6 @@ function isOfAggregateQueryType(query: AggregateQuery | Query): query is Aggrega return Boolean(query && 'sql' in query); } -function getAggregateQueryMode(query: AggregateQuery): string { - return Object.keys(query)[0]; -} - /** * Builds and returns appState and globalState containers and helper functions * Used to sync URL with UI state @@ -209,10 +205,7 @@ export function getState({ const appStateFromUrl = stateStorage.get(APP_STATE_URL_KEY) as AppState; if (appStateFromUrl && appStateFromUrl.query && !appStateFromUrl.query.language) { - if (isOfAggregateQueryType(appStateFromUrl.query)) { - const aggregatedQuery = appStateFromUrl.query as AggregateQuery; - appStateFromUrl.textBasedLanguageMode = getAggregateQueryMode(aggregatedQuery); - } else { + if (!isOfAggregateQueryType(appStateFromUrl.query)) { appStateFromUrl.query = migrateLegacyQuery(appStateFromUrl.query); } } From 9018351df3d3d47eacffb00480c120f338bdc678 Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Fri, 24 Jun 2022 20:03:46 +0300 Subject: [PATCH 024/115] Fixes --- .../main/components/top_nav/get_top_nav_links.test.ts | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/plugins/discover/public/application/main/components/top_nav/get_top_nav_links.test.ts b/src/plugins/discover/public/application/main/components/top_nav/get_top_nav_links.test.ts index 40ef4669db093..455ff79fb7509 100644 --- a/src/plugins/discover/public/application/main/components/top_nav/get_top_nav_links.test.ts +++ b/src/plugins/discover/public/application/main/components/top_nav/get_top_nav_links.test.ts @@ -24,7 +24,15 @@ const services = { }, } as unknown as DiscoverServices; -const state = {} as unknown as GetStateReturn; +const state = { + appStateContainer: { + getState: jest.fn(() => { + return { + textBasedLanguageMode: undefined, + }; + }), + }, +} as unknown as GetStateReturn; test('getTopNavLinks result', () => { const topNavLinks = getTopNavLinks({ From 8661b3e0184ef4f6bb24dcb050e0b5dc62995a55 Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Mon, 27 Jun 2022 09:19:29 +0300 Subject: [PATCH 025/115] Cleanup after merge --- .../public/application/main/services/discover_state.ts | 5 ----- .../public/application/main/utils/cleanup_url_state.ts | 1 + 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/src/plugins/discover/public/application/main/services/discover_state.ts b/src/plugins/discover/public/application/main/services/discover_state.ts index 905c1a68a6529..265564918f74f 100644 --- a/src/plugins/discover/public/application/main/services/discover_state.ts +++ b/src/plugins/discover/public/application/main/services/discover_state.ts @@ -15,7 +15,6 @@ import { FilterStateStore, compareFilters, COMPARE_ALL_OPTIONS, - AggregateQuery, Query, } from '@kbn/es-query'; import { @@ -187,10 +186,6 @@ export interface GetStateReturn { } const APP_STATE_URL_KEY = '_a'; -function isOfAggregateQueryType(query: AggregateQuery | Query): query is AggregateQuery { - return Boolean(query && 'sql' in query); -} - /** * Builds and returns appState and globalState containers and helper functions * Used to sync URL with UI state diff --git a/src/plugins/discover/public/application/main/utils/cleanup_url_state.ts b/src/plugins/discover/public/application/main/utils/cleanup_url_state.ts index 03284a91fada0..20bc620b2b3e1 100644 --- a/src/plugins/discover/public/application/main/utils/cleanup_url_state.ts +++ b/src/plugins/discover/public/application/main/utils/cleanup_url_state.ts @@ -5,6 +5,7 @@ * in compliance with, at your election, the Elastic License 2.0 or the Server * Side Public License, v 1. */ +import { AggregateQuery, Query } from '@kbn/es-query'; import { migrateLegacyQuery } from '../../../utils/migrate_legacy_query'; import { AppState, AppStateUrl } from '../services/discover_state'; From 10f7040922af703e06b8e369675b8f862315b004 Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Mon, 27 Jun 2022 09:54:13 +0300 Subject: [PATCH 026/115] Remove from state --- .../components/layout/discover_documents.tsx | 12 +++---- .../components/layout/discover_layout.tsx | 13 ++++--- .../sidebar/discover_sidebar_responsive.tsx | 4 +-- .../top_nav/discover_topnav.test.tsx | 8 +---- .../components/top_nav/discover_topnav.tsx | 9 ++--- .../top_nav/get_top_nav_links.test.ts | 10 +----- .../components/top_nav/get_top_nav_links.tsx | 3 +- .../main/hooks/use_discover_state.ts | 20 ----------- .../main/hooks/use_saved_search.ts | 35 +++++++++++++------ .../main/hooks/use_saved_search_messages.ts | 17 ++++++++- .../main/services/discover_state.ts | 4 --- .../application/main/utils/fetch_all.ts | 26 +++++++++++--- .../main/utils/fetch_documents.test.ts | 8 +++-- 13 files changed, 93 insertions(+), 76 deletions(-) diff --git a/src/plugins/discover/public/application/main/components/layout/discover_documents.tsx b/src/plugins/discover/public/application/main/components/layout/discover_documents.tsx index 44fde58660ef9..e09feb1fa5205 100644 --- a/src/plugins/discover/public/application/main/components/layout/discover_documents.tsx +++ b/src/plugins/discover/public/application/main/components/layout/discover_documents.tsx @@ -110,10 +110,10 @@ function DiscoverDocumentsComponent({ const showTimeCol = useMemo( () => - !state.textBasedLanguageMode && + !documentState.textBasedLanguageMode && !uiSettings.get(DOC_HIDE_TIME_COLUMN_SETTING, false) && !!indexPattern.timeFieldName, - [uiSettings, indexPattern.timeFieldName, state.textBasedLanguageMode] + [uiSettings, indexPattern.timeFieldName, documentState.textBasedLanguageMode] ); if ( @@ -153,7 +153,7 @@ function DiscoverDocumentsComponent({ onFilter={onAddFilter as DocViewFilterFn} onMoveColumn={onMoveColumn} onRemoveColumn={onRemoveColumn} - onSort={!state.textBasedLanguageMode ? onSort : undefined} + onSort={!documentState.textBasedLanguageMode ? onSort : undefined} useNewFieldsApi={useNewFieldsApi} dataTestSubj="discoverDocTable" /> @@ -176,19 +176,19 @@ function DiscoverDocumentsComponent({ sampleSize={sampleSize} searchDescription={savedSearch.description} searchTitle={savedSearch.title} - setExpandedDoc={!state.textBasedLanguageMode ? setExpandedDoc : undefined} + setExpandedDoc={!documentState.textBasedLanguageMode ? setExpandedDoc : undefined} showTimeCol={showTimeCol} settings={state.grid} onAddColumn={onAddColumn} onFilter={onAddFilter as DocViewFilterFn} onRemoveColumn={onRemoveColumn} onSetColumns={onSetColumns} - onSort={!state.textBasedLanguageMode ? onSort : undefined} + onSort={!documentState.textBasedLanguageMode ? onSort : undefined} onResize={onResize} useNewFieldsApi={useNewFieldsApi} rowHeightState={state.rowHeight} onUpdateRowHeight={onUpdateRowHeight} - isSortEnabled={!state.textBasedLanguageMode} + isSortEnabled={!documentState.textBasedLanguageMode} />
diff --git a/src/plugins/discover/public/application/main/components/layout/discover_layout.tsx b/src/plugins/discover/public/application/main/components/layout/discover_layout.tsx index d2af050395b69..ea8c482ad8553 100644 --- a/src/plugins/discover/public/application/main/components/layout/discover_layout.tsx +++ b/src/plugins/discover/public/application/main/components/layout/discover_layout.tsx @@ -245,6 +245,7 @@ export function DiscoverLayout({ resetSavedSearch={resetSavedSearch} onChangeIndexPattern={onChangeIndexPattern} onEditRuntimeField={onEditRuntimeField} + textBasedLanguageMode={dataState.textBasedLanguageMode} /> - {!state.textBasedLanguageMode && ( + {!dataState.textBasedLanguageMode && ( <> - !props.state.textBasedLanguageMode && canEditDataView && selectedIndexPattern + !documentState.textBasedLanguageMode && canEditDataView && selectedIndexPattern ? (fieldName?: string) => { const ref = dataViewFieldEditor.openEditor({ ctx: { @@ -230,7 +230,7 @@ export function DiscoverSidebarResponsive(props: DiscoverSidebarResponsiveProps) } : undefined, [ - props.state.textBasedLanguageMode, + documentState.textBasedLanguageMode, canEditDataView, selectedIndexPattern, dataViewFieldEditor, diff --git a/src/plugins/discover/public/application/main/components/top_nav/discover_topnav.test.tsx b/src/plugins/discover/public/application/main/components/top_nav/discover_topnav.test.tsx index 170956750be6d..2922faf1e9295 100644 --- a/src/plugins/discover/public/application/main/components/top_nav/discover_topnav.test.tsx +++ b/src/plugins/discover/public/application/main/components/top_nav/discover_topnav.test.tsx @@ -32,13 +32,7 @@ function getProps(savePermissions = true): DiscoverTopNavProps { return { stateContainer: { - appStateContainer: { - getState: jest.fn(() => { - return { - textBasedLanguageMode: undefined, - }; - }), - }, + stateContainer: {} as GetStateReturn, } as unknown as GetStateReturn, indexPattern: indexPatternMock, savedSearch: savedSearchMock, diff --git a/src/plugins/discover/public/application/main/components/top_nav/discover_topnav.tsx b/src/plugins/discover/public/application/main/components/top_nav/discover_topnav.tsx index ef58b1149fbee..13d1753f5f755 100644 --- a/src/plugins/discover/public/application/main/components/top_nav/discover_topnav.tsx +++ b/src/plugins/discover/public/application/main/components/top_nav/discover_topnav.tsx @@ -28,6 +28,7 @@ export type DiscoverTopNavProps = Pick< resetSavedSearch: () => void; onChangeIndexPattern: (indexPattern: string) => void; onEditRuntimeField: () => void; + textBasedLanguageMode?: string; }; export const DiscoverTopNav = ({ @@ -43,6 +44,7 @@ export const DiscoverTopNav = ({ resetSavedSearch, onChangeIndexPattern, onEditRuntimeField, + textBasedLanguageMode, }: DiscoverTopNavProps) => { const history = useHistory(); const showDatePicker = useMemo( @@ -135,6 +137,7 @@ export const DiscoverTopNav = ({ onOpenInspector, searchSource, onOpenSavedSearch, + textBasedLanguageMode, }), [ indexPattern, @@ -145,6 +148,7 @@ export const DiscoverTopNav = ({ onOpenInspector, searchSource, onOpenSavedSearch, + textBasedLanguageMode, ] ); @@ -177,7 +181,6 @@ export const DiscoverTopNav = ({ onChangeDataView: (newIndexPatternId: string) => onChangeIndexPattern(newIndexPatternId), textBasedLanguages: ['SQL'] as DataViewPickerProps['textBasedLanguages'], }; - const state = stateContainer.appStateContainer.getState(); return ( { - return { - textBasedLanguageMode: undefined, - }; - }), - }, -} as unknown as GetStateReturn; +const state = {} as unknown as GetStateReturn; test('getTopNavLinks result', () => { const topNavLinks = getTopNavLinks({ diff --git a/src/plugins/discover/public/application/main/components/top_nav/get_top_nav_links.tsx b/src/plugins/discover/public/application/main/components/top_nav/get_top_nav_links.tsx index 95466bd6754ac..90a1f34453687 100644 --- a/src/plugins/discover/public/application/main/components/top_nav/get_top_nav_links.tsx +++ b/src/plugins/discover/public/application/main/components/top_nav/get_top_nav_links.tsx @@ -32,6 +32,7 @@ export const getTopNavLinks = ({ onOpenInspector, searchSource, onOpenSavedSearch, + textBasedLanguageMode, }: { indexPattern: DataView; navigateTo: (url: string) => void; @@ -41,8 +42,8 @@ export const getTopNavLinks = ({ onOpenInspector: () => void; searchSource: ISearchSource; onOpenSavedSearch: (id: string) => void; + textBasedLanguageMode?: string; }): TopNavMenuData[] => { - const textBasedLanguageMode = state.appStateContainer.getState().textBasedLanguageMode; const options = { id: 'options', label: i18n.translate('discover.localMenu.localMenu.optionsTitle', { diff --git a/src/plugins/discover/public/application/main/hooks/use_discover_state.ts b/src/plugins/discover/public/application/main/hooks/use_discover_state.ts index f71d1a41f3d48..2d82e12824f04 100644 --- a/src/plugins/discover/public/application/main/hooks/use_discover_state.ts +++ b/src/plugins/discover/public/application/main/hooks/use_discover_state.ts @@ -8,7 +8,6 @@ import { useMemo, useEffect, useState, useCallback } from 'react'; import { isEqual } from 'lodash'; import { History } from 'history'; -import { AggregateQuery, Query } from '@kbn/es-query'; import { getState } from '../services/discover_state'; import { getStateDefaults } from '../utils/get_state_defaults'; import { DiscoverServices } from '../../../build_services'; @@ -27,14 +26,6 @@ import { getSwitchIndexPatternAppState } from '../utils/get_switch_index_pattern import { SortPairArr } from '../../../components/doc_table/utils/get_sort'; import { DataTableRecord } from '../../../types'; -function isOfAggregateQueryType(query: AggregateQuery | Query): query is AggregateQuery { - return Boolean(query && 'sql' in query); -} - -function getAggregateQueryMode(query: AggregateQuery): string { - return Object.keys(query)[0]; -} - export function useDiscoverState({ services, history, @@ -235,17 +226,6 @@ export function useDiscoverState({ } }, [initialFetchStatus, refetch$, indexPattern, savedSearch.id]); - useEffect(() => { - let textBasedLanguageMode = ''; - if (state.query && isOfAggregateQueryType(state.query)) { - const aggregatedQuery = state.query as AggregateQuery; - textBasedLanguageMode = getAggregateQueryMode(aggregatedQuery); - } else { - textBasedLanguageMode = ''; - } - stateContainer.setAppState({ textBasedLanguageMode }); - }, [state, stateContainer]); - return { data$, indexPattern, diff --git a/src/plugins/discover/public/application/main/hooks/use_saved_search.ts b/src/plugins/discover/public/application/main/hooks/use_saved_search.ts index 0cfa1b2e97579..ca511904d7462 100644 --- a/src/plugins/discover/public/application/main/hooks/use_saved_search.ts +++ b/src/plugins/discover/public/application/main/hooks/use_saved_search.ts @@ -7,6 +7,7 @@ */ import { useCallback, useEffect, useMemo, useRef } from 'react'; import { BehaviorSubject, Subject } from 'rxjs'; +import { AggregateQuery, Query } from '@kbn/es-query'; import type { AutoRefreshDoneFn } from '@kbn/data-plugin/public'; import { ISearchSource } from '@kbn/data-plugin/public'; import { RequestAdapter } from '@kbn/inspector-plugin/public'; @@ -58,6 +59,7 @@ export type DataRefetchMsg = 'reset' | undefined; export interface DataMsg { fetchStatus: FetchStatus; error?: Error; + textBasedLanguageMode?: string; } export interface DataMainMsg extends DataMsg { @@ -83,6 +85,14 @@ export interface DataAvailableFieldsMsg extends DataMsg { fields?: string[]; } +function isOfAggregateQueryType(query: AggregateQuery | Query): query is AggregateQuery { + return Boolean(query && 'sql' in query); +} + +function getAggregateQueryMode(query: AggregateQuery): string { + return Object.keys(query)[0]; +} + /** * This hook return 2 observables, refetch$ allows to trigger data fetching, data$ to subscribe * to the data fetching @@ -106,6 +116,14 @@ export const useSavedSearch = ({ }) => { const { data, filterManager } = services; const timefilter = data.query.timefilter.timefilter; + const { query } = stateContainer.appStateContainer.getState(); + let textBasedLanguageMode = ''; + if (query && isOfAggregateQueryType(query)) { + const aggregatedQuery = query as AggregateQuery; + textBasedLanguageMode = getAggregateQueryMode(aggregatedQuery); + } else { + textBasedLanguageMode = ''; + } const inspectorAdapters = useMemo(() => ({ requests: new RequestAdapter() }), []); @@ -113,17 +131,12 @@ export const useSavedSearch = ({ * The observables the UI (aka React component) subscribes to get notified about * the changes in the data fetching process (high level: fetching started, data was received) */ - const main$: DataMain$ = useBehaviorSubject({ fetchStatus: initialFetchStatus }); - - const documents$: DataDocuments$ = useBehaviorSubject({ fetchStatus: initialFetchStatus }); - - const totalHits$: DataTotalHits$ = useBehaviorSubject({ fetchStatus: initialFetchStatus }); - - const charts$: DataCharts$ = useBehaviorSubject({ fetchStatus: initialFetchStatus }); - - const availableFields$: AvailableFields$ = useBehaviorSubject({ - fetchStatus: initialFetchStatus, - }); + const initialState = { fetchStatus: initialFetchStatus, textBasedLanguageMode }; + const main$: DataMain$ = useBehaviorSubject(initialState) as DataMain$; + const documents$: DataDocuments$ = useBehaviorSubject(initialState) as DataDocuments$; + const totalHits$: DataTotalHits$ = useBehaviorSubject(initialState) as DataTotalHits$; + const charts$: DataCharts$ = useBehaviorSubject(initialState) as DataCharts$; + const availableFields$: AvailableFields$ = useBehaviorSubject(initialState) as AvailableFields$; const dataSubjects = useMemo(() => { return { diff --git a/src/plugins/discover/public/application/main/hooks/use_saved_search_messages.ts b/src/plugins/discover/public/application/main/hooks/use_saved_search_messages.ts index a2d42147a9e8f..ac20cf5349073 100644 --- a/src/plugins/discover/public/application/main/hooks/use_saved_search_messages.ts +++ b/src/plugins/discover/public/application/main/hooks/use_saved_search_messages.ts @@ -33,10 +33,12 @@ export function sendCompleteMsg(main$: DataMain$, foundDocuments = true) { if (main$.getValue().fetchStatus === FetchStatus.COMPLETE) { return; } + const textBasedLanguageMode = main$.getValue().textBasedLanguageMode; main$.next({ fetchStatus: FetchStatus.COMPLETE, foundDocuments, error: undefined, + textBasedLanguageMode, }); } @@ -45,8 +47,10 @@ export function sendCompleteMsg(main$: DataMain$, foundDocuments = true) { */ export function sendPartialMsg(main$: DataMain$) { if (main$.getValue().fetchStatus === FetchStatus.LOADING) { + const textBasedLanguageMode = main$.getValue().textBasedLanguageMode; main$.next({ fetchStatus: FetchStatus.PARTIAL, + textBasedLanguageMode, }); } } @@ -54,10 +58,14 @@ export function sendPartialMsg(main$: DataMain$) { /** * Send LOADING message via main observable */ -export function sendLoadingMsg(data$: DataMain$ | DataDocuments$ | DataTotalHits$ | DataCharts$) { +export function sendLoadingMsg( + data$: DataMain$ | DataDocuments$ | DataTotalHits$ | DataCharts$, + textBasedLanguageMode: string +) { if (data$.getValue().fetchStatus !== FetchStatus.LOADING) { data$.next({ fetchStatus: FetchStatus.LOADING, + textBasedLanguageMode, }); } } @@ -69,9 +77,11 @@ export function sendErrorMsg( data$: DataMain$ | DataDocuments$ | DataTotalHits$ | DataCharts$, error: Error ) { + const textBasedLanguageMode = data$.getValue().textBasedLanguageMode; data$.next({ fetchStatus: FetchStatus.ERROR, error, + textBasedLanguageMode, }); } @@ -80,21 +90,26 @@ export function sendErrorMsg( * Needed when index pattern is switched or a new runtime field is added */ export function sendResetMsg(data: SavedSearchData, initialFetchStatus: FetchStatus) { + const textBasedLanguageMode = data.main$.getValue().textBasedLanguageMode; data.main$.next({ fetchStatus: initialFetchStatus, foundDocuments: undefined, + textBasedLanguageMode, }); data.documents$.next({ fetchStatus: initialFetchStatus, result: [], + textBasedLanguageMode, }); data.charts$.next({ fetchStatus: initialFetchStatus, chartData: undefined, bucketInterval: undefined, + textBasedLanguageMode, }); data.totalHits$.next({ fetchStatus: initialFetchStatus, result: undefined, + textBasedLanguageMode, }); } diff --git a/src/plugins/discover/public/application/main/services/discover_state.ts b/src/plugins/discover/public/application/main/services/discover_state.ts index 265564918f74f..86d279ec09c20 100644 --- a/src/plugins/discover/public/application/main/services/discover_state.ts +++ b/src/plugins/discover/public/application/main/services/discover_state.ts @@ -90,10 +90,6 @@ export interface AppState { * Document explorer row height option */ rowHeight?: number; - /** - * Text based languages mode (sql | esql) - */ - textBasedLanguageMode?: string; } export interface AppStateUrl extends Omit { diff --git a/src/plugins/discover/public/application/main/utils/fetch_all.ts b/src/plugins/discover/public/application/main/utils/fetch_all.ts index 231d8b9369d00..3e0bfda6ff79b 100644 --- a/src/plugins/discover/public/application/main/utils/fetch_all.ts +++ b/src/plugins/discover/public/application/main/utils/fetch_all.ts @@ -5,6 +5,7 @@ * in compliance with, at your election, the Elastic License 2.0 or the Server * Side Public License, v 1. */ +import { AggregateQuery, Query } from '@kbn/es-query'; import { DataPublicPluginStart, ISearchSource } from '@kbn/data-plugin/public'; import { Adapters } from '@kbn/inspector-plugin'; import { ReduxLikeStateContainer } from '@kbn/kibana-utils-plugin/common'; @@ -46,6 +47,14 @@ export interface FetchDeps { useNewFieldsApi: boolean; } +function isOfAggregateQueryType(query: AggregateQuery | Query): query is AggregateQuery { + return Boolean(query && 'sql' in query); +} + +function getAggregateQueryMode(query: AggregateQuery): string { + return Object.keys(query)[0]; +} + /** * This function starts fetching all required queries in Discover. This will be the query to load the individual * documents, and depending on whether a chart is shown either the aggregation query to load the chart data @@ -87,7 +96,14 @@ export function fetchAll( sendResetMsg(dataSubjects, initialFetchStatus); } - const { hideChart, sort, query, textBasedLanguageMode } = appStateContainer.getState(); + const { hideChart, sort, query } = appStateContainer.getState(); + let textBasedLanguageMode = ''; + if (query && isOfAggregateQueryType(query)) { + const aggregatedQuery = query as AggregateQuery; + textBasedLanguageMode = getAggregateQueryMode(aggregatedQuery); + } else { + textBasedLanguageMode = ''; + } // Update the base searchSource, base for all child fetches if (!textBasedLanguageMode) { @@ -100,10 +116,10 @@ export function fetchAll( } // Mark all subjects as loading - sendLoadingMsg(dataSubjects.main$); - sendLoadingMsg(dataSubjects.documents$); - sendLoadingMsg(dataSubjects.totalHits$); - sendLoadingMsg(dataSubjects.charts$); + sendLoadingMsg(dataSubjects.main$, textBasedLanguageMode); + sendLoadingMsg(dataSubjects.documents$, textBasedLanguageMode); + sendLoadingMsg(dataSubjects.totalHits$, textBasedLanguageMode); + sendLoadingMsg(dataSubjects.charts$, textBasedLanguageMode); const isChartVisible = !hideChart && indexPattern.isTimeBased() && indexPattern.type !== DataViewType.ROLLUP; diff --git a/src/plugins/discover/public/application/main/utils/fetch_documents.test.ts b/src/plugins/discover/public/application/main/utils/fetch_documents.test.ts index 5796c488dd8fb..2a7cabb8f30e2 100644 --- a/src/plugins/discover/public/application/main/utils/fetch_documents.test.ts +++ b/src/plugins/discover/public/application/main/utils/fetch_documents.test.ts @@ -14,6 +14,9 @@ import { IKibanaSearchResponse } from '@kbn/data-plugin/public'; import { SearchResponse } from '@elastic/elasticsearch/lib/api/types'; import { FetchDeps } from './fetch_all'; import { fetchTotalHits } from './fetch_total_hits'; +import type { EsHitRecord } from '../../../types'; +import { buildDataTableRecord } from '../../../utils/build_data_record'; +import { indexPatternMock } from '../../../__mocks__/index_pattern'; const getDeps = () => ({ @@ -30,10 +33,11 @@ describe('test fetchDocuments', () => { const hits = [ { _id: '1', foo: 'bar' }, { _id: '2', foo: 'baz' }, - ]; + ] as unknown as EsHitRecord[]; + const documents = hits.map((hit) => buildDataTableRecord(hit, indexPatternMock)); savedSearchMock.searchSource.fetch$ = () => of({ rawResponse: { hits: { hits } } } as unknown as IKibanaSearchResponse); - expect(fetchDocuments(savedSearchMock.searchSource, getDeps())).resolves.toEqual(hits); + expect(fetchDocuments(savedSearchMock.searchSource, getDeps())).resolves.toEqual(documents); }); test('rejects on query failure', () => { From 29c9baa50183a346bbcbca592fbae84a8b23cdd3 Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Mon, 27 Jun 2022 11:23:23 +0300 Subject: [PATCH 027/115] Connect search errors with the unified search editor --- .../components/layout/discover_layout.tsx | 7 +++ .../components/top_nav/discover_topnav.tsx | 5 +++ .../main/hooks/use_saved_search_messages.ts | 2 +- .../application/main/utils/fetch_sql.ts | 43 +++++++++++++------ .../query_string_input/query_bar_top_row.tsx | 2 + .../editor_footer.tsx | 4 +- .../text_based_languages_editor/index.tsx | 2 +- .../public/search_bar/create_search_bar.tsx | 1 + .../public/search_bar/search_bar.tsx | 2 + 9 files changed, 52 insertions(+), 16 deletions(-) diff --git a/src/plugins/discover/public/application/main/components/layout/discover_layout.tsx b/src/plugins/discover/public/application/main/components/layout/discover_layout.tsx index ea8c482ad8553..5bc3389e953cf 100644 --- a/src/plugins/discover/public/application/main/components/layout/discover_layout.tsx +++ b/src/plugins/discover/public/application/main/components/layout/discover_layout.tsx @@ -212,6 +212,12 @@ export function DiscoverLayout({ savedSearchTitle.current?.focus(); }, []); + const textBasedLanguageModeErrors = useMemo(() => { + if (dataState.textBasedLanguageMode) { + return dataState.error; + } + }, [dataState.error, dataState.textBasedLanguageMode]); + return (

void; onEditRuntimeField: () => void; textBasedLanguageMode?: string; + textBasedLanguageModeErrors?: Error; }; export const DiscoverTopNav = ({ @@ -45,6 +46,7 @@ export const DiscoverTopNav = ({ onChangeIndexPattern, onEditRuntimeField, textBasedLanguageMode, + textBasedLanguageModeErrors, }: DiscoverTopNavProps) => { const history = useHistory(); const showDatePicker = useMemo( @@ -199,6 +201,9 @@ export const DiscoverTopNav = ({ useDefaultBehaviors={true} dataViewPickerComponentProps={dataViewPickerProps} displayStyle="detached" + textBasedLanguageModeErrors={ + textBasedLanguageModeErrors ? [textBasedLanguageModeErrors] : undefined + } /> ); }; diff --git a/src/plugins/discover/public/application/main/hooks/use_saved_search_messages.ts b/src/plugins/discover/public/application/main/hooks/use_saved_search_messages.ts index ac20cf5349073..9b4572beb0405 100644 --- a/src/plugins/discover/public/application/main/hooks/use_saved_search_messages.ts +++ b/src/plugins/discover/public/application/main/hooks/use_saved_search_messages.ts @@ -60,7 +60,7 @@ export function sendPartialMsg(main$: DataMain$) { */ export function sendLoadingMsg( data$: DataMain$ | DataDocuments$ | DataTotalHits$ | DataCharts$, - textBasedLanguageMode: string + textBasedLanguageMode?: string ) { if (data$.getValue().fetchStatus !== FetchStatus.LOADING) { data$.next({ diff --git a/src/plugins/discover/public/application/main/utils/fetch_sql.ts b/src/plugins/discover/public/application/main/utils/fetch_sql.ts index 0a3e2e9205759..627ea8f0117d4 100644 --- a/src/plugins/discover/public/application/main/utils/fetch_sql.ts +++ b/src/plugins/discover/public/application/main/utils/fetch_sql.ts @@ -15,6 +15,13 @@ import type { DataViewsContract } from '@kbn/data-views-plugin/common'; import { queryStateToExpressionAst } from '@kbn/data-plugin/common'; import { DataTableRecord } from '../../../types'; +interface SQLErrorResponse { + error: { + message: string; + }; + type: 'error'; +} + export function fetchSql( query: Query, dataViewsService: DataViewsContract, @@ -31,19 +38,31 @@ export function fetchSql( if (ast) { const execution = expressions.run(ast, null); let finalData: DataTableRecord[] = []; - execution.pipe(pluck('result')).subscribe((response) => { - const table = response as Datatable; - const rows = table?.rows ?? []; - finalData = rows.map( - (row: Record, idx: number) => - ({ - id: String(idx), - raw: row, - flattened: row, - } as unknown as DataTableRecord) - ); + let error: string | undefined; + execution.pipe(pluck('result')).subscribe((resp) => { + const response = resp as Datatable | SQLErrorResponse; + if (response.type === 'error') { + error = response.error.message; + } else { + const table = response as Datatable; + const rows = table?.rows ?? []; + finalData = rows.map( + (row: Record, idx: number) => + ({ + id: String(idx), + raw: row, + flattened: row, + } as unknown as DataTableRecord) + ); + } + }); + return lastValueFrom(execution).then(() => { + if (error) { + throw new Error(error); + } else { + return finalData || []; + } }); - return lastValueFrom(execution).then(() => finalData || []); } return []; }) diff --git a/src/plugins/unified_search/public/query_string_input/query_bar_top_row.tsx b/src/plugins/unified_search/public/query_string_input/query_bar_top_row.tsx index 5718fa16f665b..84ba23e38d67d 100644 --- a/src/plugins/unified_search/public/query_string_input/query_bar_top_row.tsx +++ b/src/plugins/unified_search/public/query_string_input/query_bar_top_row.tsx @@ -85,6 +85,7 @@ export interface QueryBarTopRowProps { filters: Filter[]; onFiltersUpdated?: (filters: Filter[]) => void; dataViewPickerComponentProps?: DataViewPickerProps; + textBasedLanguageModeErrors?: Error[]; filterBar?: React.ReactNode; showDatePickerAsBadge?: boolean; showSubmitButton?: boolean; @@ -518,6 +519,7 @@ export const QueryBarTopRow = React.memo( onTextLangQueryChange={props.onTextLangQueryChange} expandCodeEditor={(status: boolean) => setCodeEditorIsExpanded(status)} isCodeEditorExpanded={codeEditorIsExpanded} + errors={props.textBasedLanguageModeErrors} /> ) ); diff --git a/src/plugins/unified_search/public/query_string_input/text_based_languages_editor/editor_footer.tsx b/src/plugins/unified_search/public/query_string_input/text_based_languages_editor/editor_footer.tsx index b4948c1d0b5d6..e007b11c6f74c 100644 --- a/src/plugins/unified_search/public/query_string_input/text_based_languages_editor/editor_footer.tsx +++ b/src/plugins/unified_search/public/query_string_input/text_based_languages_editor/editor_footer.tsx @@ -28,7 +28,7 @@ const COMMAND_KEY = isMac ? '⌘' : '^'; interface EditorFooterProps { lines: number; containerCSS: Interpolation; - errors?: string[]; + errors?: Error[]; } export const EditorFooter = memo(function EditorFooter({ @@ -91,7 +91,7 @@ export const EditorFooter = memo(function EditorFooter({ {errors.map((error, index) => { return ( - {error} + {String(error)} ); })} diff --git a/src/plugins/unified_search/public/query_string_input/text_based_languages_editor/index.tsx b/src/plugins/unified_search/public/query_string_input/text_based_languages_editor/index.tsx index 11f1f286d5817..9da91d876b796 100644 --- a/src/plugins/unified_search/public/query_string_input/text_based_languages_editor/index.tsx +++ b/src/plugins/unified_search/public/query_string_input/text_based_languages_editor/index.tsx @@ -39,7 +39,7 @@ interface TextBasedLanguagesEditorProps { onTextLangQueryChange: (query: any) => void; expandCodeEditor: (status: boolean) => void; isCodeEditorExpanded: boolean; - errors?: string[]; + errors?: Error[]; } const MAX_COMPACT_VIEW_LENGTH = 250; diff --git a/src/plugins/unified_search/public/search_bar/create_search_bar.tsx b/src/plugins/unified_search/public/search_bar/create_search_bar.tsx index 7fbb9df37b741..111462b3a80fd 100644 --- a/src/plugins/unified_search/public/search_bar/create_search_bar.tsx +++ b/src/plugins/unified_search/public/search_bar/create_search_bar.tsx @@ -204,6 +204,7 @@ export function createSearchBar({ placeholder={props.placeholder} {...overrideDefaultBehaviors(props)} dataViewPickerComponentProps={props.dataViewPickerComponentProps} + textBasedLanguageModeErrors={props.textBasedLanguageModeErrors} displayStyle={props.displayStyle} isScreenshotMode={isScreenshotMode} /> diff --git a/src/plugins/unified_search/public/search_bar/search_bar.tsx b/src/plugins/unified_search/public/search_bar/search_bar.tsx index 1231fc719e98b..a52123b066348 100644 --- a/src/plugins/unified_search/public/search_bar/search_bar.tsx +++ b/src/plugins/unified_search/public/search_bar/search_bar.tsx @@ -87,6 +87,7 @@ export interface SearchBarOwnProps { // super update button background fill control fillSubmitButton?: boolean; dataViewPickerComponentProps?: DataViewPickerProps; + textBasedLanguageModeErrors?: Error[]; showSubmitButton?: boolean; // defines size of suggestions query popover suggestionsSize?: SuggestionsListSize; @@ -530,6 +531,7 @@ class SearchBarUI extends Component { filters={this.props.filters!} onFiltersUpdated={this.props.onFiltersUpdated} dataViewPickerComponentProps={this.props.dataViewPickerComponentProps} + textBasedLanguageModeErrors={this.props.textBasedLanguageModeErrors} showDatePickerAsBadge={this.shouldShowDatePickerAsBadge()} filterBar={filterBar} suggestionsSize={this.props.suggestionsSize} From 6be485cc8f636b24a8abfda26c57e750ab7ffcb9 Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Mon, 27 Jun 2022 13:00:45 +0300 Subject: [PATCH 028/115] Add error mrkers in unified search editor --- .../application/main/utils/fetch_all.test.ts | 31 +++++--- .../application/main/utils/fetch_all.ts | 2 - .../editor_footer.tsx | 12 ++- .../text_based_languages_editor/helpers.ts | 76 +++++++++++++++++++ .../text_based_languages_editor/index.tsx | 19 ++++- 5 files changed, 121 insertions(+), 19 deletions(-) diff --git a/src/plugins/discover/public/application/main/utils/fetch_all.test.ts b/src/plugins/discover/public/application/main/utils/fetch_all.test.ts index 813b25e5be692..ba46fbec1df3f 100644 --- a/src/plugins/discover/public/application/main/utils/fetch_all.test.ts +++ b/src/plugins/discover/public/application/main/utils/fetch_all.test.ts @@ -121,7 +121,7 @@ describe('test fetchAll', () => { await fetchAll(subjects, searchSource, false, deps); expect(await collect()).toEqual([ { fetchStatus: FetchStatus.UNINITIALIZED }, - { fetchStatus: FetchStatus.LOADING }, + { fetchStatus: FetchStatus.LOADING, textBasedLanguageMode: '' }, { fetchStatus: FetchStatus.COMPLETE, result: documents, @@ -143,7 +143,7 @@ describe('test fetchAll', () => { await fetchAll(subjects, searchSource, false, deps); expect(await collect()).toEqual([ { fetchStatus: FetchStatus.UNINITIALIZED }, - { fetchStatus: FetchStatus.LOADING }, + { fetchStatus: FetchStatus.LOADING, textBasedLanguageMode: '' }, { fetchStatus: FetchStatus.PARTIAL, result: 2 }, { fetchStatus: FetchStatus.COMPLETE, result: 42 }, ]); @@ -155,7 +155,7 @@ describe('test fetchAll', () => { await fetchAll(subjects, searchSource, false, deps); expect(await collect()).toEqual([ { fetchStatus: FetchStatus.UNINITIALIZED }, - { fetchStatus: FetchStatus.LOADING }, + { fetchStatus: FetchStatus.LOADING, textBasedLanguageMode: '' }, { fetchStatus: FetchStatus.COMPLETE, bucketInterval: {}, chartData: {} }, ]); }); @@ -168,7 +168,7 @@ describe('test fetchAll', () => { await fetchAll(subjects, searchSource, false, deps); expect(await collect()).toEqual([ { fetchStatus: FetchStatus.UNINITIALIZED }, - { fetchStatus: FetchStatus.LOADING }, + { fetchStatus: FetchStatus.LOADING, textBasedLanguageMode: '' }, { fetchStatus: FetchStatus.PARTIAL, result: 0 }, // From documents query { fetchStatus: FetchStatus.COMPLETE, result: 32 }, ]); @@ -186,15 +186,20 @@ describe('test fetchAll', () => { await fetchAll(subjects, searchSource, false, deps); expect(await collectTotalHits()).toEqual([ { fetchStatus: FetchStatus.UNINITIALIZED }, - { fetchStatus: FetchStatus.LOADING }, + { fetchStatus: FetchStatus.LOADING, textBasedLanguageMode: '' }, { fetchStatus: FetchStatus.PARTIAL, result: 1 }, { fetchStatus: FetchStatus.ERROR, error: { msg: 'Oh noes!' } }, ]); expect(await collectMain()).toEqual([ { fetchStatus: FetchStatus.UNINITIALIZED }, - { fetchStatus: FetchStatus.LOADING }, - { fetchStatus: FetchStatus.PARTIAL }, - { fetchStatus: FetchStatus.COMPLETE, foundDocuments: true }, + { fetchStatus: FetchStatus.LOADING, textBasedLanguageMode: '' }, + { fetchStatus: FetchStatus.PARTIAL, textBasedLanguageMode: '' }, + { + fetchStatus: FetchStatus.COMPLETE, + foundDocuments: true, + error: undefined, + textBasedLanguageMode: '', + }, ]); }); @@ -205,9 +210,13 @@ describe('test fetchAll', () => { await fetchAll(subjects, searchSource, false, deps); expect(await collectMain()).toEqual([ { fetchStatus: FetchStatus.UNINITIALIZED }, - { fetchStatus: FetchStatus.LOADING }, - { fetchStatus: FetchStatus.PARTIAL }, // From totalHits query - { fetchStatus: FetchStatus.ERROR, error: { msg: 'This query failed' } }, + { fetchStatus: FetchStatus.LOADING, textBasedLanguageMode: '' }, + { fetchStatus: FetchStatus.PARTIAL, textBasedLanguageMode: '' }, // From totalHits query + { + fetchStatus: FetchStatus.ERROR, + error: { msg: 'This query failed' }, + textBasedLanguageMode: '', + }, // Here should be no COMPLETE coming anymore ]); }); diff --git a/src/plugins/discover/public/application/main/utils/fetch_all.ts b/src/plugins/discover/public/application/main/utils/fetch_all.ts index 3e0bfda6ff79b..dfd5271b3b64b 100644 --- a/src/plugins/discover/public/application/main/utils/fetch_all.ts +++ b/src/plugins/discover/public/application/main/utils/fetch_all.ts @@ -101,8 +101,6 @@ export function fetchAll( if (query && isOfAggregateQueryType(query)) { const aggregatedQuery = query as AggregateQuery; textBasedLanguageMode = getAggregateQueryMode(aggregatedQuery); - } else { - textBasedLanguageMode = ''; } // Update the base searchSource, base for all child fetches diff --git a/src/plugins/unified_search/public/query_string_input/text_based_languages_editor/editor_footer.tsx b/src/plugins/unified_search/public/query_string_input/text_based_languages_editor/editor_footer.tsx index e007b11c6f74c..6f8bb66ce7da6 100644 --- a/src/plugins/unified_search/public/query_string_input/text_based_languages_editor/editor_footer.tsx +++ b/src/plugins/unified_search/public/query_string_input/text_based_languages_editor/editor_footer.tsx @@ -28,7 +28,7 @@ const COMMAND_KEY = isMac ? '⌘' : '^'; interface EditorFooterProps { lines: number; containerCSS: Interpolation; - errors?: Error[]; + errors?: Array<{ startLineNumber: number; message: string }>; } export const EditorFooter = memo(function EditorFooter({ @@ -91,7 +91,15 @@ export const EditorFooter = memo(function EditorFooter({ {errors.map((error, index) => { return ( - {String(error)} + + + + + + {`Line ${error.startLineNumber}`} + + {error.message} + ); })} diff --git a/src/plugins/unified_search/public/query_string_input/text_based_languages_editor/helpers.ts b/src/plugins/unified_search/public/query_string_input/text_based_languages_editor/helpers.ts index 27a1631ce11ec..85bbcd59f1ae9 100644 --- a/src/plugins/unified_search/public/query_string_input/text_based_languages_editor/helpers.ts +++ b/src/plugins/unified_search/public/query_string_input/text_based_languages_editor/helpers.ts @@ -8,6 +8,7 @@ import { useRef } from 'react'; import useDebounce from 'react-use/lib/useDebounce'; +import { monaco } from '@kbn/monaco'; export const useDebounceWithOptions = ( fn: Function, @@ -30,3 +31,78 @@ export const useDebounceWithOptions = ( newDeps ); }; + +function getIndexPatternFromSQLQuery(sqlQuery?: string): string { + const match = sqlQuery?.match(/FROM\s+(\w+)/); + if (match) { + return match[1]; + } + return ''; +} + +export const parseErrors = (errors: Error[], code: string) => { + return errors.map((error) => { + if (error.message.includes('line')) { + const text = error.message.split('line')[1]; + const [lineNumber, startPosition, errorMessage] = text.split(':'); + // initialize the length to 10 in case no error word found + let errorLength = 10; + const [_, wordWithError] = errorMessage.split('['); + if (wordWithError) { + errorLength = wordWithError.length - 1; + } + return { + message: errorMessage, + startColumn: Number(startPosition), + startLineNumber: Number(lineNumber), + endColumn: Number(startPosition) + errorLength, + endLineNumber: Number(lineNumber), + severity: monaco.MarkerSeverity.Error, + }; + } else if (error.message.includes('No data view found')) { + const dataviewString = getIndexPatternFromSQLQuery(code); + // 5 is the length of FROM + space + const errorLength = 5 + dataviewString.length; + // no dataview found error message + const hasLines = /\r|\n/.exec(code); + if (hasLines) { + const linesText = code.split(/\r|\n/); + let indexWithError = 1; + let lineWithError = ''; + linesText.forEach((line, index) => { + if (line.includes('FROM')) { + indexWithError = index + 1; + lineWithError = line; + } + }); + return { + message: error.message, + startColumn: lineWithError.indexOf('FROM') + 1, + startLineNumber: indexWithError, + endColumn: lineWithError.indexOf('FROM') + 1 + errorLength, + endLineNumber: indexWithError, + severity: monaco.MarkerSeverity.Error, + }; + } else { + return { + message: error.message, + startColumn: code.indexOf('FROM') + 1, + startLineNumber: 1, + endColumn: code.indexOf('FROM') + 1 + errorLength, + endLineNumber: 1, + severity: monaco.MarkerSeverity.Error, + }; + } + } else { + // unknown error message + return { + message: error.message, + startColumn: 1, + startLineNumber: 1, + endColumn: 10, + endLineNumber: 1, + severity: monaco.MarkerSeverity.Error, + }; + } + }); +}; diff --git a/src/plugins/unified_search/public/query_string_input/text_based_languages_editor/index.tsx b/src/plugins/unified_search/public/query_string_input/text_based_languages_editor/index.tsx index 9da91d876b796..b25205e629565 100644 --- a/src/plugins/unified_search/public/query_string_input/text_based_languages_editor/index.tsx +++ b/src/plugins/unified_search/public/query_string_input/text_based_languages_editor/index.tsx @@ -31,7 +31,7 @@ import { EDITOR_MIN_HEIGHT, } from './text_based_languages_editor.styles'; import { MemoizedDocumentation } from './documentation'; -import { useDebounceWithOptions } from './helpers'; +import { useDebounceWithOptions, parseErrors } from './helpers'; import { EditorFooter } from './editor_footer'; interface TextBasedLanguagesEditorProps { @@ -86,6 +86,9 @@ export const TextBasedLanguagesEditor = memo(function TextBasedLanguagesEditor({ const [isWordWrapped, setIsWordWrapped] = useState(true); const [userDrags, setUserDrags] = useState(false); const [isHelpOpen, setIsHelpOpen] = useState(false); + const [editorErrors, setEditorErrors] = useState< + Array<{ startLineNumber: number; message: string }> + >([]); const styles = textBasedLanguagedEditorStyles( euiTheme, @@ -172,10 +175,18 @@ export const TextBasedLanguagesEditor = memo(function TextBasedLanguagesEditor({ if (!isCodeEditorExpanded) { editor1.current?.onDidContentSizeChange(updateHeight); } + if (errors && errors.length) { + const parsedErrors = parseErrors(errors, code); + setEditorErrors(parsedErrors); + monaco.editor.setModelMarkers(editorModel.current, 'Unified search', parsedErrors); + } else { + monaco.editor.setModelMarkers(editorModel.current, 'Unified search', []); + setEditorErrors([]); + } }, { skipFirstRender: false }, 256, - [] + [errors] ); // Clean up the monaco editor and DOM on unmount @@ -383,7 +394,7 @@ export const TextBasedLanguagesEditor = memo(function TextBasedLanguagesEditor({ )}

@@ -431,7 +442,7 @@ export const TextBasedLanguagesEditor = memo(function TextBasedLanguagesEditor({ )}
{isCodeEditorExpanded && ( - + )} {isCodeEditorExpanded && (
From 5f0d0ed83df8eb032d2213933679046061f7bc36 Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Mon, 27 Jun 2022 16:07:03 +0300 Subject: [PATCH 029/115] Save and open saved searches --- .../search_source/create_search_source.ts | 1 - .../search_source/migrate_legacy_query.ts | 9 +++-- .../components/layout/discover_layout.tsx | 40 +++++++++++++------ .../application/main/utils/fetch_all.ts | 1 + .../main/utils/get_result_state.ts | 7 +++- .../dataview_picker/change_dataview.tsx | 2 +- .../editor_footer.tsx | 29 +++++++++++--- .../text_based_languages_editor/index.tsx | 5 ++- 8 files changed, 69 insertions(+), 25 deletions(-) diff --git a/src/plugins/data/common/search/search_source/create_search_source.ts b/src/plugins/data/common/search/search_source/create_search_source.ts index c6093da07b8c2..acf02d1dcf1a7 100644 --- a/src/plugins/data/common/search/search_source/create_search_source.ts +++ b/src/plugins/data/common/search/search_source/create_search_source.ts @@ -5,7 +5,6 @@ * in compliance with, at your election, the Elastic License 2.0 or the Server * Side Public License, v 1. */ - import { DataViewsContract } from '@kbn/data-views-plugin/common'; import { migrateLegacyQuery } from './migrate_legacy_query'; import { SearchSource, SearchSourceDependencies } from './search_source'; diff --git a/src/plugins/data/common/search/search_source/migrate_legacy_query.ts b/src/plugins/data/common/search/search_source/migrate_legacy_query.ts index 70961d705f7b8..6f7ef2d5758f0 100644 --- a/src/plugins/data/common/search/search_source/migrate_legacy_query.ts +++ b/src/plugins/data/common/search/search_source/migrate_legacy_query.ts @@ -5,9 +5,12 @@ * in compliance with, at your election, the Elastic License 2.0 or the Server * Side Public License, v 1. */ - +import { Query } from '@kbn/es-query'; import { has } from 'lodash'; -import { Query } from '../../query/types'; + +function isOfQueryType(arg: any): arg is Query { + return Boolean(arg && 'query' in arg); +} /** * Creates a standardized query object from old queries that were either strings or pure ES query DSL @@ -18,7 +21,7 @@ import { Query } from '../../query/types'; export function migrateLegacyQuery(query: Query | { [key: string]: any } | string): Query { // Lucene was the only option before, so language-less queries are all lucene - if (!has(query, 'language')) { + if (!has(query, 'language') && isOfQueryType(query)) { return { query, language: 'lucene' }; } diff --git a/src/plugins/discover/public/application/main/components/layout/discover_layout.tsx b/src/plugins/discover/public/application/main/components/layout/discover_layout.tsx index 5bc3389e953cf..c66c589c1f9dc 100644 --- a/src/plugins/discover/public/application/main/components/layout/discover_layout.tsx +++ b/src/plugins/discover/public/application/main/components/layout/discover_layout.tsx @@ -20,6 +20,7 @@ import { } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { METRIC_TYPE } from '@kbn/analytics'; +import { AggregateQuery, Query } from '@kbn/es-query'; import classNames from 'classnames'; import { generateFilters } from '@kbn/data-plugin/public'; import { DataView, DataViewField, DataViewType } from '@kbn/data-views-plugin/public'; @@ -60,6 +61,14 @@ const TopNavMemoized = React.memo(DiscoverTopNav); const DiscoverChartMemoized = React.memo(DiscoverChart); const FieldStatisticsTableMemoized = React.memo(FieldStatisticsTable); +function isOfAggregateQueryType(query: AggregateQuery | Query): query is AggregateQuery { + return Boolean(query && 'sql' in query); +} + +function getAggregateQueryMode(query: AggregateQuery): string { + return Object.keys(query)[0]; +} + export function DiscoverLayout({ indexPattern, indexPatternList, @@ -136,8 +145,13 @@ export function DiscoverLayout({ const useNewFieldsApi = useMemo(() => !uiSettings.get(SEARCH_FIELDS_FROM_SOURCE), [uiSettings]); const resultState = useMemo( - () => getResultState(dataState.fetchStatus, dataState.foundDocuments!), - [dataState.fetchStatus, dataState.foundDocuments] + () => + getResultState( + dataState.fetchStatus, + dataState.foundDocuments!, + dataState.textBasedLanguageMode + ), + [dataState.fetchStatus, dataState.foundDocuments, dataState.textBasedLanguageMode] ); const onOpenInspector = useCallback(() => { @@ -168,6 +182,12 @@ export function DiscoverLayout({ useNewFieldsApi, }); + let textBasedLanguageMode = ''; + if (state.query && isOfAggregateQueryType(state.query)) { + const aggregatedQuery = state.query as AggregateQuery; + textBasedLanguageMode = getAggregateQueryMode(aggregatedQuery); + } + const onAddFilter = useCallback( (field: DataViewField | string, values: string, operation: '+' | '-') => { const fieldName = typeof field === 'string' ? field : field.name; @@ -251,7 +271,7 @@ export function DiscoverLayout({ resetSavedSearch={resetSavedSearch} onChangeIndexPattern={onChangeIndexPattern} onEditRuntimeField={onEditRuntimeField} - textBasedLanguageMode={dataState.textBasedLanguageMode} + textBasedLanguageMode={textBasedLanguageMode} textBasedLanguageModeErrors={textBasedLanguageModeErrors} /> @@ -267,9 +287,7 @@ export function DiscoverLayout({ documents$={savedSearchData$.documents$} indexPatternList={indexPatternList} onAddField={onAddColumn} - onAddFilter={ - !dataState.textBasedLanguageMode ? (onAddFilter as DocViewFilterFn) : undefined - } + onAddFilter={!textBasedLanguageMode ? (onAddFilter as DocViewFilterFn) : undefined} onRemoveField={onRemoveColumn} onChangeIndexPattern={onChangeIndexPattern} selectedIndexPattern={indexPattern} @@ -335,7 +353,7 @@ export function DiscoverLayout({ gutterSize="none" responsive={false} > - {!dataState.textBasedLanguageMode && ( + {!textBasedLanguageMode && ( <> ); diff --git a/src/plugins/unified_search/public/query_string_input/text_based_languages_editor/editor_footer.tsx b/src/plugins/unified_search/public/query_string_input/text_based_languages_editor/editor_footer.tsx index 6f8bb66ce7da6..a6072b407d406 100644 --- a/src/plugins/unified_search/public/query_string_input/text_based_languages_editor/editor_footer.tsx +++ b/src/plugins/unified_search/public/query_string_input/text_based_languages_editor/editor_footer.tsx @@ -48,7 +48,12 @@ export const EditorFooter = memo(function EditorFooter({ -

{`${lines} lines`}

+

+ {i18n.translate('unifiedSearch.query.textBasedLanguagesEditor.lineCount', { + defaultMessage: '{count} {count, plural, one {line} other {lines}}', + values: { count: lines }, + })} +

{errors && errors.length > 0 && ( @@ -71,7 +76,15 @@ export const EditorFooter = memo(function EditorFooter({ `} onClick={() => setIsPopoverOpen(true)} > -

{`${errors.length} errors`}

+

+ {i18n.translate( + 'unifiedSearch.query.textBasedLanguagesEditor.errorCount', + { + defaultMessage: '{count} {count, plural, one {error} other {errors}}', + values: { count: errors.length }, + } + )} +

} ownFocus={false} @@ -91,12 +104,18 @@ export const EditorFooter = memo(function EditorFooter({ {errors.map((error, index) => { return ( - + - - {`Line ${error.startLineNumber}`} + + {i18n.translate( + 'unifiedSearch.query.textBasedLanguagesEditor.lineNumber', + { + defaultMessage: 'Line{lineNumber}', + values: { lineNumber: error.startLineNumber }, + } + )} {error.message} diff --git a/src/plugins/unified_search/public/query_string_input/text_based_languages_editor/index.tsx b/src/plugins/unified_search/public/query_string_input/text_based_languages_editor/index.tsx index b25205e629565..fa88dd27bcfa3 100644 --- a/src/plugins/unified_search/public/query_string_input/text_based_languages_editor/index.tsx +++ b/src/plugins/unified_search/public/query_string_input/text_based_languages_editor/index.tsx @@ -360,7 +360,10 @@ export const TextBasedLanguagesEditor = memo(function TextBasedLanguagesEditor({
{!isCompactFocused && ( - {`${lines} lines`} + {i18n.translate('unifiedSearch.query.textBasedLanguagesEditor.lineCount', { + defaultMessage: '{count} {count, plural, one {line} other {lines}}', + values: { count: lines }, + })} )} {!isCompactFocused && errors && errors.length > 0 && ( From e3ad0b3afad0e48ca7057ffd2fe1b485e129f0f2 Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Mon, 27 Jun 2022 16:53:51 +0300 Subject: [PATCH 030/115] Filter out saved searches from text based languages --- .../search/search_source/create_search_source.ts | 1 + .../application/main/utils/persist_saved_search.ts | 10 +++++++++- .../services/saved_searches/saved_searches_utils.ts | 2 ++ .../discover/public/services/saved_searches/types.ts | 2 ++ src/plugins/discover/server/saved_objects/search.ts | 1 + .../wizard/search_selection/search_selection.tsx | 4 ++++ 6 files changed, 19 insertions(+), 1 deletion(-) diff --git a/src/plugins/data/common/search/search_source/create_search_source.ts b/src/plugins/data/common/search/search_source/create_search_source.ts index acf02d1dcf1a7..c6093da07b8c2 100644 --- a/src/plugins/data/common/search/search_source/create_search_source.ts +++ b/src/plugins/data/common/search/search_source/create_search_source.ts @@ -5,6 +5,7 @@ * in compliance with, at your election, the Elastic License 2.0 or the Server * Side Public License, v 1. */ + import { DataViewsContract } from '@kbn/data-views-plugin/common'; import { migrateLegacyQuery } from './migrate_legacy_query'; import { SearchSource, SearchSourceDependencies } from './search_source'; diff --git a/src/plugins/discover/public/application/main/utils/persist_saved_search.ts b/src/plugins/discover/public/application/main/utils/persist_saved_search.ts index 30430abd941a8..69a4929f08d6a 100644 --- a/src/plugins/discover/public/application/main/utils/persist_saved_search.ts +++ b/src/plugins/discover/public/application/main/utils/persist_saved_search.ts @@ -5,7 +5,7 @@ * in compliance with, at your election, the Elastic License 2.0 or the Server * Side Public License, v 1. */ - +import { AggregateQuery, Query } from '@kbn/es-query'; import { DataView } from '@kbn/data-views-plugin/public'; import { SavedObjectSaveOpts } from '@kbn/saved-objects-plugin/public'; import { updateSearchSource } from './update_search_source'; @@ -15,6 +15,10 @@ import type { SortOrder } from '../../../services/saved_searches'; import { DiscoverServices } from '../../../build_services'; import { saveSavedSearch } from '../../../services/saved_searches'; +function isOfAggregateQueryType(query: AggregateQuery | Query): query is AggregateQuery { + return Boolean(query && 'sql' in query); +} + /** * Helper function to update and persist the given savedSearch */ @@ -63,6 +67,10 @@ export async function persistSavedSearch( savedSearch.hideAggregatedPreview = state.hideAggregatedPreview; } + if (state.query && isOfAggregateQueryType(state.query)) { + savedSearch.isTextBasedQuery = true; + } + try { const id = await saveSavedSearch(savedSearch, saveOptions, services.core.savedObjects.client); if (id) { diff --git a/src/plugins/discover/public/services/saved_searches/saved_searches_utils.ts b/src/plugins/discover/public/services/saved_searches/saved_searches_utils.ts index 26b3c0b7cf9b5..a2fbc1f575e4b 100644 --- a/src/plugins/discover/public/services/saved_searches/saved_searches_utils.ts +++ b/src/plugins/discover/public/services/saved_searches/saved_searches_utils.ts @@ -45,6 +45,7 @@ export const fromSavedSearchAttributes = ( viewMode: attributes.viewMode, hideAggregatedPreview: attributes.hideAggregatedPreview, rowHeight: attributes.rowHeight, + isTextBasedQuery: attributes.isTextBasedQuery, }); export const toSavedSearchAttributes = ( @@ -61,4 +62,5 @@ export const toSavedSearchAttributes = ( viewMode: savedSearch.viewMode, hideAggregatedPreview: savedSearch.hideAggregatedPreview, rowHeight: savedSearch.rowHeight, + isTextBasedQuery: savedSearch.isTextBasedQuery ?? false, }); diff --git a/src/plugins/discover/public/services/saved_searches/types.ts b/src/plugins/discover/public/services/saved_searches/types.ts index aba95f85afd11..800c48993f39f 100644 --- a/src/plugins/discover/public/services/saved_searches/types.ts +++ b/src/plugins/discover/public/services/saved_searches/types.ts @@ -21,6 +21,7 @@ export interface SavedSearchAttributes { columns?: Record; }; hideChart: boolean; + isTextBasedQuery: boolean; kibanaSavedObjectMeta: { searchSourceJSON: string; }; @@ -53,4 +54,5 @@ export interface SavedSearch { viewMode?: VIEW_MODE; hideAggregatedPreview?: boolean; rowHeight?: number; + isTextBasedQuery?: boolean; } diff --git a/src/plugins/discover/server/saved_objects/search.ts b/src/plugins/discover/server/saved_objects/search.ts index f30fa932aa133..833e6fd0961ef 100644 --- a/src/plugins/discover/server/saved_objects/search.ts +++ b/src/plugins/discover/server/saved_objects/search.ts @@ -38,6 +38,7 @@ export function getSavedSearchObjectType( description: { type: 'text' }, viewMode: { type: 'keyword', index: false, doc_values: false }, hideChart: { type: 'boolean', index: false, doc_values: false }, + isTextBasedQuery: { type: 'boolean', index: false, doc_values: false }, hideAggregatedPreview: { type: 'boolean', index: false, doc_values: false }, hits: { type: 'integer', index: false, doc_values: false }, kibanaSavedObjectMeta: { diff --git a/src/plugins/visualizations/public/wizard/search_selection/search_selection.tsx b/src/plugins/visualizations/public/wizard/search_selection/search_selection.tsx index e6179593de8d6..0125dc2500217 100644 --- a/src/plugins/visualizations/public/wizard/search_selection/search_selection.tsx +++ b/src/plugins/visualizations/public/wizard/search_selection/search_selection.tsx @@ -66,6 +66,10 @@ export class SearchSelection extends React.Component { defaultMessage: 'Saved search', } ), + includeFields: ['isTextBasedQuery'], + showSavedObject: (savedObject: any) => { + return !savedObject.attributes.isTextBasedQuery; + }, }, { type: 'index-pattern', From 22119af0932c91648589b50480a73318f186dc3a Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Mon, 27 Jun 2022 17:42:27 +0300 Subject: [PATCH 031/115] Some fixes --- src/plugins/data/common/query/to_expression_ast.ts | 10 ++++++---- .../public/application/main/utils/fetch_all.ts | 1 - .../saved_searches/saved_searches_utils.test.ts | 1 + .../text_based_languages_editor/helpers.ts | 10 ++++++---- 4 files changed, 13 insertions(+), 9 deletions(-) diff --git a/src/plugins/data/common/query/to_expression_ast.ts b/src/plugins/data/common/query/to_expression_ast.ts index 2bee3f40d1f28..0dc314bf6b2ec 100644 --- a/src/plugins/data/common/query/to_expression_ast.ts +++ b/src/plugins/data/common/query/to_expression_ast.ts @@ -27,11 +27,13 @@ function getAggregateQueryMode(query: AggregateQuery): string { } function getIndexPatternFromSQLQuery(sqlQuery?: string): string { - const match = sqlQuery?.match(/FROM\s+(\w+)/); - if (match) { - return match[1]; + const [_, fromText] = sqlQuery?.split('FROM') ?? []; + const dataViewString = fromText.replaceAll('"', ''); + const [indexPattern] = dataViewString?.split(' ') ?? []; + if (indexPattern) { + return indexPattern; } - return ''; + return dataViewString; } interface Args extends QueryState { diff --git a/src/plugins/discover/public/application/main/utils/fetch_all.ts b/src/plugins/discover/public/application/main/utils/fetch_all.ts index c038354779e6c..dfd5271b3b64b 100644 --- a/src/plugins/discover/public/application/main/utils/fetch_all.ts +++ b/src/plugins/discover/public/application/main/utils/fetch_all.ts @@ -168,7 +168,6 @@ export function fetchAll( dataSubjects.documents$.next({ fetchStatus: FetchStatus.COMPLETE, result: docs, - textBasedLanguageMode, }); checkHitCount(docs.length); diff --git a/src/plugins/discover/public/services/saved_searches/saved_searches_utils.test.ts b/src/plugins/discover/public/services/saved_searches/saved_searches_utils.test.ts index f0958737d3b79..375d1f2f35a8d 100644 --- a/src/plugins/discover/public/services/saved_searches/saved_searches_utils.test.ts +++ b/src/plugins/discover/public/services/saved_searches/saved_searches_utils.test.ts @@ -27,6 +27,7 @@ describe('saved_searches_utils', () => { description: 'foo', grid: {}, hideChart: true, + isTextBasedQuery: false, }; expect(fromSavedSearchAttributes('id', attributes, createSearchSourceMock(), {})) diff --git a/src/plugins/unified_search/public/query_string_input/text_based_languages_editor/helpers.ts b/src/plugins/unified_search/public/query_string_input/text_based_languages_editor/helpers.ts index 85bbcd59f1ae9..c888e743e1193 100644 --- a/src/plugins/unified_search/public/query_string_input/text_based_languages_editor/helpers.ts +++ b/src/plugins/unified_search/public/query_string_input/text_based_languages_editor/helpers.ts @@ -33,11 +33,13 @@ export const useDebounceWithOptions = ( }; function getIndexPatternFromSQLQuery(sqlQuery?: string): string { - const match = sqlQuery?.match(/FROM\s+(\w+)/); - if (match) { - return match[1]; + const [_, fromText] = sqlQuery?.split('FROM') ?? []; + const dataViewString = fromText.replaceAll('"', ''); + const [indexPattern] = dataViewString?.split(' ') ?? []; + if (indexPattern) { + return indexPattern; } - return ''; + return dataViewString; } export const parseErrors = (errors: Error[], code: string) => { From d538306c7e73962d9c3ba83a44ecda5c1007bf6b Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Tue, 28 Jun 2022 09:16:20 +0300 Subject: [PATCH 032/115] Fix unit tests --- .../search/search_source/migrate_legacy_query.ts | 15 ++++++++++----- .../saved_searches/saved_searches_utils.test.ts | 2 ++ 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/src/plugins/data/common/search/search_source/migrate_legacy_query.ts b/src/plugins/data/common/search/search_source/migrate_legacy_query.ts index 6f7ef2d5758f0..ff66e83851d64 100644 --- a/src/plugins/data/common/search/search_source/migrate_legacy_query.ts +++ b/src/plugins/data/common/search/search_source/migrate_legacy_query.ts @@ -5,11 +5,11 @@ * in compliance with, at your election, the Elastic License 2.0 or the Server * Side Public License, v 1. */ -import { Query } from '@kbn/es-query'; +import { AggregateQuery, Query } from '@kbn/es-query'; import { has } from 'lodash'; -function isOfQueryType(arg: any): arg is Query { - return Boolean(arg && 'query' in arg); +function isOfAggregateQueryType(query: AggregateQuery | Query): query is AggregateQuery { + return Boolean(query && 'sql' in query); } /** @@ -19,9 +19,14 @@ function isOfQueryType(arg: any): arg is Query { * @return Object */ -export function migrateLegacyQuery(query: Query | { [key: string]: any } | string): Query { +export function migrateLegacyQuery( + query: Query | { [key: string]: any } | string +): Query | AggregateQuery { // Lucene was the only option before, so language-less queries are all lucene - if (!has(query, 'language') && isOfQueryType(query)) { + if (!has(query, 'language')) { + if (typeof query === 'object' && isOfAggregateQueryType(query)) { + return query; + } return { query, language: 'lucene' }; } diff --git a/src/plugins/discover/public/services/saved_searches/saved_searches_utils.test.ts b/src/plugins/discover/public/services/saved_searches/saved_searches_utils.test.ts index 375d1f2f35a8d..d63364ae6c3d2 100644 --- a/src/plugins/discover/public/services/saved_searches/saved_searches_utils.test.ts +++ b/src/plugins/discover/public/services/saved_searches/saved_searches_utils.test.ts @@ -42,6 +42,7 @@ describe('saved_searches_utils', () => { "hideAggregatedPreview": undefined, "hideChart": true, "id": "id", + "isTextBasedQuery": false, "rowHeight": undefined, "searchSource": SearchSource { "dependencies": Object { @@ -116,6 +117,7 @@ describe('saved_searches_utils', () => { "grid": Object {}, "hideAggregatedPreview": undefined, "hideChart": true, + "isTextBasedQuery": false, "kibanaSavedObjectMeta": Object { "searchSourceJSON": "{}", }, From c920a647894ecf0f357af3f426e2e260df051da4 Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Tue, 28 Jun 2022 09:36:40 +0300 Subject: [PATCH 033/115] Fix checks --- .../search/search_source/migrate_legacy_query.ts | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/plugins/data/common/search/search_source/migrate_legacy_query.ts b/src/plugins/data/common/search/search_source/migrate_legacy_query.ts index ff66e83851d64..919e0ab3d0d4a 100644 --- a/src/plugins/data/common/search/search_source/migrate_legacy_query.ts +++ b/src/plugins/data/common/search/search_source/migrate_legacy_query.ts @@ -5,11 +5,11 @@ * in compliance with, at your election, the Elastic License 2.0 or the Server * Side Public License, v 1. */ -import { AggregateQuery, Query } from '@kbn/es-query'; +import { Query } from '@kbn/es-query'; import { has } from 'lodash'; -function isOfAggregateQueryType(query: AggregateQuery | Query): query is AggregateQuery { - return Boolean(query && 'sql' in query); +function isOfQueryType(arg: any): arg is Query { + return Boolean(arg && 'query' in arg); } /** @@ -19,13 +19,11 @@ function isOfAggregateQueryType(query: AggregateQuery | Query): query is Aggrega * @return Object */ -export function migrateLegacyQuery( - query: Query | { [key: string]: any } | string -): Query | AggregateQuery { +export function migrateLegacyQuery(query: Query | { [key: string]: any } | string): Query { // Lucene was the only option before, so language-less queries are all lucene if (!has(query, 'language')) { - if (typeof query === 'object' && isOfAggregateQueryType(query)) { - return query; + if (typeof query === 'object' && !isOfQueryType(query)) { + return query as Query; } return { query, language: 'lucene' }; } From 9cff6ef4c193287d0afbf6c3c72548cff3f20221 Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Tue, 28 Jun 2022 10:50:32 +0300 Subject: [PATCH 034/115] On save and exit modal implementation --- .../components/top_nav/discover_topnav.tsx | 17 +++++++++ .../components/top_nav/on_save_search.tsx | 35 ++++++++++++------- .../saved_searches/get_saved_searches.test.ts | 1 + .../dataview_picker/change_dataview.tsx | 26 ++++++++++---- .../public/dataview_picker/index.tsx | 6 +++- .../query_string_input/query_bar_top_row.tsx | 8 ++++- .../public/search_bar/create_search_bar.tsx | 1 + .../public/search_bar/search_bar.tsx | 4 ++- 8 files changed, 75 insertions(+), 23 deletions(-) diff --git a/src/plugins/discover/public/application/main/components/top_nav/discover_topnav.tsx b/src/plugins/discover/public/application/main/components/top_nav/discover_topnav.tsx index 38e12a21e742e..573e5aaa19679 100644 --- a/src/plugins/discover/public/application/main/components/top_nav/discover_topnav.tsx +++ b/src/plugins/discover/public/application/main/components/top_nav/discover_topnav.tsx @@ -15,6 +15,7 @@ import { DiscoverLayoutProps } from '../layout/types'; import { getTopNavLinks } from './get_top_nav_links'; import { getHeaderActionMenuMounter } from '../../../../kibana_services'; import { GetStateReturn } from '../../services/discover_state'; +import { onSaveSearch } from './on_save_search'; export type DiscoverTopNavProps = Pick< DiscoverLayoutProps, @@ -184,6 +185,21 @@ export const DiscoverTopNav = ({ textBasedLanguages: ['SQL'] as DataViewPickerProps['textBasedLanguages'], }; + const onTextBasedSavedAndExit = useCallback( + async ({ onSave }) => { + await onSaveSearch({ + savedSearch, + services, + indexPattern, + navigateTo, + state: stateContainer, + onClose: onSave, + onSaveCb: onSave, + }); + }, + [indexPattern, navigateTo, savedSearch, services, stateContainer] + ); + return ( ); }; diff --git a/src/plugins/discover/public/application/main/components/top_nav/on_save_search.tsx b/src/plugins/discover/public/application/main/components/top_nav/on_save_search.tsx index 6263df7814e2b..f8a16a166aa0c 100644 --- a/src/plugins/discover/public/application/main/components/top_nav/on_save_search.tsx +++ b/src/plugins/discover/public/application/main/components/top_nav/on_save_search.tsx @@ -23,6 +23,7 @@ async function saveDataSource({ saveOptions, services, state, + navigateOrReloadSavedSearch, }: { indexPattern: DataView; navigateTo: (url: string) => void; @@ -30,6 +31,7 @@ async function saveDataSource({ saveOptions: SaveSavedSearchOptions; services: DiscoverServices; state: GetStateReturn; + navigateOrReloadSavedSearch: boolean; }) { const prevSavedSearchId = savedSearch.id; function onSuccess(id: string) { @@ -43,20 +45,22 @@ async function saveDataSource({ }), 'data-test-subj': 'saveSearchSuccess', }); - if (id !== prevSavedSearchId) { - navigateTo(`/view/${encodeURIComponent(id)}`); - } else { - // Update defaults so that "reload saved query" functions correctly - state.resetAppState(); - services.chrome.docTitle.change(savedSearch.title!); + if (navigateOrReloadSavedSearch) { + if (id !== prevSavedSearchId) { + navigateTo(`/view/${encodeURIComponent(id)}`); + } else { + // Update defaults so that "reload saved query" functions correctly + state.resetAppState(); + services.chrome.docTitle.change(savedSearch.title!); - setBreadcrumbsTitle( - { - ...savedSearch, - id: prevSavedSearchId ?? id, - }, - services.chrome - ); + setBreadcrumbsTitle( + { + ...savedSearch, + id: prevSavedSearchId ?? id, + }, + services.chrome + ); + } } } } @@ -89,6 +93,7 @@ export async function onSaveSearch({ services, state, onClose, + onSaveCb, }: { indexPattern: DataView; navigateTo: (path: string) => void; @@ -96,6 +101,7 @@ export async function onSaveSearch({ services: DiscoverServices; state: GetStateReturn; onClose?: () => void; + onSaveCb?: () => void; }) { const onSave = async ({ newTitle, @@ -118,6 +124,7 @@ export async function onSaveSearch({ copyOnSave: newCopyOnSave, isTitleDuplicateConfirmed, }; + const navigateOrReloadSavedSearch = !Boolean(onSaveCb); const response = await saveDataSource({ indexPattern, saveOptions, @@ -125,6 +132,7 @@ export async function onSaveSearch({ navigateTo, savedSearch, state, + navigateOrReloadSavedSearch, }); // If the save wasn't successful, put the original values back. if (!response.id || response.error) { @@ -132,6 +140,7 @@ export async function onSaveSearch({ } else { state.resetInitialAppState(); } + onSaveCb?.(); return response; }; diff --git a/src/plugins/discover/public/services/saved_searches/get_saved_searches.test.ts b/src/plugins/discover/public/services/saved_searches/get_saved_searches.test.ts index 3e103d6ea3699..57d9e215703c4 100644 --- a/src/plugins/discover/public/services/saved_searches/get_saved_searches.test.ts +++ b/src/plugins/discover/public/services/saved_searches/get_saved_searches.test.ts @@ -104,6 +104,7 @@ describe('getSavedSearch', () => { "hideAggregatedPreview": undefined, "hideChart": false, "id": "ccf1af80-2297-11ec-86e0-1155ffb9c7a7", + "isTextBasedQuery": undefined, "rowHeight": undefined, "searchSource": Object { "create": [MockFunction], diff --git a/src/plugins/unified_search/public/dataview_picker/change_dataview.tsx b/src/plugins/unified_search/public/dataview_picker/change_dataview.tsx index 207ec1535fb5c..1c10573a27f22 100644 --- a/src/plugins/unified_search/public/dataview_picker/change_dataview.tsx +++ b/src/plugins/unified_search/public/dataview_picker/change_dataview.tsx @@ -310,21 +310,33 @@ export function ChangeDataView({ setIsTextLangTransitionModalDismissed(true); }, [storage]); - const onModalClose = useCallback( - (shouldDismissModal: boolean, needsSave?: boolean) => { + const cleanup = useCallback( + (shouldDismissModal: boolean) => { setIsTextLangTransitionModalVisible(false); - if (Boolean(needsSave)) { - onSaveTextLanguageQuery?.(); - } setIsTextBasedLangSelected(false); - // clean up the text based language query + // clean up the Text based language jQuery onTextLangQuerySubmit?.(); setTriggerLabel(trigger.label); if (shouldDismissModal) { onTransitionModalDismiss(); } }, - [onSaveTextLanguageQuery, onTextLangQuerySubmit, onTransitionModalDismiss, trigger.label] + [onTextLangQuerySubmit, onTransitionModalDismiss, trigger.label] + ); + + const onModalClose = useCallback( + (shouldDismissModal: boolean, needsSave?: boolean) => { + if (Boolean(needsSave)) { + onSaveTextLanguageQuery?.({ + onSave: () => { + cleanup(shouldDismissModal); + }, + }); + } else { + cleanup(shouldDismissModal); + } + }, + [cleanup, onSaveTextLanguageQuery] ); if (isTextLangTransitionModalVisible && !isTextLangTransitionModalDismissed) { diff --git a/src/plugins/unified_search/public/dataview_picker/index.tsx b/src/plugins/unified_search/public/dataview_picker/index.tsx index 2b2ab7fe2b995..d42a7854d6ed1 100644 --- a/src/plugins/unified_search/public/dataview_picker/index.tsx +++ b/src/plugins/unified_search/public/dataview_picker/index.tsx @@ -21,6 +21,10 @@ export enum TextBasedLanguages { ESQL = 'ESQL', } +export interface OnSaveTextLanguageQueryProps { + onSave: () => void; +} + /** @public */ export interface DataViewPickerProps { trigger: ChangeDataViewTriggerProps; @@ -34,7 +38,7 @@ export interface DataViewPickerProps { // list of the supported text-based languages per application textBasedLanguages?: TextBasedLanguages[]; // called when the user clicks the Save and switch transition modal button - onSaveTextLanguageQuery?: () => void; + onSaveTextLanguageQuery?: ({ onSave }: OnSaveTextLanguageQueryProps) => void; } export interface DataViewPickerPropsExtended extends DataViewPickerProps { diff --git a/src/plugins/unified_search/public/query_string_input/query_bar_top_row.tsx b/src/plugins/unified_search/public/query_string_input/query_bar_top_row.tsx index 84ba23e38d67d..c4983df97ad7e 100644 --- a/src/plugins/unified_search/public/query_string_input/query_bar_top_row.tsx +++ b/src/plugins/unified_search/public/query_string_input/query_bar_top_row.tsx @@ -35,7 +35,11 @@ import QueryStringInputUI from './query_string_input'; import { NoDataPopover } from './no_data_popover'; import { shallowEqual } from '../utils/shallow_equal'; import { AddFilterPopover } from './add_filter_popover'; -import { DataViewPicker, DataViewPickerProps } from '../dataview_picker'; +import { + DataViewPicker, + DataViewPickerProps, + OnSaveTextLanguageQueryProps, +} from '../dataview_picker'; import { FilterButtonGroup } from '../filter_bar/filter_button_group/filter_button_group'; import type { SuggestionsListSize } from '../typeahead/suggestions_component'; import { TextBasedLanguagesEditor } from './text_based_languages_editor'; @@ -86,6 +90,7 @@ export interface QueryBarTopRowProps { onFiltersUpdated?: (filters: Filter[]) => void; dataViewPickerComponentProps?: DataViewPickerProps; textBasedLanguageModeErrors?: Error[]; + onTextBasedSavedAndExit?: ({ onSave }: OnSaveTextLanguageQueryProps) => void; filterBar?: React.ReactNode; showDatePickerAsBadge?: boolean; showSubmitButton?: boolean; @@ -444,6 +449,7 @@ export const QueryBarTopRow = React.memo( trigger={{ fullWidth: isMobile, ...props.dataViewPickerComponentProps.trigger }} onTextLangQuerySubmit={props.onTextLangQuerySubmit} textBasedLanguage={textBasedLanguage} + onSaveTextLanguageQuery={props.onTextBasedSavedAndExit} /> ); diff --git a/src/plugins/unified_search/public/search_bar/create_search_bar.tsx b/src/plugins/unified_search/public/search_bar/create_search_bar.tsx index 111462b3a80fd..681f879914de2 100644 --- a/src/plugins/unified_search/public/search_bar/create_search_bar.tsx +++ b/src/plugins/unified_search/public/search_bar/create_search_bar.tsx @@ -205,6 +205,7 @@ export function createSearchBar({ {...overrideDefaultBehaviors(props)} dataViewPickerComponentProps={props.dataViewPickerComponentProps} textBasedLanguageModeErrors={props.textBasedLanguageModeErrors} + onTextBasedSavedAndExit={props.onTextBasedSavedAndExit} displayStyle={props.displayStyle} isScreenshotMode={isScreenshotMode} /> diff --git a/src/plugins/unified_search/public/search_bar/search_bar.tsx b/src/plugins/unified_search/public/search_bar/search_bar.tsx index a52123b066348..e72d45869df83 100644 --- a/src/plugins/unified_search/public/search_bar/search_bar.tsx +++ b/src/plugins/unified_search/public/search_bar/search_bar.tsx @@ -25,7 +25,7 @@ import { DataView } from '@kbn/data-views-plugin/public'; import { SavedQueryMeta, SaveQueryForm } from '../saved_query_form'; import { SavedQueryManagementList } from '../saved_query_management'; import { QueryBarMenu, QueryBarMenuProps } from '../query_string_input/query_bar_menu'; -import type { DataViewPickerProps } from '../dataview_picker'; +import type { DataViewPickerProps, OnSaveTextLanguageQueryProps } from '../dataview_picker'; import QueryBarTopRow from '../query_string_input/query_bar_top_row'; import { FilterBar, FilterItems } from '../filter_bar'; import type { SuggestionsListSize } from '../typeahead/suggestions_component'; @@ -88,6 +88,7 @@ export interface SearchBarOwnProps { fillSubmitButton?: boolean; dataViewPickerComponentProps?: DataViewPickerProps; textBasedLanguageModeErrors?: Error[]; + onTextBasedSavedAndExit?: ({ onSave }: OnSaveTextLanguageQueryProps) => void; showSubmitButton?: boolean; // defines size of suggestions query popover suggestionsSize?: SuggestionsListSize; @@ -532,6 +533,7 @@ class SearchBarUI extends Component { onFiltersUpdated={this.props.onFiltersUpdated} dataViewPickerComponentProps={this.props.dataViewPickerComponentProps} textBasedLanguageModeErrors={this.props.textBasedLanguageModeErrors} + onTextBasedSavedAndExit={this.props.onTextBasedSavedAndExit} showDatePickerAsBadge={this.shouldShowDatePickerAsBadge()} filterBar={filterBar} suggestionsSize={this.props.suggestionsSize} From 7bc9c2b350d445bbc2163e204cce5f4ad1d9f38f Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Tue, 28 Jun 2022 11:46:10 +0300 Subject: [PATCH 035/115] Add shortcut on the editor for submit query --- .../public/query_string_input/query_bar_top_row.tsx | 6 ++++++ .../text_based_languages_editor/index.tsx | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/src/plugins/unified_search/public/query_string_input/query_bar_top_row.tsx b/src/plugins/unified_search/public/query_string_input/query_bar_top_row.tsx index c4983df97ad7e..f678f7d6e86bd 100644 --- a/src/plugins/unified_search/public/query_string_input/query_bar_top_row.tsx +++ b/src/plugins/unified_search/public/query_string_input/query_bar_top_row.tsx @@ -526,6 +526,12 @@ export const QueryBarTopRow = React.memo( expandCodeEditor={(status: boolean) => setCodeEditorIsExpanded(status)} isCodeEditorExpanded={codeEditorIsExpanded} errors={props.textBasedLanguageModeErrors} + onTextLangQuerySubmit={() => + onSubmit({ + query: queryRef.current, + dateRange: dateRangeRef.current, + }) + } /> ) ); diff --git a/src/plugins/unified_search/public/query_string_input/text_based_languages_editor/index.tsx b/src/plugins/unified_search/public/query_string_input/text_based_languages_editor/index.tsx index fa88dd27bcfa3..abf94926811a6 100644 --- a/src/plugins/unified_search/public/query_string_input/text_based_languages_editor/index.tsx +++ b/src/plugins/unified_search/public/query_string_input/text_based_languages_editor/index.tsx @@ -37,6 +37,7 @@ import { EditorFooter } from './editor_footer'; interface TextBasedLanguagesEditorProps { query: any; onTextLangQueryChange: (query: any) => void; + onTextLangQuerySubmit: () => void; expandCodeEditor: (status: boolean) => void; isCodeEditorExpanded: boolean; errors?: Error[]; @@ -67,6 +68,7 @@ let updateLinesFromModel = false; export const TextBasedLanguagesEditor = memo(function TextBasedLanguagesEditor({ query, onTextLangQueryChange, + onTextLangQuerySubmit, expandCodeEditor, isCodeEditorExpanded, errors, @@ -172,6 +174,10 @@ export const TextBasedLanguagesEditor = memo(function TextBasedLanguagesEditor({ initialRender = false; updateLinesFromModel = true; }); + // eslint-disable-next-line no-bitwise + editor1.current?.addCommand(monaco.KeyMod.CtrlCmd | monaco.KeyCode.Enter, function () { + onTextLangQuerySubmit(); + }); if (!isCodeEditorExpanded) { editor1.current?.onDidContentSizeChange(updateHeight); } From 87297c0492722b8013504ea27d486f6db28f4acd Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Tue, 28 Jun 2022 12:04:42 +0300 Subject: [PATCH 036/115] Fix wrong condition --- .../common/search/search_source/migrate_legacy_query.ts | 8 ++++---- .../services/saved_searches/save_saved_searches.test.ts | 2 ++ 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/plugins/data/common/search/search_source/migrate_legacy_query.ts b/src/plugins/data/common/search/search_source/migrate_legacy_query.ts index 919e0ab3d0d4a..fbe913504082c 100644 --- a/src/plugins/data/common/search/search_source/migrate_legacy_query.ts +++ b/src/plugins/data/common/search/search_source/migrate_legacy_query.ts @@ -5,11 +5,11 @@ * in compliance with, at your election, the Elastic License 2.0 or the Server * Side Public License, v 1. */ -import { Query } from '@kbn/es-query'; +import { Query, AggregateQuery } from '@kbn/es-query'; import { has } from 'lodash'; -function isOfQueryType(arg: any): arg is Query { - return Boolean(arg && 'query' in arg); +function isOfAggregateQueryType(query: AggregateQuery | Query): query is AggregateQuery { + return Boolean(query && 'sql' in query); } /** @@ -22,7 +22,7 @@ function isOfQueryType(arg: any): arg is Query { export function migrateLegacyQuery(query: Query | { [key: string]: any } | string): Query { // Lucene was the only option before, so language-less queries are all lucene if (!has(query, 'language')) { - if (typeof query === 'object' && !isOfQueryType(query)) { + if (typeof query === 'object' && isOfAggregateQueryType(query)) { return query as Query; } return { query, language: 'lucene' }; diff --git a/src/plugins/discover/public/services/saved_searches/save_saved_searches.test.ts b/src/plugins/discover/public/services/saved_searches/save_saved_searches.test.ts index 23c1107fe23c1..0c88b2bcea94a 100644 --- a/src/plugins/discover/public/services/saved_searches/save_saved_searches.test.ts +++ b/src/plugins/discover/public/services/saved_searches/save_saved_searches.test.ts @@ -87,6 +87,7 @@ describe('saveSavedSearch', () => { columns: [], description: '', grid: {}, + isTextBasedQuery: false, hideChart: false, kibanaSavedObjectMeta: { searchSourceJSON: '{}' }, sort: [], @@ -106,6 +107,7 @@ describe('saveSavedSearch', () => { columns: [], description: '', grid: {}, + isTextBasedQuery: false, hideChart: false, kibanaSavedObjectMeta: { searchSourceJSON: '{}' }, sort: [], From 58412fb485024082d5d7784f1beecefcfea4dbd9 Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Tue, 28 Jun 2022 12:08:55 +0300 Subject: [PATCH 037/115] Initial types change --- .../common/search/search_source/migrate_legacy_query.ts | 6 ++++-- src/plugins/data/common/search/search_source/types.ts | 4 ++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/plugins/data/common/search/search_source/migrate_legacy_query.ts b/src/plugins/data/common/search/search_source/migrate_legacy_query.ts index fbe913504082c..48e2aa8d2c92b 100644 --- a/src/plugins/data/common/search/search_source/migrate_legacy_query.ts +++ b/src/plugins/data/common/search/search_source/migrate_legacy_query.ts @@ -19,11 +19,13 @@ function isOfAggregateQueryType(query: AggregateQuery | Query): query is Aggrega * @return Object */ -export function migrateLegacyQuery(query: Query | { [key: string]: any } | string): Query { +export function migrateLegacyQuery( + query: Query | { [key: string]: any } | string | AggregateQuery +): Query | AggregateQuery { // Lucene was the only option before, so language-less queries are all lucene if (!has(query, 'language')) { if (typeof query === 'object' && isOfAggregateQueryType(query)) { - return query as Query; + return query; } return { query, language: 'lucene' }; } diff --git a/src/plugins/data/common/search/search_source/types.ts b/src/plugins/data/common/search/search_source/types.ts index 3f199c6b338c4..4319ffddcd900 100644 --- a/src/plugins/data/common/search/search_source/types.ts +++ b/src/plugins/data/common/search/search_source/types.ts @@ -7,13 +7,13 @@ */ import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; +import { Query, AggregateQuery } from '@kbn/es-query'; import { SerializableRecord } from '@kbn/utility-types'; import { PersistableStateService } from '@kbn/kibana-utils-plugin/common'; import type { Filter } from '@kbn/es-query'; import type { DataView } from '@kbn/data-views-plugin/common'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths import { AggConfigSerialized, IAggConfigs } from '../../../public'; -import { Query } from '../..'; import type { SearchSource } from './search_source'; /** @@ -78,7 +78,7 @@ export interface SearchSourceFields { /** * {@link Query} */ - query?: Query; + query?: Query | AggregateQuery; /** * {@link Filter} */ From 75efd8092949d9041d10a0e107f8ab9415e8bfc6 Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Tue, 28 Jun 2022 13:05:41 +0300 Subject: [PATCH 038/115] Use regex to find the index pattern string --- src/plugins/data/common/query/to_expression_ast.ts | 11 +++++------ src/plugins/data/common/search/search_source/types.ts | 2 +- .../text_based_languages_editor/helpers.ts | 11 +++++------ 3 files changed, 11 insertions(+), 13 deletions(-) diff --git a/src/plugins/data/common/query/to_expression_ast.ts b/src/plugins/data/common/query/to_expression_ast.ts index 0dc314bf6b2ec..badcb8b3372e1 100644 --- a/src/plugins/data/common/query/to_expression_ast.ts +++ b/src/plugins/data/common/query/to_expression_ast.ts @@ -27,13 +27,12 @@ function getAggregateQueryMode(query: AggregateQuery): string { } function getIndexPatternFromSQLQuery(sqlQuery?: string): string { - const [_, fromText] = sqlQuery?.split('FROM') ?? []; - const dataViewString = fromText.replaceAll('"', ''); - const [indexPattern] = dataViewString?.split(' ') ?? []; - if (indexPattern) { - return indexPattern; + const sql = sqlQuery?.replaceAll('"', ''); + const matches = sql?.match(/FROM\s+([\w*]+)/); + if (matches) { + return matches[1]; } - return dataViewString; + return ''; } interface Args extends QueryState { diff --git a/src/plugins/data/common/search/search_source/types.ts b/src/plugins/data/common/search/search_source/types.ts index 4319ffddcd900..d3022ae01a109 100644 --- a/src/plugins/data/common/search/search_source/types.ts +++ b/src/plugins/data/common/search/search_source/types.ts @@ -125,7 +125,7 @@ export type SerializedSearchSourceFields = { /** * {@link Query} */ - query?: Query; + query?: Query | AggregateQuery; /** * {@link Filter} */ diff --git a/src/plugins/unified_search/public/query_string_input/text_based_languages_editor/helpers.ts b/src/plugins/unified_search/public/query_string_input/text_based_languages_editor/helpers.ts index c888e743e1193..b42497e434ebe 100644 --- a/src/plugins/unified_search/public/query_string_input/text_based_languages_editor/helpers.ts +++ b/src/plugins/unified_search/public/query_string_input/text_based_languages_editor/helpers.ts @@ -33,13 +33,12 @@ export const useDebounceWithOptions = ( }; function getIndexPatternFromSQLQuery(sqlQuery?: string): string { - const [_, fromText] = sqlQuery?.split('FROM') ?? []; - const dataViewString = fromText.replaceAll('"', ''); - const [indexPattern] = dataViewString?.split(' ') ?? []; - if (indexPattern) { - return indexPattern; + const sql = sqlQuery?.replaceAll('"', ''); + const matches = sql?.match(/FROM\s+([\w*]+)/); + if (matches) { + return matches[1]; } - return dataViewString; + return ''; } export const parseErrors = (errors: Error[], code: string) => { From 2c0730e5511caa5b9a3d72a5f45a6b0d62a1c53f Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Tue, 28 Jun 2022 13:49:23 +0300 Subject: [PATCH 039/115] Fix some types and cleanup --- .../components/layout/discover_documents.tsx | 14 ++++++----- .../components/layout/discover_layout.tsx | 16 ++---------- .../main/hooks/use_saved_search.ts | 18 ++----------- .../application/main/utils/fetch_all.ts | 16 ++---------- .../discover_grid/discover_grid.scss | 4 +++ .../discover_grid/discover_grid.tsx | 10 +++++++- src/plugins/discover/public/locator.ts | 6 ++--- .../utils/get_text_based_language_mode.ts | 25 +++++++++++++++++++ 8 files changed, 55 insertions(+), 54 deletions(-) create mode 100644 src/plugins/discover/public/utils/get_text_based_language_mode.ts diff --git a/src/plugins/discover/public/application/main/components/layout/discover_documents.tsx b/src/plugins/discover/public/application/main/components/layout/discover_documents.tsx index e09feb1fa5205..49b5865c795cd 100644 --- a/src/plugins/discover/public/application/main/components/layout/discover_documents.tsx +++ b/src/plugins/discover/public/application/main/components/layout/discover_documents.tsx @@ -32,6 +32,7 @@ import { AppState, GetStateReturn } from '../../services/discover_state'; import { useDataState } from '../../hooks/use_data_state'; import { DocTableInfinite } from '../../../../components/doc_table/doc_table_infinite'; import { SortPairArr } from '../../../../components/doc_table/utils/get_sort'; +import { getTextBasedLanguageMode } from '../../../../utils/get_text_based_language_mode'; import { DocumentExplorerCallout } from '../document_explorer_callout'; import { DocumentExplorerUpdateCallout } from '../document_explorer_callout/document_explorer_update_callout'; import { DiscoverTourProvider } from '../../../../components/discover_tour'; @@ -67,6 +68,7 @@ function DiscoverDocumentsComponent({ const sampleSize = useMemo(() => uiSettings.get(SAMPLE_SIZE_SETTING), [uiSettings]); const documentState: DataDocumentsMsg = useDataState(documents$); + const textBasedLanguageMode = state.query ? getTextBasedLanguageMode(state.query) : ''; const isLoading = documentState.fetchStatus === FetchStatus.LOADING; const rows = useMemo(() => documentState.result || [], [documentState.result]); @@ -110,10 +112,10 @@ function DiscoverDocumentsComponent({ const showTimeCol = useMemo( () => - !documentState.textBasedLanguageMode && + !textBasedLanguageMode && !uiSettings.get(DOC_HIDE_TIME_COLUMN_SETTING, false) && !!indexPattern.timeFieldName, - [uiSettings, indexPattern.timeFieldName, documentState.textBasedLanguageMode] + [uiSettings, indexPattern.timeFieldName, textBasedLanguageMode] ); if ( @@ -153,7 +155,7 @@ function DiscoverDocumentsComponent({ onFilter={onAddFilter as DocViewFilterFn} onMoveColumn={onMoveColumn} onRemoveColumn={onRemoveColumn} - onSort={!documentState.textBasedLanguageMode ? onSort : undefined} + onSort={!textBasedLanguageMode ? onSort : undefined} useNewFieldsApi={useNewFieldsApi} dataTestSubj="discoverDocTable" /> @@ -176,19 +178,19 @@ function DiscoverDocumentsComponent({ sampleSize={sampleSize} searchDescription={savedSearch.description} searchTitle={savedSearch.title} - setExpandedDoc={!documentState.textBasedLanguageMode ? setExpandedDoc : undefined} + setExpandedDoc={!textBasedLanguageMode ? setExpandedDoc : undefined} showTimeCol={showTimeCol} settings={state.grid} onAddColumn={onAddColumn} onFilter={onAddFilter as DocViewFilterFn} onRemoveColumn={onRemoveColumn} onSetColumns={onSetColumns} - onSort={!documentState.textBasedLanguageMode ? onSort : undefined} + onSort={!textBasedLanguageMode ? onSort : undefined} onResize={onResize} useNewFieldsApi={useNewFieldsApi} rowHeightState={state.rowHeight} onUpdateRowHeight={onUpdateRowHeight} - isSortEnabled={!documentState.textBasedLanguageMode} + isSortEnabled={!textBasedLanguageMode} />
diff --git a/src/plugins/discover/public/application/main/components/layout/discover_layout.tsx b/src/plugins/discover/public/application/main/components/layout/discover_layout.tsx index c66c589c1f9dc..717e4814612b7 100644 --- a/src/plugins/discover/public/application/main/components/layout/discover_layout.tsx +++ b/src/plugins/discover/public/application/main/components/layout/discover_layout.tsx @@ -20,7 +20,6 @@ import { } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { METRIC_TYPE } from '@kbn/analytics'; -import { AggregateQuery, Query } from '@kbn/es-query'; import classNames from 'classnames'; import { generateFilters } from '@kbn/data-plugin/public'; import { DataView, DataViewField, DataViewType } from '@kbn/data-views-plugin/public'; @@ -48,6 +47,7 @@ import { } from '../../../../services/saved_searches'; import { FieldStatisticsTable } from '../field_stats_table'; import { VIEW_MODE } from '../../../../components/view_mode_toggle'; +import { getTextBasedLanguageMode } from '../../../../utils/get_text_based_language_mode'; import { DOCUMENTS_VIEW_CLICK, FIELD_STATISTICS_VIEW_CLICK } from '../field_stats_table/constants'; import { hasActiveFilter } from './utils'; @@ -61,14 +61,6 @@ const TopNavMemoized = React.memo(DiscoverTopNav); const DiscoverChartMemoized = React.memo(DiscoverChart); const FieldStatisticsTableMemoized = React.memo(FieldStatisticsTable); -function isOfAggregateQueryType(query: AggregateQuery | Query): query is AggregateQuery { - return Boolean(query && 'sql' in query); -} - -function getAggregateQueryMode(query: AggregateQuery): string { - return Object.keys(query)[0]; -} - export function DiscoverLayout({ indexPattern, indexPatternList, @@ -182,11 +174,7 @@ export function DiscoverLayout({ useNewFieldsApi, }); - let textBasedLanguageMode = ''; - if (state.query && isOfAggregateQueryType(state.query)) { - const aggregatedQuery = state.query as AggregateQuery; - textBasedLanguageMode = getAggregateQueryMode(aggregatedQuery); - } + const textBasedLanguageMode = state.query ? getTextBasedLanguageMode(state.query) : ''; const onAddFilter = useCallback( (field: DataViewField | string, values: string, operation: '+' | '-') => { diff --git a/src/plugins/discover/public/application/main/hooks/use_saved_search.ts b/src/plugins/discover/public/application/main/hooks/use_saved_search.ts index ca511904d7462..89bd38c8aca1f 100644 --- a/src/plugins/discover/public/application/main/hooks/use_saved_search.ts +++ b/src/plugins/discover/public/application/main/hooks/use_saved_search.ts @@ -7,7 +7,6 @@ */ import { useCallback, useEffect, useMemo, useRef } from 'react'; import { BehaviorSubject, Subject } from 'rxjs'; -import { AggregateQuery, Query } from '@kbn/es-query'; import type { AutoRefreshDoneFn } from '@kbn/data-plugin/public'; import { ISearchSource } from '@kbn/data-plugin/public'; import { RequestAdapter } from '@kbn/inspector-plugin/public'; @@ -22,6 +21,7 @@ import { fetchAll } from '../utils/fetch_all'; import { useBehaviorSubject } from './use_behavior_subject'; import { sendResetMsg } from './use_saved_search_messages'; import { getFetch$ } from '../utils/get_fetch_observable'; +import { getTextBasedLanguageMode } from '../../../utils/get_text_based_language_mode'; import { SavedSearch } from '../../../services/saved_searches'; import type { DataTableRecord } from '../../../types'; @@ -85,14 +85,6 @@ export interface DataAvailableFieldsMsg extends DataMsg { fields?: string[]; } -function isOfAggregateQueryType(query: AggregateQuery | Query): query is AggregateQuery { - return Boolean(query && 'sql' in query); -} - -function getAggregateQueryMode(query: AggregateQuery): string { - return Object.keys(query)[0]; -} - /** * This hook return 2 observables, refetch$ allows to trigger data fetching, data$ to subscribe * to the data fetching @@ -117,13 +109,7 @@ export const useSavedSearch = ({ const { data, filterManager } = services; const timefilter = data.query.timefilter.timefilter; const { query } = stateContainer.appStateContainer.getState(); - let textBasedLanguageMode = ''; - if (query && isOfAggregateQueryType(query)) { - const aggregatedQuery = query as AggregateQuery; - textBasedLanguageMode = getAggregateQueryMode(aggregatedQuery); - } else { - textBasedLanguageMode = ''; - } + const textBasedLanguageMode = query ? getTextBasedLanguageMode(query) : ''; const inspectorAdapters = useMemo(() => ({ requests: new RequestAdapter() }), []); diff --git a/src/plugins/discover/public/application/main/utils/fetch_all.ts b/src/plugins/discover/public/application/main/utils/fetch_all.ts index dfd5271b3b64b..d986a87a7f76b 100644 --- a/src/plugins/discover/public/application/main/utils/fetch_all.ts +++ b/src/plugins/discover/public/application/main/utils/fetch_all.ts @@ -5,7 +5,6 @@ * in compliance with, at your election, the Elastic License 2.0 or the Server * Side Public License, v 1. */ -import { AggregateQuery, Query } from '@kbn/es-query'; import { DataPublicPluginStart, ISearchSource } from '@kbn/data-plugin/public'; import { Adapters } from '@kbn/inspector-plugin'; import { ReduxLikeStateContainer } from '@kbn/kibana-utils-plugin/common'; @@ -33,6 +32,7 @@ import { SavedSearchData, } from '../hooks/use_saved_search'; import { DiscoverServices } from '../../../build_services'; +import { getTextBasedLanguageMode } from '../../../utils/get_text_based_language_mode'; import { fetchSql } from './fetch_sql'; export interface FetchDeps { @@ -47,14 +47,6 @@ export interface FetchDeps { useNewFieldsApi: boolean; } -function isOfAggregateQueryType(query: AggregateQuery | Query): query is AggregateQuery { - return Boolean(query && 'sql' in query); -} - -function getAggregateQueryMode(query: AggregateQuery): string { - return Object.keys(query)[0]; -} - /** * This function starts fetching all required queries in Discover. This will be the query to load the individual * documents, and depending on whether a chart is shown either the aggregation query to load the chart data @@ -97,11 +89,7 @@ export function fetchAll( } const { hideChart, sort, query } = appStateContainer.getState(); - let textBasedLanguageMode = ''; - if (query && isOfAggregateQueryType(query)) { - const aggregatedQuery = query as AggregateQuery; - textBasedLanguageMode = getAggregateQueryMode(aggregatedQuery); - } + const textBasedLanguageMode = query ? getTextBasedLanguageMode(query) : ''; // Update the base searchSource, base for all child fetches if (!textBasedLanguageMode) { diff --git a/src/plugins/discover/public/components/discover_grid/discover_grid.scss b/src/plugins/discover/public/components/discover_grid/discover_grid.scss index 71335e968db4c..57e63c9cccb85 100644 --- a/src/plugins/discover/public/components/discover_grid/discover_grid.scss +++ b/src/plugins/discover/public/components/discover_grid/discover_grid.scss @@ -14,6 +14,10 @@ padding: 0; } + .dscDiscoverGrid__textLanguageMode .euiDataGridRowCell.euiDataGridRowCell--firstColumn { + padding: 4px; + } + .euiDataGridRowCell.euiDataGridRowCell--lastColumn { border-right: none; } diff --git a/src/plugins/discover/public/components/discover_grid/discover_grid.tsx b/src/plugins/discover/public/components/discover_grid/discover_grid.tsx index 471f5ed76b177..a16be979b5581 100644 --- a/src/plugins/discover/public/components/discover_grid/discover_grid.tsx +++ b/src/plugins/discover/public/components/discover_grid/discover_grid.tsx @@ -50,6 +50,7 @@ import { import { DiscoverGridDocumentToolbarBtn } from './discover_grid_document_selection'; import { SortPairArr } from '../doc_table/utils/get_sort'; import { getFieldsToShow } from '../../utils/get_fields_to_show'; +import { getTextBasedLanguageMode } from '../../utils/get_text_based_language_mode'; import type { DataTableRecord, ValueToStringConverter } from '../../types'; import { useRowHeightsOptions } from '../../hooks/use_row_heights_options'; import { useDiscoverServices } from '../../hooks/use_discover_services'; @@ -202,6 +203,9 @@ export const DiscoverGrid = ({ }: DiscoverGridProps) => { const dataGridRef = useRef(null); const services = useDiscoverServices(); + const query = services.data.query.queryString.getQuery(); + const textBasedLanguageMode = getTextBasedLanguageMode(query); + const [selectedDocs, setSelectedDocs] = useState([]); const [isFilterActive, setIsFilterActive] = useState(false); const displayedColumns = getDisplayedColumns(columns, indexPattern); @@ -480,7 +484,11 @@ export const DiscoverGrid = ({ data-title={searchTitle} data-description={searchDescription} data-document-number={displayedRows.length} - className={classnames(className, 'dscDiscoverGrid__table')} + className={classnames( + className, + 'dscDiscoverGrid__table', + textBasedLanguageMode ? 'dscDiscoverGrid__textLanguageMode' : '' + )} > Date: Tue, 28 Jun 2022 14:33:21 +0300 Subject: [PATCH 040/115] Fix types --- src/plugins/data/common/query/query_state.ts | 4 ++-- .../query_string/query_string_manager.test.ts | 4 ++-- .../query_string/query_string_manager.ts | 24 ++++++------------- .../public/visualize_app/types.ts | 4 ++-- 4 files changed, 13 insertions(+), 23 deletions(-) diff --git a/src/plugins/data/common/query/query_state.ts b/src/plugins/data/common/query/query_state.ts index fbc5626f9a28c..a9ca73d7f0def 100644 --- a/src/plugins/data/common/query/query_state.ts +++ b/src/plugins/data/common/query/query_state.ts @@ -8,7 +8,7 @@ import type { Filter } from '@kbn/es-query'; import type { TimeRange, RefreshInterval } from './timefilter/types'; -import type { Query } from './types'; +import type { Query, AggregateQuery } from './types'; /** * All query state service state @@ -22,5 +22,5 @@ export type QueryState = { time?: TimeRange; refreshInterval?: RefreshInterval; filters?: Filter[]; - query?: Query; + query?: Query | AggregateQuery; }; diff --git a/src/plugins/data/public/query/query_string/query_string_manager.test.ts b/src/plugins/data/public/query/query_string/query_string_manager.test.ts index 7ba28a7e04cd9..2da2ee68c86e6 100644 --- a/src/plugins/data/public/query/query_string/query_string_manager.test.ts +++ b/src/plugins/data/public/query/query_string/query_string_manager.test.ts @@ -10,7 +10,7 @@ import { QueryStringManager } from './query_string_manager'; import { Storage } from '@kbn/kibana-utils-plugin/public/storage'; import { StubBrowserStorage } from '@kbn/test-jest-helpers'; import { coreMock } from '@kbn/core/public/mocks'; -import { Query } from '../../../common/query'; +import { Query, AggregateQuery } from '../../../common/query'; describe('QueryStringManager', () => { let service: QueryStringManager; @@ -24,7 +24,7 @@ describe('QueryStringManager', () => { test('getUpdates$ is a cold emits only after query changes', () => { const obs$ = service.getUpdates$(); - const emittedValues: Query[] = []; + const emittedValues: Array = []; obs$.subscribe((v) => { emittedValues.push(v); }); diff --git a/src/plugins/data/public/query/query_string/query_string_manager.ts b/src/plugins/data/public/query/query_string/query_string_manager.ts index 296324c4958d0..360aab94134e9 100644 --- a/src/plugins/data/public/query/query_string/query_string_manager.ts +++ b/src/plugins/data/public/query/query_string/query_string_manager.ts @@ -10,23 +10,19 @@ import { BehaviorSubject } from 'rxjs'; import { skip } from 'rxjs/operators'; import { PublicMethodsOf } from '@kbn/utility-types'; import { CoreStart } from '@kbn/core/public'; -import type { Query } from '@kbn/es-query'; +import type { Query, AggregateQuery } from '@kbn/es-query'; import { IStorageWrapper } from '@kbn/kibana-utils-plugin/public'; import { isEqual } from 'lodash'; import { KIBANA_USER_QUERY_LANGUAGE_KEY, UI_SETTINGS } from '../../../common'; -function isOfQueryType(arg: any): arg is Query { - return Boolean(arg && 'query' in arg); -} - export class QueryStringManager { - private query$: BehaviorSubject; + private query$: BehaviorSubject; constructor( private readonly storage: IStorageWrapper, private readonly uiSettings: CoreStart['uiSettings'] ) { - this.query$ = new BehaviorSubject(this.getDefaultQuery()); + this.query$ = new BehaviorSubject(this.getDefaultQuery()); } private getDefaultLanguage() { @@ -60,23 +56,17 @@ export class QueryStringManager { return this.query$.asObservable().pipe(skip(1)); }; - public getQuery = (): Query => { + public getQuery = (): Query | AggregateQuery => { return this.query$.getValue(); }; /** * Updates the query. - * @param {Query} query + * @param {Query | AggregateQuery} query */ - public setQuery = (query: Query) => { + public setQuery = (query: Query | AggregateQuery) => { const curQuery = this.query$.getValue(); - const isOfTypeQuery = isOfQueryType(query); - if ( - (isOfTypeQuery && query?.language !== curQuery.language) || - query?.query !== curQuery.query - ) { - this.query$.next(query); - } else if (!isOfTypeQuery && !isEqual(query, curQuery)) { + if (!isEqual(query, curQuery)) { this.query$.next(query); } }; diff --git a/src/plugins/visualizations/public/visualize_app/types.ts b/src/plugins/visualizations/public/visualize_app/types.ts index cf46e0fc4ff1e..14157a6493183 100644 --- a/src/plugins/visualizations/public/visualize_app/types.ts +++ b/src/plugins/visualizations/public/visualize_app/types.ts @@ -28,7 +28,7 @@ import type { import type { DataViewEditorStart } from '@kbn/data-view-editor-plugin/public'; import type { NavigationPublicPluginStart as NavigationStart } from '@kbn/navigation-plugin/public'; -import type { Filter, Query, TimeRange } from '@kbn/es-query'; +import type { Filter, Query, TimeRange, AggregateQuery } from '@kbn/es-query'; import type { DataPublicPluginStart } from '@kbn/data-plugin/public'; import type { DataViewsPublicPluginStart } from '@kbn/data-views-plugin/public'; import type { SharePluginStart } from '@kbn/share-plugin/public'; @@ -56,7 +56,7 @@ export interface VisualizeAppState { filters: Filter[]; uiState: SerializableRecord; vis: SavedVisState; - query: Query; + query: Query | AggregateQuery; savedQuery?: string; linked: boolean; } From 2b120659de02a64862bc8469df3f5ee38f032412 Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Tue, 28 Jun 2022 15:07:42 +0300 Subject: [PATCH 041/115] Fix some types --- src/plugins/data/common/query/types.ts | 4 +-- .../data/public/query/query_service.ts | 4 +-- .../main/components/layout/types.ts | 7 ++-- .../query_string_input/query_bar_top_row.tsx | 22 ++++++------ .../text_based_languages_editor/index.tsx | 3 +- .../public/search_bar/create_search_bar.tsx | 6 ++-- .../lib/use_query_string_manager.ts | 28 ++++++++------- .../public/search_bar/search_bar.tsx | 36 ++++++++++++------- 8 files changed, 64 insertions(+), 46 deletions(-) diff --git a/src/plugins/data/common/query/types.ts b/src/plugins/data/common/query/types.ts index e10afaf746455..d1e5e7c758427 100644 --- a/src/plugins/data/common/query/types.ts +++ b/src/plugins/data/common/query/types.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import type { Query, Filter } from '@kbn/es-query'; +import type { Query, Filter, AggregateQuery } from '@kbn/es-query'; import type { RefreshInterval, TimeRange } from './timefilter/types'; export type { RefreshInterval, TimeRange, TimeRangeBounds } from './timefilter/types'; @@ -24,7 +24,7 @@ export interface SavedQuery { export interface SavedQueryAttributes { title: string; description: string; - query: Query; + query: Query | AggregateQuery; filters?: Filter[]; timefilter?: SavedQueryTimeFilter; } diff --git a/src/plugins/data/public/query/query_service.ts b/src/plugins/data/public/query/query_service.ts index 894db102aa041..9ad8db7de7655 100644 --- a/src/plugins/data/public/query/query_service.ts +++ b/src/plugins/data/public/query/query_service.ts @@ -10,7 +10,7 @@ import { share } from 'rxjs/operators'; import { HttpStart, IUiSettingsClient } from '@kbn/core/public'; import { PersistableStateService, VersionedState } from '@kbn/kibana-utils-plugin/common'; import { IStorageWrapper } from '@kbn/kibana-utils-plugin/public'; -import { buildEsQuery, TimeRange } from '@kbn/es-query'; +import { buildEsQuery, TimeRange, Query } from '@kbn/es-query'; import type { DataView } from '@kbn/data-views-plugin/common'; import { FilterManager } from './filter_manager'; import { createAddToQueryLog } from './lib'; @@ -125,7 +125,7 @@ export class QueryService implements PersistableStateService { return buildEsQuery( indexPattern, - this.queryStringManager.getQuery(), + this.queryStringManager.getQuery() as Query, [...this.filterManager.getFilters(), ...(timeFilter ? [timeFilter] : [])], getEsQueryConfig(getUiSettings()) ); diff --git a/src/plugins/discover/public/application/main/components/layout/types.ts b/src/plugins/discover/public/application/main/components/layout/types.ts index f381f87c7389d..46ccab3b93a7b 100644 --- a/src/plugins/discover/public/application/main/components/layout/types.ts +++ b/src/plugins/discover/public/application/main/components/layout/types.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import type { Query, TimeRange } from '@kbn/es-query'; +import type { Query, TimeRange, AggregateQuery } from '@kbn/es-query'; import type { SavedObject } from '@kbn/data-plugin/public'; import type { DataView, DataViewAttributes } from '@kbn/data-views-plugin/public'; import { ISearchSource } from '@kbn/data-plugin/public'; @@ -22,7 +22,10 @@ export interface DiscoverLayoutProps { inspectorAdapters: { requests: RequestAdapter }; navigateTo: (url: string) => void; onChangeIndexPattern: (id: string) => void; - onUpdateQuery: (payload: { dateRange: TimeRange; query?: Query }, isUpdate?: boolean) => void; + onUpdateQuery: ( + payload: { dateRange: TimeRange; query?: Query | AggregateQuery }, + isUpdate?: boolean + ) => void; resetSavedSearch: () => void; expandedDoc?: DataTableRecord; setExpandedDoc: (doc?: DataTableRecord) => void; diff --git a/src/plugins/unified_search/public/query_string_input/query_bar_top_row.tsx b/src/plugins/unified_search/public/query_string_input/query_bar_top_row.tsx index f678f7d6e86bd..6b9b2714b2e20 100644 --- a/src/plugins/unified_search/public/query_string_input/query_bar_top_row.tsx +++ b/src/plugins/unified_search/public/query_string_input/query_bar_top_row.tsx @@ -49,7 +49,7 @@ const SuperDatePicker = React.memo( EuiSuperDatePicker as any ) as unknown as typeof EuiSuperDatePicker; -function isOfQueryType(arg: any): arg is Query { +function isOfQueryType(arg: Query | AggregateQuery): arg is Query { return Boolean(arg && 'query' in arg); } @@ -71,13 +71,13 @@ export interface QueryBarTopRowProps { isLoading?: boolean; isRefreshPaused?: boolean; nonKqlMode?: 'lucene' | 'text'; - onChange: (payload: { dateRange: TimeRange; query?: Query }) => void; + onChange: (payload: { dateRange: TimeRange; query?: Query | AggregateQuery }) => void; onRefresh?: (payload: { dateRange: TimeRange }) => void; onRefreshChange?: (options: { isPaused: boolean; refreshInterval: number }) => void; - onSubmit: (payload: { dateRange: TimeRange; query?: Query }) => void; + onSubmit: (payload: { dateRange: TimeRange; query?: Query | AggregateQuery }) => void; placeholder?: string; prepend?: React.ComponentProps['prepend']; - query?: Query; + query?: Query | AggregateQuery; refreshInterval?: number; screenTitle?: string; showQueryInput?: boolean; @@ -96,8 +96,8 @@ export interface QueryBarTopRowProps { showSubmitButton?: boolean; suggestionsSize?: SuggestionsListSize; isScreenshotMode?: boolean; - onTextLangQuerySubmit: (query: any) => void; - onTextLangQueryChange: (query: any) => void; + onTextLangQuerySubmit: (query?: AggregateQuery) => void; + onTextLangQueryChange: (query: AggregateQuery) => void; } const SharingMetaFields = React.memo(function SharingMetaFields({ @@ -164,8 +164,8 @@ export const QueryBarTopRow = React.memo( const { uiSettings, storage, appName } = kibana.services; const isQueryLangSelected = props.query && !isOfQueryType(props.query); - const queryLanguage = props.query && props.query.language; - const queryRef = useRef(props.query); + const queryLanguage = props.query && isOfQueryType(props.query) && props.query.language; + const queryRef = useRef(props.query); queryRef.current = props.query; const persistedLog: PersistedLog | undefined = React.useMemo( @@ -222,7 +222,7 @@ export const QueryBarTopRow = React.memo( }); const onSubmit = useCallback( - ({ query, dateRange }: { query?: Query; dateRange: TimeRange }) => { + ({ query, dateRange }: { query?: Query | AggregateQuery; dateRange: TimeRange }) => { if (timeHistory) { timeHistory.add(dateRange); } @@ -234,7 +234,7 @@ export const QueryBarTopRow = React.memo( const onClickSubmitButton = useCallback( (event: React.MouseEvent) => { - if (persistedLog && queryRef.current) { + if (persistedLog && queryRef.current && isOfQueryType(queryRef.current)) { persistedLog.add(queryRef.current.query); } event.preventDefault(); @@ -494,7 +494,7 @@ export const QueryBarTopRow = React.memo( void; + onTextLangQueryChange: (query: AggregateQuery) => void; onTextLangQuerySubmit: () => void; expandCodeEditor: (status: boolean) => void; isCodeEditorExpanded: boolean; diff --git a/src/plugins/unified_search/public/search_bar/create_search_bar.tsx b/src/plugins/unified_search/public/search_bar/create_search_bar.tsx index 681f879914de2..c5348cbe9e25c 100644 --- a/src/plugins/unified_search/public/search_bar/create_search_bar.tsx +++ b/src/plugins/unified_search/public/search_bar/create_search_bar.tsx @@ -12,7 +12,7 @@ import { CoreStart } from '@kbn/core/public'; import { IStorageWrapper } from '@kbn/kibana-utils-plugin/public'; import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public'; import { QueryStart, SavedQuery, DataPublicPluginStart } from '@kbn/data-plugin/public'; -import type { Query } from '@kbn/es-query'; +import type { Query, AggregateQuery } from '@kbn/es-query'; import type { Filter, TimeRange } from '@kbn/es-query'; import { UsageCollectionSetup } from '@kbn/usage-collection-plugin/public'; import { SearchBar } from '.'; @@ -59,13 +59,13 @@ const defaultOnRefreshChange = (queryService: QueryStart) => { const defaultOnQuerySubmit = ( props: StatefulSearchBarProps, queryService: QueryStart, - currentQuery: Query + currentQuery: Query | AggregateQuery ) => { if (!props.useDefaultBehaviors) return props.onQuerySubmit; const { timefilter } = queryService.timefilter; - return (payload: { dateRange: TimeRange; query?: Query }) => { + return (payload: { dateRange: TimeRange; query?: Query | AggregateQuery }) => { const isUpdate = !_.isEqual(timefilter.getTime(), payload.dateRange) || !_.isEqual(payload.query, currentQuery); diff --git a/src/plugins/unified_search/public/search_bar/lib/use_query_string_manager.ts b/src/plugins/unified_search/public/search_bar/lib/use_query_string_manager.ts index 0a6cc098879e5..ff07b41388294 100644 --- a/src/plugins/unified_search/public/search_bar/lib/use_query_string_manager.ts +++ b/src/plugins/unified_search/public/search_bar/lib/use_query_string_manager.ts @@ -8,19 +8,21 @@ import { useState, useEffect, useMemo } from 'react'; import { Subscription } from 'rxjs'; -import type { Query } from '@kbn/es-query'; +import type { Query, AggregateQuery } from '@kbn/es-query'; import type { QueryStringContract } from '@kbn/data-plugin/public'; interface UseQueryStringProps { - query?: Query; + query?: Query | AggregateQuery; queryStringManager: QueryStringContract; } -function isOfQueryType(arg: any): arg is Query { +function isOfQueryType(arg: Query | AggregateQuery): arg is Query { return Boolean(arg && 'query' in arg); } export const useQueryStringManager = (props: UseQueryStringProps) => { // Filters should be either what's passed in the initial state or the current state of the filter manager - const [query, setQuery] = useState(props.query || props.queryStringManager.getQuery()); + const [query, setQuery] = useState( + props.query || props.queryStringManager.getQuery() + ); useEffect(() => { const subscriptions = new Subscription(); @@ -39,12 +41,14 @@ export const useQueryStringManager = (props: UseQueryStringProps) => { }, [props.queryStringManager]); const isQueryType = isOfQueryType(query); - const stableQuery = useMemo( - () => ({ - language: query.language, - query: query.query, - }), - [query.language, query.query] - ); - return { query: isQueryType ? stableQuery : query }; + const stableQuery = useMemo(() => { + if (isQueryType) { + return { + language: query.language, + query: query.query, + }; + } + return query; + }, [isQueryType, query]); + return { query: stableQuery }; }; diff --git a/src/plugins/unified_search/public/search_bar/search_bar.tsx b/src/plugins/unified_search/public/search_bar/search_bar.tsx index e72d45869df83..3cbd1dbac1fe5 100644 --- a/src/plugins/unified_search/public/search_bar/search_bar.tsx +++ b/src/plugins/unified_search/public/search_bar/search_bar.tsx @@ -15,7 +15,7 @@ import { get, isEqual } from 'lodash'; import memoizeOne from 'memoize-one'; import { METRIC_TYPE } from '@kbn/analytics'; -import { Query, Filter, TimeRange } from '@kbn/es-query'; +import { Query, Filter, TimeRange, AggregateQuery } from '@kbn/es-query'; import { withKibana, KibanaReactContextValue } from '@kbn/kibana-react-plugin/public'; import type { TimeHistoryContract, SavedQuery } from '@kbn/data-plugin/public'; import type { SavedQueryAttributes } from '@kbn/data-plugin/common'; @@ -61,12 +61,15 @@ export interface SearchBarOwnProps { dateRangeFrom?: string; dateRangeTo?: string; // Query bar - should be in SearchBarInjectedDeps - query?: Query; + query?: Query | AggregateQuery; // Show when user has privileges to save showSaveQuery?: boolean; savedQuery?: SavedQuery; - onQueryChange?: (payload: { dateRange: TimeRange; query?: Query }) => void; - onQuerySubmit?: (payload: { dateRange: TimeRange; query?: Query }, isUpdate?: boolean) => void; + onQueryChange?: (payload: { dateRange: TimeRange; query?: Query | AggregateQuery }) => void; + onQuerySubmit?: ( + payload: { dateRange: TimeRange; query?: Query | AggregateQuery }, + isUpdate?: boolean + ) => void; // User has saved the current state as a saved query onSaved?: (savedQuery: SavedQuery) => void; // User has modified the saved query, your app should persist the update @@ -102,7 +105,7 @@ interface State { openQueryBarMenu: boolean; showSavedQueryPopover: boolean; currentProps?: SearchBarProps; - query?: Query; + query?: Query | AggregateQuery; dateRangeFrom: string; dateRangeTo: string; } @@ -129,7 +132,7 @@ class SearchBarUI extends Component { } let nextQuery = null; - if (isOfQueryType(nextProps.query)) { + if (isOfQueryType(nextProps.query) && isOfQueryType(prevState.query)) { if (nextProps.query && nextProps.query.query !== get(prevState, 'currentProps.query.query')) { nextQuery = { query: nextProps.query.query, @@ -197,11 +200,10 @@ class SearchBarUI extends Component { public isDirty = () => { if (!this.props.showDatePicker && this.state.query && this.props.query) { - return this.state.query.query !== this.props.query.query; + return !isEqual(this.state.query, this.props.query); } return ( - (this.state.query && this.props.query && this.state.query.query !== this.props.query.query) || (this.state.query && this.props.query && !isEqual(this.state.query, this.props.query)) || this.state.dateRangeFrom !== this.props.dateRangeFrom || this.state.dateRangeTo !== this.props.dateRangeTo @@ -296,7 +298,10 @@ class SearchBarUI extends Component { } }; - public onQueryBarChange = (queryAndDateRange: { dateRange: TimeRange; query?: Query }) => { + public onQueryBarChange = (queryAndDateRange: { + dateRange: TimeRange; + query?: Query | AggregateQuery; + }) => { this.setState({ query: queryAndDateRange.query, dateRangeFrom: queryAndDateRange.dateRange.from, @@ -349,7 +354,10 @@ class SearchBarUI extends Component { ); }; - public onQueryBarSubmit = (queryAndDateRange: { dateRange?: TimeRange; query?: Query }) => { + public onQueryBarSubmit = (queryAndDateRange: { + dateRange?: TimeRange; + query?: Query | AggregateQuery; + }) => { this.setState( { query: queryAndDateRange.query, @@ -438,7 +446,7 @@ class SearchBarUI extends Component { const queryBarMenu = ( { onFiltersUpdated={this.props.onFiltersUpdated} filters={this.props.filters} hiddenPanelOptions={this.props.hiddenFilterPanelOptions} - query={this.state.query} + query={this.state.query as Query} savedQuery={this.props.savedQuery} onClearSavedQuery={this.props.onClearSavedQuery} showQueryInput={this.props.showQueryInput} @@ -547,7 +555,9 @@ class SearchBarUI extends Component { private hasFiltersOrQuery() { const hasFilters = Boolean(this.props.filters && this.props.filters.length > 0); - const hasQuery = Boolean(this.state.query && this.state.query.query); + const hasQuery = Boolean( + this.state.query && isOfQueryType(this.state.query) && this.state.query.query + ); return hasFilters || hasQuery; } From 41c90970d55f545ce655991afb05d77d705237fb Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Tue, 28 Jun 2022 15:30:57 +0300 Subject: [PATCH 042/115] Further fixes --- .../server/query/route_handler_context.ts | 19 ++++++-- .../public/search_bar/search_bar.tsx | 46 +++++++++++-------- .../search_selection/search_selection.tsx | 9 +++- 3 files changed, 50 insertions(+), 24 deletions(-) diff --git a/src/plugins/data/server/query/route_handler_context.ts b/src/plugins/data/server/query/route_handler_context.ts index a79063014abc0..bbc51fba08e72 100644 --- a/src/plugins/data/server/query/route_handler_context.ts +++ b/src/plugins/data/server/query/route_handler_context.ts @@ -7,17 +7,21 @@ */ import { CustomRequestHandlerContext, RequestHandlerContext, SavedObject } from '@kbn/core/server'; -import { isFilters } from '@kbn/es-query'; +import { isFilters, Query, AggregateQuery } from '@kbn/es-query'; import { isQuery, SavedQueryAttributes } from '../../common'; import { extract, inject } from '../../common/query/filters/persistable_state'; +function isOfQueryType(arg: Query | AggregateQuery): arg is Query { + return Boolean(arg && 'query' in arg); +} + function injectReferences({ id, attributes, references, }: Pick, 'id' | 'attributes' | 'references'>) { const { query } = attributes; - if (typeof query.query === 'string') { + if (isOfQueryType(query) && typeof query.query === 'string') { try { const parsed = JSON.parse(query.query); query.query = parsed instanceof Object ? parsed : query.query; @@ -37,13 +41,22 @@ function extractReferences({ timefilter, }: SavedQueryAttributes) { const { state: extractedFilters, references } = extract(filters); + const isOfQueryTypeQuery = isOfQueryType(query); + let queryString = ''; + if (isOfQueryTypeQuery) { + if (typeof query.query === 'string') { + queryString = query.query; + } else { + queryString = JSON.stringify(query.query); + } + } const attributes: SavedQueryAttributes = { title: title.trim(), description: description.trim(), query: { ...query, - query: typeof query.query === 'string' ? query.query : JSON.stringify(query.query), + query: queryString, }, filters: extractedFilters, ...(timefilter && { timefilter }), diff --git a/src/plugins/unified_search/public/search_bar/search_bar.tsx b/src/plugins/unified_search/public/search_bar/search_bar.tsx index 3cbd1dbac1fe5..fd692cf138898 100644 --- a/src/plugins/unified_search/public/search_bar/search_bar.tsx +++ b/src/plugins/unified_search/public/search_bar/search_bar.tsx @@ -110,7 +110,7 @@ interface State { dateRangeTo: string; } -function isOfQueryType(arg: any): arg is Query { +function isOfQueryType(arg: Query | AggregateQuery): arg is Query { return Boolean(arg && 'query' in arg); } @@ -132,23 +132,27 @@ class SearchBarUI extends Component { } let nextQuery = null; - if (isOfQueryType(nextProps.query) && isOfQueryType(prevState.query)) { - if (nextProps.query && nextProps.query.query !== get(prevState, 'currentProps.query.query')) { - nextQuery = { - query: nextProps.query.query, - language: nextProps.query.language, - }; - } else if ( - nextProps.query && - prevState.query && - nextProps.query.language !== prevState.query.language - ) { - nextQuery = { - query: '', - language: nextProps.query.language, - }; - } - } else { + if ( + nextProps.query && + isOfQueryType(nextProps.query) && + nextProps.query.query !== get(prevState, 'currentProps.query.query') + ) { + nextQuery = { + query: nextProps.query.query, + language: nextProps.query.language, + }; + } else if ( + nextProps.query && + prevState.query && + isOfQueryType(nextProps.query) && + isOfQueryType(prevState.query) && + nextProps.query.language !== prevState.query.language + ) { + nextQuery = { + query: '', + language: nextProps.query.language, + }; + } else if (nextProps.query && !isOfQueryType(nextProps.query)) { nextQuery = nextProps.query; } @@ -446,7 +450,11 @@ class SearchBarUI extends Component { const queryBarMenu = ( void; } +interface SavedSearchesAttributes extends SavedObjectAttributes { + isTextBasedQuery: boolean; +} export class SearchSelection extends React.Component { private fixedPageSize: number = 8; @@ -67,8 +71,9 @@ export class SearchSelection extends React.Component { } ), includeFields: ['isTextBasedQuery'], - showSavedObject: (savedObject: any) => { - return !savedObject.attributes.isTextBasedQuery; + showSavedObject: (savedObject) => { + const so = savedObject as unknown as SimpleSavedObject; + return !so.attributes.isTextBasedQuery; }, }, { From cda9fcebc708bf710e3e5c3991488b519afd2ee3 Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Tue, 28 Jun 2022 15:41:34 +0300 Subject: [PATCH 043/115] More fixes --- .../server/query/route_handler_context.test.ts | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/plugins/data/server/query/route_handler_context.test.ts b/src/plugins/data/server/query/route_handler_context.test.ts index 33d4597ecff0b..1c274fcfc3953 100644 --- a/src/plugins/data/server/query/route_handler_context.test.ts +++ b/src/plugins/data/server/query/route_handler_context.test.ts @@ -7,7 +7,7 @@ */ import { coreMock } from '@kbn/core/server/mocks'; -import { FilterStateStore } from '@kbn/es-query'; +import { FilterStateStore, Query } from '@kbn/es-query'; import { DATA_VIEW_SAVED_OBJECT_TYPE } from '../../common'; import type { SavedObject, SavedQueryAttributes } from '../../common'; import { registerSavedQueryRouteHandlerContext } from './route_handler_context'; @@ -438,7 +438,8 @@ describe('saved query route handler context', () => { }); const response = await context.get('food'); - expect(response.attributes.query.query).toEqual({ x: 'y' }); + const query = response.attributes.query as Query; + expect(query.query).toEqual({ x: 'y' }); }); it('should handle null string', async () => { @@ -460,7 +461,8 @@ describe('saved query route handler context', () => { }); const response = await context.get('food'); - expect(response.attributes.query.query).toEqual('null'); + const query = response.attributes.query as Query; + expect(query.query).toEqual('null'); }); it('should handle null quoted string', async () => { @@ -482,7 +484,8 @@ describe('saved query route handler context', () => { }); const response = await context.get('food'); - expect(response.attributes.query.query).toEqual('"null"'); + const query = response.attributes.query as Query; + expect(query.query).toEqual('"null"'); }); it('should not lose quotes', async () => { @@ -504,7 +507,8 @@ describe('saved query route handler context', () => { }); const response = await context.get('food'); - expect(response.attributes.query.query).toEqual('"Bob"'); + const query = response.attributes.query as Query; + expect(query.query).toEqual('"Bob"'); }); it('should inject references', async () => { From 149e1e3dccb4f9b18d6776330c7d3108439fb3a0 Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Tue, 28 Jun 2022 15:57:13 +0300 Subject: [PATCH 044/115] More fixes --- .../main/components/top_nav/discover_topnav.tsx | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/plugins/discover/public/application/main/components/top_nav/discover_topnav.tsx b/src/plugins/discover/public/application/main/components/top_nav/discover_topnav.tsx index 573e5aaa19679..3b84af40a17a3 100644 --- a/src/plugins/discover/public/application/main/components/top_nav/discover_topnav.tsx +++ b/src/plugins/discover/public/application/main/components/top_nav/discover_topnav.tsx @@ -7,7 +7,7 @@ */ import React, { useCallback, useMemo, useRef, useEffect } from 'react'; import { useHistory } from 'react-router-dom'; -import type { Query, TimeRange } from '@kbn/es-query'; +import type { Query, TimeRange, AggregateQuery } from '@kbn/es-query'; import { DataViewType } from '@kbn/data-views-plugin/public'; import type { DataViewPickerProps } from '@kbn/unified-search-plugin/public'; import { useDiscoverServices } from '../../../../hooks/use_discover_services'; @@ -24,7 +24,10 @@ export type DiscoverTopNavProps = Pick< onOpenInspector: () => void; query?: Query; savedQuery?: string; - updateQuery: (payload: { dateRange: TimeRange; query?: Query }, isUpdate?: boolean) => void; + updateQuery: ( + payload: { dateRange: TimeRange; query?: Query | AggregateQuery }, + isUpdate?: boolean + ) => void; stateContainer: GetStateReturn; resetSavedSearch: () => void; onChangeIndexPattern: (indexPattern: string) => void; From a0a091b4ea5ce0083395937934d47c6c6d47496d Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Tue, 28 Jun 2022 16:17:47 +0300 Subject: [PATCH 045/115] Fix visualize types --- src/plugins/visualizations/public/visualize_app/types.ts | 4 ++-- .../visualizations/public/visualize_app/utils/utils.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/plugins/visualizations/public/visualize_app/types.ts b/src/plugins/visualizations/public/visualize_app/types.ts index 14157a6493183..cf46e0fc4ff1e 100644 --- a/src/plugins/visualizations/public/visualize_app/types.ts +++ b/src/plugins/visualizations/public/visualize_app/types.ts @@ -28,7 +28,7 @@ import type { import type { DataViewEditorStart } from '@kbn/data-view-editor-plugin/public'; import type { NavigationPublicPluginStart as NavigationStart } from '@kbn/navigation-plugin/public'; -import type { Filter, Query, TimeRange, AggregateQuery } from '@kbn/es-query'; +import type { Filter, Query, TimeRange } from '@kbn/es-query'; import type { DataPublicPluginStart } from '@kbn/data-plugin/public'; import type { DataViewsPublicPluginStart } from '@kbn/data-views-plugin/public'; import type { SharePluginStart } from '@kbn/share-plugin/public'; @@ -56,7 +56,7 @@ export interface VisualizeAppState { filters: Filter[]; uiState: SerializableRecord; vis: SavedVisState; - query: Query | AggregateQuery; + query: Query; savedQuery?: string; linked: boolean; } diff --git a/src/plugins/visualizations/public/visualize_app/utils/utils.ts b/src/plugins/visualizations/public/visualize_app/utils/utils.ts index 70ffae134aacf..75b1543b1a2a2 100644 --- a/src/plugins/visualizations/public/visualize_app/utils/utils.ts +++ b/src/plugins/visualizations/public/visualize_app/utils/utils.ts @@ -9,7 +9,7 @@ import { i18n } from '@kbn/i18n'; import type { History } from 'history'; import type { ChromeStart, DocLinksStart } from '@kbn/core/public'; -import type { Filter } from '@kbn/es-query'; +import type { Filter, Query } from '@kbn/es-query'; import { redirectWhenMissing } from '@kbn/kibana-utils-plugin/public'; import type { DataView } from '@kbn/data-views-plugin/public'; import { VisualizeConstants } from '../../../common/constants'; @@ -56,7 +56,7 @@ export const visStateToEditorState = ( return { uiState: savedVis && savedVis.uiStateJSON ? JSON.parse(savedVis.uiStateJSON) : vis.uiState.toJSON(), - query: vis.data.searchSource?.getOwnField('query') || getDefaultQuery(services), + query: (vis.data.searchSource?.getOwnField('query') || getDefaultQuery(services)) as Query, filters: (vis.data.searchSource?.getOwnField('filter') as Filter[]) || [], vis: { ...savedVisState.visState, title: vis.title }, linked: savedVis && savedVis.id ? !!savedVis.savedSearchId : !!savedVisState.savedSearchId, From b4f71cd4c7b55c9baaacad6e1bd7b7d2e11f481d Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Tue, 28 Jun 2022 16:32:53 +0300 Subject: [PATCH 046/115] more --- src/plugins/data/server/query/route_handler_context.ts | 2 +- .../public/visualize_app/utils/use/use_editor_updates.ts | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/plugins/data/server/query/route_handler_context.ts b/src/plugins/data/server/query/route_handler_context.ts index bbc51fba08e72..162fb31f8a377 100644 --- a/src/plugins/data/server/query/route_handler_context.ts +++ b/src/plugins/data/server/query/route_handler_context.ts @@ -56,7 +56,7 @@ function extractReferences({ description: description.trim(), query: { ...query, - query: queryString, + ...(queryString && { query: queryString }), }, filters: extractedFilters, ...(timefilter && { timefilter }), diff --git a/src/plugins/visualizations/public/visualize_app/utils/use/use_editor_updates.ts b/src/plugins/visualizations/public/visualize_app/utils/use/use_editor_updates.ts index a8a6fa3eea2a4..4f7245ed436d7 100644 --- a/src/plugins/visualizations/public/visualize_app/utils/use/use_editor_updates.ts +++ b/src/plugins/visualizations/public/visualize_app/utils/use/use_editor_updates.ts @@ -9,7 +9,7 @@ import { useEffect, useState } from 'react'; import { isEqual } from 'lodash'; import { EventEmitter } from 'events'; - +import { Query } from '@kbn/es-query'; import { VisualizeServices, VisualizeAppState, @@ -51,7 +51,7 @@ export const useEditorUpdates = ( uiState: vis.uiState, timeRange: timefilter.getTime(), filters: filterManager.getFilters(), - query: queryString.getQuery(), + query: queryString.getQuery() as Query, linked: !!vis.data.savedSearchId, savedSearch, }); @@ -59,7 +59,7 @@ export const useEditorUpdates = ( embeddableHandler.updateInput({ timeRange: timefilter.getTime(), filters: filterManager.getFilters(), - query: queryString.getQuery(), + query: queryString.getQuery() as Query, searchSessionId: services.data.search.session.getSessionId(), }); } From 8b18e8183886b6e79b699074b8603468c65f8659 Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Tue, 28 Jun 2022 16:36:34 +0300 Subject: [PATCH 047/115] More fixes --- .../field_editor/components/scripting_help/test_script.tsx | 7 ++++--- .../visualize_app/utils/use/use_linked_search_updates.ts | 4 ++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/plugins/data_view_management/public/components/field_editor/components/scripting_help/test_script.tsx b/src/plugins/data_view_management/public/components/field_editor/components/scripting_help/test_script.tsx index 0eb0898f41b60..d3d682412b473 100644 --- a/src/plugins/data_view_management/public/components/field_editor/components/scripting_help/test_script.tsx +++ b/src/plugins/data_view_management/public/components/field_editor/components/scripting_help/test_script.tsx @@ -22,7 +22,7 @@ import { import { FormattedMessage } from '@kbn/i18n-react'; import { i18n } from '@kbn/i18n'; -import { Query, buildEsQuery } from '@kbn/es-query'; +import { Query, buildEsQuery, AggregateQuery } from '@kbn/es-query'; import { getEsQueryConfig } from '@kbn/data-plugin/public'; import { DataView } from '@kbn/data-views-plugin/public'; import { context as contextType } from '@kbn/kibana-react-plugin/public'; @@ -69,7 +69,7 @@ export class TestScript extends Component { } } - previewScript = async (searchContext?: { query?: Query | undefined }) => { + previewScript = async (searchContext?: { query?: Query | AggregateQuery | undefined }) => { const { indexPattern, name, script, executeScript } = this.props; if (!script || script.length === 0) { @@ -83,7 +83,8 @@ export class TestScript extends Component { let query; if (searchContext) { const esQueryConfigs = getEsQueryConfig(this.context.services.uiSettings); - query = buildEsQuery(this.props.indexPattern, searchContext.query || [], [], esQueryConfigs); + const searchContextQuery = searchContext.query as Query; + query = buildEsQuery(this.props.indexPattern, searchContextQuery || [], [], esQueryConfigs); } const scriptResponse = await executeScript({ diff --git a/src/plugins/visualizations/public/visualize_app/utils/use/use_linked_search_updates.ts b/src/plugins/visualizations/public/visualize_app/utils/use/use_linked_search_updates.ts index 84953ff0cd90e..3c8ffaa39f86d 100644 --- a/src/plugins/visualizations/public/visualize_app/utils/use/use_linked_search_updates.ts +++ b/src/plugins/visualizations/public/visualize_app/utils/use/use_linked_search_updates.ts @@ -10,7 +10,7 @@ import { useEffect } from 'react'; import { i18n } from '@kbn/i18n'; import { EventEmitter } from 'events'; -import { Filter } from '@kbn/es-query'; +import { Filter, Query } from '@kbn/es-query'; import { VisualizeServices, VisualizeAppStateContainer, @@ -38,7 +38,7 @@ export const useLinkedSearchUpdates = ( searchSource.setParent(searchSourceGrandparent); appState.transitions.unlinkSavedSearch({ - query: searchSourceParent?.getField('query'), + query: searchSourceParent?.getField('query') as Query, parentFilters: (searchSourceParent?.getOwnField('filter') as Filter[]) || [], }); From 933a3c13e64be803a01e796ced06c1581f17418c Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Tue, 28 Jun 2022 17:03:27 +0300 Subject: [PATCH 048/115] Fixes more types --- .../lib/sync_dashboard_container_input.ts | 4 ++-- .../application/lib/sync_dashboard_filter_state.ts | 6 +++--- .../application/state/dashboard_state_slice.ts | 9 ++++++--- src/plugins/dashboard/public/locator.ts | 4 ++-- src/plugins/dashboard/public/services/data.ts | 2 +- src/plugins/dashboard/public/types.ts | 6 +++--- .../query/query_string/query_string_manager.ts | 2 +- src/plugins/data/public/query/saved_query/types.ts | 4 ++-- .../edit_layer_panel/filter_editor/filter_editor.tsx | 8 ++++---- .../join_editor/resources/where_expression.tsx | 7 ++++--- x-pack/plugins/maps/public/locators.ts | 12 +++++++++--- .../maps/public/routes/map_page/map_app/map_app.tsx | 12 +++++++++--- .../routes/map_page/url_state/app_state_manager.ts | 6 +++--- 13 files changed, 49 insertions(+), 33 deletions(-) diff --git a/src/plugins/dashboard/public/application/lib/sync_dashboard_container_input.ts b/src/plugins/dashboard/public/application/lib/sync_dashboard_container_input.ts index 02f939ef69eea..023ff198046ad 100644 --- a/src/plugins/dashboard/public/application/lib/sync_dashboard_container_input.ts +++ b/src/plugins/dashboard/public/application/lib/sync_dashboard_container_input.ts @@ -13,7 +13,7 @@ import { debounceTime, tap } from 'rxjs/operators'; import { compareFilters, COMPARE_ALL_OPTIONS, type Filter } from '@kbn/es-query'; import { replaceUrlHashQuery } from '@kbn/kibana-utils-plugin/public'; import { DashboardContainer } from '../embeddable'; -import { Query } from '../../services/data'; +import type { Query, AggregateQuery } from '../../services/data'; import { DashboardConstants, DashboardSavedObject } from '../..'; import { setControlGroupState, @@ -41,7 +41,7 @@ type ApplyStateChangesToContainerProps = SyncDashboardContainerCommon & { }; type ApplyContainerChangesToStateProps = SyncDashboardContainerCommon & { - applyFilters: (query: Query, filters: Filter[]) => void; + applyFilters: (query: Query | AggregateQuery, filters: Filter[]) => void; }; type SyncDashboardContainerProps = SyncDashboardContainerCommon & ApplyContainerChangesToStateProps; diff --git a/src/plugins/dashboard/public/application/lib/sync_dashboard_filter_state.ts b/src/plugins/dashboard/public/application/lib/sync_dashboard_filter_state.ts index 94c9d996499c3..116e83163f67a 100644 --- a/src/plugins/dashboard/public/application/lib/sync_dashboard_filter_state.ts +++ b/src/plugins/dashboard/public/application/lib/sync_dashboard_filter_state.ts @@ -9,7 +9,6 @@ import _ from 'lodash'; import { merge } from 'rxjs'; import { debounceTime, finalize, map, switchMap, tap } from 'rxjs/operators'; - import { setQuery } from '../state'; import { DashboardBuildContext, DashboardState } from '../../types'; import { DashboardSavedObject } from '../../saved_dashboards'; @@ -18,6 +17,7 @@ import { syncQueryStateWithUrl, connectToQueryState, Filter, + AggregateQuery, Query, waitUntilNextSessionCompletes$, GlobalQueryStateFromUrl, @@ -57,7 +57,7 @@ export const syncDashboardFilterState = ({ }); // this callback will be used any time new filters and query need to be applied. - const applyFilters = (query: Query, filters: Filter[]) => { + const applyFilters = (query: Query | AggregateQuery, filters: Filter[]) => { savedDashboard.searchSource.setField('query', query); savedDashboard.searchSource.setField('filter', filters); dispatchDashboardStateChange(setQuery(query)); @@ -70,7 +70,7 @@ export const syncDashboardFilterState = ({ ); // starts syncing app filters between dashboard state and filterManager - const intermediateFilterState: { filters: Filter[]; query: Query } = { + const intermediateFilterState: { filters: Filter[]; query: Query | AggregateQuery } = { query: initialDashboardState.query ?? queryString.getDefaultQuery(), filters: initialDashboardState.filters ?? [], }; diff --git a/src/plugins/dashboard/public/application/state/dashboard_state_slice.ts b/src/plugins/dashboard/public/application/state/dashboard_state_slice.ts index 50ba66d1b781f..a9dbef27b693e 100644 --- a/src/plugins/dashboard/public/application/state/dashboard_state_slice.ts +++ b/src/plugins/dashboard/public/application/state/dashboard_state_slice.ts @@ -9,7 +9,7 @@ import { createSlice, PayloadAction } from '@reduxjs/toolkit'; import { PersistableControlGroupInput } from '@kbn/controls-plugin/common'; -import { Filter, Query, TimeRange } from '../../services/data'; +import { Filter, Query, TimeRange, AggregateQuery } from '../../services/data'; import { ViewMode } from '../../services/embeddable'; import { DashboardOptions, DashboardPanelMap, DashboardState } from '../../types'; @@ -84,7 +84,10 @@ export const dashboardStateSlice = createSlice({ setViewMode: (state, action: PayloadAction) => { state.viewMode = action.payload; }, - setFiltersAndQuery: (state, action: PayloadAction<{ filters: Filter[]; query: Query }>) => { + setFiltersAndQuery: ( + state, + action: PayloadAction<{ filters: Filter[]; query: Query | AggregateQuery }> + ) => { state.filters = action.payload.filters; state.query = action.payload.query; }, @@ -97,7 +100,7 @@ export const dashboardStateSlice = createSlice({ setTitle: (state, action: PayloadAction) => { state.description = action.payload; }, - setQuery: (state, action: PayloadAction) => { + setQuery: (state, action: PayloadAction) => { state.query = action.payload; }, }, diff --git a/src/plugins/dashboard/public/locator.ts b/src/plugins/dashboard/public/locator.ts index 240723ca47bce..de066da339a16 100644 --- a/src/plugins/dashboard/public/locator.ts +++ b/src/plugins/dashboard/public/locator.ts @@ -8,7 +8,7 @@ import type { SerializableRecord } from '@kbn/utility-types'; import { flow } from 'lodash'; -import type { Filter, TimeRange, Query } from '@kbn/es-query'; +import type { Filter, TimeRange, Query, AggregateQuery } from '@kbn/es-query'; import type { GlobalQueryStateFromUrl, RefreshInterval } from '@kbn/data-plugin/public'; import type { LocatorDefinition, LocatorPublic } from '@kbn/share-plugin/public'; import { SerializableControlGroupInput } from '@kbn/controls-plugin/common'; @@ -65,7 +65,7 @@ export type DashboardAppLocatorParams = { * Optionally set a query. NOTE: if given and used in conjunction with `dashboardId`, and the * saved dashboard has a query saved with it, this will _replace_ that query. */ - query?: Query; + query?: Query | AggregateQuery; /** * If not given, will use the uiSettings configuration for `storeInSessionStorage`. useHash determines * whether to hash the data in the url to avoid url length issues. diff --git a/src/plugins/dashboard/public/services/data.ts b/src/plugins/dashboard/public/services/data.ts index 1e0c02cc3d401..48cd2182fd7de 100644 --- a/src/plugins/dashboard/public/services/data.ts +++ b/src/plugins/dashboard/public/services/data.ts @@ -6,5 +6,5 @@ * Side Public License, v 1. */ -export type { Query, TimeRange, Filter } from '@kbn/es-query'; +export type { Query, TimeRange, Filter, AggregateQuery } from '@kbn/es-query'; export * from '@kbn/data-plugin/public'; diff --git a/src/plugins/dashboard/public/types.ts b/src/plugins/dashboard/public/types.ts index 8fceca0b27564..760d66fe82a30 100644 --- a/src/plugins/dashboard/public/types.ts +++ b/src/plugins/dashboard/public/types.ts @@ -31,7 +31,7 @@ import { EmbeddableStart } from './services/embeddable'; import { DashboardSessionStorage } from './application/lib'; import { UsageCollectionSetup } from './services/usage_collection'; import { NavigationPublicPluginStart } from './services/navigation'; -import { Query, RefreshInterval, TimeRange } from './services/data'; +import { Query, RefreshInterval, TimeRange, AggregateQuery } from './services/data'; import { DashboardPanelState, SavedDashboardPanel } from '../common/types'; import { SavedObjectsTaggingApi } from './services/saved_objects_tagging_oss'; import { DataPublicPluginStart, DataViewsContract } from './services/data'; @@ -58,7 +58,7 @@ export interface DashboardPanelMap { * DashboardState contains all pieces of tracked state for an individual dashboard */ export interface DashboardState { - query: Query; + query: Query | AggregateQuery; title: string; tags: string[]; filters: Filter[]; @@ -96,7 +96,7 @@ export interface DashboardContainerInput extends ContainerInput { viewMode: ViewMode; filters: Filter[]; title: string; - query: Query; + query: Query | AggregateQuery; panels: { [panelId: string]: DashboardPanelState; }; diff --git a/src/plugins/data/public/query/query_string/query_string_manager.ts b/src/plugins/data/public/query/query_string/query_string_manager.ts index 360aab94134e9..b259b29130055 100644 --- a/src/plugins/data/public/query/query_string/query_string_manager.ts +++ b/src/plugins/data/public/query/query_string/query_string_manager.ts @@ -39,7 +39,7 @@ export class QueryStringManager { }; } - public formatQuery(query: Query | string | undefined): Query { + public formatQuery(query: Query | AggregateQuery | string | undefined): Query | AggregateQuery { if (!query) { return this.getDefaultQuery(); } else if (typeof query === 'string') { diff --git a/src/plugins/data/public/query/saved_query/types.ts b/src/plugins/data/public/query/saved_query/types.ts index eac7bd3aa30b1..ec6d498193f37 100644 --- a/src/plugins/data/public/query/saved_query/types.ts +++ b/src/plugins/data/public/query/saved_query/types.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import type { TimeRange, Query, Filter } from '@kbn/es-query'; +import type { TimeRange, Query, Filter, AggregateQuery } from '@kbn/es-query'; import { RefreshInterval } from '../..'; export type SavedQueryTimeFilter = TimeRange & { @@ -21,7 +21,7 @@ export interface SavedQuery { export interface SavedQueryAttributes { title: string; description: string; - query: Query; + query: Query | AggregateQuery; filters?: Filter[]; timefilter?: SavedQueryTimeFilter; } diff --git a/x-pack/plugins/maps/public/connected_components/edit_layer_panel/filter_editor/filter_editor.tsx b/x-pack/plugins/maps/public/connected_components/edit_layer_panel/filter_editor/filter_editor.tsx index 9e3edeb1fc255..b758faa95b5b3 100644 --- a/x-pack/plugins/maps/public/connected_components/edit_layer_panel/filter_editor/filter_editor.tsx +++ b/x-pack/plugins/maps/public/connected_components/edit_layer_panel/filter_editor/filter_editor.tsx @@ -6,7 +6,7 @@ */ import React, { Component, Fragment } from 'react'; - +import type { Query, AggregateQuery } from '@kbn/es-query'; import { EuiButton, EuiCodeBlock, @@ -23,7 +23,7 @@ import { import { FormattedMessage } from '@kbn/i18n-react'; import { i18n } from '@kbn/i18n'; -import type { DataView, Query } from '@kbn/data-plugin/common'; +import type { DataView } from '@kbn/data-plugin/common'; import { APP_ID } from '../../../../common/constants'; import { getIndexPatternService, getData, getSearchBar } from '../../../kibana_services'; import { GlobalFilterCheckbox } from '../../../components/global_filter_checkbox'; @@ -33,7 +33,7 @@ import { ForceRefreshCheckbox } from '../../../components/force_refresh_checkbox export interface Props { layer: ILayer; - setLayerQuery: (id: string, query: Query) => void; + setLayerQuery: (id: string, query: Query | AggregateQuery) => void; updateSourceProp: (layerId: string, propName: string, value: unknown) => void; isFeatureEditorOpenForLayer: boolean; } @@ -101,7 +101,7 @@ export class FilterEditor extends Component { this.setState({ isPopoverOpen: false }); }; - _onQueryChange = ({ query }: { query?: Query }) => { + _onQueryChange = ({ query }: { query?: Query | AggregateQuery }) => { if (!query) { return; } diff --git a/x-pack/plugins/maps/public/connected_components/edit_layer_panel/join_editor/resources/where_expression.tsx b/x-pack/plugins/maps/public/connected_components/edit_layer_panel/join_editor/resources/where_expression.tsx index 8ee0728aa8220..e2f2bc70c9460 100644 --- a/x-pack/plugins/maps/public/connected_components/edit_layer_panel/join_editor/resources/where_expression.tsx +++ b/x-pack/plugins/maps/public/connected_components/edit_layer_panel/join_editor/resources/where_expression.tsx @@ -9,13 +9,14 @@ import React, { Component } from 'react'; import { i18n } from '@kbn/i18n'; import { EuiButton, EuiPopover, EuiExpression, EuiFormHelpText } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; -import { DataView, Query } from '@kbn/data-plugin/common'; +import type { Query, AggregateQuery } from '@kbn/es-query'; +import { DataView } from '@kbn/data-plugin/common'; import { APP_ID } from '../../../../../common/constants'; import { getData, getSearchBar } from '../../../../kibana_services'; interface Props { indexPattern: DataView; - onChange: (whereQuery?: Query) => void; + onChange: (whereQuery?: Query | AggregateQuery) => void; whereQuery?: Query; } @@ -40,7 +41,7 @@ export class WhereExpression extends Component { }); }; - _onQueryChange = ({ query }: { query?: Query }) => { + _onQueryChange = ({ query }: { query?: Query | AggregateQuery }) => { this.props.onChange(query); this._closePopover(); }; diff --git a/x-pack/plugins/maps/public/locators.ts b/x-pack/plugins/maps/public/locators.ts index b08c68ec96d61..99531d50ac50a 100644 --- a/x-pack/plugins/maps/public/locators.ts +++ b/x-pack/plugins/maps/public/locators.ts @@ -9,7 +9,13 @@ import rison from 'rison-node'; import type { SerializableRecord } from '@kbn/utility-types'; -import { type Filter, isFilterPinned, type TimeRange, type Query } from '@kbn/es-query'; +import { + type Filter, + isFilterPinned, + type TimeRange, + type Query, + type AggregateQuery, +} from '@kbn/es-query'; import type { GlobalQueryStateFromUrl, RefreshInterval } from '@kbn/data-plugin/public'; import { setStateToKbnUrl } from '@kbn/kibana-utils-plugin/public'; import type { LocatorDefinition, LocatorPublic } from '@kbn/share-plugin/public'; @@ -48,7 +54,7 @@ export interface MapsAppLocatorParams extends SerializableRecord { * Optionally set a query. NOTE: if given and used in conjunction with `mapId`, and the * saved map has a query saved with it, this will _replace_ that query. */ - query?: Query; + query?: Query | AggregateQuery; /** * If not given, will use the uiSettings configuration for `storeInSessionStorage`. useHash determines @@ -74,7 +80,7 @@ export class MapsAppLocatorDefinition implements LocatorDefinition { time, }: { filters?: Filter[]; - query?: Query; + query?: Query | AggregateQuery; time?: TimeRange; }) => { const { filterManager } = getData().query; diff --git a/x-pack/plugins/maps/public/routes/map_page/url_state/app_state_manager.ts b/x-pack/plugins/maps/public/routes/map_page/url_state/app_state_manager.ts index ff4989fd56c77..650333291f12f 100644 --- a/x-pack/plugins/maps/public/routes/map_page/url_state/app_state_manager.ts +++ b/x-pack/plugins/maps/public/routes/map_page/url_state/app_state_manager.ts @@ -7,16 +7,16 @@ import { Subject } from 'rxjs'; import { Filter } from '@kbn/es-query'; -import type { Query } from '@kbn/es-query'; +import type { Query, AggregateQuery } from '@kbn/es-query'; export interface MapsAppState { - query?: Query | null; + query?: Query | AggregateQuery | null; savedQueryId?: string; filters?: Filter[]; } export class AppStateManager { - _query: Query | null = null; + _query: Query | AggregateQuery | null = null; _savedQueryId: string = ''; _filters: Filter[] = []; From 56b68907340171781eb5c6ff7f3c381804abc45c Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Tue, 28 Jun 2022 17:17:39 +0300 Subject: [PATCH 049/115] Fix dashboard types --- .../public/application/embeddable/dashboard_container.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/plugins/dashboard/public/application/embeddable/dashboard_container.tsx b/src/plugins/dashboard/public/application/embeddable/dashboard_container.tsx index 39a7f8f0d152e..a94c810409e32 100644 --- a/src/plugins/dashboard/public/application/embeddable/dashboard_container.tsx +++ b/src/plugins/dashboard/public/application/embeddable/dashboard_container.tsx @@ -15,7 +15,7 @@ import { Start as InspectorStartContract } from '@kbn/inspector-plugin/public'; import { ControlGroupContainer } from '@kbn/controls-plugin/public'; import { UiActionsStart } from '../../services/ui_actions'; -import { RefreshInterval, TimeRange, Query, Filter } from '../../services/data'; +import { RefreshInterval, TimeRange, Query, Filter, AggregateQuery } from '../../services/data'; import { ViewMode, Container, @@ -70,7 +70,7 @@ interface IndexSignature { export interface InheritedChildInput extends IndexSignature { filters: Filter[]; - query: Query; + query: Query | AggregateQuery; timeRange: TimeRange; refreshConfig?: RefreshInterval; viewMode: ViewMode; From 57917720abdb6b710438aca1695f4499bfba7256 Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Tue, 28 Jun 2022 17:19:58 +0300 Subject: [PATCH 050/115] Fix dashboard types --- src/plugins/controls/common/types.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/plugins/controls/common/types.ts b/src/plugins/controls/common/types.ts index e98a46ff7a1af..be9707406a7ff 100644 --- a/src/plugins/controls/common/types.ts +++ b/src/plugins/controls/common/types.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import type { Filter, Query, TimeRange } from '@kbn/es-query'; +import type { Filter, Query, TimeRange, AggregateQuery } from '@kbn/es-query'; import { EmbeddableInput } from '@kbn/embeddable-plugin/common/types'; export type ControlWidth = 'small' | 'medium' | 'large'; @@ -20,7 +20,7 @@ export interface ParentIgnoreSettings { } export type ControlInput = EmbeddableInput & { - query?: Query; + query?: Query | AggregateQuery; filters?: Filter[]; timeRange?: TimeRange; controlStyle?: ControlStyle; From cd9691195e10794ddf06ed1d08aec55495497c35 Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Tue, 28 Jun 2022 17:38:55 +0300 Subject: [PATCH 051/115] Controls plugin types --- .../controls/common/control_types/options_list/types.ts | 4 ++-- .../control_types/range_slider/range_slider_embeddable.tsx | 3 ++- src/plugins/controls/public/services/kibana/options_list.ts | 4 ++-- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/plugins/controls/common/control_types/options_list/types.ts b/src/plugins/controls/common/control_types/options_list/types.ts index 4b805210f37d3..e0290aa8cb9f0 100644 --- a/src/plugins/controls/common/control_types/options_list/types.ts +++ b/src/plugins/controls/common/control_types/options_list/types.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import type { Filter, Query, BoolQuery, TimeRange } from '@kbn/es-query'; +import type { Filter, Query, BoolQuery, TimeRange, AggregateQuery } from '@kbn/es-query'; import { FieldSpec, DataView, DataViewField } from '@kbn/data-views-plugin/common'; import { DataControlInput } from '../../types'; @@ -47,7 +47,7 @@ export type OptionsListRequest = Omit< runPastTimeout?: boolean; dataView: DataView; filters?: Filter[]; - query?: Query; + query?: Query | AggregateQuery; }; /** diff --git a/src/plugins/controls/public/control_types/range_slider/range_slider_embeddable.tsx b/src/plugins/controls/public/control_types/range_slider/range_slider_embeddable.tsx index 2a5ccfc34c0ac..de215ba82da49 100644 --- a/src/plugins/controls/public/control_types/range_slider/range_slider_embeddable.tsx +++ b/src/plugins/controls/public/control_types/range_slider/range_slider_embeddable.tsx @@ -14,6 +14,7 @@ import { RangeFilterParams, Filter, Query, + AggregateQuery, } from '@kbn/es-query'; import React from 'react'; import ReactDOM from 'react-dom'; @@ -246,7 +247,7 @@ export class RangeSliderEmbeddable extends Embeddable { const searchSource = await this.dataService.searchSource.create(); searchSource.setField('size', 0); diff --git a/src/plugins/controls/public/services/kibana/options_list.ts b/src/plugins/controls/public/services/kibana/options_list.ts index aa3f28e24ab16..515acf5d7d24e 100644 --- a/src/plugins/controls/public/services/kibana/options_list.ts +++ b/src/plugins/controls/public/services/kibana/options_list.ts @@ -9,7 +9,7 @@ import { memoize } from 'lodash'; import dateMath from '@kbn/datemath'; -import { buildEsQuery, type TimeRange } from '@kbn/es-query'; +import { buildEsQuery, type TimeRange, type Query } from '@kbn/es-query'; import { KibanaPluginServiceFactory } from '@kbn/presentation-util-plugin/public'; import { @@ -81,7 +81,7 @@ class OptionsListService implements ControlsOptionsListService { const { query, filters, dataView, timeRange, field, ...passThroughProps } = request; const timeFilter = timeRange ? timeService.createFilter(dataView, timeRange) : undefined; const filtersToUse = [...(filters ?? []), ...(timeFilter ? [timeFilter] : [])]; - const esFilters = [buildEsQuery(dataView, query ?? [], filtersToUse ?? [])]; + const esFilters = [buildEsQuery(dataView, (query as Query) ?? [], filtersToUse ?? [])]; return { ...passThroughProps, filters: esFilters, From a15d6df97641915a36797c1720a387560ba7f4cf Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Tue, 28 Jun 2022 18:11:56 +0300 Subject: [PATCH 052/115] Fix Lens types --- .../common/search/expressions/kibana_context_type.ts | 4 ++-- .../lens/public/app_plugin/show_underlying_data.ts | 10 ++++++++-- x-pack/plugins/lens/public/embeddable/embeddable.tsx | 2 +- .../operations/definitions/terms/helpers.ts | 4 ++-- .../lens/public/persistence/saved_object_store.ts | 4 ++-- x-pack/plugins/lens/public/state_management/types.ts | 4 ++-- x-pack/plugins/lens/public/types.ts | 6 +++--- 7 files changed, 20 insertions(+), 14 deletions(-) diff --git a/src/plugins/data/common/search/expressions/kibana_context_type.ts b/src/plugins/data/common/search/expressions/kibana_context_type.ts index 53a443166a57f..b4697eb6b681d 100644 --- a/src/plugins/data/common/search/expressions/kibana_context_type.ts +++ b/src/plugins/data/common/search/expressions/kibana_context_type.ts @@ -5,7 +5,7 @@ * in compliance with, at your election, the Elastic License 2.0 or the Server * Side Public License, v 1. */ -import { Filter } from '@kbn/es-query'; +import { Filter, AggregateQuery } from '@kbn/es-query'; import { ExpressionValueBoxed, ExpressionValueFilter } from '@kbn/expressions-plugin/common'; import { Query, TimeRange } from '../../query'; import { adaptToExpressionValueFilter, DataViewField } from '../..'; @@ -13,7 +13,7 @@ import { adaptToExpressionValueFilter, DataViewField } from '../..'; // eslint-disable-next-line @typescript-eslint/consistent-type-definitions export type ExecutionContextSearch = { filters?: Filter[]; - query?: Query | Query[]; + query?: Query | AggregateQuery | Query[]; timeRange?: TimeRange; }; diff --git a/x-pack/plugins/lens/public/app_plugin/show_underlying_data.ts b/x-pack/plugins/lens/public/app_plugin/show_underlying_data.ts index a3900d229363f..78852672745f4 100644 --- a/x-pack/plugins/lens/public/app_plugin/show_underlying_data.ts +++ b/x-pack/plugins/lens/public/app_plugin/show_underlying_data.ts @@ -13,6 +13,7 @@ import { buildEsQuery, FilterStateStore, TimeRange, + AggregateQuery, } from '@kbn/es-query'; import { i18n } from '@kbn/i18n'; import { RecursiveReadonly } from '@kbn/utility-types'; @@ -21,6 +22,10 @@ import { partition } from 'lodash'; import { TableInspectorAdapter } from '../editor_frame_service/types'; import { Datasource } from '../types'; +function isOfQueryType(arg: Query | AggregateQuery): arg is Query { + return Boolean(arg && 'query' in arg); +} + /** * Joins a series of queries. * @@ -153,7 +158,7 @@ type QueryLanguage = 'lucene' | 'kuery'; * extra filter pill. */ export function combineQueryAndFilters( - query: Query | Query[] | undefined, + query: Query | AggregateQuery | Query[] | undefined, filters: Filter[], meta: LayerMetaInfo, dataViews: DataViewBase[] | undefined @@ -167,7 +172,8 @@ export function combineQueryAndFilters( }; const allQueries = Array.isArray(query) ? query : query ? [query] : []; - const nonEmptyQueries = allQueries.filter((q) => Boolean(q.query.trim())); + const queryTypeQueries = allQueries.filter(isOfQueryType); + const nonEmptyQueries = queryTypeQueries.filter((q) => Boolean(q.query.trim())); [queries.lucene, queries.kuery] = partition(nonEmptyQueries, (q) => q.language === 'lucene'); diff --git a/x-pack/plugins/lens/public/embeddable/embeddable.tsx b/x-pack/plugins/lens/public/embeddable/embeddable.tsx index 6c139917cca43..d5c2163d40911 100644 --- a/x-pack/plugins/lens/public/embeddable/embeddable.tsx +++ b/x-pack/plugins/lens/public/embeddable/embeddable.tsx @@ -576,7 +576,7 @@ export class Embeddable const context: ExecutionContextSearch = { timeRange: this.externalSearchContext.timeRange, - query: [this.savedVis.state.query], + query: [this.savedVis.state.query as Query], filters: this.deps.injectFilterReferences( this.savedVis.state.filters, this.savedVis.references diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms/helpers.ts b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms/helpers.ts index a3d749f4308cf..ae7ca3c7c8e41 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms/helpers.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms/helpers.ts @@ -8,7 +8,7 @@ import { i18n } from '@kbn/i18n'; import { uniq } from 'lodash'; import type { CoreStart } from '@kbn/core/public'; -import { buildEsQuery } from '@kbn/es-query'; +import { buildEsQuery, Query } from '@kbn/es-query'; import { getEsQueryConfig } from '@kbn/data-plugin/public'; import { GenericIndexPatternColumn, operationDefinitionMap } from '..'; import { defaultLabel } from '../filters'; @@ -139,7 +139,7 @@ export function getDisallowedTermsMessage( fieldName: fieldNames[0], dslQuery: buildEsQuery( indexPattern, - frame.query, + frame.query as Query, frame.filters, getEsQueryConfig(core.uiSettings) ), diff --git a/x-pack/plugins/lens/public/persistence/saved_object_store.ts b/x-pack/plugins/lens/public/persistence/saved_object_store.ts index ae2c55f66cc51..5ef35dbe4091a 100644 --- a/x-pack/plugins/lens/public/persistence/saved_object_store.ts +++ b/x-pack/plugins/lens/public/persistence/saved_object_store.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { Filter, Query } from '@kbn/es-query'; +import { Filter, Query, AggregateQuery } from '@kbn/es-query'; import { SavedObjectAttributes, SavedObjectsClientContract, @@ -24,7 +24,7 @@ export interface Document { state: { datasourceStates: Record; visualization: unknown; - query: Query; + query: Query | AggregateQuery; globalPalette?: { activePaletteId: string; state?: unknown; diff --git a/x-pack/plugins/lens/public/state_management/types.ts b/x-pack/plugins/lens/public/state_management/types.ts index 4c6f4adc59ff5..41afb2373e99f 100644 --- a/x-pack/plugins/lens/public/state_management/types.ts +++ b/x-pack/plugins/lens/public/state_management/types.ts @@ -7,7 +7,7 @@ import { VisualizeFieldContext } from '@kbn/ui-actions-plugin/public'; import { EmbeddableEditorState } from '@kbn/embeddable-plugin/public'; -import { Filter, Query } from '@kbn/es-query'; +import { Filter, Query, AggregateQuery } from '@kbn/es-query'; import { SavedQuery } from '@kbn/data-plugin/public'; import { Document } from '../persistence'; @@ -47,7 +47,7 @@ export interface LensAppState extends EditorFrameState { isSaveable: boolean; isLoading: boolean; - query: Query; + query: Query | AggregateQuery; filters: Filter[]; savedQuery?: SavedQuery; searchSessionId: string; diff --git a/x-pack/plugins/lens/public/types.ts b/x-pack/plugins/lens/public/types.ts index 770f4bee7eecd..d25f98eac9832 100644 --- a/x-pack/plugins/lens/public/types.ts +++ b/x-pack/plugins/lens/public/types.ts @@ -14,7 +14,7 @@ import type { import type { PaletteOutput } from '@kbn/coloring'; import type { TopNavMenuData } from '@kbn/navigation-plugin/public'; import type { MutableRefObject } from 'react'; -import { Filter, TimeRange } from '@kbn/es-query'; +import { Filter, TimeRange, AggregateQuery } from '@kbn/es-query'; import type { ExpressionAstExpression, ExpressionRendererEvent, @@ -714,7 +714,7 @@ export interface FramePublicAPI { activeData?: Record; } export interface FrameDatasourceAPI extends FramePublicAPI { - query: Query; + query: Query | AggregateQuery; filters: Filter[]; } @@ -1029,7 +1029,7 @@ export type LensTopNavMenuEntryGenerator = (props: { visualizationId: string; datasourceStates: Record; visualizationState: unknown; - query: Query; + query: Query | AggregateQuery; filters: Filter[]; initialContext?: VisualizeFieldContext | VisualizeEditorContext; }) => undefined | TopNavMenuData; From 41b1d9866774e3120c6e31c0c7dc4c00d52b73b1 Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Tue, 28 Jun 2022 18:33:03 +0300 Subject: [PATCH 053/115] Fix data plugin types --- src/plugins/data/common/search/expressions/eql.ts | 4 ++-- src/plugins/data/common/search/expressions/esdsl.ts | 4 ++-- src/plugins/data/common/search/expressions/essql.ts | 4 ++-- .../data/common/search/expressions/kibana_context.ts | 9 ++++++--- .../common/search/expressions/kibana_context_type.ts | 2 +- .../lens/public/app_plugin/show_underlying_data.ts | 2 +- 6 files changed, 14 insertions(+), 11 deletions(-) diff --git a/src/plugins/data/common/search/expressions/eql.ts b/src/plugins/data/common/search/expressions/eql.ts index 893b0e0ad9d06..747c4dd1407e8 100644 --- a/src/plugins/data/common/search/expressions/eql.ts +++ b/src/plugins/data/common/search/expressions/eql.ts @@ -7,7 +7,7 @@ */ import { i18n } from '@kbn/i18n'; -import { buildEsQuery } from '@kbn/es-query'; +import { buildEsQuery, Query } from '@kbn/es-query'; import { ExpressionFunctionDefinition } from '@kbn/expressions-plugin/common'; import { EqlSearchRequest } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; @@ -107,7 +107,7 @@ export const getEqlFn = ({ const esQueryConfigs = getEsQueryConfig(uiSettingsClient as any); const query = buildEsQuery( dataview, - input.query || [], + (input.query as Query) || [], input.filters || [], esQueryConfigs ); diff --git a/src/plugins/data/common/search/expressions/esdsl.ts b/src/plugins/data/common/search/expressions/esdsl.ts index 34a67223b4be5..5cd28a511cb2d 100644 --- a/src/plugins/data/common/search/expressions/esdsl.ts +++ b/src/plugins/data/common/search/expressions/esdsl.ts @@ -7,7 +7,7 @@ */ import { i18n } from '@kbn/i18n'; -import { buildEsQuery } from '@kbn/es-query'; +import { buildEsQuery, Query } from '@kbn/es-query'; import { ExpressionFunctionDefinition } from '@kbn/expressions-plugin/common'; import { lastValueFrom } from 'rxjs'; @@ -85,7 +85,7 @@ export const getEsdslFn = ({ const esQueryConfigs = getEsQueryConfig(uiSettingsClient as any); const query = buildEsQuery( undefined, // args.index, - input.query || [], + (input.query as Query) || [], input.filters || [], esQueryConfigs ); diff --git a/src/plugins/data/common/search/expressions/essql.ts b/src/plugins/data/common/search/expressions/essql.ts index 398b92de490d8..f683bdaa0a5fa 100644 --- a/src/plugins/data/common/search/expressions/essql.ts +++ b/src/plugins/data/common/search/expressions/essql.ts @@ -8,7 +8,7 @@ // eslint-disable-next-line @kbn/eslint/no-restricted-paths import type { KibanaRequest } from '@kbn/core/server'; -import { buildEsQuery } from '@kbn/es-query'; +import { buildEsQuery, Query } from '@kbn/es-query'; import { castEsToKbnFieldTypeName, ES_FIELD_TYPES, KBN_FIELD_TYPES } from '@kbn/field-types'; import { i18n } from '@kbn/i18n'; import { @@ -171,7 +171,7 @@ export const getEssqlFn = ({ getStartDependencies }: EssqlFnArguments) => { params.filter = buildEsQuery( undefined, - input.query || [], + (input.query as Query) || [], [...(input.filters ?? []), ...(timeFilter ? [timeFilter] : [])], esQueryConfigs ); diff --git a/src/plugins/data/common/search/expressions/kibana_context.ts b/src/plugins/data/common/search/expressions/kibana_context.ts index 6183484a57b46..5c29ed23aad76 100644 --- a/src/plugins/data/common/search/expressions/kibana_context.ts +++ b/src/plugins/data/common/search/expressions/kibana_context.ts @@ -11,7 +11,7 @@ import { i18n } from '@kbn/i18n'; import { ExpressionFunctionDefinition, ExecutionContext } from '@kbn/expressions-plugin/common'; import { Adapters } from '@kbn/inspector-plugin/common'; import { Filter } from '@kbn/es-query'; -import { Query, uniqFilters } from '@kbn/es-query'; +import { Query, uniqFilters, AggregateQuery } from '@kbn/es-query'; import { unboxExpressionValue } from '@kbn/expressions-plugin/common'; import { SavedObjectReference } from '@kbn/core/types'; import { SavedObjectsClientCommon } from '@kbn/data-views-plugin/common'; @@ -41,8 +41,11 @@ export type ExpressionFunctionKibanaContext = ExpressionFunctionDefinition< const getParsedValue = (data: any, defaultValue: any) => typeof data === 'string' && data.length ? JSON.parse(data) || defaultValue : defaultValue; -const mergeQueries = (first: Query | Query[] = [], second: Query | Query[]) => - uniqBy( +const mergeQueries = ( + first: Query | AggregateQuery | Array = [], + second: Query | AggregateQuery | Array +) => + uniqBy( [...(Array.isArray(first) ? first : [first]), ...(Array.isArray(second) ? second : [second])], (n: any) => JSON.stringify(n.query) ); diff --git a/src/plugins/data/common/search/expressions/kibana_context_type.ts b/src/plugins/data/common/search/expressions/kibana_context_type.ts index b4697eb6b681d..3c65f4e20a070 100644 --- a/src/plugins/data/common/search/expressions/kibana_context_type.ts +++ b/src/plugins/data/common/search/expressions/kibana_context_type.ts @@ -13,7 +13,7 @@ import { adaptToExpressionValueFilter, DataViewField } from '../..'; // eslint-disable-next-line @typescript-eslint/consistent-type-definitions export type ExecutionContextSearch = { filters?: Filter[]; - query?: Query | AggregateQuery | Query[]; + query?: Query | AggregateQuery | Array; timeRange?: TimeRange; }; diff --git a/x-pack/plugins/lens/public/app_plugin/show_underlying_data.ts b/x-pack/plugins/lens/public/app_plugin/show_underlying_data.ts index 78852672745f4..74ef60f4ae4ea 100644 --- a/x-pack/plugins/lens/public/app_plugin/show_underlying_data.ts +++ b/x-pack/plugins/lens/public/app_plugin/show_underlying_data.ts @@ -158,7 +158,7 @@ type QueryLanguage = 'lucene' | 'kuery'; * extra filter pill. */ export function combineQueryAndFilters( - query: Query | AggregateQuery | Query[] | undefined, + query: Query | AggregateQuery | Array | undefined, filters: Filter[], meta: LayerMetaInfo, dataViews: DataViewBase[] | undefined From 1ec54d4bca623cbba7c0e05a82b9fb8b4fe468bc Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Tue, 28 Jun 2022 18:54:26 +0300 Subject: [PATCH 054/115] Fix types in Lens 2 --- x-pack/plugins/lens/public/types.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/lens/public/types.ts b/x-pack/plugins/lens/public/types.ts index d25f98eac9832..4a302ec3be18e 100644 --- a/x-pack/plugins/lens/public/types.ts +++ b/x-pack/plugins/lens/public/types.ts @@ -398,7 +398,7 @@ export interface DatasourceDataPanelProps { setState: StateSetter; showNoDataPopover: () => void; core: Pick; - query: Query; + query: Query | AggregateQuery; dateRange: DateRange; filters: Filter[]; dropOntoWorkspace: (field: DragDropIdentifier) => void; From 0755330da47730de02811bcc04787501b775b53d Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Tue, 28 Jun 2022 19:10:06 +0300 Subject: [PATCH 055/115] buildEsConfig type fixes --- packages/kbn-es-query/src/es_query/build_es_query.ts | 10 +++++++--- .../controls/public/services/kibana/options_list.ts | 4 ++-- src/plugins/data/common/search/expressions/eql.ts | 4 ++-- src/plugins/data/common/search/expressions/esdsl.ts | 4 ++-- src/plugins/data/common/search/expressions/essql.ts | 4 ++-- src/plugins/data/public/query/query_service.ts | 4 ++-- .../lens/public/indexpattern_datasource/datapanel.tsx | 4 ++-- .../lens/public/indexpattern_datasource/field_item.tsx | 4 ++-- .../indexpattern_datasource/fields_accordion.tsx | 4 ++-- .../operations/definitions/terms/helpers.ts | 4 ++-- 10 files changed, 25 insertions(+), 21 deletions(-) diff --git a/packages/kbn-es-query/src/es_query/build_es_query.ts b/packages/kbn-es-query/src/es_query/build_es_query.ts index 62172fa5aa4d8..6bd6e1ea4438f 100644 --- a/packages/kbn-es-query/src/es_query/build_es_query.ts +++ b/packages/kbn-es-query/src/es_query/build_es_query.ts @@ -11,7 +11,7 @@ import { SerializableRecord } from '@kbn/utility-types'; import { buildQueryFromKuery } from './from_kuery'; import { buildQueryFromFilters } from './from_filters'; import { buildQueryFromLucene } from './from_lucene'; -import { Filter, Query } from '../filters'; +import { Filter, Query, AggregateQuery } from '../filters'; import { BoolQuery, DataViewBase } from './types'; import type { KueryQueryOptions } from '../kuery'; import type { EsQueryFiltersConfig } from './from_filters'; @@ -31,6 +31,9 @@ function removeMatchAll(filters: T[]) { (filter) => !filter || typeof filter !== 'object' || !isEqual(filter, { match_all: {} }) ); } +function isOfQueryType(arg: Query | AggregateQuery): arg is Query { + return Boolean(arg && 'query' in arg); +} /** * @param indexPattern @@ -44,7 +47,7 @@ function removeMatchAll(filters: T[]) { */ export function buildEsQuery( indexPattern: DataViewBase | undefined, - queries: Query | Query[], + queries: Query | AggregateQuery | Array, filters: Filter | Filter[], config: EsQueryConfig = { allowLeadingWildcards: false, @@ -55,7 +58,8 @@ export function buildEsQuery( queries = Array.isArray(queries) ? queries : [queries]; filters = Array.isArray(filters) ? filters : [filters]; - const validQueries = queries.filter((query) => has(query, 'query')); + const isOfQueryTypeQueries = queries.filter(isOfQueryType); + const validQueries = isOfQueryTypeQueries.filter((query) => has(query, 'query')); const queriesByLanguage = groupBy(validQueries, 'language'); const kueryQuery = buildQueryFromKuery( indexPattern, diff --git a/src/plugins/controls/public/services/kibana/options_list.ts b/src/plugins/controls/public/services/kibana/options_list.ts index 515acf5d7d24e..aa3f28e24ab16 100644 --- a/src/plugins/controls/public/services/kibana/options_list.ts +++ b/src/plugins/controls/public/services/kibana/options_list.ts @@ -9,7 +9,7 @@ import { memoize } from 'lodash'; import dateMath from '@kbn/datemath'; -import { buildEsQuery, type TimeRange, type Query } from '@kbn/es-query'; +import { buildEsQuery, type TimeRange } from '@kbn/es-query'; import { KibanaPluginServiceFactory } from '@kbn/presentation-util-plugin/public'; import { @@ -81,7 +81,7 @@ class OptionsListService implements ControlsOptionsListService { const { query, filters, dataView, timeRange, field, ...passThroughProps } = request; const timeFilter = timeRange ? timeService.createFilter(dataView, timeRange) : undefined; const filtersToUse = [...(filters ?? []), ...(timeFilter ? [timeFilter] : [])]; - const esFilters = [buildEsQuery(dataView, (query as Query) ?? [], filtersToUse ?? [])]; + const esFilters = [buildEsQuery(dataView, query ?? [], filtersToUse ?? [])]; return { ...passThroughProps, filters: esFilters, diff --git a/src/plugins/data/common/search/expressions/eql.ts b/src/plugins/data/common/search/expressions/eql.ts index 747c4dd1407e8..893b0e0ad9d06 100644 --- a/src/plugins/data/common/search/expressions/eql.ts +++ b/src/plugins/data/common/search/expressions/eql.ts @@ -7,7 +7,7 @@ */ import { i18n } from '@kbn/i18n'; -import { buildEsQuery, Query } from '@kbn/es-query'; +import { buildEsQuery } from '@kbn/es-query'; import { ExpressionFunctionDefinition } from '@kbn/expressions-plugin/common'; import { EqlSearchRequest } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; @@ -107,7 +107,7 @@ export const getEqlFn = ({ const esQueryConfigs = getEsQueryConfig(uiSettingsClient as any); const query = buildEsQuery( dataview, - (input.query as Query) || [], + input.query || [], input.filters || [], esQueryConfigs ); diff --git a/src/plugins/data/common/search/expressions/esdsl.ts b/src/plugins/data/common/search/expressions/esdsl.ts index 5cd28a511cb2d..34a67223b4be5 100644 --- a/src/plugins/data/common/search/expressions/esdsl.ts +++ b/src/plugins/data/common/search/expressions/esdsl.ts @@ -7,7 +7,7 @@ */ import { i18n } from '@kbn/i18n'; -import { buildEsQuery, Query } from '@kbn/es-query'; +import { buildEsQuery } from '@kbn/es-query'; import { ExpressionFunctionDefinition } from '@kbn/expressions-plugin/common'; import { lastValueFrom } from 'rxjs'; @@ -85,7 +85,7 @@ export const getEsdslFn = ({ const esQueryConfigs = getEsQueryConfig(uiSettingsClient as any); const query = buildEsQuery( undefined, // args.index, - (input.query as Query) || [], + input.query || [], input.filters || [], esQueryConfigs ); diff --git a/src/plugins/data/common/search/expressions/essql.ts b/src/plugins/data/common/search/expressions/essql.ts index f683bdaa0a5fa..398b92de490d8 100644 --- a/src/plugins/data/common/search/expressions/essql.ts +++ b/src/plugins/data/common/search/expressions/essql.ts @@ -8,7 +8,7 @@ // eslint-disable-next-line @kbn/eslint/no-restricted-paths import type { KibanaRequest } from '@kbn/core/server'; -import { buildEsQuery, Query } from '@kbn/es-query'; +import { buildEsQuery } from '@kbn/es-query'; import { castEsToKbnFieldTypeName, ES_FIELD_TYPES, KBN_FIELD_TYPES } from '@kbn/field-types'; import { i18n } from '@kbn/i18n'; import { @@ -171,7 +171,7 @@ export const getEssqlFn = ({ getStartDependencies }: EssqlFnArguments) => { params.filter = buildEsQuery( undefined, - (input.query as Query) || [], + input.query || [], [...(input.filters ?? []), ...(timeFilter ? [timeFilter] : [])], esQueryConfigs ); diff --git a/src/plugins/data/public/query/query_service.ts b/src/plugins/data/public/query/query_service.ts index 9ad8db7de7655..894db102aa041 100644 --- a/src/plugins/data/public/query/query_service.ts +++ b/src/plugins/data/public/query/query_service.ts @@ -10,7 +10,7 @@ import { share } from 'rxjs/operators'; import { HttpStart, IUiSettingsClient } from '@kbn/core/public'; import { PersistableStateService, VersionedState } from '@kbn/kibana-utils-plugin/common'; import { IStorageWrapper } from '@kbn/kibana-utils-plugin/public'; -import { buildEsQuery, TimeRange, Query } from '@kbn/es-query'; +import { buildEsQuery, TimeRange } from '@kbn/es-query'; import type { DataView } from '@kbn/data-views-plugin/common'; import { FilterManager } from './filter_manager'; import { createAddToQueryLog } from './lib'; @@ -125,7 +125,7 @@ export class QueryService implements PersistableStateService { return buildEsQuery( indexPattern, - this.queryStringManager.getQuery() as Query, + this.queryStringManager.getQuery(), [...this.filterManager.getFilters(), ...(timeFilter ? [timeFilter] : [])], getEsQueryConfig(getUiSettings()) ); diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/datapanel.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/datapanel.tsx index 385f92b0dc05d..4bf2a06b6e1ff 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/datapanel.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/datapanel.tsx @@ -22,7 +22,7 @@ import { EuiScreenReaderOnly, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import type { EsQueryConfig, Query, Filter } from '@kbn/es-query'; +import type { EsQueryConfig, Query, Filter, AggregateQuery } from '@kbn/es-query'; import { FormattedMessage } from '@kbn/i18n-react'; import type { CoreStart } from '@kbn/core/public'; import type { DataPublicPluginStart } from '@kbn/data-plugin/public'; @@ -100,7 +100,7 @@ const fieldTypeNames: Record = { // returning a query dsl object not matching anything function buildSafeEsQuery( indexPattern: IndexPattern, - query: Query, + query: Query | AggregateQuery, filters: Filter[], queryConfig: EsQueryConfig ) { diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/field_item.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/field_item.tsx index 5e26e49a8ef17..1fb6f82eec291 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/field_item.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/field_item.tsx @@ -39,7 +39,7 @@ import { i18n } from '@kbn/i18n'; import { FieldButton } from '@kbn/react-field'; import type { FieldFormatsStart } from '@kbn/field-formats-plugin/public'; import { EuiHighlight } from '@elastic/eui'; -import { Filter, buildEsQuery, Query } from '@kbn/es-query'; +import { Filter, buildEsQuery, Query, AggregateQuery } from '@kbn/es-query'; import { KBN_FIELD_TYPES, ES_FIELD_TYPES, getEsQueryConfig } from '@kbn/data-plugin/public'; import { ChartsPluginSetup } from '@kbn/charts-plugin/public'; import { UiActionsStart } from '@kbn/ui-actions-plugin/public'; @@ -61,7 +61,7 @@ export interface FieldItemProps { indexPattern: IndexPattern; highlight?: string; exists: boolean; - query: Query; + query: Query | AggregateQuery; dateRange: DatasourceDataPanelProps['dateRange']; chartsThemeService: ChartsPluginSetup['theme']; filters: Filter[]; diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/fields_accordion.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/fields_accordion.tsx index 33413bf0ba59b..36d4311d05e42 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/fields_accordion.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/fields_accordion.tsx @@ -19,7 +19,7 @@ import { import classNames from 'classnames'; import { FieldFormatsStart } from '@kbn/field-formats-plugin/public'; import { Filter } from '@kbn/es-query'; -import type { Query } from '@kbn/es-query'; +import type { Query, AggregateQuery } from '@kbn/es-query'; import { ChartsPluginSetup } from '@kbn/charts-plugin/public'; import { UiActionsStart } from '@kbn/ui-actions-plugin/public'; import { IndexPatternField } from './types'; @@ -33,7 +33,7 @@ export interface FieldItemSharedProps { chartsThemeService: ChartsPluginSetup['theme']; indexPattern: IndexPattern; highlight?: string; - query: Query; + query: Query | AggregateQuery; dateRange: DatasourceDataPanelProps['dateRange']; filters: Filter[]; } diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms/helpers.ts b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms/helpers.ts index ae7ca3c7c8e41..a3d749f4308cf 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms/helpers.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms/helpers.ts @@ -8,7 +8,7 @@ import { i18n } from '@kbn/i18n'; import { uniq } from 'lodash'; import type { CoreStart } from '@kbn/core/public'; -import { buildEsQuery, Query } from '@kbn/es-query'; +import { buildEsQuery } from '@kbn/es-query'; import { getEsQueryConfig } from '@kbn/data-plugin/public'; import { GenericIndexPatternColumn, operationDefinitionMap } from '..'; import { defaultLabel } from '../filters'; @@ -139,7 +139,7 @@ export function getDisallowedTermsMessage( fieldName: fieldNames[0], dslQuery: buildEsQuery( indexPattern, - frame.query as Query, + frame.query, frame.filters, getEsQueryConfig(core.uiSettings) ), From 9c17d3a03378d3c13b80ff15034d7d356a6f8dbd Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Tue, 28 Jun 2022 19:35:41 +0300 Subject: [PATCH 056/115] Fix observability types --- .../configurations/lens_attributes.test.ts | 5 +++-- .../pages/alerts/components/alerts_search_bar.tsx | 10 +++++++--- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/lens_attributes.test.ts b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/lens_attributes.test.ts index 4de565e5a87e4..9de152c9d8597 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/lens_attributes.test.ts +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/lens_attributes.test.ts @@ -4,7 +4,7 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ - +import type { Query } from '@kbn/es-query'; import { LayerConfig, LensAttributes } from './lens_attributes'; import { mockAppDataView, mockDataView } from '../rtl_helpers'; import { getDefaultConfigs } from './default_configs'; @@ -438,7 +438,8 @@ describe('Lens Attribute', () => { reportViewConfig.reportType, formulaHelper ).getJSON(); - expect(multiSeriesLensAttr.state.query.query).toEqual('transaction.duration.us < 60000000'); + const query = multiSeriesLensAttr.state.query as Query; + expect(query).toEqual('transaction.duration.us < 60000000'); }); describe('Layer breakdowns', function () { diff --git a/x-pack/plugins/observability/public/pages/alerts/components/alerts_search_bar.tsx b/x-pack/plugins/observability/public/pages/alerts/components/alerts_search_bar.tsx index 0798e3afded80..10eb587496a80 100644 --- a/x-pack/plugins/observability/public/pages/alerts/components/alerts_search_bar.tsx +++ b/x-pack/plugins/observability/public/pages/alerts/components/alerts_search_bar.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import { DataViewBase } from '@kbn/es-query'; +import { DataViewBase, Query, AggregateQuery } from '@kbn/es-query'; import React, { useMemo, useState } from 'react'; import { TimeHistory } from '@kbn/data-plugin/public'; import { DataView } from '@kbn/data-views-plugin/public'; @@ -14,6 +14,9 @@ import { Storage } from '@kbn/kibana-utils-plugin/public'; import { translations } from '../../../config'; type QueryLanguageType = 'lucene' | 'kuery'; +function isOfQueryType(arg: Query | AggregateQuery): arg is Query { + return Boolean(arg && 'query' in arg); +} export function AlertsSearchBar({ dynamicIndexPatterns, @@ -58,11 +61,12 @@ export function AlertsSearchBar({ onQueryChange({ dateRange, query }); }} onQuerySubmit={({ dateRange, query: nextQuery }) => { + const nQuery = nextQuery && isOfQueryType(nextQuery) ? nextQuery : undefined; onQueryChange({ dateRange, - query: typeof nextQuery?.query === 'string' ? nextQuery.query : '', + query: typeof nQuery?.query === 'string' ? nQuery.query : '', }); - setQueryLanguage((nextQuery?.language ?? 'kuery') as QueryLanguageType); + setQueryLanguage((nQuery?.language ?? 'kuery') as QueryLanguageType); }} displayStyle="inPage" /> From 47aa9b04628d4db105b9b8dda4feef35f199c4ad Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Tue, 28 Jun 2022 19:47:44 +0300 Subject: [PATCH 057/115] Fix maps types --- x-pack/plugins/maps/public/actions/layer_actions.ts | 4 ++-- x-pack/plugins/maps/public/actions/map_actions.ts | 4 ++-- .../edit_layer_panel/filter_editor/index.ts | 4 ++-- .../edit_layer_panel/join_editor/resources/join.tsx | 3 ++- .../plugins/maps/public/embeddable/map_component.tsx | 4 ++-- .../plugins/maps/public/embeddable/map_embeddable.tsx | 10 ++++++++-- x-pack/plugins/maps/public/embeddable/types.ts | 4 ++-- .../legacy_visualizations/region_map/region_map_fn.ts | 4 ++-- .../region_map/region_map_visualization.tsx | 4 ++-- .../legacy_visualizations/tile_map/tile_map_fn.ts | 4 ++-- .../tile_map/tile_map_visualization.tsx | 4 ++-- .../maps/public/routes/map_page/map_app/index.ts | 4 ++-- 12 files changed, 30 insertions(+), 23 deletions(-) diff --git a/x-pack/plugins/maps/public/actions/layer_actions.ts b/x-pack/plugins/maps/public/actions/layer_actions.ts index fee8754b48d7e..9a83563033639 100644 --- a/x-pack/plugins/maps/public/actions/layer_actions.ts +++ b/x-pack/plugins/maps/public/actions/layer_actions.ts @@ -7,7 +7,7 @@ import { AnyAction, Dispatch } from 'redux'; import { ThunkDispatch } from 'redux-thunk'; -import type { Query } from '@kbn/es-query'; +import type { Query, AggregateQuery } from '@kbn/es-query'; import { Adapters } from '@kbn/inspector-plugin/common/adapters'; import { MapStoreState } from '../reducers/store'; import { @@ -543,7 +543,7 @@ export function updateFittableFlag(id: string, includeInFitToBounds: boolean) { }; } -export function setLayerQuery(id: string, query: Query) { +export function setLayerQuery(id: string, query: Query | AggregateQuery) { return (dispatch: ThunkDispatch) => { dispatch({ type: UPDATE_LAYER_PROP, diff --git a/x-pack/plugins/maps/public/actions/map_actions.ts b/x-pack/plugins/maps/public/actions/map_actions.ts index 8a14bc50dc975..396fa20e41725 100644 --- a/x-pack/plugins/maps/public/actions/map_actions.ts +++ b/x-pack/plugins/maps/public/actions/map_actions.ts @@ -12,7 +12,7 @@ import { ThunkDispatch } from 'redux-thunk'; import turfBboxPolygon from '@turf/bbox-polygon'; import turfBooleanContains from '@turf/boolean-contains'; import { Filter } from '@kbn/es-query'; -import type { Query, TimeRange } from '@kbn/es-query'; +import type { Query, TimeRange, AggregateQuery } from '@kbn/es-query'; import { Geometry, Position } from 'geojson'; import { asyncForEach, asyncMap } from '@kbn/std'; import { DRAW_MODE, DRAW_SHAPE, LAYER_STYLE_TYPE } from '../../common/constants'; @@ -292,7 +292,7 @@ export function setQuery({ clearTimeslice, }: { filters?: Filter[]; - query?: Query; + query?: Query | AggregateQuery; timeFilters?: TimeRange; timeslice?: Timeslice; forceRefresh?: boolean; diff --git a/x-pack/plugins/maps/public/connected_components/edit_layer_panel/filter_editor/index.ts b/x-pack/plugins/maps/public/connected_components/edit_layer_panel/filter_editor/index.ts index 09c9cd5f9482c..41a25029a5b0e 100644 --- a/x-pack/plugins/maps/public/connected_components/edit_layer_panel/filter_editor/index.ts +++ b/x-pack/plugins/maps/public/connected_components/edit_layer_panel/filter_editor/index.ts @@ -8,7 +8,7 @@ import { AnyAction } from 'redux'; import { ThunkDispatch } from 'redux-thunk'; import { connect } from 'react-redux'; -import type { Query } from '@kbn/es-query'; +import type { Query, AggregateQuery } from '@kbn/es-query'; import { FilterEditor } from './filter_editor'; import { getEditState, getSelectedLayer } from '../../../selectors/map_selectors'; import { setLayerQuery, updateSourceProp } from '../../../actions'; @@ -24,7 +24,7 @@ function mapStateToProps(state: MapStoreState) { function mapDispatchToProps(dispatch: ThunkDispatch) { return { - setLayerQuery: (layerId: string, query: Query) => { + setLayerQuery: (layerId: string, query: Query | AggregateQuery) => { dispatch(setLayerQuery(layerId, query)); }, updateSourceProp: (id: string, propName: string, value: unknown) => diff --git a/x-pack/plugins/maps/public/connected_components/edit_layer_panel/join_editor/resources/join.tsx b/x-pack/plugins/maps/public/connected_components/edit_layer_panel/join_editor/resources/join.tsx index bbfc9bbb0f302..301943e8b1dd7 100644 --- a/x-pack/plugins/maps/public/connected_components/edit_layer_panel/join_editor/resources/join.tsx +++ b/x-pack/plugins/maps/public/connected_components/edit_layer_panel/join_editor/resources/join.tsx @@ -9,6 +9,7 @@ import _ from 'lodash'; import React, { Component } from 'react'; import { EuiFlexItem, EuiFlexGroup, EuiButtonIcon } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; +import type { AggregateQuery } from '@kbn/es-query'; import type { DataViewField, DataView, Query } from '@kbn/data-plugin/common'; import { indexPatterns } from '@kbn/data-plugin/public'; import { JoinExpression } from './join_expression'; @@ -149,7 +150,7 @@ export class Join extends Component { }); }; - _onWhereQueryChange = (whereQuery?: Query) => { + _onWhereQueryChange = (whereQuery?: Query | AggregateQuery) => { this.props.onChange({ leftField: this.props.join.leftField, right: { diff --git a/x-pack/plugins/maps/public/embeddable/map_component.tsx b/x-pack/plugins/maps/public/embeddable/map_component.tsx index 4709255927734..9c4f79cc0ebb1 100644 --- a/x-pack/plugins/maps/public/embeddable/map_component.tsx +++ b/x-pack/plugins/maps/public/embeddable/map_component.tsx @@ -9,7 +9,7 @@ import React, { Component, RefObject } from 'react'; import uuid from 'uuid/v4'; import { EuiLoadingChart } from '@elastic/eui'; import type { Filter } from '@kbn/es-query'; -import type { Query, TimeRange } from '@kbn/es-query'; +import type { Query, TimeRange, AggregateQuery } from '@kbn/es-query'; import type { LayerDescriptor, MapCenterAndZoom } from '../../common/descriptor_types'; import type { MapEmbeddableType } from './types'; import type { LazyLoadedMapModules } from '../lazy_load_bundle'; @@ -17,7 +17,7 @@ import { lazyLoadMapModules } from '../lazy_load_bundle'; interface Props { filters?: Filter[]; - query?: Query; + query?: Query | AggregateQuery; timeRange?: TimeRange; getLayerDescriptors: ( mapModules: Pick< diff --git a/x-pack/plugins/maps/public/embeddable/map_embeddable.tsx b/x-pack/plugins/maps/public/embeddable/map_embeddable.tsx index 0b2d2e4f9bbc6..31deaf97ee0b8 100644 --- a/x-pack/plugins/maps/public/embeddable/map_embeddable.tsx +++ b/x-pack/plugins/maps/public/embeddable/map_embeddable.tsx @@ -14,7 +14,13 @@ import { render, unmountComponentAtNode } from 'react-dom'; import { Subscription } from 'rxjs'; import { Unsubscribe } from 'redux'; import { EuiEmptyPrompt } from '@elastic/eui'; -import { type Filter, compareFilters, type TimeRange, type Query } from '@kbn/es-query'; +import { + type Filter, + compareFilters, + type TimeRange, + type Query, + type AggregateQuery, +} from '@kbn/es-query'; import { KibanaThemeProvider } from '@kbn/kibana-react-plugin/public'; import { Embeddable, @@ -110,7 +116,7 @@ export class MapEmbeddable private _prevIsRestore: boolean = false; private _prevMapExtent?: MapExtent; private _prevTimeRange?: TimeRange; - private _prevQuery?: Query; + private _prevQuery?: Query | AggregateQuery; private _prevFilters: Filter[] = []; private _prevSyncColors?: boolean; private _prevSearchSessionId?: string; diff --git a/x-pack/plugins/maps/public/embeddable/types.ts b/x-pack/plugins/maps/public/embeddable/types.ts index bf1691efde2d6..a5bdb205e2bc9 100644 --- a/x-pack/plugins/maps/public/embeddable/types.ts +++ b/x-pack/plugins/maps/public/embeddable/types.ts @@ -13,7 +13,7 @@ import { EmbeddableOutput, SavedObjectEmbeddableInput, } from '@kbn/embeddable-plugin/public'; -import type { Query, TimeRange } from '@kbn/es-query'; +import type { Query, TimeRange, AggregateQuery } from '@kbn/es-query'; import { MapCenterAndZoom, MapExtent } from '../../common/descriptor_types'; import { MapSavedObjectAttributes } from '../../common/map_saved_object_type'; import { MapSettings } from '../reducers/map'; @@ -31,7 +31,7 @@ interface MapEmbeddableState { hiddenLayers?: string[]; hideFilterActions?: boolean; filters?: Filter[]; - query?: Query; + query?: Query | AggregateQuery; timeRange?: TimeRange; } export type MapByValueInput = { diff --git a/x-pack/plugins/maps/public/legacy_visualizations/region_map/region_map_fn.ts b/x-pack/plugins/maps/public/legacy_visualizations/region_map/region_map_fn.ts index 79bfc0ce0d8fd..41b2afb50e58e 100644 --- a/x-pack/plugins/maps/public/legacy_visualizations/region_map/region_map_fn.ts +++ b/x-pack/plugins/maps/public/legacy_visualizations/region_map/region_map_fn.ts @@ -7,7 +7,7 @@ import { i18n } from '@kbn/i18n'; import type { Filter } from '@kbn/es-query'; -import type { Query, TimeRange } from '@kbn/es-query'; +import type { Query, AggregateQuery, TimeRange } from '@kbn/es-query'; import type { ExpressionValueSearchContext } from '@kbn/data-plugin/common/search/expressions/kibana_context_type'; import type { ExpressionFunctionDefinition, Render } from '@kbn/expressions-plugin/public'; import { REGION_MAP_RENDER, REGION_MAP_VIS_TYPE, RegionMapVisConfig } from './types'; @@ -20,7 +20,7 @@ export interface RegionMapVisRenderValue { visType: typeof REGION_MAP_VIS_TYPE; visConfig: RegionMapVisConfig; filters?: Filter[]; - query?: Query; + query?: Query | AggregateQuery; timeRange?: TimeRange; } diff --git a/x-pack/plugins/maps/public/legacy_visualizations/region_map/region_map_visualization.tsx b/x-pack/plugins/maps/public/legacy_visualizations/region_map/region_map_visualization.tsx index 2334b9af1996c..902d3ae9999f4 100644 --- a/x-pack/plugins/maps/public/legacy_visualizations/region_map/region_map_visualization.tsx +++ b/x-pack/plugins/maps/public/legacy_visualizations/region_map/region_map_visualization.tsx @@ -7,14 +7,14 @@ import React from 'react'; import type { Filter } from '@kbn/es-query'; -import type { Query, TimeRange } from '@kbn/es-query'; +import type { Query, TimeRange, AggregateQuery } from '@kbn/es-query'; import { RegionMapVisConfig } from './types'; import type { LazyLoadedMapModules } from '../../lazy_load_bundle'; import { MapComponent } from '../../embeddable/map_component'; interface Props { filters?: Filter[]; - query?: Query; + query?: Query | AggregateQuery; timeRange?: TimeRange; visConfig: RegionMapVisConfig; onInitialRenderComplete: () => void; diff --git a/x-pack/plugins/maps/public/legacy_visualizations/tile_map/tile_map_fn.ts b/x-pack/plugins/maps/public/legacy_visualizations/tile_map/tile_map_fn.ts index 116b3375eba76..2d881e9a5b2f8 100644 --- a/x-pack/plugins/maps/public/legacy_visualizations/tile_map/tile_map_fn.ts +++ b/x-pack/plugins/maps/public/legacy_visualizations/tile_map/tile_map_fn.ts @@ -7,7 +7,7 @@ import { i18n } from '@kbn/i18n'; import type { Filter } from '@kbn/es-query'; -import type { Query, TimeRange } from '@kbn/es-query'; +import type { Query, TimeRange, AggregateQuery } from '@kbn/es-query'; import type { ExpressionValueSearchContext } from '@kbn/data-plugin/common/search/expressions/kibana_context_type'; import type { ExpressionFunctionDefinition, Render } from '@kbn/expressions-plugin/public'; import { TILE_MAP_RENDER, TILE_MAP_VIS_TYPE, TileMapVisConfig } from './types'; @@ -20,7 +20,7 @@ export interface TileMapVisRenderValue { visType: typeof TILE_MAP_VIS_TYPE; visConfig: TileMapVisConfig; filters?: Filter[]; - query?: Query; + query?: Query | AggregateQuery; timeRange?: TimeRange; } diff --git a/x-pack/plugins/maps/public/legacy_visualizations/tile_map/tile_map_visualization.tsx b/x-pack/plugins/maps/public/legacy_visualizations/tile_map/tile_map_visualization.tsx index fd5f916baf4c6..c7976ede57870 100644 --- a/x-pack/plugins/maps/public/legacy_visualizations/tile_map/tile_map_visualization.tsx +++ b/x-pack/plugins/maps/public/legacy_visualizations/tile_map/tile_map_visualization.tsx @@ -7,14 +7,14 @@ import React from 'react'; import type { Filter } from '@kbn/es-query'; -import type { Query, TimeRange } from '@kbn/es-query'; +import type { Query, TimeRange, AggregateQuery } from '@kbn/es-query'; import { TileMapVisConfig } from './types'; import type { LazyLoadedMapModules } from '../../lazy_load_bundle'; import { MapComponent } from '../../embeddable/map_component'; interface Props { filters?: Filter[]; - query?: Query; + query?: Query | AggregateQuery; timeRange?: TimeRange; visConfig: TileMapVisConfig; onInitialRenderComplete: () => void; diff --git a/x-pack/plugins/maps/public/routes/map_page/map_app/index.ts b/x-pack/plugins/maps/public/routes/map_page/map_app/index.ts index af9054cd99570..5738e9d11f6dc 100644 --- a/x-pack/plugins/maps/public/routes/map_page/map_app/index.ts +++ b/x-pack/plugins/maps/public/routes/map_page/map_app/index.ts @@ -9,7 +9,7 @@ import { connect } from 'react-redux'; import { ThunkDispatch } from 'redux-thunk'; import { AnyAction } from 'redux'; import { Filter } from '@kbn/es-query'; -import type { Query, TimeRange } from '@kbn/es-query'; +import type { Query, TimeRange, AggregateQuery } from '@kbn/es-query'; import { MapApp } from './map_app'; import { getFlyoutDisplay, getIsFullScreen } from '../../../selectors/ui_selectors'; import { @@ -48,7 +48,7 @@ function mapDispatchToProps(dispatch: ThunkDispatch Date: Tue, 28 Jun 2022 20:13:16 +0300 Subject: [PATCH 058/115] data visualizer types --- .../components/search_panel/search_panel.tsx | 9 +++++---- .../index_data_visualizer/utils/saved_search_utils.ts | 8 ++++++-- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/components/search_panel/search_panel.tsx b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/components/search_panel/search_panel.tsx index 5dc3eee801abd..d1252b45b0691 100644 --- a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/components/search_panel/search_panel.tsx +++ b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/components/search_panel/search_panel.tsx @@ -8,7 +8,7 @@ import React, { FC, useEffect, useState } from 'react'; import { EuiFlexItem, EuiFlexGroup } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { Query, Filter } from '@kbn/es-query'; +import { Query, Filter, AggregateQuery } from '@kbn/es-query'; import type { TimeRange } from '@kbn/es-query'; import { DataView, DataViewField } from '@kbn/data-views-plugin/public'; import { ShardSizeFilter } from './shard_size_select'; @@ -132,9 +132,10 @@ export const SearchPanel: FC = ({ showDatePicker={false} showQueryInput={true} query={searchInput} - onQuerySubmit={(params: { dateRange: TimeRange; query?: Query | undefined }) => - searchHandler({ query: params.query }) - } + onQuerySubmit={(params: { + dateRange: TimeRange; + query?: Query | AggregateQuery | undefined; + }) => searchHandler({ query: params.query as Query })} // @ts-expect-error onFiltersUpdated is a valid prop on SearchBar onFiltersUpdated={(filters: Filter[]) => searchHandler({ filters })} indexPatterns={[dataView]} diff --git a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/utils/saved_search_utils.ts b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/utils/saved_search_utils.ts index 7a5231a89abd0..a4367acaf535b 100644 --- a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/utils/saved_search_utils.ts +++ b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/utils/saved_search_utils.ts @@ -13,6 +13,7 @@ import { buildQueryFromFilters, buildEsQuery, Query, + AggregateQuery, Filter, } from '@kbn/es-query'; import { QueryDslQueryContainer } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; @@ -37,6 +38,9 @@ const DEFAULT_QUERY = { export function getDefaultQuery() { return cloneDeep(DEFAULT_QUERY); } +function isOfQueryType(arg: Query | AggregateQuery): arg is Query { + return Boolean(arg && 'query' in arg); +} /** * Parse the stringified searchSourceJSON @@ -73,14 +77,14 @@ export function getQueryFromSavedSearchObject(savedSearch: SavedSearchSavedObjec * Should also form a valid query if only the query or filters is provided */ export function createMergedEsQuery( - query?: Query, + query?: Query | AggregateQuery, filters?: Filter[], dataView?: DataView, uiSettings?: IUiSettingsClient ) { let combinedQuery: QueryDslQueryContainer = getDefaultQuery(); - if (query && query.language === SEARCH_QUERY_LANGUAGE.KUERY) { + if (query && isOfQueryType(query) && query.language === SEARCH_QUERY_LANGUAGE.KUERY) { const ast = fromKueryExpression(query.query); if (query.query !== '') { combinedQuery = toElasticsearchQuery(ast, dataView); From 141682c0d1500814ac0057001d2c905c58cf8a85 Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Tue, 28 Jun 2022 20:40:04 +0300 Subject: [PATCH 059/115] Fix ml types --- .../public/application/jobs/new_job/job_from_lens/create_job.ts | 2 +- .../ml/public/application/jobs/new_job/utils/new_job_utils.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/x-pack/plugins/ml/public/application/jobs/new_job/job_from_lens/create_job.ts b/x-pack/plugins/ml/public/application/jobs/new_job/job_from_lens/create_job.ts index 35654fbc28786..193758dc5149a 100644 --- a/x-pack/plugins/ml/public/application/jobs/new_job/job_from_lens/create_job.ts +++ b/x-pack/plugins/ml/public/application/jobs/new_job/job_from_lens/create_job.ts @@ -207,7 +207,7 @@ async function createADJobFromLensSavedObject( const combinedFiltersAndQueries = combineQueriesAndFilters( { query, filters }, - { query: vis.state.query, filters: vis.state.filters }, + { query: vis.state.query as Query, filters: vis.state.filters }, dataView, kibanaConfig ); diff --git a/x-pack/plugins/ml/public/application/jobs/new_job/utils/new_job_utils.ts b/x-pack/plugins/ml/public/application/jobs/new_job/utils/new_job_utils.ts index 1f0be5bdb0516..1e02bd891ca73 100644 --- a/x-pack/plugins/ml/public/application/jobs/new_job/utils/new_job_utils.ts +++ b/x-pack/plugins/ml/public/application/jobs/new_job/utils/new_job_utils.ts @@ -72,7 +72,7 @@ export function createQueries( dataView: DataViewBase | undefined, kibanaConfig: IUiSettingsClient ) { - let query = getDefaultQuery(); + let query: Query = getDefaultQuery(); let combinedQuery: estypes.QueryDslQueryContainer = getDefaultDatafeedQuery(); query = data.query; From f75b525c979a3a6ae9f7f8c0f41bf83bb091be9c Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Tue, 28 Jun 2022 21:40:04 +0300 Subject: [PATCH 060/115] xpack rest types --- .../public/pages/findings/types.ts | 4 ++-- .../common/components/query_bar/index.tsx | 18 ++++++++++------- .../common/components/search_bar/index.tsx | 10 +++++++--- .../url_state/initialize_redux_by_url.tsx | 20 ++++++++++++------- .../components/rules/query_bar/index.tsx | 16 +++++++++------ .../view/components/search_bar.tsx | 15 ++++++++++---- .../components/timeline/query_bar/index.tsx | 15 +++++++++----- .../search_source_expression_form.tsx | 18 ++++++++++------- 8 files changed, 75 insertions(+), 41 deletions(-) diff --git a/x-pack/plugins/cloud_security_posture/public/pages/findings/types.ts b/x-pack/plugins/cloud_security_posture/public/pages/findings/types.ts index b5f05a02bcc51..c4247e8b1e1ba 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/findings/types.ts +++ b/x-pack/plugins/cloud_security_posture/public/pages/findings/types.ts @@ -5,13 +5,13 @@ * 2.0. */ import type { DataView } from '@kbn/data-views-plugin/common'; -import type { BoolQuery, Filter, Query } from '@kbn/es-query'; +import type { BoolQuery, Filter, Query, AggregateQuery } from '@kbn/es-query'; import type { CspRuleMetadataType } from '../../../common/schemas'; export type FindingsGroupByKind = 'default' | 'resource'; export interface FindingsBaseURLQuery { - query: Query; + query: Query | AggregateQuery; filters: Filter[]; } diff --git a/x-pack/plugins/security_solution/public/common/components/query_bar/index.tsx b/x-pack/plugins/security_solution/public/common/components/query_bar/index.tsx index feb9cf30afc4f..d01f497cbaf7c 100644 --- a/x-pack/plugins/security_solution/public/common/components/query_bar/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/query_bar/index.tsx @@ -8,7 +8,7 @@ import React, { memo, useMemo, useCallback } from 'react'; import deepEqual from 'fast-deep-equal'; -import type { DataViewBase, Filter, Query, TimeRange } from '@kbn/es-query'; +import type { DataViewBase, Filter, Query, TimeRange, AggregateQuery } from '@kbn/es-query'; import { FilterManager, TimeHistory, @@ -19,6 +19,10 @@ import { DataView } from '@kbn/data-views-plugin/public'; import { SearchBar, SearchBarProps } from '@kbn/unified-search-plugin/public'; import { Storage } from '@kbn/kibana-utils-plugin/public'; +function isOfQueryType(arg: Query | AggregateQuery): arg is Query { + return Boolean(arg && 'query' in arg); +} + export interface QueryBarComponentProps { dataTestSubj?: string; dateRangeFrom?: string; @@ -27,11 +31,11 @@ export interface QueryBarComponentProps { indexPattern: DataViewBase; isLoading?: boolean; isRefreshPaused?: boolean; - filterQuery: Query; + filterQuery: Query | AggregateQuery; filterManager: FilterManager; filters: Filter[]; - onChangedQuery?: (query: Query) => void; - onSubmitQuery: (query: Query, timefilter?: SavedQueryTimeFilter) => void; + onChangedQuery?: (query: Query | AggregateQuery) => void; + onSubmitQuery: (query: Query | AggregateQuery, timefilter?: SavedQueryTimeFilter) => void; refreshInterval?: number; savedQuery?: SavedQuery; onSavedQuery: (savedQuery: SavedQuery | undefined) => void; @@ -58,7 +62,7 @@ export const QueryBar = memo( displayStyle, }) => { const onQuerySubmit = useCallback( - (payload: { dateRange: TimeRange; query?: Query }) => { + (payload: { dateRange: TimeRange; query?: Query | AggregateQuery }) => { if (payload.query != null && !deepEqual(payload.query, filterQuery)) { onSubmitQuery(payload.query); } @@ -67,7 +71,7 @@ export const QueryBar = memo( ); const onQueryChange = useCallback( - (payload: { dateRange: TimeRange; query?: Query }) => { + (payload: { dateRange: TimeRange; query?: Query | AggregateQuery }) => { if (onChangedQuery && payload.query != null && !deepEqual(payload.query, filterQuery)) { onChangedQuery(payload.query); } @@ -86,7 +90,7 @@ export const QueryBar = memo( ); const onClearSavedQuery = useCallback(() => { - if (savedQuery != null) { + if (savedQuery != null && isOfQueryType(savedQuery.attributes.query)) { onSubmitQuery({ query: '', language: savedQuery.attributes.query.language, diff --git a/x-pack/plugins/security_solution/public/common/components/search_bar/index.tsx b/x-pack/plugins/security_solution/public/common/components/search_bar/index.tsx index 2c2e1c5cfcc19..8916e5f50cc74 100644 --- a/x-pack/plugins/security_solution/public/common/components/search_bar/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/search_bar/index.tsx @@ -13,7 +13,7 @@ import { Dispatch } from 'redux'; import { Subscription } from 'rxjs'; import deepEqual from 'fast-deep-equal'; -import type { DataViewBase, Filter, Query, TimeRange } from '@kbn/es-query'; +import type { DataViewBase, Filter, Query, TimeRange, AggregateQuery } from '@kbn/es-query'; import type { FilterManager, SavedQuery } from '@kbn/data-plugin/public'; import type { DataView } from '@kbn/data-views-plugin/public'; @@ -40,6 +40,10 @@ import { usersActions } from '../../../users/store'; import { hostsActions } from '../../../hosts/store'; import { networkActions } from '../../../network/store'; +function isOfQueryType(arg?: Query | AggregateQuery): arg is Query { + return Boolean(arg && 'query' in arg); +} + const APP_STATE_STORAGE_KEY = 'securitySolution.searchBar.appState'; interface SiemSearchBarProps { @@ -106,7 +110,7 @@ export const SearchBarComponent = memo( }, [end, fromStr, start, timefilter, toStr, setTablesActivePageToZero]); const onQuerySubmit = useCallback( - (payload: { dateRange: TimeRange; query?: Query }) => { + (payload: { dateRange: TimeRange; query?: Query | AggregateQuery }) => { // if the function is there, call it to check if the signals index exists yet // in order to update the index fields if (pollForSignalIndex != null) { @@ -237,7 +241,7 @@ export const SearchBarComponent = memo( ); const onClearSavedQuery = useCallback(() => { - if (savedQuery != null) { + if (savedQuery != null && isOfQueryType(savedQuery.attributes.query)) { updateSearch({ id, filters: [], diff --git a/x-pack/plugins/security_solution/public/common/components/url_state/initialize_redux_by_url.tsx b/x-pack/plugins/security_solution/public/common/components/url_state/initialize_redux_by_url.tsx index a417ad7c5950f..75b21068a947c 100644 --- a/x-pack/plugins/security_solution/public/common/components/url_state/initialize_redux_by_url.tsx +++ b/x-pack/plugins/security_solution/public/common/components/url_state/initialize_redux_by_url.tsx @@ -10,7 +10,7 @@ import { Dispatch } from 'redux'; import { useCallback, useMemo } from 'react'; import { useDispatch } from 'react-redux'; -import type { Filter, Query } from '@kbn/es-query'; +import type { Filter, Query, AggregateQuery } from '@kbn/es-query'; import { inputsActions } from '../../store/actions'; import { InputsModelId, TimeRangeKinds } from '../../store/inputs/constants'; import { @@ -30,6 +30,10 @@ import { } from '../../../timelines/components/open_timeline/helpers'; import { timelineActions } from '../../../timelines/store/timeline'; +function isOfQueryType(arg: Query | AggregateQuery): arg is Query { + return Boolean(arg && 'query' in arg); +} + export const useSetInitialStateFromUrl = () => { const dispatch = useDispatch(); @@ -77,12 +81,14 @@ export const useSetInitialStateFromUrl = () => { if (savedQueryId != null && savedQueryId !== '') { savedQueries.getSavedQuery(savedQueryId).then((savedQueryData) => { filterManager.setFilters(savedQueryData.attributes.filters || []); - dispatch( - inputsActions.setFilterQuery({ - id: 'global', - ...savedQueryData.attributes.query, - }) - ); + if (isOfQueryType(savedQueryData.attributes.query)) { + dispatch( + inputsActions.setFilterQuery({ + id: 'global', + ...savedQueryData.attributes.query, + }) + ); + } dispatch(inputsActions.setSavedQuery({ id: 'global', savedQuery: savedQueryData })); }); } diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/query_bar/index.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/query_bar/index.tsx index 63e9cb27fc082..7c22a590b48c4 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/query_bar/index.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/rules/query_bar/index.tsx @@ -10,7 +10,7 @@ import React, { useCallback, useEffect, useState } from 'react'; import { Subscription } from 'rxjs'; import styled from 'styled-components'; import deepEqual from 'fast-deep-equal'; -import type { DataViewBase, Filter, Query } from '@kbn/es-query'; +import type { DataViewBase, Filter, Query, AggregateQuery } from '@kbn/es-query'; import { FilterManager, SavedQuery } from '@kbn/data-plugin/public'; import { BrowserFields } from '../../../../common/containers/source'; @@ -26,6 +26,10 @@ import { useSavedQueryServices } from '../../../../common/utils/saved_query_serv import { FieldHook, getFieldValidityAndErrorMessage } from '../../../../shared_imports'; import * as i18n from './translations'; +function isOfQueryType(arg: Query | AggregateQuery): arg is Query { + return Boolean(arg && 'query' in arg); +} + export interface FieldValueQueryBar { filters: Filter[]; query: Query; @@ -138,9 +142,9 @@ export const QueryBarDefineRule = ({ }, [fieldValue, filterManager, savedQuery, savedQueryServices]); const onSubmitQuery = useCallback( - (newQuery: Query) => { + (newQuery: Query | AggregateQuery) => { const { query } = fieldValue; - if (!deepEqual(query, newQuery)) { + if (!deepEqual(query, newQuery) && isOfQueryType(newQuery)) { setFieldValue({ ...fieldValue, query: newQuery }); } }, @@ -148,9 +152,9 @@ export const QueryBarDefineRule = ({ ); const onChangedQuery = useCallback( - (newQuery: Query) => { + (newQuery: Query | AggregateQuery) => { const { query } = fieldValue; - if (!deepEqual(query, newQuery)) { + if (!deepEqual(query, newQuery) && isOfQueryType(newQuery)) { setFieldValue({ ...fieldValue, query: newQuery }); } }, @@ -165,7 +169,7 @@ export const QueryBarDefineRule = ({ setSavedQuery(newSavedQuery); setFieldValue({ filters: newSavedQuery.attributes.filters ?? [], - query: newSavedQuery.attributes.query, + query: newSavedQuery.attributes.query as Query, saved_id: newSavedQuery.id, }); } else { diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/components/search_bar.tsx b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/components/search_bar.tsx index 24da8b3b86a35..3bee9841d52e7 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/components/search_bar.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/components/search_bar.tsx @@ -8,7 +8,7 @@ import React, { memo, useCallback, useMemo } from 'react'; import { useHistory } from 'react-router-dom'; import { encode, RisonValue } from 'rison-node'; -import type { Query } from '@kbn/es-query'; +import type { Query, AggregateQuery } from '@kbn/es-query'; import { TimeHistory } from '@kbn/data-plugin/public'; import { DataView } from '@kbn/data-views-plugin/public'; import { SearchBar } from '@kbn/unified-search-plugin/public'; @@ -18,6 +18,10 @@ import { useEndpointSelector } from '../hooks'; import * as selectors from '../../store/selectors'; import { clone } from '../../models/index_pattern'; +function isOfQueryType(arg?: Query | AggregateQuery): arg is Query { + return Boolean(arg && 'query' in arg); +} + export const AdminSearchBar = memo(() => { const history = useHistory(); const { admin_query: _, ...queryParams } = useEndpointSelector(selectors.uiQueryParams); @@ -29,14 +33,17 @@ export const AdminSearchBar = memo(() => { ); const onQuerySubmit = useCallback( - (params: { query?: Query }) => { + (params: { query?: Query | AggregateQuery }) => { history.push( urlFromQueryParams({ ...queryParams, // if query is changed, reset back to first page // so that user is not (possibly) being left on an invalid page - page_index: params.query?.query === searchBarQuery.query ? queryParams.page_index : '0', - ...(params.query?.query.trim() + page_index: + isOfQueryType(params.query) && params.query?.query === searchBarQuery.query + ? queryParams.page_index + : '0', + ...(isOfQueryType(params.query) && params.query?.query.trim() ? { admin_query: encode(params.query as unknown as RisonValue) } : {}), }) diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/query_bar/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/query_bar/index.tsx index c62869c0f0746..d28efff5068c0 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/query_bar/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/query_bar/index.tsx @@ -11,7 +11,7 @@ import { useDispatch } from 'react-redux'; import { Subscription } from 'rxjs'; import deepEqual from 'fast-deep-equal'; -import { FilterStateStore, Filter, Query } from '@kbn/es-query'; +import { FilterStateStore, Filter, Query, AggregateQuery } from '@kbn/es-query'; import type { FilterManager, SavedQuery, SavedQueryTimeFilter } from '@kbn/data-plugin/public'; import { useSourcererDataView } from '../../../../common/containers/sourcerer'; import { SourcererScopeName } from '../../../../common/store/sourcerer/model'; @@ -45,6 +45,10 @@ export interface QueryBarTimelineComponentProps { updateReduxTime: DispatchUpdateReduxTime; } +function isOfQueryType(arg?: Query | AggregateQuery): arg is Query { + return Boolean(arg && 'query' in arg); +} + export const TIMELINE_FILTER_DROP_AREA = 'timeline-filter-drop-area'; const getNonDropAreaFilters = (filters: Filter[] = []) => @@ -200,11 +204,12 @@ export const QueryBarTimeline = memo( }, [savedQueryId]); const onSubmitQuery = useCallback( - (newQuery: Query, timefilter?: SavedQueryTimeFilter) => { + (newQuery: Query | AggregateQuery, timefilter?: SavedQueryTimeFilter) => { if ( - filterQuery == null || - (filterQuery != null && filterQuery.expression !== newQuery.query) || - filterQuery.kind !== newQuery.language + isOfQueryType(newQuery) && + (filterQuery == null || + (filterQuery != null && filterQuery.expression !== newQuery.query) || + filterQuery.kind !== newQuery.language) ) { applyKqlFilterQuery(newQuery.query as string, newQuery.language as KueryFilterQueryKind); } diff --git a/x-pack/plugins/stack_alerts/public/alert_types/es_query/expression/search_source_expression_form.tsx b/x-pack/plugins/stack_alerts/public/alert_types/es_query/expression/search_source_expression_form.tsx index bd03babf85a0b..7d7cf8e1c42c5 100644 --- a/x-pack/plugins/stack_alerts/public/alert_types/es_query/expression/search_source_expression_form.tsx +++ b/x-pack/plugins/stack_alerts/public/alert_types/es_query/expression/search_source_expression_form.tsx @@ -8,7 +8,7 @@ import React, { Fragment, useCallback, useEffect, useMemo, useReducer, useState } from 'react'; import deepEqual from 'fast-deep-equal'; import { lastValueFrom } from 'rxjs'; -import { Filter } from '@kbn/es-query'; +import { Filter, AggregateQuery } from '@kbn/es-query'; import { FormattedMessage } from '@kbn/i18n-react'; import { EuiSpacer, EuiTitle } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; @@ -29,10 +29,14 @@ import { useTriggersAndActionsUiDeps } from '../util'; import { totalHitsToNumber } from './use_test_query'; import { TestQueryRow } from './test_query_row'; +function isOfQueryType(arg?: Query | AggregateQuery): arg is Query { + return Boolean(arg && 'query' in arg); +} + interface LocalState { index: DataView; filter: Filter[]; - query: Query; + query: Query | AggregateQuery; threshold: number[]; timeWindowSize: number; size: number; @@ -47,7 +51,7 @@ type LocalStateReducer = (prevState: LocalState, action: LocalStateAction) => Lo interface SearchSourceParamsAction { type: 'index' | 'filter' | 'query'; - payload: DataView | Filter[] | Query; + payload: DataView | Filter[] | Query | AggregateQuery; } interface SearchSourceExpressionFormProps { @@ -114,8 +118,8 @@ export const SearchSourceExpressionForm = (props: SearchSourceExpressionFormProp }, []); const onChangeQuery = useCallback( - ({ query: newQuery }: { query?: Query }) => { - if (!deepEqual(newQuery, query)) { + ({ query: newQuery }: { query?: Query | AggregateQuery }) => { + if (!deepEqual(newQuery, query) && isOfQueryType(query)) { dispatch({ type: 'query', payload: newQuery || { ...query, query: '' } }); } }, @@ -123,8 +127,8 @@ export const SearchSourceExpressionForm = (props: SearchSourceExpressionFormProp ); // needs to change language mode only - const onQueryBarSubmit = ({ query: newQuery }: { query?: Query }) => { - if (newQuery?.language !== query.language) { + const onQueryBarSubmit = ({ query: newQuery }: { query?: Query | AggregateQuery }) => { + if (isOfQueryType(query) && isOfQueryType(newQuery) && newQuery?.language !== query.language) { dispatch({ type: 'query', payload: { ...query, language: newQuery?.language } as Query }); } }; From 20246b118e70b7486fc15cadcf0c705a835fc7c5 Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Tue, 28 Jun 2022 21:52:32 +0300 Subject: [PATCH 061/115] Fix jest test --- .../exploratory_view/configurations/lens_attributes.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/lens_attributes.test.ts b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/lens_attributes.test.ts index 9de152c9d8597..5a13b394e093a 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/lens_attributes.test.ts +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/lens_attributes.test.ts @@ -439,7 +439,7 @@ describe('Lens Attribute', () => { formulaHelper ).getJSON(); const query = multiSeriesLensAttr.state.query as Query; - expect(query).toEqual('transaction.duration.us < 60000000'); + expect(query.query).toEqual('transaction.duration.us < 60000000'); }); describe('Layer breakdowns', function () { From 40b63baf8e83c2860d6c5423c2bb88a2f07fe060 Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Wed, 29 Jun 2022 09:16:30 +0300 Subject: [PATCH 062/115] Fix --- .../field_editor/components/scripting_help/test_script.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/plugins/data_view_management/public/components/field_editor/components/scripting_help/test_script.tsx b/src/plugins/data_view_management/public/components/field_editor/components/scripting_help/test_script.tsx index d3d682412b473..ea17b96d64a56 100644 --- a/src/plugins/data_view_management/public/components/field_editor/components/scripting_help/test_script.tsx +++ b/src/plugins/data_view_management/public/components/field_editor/components/scripting_help/test_script.tsx @@ -83,8 +83,7 @@ export class TestScript extends Component { let query; if (searchContext) { const esQueryConfigs = getEsQueryConfig(this.context.services.uiSettings); - const searchContextQuery = searchContext.query as Query; - query = buildEsQuery(this.props.indexPattern, searchContextQuery || [], [], esQueryConfigs); + query = buildEsQuery(this.props.indexPattern, searchContext.query || [], [], esQueryConfigs); } const scriptResponse = await executeScript({ From 07e70799cb482156e5b2148bb279fa1efb659ec5 Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Wed, 29 Jun 2022 11:18:58 +0300 Subject: [PATCH 063/115] Move helper functions to es config --- .../kbn-es-query/src/es_query/es_query.sql.ts | 25 +++++++++++++++++++ packages/kbn-es-query/src/es_query/index.ts | 1 + packages/kbn-es-query/src/index.ts | 3 +++ .../data/common/query/to_expression_ast.ts | 12 +-------- .../search_source/migrate_legacy_query.ts | 6 +---- .../server/query/route_handler_context.ts | 6 +---- .../main/utils/cleanup_url_state.ts | 5 +--- .../main/utils/persist_saved_search.ts | 7 +----- .../utils/get_text_based_language_mode.ts | 15 +++++------ .../query_string_input/query_bar_top_row.tsx | 5 +--- .../text_based_languages_editor/index.tsx | 24 ++++++++++++++---- .../lib/use_query_string_manager.ts | 5 ++-- .../public/search_bar/search_bar.tsx | 6 +---- .../utils/saved_search_utils.ts | 4 +-- .../public/app_plugin/show_underlying_data.ts | 5 +--- .../alerts/components/alerts_search_bar.tsx | 5 +--- .../common/components/query_bar/index.tsx | 5 +--- .../common/components/search_bar/index.tsx | 5 +--- .../url_state/initialize_redux_by_url.tsx | 7 ++---- .../components/rules/query_bar/index.tsx | 5 +--- .../view/components/search_bar.tsx | 5 +--- .../components/timeline/query_bar/index.tsx | 6 +---- .../search_source_expression_form.tsx | 6 +---- 23 files changed, 74 insertions(+), 99 deletions(-) create mode 100644 packages/kbn-es-query/src/es_query/es_query.sql.ts diff --git a/packages/kbn-es-query/src/es_query/es_query.sql.ts b/packages/kbn-es-query/src/es_query/es_query.sql.ts new file mode 100644 index 0000000000000..10995aeff1a8a --- /dev/null +++ b/packages/kbn-es-query/src/es_query/es_query.sql.ts @@ -0,0 +1,25 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ +import type { Query, AggregateQuery } from '../filters'; + +// Checks if the query is of type Query +export function isOfQueryType(arg?: Query | AggregateQuery): arg is Query { + return Boolean(arg && 'query' in arg); +} + +// Checks if the query is of type AggregateQuery +// currently only supports the sql query type +// should be enhanced to support other query types +export function isOfAggregateQueryType(query: AggregateQuery | Query): query is AggregateQuery { + return Boolean(query && 'sql' in query); +} + +// returns the language of the aggregate Query, sql, esql etc +export function getAggregateQueryMode(query: AggregateQuery): string { + return Object.keys(query)[0]; +} diff --git a/packages/kbn-es-query/src/es_query/index.ts b/packages/kbn-es-query/src/es_query/index.ts index d4e45b35728f6..08ca93fc4d442 100644 --- a/packages/kbn-es-query/src/es_query/index.ts +++ b/packages/kbn-es-query/src/es_query/index.ts @@ -13,6 +13,7 @@ export { buildEsQuery } from './build_es_query'; export { buildQueryFromFilters } from './from_filters'; export { luceneStringToDsl } from './lucene_string_to_dsl'; export { decorateQuery } from './decorate_query'; +export { isOfQueryType, isOfAggregateQueryType, getAggregateQueryMode } from './es_query.sql'; export type { IFieldSubType, BoolQuery, diff --git a/packages/kbn-es-query/src/index.ts b/packages/kbn-es-query/src/index.ts index 62fa19d14bfa4..a0c04f5557164 100644 --- a/packages/kbn-es-query/src/index.ts +++ b/packages/kbn-es-query/src/index.ts @@ -53,6 +53,9 @@ export { decorateQuery, luceneStringToDsl, migrateFilter, + isOfQueryType, + isOfAggregateQueryType, + getAggregateQueryMode, } from './es_query'; export { diff --git a/src/plugins/data/common/query/to_expression_ast.ts b/src/plugins/data/common/query/to_expression_ast.ts index badcb8b3372e1..c7997724f1333 100644 --- a/src/plugins/data/common/query/to_expression_ast.ts +++ b/src/plugins/data/common/query/to_expression_ast.ts @@ -5,7 +5,7 @@ * in compliance with, at your election, the Elastic License 2.0 or the Server * Side Public License, v 1. */ - +import { isOfAggregateQueryType, getAggregateQueryMode } from '@kbn/es-query'; import { buildExpression, buildExpressionFunction } from '@kbn/expressions-plugin/common'; import type { DataViewsContract } from '@kbn/data-views-plugin/common'; import { @@ -16,16 +16,6 @@ import { timerangeToAst, } from '..'; -import type { Query, AggregateQuery } from './types'; - -function isOfAggregateQueryType(query: AggregateQuery | Query): query is AggregateQuery { - return Boolean(query && 'sql' in query); -} - -function getAggregateQueryMode(query: AggregateQuery): string { - return Object.keys(query)[0]; -} - function getIndexPatternFromSQLQuery(sqlQuery?: string): string { const sql = sqlQuery?.replaceAll('"', ''); const matches = sql?.match(/FROM\s+([\w*]+)/); diff --git a/src/plugins/data/common/search/search_source/migrate_legacy_query.ts b/src/plugins/data/common/search/search_source/migrate_legacy_query.ts index 48e2aa8d2c92b..774b01c029ea0 100644 --- a/src/plugins/data/common/search/search_source/migrate_legacy_query.ts +++ b/src/plugins/data/common/search/search_source/migrate_legacy_query.ts @@ -5,13 +5,9 @@ * in compliance with, at your election, the Elastic License 2.0 or the Server * Side Public License, v 1. */ -import { Query, AggregateQuery } from '@kbn/es-query'; +import { Query, AggregateQuery, isOfAggregateQueryType } from '@kbn/es-query'; import { has } from 'lodash'; -function isOfAggregateQueryType(query: AggregateQuery | Query): query is AggregateQuery { - return Boolean(query && 'sql' in query); -} - /** * Creates a standardized query object from old queries that were either strings or pure ES query DSL * diff --git a/src/plugins/data/server/query/route_handler_context.ts b/src/plugins/data/server/query/route_handler_context.ts index 162fb31f8a377..6179a909eee57 100644 --- a/src/plugins/data/server/query/route_handler_context.ts +++ b/src/plugins/data/server/query/route_handler_context.ts @@ -7,14 +7,10 @@ */ import { CustomRequestHandlerContext, RequestHandlerContext, SavedObject } from '@kbn/core/server'; -import { isFilters, Query, AggregateQuery } from '@kbn/es-query'; +import { isFilters, isOfQueryType } from '@kbn/es-query'; import { isQuery, SavedQueryAttributes } from '../../common'; import { extract, inject } from '../../common/query/filters/persistable_state'; -function isOfQueryType(arg: Query | AggregateQuery): arg is Query { - return Boolean(arg && 'query' in arg); -} - function injectReferences({ id, attributes, diff --git a/src/plugins/discover/public/application/main/utils/cleanup_url_state.ts b/src/plugins/discover/public/application/main/utils/cleanup_url_state.ts index 20bc620b2b3e1..69c8540f0fbe7 100644 --- a/src/plugins/discover/public/application/main/utils/cleanup_url_state.ts +++ b/src/plugins/discover/public/application/main/utils/cleanup_url_state.ts @@ -5,13 +5,10 @@ * in compliance with, at your election, the Elastic License 2.0 or the Server * Side Public License, v 1. */ -import { AggregateQuery, Query } from '@kbn/es-query'; +import { isOfAggregateQueryType } from '@kbn/es-query'; import { migrateLegacyQuery } from '../../../utils/migrate_legacy_query'; import { AppState, AppStateUrl } from '../services/discover_state'; -function isOfAggregateQueryType(query: AggregateQuery | Query): query is AggregateQuery { - return Boolean(query && 'sql' in query); -} /** * Takes care of the given url state, migrates legacy props and cleans up empty props * @param appStateFromUrl diff --git a/src/plugins/discover/public/application/main/utils/persist_saved_search.ts b/src/plugins/discover/public/application/main/utils/persist_saved_search.ts index 69a4929f08d6a..70ca689ee2538 100644 --- a/src/plugins/discover/public/application/main/utils/persist_saved_search.ts +++ b/src/plugins/discover/public/application/main/utils/persist_saved_search.ts @@ -5,7 +5,7 @@ * in compliance with, at your election, the Elastic License 2.0 or the Server * Side Public License, v 1. */ -import { AggregateQuery, Query } from '@kbn/es-query'; +import { isOfAggregateQueryType } from '@kbn/es-query'; import { DataView } from '@kbn/data-views-plugin/public'; import { SavedObjectSaveOpts } from '@kbn/saved-objects-plugin/public'; import { updateSearchSource } from './update_search_source'; @@ -14,11 +14,6 @@ import { AppState } from '../services/discover_state'; import type { SortOrder } from '../../../services/saved_searches'; import { DiscoverServices } from '../../../build_services'; import { saveSavedSearch } from '../../../services/saved_searches'; - -function isOfAggregateQueryType(query: AggregateQuery | Query): query is AggregateQuery { - return Boolean(query && 'sql' in query); -} - /** * Helper function to update and persist the given savedSearch */ diff --git a/src/plugins/discover/public/utils/get_text_based_language_mode.ts b/src/plugins/discover/public/utils/get_text_based_language_mode.ts index 69bc3dcf42b74..b87b45ec8d5e1 100644 --- a/src/plugins/discover/public/utils/get_text_based_language_mode.ts +++ b/src/plugins/discover/public/utils/get_text_based_language_mode.ts @@ -5,15 +5,12 @@ * in compliance with, at your election, the Elastic License 2.0 or the Server * Side Public License, v 1. */ -import { AggregateQuery, Query } from '@kbn/es-query'; - -function isOfAggregateQueryType(query: AggregateQuery | Query): query is AggregateQuery { - return Boolean(query && 'sql' in query); -} - -function getAggregateQueryMode(query: AggregateQuery): string { - return Object.keys(query)[0]; -} +import { + AggregateQuery, + Query, + isOfAggregateQueryType, + getAggregateQueryMode, +} from '@kbn/es-query'; export function getTextBasedLanguageMode(query: Query | AggregateQuery): string { let textBasedLanguageMode = ''; diff --git a/src/plugins/unified_search/public/query_string_input/query_bar_top_row.tsx b/src/plugins/unified_search/public/query_string_input/query_bar_top_row.tsx index 6b9b2714b2e20..589ed89d22b60 100644 --- a/src/plugins/unified_search/public/query_string_input/query_bar_top_row.tsx +++ b/src/plugins/unified_search/public/query_string_input/query_bar_top_row.tsx @@ -12,6 +12,7 @@ import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react' import deepEqual from 'fast-deep-equal'; import useObservable from 'react-use/lib/useObservable'; import type { Filter, TimeRange, Query, AggregateQuery } from '@kbn/es-query'; +import { isOfQueryType } from '@kbn/es-query'; import { EMPTY } from 'rxjs'; import { map } from 'rxjs/operators'; import { @@ -49,10 +50,6 @@ const SuperDatePicker = React.memo( EuiSuperDatePicker as any ) as unknown as typeof EuiSuperDatePicker; -function isOfQueryType(arg: Query | AggregateQuery): arg is Query { - return Boolean(arg && 'query' in arg); -} - const QueryStringInput = withKibana(QueryStringInputUI); // @internal diff --git a/src/plugins/unified_search/public/query_string_input/text_based_languages_editor/index.tsx b/src/plugins/unified_search/public/query_string_input/text_based_languages_editor/index.tsx index d34c38dbd8413..c89dc0af6154c 100644 --- a/src/plugins/unified_search/public/query_string_input/text_based_languages_editor/index.tsx +++ b/src/plugins/unified_search/public/query_string_input/text_based_languages_editor/index.tsx @@ -210,11 +210,11 @@ export const TextBasedLanguagesEditor = memo(function TextBasedLanguagesEditor({ (width: number, force?: boolean) => { const containerWidth = containerRef.current?.offsetWidth; if (containerWidth && (!isCompactFocused || force)) { - const hasLines = /\r|\n/.exec(code); + const hasLines = /\r|\n/.exec(query.sql); if (hasLines && !updateLinesFromModel) { - setLines(code.split(/\r|\n/).length); + setLines(query.sql.split(/\r|\n/).length); } - const text = hasLines ? code.split(/\r|\n/)[0] : code; + const text = hasLines ? query.sql.split(/\r|\n/)[0] : query.sql; const queryLength = text.length; const unusedSpace = errors && errors.length @@ -230,9 +230,22 @@ export const TextBasedLanguagesEditor = memo(function TextBasedLanguagesEditor({ } } }, - [code, errors, isCompactFocused] + [query.sql, errors, isCompactFocused] ); + useEffect(() => { + if (editor1.current) { + const editorElement = editor1.current.getDomNode(); + if (editorElement) { + const contentWidth = Number(editorElement?.style.width.replace('px', '')); + if (code !== query.sql) { + setCode(query.sql); + calculateVisibleCode(contentWidth); + } + } + } + }, [calculateVisibleCode, code, query.sql]); + const onResize = ({ width }: { width: number }) => { calculateVisibleCode(width); if (editor1.current) { @@ -258,7 +271,8 @@ export const TextBasedLanguagesEditor = memo(function TextBasedLanguagesEditor({ wordWrap: isWordWrapped ? 'on' : 'off', lineNumbers: showLineNumbers ? 'on' : 'off', lineDecorationsWidth: 16, - autoIndent: 'brackets', + accessibilitySupport: 'off', + autoIndent: 'none', wrappingIndent: 'none', overviewRulerLanes: 0, hideCursorInOverviewRuler: true, diff --git a/src/plugins/unified_search/public/search_bar/lib/use_query_string_manager.ts b/src/plugins/unified_search/public/search_bar/lib/use_query_string_manager.ts index ff07b41388294..3f72d6eacc630 100644 --- a/src/plugins/unified_search/public/search_bar/lib/use_query_string_manager.ts +++ b/src/plugins/unified_search/public/search_bar/lib/use_query_string_manager.ts @@ -9,15 +9,14 @@ import { useState, useEffect, useMemo } from 'react'; import { Subscription } from 'rxjs'; import type { Query, AggregateQuery } from '@kbn/es-query'; +import { isOfQueryType } from '@kbn/es-query'; import type { QueryStringContract } from '@kbn/data-plugin/public'; interface UseQueryStringProps { query?: Query | AggregateQuery; queryStringManager: QueryStringContract; } -function isOfQueryType(arg: Query | AggregateQuery): arg is Query { - return Boolean(arg && 'query' in arg); -} + export const useQueryStringManager = (props: UseQueryStringProps) => { // Filters should be either what's passed in the initial state or the current state of the filter manager const [query, setQuery] = useState( diff --git a/src/plugins/unified_search/public/search_bar/search_bar.tsx b/src/plugins/unified_search/public/search_bar/search_bar.tsx index fd692cf138898..7d6d2957e3fe7 100644 --- a/src/plugins/unified_search/public/search_bar/search_bar.tsx +++ b/src/plugins/unified_search/public/search_bar/search_bar.tsx @@ -15,7 +15,7 @@ import { get, isEqual } from 'lodash'; import memoizeOne from 'memoize-one'; import { METRIC_TYPE } from '@kbn/analytics'; -import { Query, Filter, TimeRange, AggregateQuery } from '@kbn/es-query'; +import { Query, Filter, TimeRange, AggregateQuery, isOfQueryType } from '@kbn/es-query'; import { withKibana, KibanaReactContextValue } from '@kbn/kibana-react-plugin/public'; import type { TimeHistoryContract, SavedQuery } from '@kbn/data-plugin/public'; import type { SavedQueryAttributes } from '@kbn/data-plugin/common'; @@ -110,10 +110,6 @@ interface State { dateRangeTo: string; } -function isOfQueryType(arg: Query | AggregateQuery): arg is Query { - return Boolean(arg && 'query' in arg); -} - class SearchBarUI extends Component { public static defaultProps = { showQueryBar: true, diff --git a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/utils/saved_search_utils.ts b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/utils/saved_search_utils.ts index a4367acaf535b..f304aba4f4431 100644 --- a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/utils/saved_search_utils.ts +++ b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/utils/saved_search_utils.ts @@ -15,6 +15,7 @@ import { Query, AggregateQuery, Filter, + isOfQueryType, } from '@kbn/es-query'; import { QueryDslQueryContainer } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import { SearchSource } from '@kbn/data-plugin/common'; @@ -38,9 +39,6 @@ const DEFAULT_QUERY = { export function getDefaultQuery() { return cloneDeep(DEFAULT_QUERY); } -function isOfQueryType(arg: Query | AggregateQuery): arg is Query { - return Boolean(arg && 'query' in arg); -} /** * Parse the stringified searchSourceJSON diff --git a/x-pack/plugins/lens/public/app_plugin/show_underlying_data.ts b/x-pack/plugins/lens/public/app_plugin/show_underlying_data.ts index 74ef60f4ae4ea..b6fc6359f4d2c 100644 --- a/x-pack/plugins/lens/public/app_plugin/show_underlying_data.ts +++ b/x-pack/plugins/lens/public/app_plugin/show_underlying_data.ts @@ -14,6 +14,7 @@ import { FilterStateStore, TimeRange, AggregateQuery, + isOfQueryType, } from '@kbn/es-query'; import { i18n } from '@kbn/i18n'; import { RecursiveReadonly } from '@kbn/utility-types'; @@ -22,10 +23,6 @@ import { partition } from 'lodash'; import { TableInspectorAdapter } from '../editor_frame_service/types'; import { Datasource } from '../types'; -function isOfQueryType(arg: Query | AggregateQuery): arg is Query { - return Boolean(arg && 'query' in arg); -} - /** * Joins a series of queries. * diff --git a/x-pack/plugins/observability/public/pages/alerts/components/alerts_search_bar.tsx b/x-pack/plugins/observability/public/pages/alerts/components/alerts_search_bar.tsx index 10eb587496a80..c2c54be1e6f06 100644 --- a/x-pack/plugins/observability/public/pages/alerts/components/alerts_search_bar.tsx +++ b/x-pack/plugins/observability/public/pages/alerts/components/alerts_search_bar.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import { DataViewBase, Query, AggregateQuery } from '@kbn/es-query'; +import { DataViewBase, isOfQueryType } from '@kbn/es-query'; import React, { useMemo, useState } from 'react'; import { TimeHistory } from '@kbn/data-plugin/public'; import { DataView } from '@kbn/data-views-plugin/public'; @@ -14,9 +14,6 @@ import { Storage } from '@kbn/kibana-utils-plugin/public'; import { translations } from '../../../config'; type QueryLanguageType = 'lucene' | 'kuery'; -function isOfQueryType(arg: Query | AggregateQuery): arg is Query { - return Boolean(arg && 'query' in arg); -} export function AlertsSearchBar({ dynamicIndexPatterns, diff --git a/x-pack/plugins/security_solution/public/common/components/query_bar/index.tsx b/x-pack/plugins/security_solution/public/common/components/query_bar/index.tsx index d01f497cbaf7c..474432fb182b4 100644 --- a/x-pack/plugins/security_solution/public/common/components/query_bar/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/query_bar/index.tsx @@ -9,6 +9,7 @@ import React, { memo, useMemo, useCallback } from 'react'; import deepEqual from 'fast-deep-equal'; import type { DataViewBase, Filter, Query, TimeRange, AggregateQuery } from '@kbn/es-query'; +import { isOfQueryType } from '@kbn/es-query'; import { FilterManager, TimeHistory, @@ -19,10 +20,6 @@ import { DataView } from '@kbn/data-views-plugin/public'; import { SearchBar, SearchBarProps } from '@kbn/unified-search-plugin/public'; import { Storage } from '@kbn/kibana-utils-plugin/public'; -function isOfQueryType(arg: Query | AggregateQuery): arg is Query { - return Boolean(arg && 'query' in arg); -} - export interface QueryBarComponentProps { dataTestSubj?: string; dateRangeFrom?: string; diff --git a/x-pack/plugins/security_solution/public/common/components/search_bar/index.tsx b/x-pack/plugins/security_solution/public/common/components/search_bar/index.tsx index 8916e5f50cc74..f63184f9402e9 100644 --- a/x-pack/plugins/security_solution/public/common/components/search_bar/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/search_bar/index.tsx @@ -14,6 +14,7 @@ import { Subscription } from 'rxjs'; import deepEqual from 'fast-deep-equal'; import type { DataViewBase, Filter, Query, TimeRange, AggregateQuery } from '@kbn/es-query'; +import { isOfQueryType } from '@kbn/es-query'; import type { FilterManager, SavedQuery } from '@kbn/data-plugin/public'; import type { DataView } from '@kbn/data-views-plugin/public'; @@ -40,10 +41,6 @@ import { usersActions } from '../../../users/store'; import { hostsActions } from '../../../hosts/store'; import { networkActions } from '../../../network/store'; -function isOfQueryType(arg?: Query | AggregateQuery): arg is Query { - return Boolean(arg && 'query' in arg); -} - const APP_STATE_STORAGE_KEY = 'securitySolution.searchBar.appState'; interface SiemSearchBarProps { diff --git a/x-pack/plugins/security_solution/public/common/components/url_state/initialize_redux_by_url.tsx b/x-pack/plugins/security_solution/public/common/components/url_state/initialize_redux_by_url.tsx index 75b21068a947c..89bf45681691c 100644 --- a/x-pack/plugins/security_solution/public/common/components/url_state/initialize_redux_by_url.tsx +++ b/x-pack/plugins/security_solution/public/common/components/url_state/initialize_redux_by_url.tsx @@ -10,7 +10,8 @@ import { Dispatch } from 'redux'; import { useCallback, useMemo } from 'react'; import { useDispatch } from 'react-redux'; -import type { Filter, Query, AggregateQuery } from '@kbn/es-query'; +import type { Filter, Query } from '@kbn/es-query'; +import { isOfQueryType } from '@kbn/es-query'; import { inputsActions } from '../../store/actions'; import { InputsModelId, TimeRangeKinds } from '../../store/inputs/constants'; import { @@ -30,10 +31,6 @@ import { } from '../../../timelines/components/open_timeline/helpers'; import { timelineActions } from '../../../timelines/store/timeline'; -function isOfQueryType(arg: Query | AggregateQuery): arg is Query { - return Boolean(arg && 'query' in arg); -} - export const useSetInitialStateFromUrl = () => { const dispatch = useDispatch(); diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/query_bar/index.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/query_bar/index.tsx index 7c22a590b48c4..05f080bca1587 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/query_bar/index.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/rules/query_bar/index.tsx @@ -11,6 +11,7 @@ import { Subscription } from 'rxjs'; import styled from 'styled-components'; import deepEqual from 'fast-deep-equal'; import type { DataViewBase, Filter, Query, AggregateQuery } from '@kbn/es-query'; +import { isOfQueryType } from '@kbn/es-query'; import { FilterManager, SavedQuery } from '@kbn/data-plugin/public'; import { BrowserFields } from '../../../../common/containers/source'; @@ -26,10 +27,6 @@ import { useSavedQueryServices } from '../../../../common/utils/saved_query_serv import { FieldHook, getFieldValidityAndErrorMessage } from '../../../../shared_imports'; import * as i18n from './translations'; -function isOfQueryType(arg: Query | AggregateQuery): arg is Query { - return Boolean(arg && 'query' in arg); -} - export interface FieldValueQueryBar { filters: Filter[]; query: Query; diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/components/search_bar.tsx b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/components/search_bar.tsx index 3bee9841d52e7..95f2cf27886ce 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/components/search_bar.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/components/search_bar.tsx @@ -9,6 +9,7 @@ import React, { memo, useCallback, useMemo } from 'react'; import { useHistory } from 'react-router-dom'; import { encode, RisonValue } from 'rison-node'; import type { Query, AggregateQuery } from '@kbn/es-query'; +import { isOfQueryType } from '@kbn/es-query'; import { TimeHistory } from '@kbn/data-plugin/public'; import { DataView } from '@kbn/data-views-plugin/public'; import { SearchBar } from '@kbn/unified-search-plugin/public'; @@ -18,10 +19,6 @@ import { useEndpointSelector } from '../hooks'; import * as selectors from '../../store/selectors'; import { clone } from '../../models/index_pattern'; -function isOfQueryType(arg?: Query | AggregateQuery): arg is Query { - return Boolean(arg && 'query' in arg); -} - export const AdminSearchBar = memo(() => { const history = useHistory(); const { admin_query: _, ...queryParams } = useEndpointSelector(selectors.uiQueryParams); diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/query_bar/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/query_bar/index.tsx index d28efff5068c0..e41016f745b91 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/query_bar/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/query_bar/index.tsx @@ -11,7 +11,7 @@ import { useDispatch } from 'react-redux'; import { Subscription } from 'rxjs'; import deepEqual from 'fast-deep-equal'; -import { FilterStateStore, Filter, Query, AggregateQuery } from '@kbn/es-query'; +import { FilterStateStore, Filter, Query, AggregateQuery, isOfQueryType } from '@kbn/es-query'; import type { FilterManager, SavedQuery, SavedQueryTimeFilter } from '@kbn/data-plugin/public'; import { useSourcererDataView } from '../../../../common/containers/sourcerer'; import { SourcererScopeName } from '../../../../common/store/sourcerer/model'; @@ -45,10 +45,6 @@ export interface QueryBarTimelineComponentProps { updateReduxTime: DispatchUpdateReduxTime; } -function isOfQueryType(arg?: Query | AggregateQuery): arg is Query { - return Boolean(arg && 'query' in arg); -} - export const TIMELINE_FILTER_DROP_AREA = 'timeline-filter-drop-area'; const getNonDropAreaFilters = (filters: Filter[] = []) => diff --git a/x-pack/plugins/stack_alerts/public/alert_types/es_query/expression/search_source_expression_form.tsx b/x-pack/plugins/stack_alerts/public/alert_types/es_query/expression/search_source_expression_form.tsx index 7d7cf8e1c42c5..128cb5aa45e36 100644 --- a/x-pack/plugins/stack_alerts/public/alert_types/es_query/expression/search_source_expression_form.tsx +++ b/x-pack/plugins/stack_alerts/public/alert_types/es_query/expression/search_source_expression_form.tsx @@ -8,7 +8,7 @@ import React, { Fragment, useCallback, useEffect, useMemo, useReducer, useState } from 'react'; import deepEqual from 'fast-deep-equal'; import { lastValueFrom } from 'rxjs'; -import { Filter, AggregateQuery } from '@kbn/es-query'; +import { Filter, AggregateQuery, isOfQueryType } from '@kbn/es-query'; import { FormattedMessage } from '@kbn/i18n-react'; import { EuiSpacer, EuiTitle } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; @@ -29,10 +29,6 @@ import { useTriggersAndActionsUiDeps } from '../util'; import { totalHitsToNumber } from './use_test_query'; import { TestQueryRow } from './test_query_row'; -function isOfQueryType(arg?: Query | AggregateQuery): arg is Query { - return Boolean(arg && 'query' in arg); -} - interface LocalState { index: DataView; filter: Filter[]; From 84c5128c3fab001ecfa9fe2988473ad0742d7918 Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Wed, 29 Jun 2022 11:59:13 +0300 Subject: [PATCH 064/115] fix bug on breadcrumb click --- packages/kbn-es-query/src/es_query/build_es_query.ts | 4 +--- .../public/dataview_picker/change_dataview.tsx | 6 ++++++ .../public/query_string_input/query_bar_top_row.tsx | 4 ++-- .../public/search_bar/lib/use_query_string_manager.ts | 7 +++++-- 4 files changed, 14 insertions(+), 7 deletions(-) diff --git a/packages/kbn-es-query/src/es_query/build_es_query.ts b/packages/kbn-es-query/src/es_query/build_es_query.ts index 6bd6e1ea4438f..3ff2f0f650001 100644 --- a/packages/kbn-es-query/src/es_query/build_es_query.ts +++ b/packages/kbn-es-query/src/es_query/build_es_query.ts @@ -12,6 +12,7 @@ import { buildQueryFromKuery } from './from_kuery'; import { buildQueryFromFilters } from './from_filters'; import { buildQueryFromLucene } from './from_lucene'; import { Filter, Query, AggregateQuery } from '../filters'; +import { isOfQueryType } from './es_query.sql'; import { BoolQuery, DataViewBase } from './types'; import type { KueryQueryOptions } from '../kuery'; import type { EsQueryFiltersConfig } from './from_filters'; @@ -31,9 +32,6 @@ function removeMatchAll(filters: T[]) { (filter) => !filter || typeof filter !== 'object' || !isEqual(filter, { match_all: {} }) ); } -function isOfQueryType(arg: Query | AggregateQuery): arg is Query { - return Boolean(arg && 'query' in arg); -} /** * @param indexPattern diff --git a/src/plugins/unified_search/public/dataview_picker/change_dataview.tsx b/src/plugins/unified_search/public/dataview_picker/change_dataview.tsx index 1c10573a27f22..e77597ca74d5c 100644 --- a/src/plugins/unified_search/public/dataview_picker/change_dataview.tsx +++ b/src/plugins/unified_search/public/dataview_picker/change_dataview.tsx @@ -146,6 +146,12 @@ export function ChangeDataView({ } }, [textBasedLanguage, trigger.label]); + useEffect(() => { + if (Boolean(textBasedLanguage) !== isTextBasedLangSelected) { + setIsTextBasedLangSelected(Boolean(textBasedLanguage)); + } + }, [isTextBasedLangSelected, textBasedLanguage]); + const createTrigger = function () { const { label, title, 'data-test-subj': dataTestSubj, fullWidth, ...rest } = trigger; return ( diff --git a/src/plugins/unified_search/public/query_string_input/query_bar_top_row.tsx b/src/plugins/unified_search/public/query_string_input/query_bar_top_row.tsx index 589ed89d22b60..8cdb14f944796 100644 --- a/src/plugins/unified_search/public/query_string_input/query_bar_top_row.tsx +++ b/src/plugins/unified_search/public/query_string_input/query_bar_top_row.tsx @@ -12,7 +12,7 @@ import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react' import deepEqual from 'fast-deep-equal'; import useObservable from 'react-use/lib/useObservable'; import type { Filter, TimeRange, Query, AggregateQuery } from '@kbn/es-query'; -import { isOfQueryType } from '@kbn/es-query'; +import { getAggregateQueryMode, isOfQueryType } from '@kbn/es-query'; import { EMPTY } from 'rxjs'; import { map } from 'rxjs/operators'; import { @@ -436,7 +436,7 @@ export const QueryBarTopRow = React.memo( let textBasedLanguage; if (Boolean(isQueryLangSelected)) { const query = props.query as AggregateQuery; - textBasedLanguage = Object.keys(query)[0]; + textBasedLanguage = getAggregateQueryMode(query); } return ( diff --git a/src/plugins/unified_search/public/search_bar/lib/use_query_string_manager.ts b/src/plugins/unified_search/public/search_bar/lib/use_query_string_manager.ts index 3f72d6eacc630..6bd27727b28db 100644 --- a/src/plugins/unified_search/public/search_bar/lib/use_query_string_manager.ts +++ b/src/plugins/unified_search/public/search_bar/lib/use_query_string_manager.ts @@ -8,10 +8,13 @@ import { useState, useEffect, useMemo } from 'react'; import { Subscription } from 'rxjs'; -import type { Query, AggregateQuery } from '@kbn/es-query'; -import { isOfQueryType } from '@kbn/es-query'; +import { Query, AggregateQuery } from '@kbn/es-query'; import type { QueryStringContract } from '@kbn/data-plugin/public'; +function isOfQueryType(arg: Query | AggregateQuery): arg is Query { + return Boolean(arg && 'query' in arg); +} + interface UseQueryStringProps { query?: Query | AggregateQuery; queryStringManager: QueryStringContract; From d32ff4fccb51345d8e728da155c10e541cbefc5f Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Wed, 29 Jun 2022 13:08:24 +0300 Subject: [PATCH 065/115] Fix time field bug --- .../data/common/query/to_expression_ast.ts | 8 +++--- .../main/hooks/use_discover_state.ts | 26 +++++++++++++++++++ 2 files changed, 31 insertions(+), 3 deletions(-) diff --git a/src/plugins/data/common/query/to_expression_ast.ts b/src/plugins/data/common/query/to_expression_ast.ts index c7997724f1333..a3102b12cffec 100644 --- a/src/plugins/data/common/query/to_expression_ast.ts +++ b/src/plugins/data/common/query/to_expression_ast.ts @@ -46,9 +46,11 @@ export async function queryStateToExpressionAst({ filters, query, time, dataView const mode = getAggregateQueryMode(query); if (mode === 'sql') { const idxPattern = getIndexPatternFromSQLQuery(query.sql); - const dataView = await dataViewsService.find(idxPattern); - if (dataView && dataView.length) { - const timeFieldName = dataView[0].timeFieldName; + const idsTitles = await dataViewsService.getIdsWithTitle(); + const dataViewIdTitle = idsTitles.find(({ title }) => title === idxPattern); + if (dataViewIdTitle) { + const dataView = await dataViewsService.get(dataViewIdTitle.id); + const timeFieldName = dataView.timeFieldName; const essql = aggregateQueryToAst(query, timeFieldName); if (essql) { diff --git a/src/plugins/discover/public/application/main/hooks/use_discover_state.ts b/src/plugins/discover/public/application/main/hooks/use_discover_state.ts index 2d82e12824f04..109019ca945ba 100644 --- a/src/plugins/discover/public/application/main/hooks/use_discover_state.ts +++ b/src/plugins/discover/public/application/main/hooks/use_discover_state.ts @@ -8,6 +8,7 @@ import { useMemo, useEffect, useState, useCallback } from 'react'; import { isEqual } from 'lodash'; import { History } from 'history'; +import { isOfAggregateQueryType } from '@kbn/es-query'; import { getState } from '../services/discover_state'; import { getStateDefaults } from '../utils/get_state_defaults'; import { DiscoverServices } from '../../../build_services'; @@ -26,6 +27,15 @@ import { getSwitchIndexPatternAppState } from '../utils/get_switch_index_pattern import { SortPairArr } from '../../../components/doc_table/utils/get_sort'; import { DataTableRecord } from '../../../types'; +function getIndexPatternFromSQLQuery(sqlQuery?: string): string { + const sql = sqlQuery?.replaceAll('"', ''); + const matches = sql?.match(/FROM\s+([\w*]+)/); + if (matches) { + return matches[1]; + } + return ''; +} + export function useDiscoverState({ services, history, @@ -42,6 +52,7 @@ export function useDiscoverState({ const { timefilter } = data.query.timefilter; const indexPattern = savedSearch.searchSource.getField('index')!; + // console.log(indexPattern); const searchSource = useMemo(() => { savedSearch.searchSource.setField('index', indexPattern); @@ -226,6 +237,21 @@ export function useDiscoverState({ } }, [initialFetchStatus, refetch$, indexPattern, savedSearch.id]); + useEffect(() => { + async function fetchDataview() { + if (state.query && isOfAggregateQueryType(state.query)) { + const indexPatternFROMQuery = getIndexPatternFromSQLQuery(state.query.sql); + const idsTitles = await indexPatterns.getIdsWithTitle(); + const dataView = idsTitles.find(({ title }) => title === indexPatternFROMQuery); + if (dataView) { + stateContainer.setAppState({ index: dataView.id }); + } + } + } + + fetchDataview(); + }, [config, indexPatterns, savedSearch.searchSource, state.query, stateContainer]); + return { data$, indexPattern, From 3b8887087d38db94e5b1e2faa08c8ef5b65bbef4 Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Thu, 30 Jun 2022 10:11:29 +0300 Subject: [PATCH 066/115] Add enableSql advanced setting to discover for enabling the sql mode --- docs/management/advanced-options.asciidoc | 3 +++ src/plugins/discover/common/index.ts | 1 + .../main/components/top_nav/discover_topnav.tsx | 11 ++++++++--- src/plugins/discover/server/ui_settings.ts | 13 +++++++++++++ .../server/collectors/management/schema.ts | 4 ++++ .../server/collectors/management/types.ts | 1 + src/plugins/telemetry/schema/oss_plugins.json | 6 ++++++ 7 files changed, 36 insertions(+), 3 deletions(-) diff --git a/docs/management/advanced-options.asciidoc b/docs/management/advanced-options.asciidoc index 5ed2d734a81c2..2b64d02d0fb69 100644 --- a/docs/management/advanced-options.asciidoc +++ b/docs/management/advanced-options.asciidoc @@ -316,6 +316,9 @@ The default sort direction for time-based data views. [[doctable-hidetimecolumn]]`doc_table:hideTimeColumn`:: Hides the "Time" column in *Discover* and in all saved searches on dashboards. +[[discover:enableSql]]`discover:enableSql`:: +When enabled, allows SQL queries for search. + [[doctable-highlight]]`doc_table:highlight`:: Highlights results in *Discover* and saved searches on dashboards. Highlighting slows requests when working on big documents. diff --git a/src/plugins/discover/common/index.ts b/src/plugins/discover/common/index.ts index b5f2238430d02..40f2a520a30d7 100644 --- a/src/plugins/discover/common/index.ts +++ b/src/plugins/discover/common/index.ts @@ -27,3 +27,4 @@ export const TRUNCATE_MAX_HEIGHT = 'truncate:maxHeight'; export const ROW_HEIGHT_OPTION = 'discover:rowHeightOption'; export const SEARCH_EMBEDDABLE_TYPE = 'search'; export const HIDE_ANNOUNCEMENTS = 'hideAnnouncements'; +export const ENABLE_SQL = 'discover:enableSql'; diff --git a/src/plugins/discover/public/application/main/components/top_nav/discover_topnav.tsx b/src/plugins/discover/public/application/main/components/top_nav/discover_topnav.tsx index 3b84af40a17a3..efda8294cbd08 100644 --- a/src/plugins/discover/public/application/main/components/top_nav/discover_topnav.tsx +++ b/src/plugins/discover/public/application/main/components/top_nav/discover_topnav.tsx @@ -10,6 +10,7 @@ import { useHistory } from 'react-router-dom'; import type { Query, TimeRange, AggregateQuery } from '@kbn/es-query'; import { DataViewType } from '@kbn/data-views-plugin/public'; import type { DataViewPickerProps } from '@kbn/unified-search-plugin/public'; +import { ENABLE_SQL } from '../../../../../common'; import { useDiscoverServices } from '../../../../hooks/use_discover_services'; import { DiscoverLayoutProps } from '../layout/types'; import { getTopNavLinks } from './get_top_nav_links'; @@ -58,7 +59,7 @@ export const DiscoverTopNav = ({ [indexPattern] ); const services = useDiscoverServices(); - const { dataViewEditor, navigation, dataViewFieldEditor, data } = services; + const { dataViewEditor, navigation, dataViewFieldEditor, data, uiSettings } = services; const canEditDataView = Boolean(dataViewEditor?.userPermissions.editDataView()); @@ -174,7 +175,11 @@ export const DiscoverTopNav = ({ const setMenuMountPoint = useMemo(() => { return getHeaderActionMenuMounter(); }, []); - + const SQLModeIsEnabled = uiSettings.get(ENABLE_SQL); + const supportedTextBasedLanguages = []; + if (SQLModeIsEnabled) { + supportedTextBasedLanguages.push('SQL'); + } const dataViewPickerProps = { trigger: { label: indexPattern?.getName() || '', @@ -185,7 +190,7 @@ export const DiscoverTopNav = ({ onAddField: addField, onDataViewCreated: createNewDataView, onChangeDataView: (newIndexPatternId: string) => onChangeIndexPattern(newIndexPatternId), - textBasedLanguages: ['SQL'] as DataViewPickerProps['textBasedLanguages'], + textBasedLanguages: supportedTextBasedLanguages as DataViewPickerProps['textBasedLanguages'], }; const onTextBasedSavedAndExit = useCallback( diff --git a/src/plugins/discover/server/ui_settings.ts b/src/plugins/discover/server/ui_settings.ts index 46e2d4d9969c6..164ec1b92c13a 100644 --- a/src/plugins/discover/server/ui_settings.ts +++ b/src/plugins/discover/server/ui_settings.ts @@ -29,6 +29,7 @@ import { TRUNCATE_MAX_HEIGHT, SHOW_FIELD_STATISTICS, ROW_HEIGHT_OPTION, + ENABLE_SQL, } from '../common'; export const getUiSettings: (docLinks: DocLinksServiceSetup) => Record = ( @@ -288,4 +289,16 @@ export const getUiSettings: (docLinks: DocLinksServiceSetup) => Record = { type: 'boolean', _meta: { description: 'Non-default value of setting.' }, }, + 'discover:enableSql': { + type: 'boolean', + _meta: { description: 'Non-default value of setting.' }, + }, }; diff --git a/src/plugins/kibana_usage_collection/server/collectors/management/types.ts b/src/plugins/kibana_usage_collection/server/collectors/management/types.ts index de22ac0cecb8a..1332ac15b2246 100644 --- a/src/plugins/kibana_usage_collection/server/collectors/management/types.ts +++ b/src/plugins/kibana_usage_collection/server/collectors/management/types.ts @@ -33,6 +33,7 @@ export interface UsageStats { 'discover:searchFieldsFromSource': boolean; 'discover:showFieldStatistics': boolean; 'discover:showMultiFields': boolean; + 'discover:enableSql': boolean; 'discover:maxDocFieldsDisplayed': number; 'securitySolution:rulesTableRefresh': string; 'observability:enableInspectEsQueries': boolean; diff --git a/src/plugins/telemetry/schema/oss_plugins.json b/src/plugins/telemetry/schema/oss_plugins.json index b234c0b1e3599..db2c4828471c4 100644 --- a/src/plugins/telemetry/schema/oss_plugins.json +++ b/src/plugins/telemetry/schema/oss_plugins.json @@ -8490,6 +8490,12 @@ "_meta": { "description": "Non-default value of setting." } + }, + "discover:enableSql": { + "type": "boolean", + "_meta": { + "description": "Non-default value of setting." + } } } }, From 9ac6c2711f9d6a08e0737de0e006fa9462877fdf Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Thu, 30 Jun 2022 11:32:11 +0300 Subject: [PATCH 067/115] Make the documentation component more dynamic --- .../documentation.tsx | 93 ++++++------------- .../text_based_languages_editor/helpers.ts | 25 +++++ .../text_based_languages_editor/index.tsx | 25 +++-- ...ons.tsx => sql_documentation_sections.tsx} | 34 +++++++ 4 files changed, 105 insertions(+), 72 deletions(-) rename src/plugins/unified_search/public/query_string_input/text_based_languages_editor/{documentation_sections.tsx => sql_documentation_sections.tsx} (91%) diff --git a/src/plugins/unified_search/public/query_string_input/text_based_languages_editor/documentation.tsx b/src/plugins/unified_search/public/query_string_input/text_based_languages_editor/documentation.tsx index 816a2726d4eb9..9d8ede95bc17a 100644 --- a/src/plugins/unified_search/public/query_string_input/text_based_languages_editor/documentation.tsx +++ b/src/plugins/unified_search/public/query_string_input/text_based_languages_editor/documentation.tsx @@ -20,40 +20,38 @@ import { EuiHighlight, EuiSpacer, } from '@elastic/eui'; -import { Markdown } from '@kbn/kibana-react-plugin/public'; -import { comparisonOperators, logicalOperators, mathOperators } from './documentation_sections'; import './documentation.scss'; -function Documentation() { - const [selectedOperator, setSelectedOperator] = useState(); - const scrollTargets = useRef>({}); - - useEffect(() => { - if (selectedOperator && scrollTargets.current[selectedOperator]) { - scrollTargets.current[selectedOperator].scrollIntoView(); - } - }, [selectedOperator]); - - const groups: Array<{ +export interface DocumentationSections { + groups: Array<{ label: string; description?: string; items: Array<{ label: string; description?: JSX.Element }>; - }> = []; + }>; + initialSection: JSX.Element; +} - groups.push({ - label: i18n.translate('unifiedSearch.query.textBasedLanguagesEditor.howItWorks', { - defaultMessage: 'How it works', - }), - items: [], - }); - groups.push(comparisonOperators, logicalOperators, mathOperators); +interface DocumentationProps { + language: string; + sections?: DocumentationSections; +} + +function Documentation({ language, sections }: DocumentationProps) { + const [selectedSection, setSelectedSection] = useState(); + const scrollTargets = useRef>({}); + + useEffect(() => { + if (selectedSection && scrollTargets.current[selectedSection]) { + scrollTargets.current[selectedSection].scrollIntoView(); + } + }, [selectedSection]); const [searchText, setSearchText] = useState(''); const normalizedSearchText = searchText.trim().toLocaleLowerCase(); - const filteredGroups = groups + const filteredGroups = sections?.groups .map((group) => { const items = group.items.filter((helpItem) => { return ( @@ -73,7 +71,8 @@ function Documentation() { <> {i18n.translate('unifiedSearch.query.textBasedLanguagesEditor.documentation.header', { - defaultMessage: 'SQL reference', + defaultMessage: '{language} reference', + values: { language: language.toUpperCase() }, })} - {filteredGroups.map((helpGroup, index) => { + {filteredGroups?.map((helpGroup, index) => { return (
diff --git a/src/plugins/discover/public/application/main/components/layout/discover_layout.tsx b/src/plugins/discover/public/application/main/components/layout/discover_layout.tsx index 0108e1fa16f1b..1fdc30a06f9ae 100644 --- a/src/plugins/discover/public/application/main/components/layout/discover_layout.tsx +++ b/src/plugins/discover/public/application/main/components/layout/discover_layout.tsx @@ -36,7 +36,7 @@ import { DocViewFilterFn } from '../../../../services/doc_views/doc_views_types' import { DiscoverChart } from '../chart'; import { getResultState } from '../../utils/get_result_state'; import { DiscoverUninitialized } from '../uninitialized/uninitialized'; -import { DataMainMsg } from '../../hooks/use_saved_search'; +import { DataMainMsg, RecordRawType } from '../../hooks/use_saved_search'; import { useColumns } from '../../../../hooks/use_data_grid_columns'; import { DiscoverDocuments } from './discover_documents'; import { FetchStatus } from '../../../types'; @@ -44,9 +44,9 @@ import { useDataState } from '../../hooks/use_data_state'; import { SavedSearchURLConflictCallout } from '../../../../services/saved_searches'; import { FieldStatisticsTable } from '../field_stats_table'; import { VIEW_MODE } from '../../../../components/view_mode_toggle'; -import { getTextBasedLanguageMode } from '../../../../utils/get_text_based_language_mode'; import { DOCUMENTS_VIEW_CLICK, FIELD_STATISTICS_VIEW_CLICK } from '../field_stats_table/constants'; import { hasActiveFilter } from './utils'; +import { DiscoverLayoutContextProvider } from './discover_layout_context'; /** * Local storage key for sidebar persistence state @@ -89,6 +89,7 @@ export function DiscoverLayout({ } = useDiscoverServices(); const { main$, charts$, totalHits$ } = savedSearchData$; const [inspectorSession, setInspectorSession] = useState(undefined); + const dataState: DataMainMsg = useDataState(main$); const viewMode = useMemo(() => { if (uiSettings.get(SHOW_FIELD_STATISTICS) !== true) return VIEW_MODE.DOCUMENT_LEVEL; @@ -111,7 +112,6 @@ export function DiscoverLayout({ ); const fetchCounter = useRef(0); - const dataState: DataMainMsg = useDataState(main$); useEffect(() => { if (dataState.fetchStatus === FetchStatus.LOADING) { @@ -132,14 +132,10 @@ export function DiscoverLayout({ const useNewFieldsApi = useMemo(() => !uiSettings.get(SEARCH_FIELDS_FROM_SOURCE), [uiSettings]); const resultState = useMemo( - () => - getResultState( - dataState.fetchStatus, - dataState.foundDocuments!, - dataState.textBasedLanguageMode - ), - [dataState.fetchStatus, dataState.foundDocuments, dataState.textBasedLanguageMode] + () => getResultState(dataState.fetchStatus, dataState.foundDocuments!, dataState.recordRawType), + [dataState.fetchStatus, dataState.foundDocuments, dataState.recordRawType] ); + const isPlainRecord = dataState.recordRawType === RecordRawType.PLAIN; const onOpenInspector = useCallback(() => { // prevent overlapping @@ -169,8 +165,6 @@ export function DiscoverLayout({ useNewFieldsApi, }); - const textBasedLanguageMode = state.query ? getTextBasedLanguageMode(state.query) : ''; - const onAddFilter = useCallback( (field: DataViewField | string, values: string, operation: '+' | '-') => { const fieldName = typeof field === 'string' ? field : field.name; @@ -216,181 +210,179 @@ export function DiscoverLayout({ }, []); const textBasedLanguageModeErrors = useMemo(() => { - if (dataState.textBasedLanguageMode) { + if (dataState.recordRawType) { return dataState.error; } - }, [dataState.error, dataState.textBasedLanguageMode]); + }, [dataState.error, dataState.recordRawType]); return ( - -

- {savedSearch.title - ? i18n.translate('discover.pageTitleWithSavedSearch', { - defaultMessage: 'Discover - {savedSearchTitle}', - values: { - savedSearchTitle: savedSearch.title, - }, - }) - : i18n.translate('discover.pageTitleWithoutSavedSearch', { - defaultMessage: 'Discover - Search not yet saved', - })} -

- - - + +

+ {savedSearch.title + ? i18n.translate('discover.pageTitleWithSavedSearch', { + defaultMessage: 'Discover - {savedSearchTitle}', + values: { + savedSearchTitle: savedSearch.title, + }, + }) + : i18n.translate('discover.pageTitleWithoutSavedSearch', { + defaultMessage: 'Discover - Search not yet saved', + })} +

+ - - - - - + + + -
- - -
+
-
- - - {resultState === 'none' && ( - - )} - {resultState === 'uninitialized' && ( - savedSearchRefetch$.next(undefined)} /> - )} - {resultState === 'loading' && } - {resultState === 'ready' && ( - - {!textBasedLanguageMode && ( - <> - - - - - - )} - {viewMode === VIEW_MODE.DOCUMENT_LEVEL ? ( - - ) : ( - - )} - - )} - - -
-
-
+ + +
+ + +
+
+
+ + + {resultState === 'none' && ( + + )} + {resultState === 'uninitialized' && ( + savedSearchRefetch$.next(undefined)} /> + )} + {resultState === 'loading' && } + {resultState === 'ready' && ( + + {!isPlainRecord && ( + <> + + + + + + )} + + {viewMode === VIEW_MODE.DOCUMENT_LEVEL ? ( + + ) : ( + + )} + + )} + + + + + + ); } diff --git a/src/plugins/discover/public/application/main/components/layout/discover_layout_context.tsx b/src/plugins/discover/public/application/main/components/layout/discover_layout_context.tsx new file mode 100644 index 0000000000000..09d41db732f43 --- /dev/null +++ b/src/plugins/discover/public/application/main/components/layout/discover_layout_context.tsx @@ -0,0 +1,40 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import React, { ReactNode, useMemo, useState } from 'react'; + +export interface DiscoverLayoutContextProps { + isPlainRecord: boolean; + setIsPlainRecord: (newIsPlainRecord: boolean) => void; +} + +const defaultContext = {}; + +export const DiscoverLayoutContext = React.createContext( + defaultContext as DiscoverLayoutContextProps +); + +export const DiscoverLayoutContextProvider = ({ + initialIsPlainRecord, + children, +}: { + initialIsPlainRecord: boolean; + children: ReactNode; +}) => { + const [isPlainRecord, setIsPlainRecord] = useState(initialIsPlainRecord); + + const value = useMemo( + () => ({ + isPlainRecord, + setIsPlainRecord, + }), + [isPlainRecord] + ); + + return {children}; +}; diff --git a/src/plugins/discover/public/application/main/components/sidebar/discover_sidebar_responsive.tsx b/src/plugins/discover/public/application/main/components/sidebar/discover_sidebar_responsive.tsx index 88dccabc9e1d7..f090863f8ebe7 100644 --- a/src/plugins/discover/public/application/main/components/sidebar/discover_sidebar_responsive.tsx +++ b/src/plugins/discover/public/application/main/components/sidebar/discover_sidebar_responsive.tsx @@ -6,23 +6,23 @@ * Side Public License, v 1. */ -import React, { useEffect, useRef, useState, useCallback, useMemo } from 'react'; +import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; import { UiCounterMetricType } from '@kbn/analytics'; import { - EuiTitle, - EuiHideFor, - EuiShowFor, - EuiButton, EuiBadge, - EuiFlyoutHeader, + EuiButton, EuiFlyout, + EuiFlyoutHeader, + EuiHideFor, EuiIcon, EuiLink, EuiPortal, + EuiShowFor, + EuiTitle, } from '@elastic/eui'; -import type { DataViewField, DataView, DataViewAttributes } from '@kbn/data-views-plugin/public'; +import type { DataView, DataViewAttributes, DataViewField } from '@kbn/data-views-plugin/public'; import { SavedObject } from '@kbn/core/types'; import { useDiscoverServices } from '../../../../hooks/use_discover_services'; import { getDefaultFieldFilter } from './lib/field_filter'; @@ -33,6 +33,7 @@ import { calcFieldCounts } from '../../utils/calc_field_counts'; import { VIEW_MODE } from '../../../../components/view_mode_toggle'; import { FetchStatus } from '../../../types'; import { DISCOVER_TOUR_STEP_ANCHOR_IDS } from '../../../../components/discover_tour'; +import { useDiscoverLayoutContext } from '../../hooks/use_discover_layout_context'; export interface DiscoverSidebarResponsiveProps { /** @@ -115,6 +116,7 @@ export interface DiscoverSidebarResponsiveProps { */ export function DiscoverSidebarResponsive(props: DiscoverSidebarResponsiveProps) { const services = useDiscoverServices(); + const { isPlainRecord } = useDiscoverLayoutContext(); const { selectedIndexPattern, onEditRuntimeField, onDataViewCreated } = props; const [fieldFilter, setFieldFilter] = useState(getDefaultFieldFilter()); const [isFlyoutVisible, setIsFlyoutVisible] = useState(false); @@ -210,7 +212,7 @@ export function DiscoverSidebarResponsive(props: DiscoverSidebarResponsiveProps) const editField = useMemo( () => - !documentState.textBasedLanguageMode && canEditDataView && selectedIndexPattern + !isPlainRecord && canEditDataView && selectedIndexPattern ? (fieldName?: string) => { const ref = dataViewFieldEditor.openEditor({ ctx: { @@ -230,7 +232,7 @@ export function DiscoverSidebarResponsive(props: DiscoverSidebarResponsiveProps) } : undefined, [ - documentState.textBasedLanguageMode, + isPlainRecord, canEditDataView, selectedIndexPattern, dataViewFieldEditor, diff --git a/src/plugins/discover/public/application/main/components/top_nav/discover_topnav.tsx b/src/plugins/discover/public/application/main/components/top_nav/discover_topnav.tsx index efda8294cbd08..b37dcb998d3ad 100644 --- a/src/plugins/discover/public/application/main/components/top_nav/discover_topnav.tsx +++ b/src/plugins/discover/public/application/main/components/top_nav/discover_topnav.tsx @@ -5,7 +5,7 @@ * in compliance with, at your election, the Elastic License 2.0 or the Server * Side Public License, v 1. */ -import React, { useCallback, useMemo, useRef, useEffect } from 'react'; +import React, { useCallback, useEffect, useMemo, useRef } from 'react'; import { useHistory } from 'react-router-dom'; import type { Query, TimeRange, AggregateQuery } from '@kbn/es-query'; import { DataViewType } from '@kbn/data-views-plugin/public'; @@ -17,13 +17,14 @@ import { getTopNavLinks } from './get_top_nav_links'; import { getHeaderActionMenuMounter } from '../../../../kibana_services'; import { GetStateReturn } from '../../services/discover_state'; import { onSaveSearch } from './on_save_search'; +import { useDiscoverLayoutContext } from '../../hooks/use_discover_layout_context'; export type DiscoverTopNavProps = Pick< DiscoverLayoutProps, 'indexPattern' | 'navigateTo' | 'savedSearch' | 'searchSource' > & { onOpenInspector: () => void; - query?: Query; + query?: Query | AggregateQuery; savedQuery?: string; updateQuery: ( payload: { dateRange: TimeRange; query?: Query | AggregateQuery }, @@ -33,7 +34,6 @@ export type DiscoverTopNavProps = Pick< resetSavedSearch: () => void; onChangeIndexPattern: (indexPattern: string) => void; onEditRuntimeField: () => void; - textBasedLanguageMode?: string; textBasedLanguageModeErrors?: Error; }; @@ -50,13 +50,14 @@ export const DiscoverTopNav = ({ resetSavedSearch, onChangeIndexPattern, onEditRuntimeField, - textBasedLanguageMode, textBasedLanguageModeErrors, }: DiscoverTopNavProps) => { const history = useHistory(); + const { isPlainRecord, setIsPlainRecord } = useDiscoverLayoutContext(); + const showDatePicker = useMemo( - () => indexPattern.isTimeBased() && indexPattern.type !== DataViewType.ROLLUP, - [indexPattern] + () => !isPlainRecord && indexPattern.isTimeBased() && indexPattern.type !== DataViewType.ROLLUP, + [indexPattern, isPlainRecord] ); const services = useDiscoverServices(); const { dataViewEditor, navigation, dataViewFieldEditor, data, uiSettings } = services; @@ -133,6 +134,11 @@ export const DiscoverTopNav = ({ [canEditDataView, dataViewEditor, onChangeIndexPattern] ); + const onUpdateIsTextQueryLangSelected = useCallback( + (isTextLangSelected: boolean) => setIsPlainRecord(isTextLangSelected), + [setIsPlainRecord] + ); + const topNavMenu = useMemo( () => getTopNavLinks({ @@ -144,7 +150,7 @@ export const DiscoverTopNav = ({ onOpenInspector, searchSource, onOpenSavedSearch, - textBasedLanguageMode, + isPlainRecord, }), [ indexPattern, @@ -155,7 +161,7 @@ export const DiscoverTopNav = ({ onOpenInspector, searchSource, onOpenSavedSearch, - textBasedLanguageMode, + isPlainRecord, ] ); @@ -189,7 +195,8 @@ export const DiscoverTopNav = ({ currentDataViewId: indexPattern?.id, onAddField: addField, onDataViewCreated: createNewDataView, - onChangeDataView: (newIndexPatternId: string) => onChangeIndexPattern(newIndexPatternId), + onChangeDataView: onChangeIndexPattern, + onUpdateIsTextQueryLangSelected, textBasedLanguages: supportedTextBasedLanguages as DataViewPickerProps['textBasedLanguages'], }; @@ -220,7 +227,7 @@ export const DiscoverTopNav = ({ savedQueryId={savedQuery} screenTitle={savedSearch.title} showDatePicker={showDatePicker} - showSaveQuery={!textBasedLanguageMode && Boolean(services.capabilities.discover.saveQuery)} + showSaveQuery={!isPlainRecord && Boolean(services.capabilities.discover.saveQuery)} showSearchBar={true} useDefaultBehaviors={true} dataViewPickerComponentProps={dataViewPickerProps} diff --git a/src/plugins/discover/public/application/main/components/top_nav/get_top_nav_links.tsx b/src/plugins/discover/public/application/main/components/top_nav/get_top_nav_links.tsx index 90a1f34453687..b2022dcfdab31 100644 --- a/src/plugins/discover/public/application/main/components/top_nav/get_top_nav_links.tsx +++ b/src/plugins/discover/public/application/main/components/top_nav/get_top_nav_links.tsx @@ -32,7 +32,7 @@ export const getTopNavLinks = ({ onOpenInspector, searchSource, onOpenSavedSearch, - textBasedLanguageMode, + isPlainRecord, }: { indexPattern: DataView; navigateTo: (url: string) => void; @@ -42,7 +42,7 @@ export const getTopNavLinks = ({ onOpenInspector: () => void; searchSource: ISearchSource; onOpenSavedSearch: (id: string) => void; - textBasedLanguageMode?: string; + isPlainRecord: boolean; }): TopNavMenuData[] => { const options = { id: 'options', @@ -198,8 +198,8 @@ export const getTopNavLinks = ({ ...(services.capabilities.advancedSettings.save ? [options] : []), newSearch, openSearch, - ...(services.triggersActionsUi && !textBasedLanguageMode ? [alerts] : []), - ...(!textBasedLanguageMode ? [shareSearch] : []), + ...(services.triggersActionsUi && !isPlainRecord ? [alerts] : []), + ...(!isPlainRecord ? [shareSearch] : []), inspectSearch, ...(services.capabilities.discover.save ? [saveSearch] : []), ]; diff --git a/src/plugins/discover/public/application/main/hooks/use_discover_layout_context.ts b/src/plugins/discover/public/application/main/hooks/use_discover_layout_context.ts new file mode 100644 index 0000000000000..7545cc704cced --- /dev/null +++ b/src/plugins/discover/public/application/main/hooks/use_discover_layout_context.ts @@ -0,0 +1,12 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { useContext } from 'react'; +import { DiscoverLayoutContext } from '../components/layout/discover_layout_context'; + +export const useDiscoverLayoutContext = () => useContext(DiscoverLayoutContext); diff --git a/src/plugins/discover/public/application/main/hooks/use_saved_search.ts b/src/plugins/discover/public/application/main/hooks/use_saved_search.ts index 89bd38c8aca1f..def74bd94e7b1 100644 --- a/src/plugins/discover/public/application/main/hooks/use_saved_search.ts +++ b/src/plugins/discover/public/application/main/hooks/use_saved_search.ts @@ -10,6 +10,7 @@ import { BehaviorSubject, Subject } from 'rxjs'; import type { AutoRefreshDoneFn } from '@kbn/data-plugin/public'; import { ISearchSource } from '@kbn/data-plugin/public'; import { RequestAdapter } from '@kbn/inspector-plugin/public'; +import { getRawRecordType } from '../utils/get_raw_record_type'; import { DiscoverServices } from '../../../build_services'; import { DiscoverSearchSessionManager } from '../services/discover_search_session'; import { GetStateReturn } from '../services/discover_state'; @@ -21,7 +22,6 @@ import { fetchAll } from '../utils/fetch_all'; import { useBehaviorSubject } from './use_behavior_subject'; import { sendResetMsg } from './use_saved_search_messages'; import { getFetch$ } from '../utils/get_fetch_observable'; -import { getTextBasedLanguageMode } from '../../../utils/get_text_based_language_mode'; import { SavedSearch } from '../../../services/saved_searches'; import type { DataTableRecord } from '../../../types'; @@ -54,12 +54,23 @@ export interface UseSavedSearch { inspectorAdapters: { requests: RequestAdapter }; } +export enum RecordRawType { + /** + * Documents returned Elasticsearch, nested structure + */ + DOCUMENT = 'document', + /** + * Data returned e.g. SQL queries, flat structure + * */ + PLAIN = 'plain', +} + export type DataRefetchMsg = 'reset' | undefined; export interface DataMsg { fetchStatus: FetchStatus; error?: Error; - textBasedLanguageMode?: string; + recordRawType?: RecordRawType; } export interface DataMainMsg extends DataMsg { @@ -109,7 +120,8 @@ export const useSavedSearch = ({ const { data, filterManager } = services; const timefilter = data.query.timefilter.timefilter; const { query } = stateContainer.appStateContainer.getState(); - const textBasedLanguageMode = query ? getTextBasedLanguageMode(query) : ''; + + const recordRawType = useMemo(() => getRawRecordType(query), [query]); const inspectorAdapters = useMemo(() => ({ requests: new RequestAdapter() }), []); @@ -117,7 +129,7 @@ export const useSavedSearch = ({ * The observables the UI (aka React component) subscribes to get notified about * the changes in the data fetching process (high level: fetching started, data was received) */ - const initialState = { fetchStatus: initialFetchStatus, textBasedLanguageMode }; + const initialState = { fetchStatus: initialFetchStatus, recordRawType }; const main$: DataMain$ = useBehaviorSubject(initialState) as DataMain$; const documents$: DataDocuments$ = useBehaviorSubject(initialState) as DataDocuments$; const totalHits$: DataTotalHits$ = useBehaviorSubject(initialState) as DataTotalHits$; diff --git a/src/plugins/discover/public/application/main/hooks/use_saved_search_messages.ts b/src/plugins/discover/public/application/main/hooks/use_saved_search_messages.ts index 9b4572beb0405..f4aab13592500 100644 --- a/src/plugins/discover/public/application/main/hooks/use_saved_search_messages.ts +++ b/src/plugins/discover/public/application/main/hooks/use_saved_search_messages.ts @@ -12,6 +12,7 @@ import { DataDocuments$, DataMain$, DataTotalHits$, + RecordRawType, SavedSearchData, } from './use_saved_search'; @@ -33,12 +34,12 @@ export function sendCompleteMsg(main$: DataMain$, foundDocuments = true) { if (main$.getValue().fetchStatus === FetchStatus.COMPLETE) { return; } - const textBasedLanguageMode = main$.getValue().textBasedLanguageMode; + const recordRawType = main$.getValue().recordRawType; main$.next({ fetchStatus: FetchStatus.COMPLETE, foundDocuments, error: undefined, - textBasedLanguageMode, + recordRawType, }); } @@ -47,10 +48,10 @@ export function sendCompleteMsg(main$: DataMain$, foundDocuments = true) { */ export function sendPartialMsg(main$: DataMain$) { if (main$.getValue().fetchStatus === FetchStatus.LOADING) { - const textBasedLanguageMode = main$.getValue().textBasedLanguageMode; + const recordRawType = main$.getValue().recordRawType; main$.next({ fetchStatus: FetchStatus.PARTIAL, - textBasedLanguageMode, + recordRawType, }); } } @@ -60,12 +61,12 @@ export function sendPartialMsg(main$: DataMain$) { */ export function sendLoadingMsg( data$: DataMain$ | DataDocuments$ | DataTotalHits$ | DataCharts$, - textBasedLanguageMode?: string + recordRawType: RecordRawType ) { if (data$.getValue().fetchStatus !== FetchStatus.LOADING) { data$.next({ fetchStatus: FetchStatus.LOADING, - textBasedLanguageMode, + recordRawType, }); } } @@ -77,11 +78,11 @@ export function sendErrorMsg( data$: DataMain$ | DataDocuments$ | DataTotalHits$ | DataCharts$, error: Error ) { - const textBasedLanguageMode = data$.getValue().textBasedLanguageMode; + const recordRawType = data$.getValue().recordRawType; data$.next({ fetchStatus: FetchStatus.ERROR, error, - textBasedLanguageMode, + recordRawType, }); } @@ -90,26 +91,26 @@ export function sendErrorMsg( * Needed when index pattern is switched or a new runtime field is added */ export function sendResetMsg(data: SavedSearchData, initialFetchStatus: FetchStatus) { - const textBasedLanguageMode = data.main$.getValue().textBasedLanguageMode; + const recordRawType = data.main$.getValue().recordRawType; data.main$.next({ fetchStatus: initialFetchStatus, foundDocuments: undefined, - textBasedLanguageMode, + recordRawType, }); data.documents$.next({ fetchStatus: initialFetchStatus, result: [], - textBasedLanguageMode, + recordRawType, }); data.charts$.next({ fetchStatus: initialFetchStatus, chartData: undefined, bucketInterval: undefined, - textBasedLanguageMode, + recordRawType, }); data.totalHits$.next({ fetchStatus: initialFetchStatus, result: undefined, - textBasedLanguageMode, + recordRawType, }); } diff --git a/src/plugins/discover/public/application/main/utils/fetch_all.ts b/src/plugins/discover/public/application/main/utils/fetch_all.ts index d986a87a7f76b..c1e985bcdfec1 100644 --- a/src/plugins/discover/public/application/main/utils/fetch_all.ts +++ b/src/plugins/discover/public/application/main/utils/fetch_all.ts @@ -9,6 +9,7 @@ import { DataPublicPluginStart, ISearchSource } from '@kbn/data-plugin/public'; import { Adapters } from '@kbn/inspector-plugin'; import { ReduxLikeStateContainer } from '@kbn/kibana-utils-plugin/common'; import { DataViewType } from '@kbn/data-views-plugin/public'; +import { getRawRecordType } from './get_raw_record_type'; import { sendCompleteMsg, sendErrorMsg, @@ -29,10 +30,10 @@ import { DataDocuments$, DataMain$, DataTotalHits$, + RecordRawType, SavedSearchData, } from '../hooks/use_saved_search'; import { DiscoverServices } from '../../../build_services'; -import { getTextBasedLanguageMode } from '../../../utils/get_text_based_language_mode'; import { fetchSql } from './fetch_sql'; export interface FetchDeps { @@ -82,19 +83,18 @@ export function fetchAll( }; try { - const indexPattern = searchSource.getField('index')!; - + const dataView = searchSource.getField('index')!; if (reset) { sendResetMsg(dataSubjects, initialFetchStatus); } - const { hideChart, sort, query } = appStateContainer.getState(); - const textBasedLanguageMode = query ? getTextBasedLanguageMode(query) : ''; + const recordRawType = getRawRecordType(query); + const useSql = recordRawType === RecordRawType.PLAIN; - // Update the base searchSource, base for all child fetches - if (!textBasedLanguageMode) { + if (recordRawType === RecordRawType.DOCUMENT) { + // Update the base searchSource, base for all child fetches updateSearchSource(searchSource, false, { - indexPattern, + indexPattern: dataView, services, sort: sort as SortOrder[], useNewFieldsApi, @@ -102,29 +102,23 @@ export function fetchAll( } // Mark all subjects as loading - sendLoadingMsg(dataSubjects.main$, textBasedLanguageMode); - sendLoadingMsg(dataSubjects.documents$, textBasedLanguageMode); - sendLoadingMsg(dataSubjects.totalHits$, textBasedLanguageMode); - sendLoadingMsg(dataSubjects.charts$, textBasedLanguageMode); + sendLoadingMsg(dataSubjects.main$, recordRawType); + sendLoadingMsg(dataSubjects.documents$, recordRawType); + sendLoadingMsg(dataSubjects.totalHits$, recordRawType); + sendLoadingMsg(dataSubjects.charts$, recordRawType); const isChartVisible = - !hideChart && indexPattern.isTimeBased() && indexPattern.type !== DataViewType.ROLLUP; + !hideChart && dataView.isTimeBased() && dataView.type !== DataViewType.ROLLUP; // Start fetching all required requests const documents = - textBasedLanguageMode && query + useSql && query ? fetchSql(query, services.indexPatterns, data, services.expressions) : fetchDocuments(searchSource.createCopy(), fetchDeps); - const charts = - isChartVisible && !textBasedLanguageMode - ? fetchChart(searchSource.createCopy(), fetchDeps) - : undefined; + isChartVisible && !useSql ? fetchChart(searchSource.createCopy(), fetchDeps) : undefined; const totalHits = - !isChartVisible && !textBasedLanguageMode - ? fetchTotalHits(searchSource.createCopy(), fetchDeps) - : undefined; - + !isChartVisible && !useSql ? fetchTotalHits(searchSource.createCopy(), fetchDeps) : undefined; /** * This method checks the passed in hit count and will send a PARTIAL message to main$ * if there are results, indicating that we have finished some of the requests that have been @@ -150,12 +144,14 @@ export function fetchAll( dataSubjects.totalHits$.next({ fetchStatus: FetchStatus.PARTIAL, result: docs.length, + recordRawType, }); } dataSubjects.documents$.next({ fetchStatus: FetchStatus.COMPLETE, result: docs, + recordRawType, }); checkHitCount(docs.length); @@ -170,12 +166,14 @@ export function fetchAll( dataSubjects.totalHits$.next({ fetchStatus: FetchStatus.COMPLETE, result: chart.totalHits, + recordRawType, }); dataSubjects.charts$.next({ fetchStatus: FetchStatus.COMPLETE, chartData: chart.chartData, bucketInterval: chart.bucketInterval, + recordRawType, }); checkHitCount(chart.totalHits); @@ -184,7 +182,11 @@ export function fetchAll( totalHits ?.then((hitCount) => { - dataSubjects.totalHits$.next({ fetchStatus: FetchStatus.COMPLETE, result: hitCount }); + dataSubjects.totalHits$.next({ + fetchStatus: FetchStatus.COMPLETE, + result: hitCount, + recordRawType, + }); checkHitCount(hitCount); }) .catch(sendErrorTo(dataSubjects.totalHits$)); diff --git a/src/plugins/discover/public/application/main/utils/fetch_documents.ts b/src/plugins/discover/public/application/main/utils/fetch_documents.ts index 05d3d2f50f9a0..7653d7096b49b 100644 --- a/src/plugins/discover/public/application/main/utils/fetch_documents.ts +++ b/src/plugins/discover/public/application/main/utils/fetch_documents.ts @@ -9,8 +9,8 @@ import { i18n } from '@kbn/i18n'; import { filter, map } from 'rxjs/operators'; import { lastValueFrom } from 'rxjs'; import { isCompleteResponse, ISearchSource } from '@kbn/data-plugin/public'; -import { SAMPLE_SIZE_SETTING } from '../../../../common'; import { buildDataTableRecordList } from '../../../utils/build_data_record'; +import { SAMPLE_SIZE_SETTING } from '../../../../common'; import { FetchDeps } from './fetch_all'; /** diff --git a/src/plugins/discover/public/utils/get_text_based_language_mode.ts b/src/plugins/discover/public/application/main/utils/get_raw_record_type.ts similarity index 59% rename from src/plugins/discover/public/utils/get_text_based_language_mode.ts rename to src/plugins/discover/public/application/main/utils/get_raw_record_type.ts index b87b45ec8d5e1..06039816f3799 100644 --- a/src/plugins/discover/public/utils/get_text_based_language_mode.ts +++ b/src/plugins/discover/public/application/main/utils/get_raw_record_type.ts @@ -5,18 +5,19 @@ * in compliance with, at your election, the Elastic License 2.0 or the Server * Side Public License, v 1. */ + import { AggregateQuery, Query, isOfAggregateQueryType, getAggregateQueryMode, } from '@kbn/es-query'; +import { RecordRawType } from '../hooks/use_saved_search'; -export function getTextBasedLanguageMode(query: Query | AggregateQuery): string { - let textBasedLanguageMode = ''; - if (query && isOfAggregateQueryType(query)) { - const aggregatedQuery = query as AggregateQuery; - textBasedLanguageMode = getAggregateQueryMode(aggregatedQuery); +export function getRawRecordType(query?: Query | AggregateQuery) { + if (query && isOfAggregateQueryType(query) && getAggregateQueryMode(query) === 'sql') { + return RecordRawType.PLAIN; } - return textBasedLanguageMode; + + return RecordRawType.DOCUMENT; } diff --git a/src/plugins/discover/public/application/main/utils/get_result_state.ts b/src/plugins/discover/public/application/main/utils/get_result_state.ts index 172d831d9b15b..3606f17952b5a 100644 --- a/src/plugins/discover/public/application/main/utils/get_result_state.ts +++ b/src/plugins/discover/public/application/main/utils/get_result_state.ts @@ -21,12 +21,12 @@ export const resultStatuses = { export function getResultState( fetchStatus: FetchStatus, foundDocuments: boolean = false, - textBasedLanguageMode?: string + isPlainRecord?: string ) { if (fetchStatus === FetchStatus.UNINITIALIZED) { return resultStatuses.UNINITIALIZED; } - if (textBasedLanguageMode && fetchStatus === FetchStatus.ERROR) return resultStatuses.NO_RESULTS; + if (isPlainRecord && fetchStatus === FetchStatus.ERROR) return resultStatuses.NO_RESULTS; if (!foundDocuments && fetchStatus === FetchStatus.LOADING) return resultStatuses.LOADING; else if (foundDocuments) return resultStatuses.READY; diff --git a/src/plugins/discover/public/components/discover_grid/discover_grid.tsx b/src/plugins/discover/public/components/discover_grid/discover_grid.tsx index a16be979b5581..15c7c4254e16b 100644 --- a/src/plugins/discover/public/components/discover_grid/discover_grid.tsx +++ b/src/plugins/discover/public/components/discover_grid/discover_grid.tsx @@ -50,11 +50,11 @@ import { import { DiscoverGridDocumentToolbarBtn } from './discover_grid_document_selection'; import { SortPairArr } from '../doc_table/utils/get_sort'; import { getFieldsToShow } from '../../utils/get_fields_to_show'; -import { getTextBasedLanguageMode } from '../../utils/get_text_based_language_mode'; import type { DataTableRecord, ValueToStringConverter } from '../../types'; import { useRowHeightsOptions } from '../../hooks/use_row_heights_options'; import { useDiscoverServices } from '../../hooks/use_discover_services'; import { convertValueToString } from '../../utils/convert_value_to_string'; +import { useDiscoverLayoutContext } from '../../application/main/hooks/use_discover_layout_context'; interface SortObj { id: string; @@ -203,13 +203,11 @@ export const DiscoverGrid = ({ }: DiscoverGridProps) => { const dataGridRef = useRef(null); const services = useDiscoverServices(); - const query = services.data.query.queryString.getQuery(); - const textBasedLanguageMode = getTextBasedLanguageMode(query); - const [selectedDocs, setSelectedDocs] = useState([]); const [isFilterActive, setIsFilterActive] = useState(false); const displayedColumns = getDisplayedColumns(columns, indexPattern); const defaultColumns = displayedColumns.includes('_source'); + const { isPlainRecord } = useDiscoverLayoutContext(); const usedSelectedDocs = useMemo(() => { if (!selectedDocs.length || !rows?.length) { return []; @@ -487,7 +485,7 @@ export const DiscoverGrid = ({ className={classnames( className, 'dscDiscoverGrid__table', - textBasedLanguageMode ? 'dscDiscoverGrid__textLanguageMode' : '' + isPlainRecord ? 'dscDiscoverGrid__textLanguageMode' : '' )} > diff --git a/src/plugins/discover/public/components/discover_tour/discover_tour_provider.tsx b/src/plugins/discover/public/components/discover_tour/discover_tour_provider.tsx index 8b50261981f3d..8ab69f7bebd52 100644 --- a/src/plugins/discover/public/components/discover_tour/discover_tour_provider.tsx +++ b/src/plugins/discover/public/components/discover_tour/discover_tour_provider.tsx @@ -29,6 +29,7 @@ import { PLUGIN_ID } from '../../../common'; import { useDiscoverServices } from '../../hooks/use_discover_services'; import { DiscoverTourContext, DiscoverTourContextProps } from './discover_tour_context'; import { DISCOVER_TOUR_STEP_ANCHORS } from './discover_tour_anchors'; +import { useDiscoverLayoutContext } from '../../application/main/hooks/use_discover_layout_context'; const MAX_WIDTH = 350; @@ -40,43 +41,64 @@ interface TourStepDefinition { imageAltText: string; } +const ADD_FIELDS_STEP = { + anchor: DISCOVER_TOUR_STEP_ANCHORS.addFields, + title: i18n.translate('discover.dscTour.stepAddFields.title', { + defaultMessage: 'Add fields to the table', + }), + content: ( + , + }} + /> + ), + imageName: 'add_fields.gif', + imageAltText: i18n.translate('discover.dscTour.stepAddFields.imageAltText', { + defaultMessage: + 'In the Available fields list, click the plus icon to toggle a field into the document table.', + }), +}; + +const ORDER_TABLE_COLUMNS_STEP = { + anchor: DISCOVER_TOUR_STEP_ANCHORS.reorderColumns, + title: i18n.translate('discover.dscTour.stepReorderColumns.title', { + defaultMessage: 'Order the table columns', + }), + content: ( + + ), + imageName: 'reorder_columns.gif', + imageAltText: i18n.translate('discover.dscTour.stepReorderColumns.imageAltText', { + defaultMessage: 'Use the Columns popover to drag the columns to the order you prefer.', + }), +}; + +const CHANGE_ROW_HEIGHT_STEP = { + anchor: DISCOVER_TOUR_STEP_ANCHORS.changeRowHeight, + title: i18n.translate('discover.dscTour.stepChangeRowHeight.title', { + defaultMessage: 'Change the row height', + }), + content: ( + + ), + imageName: 'rows_per_line.gif', + imageAltText: i18n.translate('discover.dscTour.stepChangeRowHeight.imageAltText', { + defaultMessage: 'Click the display options icon to adjust the row height to fit the contents.', + }), +}; + const tourStepDefinitions: TourStepDefinition[] = [ - { - anchor: DISCOVER_TOUR_STEP_ANCHORS.addFields, - title: i18n.translate('discover.dscTour.stepAddFields.title', { - defaultMessage: 'Add fields to the table', - }), - content: ( - , - }} - /> - ), - imageName: 'add_fields.gif', - imageAltText: i18n.translate('discover.dscTour.stepAddFields.imageAltText', { - defaultMessage: - 'In the Available fields list, click the plus icon to toggle a field into the document table.', - }), - }, - { - anchor: DISCOVER_TOUR_STEP_ANCHORS.reorderColumns, - title: i18n.translate('discover.dscTour.stepReorderColumns.title', { - defaultMessage: 'Order the table columns', - }), - content: ( - - ), - imageName: 'reorder_columns.gif', - imageAltText: i18n.translate('discover.dscTour.stepReorderColumns.imageAltText', { - defaultMessage: 'Use the Columns popover to drag the columns to the order you prefer.', - }), - }, + ADD_FIELDS_STEP, + ORDER_TABLE_COLUMNS_STEP, { anchor: DISCOVER_TOUR_STEP_ANCHORS.sort, title: i18n.translate('discover.dscTour.stepSort.title', { @@ -94,23 +116,7 @@ const tourStepDefinitions: TourStepDefinition[] = [ 'Click a column header and select the desired sort order. Adjust a multi-field sort using the fields sorted popover.', }), }, - { - anchor: DISCOVER_TOUR_STEP_ANCHORS.changeRowHeight, - title: i18n.translate('discover.dscTour.stepChangeRowHeight.title', { - defaultMessage: 'Change the row height', - }), - content: ( - - ), - imageName: 'rows_per_line.gif', - imageAltText: i18n.translate('discover.dscTour.stepChangeRowHeight.imageAltText', { - defaultMessage: - 'Click the display options icon to adjust the row height to fit the contents.', - }), - }, + CHANGE_ROW_HEIGHT_STEP, { anchor: DISCOVER_TOUR_STEP_ANCHORS.expandDocument, title: i18n.translate('discover.dscTour.stepExpand.title', { @@ -195,6 +201,7 @@ const tourConfig: EuiTourState = { export const DiscoverTourProvider: React.FC = ({ children }) => { const services = useDiscoverServices(); + const { isPlainRecord } = useDiscoverLayoutContext(); const prependToBasePath = services.core.http.basePath.prepend; const getAssetPath = useCallback( (imageName: string) => { @@ -203,8 +210,14 @@ export const DiscoverTourProvider: React.FC = ({ children }) => { [prependToBasePath] ); const tourSteps = useMemo( - () => prepareTourSteps(tourStepDefinitions, getAssetPath), - [getAssetPath] + () => + isPlainRecord + ? prepareTourSteps( + [ADD_FIELDS_STEP, ORDER_TABLE_COLUMNS_STEP, CHANGE_ROW_HEIGHT_STEP], + getAssetPath + ) + : prepareTourSteps(tourStepDefinitions, getAssetPath), + [getAssetPath, isPlainRecord] ); const [steps, actions, reducerState] = useEuiTour(tourSteps, tourConfig); const currentTourStep = reducerState.currentTourStep; diff --git a/src/plugins/unified_search/public/dataview_picker/change_dataview.tsx b/src/plugins/unified_search/public/dataview_picker/change_dataview.tsx index e77597ca74d5c..a98d5a8d834f5 100644 --- a/src/plugins/unified_search/public/dataview_picker/change_dataview.tsx +++ b/src/plugins/unified_search/public/dataview_picker/change_dataview.tsx @@ -91,6 +91,7 @@ export function ChangeDataView({ textBasedLanguages, onSaveTextLanguageQuery, onTextLangQuerySubmit, + onUpdateIsTextQueryLangSelected, textBasedLanguage, }: DataViewPickerPropsExtended) { const { euiTheme } = useEuiTheme(); @@ -100,6 +101,13 @@ export function ChangeDataView({ const [isTextBasedLangSelected, setIsTextBasedLangSelected] = useState( Boolean(textBasedLanguage) ); + const updateIsTextQueryLangSelected = useCallback( + (isTextLangSelected: boolean) => { + setIsTextBasedLangSelected(isTextLangSelected); + onUpdateIsTextQueryLangSelected?.(isTextLangSelected); + }, + [onUpdateIsTextQueryLangSelected] + ); const [isTextLangTransitionModalVisible, setIsTextLangTransitionModalVisible] = useState(false); const kibana = useKibana(); @@ -148,9 +156,9 @@ export function ChangeDataView({ useEffect(() => { if (Boolean(textBasedLanguage) !== isTextBasedLangSelected) { - setIsTextBasedLangSelected(Boolean(textBasedLanguage)); + updateIsTextQueryLangSelected(Boolean(textBasedLanguage)); } - }, [isTextBasedLangSelected, textBasedLanguage]); + }, [updateIsTextQueryLangSelected, isTextBasedLangSelected, textBasedLanguage]); const createTrigger = function () { const { label, title, 'data-test-subj': dataTestSubj, fullWidth, ...rest } = trigger; @@ -298,7 +306,7 @@ export function ChangeDataView({ onChange={(lang) => { setTriggerLabel(lang); setPopoverIsOpen(false); - setIsTextBasedLangSelected(true); + updateIsTextQueryLangSelected(true); // also update the query with the sql query onTextLangQuerySubmit?.({ sql: `SELECT * FROM "${trigger.title}"` }); }} @@ -319,7 +327,7 @@ export function ChangeDataView({ const cleanup = useCallback( (shouldDismissModal: boolean) => { setIsTextLangTransitionModalVisible(false); - setIsTextBasedLangSelected(false); + updateIsTextQueryLangSelected(true); // clean up the Text based language jQuery onTextLangQuerySubmit?.(); setTriggerLabel(trigger.label); @@ -327,7 +335,7 @@ export function ChangeDataView({ onTransitionModalDismiss(); } }, - [onTextLangQuerySubmit, onTransitionModalDismiss, trigger.label] + [updateIsTextQueryLangSelected, onTextLangQuerySubmit, onTransitionModalDismiss, trigger.label] ); const onModalClose = useCallback( diff --git a/src/plugins/unified_search/public/dataview_picker/index.tsx b/src/plugins/unified_search/public/dataview_picker/index.tsx index 6fd46b99638de..52cd23fe49de4 100644 --- a/src/plugins/unified_search/public/dataview_picker/index.tsx +++ b/src/plugins/unified_search/public/dataview_picker/index.tsx @@ -74,14 +74,18 @@ export interface DataViewPickerProps { } export interface DataViewPickerPropsExtended extends DataViewPickerProps { + /** + * Text based language that is currently selected; depends on the query + */ + textBasedLanguage?: string; /** * Callback that is called when the user clicks the submit button */ onTextLangQuerySubmit?: (query?: AggregateQuery) => void; /** - * Text based language that is currently selected; depends on the query + * Callback that is called when the user toggle between text lang and data views */ - textBasedLanguage?: string; + onUpdateIsTextQueryLangSelected?: (isTextLangSelected: boolean) => void; } export const DataViewPicker = ({ @@ -96,6 +100,7 @@ export const DataViewPicker = ({ textBasedLanguages, onSaveTextLanguageQuery, onTextLangQuerySubmit, + onUpdateIsTextQueryLangSelected, textBasedLanguage, }: DataViewPickerPropsExtended) => { return ( @@ -111,6 +116,7 @@ export const DataViewPicker = ({ textBasedLanguages={textBasedLanguages} onSaveTextLanguageQuery={onSaveTextLanguageQuery} onTextLangQuerySubmit={onTextLangQuerySubmit} + onUpdateIsTextQueryLangSelected={onUpdateIsTextQueryLangSelected} textBasedLanguage={textBasedLanguage} /> ); From ad712de44e4bf991cddb9b7462026c21e874aad0 Mon Sep 17 00:00:00 2001 From: Dzmitry Tamashevich Date: Sun, 3 Jul 2022 15:43:53 +0300 Subject: [PATCH 073/115] [Discover] add functional tests --- .../data/common/query/to_expression_ast.ts | 3 +- test/functional/apps/discover/_sql_view.ts | 91 +++++++++++++++++++ test/functional/apps/discover/index.ts | 1 + test/functional/page_objects/discover_page.ts | 8 ++ 4 files changed, 101 insertions(+), 2 deletions(-) create mode 100644 test/functional/apps/discover/_sql_view.ts diff --git a/src/plugins/data/common/query/to_expression_ast.ts b/src/plugins/data/common/query/to_expression_ast.ts index a3102b12cffec..ab3345bce5e97 100644 --- a/src/plugins/data/common/query/to_expression_ast.ts +++ b/src/plugins/data/common/query/to_expression_ast.ts @@ -17,8 +17,7 @@ import { } from '..'; function getIndexPatternFromSQLQuery(sqlQuery?: string): string { - const sql = sqlQuery?.replaceAll('"', ''); - const matches = sql?.match(/FROM\s+([\w*]+)/); + const matches = sqlQuery?.match(/FROM\s+"([^\s]+)"/); if (matches) { return matches[1]; } diff --git a/test/functional/apps/discover/_sql_view.ts b/test/functional/apps/discover/_sql_view.ts new file mode 100644 index 0000000000000..63f135f6593ad --- /dev/null +++ b/test/functional/apps/discover/_sql_view.ts @@ -0,0 +1,91 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import expect from '@kbn/expect'; + +import { FtrProviderContext } from '../../ftr_provider_context'; + +export default function ({ getService, getPageObjects }: FtrProviderContext) { + const esArchiver = getService('esArchiver'); + const kibanaServer = getService('kibanaServer'); + const log = getService('log'); + const dataGrid = getService('dataGrid'); + const testSubjects = getService('testSubjects'); + const monacoEditor = getService('monacoEditor'); + const PageObjects = getPageObjects(['common', 'discover', 'header', 'timePicker']); + + const defaultSettings = { + defaultIndex: 'logstash-*', + 'discover:enableSql': true, + }; + + describe('discover sql view', async function () { + before(async () => { + log.debug('load kibana index with default index pattern'); + await kibanaServer.importExport.load('test/functional/fixtures/kbn_archiver/discover'); + // and load a set of makelogs data + await esArchiver.loadIfNeeded('test/functional/fixtures/es_archiver/logstash_functional'); + await kibanaServer.uiSettings.replace(defaultSettings); + await PageObjects.common.navigateToApp('discover'); + await PageObjects.timePicker.setDefaultAbsoluteRange(); + }); + + describe('test', () => { + it('should render sql view correctly', async function () { + expect(await testSubjects.exists('showQueryBarMenu')).to.be(true); + expect(await testSubjects.exists('superDatePickerToggleQuickMenuButton')).to.be(true); + expect(await testSubjects.exists('addFilter')).to.be(true); + expect(await testSubjects.exists('dscViewModeDocumentButton')).to.be(true); + expect(await testSubjects.exists('discoverChart')).to.be(true); + expect(await testSubjects.exists('discoverQueryHits')).to.be(true); + expect(await testSubjects.exists('discoverAlertsButton')).to.be(true); + expect(await testSubjects.exists('shareTopNavButton')).to.be(true); + expect(await testSubjects.exists('docTableExpandToggleColumn')).to.be(true); + expect(await testSubjects.exists('dataGridColumnSortingButton')).to.be(true); + expect(await testSubjects.exists('fieldFilterSearchInput')).to.be(true); + expect(await testSubjects.exists('toggleFieldFilterButton')).to.be(true); + expect(await testSubjects.exists('fieldTypesHelpButton')).to.be(true); + await testSubjects.click('field-@message-showDetails'); + expect(await testSubjects.exists('discoverFieldListPanelEditItem')).to.be(true); + await PageObjects.discover.selectTextBaseLang('SQL'); + expect(await testSubjects.exists('unifiedTextLandEditor')).to.be(true); + expect(await testSubjects.exists('showQueryBarMenu')).to.be(false); + expect(await testSubjects.exists('superDatePickerToggleQuickMenuButton')).to.be(false); + expect(await testSubjects.exists('addFilter')).to.be(false); + expect(await testSubjects.exists('dscViewModeDocumentButton')).to.be(false); + expect(await testSubjects.exists('discoverChart')).to.be(false); + expect(await testSubjects.exists('discoverQueryHits')).to.be(false); + expect(await testSubjects.exists('discoverAlertsButton')).to.be(false); + expect(await testSubjects.exists('shareTopNavButton')).to.be(false); + expect(await testSubjects.exists('docTableExpandToggleColumn')).to.be(false); + expect(await testSubjects.exists('dataGridColumnSortingButton')).to.be(false); + expect(await testSubjects.exists('fieldFilterSearchInput')).to.be(false); + expect(await testSubjects.exists('toggleFieldFilterButton')).to.be(false); + expect(await testSubjects.exists('fieldTypesHelpButton')).to.be(false); + await testSubjects.click('field-@message-showDetails'); + expect(await testSubjects.exists('discoverFieldListPanelEditItem')).to.be(false); + }); + + it('should perform test query correctly', async function () { + await PageObjects.discover.selectTextBaseLang('SQL'); + + const testQuery = `SELECT "@tags", geo.dest, count(*) occurred FROM "logstash-*" + GROUP BY "@tags", geo.dest + HAVING occurred > 20 + ORDER BY occurred DESC`; + + await monacoEditor.setCodeEditorValue(testQuery); + await testSubjects.click('querySubmitButton'); + await PageObjects.header.waitUntilLoadingHasFinished(); + + const [rows] = await dataGrid.getBodyRows(); + expect(await rows[1].getVisibleText()).to.be('@tagssuccessgeo.destCNoccurred2269'); + }); + }); + }); +} diff --git a/test/functional/apps/discover/index.ts b/test/functional/apps/discover/index.ts index 803ca1fec57cd..f25b35bd2608c 100644 --- a/test/functional/apps/discover/index.ts +++ b/test/functional/apps/discover/index.ts @@ -58,6 +58,7 @@ export default function ({ getService, loadTestFile }: FtrProviderContext) { loadTestFile(require.resolve('./_data_grid_doc_table')); loadTestFile(require.resolve('./_data_grid_copy_to_clipboard')); loadTestFile(require.resolve('./_data_grid_pagination')); + loadTestFile(require.resolve('./_sql_view')); loadTestFile(require.resolve('./_indexpattern_with_unmapped_fields')); loadTestFile(require.resolve('./_runtime_fields_editor')); loadTestFile(require.resolve('./_huge_fields')); diff --git a/test/functional/page_objects/discover_page.ts b/test/functional/page_objects/discover_page.ts index f16f0396cf091..3f81384310eea 100644 --- a/test/functional/page_objects/discover_page.ts +++ b/test/functional/page_objects/discover_page.ts @@ -507,6 +507,14 @@ export class DiscoverPageObject extends FtrService { await this.header.waitUntilLoadingHasFinished(); } + public async selectTextBaseLang(lang: 'SQL') { + await this.testSubjects.click('discover-dataView-switch-link'); + await this.find.clickByCssSelector( + `[data-test-subj="text-based-languages-switcher"] [title="${lang}"]` + ); + await this.header.waitUntilLoadingHasFinished(); + } + public async removeHeaderColumn(name: string) { const isLegacyDefault = await this.useLegacyTable(); if (isLegacyDefault) { From 97ced81a5611f54cc019d0538d28917c08404448 Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Mon, 4 Jul 2022 10:26:10 +0300 Subject: [PATCH 074/115] Add aggregate functions to the documentation --- .../kbn-es-query/src/es_query/es_query.sql.ts | 10 + packages/kbn-es-query/src/es_query/index.ts | 7 +- packages/kbn-es-query/src/index.ts | 1 + .../data/common/query/to_expression_ast.ts | 15 +- .../main/hooks/use_discover_state.ts | 11 +- .../text_based_languages_editor/helpers.ts | 22 +- .../sql_documentation_sections.tsx | 643 ++++++++++++++++++ 7 files changed, 675 insertions(+), 34 deletions(-) diff --git a/packages/kbn-es-query/src/es_query/es_query.sql.ts b/packages/kbn-es-query/src/es_query/es_query.sql.ts index 10995aeff1a8a..2c17d6c89a01e 100644 --- a/packages/kbn-es-query/src/es_query/es_query.sql.ts +++ b/packages/kbn-es-query/src/es_query/es_query.sql.ts @@ -23,3 +23,13 @@ export function isOfAggregateQueryType(query: AggregateQuery | Query): query is export function getAggregateQueryMode(query: AggregateQuery): string { return Object.keys(query)[0]; } + +// retrieves the index pattern from the aggregate query +export function getIndexPatternFromSQLQuery(sqlQuery?: string): string { + const sql = sqlQuery?.replaceAll('"', '').replaceAll("'", ''); + const matches = sql?.match(/FROM\s+([\w*]+)/); + if (matches) { + return matches[1]; + } + return ''; +} diff --git a/packages/kbn-es-query/src/es_query/index.ts b/packages/kbn-es-query/src/es_query/index.ts index 08ca93fc4d442..61f11e9d4d89e 100644 --- a/packages/kbn-es-query/src/es_query/index.ts +++ b/packages/kbn-es-query/src/es_query/index.ts @@ -13,7 +13,12 @@ export { buildEsQuery } from './build_es_query'; export { buildQueryFromFilters } from './from_filters'; export { luceneStringToDsl } from './lucene_string_to_dsl'; export { decorateQuery } from './decorate_query'; -export { isOfQueryType, isOfAggregateQueryType, getAggregateQueryMode } from './es_query.sql'; +export { + isOfQueryType, + isOfAggregateQueryType, + getAggregateQueryMode, + getIndexPatternFromSQLQuery, +} from './es_query.sql'; export type { IFieldSubType, BoolQuery, diff --git a/packages/kbn-es-query/src/index.ts b/packages/kbn-es-query/src/index.ts index a0c04f5557164..d29141ab39ac9 100644 --- a/packages/kbn-es-query/src/index.ts +++ b/packages/kbn-es-query/src/index.ts @@ -56,6 +56,7 @@ export { isOfQueryType, isOfAggregateQueryType, getAggregateQueryMode, + getIndexPatternFromSQLQuery, } from './es_query'; export { diff --git a/src/plugins/data/common/query/to_expression_ast.ts b/src/plugins/data/common/query/to_expression_ast.ts index a3102b12cffec..fb60a77a505d9 100644 --- a/src/plugins/data/common/query/to_expression_ast.ts +++ b/src/plugins/data/common/query/to_expression_ast.ts @@ -5,7 +5,11 @@ * in compliance with, at your election, the Elastic License 2.0 or the Server * Side Public License, v 1. */ -import { isOfAggregateQueryType, getAggregateQueryMode } from '@kbn/es-query'; +import { + isOfAggregateQueryType, + getAggregateQueryMode, + getIndexPatternFromSQLQuery, +} from '@kbn/es-query'; import { buildExpression, buildExpressionFunction } from '@kbn/expressions-plugin/common'; import type { DataViewsContract } from '@kbn/data-views-plugin/common'; import { @@ -16,15 +20,6 @@ import { timerangeToAst, } from '..'; -function getIndexPatternFromSQLQuery(sqlQuery?: string): string { - const sql = sqlQuery?.replaceAll('"', ''); - const matches = sql?.match(/FROM\s+([\w*]+)/); - if (matches) { - return matches[1]; - } - return ''; -} - interface Args extends QueryState { dataViewsService: DataViewsContract; } diff --git a/src/plugins/discover/public/application/main/hooks/use_discover_state.ts b/src/plugins/discover/public/application/main/hooks/use_discover_state.ts index 109019ca945ba..89e213d6c4155 100644 --- a/src/plugins/discover/public/application/main/hooks/use_discover_state.ts +++ b/src/plugins/discover/public/application/main/hooks/use_discover_state.ts @@ -8,7 +8,7 @@ import { useMemo, useEffect, useState, useCallback } from 'react'; import { isEqual } from 'lodash'; import { History } from 'history'; -import { isOfAggregateQueryType } from '@kbn/es-query'; +import { isOfAggregateQueryType, getIndexPatternFromSQLQuery } from '@kbn/es-query'; import { getState } from '../services/discover_state'; import { getStateDefaults } from '../utils/get_state_defaults'; import { DiscoverServices } from '../../../build_services'; @@ -27,15 +27,6 @@ import { getSwitchIndexPatternAppState } from '../utils/get_switch_index_pattern import { SortPairArr } from '../../../components/doc_table/utils/get_sort'; import { DataTableRecord } from '../../../types'; -function getIndexPatternFromSQLQuery(sqlQuery?: string): string { - const sql = sqlQuery?.replaceAll('"', ''); - const matches = sql?.match(/FROM\s+([\w*]+)/); - if (matches) { - return matches[1]; - } - return ''; -} - export function useDiscoverState({ services, history, diff --git a/src/plugins/unified_search/public/query_string_input/text_based_languages_editor/helpers.ts b/src/plugins/unified_search/public/query_string_input/text_based_languages_editor/helpers.ts index c7475c42bed45..d6bb55b0c509a 100644 --- a/src/plugins/unified_search/public/query_string_input/text_based_languages_editor/helpers.ts +++ b/src/plugins/unified_search/public/query_string_input/text_based_languages_editor/helpers.ts @@ -9,6 +9,7 @@ import { useRef } from 'react'; import useDebounce from 'react-use/lib/useDebounce'; import { monaco } from '@kbn/monaco'; +import { getIndexPatternFromSQLQuery } from '@kbn/es-query'; import { i18n } from '@kbn/i18n'; export const useDebounceWithOptions = ( @@ -33,15 +34,6 @@ export const useDebounceWithOptions = ( ); }; -function getIndexPatternFromSQLQuery(sqlQuery?: string): string { - const sql = sqlQuery?.replaceAll('"', ''); - const matches = sql?.match(/FROM\s+([\w*]+)/); - if (matches) { - return matches[1]; - } - return ''; -} - export const parseErrors = (errors: Error[], code: string) => { return errors.map((error) => { if (error.message.includes('line')) { @@ -116,16 +108,20 @@ export const getDocumentationSections = async (language: string) => { items: Array<{ label: string; description?: JSX.Element }>; }> = []; if (language === 'sql') { - const { comparisonOperators, logicalOperators, mathOperators, initialSection } = await import( - './sql_documentation_sections' - ); + const { + comparisonOperators, + logicalOperators, + mathOperators, + initialSection, + aggregateFunctions, + } = await import('./sql_documentation_sections'); groups.push({ label: i18n.translate('unifiedSearch.query.textBasedLanguagesEditor.howItWorks', { defaultMessage: 'How it works', }), items: [], }); - groups.push(comparisonOperators, logicalOperators, mathOperators); + groups.push(comparisonOperators, logicalOperators, mathOperators, aggregateFunctions); return { groups, initialSection, diff --git a/src/plugins/unified_search/public/query_string_input/text_based_languages_editor/sql_documentation_sections.tsx b/src/plugins/unified_search/public/query_string_input/text_based_languages_editor/sql_documentation_sections.tsx index 45e69e75722bd..ead231afe55bb 100644 --- a/src/plugins/unified_search/public/query_string_input/text_based_languages_editor/sql_documentation_sections.tsx +++ b/src/plugins/unified_search/public/query_string_input/text_based_languages_editor/sql_documentation_sections.tsx @@ -482,3 +482,646 @@ SELECT 5 % 2 AS x; }, ], }; + +export const aggregateFunctions = { + label: i18n.translate('unifiedSearch.query.textBasedLanguagesEditor.aggregateFunctions', { + defaultMessage: 'Aggregate functions', + }), + description: i18n.translate( + 'unifiedSearch.query.textBasedLanguagesEditor.aggregateFunctionsDocumentationDescription', + { + defaultMessage: `Functions for computing a single result from a set of input values. Elasticsearch SQL supports aggregate functions only alongside grouping (implicit or explicit).`, + } + ), + items: [ + { + label: i18n.translate( + 'unifiedSearch.query.textBasedLanguagesEditor.documentation.averageFunction', + { + defaultMessage: 'Average', + } + ), + description: ( + + ), + }, + { + label: i18n.translate( + 'unifiedSearch.query.textBasedLanguagesEditor.documentation.countFunction', + { + defaultMessage: 'Count', + } + ), + description: ( + ), all values are considered, including null or missing ones. For COUNT(), null values are not considered. +\`\`\` +SELECT COUNT(*) AS count FROM emp; +\`\`\` + `, + description: + 'Text is in markdown. Do not translate function names, special characters, or field names like sum(bytes)', + } + )} + /> + ), + }, + { + label: i18n.translate( + 'unifiedSearch.query.textBasedLanguagesEditor.documentation.countAllFunction', + { + defaultMessage: 'Count (All)', + } + ), + description: ( + ) and COUNT(ALL ) are equivalent. + +\`\`\` +COUNT(ALL field_name) +\`\`\` +- a field name. If this field contains only null values, the function returns null. Otherwise, the function ignores null values in this field. +\`\`\` +SELECT COUNT(ALL last_name) AS count_all, COUNT(DISTINCT last_name) count_distinct FROM emp; +\`\`\` + `, + description: + 'Text is in markdown. Do not translate function names, special characters, or field names like sum(bytes)', + } + )} + /> + ), + }, + { + label: i18n.translate( + 'unifiedSearch.query.textBasedLanguagesEditor.documentation.countDistinctFunction', + { + defaultMessage: 'Count (Distinct)', + } + ), + description: ( + + ), + }, + { + label: i18n.translate( + 'unifiedSearch.query.textBasedLanguagesEditor.documentation.firstFunction', + { + defaultMessage: 'First / First_value', + } + ), + description: ( + + ), + }, + { + label: i18n.translate( + 'unifiedSearch.query.textBasedLanguagesEditor.documentation.lastFunction', + { + defaultMessage: 'Last / Last_value', + } + ), + description: ( + + ), + }, + { + label: i18n.translate( + 'unifiedSearch.query.textBasedLanguagesEditor.documentation.maxFunction', + { + defaultMessage: 'Max', + } + ), + description: ( + + ), + }, + { + label: i18n.translate( + 'unifiedSearch.query.textBasedLanguagesEditor.documentation.minFunction', + { + defaultMessage: 'Min', + } + ), + description: ( + + ), + }, + { + label: i18n.translate( + 'unifiedSearch.query.textBasedLanguagesEditor.documentation.sumFunction', + { + defaultMessage: 'Sum', + } + ), + description: ( + + ), + }, + { + label: i18n.translate( + 'unifiedSearch.query.textBasedLanguagesEditor.documentation.kurtosisFunction', + { + defaultMessage: 'Kurtosis', + } + ), + description: ( + + ), + }, + { + label: i18n.translate( + 'unifiedSearch.query.textBasedLanguagesEditor.documentation.madFunction', + { + defaultMessage: 'Mad', + } + ), + description: ( + + ), + }, + { + label: i18n.translate( + 'unifiedSearch.query.textBasedLanguagesEditor.documentation.percentileFunction', + { + defaultMessage: 'Percentile', + } + ), + description: ( + + ), + }, + { + label: i18n.translate( + 'unifiedSearch.query.textBasedLanguagesEditor.documentation.percentileRankFunction', + { + defaultMessage: 'Percentile rank', + } + ), + description: ( + + ), + }, + { + label: i18n.translate( + 'unifiedSearch.query.textBasedLanguagesEditor.documentation.skewnessFunction', + { + defaultMessage: 'Skewness', + } + ), + description: ( + + ), + }, + { + label: i18n.translate( + 'unifiedSearch.query.textBasedLanguagesEditor.documentation.stsdevpopFunction', + { + defaultMessage: 'STDDEV_POP', + } + ), + description: ( + + ), + }, + { + label: i18n.translate( + 'unifiedSearch.query.textBasedLanguagesEditor.documentation.stsdevsampFunction', + { + defaultMessage: 'STDDEV_SAMP', + } + ), + description: ( + + ), + }, + { + label: i18n.translate( + 'unifiedSearch.query.textBasedLanguagesEditor.documentation.sumofsquaresFunction', + { + defaultMessage: 'Sum of squares', + } + ), + description: ( + + ), + }, + { + label: i18n.translate( + 'unifiedSearch.query.textBasedLanguagesEditor.documentation.varpopFunction', + { + defaultMessage: 'VAR_POP', + } + ), + description: ( + + ), + }, + { + label: i18n.translate( + 'unifiedSearch.query.textBasedLanguagesEditor.documentation.varsampFunction', + { + defaultMessage: 'VAR_SAMP', + } + ), + description: ( + + ), + }, + ], +}; From 84560152cd0e07ef3ca1c041a274c712c42419cc Mon Sep 17 00:00:00 2001 From: Dzmitry Tamashevich Date: Mon, 4 Jul 2022 12:29:15 +0300 Subject: [PATCH 075/115] [Discover] fix tests --- .../components/layout/discover_layout.tsx | 10 ++--- .../top_nav/get_top_nav_links.test.ts | 1 + .../hooks/use_saved_search_messages.test.ts | 9 ++-- .../application/main/utils/fetch_all.test.ts | 42 +++++++++++-------- 4 files changed, 36 insertions(+), 26 deletions(-) diff --git a/src/plugins/discover/public/application/main/components/layout/discover_layout.tsx b/src/plugins/discover/public/application/main/components/layout/discover_layout.tsx index 1fdc30a06f9ae..661d9e31dfa4f 100644 --- a/src/plugins/discover/public/application/main/components/layout/discover_layout.tsx +++ b/src/plugins/discover/public/application/main/components/layout/discover_layout.tsx @@ -131,11 +131,11 @@ export function DiscoverLayout({ const [isSidebarClosed, setIsSidebarClosed] = useState(initialSidebarClosed); const useNewFieldsApi = useMemo(() => !uiSettings.get(SEARCH_FIELDS_FROM_SOURCE), [uiSettings]); + const isPlainRecord = dataState.recordRawType === RecordRawType.PLAIN; const resultState = useMemo( - () => getResultState(dataState.fetchStatus, dataState.foundDocuments!, dataState.recordRawType), - [dataState.fetchStatus, dataState.foundDocuments, dataState.recordRawType] + () => getResultState(dataState.fetchStatus, dataState.foundDocuments!, isPlainRecord), + [dataState.fetchStatus, dataState.foundDocuments, isPlainRecord] ); - const isPlainRecord = dataState.recordRawType === RecordRawType.PLAIN; const onOpenInspector = useCallback(() => { // prevent overlapping @@ -210,10 +210,10 @@ export function DiscoverLayout({ }, []); const textBasedLanguageModeErrors = useMemo(() => { - if (dataState.recordRawType) { + if (isPlainRecord) { return dataState.error; } - }, [dataState.error, dataState.recordRawType]); + }, [dataState.error, isPlainRecord]); return ( diff --git a/src/plugins/discover/public/application/main/components/top_nav/get_top_nav_links.test.ts b/src/plugins/discover/public/application/main/components/top_nav/get_top_nav_links.test.ts index 40ef4669db093..7906cabce469e 100644 --- a/src/plugins/discover/public/application/main/components/top_nav/get_top_nav_links.test.ts +++ b/src/plugins/discover/public/application/main/components/top_nav/get_top_nav_links.test.ts @@ -36,6 +36,7 @@ test('getTopNavLinks result', () => { state, searchSource: {} as ISearchSource, onOpenSavedSearch: () => {}, + isPlainRecord: false, }); expect(topNavLinks).toMatchInlineSnapshot(` Array [ diff --git a/src/plugins/discover/public/application/main/hooks/use_saved_search_messages.test.ts b/src/plugins/discover/public/application/main/hooks/use_saved_search_messages.test.ts index 0d74061ac46a3..1159aee1c5d13 100644 --- a/src/plugins/discover/public/application/main/hooks/use_saved_search_messages.test.ts +++ b/src/plugins/discover/public/application/main/hooks/use_saved_search_messages.test.ts @@ -14,7 +14,7 @@ import { } from './use_saved_search_messages'; import { FetchStatus } from '../../types'; import { BehaviorSubject } from 'rxjs'; -import { DataMainMsg } from './use_saved_search'; +import { DataMainMsg, RecordRawType } from './use_saved_search'; import { filter } from 'rxjs/operators'; describe('test useSavedSearch message generators', () => { @@ -52,14 +52,17 @@ describe('test useSavedSearch message generators', () => { sendPartialMsg(main$); }); test('sendLoadingMsg', (done) => { - const main$ = new BehaviorSubject({ fetchStatus: FetchStatus.COMPLETE }); + const main$ = new BehaviorSubject({ + fetchStatus: FetchStatus.COMPLETE, + }); main$.subscribe((value) => { if (value.fetchStatus !== FetchStatus.COMPLETE) { expect(value.fetchStatus).toBe(FetchStatus.LOADING); + expect(value.recordRawType).toBe(RecordRawType.DOCUMENT); done(); } }); - sendLoadingMsg(main$); + sendLoadingMsg(main$, RecordRawType.DOCUMENT); }); test('sendErrorMsg', (done) => { const main$ = new BehaviorSubject({ fetchStatus: FetchStatus.PARTIAL }); diff --git a/src/plugins/discover/public/application/main/utils/fetch_all.test.ts b/src/plugins/discover/public/application/main/utils/fetch_all.test.ts index ba46fbec1df3f..8fa058ac10605 100644 --- a/src/plugins/discover/public/application/main/utils/fetch_all.test.ts +++ b/src/plugins/discover/public/application/main/utils/fetch_all.test.ts @@ -121,9 +121,10 @@ describe('test fetchAll', () => { await fetchAll(subjects, searchSource, false, deps); expect(await collect()).toEqual([ { fetchStatus: FetchStatus.UNINITIALIZED }, - { fetchStatus: FetchStatus.LOADING, textBasedLanguageMode: '' }, + { fetchStatus: FetchStatus.LOADING, recordRawType: 'document' }, { fetchStatus: FetchStatus.COMPLETE, + recordRawType: 'document', result: documents, }, ]); @@ -143,9 +144,9 @@ describe('test fetchAll', () => { await fetchAll(subjects, searchSource, false, deps); expect(await collect()).toEqual([ { fetchStatus: FetchStatus.UNINITIALIZED }, - { fetchStatus: FetchStatus.LOADING, textBasedLanguageMode: '' }, - { fetchStatus: FetchStatus.PARTIAL, result: 2 }, - { fetchStatus: FetchStatus.COMPLETE, result: 42 }, + { fetchStatus: FetchStatus.LOADING, recordRawType: 'document' }, + { fetchStatus: FetchStatus.PARTIAL, recordRawType: 'document', result: 2 }, + { fetchStatus: FetchStatus.COMPLETE, recordRawType: 'document', result: 42 }, ]); }); @@ -155,8 +156,13 @@ describe('test fetchAll', () => { await fetchAll(subjects, searchSource, false, deps); expect(await collect()).toEqual([ { fetchStatus: FetchStatus.UNINITIALIZED }, - { fetchStatus: FetchStatus.LOADING, textBasedLanguageMode: '' }, - { fetchStatus: FetchStatus.COMPLETE, bucketInterval: {}, chartData: {} }, + { fetchStatus: FetchStatus.LOADING, recordRawType: 'document' }, + { + fetchStatus: FetchStatus.COMPLETE, + recordRawType: 'document', + bucketInterval: {}, + chartData: {}, + }, ]); }); @@ -168,9 +174,9 @@ describe('test fetchAll', () => { await fetchAll(subjects, searchSource, false, deps); expect(await collect()).toEqual([ { fetchStatus: FetchStatus.UNINITIALIZED }, - { fetchStatus: FetchStatus.LOADING, textBasedLanguageMode: '' }, - { fetchStatus: FetchStatus.PARTIAL, result: 0 }, // From documents query - { fetchStatus: FetchStatus.COMPLETE, result: 32 }, + { fetchStatus: FetchStatus.LOADING, recordRawType: 'document' }, + { fetchStatus: FetchStatus.PARTIAL, recordRawType: 'document', result: 0 }, // From documents query + { fetchStatus: FetchStatus.COMPLETE, recordRawType: 'document', result: 32 }, ]); expect(mockFetchTotalHits).not.toHaveBeenCalled(); }); @@ -186,19 +192,19 @@ describe('test fetchAll', () => { await fetchAll(subjects, searchSource, false, deps); expect(await collectTotalHits()).toEqual([ { fetchStatus: FetchStatus.UNINITIALIZED }, - { fetchStatus: FetchStatus.LOADING, textBasedLanguageMode: '' }, - { fetchStatus: FetchStatus.PARTIAL, result: 1 }, - { fetchStatus: FetchStatus.ERROR, error: { msg: 'Oh noes!' } }, + { fetchStatus: FetchStatus.LOADING, recordRawType: 'document' }, + { fetchStatus: FetchStatus.PARTIAL, recordRawType: 'document', result: 1 }, + { fetchStatus: FetchStatus.ERROR, recordRawType: 'document', error: { msg: 'Oh noes!' } }, ]); expect(await collectMain()).toEqual([ { fetchStatus: FetchStatus.UNINITIALIZED }, - { fetchStatus: FetchStatus.LOADING, textBasedLanguageMode: '' }, - { fetchStatus: FetchStatus.PARTIAL, textBasedLanguageMode: '' }, + { fetchStatus: FetchStatus.LOADING, recordRawType: 'document' }, + { fetchStatus: FetchStatus.PARTIAL, recordRawType: 'document' }, { fetchStatus: FetchStatus.COMPLETE, foundDocuments: true, error: undefined, - textBasedLanguageMode: '', + recordRawType: 'document', }, ]); }); @@ -210,12 +216,12 @@ describe('test fetchAll', () => { await fetchAll(subjects, searchSource, false, deps); expect(await collectMain()).toEqual([ { fetchStatus: FetchStatus.UNINITIALIZED }, - { fetchStatus: FetchStatus.LOADING, textBasedLanguageMode: '' }, - { fetchStatus: FetchStatus.PARTIAL, textBasedLanguageMode: '' }, // From totalHits query + { fetchStatus: FetchStatus.LOADING, recordRawType: 'document' }, + { fetchStatus: FetchStatus.PARTIAL, recordRawType: 'document' }, // From totalHits query { fetchStatus: FetchStatus.ERROR, error: { msg: 'This query failed' }, - textBasedLanguageMode: '', + recordRawType: 'document', }, // Here should be no COMPLETE coming anymore ]); From 4a64882a96596795be8032d05ac9f9c62e97cf69 Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Mon, 4 Jul 2022 12:51:13 +0300 Subject: [PATCH 076/115] Add some unit tests --- .../src/es_query/build_es_query.ts | 2 +- .../src/es_query/es_query_sql.test.ts | 62 +++++++++++++++ .../{es_query.sql.ts => es_query_sql.ts} | 0 packages/kbn-es-query/src/es_query/index.ts | 2 +- .../common/query/to_expression_ast.test.ts | 79 +++++++++++++++++++ .../data/common/query/to_expression_ast.ts | 1 + .../aggregate_query_to_ast.test.ts | 25 ++++++ .../discover/public/__mocks__/services.ts | 3 + .../layout/discover_layout.test.tsx | 16 +++- .../sidebar/discover_field.test.tsx | 21 ++++- .../components/sidebar/discover_field.tsx | 1 + .../discover_sidebar_responsive.test.tsx | 18 +++++ .../top_nav/discover_topnav.test.tsx | 4 +- .../components/top_nav/discover_topnav.tsx | 2 +- .../top_nav/get_top_nav_links.test.ts | 55 +++++++++++++ .../main/hooks/use_saved_search.test.ts | 29 ++++++- .../application/main/utils/fetch_all.test.ts | 41 ++++++++++ .../editor_footer.tsx | 2 +- 18 files changed, 352 insertions(+), 11 deletions(-) create mode 100644 packages/kbn-es-query/src/es_query/es_query_sql.test.ts rename packages/kbn-es-query/src/es_query/{es_query.sql.ts => es_query_sql.ts} (100%) create mode 100644 src/plugins/data/common/search/expressions/aggregate_query_to_ast.test.ts diff --git a/packages/kbn-es-query/src/es_query/build_es_query.ts b/packages/kbn-es-query/src/es_query/build_es_query.ts index 3ff2f0f650001..4428541413e78 100644 --- a/packages/kbn-es-query/src/es_query/build_es_query.ts +++ b/packages/kbn-es-query/src/es_query/build_es_query.ts @@ -12,7 +12,7 @@ import { buildQueryFromKuery } from './from_kuery'; import { buildQueryFromFilters } from './from_filters'; import { buildQueryFromLucene } from './from_lucene'; import { Filter, Query, AggregateQuery } from '../filters'; -import { isOfQueryType } from './es_query.sql'; +import { isOfQueryType } from './es_query_sql'; import { BoolQuery, DataViewBase } from './types'; import type { KueryQueryOptions } from '../kuery'; import type { EsQueryFiltersConfig } from './from_filters'; diff --git a/packages/kbn-es-query/src/es_query/es_query_sql.test.ts b/packages/kbn-es-query/src/es_query/es_query_sql.test.ts new file mode 100644 index 0000000000000..e1e4d0b4796d6 --- /dev/null +++ b/packages/kbn-es-query/src/es_query/es_query_sql.test.ts @@ -0,0 +1,62 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { + isOfQueryType, + isOfAggregateQueryType, + getAggregateQueryMode, + getIndexPatternFromSQLQuery, +} from './es_query_sql'; + +describe('sql query helpers', () => { + describe('isOfQueryType', () => { + it('should return true for a Query type query', () => { + const flag = isOfQueryType({ query: 'foo', language: 'test' }); + expect(flag).toBe(true); + }); + + it('should return false for an Aggregate type query', () => { + const flag = isOfQueryType({ sql: 'SELECT * FROM foo' }); + expect(flag).toBe(false); + }); + }); + + describe('isOfAggregateQueryType', () => { + it('should return false for a Query type query', () => { + const flag = isOfAggregateQueryType({ query: 'foo', language: 'test' }); + expect(flag).toBe(false); + }); + + it('should return true for an Aggregate type query', () => { + const flag = isOfAggregateQueryType({ sql: 'SELECT * FROM foo' }); + expect(flag).toBe(true); + }); + }); + + describe('getAggregateQueryMode', () => { + it('should return sql for an SQL AggregateQuery type', () => { + const mode = getAggregateQueryMode({ sql: 'SELECT * FROM foo' }); + expect(mode).toBe('sql'); + }); + + it('should return esql for an ESQL AggregateQuery type', () => { + const mode = getAggregateQueryMode({ esql: 'foo | where field > 100' }); + expect(mode).toBe('esql'); + }); + }); + + describe('getIndexPatternFromSQLQuery', () => { + it('should return the index pattern string from sql queries', () => { + const idxPattern1 = getIndexPatternFromSQLQuery('SELECT * FROM foo'); + expect(idxPattern1).toBe('foo'); + + const idxPattern2 = getIndexPatternFromSQLQuery('SELECT woof, meow FROM "foo"'); + expect(idxPattern2).toBe('foo'); + }); + }); +}); diff --git a/packages/kbn-es-query/src/es_query/es_query.sql.ts b/packages/kbn-es-query/src/es_query/es_query_sql.ts similarity index 100% rename from packages/kbn-es-query/src/es_query/es_query.sql.ts rename to packages/kbn-es-query/src/es_query/es_query_sql.ts diff --git a/packages/kbn-es-query/src/es_query/index.ts b/packages/kbn-es-query/src/es_query/index.ts index 61f11e9d4d89e..5f14b1f03769e 100644 --- a/packages/kbn-es-query/src/es_query/index.ts +++ b/packages/kbn-es-query/src/es_query/index.ts @@ -18,7 +18,7 @@ export { isOfAggregateQueryType, getAggregateQueryMode, getIndexPatternFromSQLQuery, -} from './es_query.sql'; +} from './es_query_sql'; export type { IFieldSubType, BoolQuery, diff --git a/src/plugins/data/common/query/to_expression_ast.test.ts b/src/plugins/data/common/query/to_expression_ast.test.ts index 0f376e98256a8..7a72359535a2a 100644 --- a/src/plugins/data/common/query/to_expression_ast.test.ts +++ b/src/plugins/data/common/query/to_expression_ast.test.ts @@ -59,4 +59,83 @@ describe('queryStateToExpressionAst', () => { } `); }); + + it('returns an object with the correct structure for an SQL query', async () => { + const dataViewsService = { + getIdsWithTitle: jest.fn(() => { + return [ + { + title: 'foo', + id: 'bar', + }, + ]; + }), + get: jest.fn(() => { + return { + title: 'foo', + id: 'bar', + timeFieldName: 'baz', + }; + }), + } as unknown as DataViewsContract; + const actual = await queryStateToExpressionAst({ + filters: [], + query: { sql: 'SELECT * FROM foo' }, + time: { + from: 'now', + to: 'now+7d', + }, + dataViewsService, + }); + + expect(actual).toMatchInlineSnapshot(` + Object { + "chain": Array [ + Object { + "arguments": Object {}, + "function": "kibana", + "type": "function", + }, + Object { + "arguments": Object { + "timeRange": Array [ + Object { + "chain": Array [ + Object { + "arguments": Object { + "from": Array [ + "now", + ], + "to": Array [ + "now+7d", + ], + }, + "function": "timerange", + "type": "function", + }, + ], + "type": "expression", + }, + ], + }, + "function": "kibana_context", + "type": "function", + }, + Object { + "arguments": Object { + "query": Array [ + "SELECT * FROM foo", + ], + "timeField": Array [ + "baz", + ], + }, + "function": "essql", + "type": "function", + }, + ], + "type": "expression", + } + `); + }); }); diff --git a/src/plugins/data/common/query/to_expression_ast.ts b/src/plugins/data/common/query/to_expression_ast.ts index fb60a77a505d9..23b23aec03aa2 100644 --- a/src/plugins/data/common/query/to_expression_ast.ts +++ b/src/plugins/data/common/query/to_expression_ast.ts @@ -39,6 +39,7 @@ export async function queryStateToExpressionAst({ filters, query, time, dataView if (query && isOfAggregateQueryType(query)) { const mode = getAggregateQueryMode(query); + // sql query if (mode === 'sql') { const idxPattern = getIndexPatternFromSQLQuery(query.sql); const idsTitles = await dataViewsService.getIdsWithTitle(); diff --git a/src/plugins/data/common/search/expressions/aggregate_query_to_ast.test.ts b/src/plugins/data/common/search/expressions/aggregate_query_to_ast.test.ts new file mode 100644 index 0000000000000..f292954feea82 --- /dev/null +++ b/src/plugins/data/common/search/expressions/aggregate_query_to_ast.test.ts @@ -0,0 +1,25 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { aggregateQueryToAst } from './aggregate_query_to_ast'; + +describe('aggregateQueryToAst', () => { + it('should return a function', () => { + expect(aggregateQueryToAst({ sql: 'SELECT * from foo' })).toHaveProperty('type', 'function'); + }); + + it('should forward arguments', () => { + expect(aggregateQueryToAst({ sql: 'SELECT * from foo' }, 'baz')).toHaveProperty( + 'arguments', + expect.objectContaining({ + query: ['SELECT * from foo'], + timeField: ['baz'], + }) + ); + }); +}); diff --git a/src/plugins/discover/public/__mocks__/services.ts b/src/plugins/discover/public/__mocks__/services.ts index 5e528433f22ab..54cbae557c6ab 100644 --- a/src/plugins/discover/public/__mocks__/services.ts +++ b/src/plugins/discover/public/__mocks__/services.ts @@ -8,6 +8,7 @@ import { EUI_CHARTS_THEME_LIGHT } from '@elastic/eui/dist/eui_charts_theme'; import { DiscoverServices } from '../build_services'; import { dataPluginMock } from '@kbn/data-plugin/public/mocks'; +import { expressionsPluginMock } from '@kbn/expressions-plugin/public/mocks'; import { chromeServiceMock, coreMock, docLinksServiceMock } from '@kbn/core/public/mocks'; import { CONTEXT_STEP_SETTING, @@ -24,6 +25,7 @@ import { FORMATS_UI_SETTINGS } from '@kbn/field-formats-plugin/common'; import { LocalStorageMock } from './local_storage_mock'; import { fieldFormatsMock } from '@kbn/field-formats-plugin/common/mocks'; const dataPlugin = dataPluginMock.createStartContract(); +const expressionsPlugin = expressionsPluginMock.createStartContract(); export const discoverServiceMock = { core: coreMock.createStart(), @@ -102,4 +104,5 @@ export const discoverServiceMock = { addInfo: jest.fn(), addWarning: jest.fn(), }, + expressions: expressionsPlugin, } as unknown as DiscoverServices; diff --git a/src/plugins/discover/public/application/main/components/layout/discover_layout.test.tsx b/src/plugins/discover/public/application/main/components/layout/discover_layout.test.tsx index 4aff6e2a78070..29198a08639ae 100644 --- a/src/plugins/discover/public/application/main/components/layout/discover_layout.test.tsx +++ b/src/plugins/discover/public/application/main/components/layout/discover_layout.test.tsx @@ -9,6 +9,7 @@ import React from 'react'; import { Subject, BehaviorSubject } from 'rxjs'; import { mountWithIntl } from '@kbn/test-jest-helpers'; +import type { Query, AggregateQuery } from '@kbn/es-query'; import { setHeaderActionMenuMounter } from '../../../../kibana_services'; import { DiscoverLayout, SIDEBAR_CLOSED_KEY } from './discover_layout'; import { esHits } from '../../../../__mocks__/es_hits'; @@ -42,7 +43,8 @@ setHeaderActionMenuMounter(jest.fn()); function mountComponent( indexPattern: DataView, prevSidebarClosed?: boolean, - mountOptions: { attachTo?: HTMLElement } = {} + mountOptions: { attachTo?: HTMLElement } = {}, + query?: Query | AggregateQuery ) { const searchSourceMock = createSearchSourceMock({}); const services = { @@ -148,7 +150,7 @@ function mountComponent( savedSearchData$, savedSearchRefetch$: new Subject(), searchSource: searchSourceMock, - state: { columns: [] }, + state: { columns: [], query }, stateContainer: { setAppState: () => {}, appStateContainer: { @@ -179,6 +181,16 @@ describe('Discover component', () => { expect(component.find('[data-test-subj="discoverChartOptionsToggle"]').exists()).toBeTruthy(); }); + test('sql query displays no chart toggle', () => { + const component = mountComponent( + indexPatternWithTimefieldMock, + false, + {}, + { sql: 'SELECT * FROM test' } + ); + expect(component.find('[data-test-subj="discoverChartOptionsToggle"]').exists()).toBeFalsy(); + }); + test('the saved search title h1 gains focus on navigate', () => { const container = document.createElement('div'); document.body.appendChild(container); diff --git a/src/plugins/discover/public/application/main/components/sidebar/discover_field.test.tsx b/src/plugins/discover/public/application/main/components/sidebar/discover_field.test.tsx index 4b2ccf1bff0bb..4b2aa7f9ded84 100644 --- a/src/plugins/discover/public/application/main/components/sidebar/discover_field.test.tsx +++ b/src/plugins/discover/public/application/main/components/sidebar/discover_field.test.tsx @@ -27,10 +27,12 @@ function getComponent({ selected = false, showDetails = false, field, + onAddFilterExists = true, }: { selected?: boolean; showDetails?: boolean; field?: DataViewField; + onAddFilterExists?: boolean; }) { const finalField = field ?? @@ -49,7 +51,7 @@ function getComponent({ indexPattern: stubDataView, field: finalField, getDetails: jest.fn(() => ({ buckets: [], error: '', exists: 1, total: 2, columns: [] })), - onAddFilter: jest.fn(), + ...(onAddFilterExists && { onAddFilter: jest.fn() }), onAddField: jest.fn(), onRemoveField: jest.fn(), showDetails, @@ -139,4 +141,21 @@ describe('discover sidebar field', function () { findTestSubject(comp, 'field-bytes-showDetails').simulate('click'); expect(props.getDetails.mock.calls.length).toEqual(1); }); + it('should not return the popover if onAddFilter is not provided', function () { + const field = new DataViewField({ + name: '_source', + type: '_source', + esTypes: ['_source'], + searchable: true, + aggregatable: true, + readFromDocValues: true, + }); + const { comp } = getComponent({ + selected: true, + field, + onAddFilterExists: false, + }); + const popover = findTestSubject(comp, 'discoverFieldListPanelPopover'); + expect(popover.length).toBe(0); + }); }); diff --git a/src/plugins/discover/public/application/main/components/sidebar/discover_field.tsx b/src/plugins/discover/public/application/main/components/sidebar/discover_field.tsx index 7bda9268b8621..f8780a9b10957 100644 --- a/src/plugins/discover/public/application/main/components/sidebar/discover_field.tsx +++ b/src/plugins/discover/public/application/main/components/sidebar/discover_field.tsx @@ -442,6 +442,7 @@ function DiscoverFieldComponent({ button={button} isOpen={infoIsOpen} closePopover={() => setOpen(false)} + data-test-subj="discoverFieldListPanelPopover" anchorPosition="rightUp" panelClassName="dscSidebarItem__fieldPopoverPanel" > diff --git a/src/plugins/discover/public/application/main/components/sidebar/discover_sidebar_responsive.test.tsx b/src/plugins/discover/public/application/main/components/sidebar/discover_sidebar_responsive.test.tsx index da2d232108d1d..0c20ec88b7966 100644 --- a/src/plugins/discover/public/application/main/components/sidebar/discover_sidebar_responsive.test.tsx +++ b/src/plugins/discover/public/application/main/components/sidebar/discover_sidebar_responsive.test.tsx @@ -166,6 +166,24 @@ describe('discover responsive sidebar', function () { expect(findTestSubject(comp, 'indexPattern-add-field_btn').length).toBe(1); }); + it('should not show "Add a field" button on the sql mode', () => { + const initialProps = getCompProps(); + const propsWithTextBasedMode = { + ...initialProps, + documents$: new BehaviorSubject({ + fetchStatus: FetchStatus.COMPLETE, + result: [], + textBasedLanguageMode: 'sql', + }) as unknown as DataDocuments$, + }; + const compInViewerMode = mountWithIntl( + + + + ); + expect(findTestSubject(compInViewerMode, 'indexPattern-add-field_btn').length).toBe(0); + }); + it('should not show "Add a field" button in viewer mode', () => { const mockedServicesInViewerMode = { ...mockServices, diff --git a/src/plugins/discover/public/application/main/components/top_nav/discover_topnav.test.tsx b/src/plugins/discover/public/application/main/components/top_nav/discover_topnav.test.tsx index 2922faf1e9295..f228c17341ac1 100644 --- a/src/plugins/discover/public/application/main/components/top_nav/discover_topnav.test.tsx +++ b/src/plugins/discover/public/application/main/components/top_nav/discover_topnav.test.tsx @@ -31,9 +31,7 @@ function getProps(savePermissions = true): DiscoverTopNavProps { discoverServiceMock.capabilities.discover!.save = savePermissions; return { - stateContainer: { - stateContainer: {} as GetStateReturn, - } as unknown as GetStateReturn, + stateContainer: {} as GetStateReturn, indexPattern: indexPatternMock, savedSearch: savedSearchMock, navigateTo: jest.fn(), diff --git a/src/plugins/discover/public/application/main/components/top_nav/discover_topnav.tsx b/src/plugins/discover/public/application/main/components/top_nav/discover_topnav.tsx index efda8294cbd08..8698fad36e42b 100644 --- a/src/plugins/discover/public/application/main/components/top_nav/discover_topnav.tsx +++ b/src/plugins/discover/public/application/main/components/top_nav/discover_topnav.tsx @@ -23,7 +23,7 @@ export type DiscoverTopNavProps = Pick< 'indexPattern' | 'navigateTo' | 'savedSearch' | 'searchSource' > & { onOpenInspector: () => void; - query?: Query; + query?: Query | AggregateQuery; savedQuery?: string; updateQuery: ( payload: { dateRange: TimeRange; query?: Query | AggregateQuery }, diff --git a/src/plugins/discover/public/application/main/components/top_nav/get_top_nav_links.test.ts b/src/plugins/discover/public/application/main/components/top_nav/get_top_nav_links.test.ts index 40ef4669db093..762873a807e7c 100644 --- a/src/plugins/discover/public/application/main/components/top_nav/get_top_nav_links.test.ts +++ b/src/plugins/discover/public/application/main/components/top_nav/get_top_nav_links.test.ts @@ -86,3 +86,58 @@ test('getTopNavLinks result', () => { ] `); }); + +test('getTopNavLinks result for sql mode', () => { + const topNavLinks = getTopNavLinks({ + indexPattern: indexPatternMock, + navigateTo: jest.fn(), + onOpenInspector: jest.fn(), + savedSearch: savedSearchMock, + services, + state, + searchSource: {} as ISearchSource, + onOpenSavedSearch: () => {}, + textBasedLanguageMode: 'sql', + }); + expect(topNavLinks).toMatchInlineSnapshot(` + Array [ + Object { + "description": "Options", + "id": "options", + "label": "Options", + "run": [Function], + "testId": "discoverOptionsButton", + }, + Object { + "description": "New Search", + "id": "new", + "label": "New", + "run": [Function], + "testId": "discoverNewButton", + }, + Object { + "description": "Open Saved Search", + "id": "open", + "label": "Open", + "run": [Function], + "testId": "discoverOpenButton", + }, + Object { + "description": "Open Inspector for search", + "id": "inspect", + "label": "Inspect", + "run": [Function], + "testId": "openInspectorButton", + }, + Object { + "description": "Save Search", + "emphasize": true, + "iconType": "save", + "id": "save", + "label": "Save", + "run": [Function], + "testId": "discoverSaveButton", + }, + ] + `); +}); diff --git a/src/plugins/discover/public/application/main/hooks/use_saved_search.test.ts b/src/plugins/discover/public/application/main/hooks/use_saved_search.test.ts index 597d8b76be8aa..20b0fce8ec150 100644 --- a/src/plugins/discover/public/application/main/hooks/use_saved_search.test.ts +++ b/src/plugins/discover/public/application/main/hooks/use_saved_search.test.ts @@ -11,7 +11,7 @@ import { createSearchSessionMock } from '../../../__mocks__/search_session'; import { discoverServiceMock } from '../../../__mocks__/services'; import { savedSearchMock } from '../../../__mocks__/saved_search'; import { useSavedSearch } from './use_saved_search'; -import { getState } from '../services/discover_state'; +import { getState, AppState } from '../services/discover_state'; import { uiSettingsMock } from '../../../__mocks__/ui_settings'; import { useDiscoverState } from './use_discover_state'; import { FetchStatus } from '../../types'; @@ -128,4 +128,31 @@ describe('test useSavedSearch', () => { result.current.reset(); expect(result.current.data$.main$.value.fetchStatus).toBe(FetchStatus.LOADING); }); + + test('useSavedSearch returns textBasedLanguageMode', async () => { + const { history, searchSessionManager } = createSearchSessionMock(); + const stateContainer = getState({ + getStateDefaults: () => + ({ + index: 'the-index-pattern-id', + query: { sql: 'SELECT * FROM test' }, + } as unknown as AppState), + history, + uiSettings: uiSettingsMock, + }); + + const { result } = renderHook(() => { + return useSavedSearch({ + initialFetchStatus: FetchStatus.LOADING, + savedSearch: savedSearchMock, + searchSessionManager, + searchSource: savedSearchMock.searchSource.createCopy(), + services: discoverServiceMock, + stateContainer, + useNewFieldsApi: true, + }); + }); + + expect(result.current.data$.main$.getValue().textBasedLanguageMode).toBe('sql'); + }); }); diff --git a/src/plugins/discover/public/application/main/utils/fetch_all.test.ts b/src/plugins/discover/public/application/main/utils/fetch_all.test.ts index ba46fbec1df3f..59d3bd1053bab 100644 --- a/src/plugins/discover/public/application/main/utils/fetch_all.test.ts +++ b/src/plugins/discover/public/application/main/utils/fetch_all.test.ts @@ -25,6 +25,7 @@ import { } from '../hooks/use_saved_search'; import { fetchDocuments } from './fetch_documents'; +import { fetchSql } from './fetch_sql'; import { fetchChart } from './fetch_chart'; import { fetchTotalHits } from './fetch_total_hits'; import { indexPatternMock } from '../../../__mocks__/index_pattern'; @@ -34,6 +35,10 @@ jest.mock('./fetch_documents', () => ({ fetchDocuments: jest.fn().mockResolvedValue([]), })); +jest.mock('./fetch_sql', () => ({ + fetchSql: jest.fn().mockResolvedValue([]), +})); + jest.mock('./fetch_chart', () => ({ fetchChart: jest.fn(), })); @@ -45,6 +50,7 @@ jest.mock('./fetch_total_hits', () => ({ const mockFetchDocuments = fetchDocuments as unknown as jest.MockedFunction; const mockFetchTotalHits = fetchTotalHits as unknown as jest.MockedFunction; const mockFetchChart = fetchChart as unknown as jest.MockedFunction; +const mockFetchSQL = fetchSql as unknown as jest.MockedFunction; function subjectCollector(subject: Subject): () => Promise { const promise = firstValueFrom( @@ -89,6 +95,7 @@ describe('test fetchAll', () => { searchSource = savedSearchMock.searchSource.createChild(); mockFetchDocuments.mockReset().mockResolvedValue([]); + mockFetchSQL.mockReset().mockResolvedValue([]); mockFetchTotalHits.mockReset().mockResolvedValue(42); mockFetchChart .mockReset() @@ -220,4 +227,38 @@ describe('test fetchAll', () => { // Here should be no COMPLETE coming anymore ]); }); + + test('emits loading and documents on documents$ correctly for SQL query', async () => { + const collect = subjectCollector(subjects.documents$); + const hits = [ + { _id: '1', _index: 'logs' }, + { _id: '2', _index: 'logs' }, + ]; + const documents = hits.map((hit) => buildDataTableRecord(hit, indexPatternMock)); + mockFetchSQL.mockResolvedValue(documents); + deps = { + appStateContainer: { + getState: () => { + return { interval: 'auto', query: { sql: 'SELECT * from foo' } }; + }, + } as unknown as ReduxLikeStateContainer, + abortController: new AbortController(), + data: discoverServiceMock.data, + inspectorAdapters: { requests: new RequestAdapter() }, + searchSessionId: '123', + initialFetchStatus: FetchStatus.UNINITIALIZED, + useNewFieldsApi: true, + savedSearch: savedSearchMock, + services: discoverServiceMock, + }; + await fetchAll(subjects, searchSource, false, deps); + expect(await collect()).toEqual([ + { fetchStatus: FetchStatus.UNINITIALIZED }, + { fetchStatus: FetchStatus.LOADING, textBasedLanguageMode: 'sql' }, + { + fetchStatus: FetchStatus.COMPLETE, + result: documents, + }, + ]); + }); }); diff --git a/src/plugins/unified_search/public/query_string_input/text_based_languages_editor/editor_footer.tsx b/src/plugins/unified_search/public/query_string_input/text_based_languages_editor/editor_footer.tsx index a6072b407d406..4cdb659781d9d 100644 --- a/src/plugins/unified_search/public/query_string_input/text_based_languages_editor/editor_footer.tsx +++ b/src/plugins/unified_search/public/query_string_input/text_based_languages_editor/editor_footer.tsx @@ -91,7 +91,7 @@ export const EditorFooter = memo(function EditorFooter({ isOpen={isPopoverOpen} closePopover={() => setIsPopoverOpen(false)} > -
+
{i18n.translate( 'unifiedSearch.query.textBasedLanguagesEditor.errorsTitle', From 2f7a36a08e0968c61f6f338b6045f9b852ae3da7 Mon Sep 17 00:00:00 2001 From: Dzmitry Tamashevich Date: Mon, 4 Jul 2022 12:52:22 +0300 Subject: [PATCH 077/115] [Discover] fix linting --- .../discover/public/application/main/utils/get_result_state.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/discover/public/application/main/utils/get_result_state.ts b/src/plugins/discover/public/application/main/utils/get_result_state.ts index 3606f17952b5a..32f3914b79c19 100644 --- a/src/plugins/discover/public/application/main/utils/get_result_state.ts +++ b/src/plugins/discover/public/application/main/utils/get_result_state.ts @@ -21,7 +21,7 @@ export const resultStatuses = { export function getResultState( fetchStatus: FetchStatus, foundDocuments: boolean = false, - isPlainRecord?: string + isPlainRecord: boolean ) { if (fetchStatus === FetchStatus.UNINITIALIZED) { return resultStatuses.UNINITIALIZED; From 90e314077eaa22aa75f2fc4d3adc3e76f9727d16 Mon Sep 17 00:00:00 2001 From: Dzmitry Tamashevich Date: Mon, 4 Jul 2022 13:42:44 +0300 Subject: [PATCH 078/115] [Discover] update linting --- .../discover/public/application/main/utils/get_result_state.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/discover/public/application/main/utils/get_result_state.ts b/src/plugins/discover/public/application/main/utils/get_result_state.ts index 32f3914b79c19..ff31e114754ad 100644 --- a/src/plugins/discover/public/application/main/utils/get_result_state.ts +++ b/src/plugins/discover/public/application/main/utils/get_result_state.ts @@ -21,7 +21,7 @@ export const resultStatuses = { export function getResultState( fetchStatus: FetchStatus, foundDocuments: boolean = false, - isPlainRecord: boolean + isPlainRecord?: boolean ) { if (fetchStatus === FetchStatus.UNINITIALIZED) { return resultStatuses.UNINITIALIZED; From c4ac2d0b3a647e619aa264aa1b851678f085f1f2 Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Mon, 4 Jul 2022 13:58:59 +0300 Subject: [PATCH 079/115] More unti tests --- .../src/es_query/es_query_sql.test.ts | 17 ++++ .../kbn-es-query/src/es_query/es_query_sql.ts | 4 +- .../saved_searches/get_saved_searches.test.ts | 90 +++++++++++++++++++ .../saved_searches_utils.test.ts | 3 +- .../get_text_based_language_mode.test.ts | 22 +++++ 5 files changed, 134 insertions(+), 2 deletions(-) create mode 100644 src/plugins/discover/public/utils/get_text_based_language_mode.test.ts diff --git a/packages/kbn-es-query/src/es_query/es_query_sql.test.ts b/packages/kbn-es-query/src/es_query/es_query_sql.test.ts index e1e4d0b4796d6..2747411769c98 100644 --- a/packages/kbn-es-query/src/es_query/es_query_sql.test.ts +++ b/packages/kbn-es-query/src/es_query/es_query_sql.test.ts @@ -57,6 +57,23 @@ describe('sql query helpers', () => { const idxPattern2 = getIndexPatternFromSQLQuery('SELECT woof, meow FROM "foo"'); expect(idxPattern2).toBe('foo'); + + const idxPattern3 = getIndexPatternFromSQLQuery('SELECT woof, meow FROM "the_index_pattern"'); + expect(idxPattern3).toBe('the_index_pattern'); + + const idxPattern4 = getIndexPatternFromSQLQuery('SELECT woof, meow FROM "the-index-pattern"'); + expect(idxPattern4).toBe('the-index-pattern'); + + const idxPattern5 = getIndexPatternFromSQLQuery('SELECT woof, meow from "the-index-pattern"'); + expect(idxPattern5).toBe('the-index-pattern'); + + const idxPattern6 = getIndexPatternFromSQLQuery('SELECT woof, meow from "logstash-*"'); + expect(idxPattern6).toBe('logstash-*'); + + const idxPattern7 = getIndexPatternFromSQLQuery( + 'SELECT woof, meow from logstash-1234! WHERE field > 100' + ); + expect(idxPattern7).toBe('logstash-1234!'); }); }); }); diff --git a/packages/kbn-es-query/src/es_query/es_query_sql.ts b/packages/kbn-es-query/src/es_query/es_query_sql.ts index 2c17d6c89a01e..2b604d62fd269 100644 --- a/packages/kbn-es-query/src/es_query/es_query_sql.ts +++ b/packages/kbn-es-query/src/es_query/es_query_sql.ts @@ -27,7 +27,9 @@ export function getAggregateQueryMode(query: AggregateQuery): string { // retrieves the index pattern from the aggregate query export function getIndexPatternFromSQLQuery(sqlQuery?: string): string { const sql = sqlQuery?.replaceAll('"', '').replaceAll("'", ''); - const matches = sql?.match(/FROM\s+([\w*]+)/); + // case insensitive match for the index pattern + const regex = new RegExp(/FROM\s+([\w*-.!@$^()~;]+)/, 'i'); + const matches = sql?.match(regex); if (matches) { return matches[1]; } diff --git a/src/plugins/discover/public/services/saved_searches/get_saved_searches.test.ts b/src/plugins/discover/public/services/saved_searches/get_saved_searches.test.ts index 57d9e215703c4..6d7b28eda71cd 100644 --- a/src/plugins/discover/public/services/saved_searches/get_saved_searches.test.ts +++ b/src/plugins/discover/public/services/saved_searches/get_saved_searches.test.ts @@ -147,4 +147,94 @@ describe('getSavedSearch', () => { } `); }); + + test('should find saved search with sql mode', async () => { + savedObjectsClient.resolve = jest.fn().mockReturnValue({ + saved_object: { + attributes: { + kibanaSavedObjectMeta: { + searchSourceJSON: + '{"query":{"sql":"SELECT * FROM foo"},"filter":[],"indexRefName":"kibanaSavedObjectMeta.searchSourceJSON.index"}', + }, + title: 'test2', + sort: [['order_date', 'desc']], + columns: ['_source'], + description: 'description', + grid: {}, + hideChart: true, + isTextBasedQuery: true, + }, + id: 'ccf1af80-2297-11ec-86e0-1155ffb9c7a7', + type: 'search', + references: [ + { + name: 'kibanaSavedObjectMeta.searchSourceJSON.index', + id: 'ff959d40-b880-11e8-a6d9-e546fe2bba5f', + type: 'index-pattern', + }, + ], + namespaces: ['default'], + }, + outcome: 'exactMatch', + }); + + const savedSearch = await getSavedSearch('ccf1af80-2297-11ec-86e0-1155ffb9c7a7', { + savedObjectsClient, + search, + }); + + expect(savedObjectsClient.resolve).toHaveBeenCalled(); + expect(savedSearch).toMatchInlineSnapshot(` + Object { + "columns": Array [ + "_source", + ], + "description": "description", + "grid": Object {}, + "hideAggregatedPreview": undefined, + "hideChart": true, + "id": "ccf1af80-2297-11ec-86e0-1155ffb9c7a7", + "isTextBasedQuery": true, + "rowHeight": undefined, + "searchSource": Object { + "create": [MockFunction], + "createChild": [MockFunction], + "createCopy": [MockFunction], + "destroy": [MockFunction], + "fetch": [MockFunction], + "fetch$": [MockFunction], + "getField": [MockFunction], + "getFields": [MockFunction], + "getId": [MockFunction], + "getOwnField": [MockFunction], + "getParent": [MockFunction], + "getSearchRequestBody": [MockFunction], + "getSerializedFields": [MockFunction], + "history": Array [], + "onRequestStart": [MockFunction], + "removeField": [MockFunction], + "serialize": [MockFunction], + "setField": [MockFunction], + "setFields": [MockFunction], + "setOverwriteDataViewType": [MockFunction], + "setParent": [MockFunction], + "toExpressionAst": [MockFunction], + }, + "sharingSavedObjectProps": Object { + "aliasPurpose": undefined, + "aliasTargetId": undefined, + "errorJSON": undefined, + "outcome": "exactMatch", + }, + "sort": Array [ + Array [ + "order_date", + "desc", + ], + ], + "title": "test2", + "viewMode": undefined, + } + `); + }); }); diff --git a/src/plugins/discover/public/services/saved_searches/saved_searches_utils.test.ts b/src/plugins/discover/public/services/saved_searches/saved_searches_utils.test.ts index d63364ae6c3d2..39fd5442512d1 100644 --- a/src/plugins/discover/public/services/saved_searches/saved_searches_utils.test.ts +++ b/src/plugins/discover/public/services/saved_searches/saved_searches_utils.test.ts @@ -105,6 +105,7 @@ describe('saved_searches_utils', () => { description: 'description', grid: {}, hideChart: true, + isTextBasedQuery: true, }; expect(toSavedSearchAttributes(savedSearch, '{}')).toMatchInlineSnapshot(` @@ -117,7 +118,7 @@ describe('saved_searches_utils', () => { "grid": Object {}, "hideAggregatedPreview": undefined, "hideChart": true, - "isTextBasedQuery": false, + "isTextBasedQuery": true, "kibanaSavedObjectMeta": Object { "searchSourceJSON": "{}", }, diff --git a/src/plugins/discover/public/utils/get_text_based_language_mode.test.ts b/src/plugins/discover/public/utils/get_text_based_language_mode.test.ts new file mode 100644 index 0000000000000..35fea9fe3c064 --- /dev/null +++ b/src/plugins/discover/public/utils/get_text_based_language_mode.test.ts @@ -0,0 +1,22 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { getTextBasedLanguageMode } from './get_text_based_language_mode'; + +describe('getTextBasedLanguageMode', () => { + it('returns empty string for Query type query', () => { + const mode = getTextBasedLanguageMode({ query: '', language: 'lucene' }); + expect(mode).toEqual(''); + }); + + it('returns sql for Query type query', () => { + const mode = getTextBasedLanguageMode({ sql: 'SELECT * from foo' }); + + expect(mode).toEqual('sql'); + }); +}); From 0b99300ff6e08155498f70330b3fe56a8424f3c2 Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Mon, 4 Jul 2022 15:23:49 +0300 Subject: [PATCH 080/115] Dataview picker unit tests --- .../application/main/utils/fetch_sql.ts | 4 +- .../dataview_picker/change_dataview.test.tsx | 19 ++++- .../dataview_picker/change_dataview.tsx | 79 ++++++++++--------- .../dataview_picker/dataview_list.test.tsx | 6 ++ .../public/dataview_picker/dataview_list.tsx | 2 +- .../text_languages_list.test.tsx | 63 +++++++++++++++ 6 files changed, 130 insertions(+), 43 deletions(-) create mode 100644 src/plugins/unified_search/public/dataview_picker/text_languages_list.test.tsx diff --git a/src/plugins/discover/public/application/main/utils/fetch_sql.ts b/src/plugins/discover/public/application/main/utils/fetch_sql.ts index 627ea8f0117d4..bada02de40442 100644 --- a/src/plugins/discover/public/application/main/utils/fetch_sql.ts +++ b/src/plugins/discover/public/application/main/utils/fetch_sql.ts @@ -7,7 +7,7 @@ */ import { pluck } from 'rxjs/operators'; import { lastValueFrom } from 'rxjs'; -import { Query } from '@kbn/es-query'; +import { Query, AggregateQuery } from '@kbn/es-query'; import type { DataPublicPluginStart } from '@kbn/data-plugin/public'; import type { ExpressionsStart } from '@kbn/expressions-plugin/public'; import type { Datatable } from '@kbn/expressions-plugin/public'; @@ -23,7 +23,7 @@ interface SQLErrorResponse { } export function fetchSql( - query: Query, + query: Query | AggregateQuery, dataViewsService: DataViewsContract, data: DataPublicPluginStart, expressions: ExpressionsStart diff --git a/src/plugins/unified_search/public/dataview_picker/change_dataview.test.tsx b/src/plugins/unified_search/public/dataview_picker/change_dataview.test.tsx index ef1f6ff91d237..4b68f7c093d73 100644 --- a/src/plugins/unified_search/public/dataview_picker/change_dataview.test.tsx +++ b/src/plugins/unified_search/public/dataview_picker/change_dataview.test.tsx @@ -15,7 +15,7 @@ import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public'; import { dataPluginMock } from '@kbn/data-plugin/public/mocks'; import { ChangeDataView } from './change_dataview'; import { EuiTourStep } from '@elastic/eui'; -import type { DataViewPickerPropsExtended } from '.'; +import { DataViewPickerPropsExtended, TextBasedLanguages } from '.'; describe('DataView component', () => { const createMockWebStorage = () => ({ @@ -139,4 +139,21 @@ describe('DataView component', () => { component.find('[data-test-subj="dataview-create-new"]').first().simulate('click'); expect(addDataViewSpy).toHaveBeenCalled(); }); + + it('should render the text based languages panels if languages are given', async () => { + const component = mount( + wrapDataViewComponentInContext( + { + ...props, + showNewMenuTour: true, + textBasedLanguages: [TextBasedLanguages.ESQL, TextBasedLanguages.SQL], + textBasedLanguage: TextBasedLanguages.SQL, + }, + false + ) + ); + findTestSubject(component, 'dataview-trigger').simulate('click'); + const text = component.find('[data-test-subj="select-text-based-language-panel"]'); + expect(text.length).not.toBe(0); + }); }); diff --git a/src/plugins/unified_search/public/dataview_picker/change_dataview.tsx b/src/plugins/unified_search/public/dataview_picker/change_dataview.tsx index e77597ca74d5c..8f871f457b26b 100644 --- a/src/plugins/unified_search/public/dataview_picker/change_dataview.tsx +++ b/src/plugins/unified_search/public/dataview_picker/change_dataview.tsx @@ -264,48 +264,49 @@ export function ChangeDataView({ searchListInputId={searchListInputId} isTextBasedLangSelected={isTextBasedLangSelected} /> + + {textBasedLanguages?.length && ( + <> + + + + +
+ {i18n.translate( + 'unifiedSearch.query.queryBar.indexPattern.textBasedLanguagesLabel', + { + defaultMessage: 'Text-based query languages', + } + )} +
+
+
+
+ { + setTriggerLabel(lang); + setPopoverIsOpen(false); + setIsTextBasedLangSelected(true); + // also update the query with the sql query + onTextLangQuerySubmit?.({ sql: `SELECT * FROM "${trigger.title}"` }); + }} + /> + + )} ); - if (textBasedLanguages?.length) { - panelItems.push( - , - - - -
- {i18n.translate( - 'unifiedSearch.query.queryBar.indexPattern.textBasedLanguagesLabel', - { - defaultMessage: 'Text-based query languages', - } - )} -
-
-
-
, - { - setTriggerLabel(lang); - setPopoverIsOpen(false); - setIsTextBasedLangSelected(true); - // also update the query with the sql query - onTextLangQuerySubmit?.({ sql: `SELECT * FROM "${trigger.title}"` }); - }} - /> - ); - } - return panelItems; }; diff --git a/src/plugins/unified_search/public/dataview_picker/dataview_list.test.tsx b/src/plugins/unified_search/public/dataview_picker/dataview_list.test.tsx index 1eef27b72958a..fe6601b90d79c 100644 --- a/src/plugins/unified_search/public/dataview_picker/dataview_list.test.tsx +++ b/src/plugins/unified_search/public/dataview_picker/dataview_list.test.tsx @@ -70,4 +70,10 @@ describe('DataView list component', () => { 'dataview-2', ]); }); + + it('should render a warning icon if a text based language is selected', () => { + const component = shallow(); + + expect(getDataViewPickerOptions(component)!.map((option: any) => option.append)).not.toBeNull(); + }); }); diff --git a/src/plugins/unified_search/public/dataview_picker/dataview_list.tsx b/src/plugins/unified_search/public/dataview_picker/dataview_list.tsx index 8e4ddd5d78d0a..9aa9742e2b1c3 100644 --- a/src/plugins/unified_search/public/dataview_picker/dataview_list.tsx +++ b/src/plugins/unified_search/public/dataview_picker/dataview_list.tsx @@ -56,7 +56,7 @@ export function DataViewsList({ } )} > - + ) : null, }))} diff --git a/src/plugins/unified_search/public/dataview_picker/text_languages_list.test.tsx b/src/plugins/unified_search/public/dataview_picker/text_languages_list.test.tsx new file mode 100644 index 0000000000000..21b77534f90a4 --- /dev/null +++ b/src/plugins/unified_search/public/dataview_picker/text_languages_list.test.tsx @@ -0,0 +1,63 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import React, { MouseEvent } from 'react'; +import { EuiSelectable } from '@elastic/eui'; +import { act } from 'react-dom/test-utils'; +import { ShallowWrapper } from 'enzyme'; +import { shallowWithIntl as shallow } from '@kbn/test-jest-helpers'; +import TextBasedLanguagesList, { TextBasedLanguagesListProps } from './text_languages_list'; +import { TextBasedLanguages } from '.'; + +function getTextLanguagesPickerList(instance: ShallowWrapper) { + return instance.find(EuiSelectable).first(); +} + +function getTextLanguagesPickerOptions(instance: ShallowWrapper) { + return getTextLanguagesPickerList(instance).prop('options'); +} + +function selectTextLanguagePickerOption(instance: ShallowWrapper, selectedLabel: string) { + const event = {} as MouseEvent; + const options: Array<{ label: string; checked?: 'on' | 'off' }> = getTextLanguagesPickerOptions( + instance + ).map((option: { label: string }) => + option.label === selectedLabel + ? { ...option, checked: 'on' } + : { ...option, checked: undefined } + ); + return getTextLanguagesPickerList(instance).prop('onChange')!(options, event); +} + +describe('Text based languages list component', () => { + const changeLanguageSpy = jest.fn(); + let props: TextBasedLanguagesListProps; + beforeEach(() => { + props = { + selectedOption: 'ESQL', + onChange: changeLanguageSpy, + textBasedLanguages: [TextBasedLanguages.ESQL, TextBasedLanguages.SQL], + }; + }); + it('should trigger the onChange if a new language is selected', async () => { + const component = shallow(); + await act(async () => { + selectTextLanguagePickerOption(component, 'SQL'); + }); + expect(changeLanguageSpy).toHaveBeenCalled(); + }); + + it('should list all languages', () => { + const component = shallow(); + + expect(getTextLanguagesPickerOptions(component)!.map((option: any) => option.label)).toEqual([ + 'ESQL', + 'SQL', + ]); + }); +}); From 318bc9201d35376e200357c7c44aae0c18e64f79 Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Tue, 5 Jul 2022 09:07:43 +0300 Subject: [PATCH 081/115] Fix a bug on the dataview picker --- .../dataview_picker/change_dataview.tsx | 81 ++++++++++--------- 1 file changed, 41 insertions(+), 40 deletions(-) diff --git a/src/plugins/unified_search/public/dataview_picker/change_dataview.tsx b/src/plugins/unified_search/public/dataview_picker/change_dataview.tsx index 8f871f457b26b..e14d0256138e1 100644 --- a/src/plugins/unified_search/public/dataview_picker/change_dataview.tsx +++ b/src/plugins/unified_search/public/dataview_picker/change_dataview.tsx @@ -264,49 +264,50 @@ export function ChangeDataView({ searchListInputId={searchListInputId} isTextBasedLangSelected={isTextBasedLangSelected} /> - - {textBasedLanguages?.length && ( - <> - - - - -
- {i18n.translate( - 'unifiedSearch.query.queryBar.indexPattern.textBasedLanguagesLabel', - { - defaultMessage: 'Text-based query languages', - } - )} -
-
-
-
- { - setTriggerLabel(lang); - setPopoverIsOpen(false); - setIsTextBasedLangSelected(true); - // also update the query with the sql query - onTextLangQuerySubmit?.({ sql: `SELECT * FROM "${trigger.title}"` }); - }} - /> - - )} ); + if (textBasedLanguages?.length) { + panelItems.push( + , + + , + + +
+ {i18n.translate( + 'unifiedSearch.query.queryBar.indexPattern.textBasedLanguagesLabel', + { + defaultMessage: 'Text-based query languages', + } + )} +
+
+
+
, + { + setTriggerLabel(lang); + setPopoverIsOpen(false); + setIsTextBasedLangSelected(true); + // also update the query with the sql query + onTextLangQuerySubmit?.({ sql: `SELECT * FROM "${trigger.title}"` }); + }} + /> + ); + } + return panelItems; }; From aad76c3856555223ecb0c4252524daa54209f42b Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Tue, 5 Jul 2022 13:02:46 +0300 Subject: [PATCH 082/115] Add unit tests for the editor --- .../dataview_picker/change_dataview.tsx | 1 - .../query_bar_top_row.test.tsx | 23 ++- .../editor_footer.tsx | 3 +- .../helpers.test.ts | 105 ++++++++++++ .../text_based_languages_editor/helpers.ts | 11 +- .../text_based_languages_editor/index.tsx | 17 +- .../text_based_languages_editor.test.tsx | 153 ++++++++++++++++++ .../public/search_bar/search_bar.test.tsx | 18 +++ 8 files changed, 320 insertions(+), 11 deletions(-) create mode 100644 src/plugins/unified_search/public/query_string_input/text_based_languages_editor/helpers.test.ts create mode 100644 src/plugins/unified_search/public/query_string_input/text_based_languages_editor/text_based_languages_editor.test.tsx diff --git a/src/plugins/unified_search/public/dataview_picker/change_dataview.tsx b/src/plugins/unified_search/public/dataview_picker/change_dataview.tsx index e14d0256138e1..dd4b1f5a15694 100644 --- a/src/plugins/unified_search/public/dataview_picker/change_dataview.tsx +++ b/src/plugins/unified_search/public/dataview_picker/change_dataview.tsx @@ -280,7 +280,6 @@ export function ChangeDataView({ margin-bottom: 0; `} > - ,
diff --git a/src/plugins/unified_search/public/query_string_input/query_bar_top_row.test.tsx b/src/plugins/unified_search/public/query_string_input/query_bar_top_row.test.tsx index 189f12765ad15..fd1ea2e9bce78 100644 --- a/src/plugins/unified_search/public/query_string_input/query_bar_top_row.test.tsx +++ b/src/plugins/unified_search/public/query_string_input/query_bar_top_row.test.tsx @@ -9,7 +9,7 @@ import { mockPersistedLogFactory } from './query_string_input.test.mocks'; import React from 'react'; -import { mount } from 'enzyme'; +import { mount, shallow } from 'enzyme'; import { render } from '@testing-library/react'; import { EMPTY } from 'rxjs'; @@ -65,6 +65,10 @@ const kqlQuery = { language: 'kuery', }; +const sqlQuery = { + sql: 'SELECT * FROM test', +}; + const createMockWebStorage = () => ({ clear: jest.fn(), getItem: jest.fn(), @@ -257,4 +261,21 @@ describe('QueryBarTopRowTopRow', () => { expect(component.find(QUERY_INPUT_SELECTOR).length).toBe(0); expect(component.find(TIMEPICKER_SELECTOR).length).toBe(0); }); + + it('Should NOT render query input bar if on text based languages mode', () => { + const component = shallow( + wrapQueryBarTopRowInContext({ + query: sqlQuery, + isDirty: false, + screenTitle: 'SQL Screen', + timeHistory: mockTimeHistory, + indexPatterns: [stubIndexPattern], + showDatePicker: false, + dateRangeFrom: 'now-7d', + dateRangeTo: 'now', + }) + ); + + expect(component.find(QUERY_INPUT_SELECTOR).length).toBe(0); + }); }); diff --git a/src/plugins/unified_search/public/query_string_input/text_based_languages_editor/editor_footer.tsx b/src/plugins/unified_search/public/query_string_input/text_based_languages_editor/editor_footer.tsx index 4cdb659781d9d..7b935bb0225a6 100644 --- a/src/plugins/unified_search/public/query_string_input/text_based_languages_editor/editor_footer.tsx +++ b/src/plugins/unified_search/public/query_string_input/text_based_languages_editor/editor_footer.tsx @@ -41,13 +41,14 @@ export const EditorFooter = memo(function EditorFooter({ - +

{i18n.translate('unifiedSearch.query.textBasedLanguagesEditor.lineCount', { defaultMessage: '{count} {count, plural, one {line} other {lines}}', diff --git a/src/plugins/unified_search/public/query_string_input/text_based_languages_editor/helpers.test.ts b/src/plugins/unified_search/public/query_string_input/text_based_languages_editor/helpers.test.ts new file mode 100644 index 0000000000000..372af2898f8de --- /dev/null +++ b/src/plugins/unified_search/public/query_string_input/text_based_languages_editor/helpers.test.ts @@ -0,0 +1,105 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { parseErrors } from './helpers'; + +describe('helpers', function () { + describe('parseErrors', function () { + it('should return the correct error object from SQL ES response for an one liner query', function () { + const error = new Error( + '[essql] > Unexpected error from Elasticsearch: verification_exception - Found 1 problem\nline 1:8: Unknown column [miaou]' + ); + const errors = [error]; + expect(parseErrors(errors, 'SELECT miaou from test')).toEqual([ + { + endColumn: 13, + endLineNumber: 1, + message: ' Unknown column [miaou]', + severity: 8, + startColumn: 8, + startLineNumber: 1, + }, + ]); + }); + + it('should return the correct error object from SQL ES response for an multi liner query', function () { + const error = new Error( + '[essql] > Unexpected error from Elasticsearch: verification_exception - Found 1 problem line 3:7: Condition expression needs to be boolean, found [TEXT]' + ); + const errors = [error]; + expect( + parseErrors( + errors, + `SELECT * + FROM "kibana_sample_data_ecommerce" + WHERE category` + ) + ).toEqual([ + { + endColumn: 11, + endLineNumber: 3, + message: ' Condition expression needs to be boolean, found [TEXT]', + severity: 8, + startColumn: 7, + startLineNumber: 3, + }, + ]); + }); + + it('should return the correct error object if dataview not found for an one liner query', function () { + const error = new Error('No data view found for index pattern kibana_sample_data_ecommerce1'); + const errors = [error]; + expect(parseErrors(errors, `SELECT * FROM "kibana_sample_data_ecommerce1"`)).toEqual([ + { + endColumn: 44, + endLineNumber: 1, + message: 'No data view found for index pattern kibana_sample_data_ecommerce1', + severity: 8, + startColumn: 10, + startLineNumber: 1, + }, + ]); + }); + + it('should return the correct error object if dataview not found for a multiline query', function () { + const error = new Error('No data view found for index pattern kibana_sample_data_ecommerce1'); + const errors = [error]; + expect( + parseErrors( + errors, + `SELECT * + from "kibana_sample_data_ecommerce1"` + ) + ).toEqual([ + { + endColumn: 39, + endLineNumber: 2, + message: 'No data view found for index pattern kibana_sample_data_ecommerce1', + severity: 8, + startColumn: 5, + startLineNumber: 2, + }, + ]); + }); + + it('should return the generic error object for an error of unknown format', function () { + const error = new Error('I am an unknown error'); + const errors = [error]; + expect(parseErrors(errors, `SELECT * FROM "kibana_sample_data_ecommerce"`)).toEqual([ + { + endColumn: 10, + endLineNumber: 1, + message: 'I am an unknown error', + severity: 8, + startColumn: 1, + startLineNumber: 1, + }, + ]); + }); + }); +}); diff --git a/src/plugins/unified_search/public/query_string_input/text_based_languages_editor/helpers.ts b/src/plugins/unified_search/public/query_string_input/text_based_languages_editor/helpers.ts index d6bb55b0c509a..f83b8a799f994 100644 --- a/src/plugins/unified_search/public/query_string_input/text_based_languages_editor/helpers.ts +++ b/src/plugins/unified_search/public/query_string_input/text_based_languages_editor/helpers.ts @@ -64,25 +64,26 @@ export const parseErrors = (errors: Error[], code: string) => { let indexWithError = 1; let lineWithError = ''; linesText.forEach((line, index) => { - if (line.includes('FROM')) { + if (line.includes('FROM') || line.includes('from')) { indexWithError = index + 1; lineWithError = line; } }); + const lineWithErrorUpperCase = lineWithError.toUpperCase(); return { message: error.message, - startColumn: lineWithError.indexOf('FROM') + 1, + startColumn: lineWithErrorUpperCase.indexOf('FROM') + 1, startLineNumber: indexWithError, - endColumn: lineWithError.indexOf('FROM') + 1 + errorLength, + endColumn: lineWithErrorUpperCase.indexOf('FROM') + 1 + errorLength, endLineNumber: indexWithError, severity: monaco.MarkerSeverity.Error, }; } else { return { message: error.message, - startColumn: code.indexOf('FROM') + 1, + startColumn: code.toUpperCase().indexOf('FROM') + 1, startLineNumber: 1, - endColumn: code.indexOf('FROM') + 1 + errorLength, + endColumn: code.toUpperCase().indexOf('FROM') + 1 + errorLength, endLineNumber: 1, severity: monaco.MarkerSeverity.Error, }; diff --git a/src/plugins/unified_search/public/query_string_input/text_based_languages_editor/index.tsx b/src/plugins/unified_search/public/query_string_input/text_based_languages_editor/index.tsx index effedca110548..732554f29983a 100644 --- a/src/plugins/unified_search/public/query_string_input/text_based_languages_editor/index.tsx +++ b/src/plugins/unified_search/public/query_string_input/text_based_languages_editor/index.tsx @@ -36,7 +36,7 @@ import { MemoizedDocumentation, DocumentationSections } from './documentation'; import { useDebounceWithOptions, parseErrors, getDocumentationSections } from './helpers'; import { EditorFooter } from './editor_footer'; -interface TextBasedLanguagesEditorProps { +export interface TextBasedLanguagesEditorProps { query: AggregateQuery; onTextLangQueryChange: (query: AggregateQuery) => void; onTextLangQuerySubmit: () => void; @@ -312,6 +312,7 @@ export const TextBasedLanguagesEditor = memo(function TextBasedLanguagesEditor({ iconType={isWordWrapped ? 'wordWrap' : 'wordWrapDisabled'} display={!isWordWrapped ? 'fill' : undefined} color="text" + data-test-subj="unifiedTextLangEditor-toggleWordWrap" aria-label={ isWordWrapped ? i18n.translate( @@ -348,6 +349,7 @@ export const TextBasedLanguagesEditor = memo(function TextBasedLanguagesEditor({ defaultMessage: 'Minimize editor', } )} + data-test-subj="unifiedTextLangEditor-minimize" onClick={() => { expandCodeEditor(false); updateLinesFromModel = false; @@ -365,6 +367,7 @@ export const TextBasedLanguagesEditor = memo(function TextBasedLanguagesEditor({

- +
{!isCompactFocused && ( - + {i18n.translate('unifiedSearch.query.textBasedLanguagesEditor.lineCount', { defaultMessage: '{count} {count, plural, one {line} other {lines}}', values: { count: lines }, @@ -407,6 +414,7 @@ export const TextBasedLanguagesEditor = memo(function TextBasedLanguagesEditor({ css={styles.errorsBadge} iconType="crossInACircleFilled" iconSide="left" + data-test-subj="unifiedTextLangEditor-inline-errors-badge" > {errors.length} @@ -451,6 +459,7 @@ export const TextBasedLanguagesEditor = memo(function TextBasedLanguagesEditor({ size="m" aria-label="Expand" onClick={() => expandCodeEditor(true)} + data-test-subj="unifiedTextLangEditor-expand" css={{ borderRadius: 0 }} /> @@ -467,6 +476,7 @@ export const TextBasedLanguagesEditor = memo(function TextBasedLanguagesEditor({ iconType="documentation" size="m" aria-label="Documentation" + data-test-subj="unifiedTextLangEditor-inline-documentation" onClick={() => setIsHelpOpen(true)} css={{ borderTopLeftRadius: 0, borderBottomLeftRadius: 0 }} /> @@ -489,6 +499,7 @@ export const TextBasedLanguagesEditor = memo(function TextBasedLanguagesEditor({ color="primary" iconType="grab" aria-label="Resize editor" + data-test-subj="unifiedTextLangEditor-resize" css={styles.dragResizeButton} /> )} diff --git a/src/plugins/unified_search/public/query_string_input/text_based_languages_editor/text_based_languages_editor.test.tsx b/src/plugins/unified_search/public/query_string_input/text_based_languages_editor/text_based_languages_editor.test.tsx new file mode 100644 index 0000000000000..8c0b4b9a4b8a1 --- /dev/null +++ b/src/plugins/unified_search/public/query_string_input/text_based_languages_editor/text_based_languages_editor.test.tsx @@ -0,0 +1,153 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import React from 'react'; +import { act } from 'react-dom/test-utils'; +import { IUiSettingsClient } from '@kbn/core/public'; +import { mountWithIntl as mount } from '@kbn/test-jest-helpers'; +import { findTestSubject } from '@elastic/eui/lib/test'; +import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public'; +import { TextBasedLanguagesEditor, TextBasedLanguagesEditorProps } from '.'; + +describe('TextBasedLanguagesEditor', () => { + const uiConfig: Record = {}; + const uiSettings = { + get: (key: string) => uiConfig[key], + } as IUiSettingsClient; + + const services = { + uiSettings, + }; + + function renderTextBasedLanguagesEditorComponent(testProps: TextBasedLanguagesEditorProps) { + return ( + + + + ); + } + let props: TextBasedLanguagesEditorProps; + beforeEach(() => { + props = { + query: { sql: 'SELECT * FROM test' }, + isCodeEditorExpanded: false, + onTextLangQueryChange: jest.fn(), + onTextLangQuerySubmit: jest.fn(), + expandCodeEditor: jest.fn(), + }; + }); + it('should render the editor component', async () => { + await act(async () => { + const component = mount(renderTextBasedLanguagesEditorComponent({ ...props })); + expect(component.find('[data-test-subj="unifiedTextLangEditor"]').length).not.toBe(0); + }); + }); + + it('should render the lines badge for the inline mode by default', async () => { + await act(async () => { + const component = mount(renderTextBasedLanguagesEditorComponent({ ...props })); + expect( + component.find('[data-test-subj="unifiedTextLangEditor-inline-lines-badge"]').length + ).not.toBe(0); + }); + }); + + it('should render the errors badge for the inline mode by default if errors are provides', async () => { + const newProps = { + ...props, + errors: [new Error('error1')], + }; + await act(async () => { + const component = mount(renderTextBasedLanguagesEditorComponent({ ...newProps })); + expect( + component.find('[data-test-subj="unifiedTextLangEditor-inline-errors-badge"]').length + ).not.toBe(0); + }); + }); + + it('should render the correct buttons for the inline code editor mode', async () => { + await act(async () => { + const component = mount(renderTextBasedLanguagesEditorComponent({ ...props })); + expect(component.find('[data-test-subj="unifiedTextLangEditor-expand"]').length).not.toBe(0); + expect( + component.find('[data-test-subj="unifiedTextLangEditor-inline-documentation"]').length + ).not.toBe(0); + }); + }); + + it('should call the expand editor function when expand button is clicked', async () => { + const expandCodeEditorSpy = jest.fn(); + const newProps = { + ...props, + expandCodeEditor: expandCodeEditorSpy, + }; + await act(async () => { + const component = mount(renderTextBasedLanguagesEditorComponent({ ...newProps })); + findTestSubject(component, 'unifiedTextLangEditor-expand').simulate('click'); + expect(expandCodeEditorSpy).toHaveBeenCalled(); + }); + }); + + it('should render the correct buttons for the expanded code editor mode', async () => { + const newProps = { + ...props, + isCodeEditorExpanded: true, + }; + await act(async () => { + const component = mount(renderTextBasedLanguagesEditorComponent({ ...newProps })); + expect( + component.find('[data-test-subj="unifiedTextLangEditor-toggleWordWrap"]').length + ).not.toBe(0); + expect(component.find('[data-test-subj="unifiedTextLangEditor-minimize"]').length).not.toBe( + 0 + ); + expect( + component.find('[data-test-subj="unifiedTextLangEditor-documentation"]').length + ).not.toBe(0); + }); + }); + + it('should call the expand editor function when minimize button is clicked', async () => { + const expandCodeEditorSpy = jest.fn(); + const newProps = { + ...props, + isCodeEditorExpanded: true, + expandCodeEditor: expandCodeEditorSpy, + }; + await act(async () => { + const component = mount(renderTextBasedLanguagesEditorComponent({ ...newProps })); + findTestSubject(component, 'unifiedTextLangEditor-minimize').simulate('click'); + expect(expandCodeEditorSpy).toHaveBeenCalled(); + }); + }); + + it('should render the resize for the expanded code editor mode', async () => { + const newProps = { + ...props, + isCodeEditorExpanded: true, + }; + await act(async () => { + const component = mount(renderTextBasedLanguagesEditorComponent({ ...newProps })); + expect(component.find('[data-test-subj="unifiedTextLangEditor-resize"]').length).not.toBe(0); + }); + }); + + it('should render the footer for the expanded code editor mode', async () => { + const newProps = { + ...props, + isCodeEditorExpanded: true, + }; + await act(async () => { + const component = mount(renderTextBasedLanguagesEditorComponent({ ...newProps })); + expect(component.find('[data-test-subj="unifiedTextLangEditor-footer"]').length).not.toBe(0); + expect( + component.find('[data-test-subj="unifiedTextLangEditor-footer-lines"]').at(0).text() + ).toBe('1 line'); + }); + }); +}); diff --git a/src/plugins/unified_search/public/search_bar/search_bar.test.tsx b/src/plugins/unified_search/public/search_bar/search_bar.test.tsx index fe5e03ab7fb37..2b7eaff8da1a1 100644 --- a/src/plugins/unified_search/public/search_bar/search_bar.test.tsx +++ b/src/plugins/unified_search/public/search_bar/search_bar.test.tsx @@ -70,6 +70,10 @@ const kqlQuery = { language: 'kuery', }; +const sqlQuery = { + sql: 'SELECT * from test', +}; + function wrapSearchBarInContext(testProps: any) { const defaultOptions = { appName: 'test', @@ -124,6 +128,7 @@ describe('SearchBar', () => { const FILTER_BAR = '[data-test-subj="unifiedFilterBar"]'; const QUERY_BAR = '.kbnQueryBar'; const QUERY_INPUT = '[data-test-subj="unifiedQueryInput"]'; + const EDITOR = '[data-test-subj="unifiedTextLandEditor"]'; beforeEach(() => { jest.clearAllMocks(); @@ -224,4 +229,17 @@ describe('SearchBar', () => { expect(component.find(QUERY_BAR).length).toBeTruthy(); expect(component.find(QUERY_INPUT).length).toBeTruthy(); }); + + it('Should NOT render the input query input, for sql query', () => { + const component = mount( + wrapSearchBarInContext({ + indexPatterns: [mockIndexPattern], + screenTitle: 'test screen', + onQuerySubmit: noop, + query: sqlQuery, + }) + ); + expect(component.find(QUERY_INPUT).length).toBeFalsy(); + expect(component.find(EDITOR).length).toBeTruthy(); + }); }); From 1a6ec3c24ccd30cfe855eb4fee8e47ffdd60883c Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Tue, 5 Jul 2022 14:12:35 +0300 Subject: [PATCH 083/115] Fix jest test --- .../unified_search/public/search_bar/search_bar.test.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/unified_search/public/search_bar/search_bar.test.tsx b/src/plugins/unified_search/public/search_bar/search_bar.test.tsx index 2b7eaff8da1a1..7e1ad2bba21c8 100644 --- a/src/plugins/unified_search/public/search_bar/search_bar.test.tsx +++ b/src/plugins/unified_search/public/search_bar/search_bar.test.tsx @@ -128,7 +128,7 @@ describe('SearchBar', () => { const FILTER_BAR = '[data-test-subj="unifiedFilterBar"]'; const QUERY_BAR = '.kbnQueryBar'; const QUERY_INPUT = '[data-test-subj="unifiedQueryInput"]'; - const EDITOR = '[data-test-subj="unifiedTextLandEditor"]'; + const EDITOR = '[data-test-subj="unifiedTextLangEditor"]'; beforeEach(() => { jest.clearAllMocks(); From 331f92a1ac7796d83eea870dc0d4fd72027bfeae Mon Sep 17 00:00:00 2001 From: Dzmitry Tamashevich Date: Tue, 5 Jul 2022 17:20:44 +0300 Subject: [PATCH 084/115] [Discover] apply suggestions --- .../components/layout/discover_documents.tsx | 9 +- .../components/layout/discover_layout.tsx | 327 +++++++++--------- .../layout/discover_layout_context.tsx | 40 --- .../components/sidebar/discover_field.tsx | 13 +- .../sidebar/discover_field_search.tsx | 158 +++++---- .../components/sidebar/discover_sidebar.tsx | 26 +- .../sidebar/discover_sidebar_responsive.tsx | 4 +- .../components/top_nav/discover_topnav.tsx | 10 +- .../is_plain_record.ts} | 6 +- .../discover_grid/discover_grid.tsx | 9 +- .../discover_tour/discover_tour_provider.tsx | 12 +- .../dataview_picker/change_dataview.tsx | 11 +- .../public/dataview_picker/index.tsx | 10 +- 13 files changed, 299 insertions(+), 336 deletions(-) delete mode 100644 src/plugins/discover/public/application/main/components/layout/discover_layout_context.tsx rename src/plugins/discover/public/application/main/{hooks/use_discover_layout_context.ts => utils/is_plain_record.ts} (63%) diff --git a/src/plugins/discover/public/application/main/components/layout/discover_documents.tsx b/src/plugins/discover/public/application/main/components/layout/discover_documents.tsx index d0744b495d284..bd13c7d50b496 100644 --- a/src/plugins/discover/public/application/main/components/layout/discover_documents.tsx +++ b/src/plugins/discover/public/application/main/components/layout/discover_documents.tsx @@ -5,7 +5,7 @@ * in compliance with, at your election, the Elastic License 2.0 or the Server * Side Public License, v 1. */ -import React, { memo, useCallback, useContext, useMemo } from 'react'; +import React, { memo, useCallback, useMemo } from 'react'; import { EuiFlexItem, EuiLoadingSpinner, @@ -37,7 +37,7 @@ import { DocumentExplorerCallout } from '../document_explorer_callout'; import { DocumentExplorerUpdateCallout } from '../document_explorer_callout/document_explorer_update_callout'; import { DiscoverTourProvider } from '../../../../components/discover_tour'; import { DataTableRecord } from '../../../../types'; -import { DiscoverLayoutContext } from './discover_layout_context'; +import { isPlainRecordType } from '../../utils/is_plain_record'; const DocTableInfiniteMemoized = React.memo(DocTableInfinite); const DataGridMemoized = React.memo(DiscoverGrid); @@ -70,7 +70,7 @@ function DiscoverDocumentsComponent({ const documentState: DataDocumentsMsg = useDataState(documents$); const isLoading = documentState.fetchStatus === FetchStatus.LOADING; - const { isPlainRecord } = useContext(DiscoverLayoutContext); + const isPlainRecord = isPlainRecordType(documentState.recordRawType); const rows = useMemo(() => documentState.result || [], [documentState.result]); @@ -165,7 +165,7 @@ function DiscoverDocumentsComponent({ {!isLegacy && ( <> {!hideAnnouncements && ( - + )} @@ -194,6 +194,7 @@ function DiscoverDocumentsComponent({ rowHeightState={state.rowHeight} onUpdateRowHeight={onUpdateRowHeight} isSortEnabled={!isPlainRecord} + isPlainRecord={isPlainRecord} />
diff --git a/src/plugins/discover/public/application/main/components/layout/discover_layout.tsx b/src/plugins/discover/public/application/main/components/layout/discover_layout.tsx index 661d9e31dfa4f..f9a05024180e4 100644 --- a/src/plugins/discover/public/application/main/components/layout/discover_layout.tsx +++ b/src/plugins/discover/public/application/main/components/layout/discover_layout.tsx @@ -46,7 +46,6 @@ import { FieldStatisticsTable } from '../field_stats_table'; import { VIEW_MODE } from '../../../../components/view_mode_toggle'; import { DOCUMENTS_VIEW_CLICK, FIELD_STATISTICS_VIEW_CLICK } from '../field_stats_table/constants'; import { hasActiveFilter } from './utils'; -import { DiscoverLayoutContextProvider } from './discover_layout_context'; /** * Local storage key for sidebar persistence state @@ -216,173 +215,171 @@ export function DiscoverLayout({ }, [dataState.error, isPlainRecord]); return ( - - -

- {savedSearch.title - ? i18n.translate('discover.pageTitleWithSavedSearch', { - defaultMessage: 'Discover - {savedSearchTitle}', - values: { - savedSearchTitle: savedSearch.title, - }, - }) - : i18n.translate('discover.pageTitleWithoutSavedSearch', { - defaultMessage: 'Discover - Search not yet saved', - })} -

- +

+ {savedSearch.title + ? i18n.translate('discover.pageTitleWithSavedSearch', { + defaultMessage: 'Discover - {savedSearchTitle}', + values: { + savedSearchTitle: savedSearch.title, + }, + }) + : i18n.translate('discover.pageTitleWithoutSavedSearch', { + defaultMessage: 'Discover - Search not yet saved', + })} +

+ + + - - - + + + + + - - - - -
- - -
-
-
- - - {resultState === 'none' && ( - - )} - {resultState === 'uninitialized' && ( - savedSearchRefetch$.next(undefined)} /> - )} - {resultState === 'loading' && } - {resultState === 'ready' && ( - - {!isPlainRecord && ( - <> - - - - - - )} - - {viewMode === VIEW_MODE.DOCUMENT_LEVEL ? ( - - ) : ( - - )} - - )} - +
+ + +
-
-
-
-
+ + + + {resultState === 'none' && ( + + )} + {resultState === 'uninitialized' && ( + savedSearchRefetch$.next(undefined)} /> + )} + {resultState === 'loading' && } + {resultState === 'ready' && ( + + {!isPlainRecord && ( + <> + + + + + + )} + {viewMode === VIEW_MODE.DOCUMENT_LEVEL ? ( + + ) : ( + + )} + + )} + + + + + ); } diff --git a/src/plugins/discover/public/application/main/components/layout/discover_layout_context.tsx b/src/plugins/discover/public/application/main/components/layout/discover_layout_context.tsx deleted file mode 100644 index 09d41db732f43..0000000000000 --- a/src/plugins/discover/public/application/main/components/layout/discover_layout_context.tsx +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -import React, { ReactNode, useMemo, useState } from 'react'; - -export interface DiscoverLayoutContextProps { - isPlainRecord: boolean; - setIsPlainRecord: (newIsPlainRecord: boolean) => void; -} - -const defaultContext = {}; - -export const DiscoverLayoutContext = React.createContext( - defaultContext as DiscoverLayoutContextProps -); - -export const DiscoverLayoutContextProvider = ({ - initialIsPlainRecord, - children, -}: { - initialIsPlainRecord: boolean; - children: ReactNode; -}) => { - const [isPlainRecord, setIsPlainRecord] = useState(initialIsPlainRecord); - - const value = useMemo( - () => ({ - isPlainRecord, - setIsPlainRecord, - }), - [isPlainRecord] - ); - - return {children}; -}; diff --git a/src/plugins/discover/public/application/main/components/sidebar/discover_field.tsx b/src/plugins/discover/public/application/main/components/sidebar/discover_field.tsx index 7bda9268b8621..63a710e9b60d3 100644 --- a/src/plugins/discover/public/application/main/components/sidebar/discover_field.tsx +++ b/src/plugins/discover/public/application/main/components/sidebar/discover_field.tsx @@ -166,10 +166,11 @@ interface MultiFieldsProps { multiFields: NonNullable; toggleDisplay: (field: DataViewField) => void; alwaysShowActionButton: boolean; + isPlainRecord: boolean; } const MultiFields: React.FC = memo( - ({ multiFields, toggleDisplay, alwaysShowActionButton }) => ( + ({ multiFields, toggleDisplay, alwaysShowActionButton, isPlainRecord }) => (
@@ -185,7 +186,7 @@ const MultiFields: React.FC = memo( className="dscSidebarItem dscSidebarItem--multi" isActive={false} dataTestSubj={`field-${entry.field.name}-showDetails`} - fieldIcon={} + fieldIcon={isPlainRecord && } fieldAction={ { @@ -303,7 +305,7 @@ function DiscoverFieldComponent({ size="s" className="dscSidebarItem" dataTestSubj={`field-${field.name}-showDetails`} - fieldIcon={} + fieldIcon={isPlainRecord && } fieldAction={ } + fieldIcon={isPlainRecord && } fieldAction={ } /> ); - if (!onAddFilter) { + if (!isPlainRecord) { return button; } @@ -422,6 +424,7 @@ function DiscoverFieldComponent({ multiFields={multiFields} alwaysShowActionButton={alwaysShowActionButton} toggleDisplay={toggleDisplay} + isPlainRecord={isPlainRecord} /> )} diff --git a/src/plugins/discover/public/application/main/components/sidebar/discover_field_search.tsx b/src/plugins/discover/public/application/main/components/sidebar/discover_field_search.tsx index 0c0e88c8ca424..45268cf84c788 100644 --- a/src/plugins/discover/public/application/main/components/sidebar/discover_field_search.tsx +++ b/src/plugins/discover/public/application/main/components/sidebar/discover_field_search.tsx @@ -64,6 +64,10 @@ export interface Props { * the input value of the user */ value?: string; + /** + * is text base lang mode + */ + isPlainRecord: boolean; } interface FieldTypeTableItem { @@ -76,7 +80,13 @@ interface FieldTypeTableItem { * Component is Discover's side bar to search of available fields * Additionally there's a button displayed that allows the user to show/hide more filter fields */ -export function DiscoverFieldSearch({ onChange, value, types, presentFieldTypes }: Props) { +export function DiscoverFieldSearch({ + onChange, + value, + types, + presentFieldTypes, + isPlainRecord, +}: Props) { const searchPlaceholder = i18n.translate('discover.fieldChooser.searchPlaceHolder', { defaultMessage: 'Search field names', }); @@ -353,81 +363,83 @@ export function DiscoverFieldSearch({ onChange, value, types, presentFieldTypes - - - { - setPopoverOpen(false); - }} - button={buttonContent} - > - - {i18n.translate('discover.fieldChooser.filter.filterByTypeLabel', { - defaultMessage: 'Filter by type', - })} - - {selectionPanel} - {footer()} - - - - {i18n.translate('discover.fieldChooser.popoverTitle', { - defaultMessage: 'Field types', - })} - - + + { + setPopoverOpen(false); + }} + button={buttonContent} > - + {i18n.translate('discover.fieldChooser.filter.filterByTypeLabel', { + defaultMessage: 'Filter by type', })} - items={items} - compressed={true} - rowHeader="firstName" - columns={columnsSidebar} - responsive={false} - /> - - - -

- {i18n.translate('discover.fieldTypesPopover.learnMoreText', { - defaultMessage: 'Learn more about', + + {selectionPanel} + {footer()} + + + + {i18n.translate('discover.fieldChooser.popoverTitle', { + defaultMessage: 'Field types', + })} + + + - - -

-
-
-
-
-
+ items={items} + compressed={true} + rowHeader="firstName" + columns={columnsSidebar} + responsive={false} + /> + + + +

+ {i18n.translate('discover.fieldTypesPopover.learnMoreText', { + defaultMessage: 'Learn more about', + })} +   + + + +

+
+
+ + + + )} ); } diff --git a/src/plugins/discover/public/application/main/components/sidebar/discover_sidebar.tsx b/src/plugins/discover/public/application/main/components/sidebar/discover_sidebar.tsx index f7fdb09d4c6b8..e1cc78db1ed5c 100644 --- a/src/plugins/discover/public/application/main/components/sidebar/discover_sidebar.tsx +++ b/src/plugins/discover/public/application/main/components/sidebar/discover_sidebar.tsx @@ -125,6 +125,7 @@ export function DiscoverSidebarComponent({ const [fieldsToRender, setFieldsToRender] = useState(FIELDS_PER_PAGE); const [fieldsPerPage, setFieldsPerPage] = useState(FIELDS_PER_PAGE); const availableFieldsContainer = useRef(null); + const isPlainRecord = !!onAddFilter; useEffect(() => { if (documents) { @@ -335,18 +336,17 @@ export function DiscoverSidebarComponent({ }} /> )} - {onAddFilter && ( - -
- - -
- )} + +
+ + +
{ @@ -435,7 +435,7 @@ export function DiscoverSidebarComponent({ } > - {popularFields.length > 0 && ( + {isPlainRecord && popularFields.length > 0 && ( <>