From f08fdc68852c14bfc66329ba00d4f0bfe55a480d Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Mon, 4 Nov 2024 15:40:39 +0000 Subject: [PATCH] [Discover] Query editor state sync fixes (#8803) * mosty working * fix reference issue * Changeset file for PR #8803 created/updated * Apply suggestions from code review Co-authored-by: Kawika Avilla Signed-off-by: Ashwin P Chandran --------- Signed-off-by: Ashwin P Chandran Co-authored-by: opensearch-changeset-bot[bot] <154024398+opensearch-changeset-bot[bot]@users.noreply.github.com> Co-authored-by: Kawika Avilla (cherry picked from commit 358d82379c063e89cb78543b0c8530203a7dbead) Signed-off-by: github-actions[bot] --- changelogs/fragments/8803.yml | 2 + .../public/ui/query_editor/query_editor.tsx | 96 +++++++++---------- 2 files changed, 50 insertions(+), 48 deletions(-) create mode 100644 changelogs/fragments/8803.yml diff --git a/changelogs/fragments/8803.yml b/changelogs/fragments/8803.yml new file mode 100644 index 000000000000..0cf82dc68ddf --- /dev/null +++ b/changelogs/fragments/8803.yml @@ -0,0 +1,2 @@ +fix: +- Query Editor state sync issues in Discover ([#8803](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/8803)) \ No newline at end of file diff --git a/src/plugins/data/public/ui/query_editor/query_editor.tsx b/src/plugins/data/public/ui/query_editor/query_editor.tsx index dd6058f8c61a..2223b577b513 100644 --- a/src/plugins/data/public/ui/query_editor/query_editor.tsx +++ b/src/plugins/data/public/ui/query_editor/query_editor.tsx @@ -25,6 +25,7 @@ import { RecentQueriesTable, QueryResult, QueryStatus, + useQueryStringManager, } from '../..'; import { OpenSearchDashboardsReactContextValue } from '../../../../opensearch_dashboards_react/public'; import { fromUser, getQueryLog, PersistedLog, toUser } from '../../query'; @@ -74,15 +75,26 @@ export const QueryEditorUI: React.FC = (props) => { const headerRef = useRef(null); const bannerRef = useRef(null); const queryControlsContainer = useRef(null); + // TODO: https://github.com/opensearch-project/OpenSearch-Dashboards/issues/8801 + const editorQuery = props.query; // local query state managed by the editor. Not to be confused by the app query state. const queryString = getQueryService().queryString; const languageManager = queryString.getLanguageService(); const extensionMap = languageManager.getQueryEditorExtensionMap(); const services = props.opensearchDashboards.services; + const { query } = useQueryStringManager({ + queryString, + }); + const queryRef = useRef(query); + + // Monaco commands are registered once at startup, we need a ref to access the latest query state inside command callbacks + useEffect(() => { + queryRef.current = query; + }, [query]); const persistedLogRef = useRef( props.persistedLog || - getQueryLog(services.uiSettings, services.storage, services.appName, props.query.language) + getQueryLog(services.uiSettings, services.storage, services.appName, query.language) ); const abortControllerRef = useRef(); @@ -95,17 +107,13 @@ export const QueryEditorUI: React.FC = (props) => { }; }, []); - const getQueryString = useCallback(() => { - return toUser(props.query.query); - }, [props.query]); - const renderQueryEditorExtensions = () => { if ( !( headerRef.current && bannerRef.current && queryControlsContainer.current && - props.query.language && + query.language && extensionMap && Object.keys(extensionMap).length > 0 ) @@ -114,7 +122,7 @@ export const QueryEditorUI: React.FC = (props) => { } return ( = (props) => { ); }; - const onSubmit = (query: Query, dateRange?: TimeRange) => { + const onSubmit = (currentQuery: Query, dateRange?: TimeRange) => { if (props.onSubmit) { if (persistedLogRef.current) { - persistedLogRef.current.add(query.query); + persistedLogRef.current.add(currentQuery.query); } props.onSubmit( { - query: fromUser(query.query), - language: query.language, - dataset: query.dataset, + ...currentQuery, + query: fromUser(currentQuery.query), }, dateRange ); } }; - const onChange = (query: Query, dateRange?: TimeRange) => { + const onChange = (currentQuery: Query, dateRange?: TimeRange) => { if (props.onChange) { props.onChange( - { query: fromUser(query.query), language: query.language, dataset: query.dataset }, + { + ...currentQuery, + query: fromUser(currentQuery.query), + }, dateRange ); } @@ -155,13 +165,13 @@ export const QueryEditorUI: React.FC = (props) => { const onQueryStringChange = (value: string) => { onChange({ query: value, - language: props.query.language, - dataset: props.query.dataset, + language: query.language, + dataset: query.dataset, }); }; - const onClickRecentQuery = (query: Query, timeRange?: TimeRange) => { - onSubmit(query, timeRange); + const onClickRecentQuery = (currentQuery: Query, timeRange?: TimeRange) => { + onSubmit(currentQuery, timeRange); }; const onInputChange = (value: string) => { @@ -175,13 +185,6 @@ export const QueryEditorUI: React.FC = (props) => { }; const onSelectLanguage = (languageId: string) => { - // Send telemetry info every time the user opts in or out of kuery - // As a result it is important this function only ever gets called in the - // UI component's change handler. - services.http.post('/api/opensearch-dashboards/dql_opt_in_stats', { - body: JSON.stringify({ opt_in: languageId === 'kuery' }), - }); - const newQuery = queryString.getInitialQueryByLanguage(languageId); onChange(newQuery); @@ -223,10 +226,10 @@ export const QueryEditorUI: React.FC = (props) => { ): Promise => { const indexPattern = await fetchIndexPattern(); const suggestions = await services.data.autocomplete.getQuerySuggestions({ - query: getQueryString(), + query: inputRef.current?.getValue() ?? '', selectionStart: model.getOffsetAt(position), selectionEnd: model.getOffsetAt(position), - language: props.query.language, + language: queryRef.current.language, indexPattern, position, services, @@ -263,7 +266,7 @@ export const QueryEditorUI: React.FC = (props) => { }; }; - const useQueryEditor = props.query.language !== 'kuery' && props.query.language !== 'lucene'; + const useQueryEditor = query.language !== 'kuery' && query.language !== 'lucene'; const languageSelector = ( = (props) => { ); const baseInputProps = { - languageId: props.query.language, - value: getQueryString(), + languageId: query.language, + value: toUser(editorQuery.query), }; const defaultInputProps: DefaultInputProps = { @@ -287,7 +290,12 @@ export const QueryEditorUI: React.FC = (props) => { inputRef.current = editor; // eslint-disable-next-line no-bitwise editor.addCommand(monaco.KeyMod.CtrlCmd | monaco.KeyCode.Enter, () => { - onSubmit(props.query); + const newQuery = { + ...queryRef.current, + query: editor.getValue(), + }; + + onSubmit(newQuery); }); return () => { @@ -305,7 +313,7 @@ export const QueryEditorUI: React.FC = (props) => { data-test-subj="queryEditorFooterTimestamp" className="queryEditor__footerItem" > - {props.query.dataset?.timeFieldName || ''} + {query.dataset?.timeFieldName || ''} , , ], @@ -336,22 +344,14 @@ export const QueryEditorUI: React.FC = (props) => { editorDidMount: (editor: monaco.editor.IStandaloneCodeEditor) => { inputRef.current = editor; - const handleEnterPress = () => { - onSubmit(props.query); - }; + editor.addCommand(monaco.KeyCode.Enter, () => { + const newQuery = { + ...query, + query: editor.getValue(), + }; - const disposable = editor.onKeyDown((e) => { - if (e.keyCode === monaco.KeyCode.Enter) { - // Prevent default Enter key behavior - e.preventDefault(); - handleEnterPress(); - } + onSubmit(newQuery); }); - - // Optional: Cleanup on component unmount - return () => { - disposable.dispose(); - }; }, provideCompletionItems, prepend: props.prepend, @@ -361,7 +361,7 @@ export const QueryEditorUI: React.FC = (props) => { {`${lineCount ?? 1} ${lineCount === 1 || !lineCount ? 'line' : 'lines'}`} , - {props.query.dataset?.timeFieldName || ''} + {query.dataset?.timeFieldName || ''} , , ], @@ -383,7 +383,7 @@ export const QueryEditorUI: React.FC = (props) => { }, }; - const languageEditorFunc = languageManager.getLanguage(props.query.language)!.editor; + const languageEditorFunc = languageManager.getLanguage(query.language)!.editor; const languageEditor = useQueryEditor ? languageEditorFunc(singleLineInputProps, {}, defaultInputProps)