From d1256427ef157ef5861f2af71ef841df502ebdd9 Mon Sep 17 00:00:00 2001 From: Anton Standrik Date: Thu, 25 Jul 2024 16:00:49 +0300 Subject: [PATCH 1/6] feat: settings usage in requests and modification... #1060 --- .../LabelWithPopover/LabelWithPopover.tsx | 12 +- .../QueryExecutionStatus.scss | 11 ++ .../QueryExecutionStatus.tsx | 38 ++++- .../Query/ExecuteResult/ExecuteResult.tsx | 9 +- .../Query/ExplainResult/ExplainResult.scss | 1 + .../Query/ExplainResult/ExplainResult.tsx | 7 +- .../Query/QueriesHistory/QueriesHistory.tsx | 16 +- .../Query/QueryDuration/QueryDuration.scss | 11 ++ .../Query/QueryDuration/QueryDuration.tsx | 4 +- .../Tenant/Query/QueryEditor/QueryEditor.tsx | 156 ++++++++++++------ .../QueryEditorControls.scss | 7 + .../QueryEditorControls.tsx | 73 ++++++-- .../getChangedQueryExecutionSettings.test.ts | 53 ++++++ .../utils/getChangedQueryExecutionSettings.ts | 13 ++ ...dQueryExecutionSettingsDescription.test.ts | 69 ++++++++ ...hangedQueryExecutionSettingsDescription.ts | 28 ++++ .../QuerySettingsBanner.scss | 10 ++ .../QuerySettingsBanner.tsx | 32 ++++ .../QuerySettingsDialog.tsx | 90 +++++----- .../Query/QuerySettingsDialog/constants.ts | 44 +++-- src/containers/Tenant/Query/i18n/en.json | 5 +- .../Tenant/Schema/SchemaTree/SchemaTree.tsx | 7 +- src/containers/Tenant/utils/schemaActions.ts | 9 +- src/services/api.ts | 40 +++-- src/services/settings.ts | 11 +- src/store/reducers/executeQuery.ts | 22 ++- .../reducers/explainQuery/explainQuery.ts | 22 ++- src/types/api/query.ts | 24 +++ src/types/store/query.ts | 8 + src/utils/constants.ts | 17 +- src/utils/hooks/index.ts | 2 +- src/utils/hooks/useChangedQuerySettings.ts | 89 ++++++++++ .../hooks/useLastQueryExecutionSettings.ts | 8 + src/utils/hooks/useQueryExecutionSettings.ts | 8 + src/utils/hooks/useQueryModes.ts | 8 - 35 files changed, 775 insertions(+), 189 deletions(-) create mode 100644 src/containers/Tenant/Query/QueryEditorControls/utils/getChangedQueryExecutionSettings.test.ts create mode 100644 src/containers/Tenant/Query/QueryEditorControls/utils/getChangedQueryExecutionSettings.ts create mode 100644 src/containers/Tenant/Query/QueryEditorControls/utils/getChangedQueryExecutionSettingsDescription.test.ts create mode 100644 src/containers/Tenant/Query/QueryEditorControls/utils/getChangedQueryExecutionSettingsDescription.ts create mode 100644 src/containers/Tenant/Query/QuerySettingsBanner/QuerySettingsBanner.scss create mode 100644 src/containers/Tenant/Query/QuerySettingsBanner/QuerySettingsBanner.tsx create mode 100644 src/utils/hooks/useChangedQuerySettings.ts create mode 100644 src/utils/hooks/useLastQueryExecutionSettings.ts create mode 100644 src/utils/hooks/useQueryExecutionSettings.ts delete mode 100644 src/utils/hooks/useQueryModes.ts diff --git a/src/components/LabelWithPopover/LabelWithPopover.tsx b/src/components/LabelWithPopover/LabelWithPopover.tsx index de5328b1f..e90db0151 100644 --- a/src/components/LabelWithPopover/LabelWithPopover.tsx +++ b/src/components/LabelWithPopover/LabelWithPopover.tsx @@ -1,21 +1,31 @@ import {HelpPopover} from '@gravity-ui/components'; +import type {ButtonProps} from '@gravity-ui/uikit'; interface LabelWithPopoverProps { text: React.ReactNode; popoverContent: React.ReactNode; + popoverClassName?: string; className?: string; contentClassName?: string; + buttonProps?: ButtonProps; } export const LabelWithPopover = ({ text, popoverContent, + popoverClassName, className, contentClassName, + buttonProps, }: LabelWithPopoverProps) => (
{text} {'\u00a0'} - +
); diff --git a/src/components/QueryExecutionStatus/QueryExecutionStatus.scss b/src/components/QueryExecutionStatus/QueryExecutionStatus.scss index cc59d52a8..191c35916 100644 --- a/src/components/QueryExecutionStatus/QueryExecutionStatus.scss +++ b/src/components/QueryExecutionStatus/QueryExecutionStatus.scss @@ -10,4 +10,15 @@ color: var(--g-color-text-danger); } } + + &__query-settings-icon { + color: var(--g-color-text-hint); + } + + &__message { + display: flex; + flex-wrap: wrap; + + white-space: pre; + } } diff --git a/src/components/QueryExecutionStatus/QueryExecutionStatus.tsx b/src/components/QueryExecutionStatus/QueryExecutionStatus.tsx index 359561159..861c8d887 100644 --- a/src/components/QueryExecutionStatus/QueryExecutionStatus.tsx +++ b/src/components/QueryExecutionStatus/QueryExecutionStatus.tsx @@ -1,8 +1,13 @@ -import {CircleCheck, CircleQuestionFill, CircleXmark} from '@gravity-ui/icons'; -import {Icon} from '@gravity-ui/uikit'; +import React from 'react'; + +import {CircleCheck, CircleInfo, CircleQuestionFill, CircleXmark} from '@gravity-ui/icons'; +import {Icon, Tooltip} from '@gravity-ui/uikit'; import {isAxiosError} from 'axios'; +import i18n from '../../containers/Tenant/Query/i18n'; +import {QUERY_SETTINGS, useSetting} from '../../lib'; import {cn} from '../../utils/cn'; +import {useChangedQuerySettings} from '../../utils/hooks/useChangedQuerySettings'; import './QueryExecutionStatus.scss'; @@ -13,6 +18,34 @@ interface QueryExecutionStatusProps { error?: unknown; } +const QuerySettingsIndicator = () => { + const [useQuerySettings] = useSetting(QUERY_SETTINGS); + const {isIndicatorShown, changedLastExecutionSettingsDescriptions} = useChangedQuerySettings(); + + if (!isIndicatorShown || !useQuerySettings) { + return null; + } + + return ( + + {i18n('banner.query-settings.message')} + {changedLastExecutionSettingsDescriptions.map((description, index, arr) => ( + + {description} + {index < arr.length - 1 ? ', ' : null} + + ))} + + } + > + + + ); +}; + export const QueryExecutionStatus = ({className, error}: QueryExecutionStatusProps) => { let icon: React.ReactNode; let label: string; @@ -35,6 +68,7 @@ export const QueryExecutionStatus = ({className, error}: QueryExecutionStatusPro
{icon} {label} +
); }; diff --git a/src/containers/Tenant/Query/ExecuteResult/ExecuteResult.tsx b/src/containers/Tenant/Query/ExecuteResult/ExecuteResult.tsx index acefb768a..704ab0647 100644 --- a/src/containers/Tenant/Query/ExecuteResult/ExecuteResult.tsx +++ b/src/containers/Tenant/Query/ExecuteResult/ExecuteResult.tsx @@ -11,17 +11,19 @@ import Fullscreen from '../../../../components/Fullscreen/Fullscreen'; import {YDBGraph} from '../../../../components/Graph/Graph'; import {QueryExecutionStatus} from '../../../../components/QueryExecutionStatus'; import {QueryResultTable} from '../../../../components/QueryResultTable/QueryResultTable'; +import {QUERY_SETTINGS} from '../../../../lib'; import {disableFullscreen} from '../../../../store/reducers/fullscreen'; import type {ColumnType, KeyValueRow} from '../../../../types/api/query'; import type {ValueOf} from '../../../../types/common'; import type {IQueryResult} from '../../../../types/store/query'; import {getArray} from '../../../../utils'; import {cn} from '../../../../utils/cn'; -import {useTypedDispatch, useTypedSelector} from '../../../../utils/hooks'; +import {useSetting, useTypedDispatch, useTypedSelector} from '../../../../utils/hooks'; import {parseQueryError} from '../../../../utils/query'; import {PaneVisibilityToggleButtons} from '../../utils/paneVisibilityToggleHelpers'; import {ResultIssues} from '../Issues/Issues'; import {QueryDuration} from '../QueryDuration/QueryDuration'; +import {QuerySettingsBanner} from '../QuerySettingsBanner/QuerySettingsBanner'; import {getPreparedResult} from '../utils/getPreparedResult'; import {getPlan} from './utils'; @@ -57,9 +59,9 @@ export function ExecuteResult({ }: ExecuteResultProps) { const [selectedResultSet, setSelectedResultSet] = React.useState(0); const [activeSection, setActiveSection] = React.useState(resultOptionsIds.result); - const isFullscreen = useTypedSelector((state) => state.fullscreen); const dispatch = useTypedDispatch(); + const [useQuerySettings] = useSetting(QUERY_SETTINGS); const stats = data?.stats; const resultsSetsCount = data?.resultSets?.length; @@ -238,7 +240,6 @@ export function ExecuteResult({
- {stats && !error && ( @@ -262,7 +263,7 @@ export function ExecuteResult({ />
- + {useQuerySettings && } {renderResultSection()} ); diff --git a/src/containers/Tenant/Query/ExplainResult/ExplainResult.scss b/src/containers/Tenant/Query/ExplainResult/ExplainResult.scss index 6e67b6a1c..a7b37db4d 100644 --- a/src/containers/Tenant/Query/ExplainResult/ExplainResult.scss +++ b/src/containers/Tenant/Query/ExplainResult/ExplainResult.scss @@ -8,6 +8,7 @@ &__text-message { padding: 15px 20px; } + &__controls { position: sticky; z-index: 2; diff --git a/src/containers/Tenant/Query/ExplainResult/ExplainResult.tsx b/src/containers/Tenant/Query/ExplainResult/ExplainResult.tsx index 425762086..440a6e343 100644 --- a/src/containers/Tenant/Query/ExplainResult/ExplainResult.tsx +++ b/src/containers/Tenant/Query/ExplainResult/ExplainResult.tsx @@ -7,13 +7,15 @@ import EnableFullscreenButton from '../../../../components/EnableFullscreenButto import Fullscreen from '../../../../components/Fullscreen/Fullscreen'; import {LoaderWrapper} from '../../../../components/LoaderWrapper/LoaderWrapper'; import {QueryExecutionStatus} from '../../../../components/QueryExecutionStatus'; +import {QUERY_SETTINGS} from '../../../../lib'; import type {PreparedExplainResponse} from '../../../../store/reducers/explainQuery/types'; import {disableFullscreen} from '../../../../store/reducers/fullscreen'; import type {ValueOf} from '../../../../types/common'; import {cn} from '../../../../utils/cn'; -import {useTypedDispatch, useTypedSelector} from '../../../../utils/hooks'; +import {useSetting, useTypedDispatch, useTypedSelector} from '../../../../utils/hooks'; import {parseQueryErrorToString} from '../../../../utils/query'; import {PaneVisibilityToggleButtons} from '../../utils/paneVisibilityToggleHelpers'; +import {QuerySettingsBanner} from '../QuerySettingsBanner/QuerySettingsBanner'; import {Ast} from './components/Ast/Ast'; import {Graph} from './components/Graph/Graph'; @@ -82,6 +84,8 @@ export function ExplainResult({ const isFullscreen = useTypedSelector((state) => state.fullscreen); + const [useQuerySettings] = useSetting(QUERY_SETTINGS); + React.useEffect(() => { return () => { dispatch(disableFullscreen()); @@ -165,6 +169,7 @@ export function ExplainResult({ )} + {useQuerySettings && } {/* this is a hack: only one Graph component may be in DOM because of it's canvas id */} {activeOption === EXPLAIN_OPTIONS_IDS.schema && isFullscreen ? null : ( diff --git a/src/containers/Tenant/Query/QueriesHistory/QueriesHistory.tsx b/src/containers/Tenant/Query/QueriesHistory/QueriesHistory.tsx index e3a840e76..295dbf010 100644 --- a/src/containers/Tenant/Query/QueriesHistory/QueriesHistory.tsx +++ b/src/containers/Tenant/Query/QueriesHistory/QueriesHistory.tsx @@ -7,7 +7,11 @@ import {TENANT_QUERY_TABS_ID} from '../../../../store/reducers/tenant/constants' import {setQueryTab} from '../../../../store/reducers/tenant/tenant'; import type {QueryInHistory} from '../../../../types/store/executeQuery'; import {cn} from '../../../../utils/cn'; -import {useQueryModes, useTypedDispatch, useTypedSelector} from '../../../../utils/hooks'; +import { + useQueryExecutionSettings, + useTypedDispatch, + useTypedSelector, +} from '../../../../utils/hooks'; import {QUERY_MODES, QUERY_SYNTAX} from '../../../../utils/query'; import {MAX_QUERY_HEIGHT, QUERY_TABLE_SETTINGS} from '../../utils/constants'; import i18n from '../i18n'; @@ -25,17 +29,17 @@ interface QueriesHistoryProps { function QueriesHistory({changeUserInput}: QueriesHistoryProps) { const dispatch = useTypedDispatch(); - const [queryMode, setQueryMode] = useQueryModes(); + const [settings, setQuerySettings] = useQueryExecutionSettings(); const queriesHistory = useTypedSelector(selectQueriesHistory); const reversedHistory = [...queriesHistory].reverse(); const onQueryClick = (query: QueryInHistory) => { - if (query.syntax === QUERY_SYNTAX.pg && queryMode !== QUERY_MODES.pg) { - setQueryMode(QUERY_MODES.pg); - } else if (query.syntax !== QUERY_SYNTAX.pg && queryMode === QUERY_MODES.pg) { + if (query.syntax === QUERY_SYNTAX.pg && settings.queryMode !== QUERY_MODES.pg) { + setQuerySettings({...settings, queryMode: QUERY_MODES.pg}); + } else if (query.syntax !== QUERY_SYNTAX.pg && settings.queryMode === QUERY_MODES.pg) { // Set query mode for queries with yql syntax - setQueryMode(QUERY_MODES.script); + setQuerySettings({...settings, queryMode: QUERY_MODES.script}); } changeUserInput({input: query.queryText}); diff --git a/src/containers/Tenant/Query/QueryDuration/QueryDuration.scss b/src/containers/Tenant/Query/QueryDuration/QueryDuration.scss index e7700ec5b..cad246f07 100644 --- a/src/containers/Tenant/Query/QueryDuration/QueryDuration.scss +++ b/src/containers/Tenant/Query/QueryDuration/QueryDuration.scss @@ -7,10 +7,21 @@ color: var(--g-color-text-complementary); &__item-with-popover { + display: flex; + white-space: nowrap; } &__popover { + display: flex; + align-items: center; + } + + &__popover-content { max-width: 300px; } + + &__popover-button { + display: flex; + } } diff --git a/src/containers/Tenant/Query/QueryDuration/QueryDuration.tsx b/src/containers/Tenant/Query/QueryDuration/QueryDuration.tsx index b459687ae..40cca7817 100644 --- a/src/containers/Tenant/Query/QueryDuration/QueryDuration.tsx +++ b/src/containers/Tenant/Query/QueryDuration/QueryDuration.tsx @@ -22,9 +22,11 @@ export const QueryDuration = ({duration}: QueryDurationProps) => { ); diff --git a/src/containers/Tenant/Query/QueryEditor/QueryEditor.tsx b/src/containers/Tenant/Query/QueryEditor/QueryEditor.tsx index a8b51655a..983734480 100644 --- a/src/containers/Tenant/Query/QueryEditor/QueryEditor.tsx +++ b/src/containers/Tenant/Query/QueryEditor/QueryEditor.tsx @@ -1,5 +1,6 @@ import React from 'react'; +import {isEqual} from 'lodash'; import throttle from 'lodash/throttle'; import type Monaco from 'monaco-editor'; import {connect} from 'react-redux'; @@ -21,15 +22,18 @@ import {setShowPreview} from '../../../../store/reducers/schema/schema'; import type {EPathType} from '../../../../types/api/schema'; import type {ValueOf} from '../../../../types/common'; import type {ExecuteQueryState} from '../../../../types/store/executeQuery'; -import type {IQueryResult, QueryAction, QueryMode} from '../../../../types/store/query'; +import type {IQueryResult, QueryAction, QuerySettings} from '../../../../types/store/query'; import {cn} from '../../../../utils/cn'; import { DEFAULT_IS_QUERY_RESULT_COLLAPSED, DEFAULT_SIZE_RESULT_PANE_KEY, LAST_USED_QUERY_ACTION_KEY, + QUERY_SETTINGS, QUERY_USE_MULTI_SCHEMA_KEY, } from '../../../../utils/constants'; -import {useQueryModes, useSetting} from '../../../../utils/hooks'; +import {useQueryExecutionSettings, useSetting} from '../../../../utils/hooks'; +import {useChangedQuerySettings} from '../../../../utils/hooks/useChangedQuerySettings'; +import {useLastQueryExecutionSettings} from '../../../../utils/hooks/useLastQueryExecutionSettings'; import {YQL_LANGUAGE_ID} from '../../../../utils/monaco/constats'; import {QUERY_ACTIONS} from '../../../../utils/query'; import type {InitialPaneState} from '../../utils/paneVisibilityToggleHelpers'; @@ -91,6 +95,8 @@ function QueryEditor(props: QueryEditorProps) { path, setTenantPath: setPath, setQueryAction, + saveQueryToHistory, + setShowPreview, executeQuery, type, theme, @@ -100,9 +106,13 @@ function QueryEditor(props: QueryEditorProps) { const {tenantPath: savedPath} = executeQuery; const [resultType, setResultType] = React.useState(RESULT_TYPES.EXECUTE); - + const [querySettingsFlag] = useSetting(QUERY_SETTINGS); const [isResultLoaded, setIsResultLoaded] = React.useState(false); - const [queryMode, setQueryMode] = useQueryModes(); + const [querySettings, setQuerySettings] = useQueryExecutionSettings(); + const [lastQueryExecutionSettings, setLastQueryExecutionSettings] = + useLastQueryExecutionSettings(); + const {resetBanner} = useChangedQuerySettings(); + const [useMultiSchema] = useSetting(QUERY_USE_MULTI_SCHEMA_KEY); const [lastUsedQueryAction, setLastUsedQueryAction] = useSetting( LAST_USED_QUERY_ACTION_KEY, @@ -182,56 +192,106 @@ function QueryEditor(props: QueryEditorProps) { }; }, [executeQuery]); - const handleSendExecuteClick = (mode: QueryMode | undefined, text?: string) => { - if (!mode) { - return; - } - const {input, history} = executeQuery; + const handleSendExecuteClick = React.useCallback( + (settings: QuerySettings, text?: string) => { + const {input, history} = executeQuery; - const schema = useMultiSchema ? 'multi' : 'modern'; + const schema = useMultiSchema ? 'multi' : 'modern'; - const query = text ?? input; + const query = text ?? input; - setLastUsedQueryAction(QUERY_ACTIONS.execute); - setResultType(RESULT_TYPES.EXECUTE); - sendExecuteQuery({ - query, - database: tenantName, - mode, - schema, - }); - setIsResultLoaded(true); - props.setShowPreview(false); + setLastUsedQueryAction(QUERY_ACTIONS.execute); + if (!isEqual(lastQueryExecutionSettings, settings)) { + resetBanner(); + setLastQueryExecutionSettings(settings); + } - // Don't save partial queries in history - if (!text) { - const {queries, currentIndex} = history; - if (query !== queries[currentIndex]?.queryText) { - props.saveQueryToHistory(input, mode); + const executeQuerySettings = querySettingsFlag + ? querySettings + : { + queryMode: querySettings.queryMode, + }; + + setResultType(RESULT_TYPES.EXECUTE); + sendExecuteQuery({ + query, + database: tenantName, + querySettings: executeQuerySettings, + schema, + }); + setIsResultLoaded(true); + setShowPreview(false); + + // Don't save partial queries in history + if (!text) { + const {queries, currentIndex} = history; + if (query !== queries[currentIndex]?.queryText) { + saveQueryToHistory(input, querySettings.queryMode); + } } - } - dispatchResultVisibilityState(PaneVisibilityActionTypes.triggerExpand); - }; + dispatchResultVisibilityState(PaneVisibilityActionTypes.triggerExpand); + }, + [ + executeQuery, + useMultiSchema, + setLastUsedQueryAction, + lastQueryExecutionSettings, + querySettingsFlag, + querySettings, + sendExecuteQuery, + saveQueryToHistory, + setShowPreview, + tenantName, + resetBanner, + setLastQueryExecutionSettings, + ], + ); const handleSettingsClick = () => { setQueryAction('settings'); props.setShowPreview(false); }; - const handleGetExplainQueryClick = (mode: QueryMode | undefined) => { - const {input} = executeQuery; + const handleGetExplainQueryClick = React.useCallback( + (settings: QuerySettings) => { + const {input} = executeQuery; - setLastUsedQueryAction(QUERY_ACTIONS.explain); - setResultType(RESULT_TYPES.EXPLAIN); - sendExplainQuery({ - query: input, - database: tenantName, - mode: mode, - }); - setIsResultLoaded(true); - props.setShowPreview(false); - dispatchResultVisibilityState(PaneVisibilityActionTypes.triggerExpand); - }; + setLastUsedQueryAction(QUERY_ACTIONS.explain); + + if (!isEqual(lastQueryExecutionSettings, settings)) { + resetBanner(); + setLastQueryExecutionSettings(settings); + } + + const explainQuerySettings = querySettingsFlag + ? querySettings + : { + queryMode: querySettings.queryMode, + }; + + setResultType(RESULT_TYPES.EXPLAIN); + sendExplainQuery({ + query: input, + database: tenantName, + querySettings: explainQuerySettings, + }); + setIsResultLoaded(true); + setShowPreview(false); + dispatchResultVisibilityState(PaneVisibilityActionTypes.triggerExpand); + }, + [ + executeQuery, + lastQueryExecutionSettings, + querySettings, + querySettingsFlag, + resetBanner, + sendExplainQuery, + setLastQueryExecutionSettings, + setLastUsedQueryAction, + setShowPreview, + tenantName, + ], + ); React.useEffect(() => { if (monacoHotKey === null) { @@ -241,9 +301,9 @@ function QueryEditor(props: QueryEditorProps) { switch (monacoHotKey) { case MONACO_HOT_KEY_ACTIONS.sendQuery: { if (lastUsedQueryAction === QUERY_ACTIONS.explain) { - handleGetExplainQueryClick(queryMode); + handleGetExplainQueryClick(querySettings); } else { - handleSendExecuteClick(queryMode); + handleSendExecuteClick(querySettings); } break; } @@ -257,13 +317,13 @@ function QueryEditor(props: QueryEditorProps) { endLineNumber: selection.getPosition().lineNumber, endColumn: selection.getPosition().column, }); - handleSendExecuteClick(queryMode, text); + handleSendExecuteClick(querySettings, text); } break; } } // eslint-disable-next-line react-hooks/exhaustive-deps - }, [monacoHotKey]); + }, [monacoHotKey, handleSendExecuteClick, handleGetExplainQueryClick]); const editorDidMount = (editor: Monaco.editor.IStandaloneCodeEditor, monaco: typeof Monaco) => { const keybindings = getKeyBindings(monaco); @@ -356,8 +416,8 @@ function QueryEditor(props: QueryEditorProps) { onExplainButtonClick={handleGetExplainQueryClick} explainIsLoading={explainQueryResult.isLoading} disabled={!executeQuery.input} - onUpdateQueryMode={setQueryMode} - queryMode={queryMode} + onUpdateQueryMode={(queryMode) => setQuerySettings({...querySettings, queryMode})} + querySettings={querySettings} highlightedAction={lastUsedQueryAction} /> ); diff --git a/src/containers/Tenant/Query/QueryEditorControls/QueryEditorControls.scss b/src/containers/Tenant/Query/QueryEditorControls/QueryEditorControls.scss index 4241d69f9..80ac6a8c9 100644 --- a/src/containers/Tenant/Query/QueryEditorControls/QueryEditorControls.scss +++ b/src/containers/Tenant/Query/QueryEditorControls/QueryEditorControls.scss @@ -48,4 +48,11 @@ white-space: pre-wrap; } + + &__message { + display: flex; + flex-wrap: wrap; + + white-space: pre; + } } diff --git a/src/containers/Tenant/Query/QueryEditorControls/QueryEditorControls.tsx b/src/containers/Tenant/Query/QueryEditorControls/QueryEditorControls.tsx index e6295db7d..4fa3dcd13 100644 --- a/src/containers/Tenant/Query/QueryEditorControls/QueryEditorControls.tsx +++ b/src/containers/Tenant/Query/QueryEditorControls/QueryEditorControls.tsx @@ -2,12 +2,13 @@ import React from 'react'; import {ChevronDown, Gear, PlayFill} from '@gravity-ui/icons'; import type {ButtonView} from '@gravity-ui/uikit'; -import {Button, DropdownMenu, Icon} from '@gravity-ui/uikit'; +import {Button, DropdownMenu, Icon, Tooltip} from '@gravity-ui/uikit'; import {LabelWithPopover} from '../../../../components/LabelWithPopover'; import {QUERY_SETTINGS, useSetting} from '../../../../lib'; -import type {QueryAction, QueryMode} from '../../../../types/store/query'; +import type {QueryAction, QueryMode, QuerySettings} from '../../../../types/store/query'; import {cn} from '../../../../utils/cn'; +import {useChangedQuerySettings} from '../../../../utils/hooks/useChangedQuerySettings'; import {QUERY_MODES, QUERY_MODES_TITLES} from '../../../../utils/query'; import {SaveQuery} from '../SaveQuery/SaveQuery'; import i18n from '../i18n'; @@ -42,15 +43,60 @@ const QueryModeSelectorOptions = { }, } as const; +interface SettingsButtonProps { + onClick: () => void; + runIsLoading: boolean; +} + +const SettingsButton = ({onClick, runIsLoading}: SettingsButtonProps) => { + const {changedCurrentSettings, changedCurrentSettingsDescriptions} = useChangedQuerySettings(); + + const extraGearProps = + changedCurrentSettings.length > 0 + ? ({view: 'outlined-info', selected: true} as const) + : null; + + return ( + + {i18n('gear.tooltip')} + {changedCurrentSettingsDescriptions.map((description, index, arr) => ( + + {description} + {index < arr.length - 1 ? ', ' : null} + + ))} + + } + openDelay={0} + placement={['top-start']} + > + + + ); +}; + interface QueryEditorControlsProps { - onRunButtonClick: (mode?: QueryMode) => void; + onRunButtonClick: (querySettings: QuerySettings) => void; onSettingsButtonClick: () => void; runIsLoading: boolean; - onExplainButtonClick: (mode?: QueryMode) => void; + onExplainButtonClick: (querySettings: QuerySettings) => void; explainIsLoading: boolean; disabled: boolean; onUpdateQueryMode: (mode: QueryMode) => void; - queryMode: QueryMode; + querySettings: QuerySettings; highlightedAction: QueryAction; } @@ -62,10 +108,11 @@ export const QueryEditorControls = ({ onExplainButtonClick, explainIsLoading, disabled, - queryMode, + querySettings, highlightedAction, }: QueryEditorControlsProps) => { const [useQuerySettings] = useSetting(QUERY_SETTINGS); + const runView: ButtonView | undefined = highlightedAction === 'execute' ? 'action' : undefined; const explainView: ButtonView | undefined = highlightedAction === 'explain' ? 'action' : undefined; @@ -93,7 +140,7 @@ export const QueryEditorControls = ({
{useQuerySettings ? ( - + ) : (
{`${i18n('controls.query-mode-selector_type')} ${ - QueryModeSelectorOptions[queryMode].title + QueryModeSelectorOptions[querySettings.queryMode].title }`} diff --git a/src/containers/Tenant/Query/QueryEditorControls/utils/getChangedQueryExecutionSettings.test.ts b/src/containers/Tenant/Query/QueryEditorControls/utils/getChangedQueryExecutionSettings.test.ts new file mode 100644 index 000000000..38f2d9994 --- /dev/null +++ b/src/containers/Tenant/Query/QueryEditorControls/utils/getChangedQueryExecutionSettings.test.ts @@ -0,0 +1,53 @@ +import type {QuerySettings} from '../../../../../types/store/query'; +import { + ISOLATION_LEVELS, + QUERY_MODES, + STATISTICS_MODES, + TRACING_LEVELS, +} from '../../../../../utils/query'; + +const DEFAULT_QUERY_SETTINGS: QuerySettings = { + queryMode: QUERY_MODES.script, + isolationLevel: ISOLATION_LEVELS.serializable, + timeout: '60', + statisticsMode: STATISTICS_MODES.none, + tracingLevel: TRACING_LEVELS.detailed, +}; + +import getChangedQueryExecutionSettings from './getChangedQueryExecutionSettings'; + +describe('getChangedQueryExecutionSettings', () => { + it('should return an empty array if no settings have changed', () => { + const currentSettings: QuerySettings = {...DEFAULT_QUERY_SETTINGS}; + const result = getChangedQueryExecutionSettings(currentSettings, DEFAULT_QUERY_SETTINGS); + expect(result).toEqual([]); + }); + + it('should return the keys of settings that have changed', () => { + const currentSettings: QuerySettings = { + ...DEFAULT_QUERY_SETTINGS, + queryMode: QUERY_MODES.data, + timeout: '30', + }; + const result = getChangedQueryExecutionSettings(currentSettings, DEFAULT_QUERY_SETTINGS); + expect(result).toEqual(['queryMode', 'timeout']); + }); + + it('should return all keys if all settings have changed', () => { + const currentSettings: QuerySettings = { + queryMode: QUERY_MODES.data, + isolationLevel: ISOLATION_LEVELS.onlinero, + timeout: '90', + statisticsMode: STATISTICS_MODES.basic, + tracingLevel: TRACING_LEVELS.basic, + }; + const result = getChangedQueryExecutionSettings(currentSettings, DEFAULT_QUERY_SETTINGS); + expect(result).toEqual([ + 'queryMode', + 'isolationLevel', + 'timeout', + 'statisticsMode', + 'tracingLevel', + ]); + }); +}); diff --git a/src/containers/Tenant/Query/QueryEditorControls/utils/getChangedQueryExecutionSettings.ts b/src/containers/Tenant/Query/QueryEditorControls/utils/getChangedQueryExecutionSettings.ts new file mode 100644 index 000000000..a456a2e5b --- /dev/null +++ b/src/containers/Tenant/Query/QueryEditorControls/utils/getChangedQueryExecutionSettings.ts @@ -0,0 +1,13 @@ +import type {QuerySettings} from '../../../../../types/store/query'; + +export default function getChangedQueryExecutionSettings( + currentSettings: QuerySettings, + defaultSettings: QuerySettings, +): (keyof QuerySettings)[] { + const currentMap = new Map(Object.entries(currentSettings)); + const defaultMap = new Map(Object.entries(defaultSettings)); + + return Array.from(currentMap.keys()).filter( + (key) => currentMap.get(key) !== defaultMap.get(key), + ) as (keyof QuerySettings)[]; +} diff --git a/src/containers/Tenant/Query/QueryEditorControls/utils/getChangedQueryExecutionSettingsDescription.test.ts b/src/containers/Tenant/Query/QueryEditorControls/utils/getChangedQueryExecutionSettingsDescription.test.ts new file mode 100644 index 000000000..98ed9d5ce --- /dev/null +++ b/src/containers/Tenant/Query/QueryEditorControls/utils/getChangedQueryExecutionSettingsDescription.test.ts @@ -0,0 +1,69 @@ +import type {QuerySettings} from '../../../../../types/store/query'; +import { + ISOLATION_LEVELS, + QUERY_MODES, + STATISTICS_MODES, + TRACING_LEVELS, +} from '../../../../../utils/query'; +import {QUERY_SETTINGS_FIELD_SETTINGS} from '../../QuerySettingsDialog/constants'; + +import getChangedQueryExecutionSettingsDescription from './getChangedQueryExecutionSettingsDescription'; + +const DEFAULT_QUERY_SETTINGS: QuerySettings = { + queryMode: QUERY_MODES.script, + isolationLevel: ISOLATION_LEVELS.serializable, + timeout: '60', + statisticsMode: STATISTICS_MODES.none, + tracingLevel: TRACING_LEVELS.detailed, +}; + +describe('getChangedQueryExecutionSettingsDescription', () => { + it('should return an empty array if no settings changed', () => { + const currentSettings: QuerySettings = DEFAULT_QUERY_SETTINGS; + + const result = getChangedQueryExecutionSettingsDescription({ + currentSettings, + defaultSettings: DEFAULT_QUERY_SETTINGS, + }); + expect(result).toEqual([]); + }); + + it('should return the description for changed settings', () => { + const currentSettings: QuerySettings = { + ...DEFAULT_QUERY_SETTINGS, + queryMode: QUERY_MODES.pg, + timeout: '63', + }; + + const result = getChangedQueryExecutionSettingsDescription({ + currentSettings, + defaultSettings: DEFAULT_QUERY_SETTINGS, + }); + expect(result).toEqual([ + `${QUERY_SETTINGS_FIELD_SETTINGS.queryMode.title}: ${QUERY_SETTINGS_FIELD_SETTINGS.queryMode.options.find((option) => option.value === QUERY_MODES.pg)?.content}`, + `${QUERY_SETTINGS_FIELD_SETTINGS.timeout.title}: 63`, + ]); + }); + + it('should return the correct description for all changed settings', () => { + const currentSettings: QuerySettings = { + queryMode: QUERY_MODES.data, + isolationLevel: ISOLATION_LEVELS.snapshot, + timeout: '120', + statisticsMode: STATISTICS_MODES.profile, + tracingLevel: TRACING_LEVELS.diagnostic, + }; + + const result = getChangedQueryExecutionSettingsDescription({ + currentSettings, + defaultSettings: DEFAULT_QUERY_SETTINGS, + }); + expect(result).toEqual([ + `${QUERY_SETTINGS_FIELD_SETTINGS.queryMode.title}: ${QUERY_SETTINGS_FIELD_SETTINGS.queryMode.options.find((option) => option.value === QUERY_MODES.data)?.content}`, + `${QUERY_SETTINGS_FIELD_SETTINGS.isolationLevel.title}: ${QUERY_SETTINGS_FIELD_SETTINGS.isolationLevel.options.find((option) => option.value === ISOLATION_LEVELS.snapshot)?.content}`, + `${QUERY_SETTINGS_FIELD_SETTINGS.timeout.title}: 120`, + `${QUERY_SETTINGS_FIELD_SETTINGS.statisticsMode.title}: ${QUERY_SETTINGS_FIELD_SETTINGS.statisticsMode.options.find((option) => option.value === STATISTICS_MODES.profile)?.content}`, + `${QUERY_SETTINGS_FIELD_SETTINGS.tracingLevel.title}: ${QUERY_SETTINGS_FIELD_SETTINGS.tracingLevel.options.find((option) => option.value === TRACING_LEVELS.diagnostic)?.content}`, + ]); + }); +}); diff --git a/src/containers/Tenant/Query/QueryEditorControls/utils/getChangedQueryExecutionSettingsDescription.ts b/src/containers/Tenant/Query/QueryEditorControls/utils/getChangedQueryExecutionSettingsDescription.ts new file mode 100644 index 000000000..b81cc6100 --- /dev/null +++ b/src/containers/Tenant/Query/QueryEditorControls/utils/getChangedQueryExecutionSettingsDescription.ts @@ -0,0 +1,28 @@ +import type {QuerySettings} from '../../../../../types/store/query'; +import {QUERY_SETTINGS_FIELD_SETTINGS} from '../../QuerySettingsDialog/constants'; + +import getChangedQueryExecutionSettings from './getChangedQueryExecutionSettings'; + +export default function getChangedQueryExecutionSettingsDescription({ + currentSettings, + defaultSettings, +}: { + currentSettings: QuerySettings; + defaultSettings: QuerySettings; +}): string[] { + const keys = getChangedQueryExecutionSettings(currentSettings, defaultSettings); + return keys.map((key) => { + const settings = QUERY_SETTINGS_FIELD_SETTINGS[key]; + const currentValue = currentSettings[key]; + + if ('options' in settings) { + const content = settings.options.find( + (option) => option.value === currentValue, + )?.content; + + return `${settings.title}: ${content}`; + } + + return `${settings.title}: ${currentValue}`; + }); +} diff --git a/src/containers/Tenant/Query/QuerySettingsBanner/QuerySettingsBanner.scss b/src/containers/Tenant/Query/QuerySettingsBanner/QuerySettingsBanner.scss new file mode 100644 index 000000000..e9b74aef0 --- /dev/null +++ b/src/containers/Tenant/Query/QuerySettingsBanner/QuerySettingsBanner.scss @@ -0,0 +1,10 @@ +.ydb-query-settings-banner { + margin: var(--g-spacing-1) var(--g-spacing-5); + + &__message { + display: flex; + flex-wrap: wrap; + + white-space: pre; + } +} diff --git a/src/containers/Tenant/Query/QuerySettingsBanner/QuerySettingsBanner.tsx b/src/containers/Tenant/Query/QuerySettingsBanner/QuerySettingsBanner.tsx new file mode 100644 index 000000000..e43f061c1 --- /dev/null +++ b/src/containers/Tenant/Query/QuerySettingsBanner/QuerySettingsBanner.tsx @@ -0,0 +1,32 @@ +import {Alert} from '@gravity-ui/uikit'; + +import {cn} from '../../../../utils/cn'; +import {useChangedQuerySettings} from '../../../../utils/hooks/useChangedQuerySettings'; +import i18n from '../i18n'; + +const b = cn('ydb-query-settings-banner'); + +import './QuerySettingsBanner.scss'; + +export function QuerySettingsBanner() { + const {isBannerShown, changedLastExecutionSettingsDescriptions, closeBanner} = + useChangedQuerySettings(); + return isBannerShown ? ( + + {i18n('banner.query-settings.message')} + {changedLastExecutionSettingsDescriptions.map((description, index, arr) => ( + + {description} + {index < arr.length - 1 ? ', ' : null} + + ))} +
+ } + onClose={closeBanner} + /> + ) : null; +} diff --git a/src/containers/Tenant/Query/QuerySettingsDialog/QuerySettingsDialog.tsx b/src/containers/Tenant/Query/QuerySettingsDialog/QuerySettingsDialog.tsx index 1bf556605..419796a11 100644 --- a/src/containers/Tenant/Query/QuerySettingsDialog/QuerySettingsDialog.tsx +++ b/src/containers/Tenant/Query/QuerySettingsDialog/QuerySettingsDialog.tsx @@ -7,65 +7,47 @@ import { selectQueryAction, setQueryAction, } from '../../../../store/reducers/queryActions/queryActions'; -import type { - IsolationLevel, - QueryMode, - StatisticsMode, - TracingLevel, -} from '../../../../types/store/query'; +import type {QuerySettings} from '../../../../types/store/query'; import {cn} from '../../../../utils/cn'; -import {useTypedDispatch, useTypedSelector} from '../../../../utils/hooks'; import { - ISOLATION_LEVELS, - QUERY_MODES, - STATISTICS_MODES, - TRACING_LEVELS, -} from '../../../../utils/query'; + useQueryExecutionSettings, + useTypedDispatch, + useTypedSelector, +} from '../../../../utils/hooks'; import {QuerySettingsSelect} from './QuerySettingsSelect'; -import { - ISOLATION_LEVEL_SELECT_OPTIONS, - QUERY_MODE_SELECT_OPTIONS, - STATISTICS_MODE_SELECT_OPTIONS, - TRACING_LEVEL_SELECT_OPTIONS, -} from './constants'; +import {QUERY_SETTINGS_FIELD_SETTINGS} from './constants'; import i18n from './i18n'; import './QuerySettingsDialog.scss'; const b = cn('ydb-query-settings-dialog'); -type FormValues = { - queryMode: QueryMode; - timeout: string; - isolationLevel: IsolationLevel; - statisticsMode: StatisticsMode; - tracingLevel: TracingLevel; -}; - export function QuerySettingsDialog() { const dispatch = useTypedDispatch(); const queryAction = useTypedSelector(selectQueryAction); - const {control, handleSubmit, reset} = useForm({ - defaultValues: { - queryMode: QUERY_MODES.script, - timeout: '60', - isolationLevel: ISOLATION_LEVELS.serializable, - statisticsMode: STATISTICS_MODES.none, - tracingLevel: TRACING_LEVELS.detailed, - }, + const [querySettings, setQuerySettings] = useQueryExecutionSettings(); + const {control, handleSubmit, reset} = useForm({ + defaultValues: querySettings, }); - const onCloseDialog = () => { + React.useEffect(() => { + reset(querySettings); + }, [querySettings, reset]); + + const onCloseDialog = React.useCallback(() => { dispatch(setQueryAction('idle')); reset(); - }; + }, [dispatch, reset]); - const onSaveClick = (data: FormValues) => { - console.log('Form Data:', data); - // dispatch(saveQuerySettings(data)); - onCloseDialog(); - }; + const onSaveClick = React.useCallback( + (data: QuerySettings) => { + setQuerySettings(data); + reset(data); + onCloseDialog(); + }, + [onCloseDialog, reset, setQuerySettings], + ); return (
)} /> @@ -98,7 +82,7 @@ export function QuerySettingsDialog() {
)} /> @@ -140,7 +126,7 @@ export function QuerySettingsDialog() {
)} /> @@ -158,7 +146,7 @@ export function QuerySettingsDialog() {
)} /> diff --git a/src/containers/Tenant/Query/QuerySettingsDialog/constants.ts b/src/containers/Tenant/Query/QuerySettingsDialog/constants.ts index ab9d89f33..296ed494f 100644 --- a/src/containers/Tenant/Query/QuerySettingsDialog/constants.ts +++ b/src/containers/Tenant/Query/QuerySettingsDialog/constants.ts @@ -10,6 +10,8 @@ import { } from '../../../../utils/query'; import i18n from '../i18n'; +import formI18n from './i18n'; + export const ISOLATION_LEVEL_SELECT_OPTIONS = [ { value: ISOLATION_LEVELS.serializable, @@ -88,6 +90,16 @@ export const STATISTICS_MODE_SELECT_OPTIONS = [ ]; export const TRACING_LEVEL_SELECT_OPTIONS = [ + { + value: TRACING_LEVELS.off, + content: TRACING_LEVELS_TITLES[TRACING_LEVELS.off], + text: i18n('tracing-level-description.off'), + }, + { + value: TRACING_LEVELS.toplevel, + content: TRACING_LEVELS_TITLES[TRACING_LEVELS.toplevel], + text: i18n('tracing-level-description.toplevel'), + }, { value: TRACING_LEVELS.basic, content: TRACING_LEVELS_TITLES[TRACING_LEVELS.basic], @@ -104,19 +116,31 @@ export const TRACING_LEVEL_SELECT_OPTIONS = [ content: TRACING_LEVELS_TITLES[TRACING_LEVELS.diagnostic], text: i18n('tracing-level-description.diagnostic'), }, - { - value: TRACING_LEVELS.off, - content: TRACING_LEVELS_TITLES[TRACING_LEVELS.off], - text: i18n('tracing-level-description.off'), - }, - { - value: TRACING_LEVELS.toplevel, - content: TRACING_LEVELS_TITLES[TRACING_LEVELS.toplevel], - text: i18n('tracing-level-description.toplevel'), - }, { value: TRACING_LEVELS.trace, content: TRACING_LEVELS_TITLES[TRACING_LEVELS.trace], text: i18n('tracing-level-description.trace'), }, ]; + +export const QUERY_SETTINGS_FIELD_SETTINGS = { + isolationLevel: { + title: formI18n('form.isolation-level'), + options: ISOLATION_LEVEL_SELECT_OPTIONS, + }, + queryMode: { + title: formI18n('form.query-mode'), + options: QUERY_MODE_SELECT_OPTIONS, + }, + statisticsMode: { + title: formI18n('form.statistics-mode'), + options: STATISTICS_MODE_SELECT_OPTIONS, + }, + tracingLevel: { + title: formI18n('form.tracing-level'), + options: TRACING_LEVEL_SELECT_OPTIONS, + }, + timeout: { + title: formI18n('form.timeout'), + }, +} as const; diff --git a/src/containers/Tenant/Query/i18n/en.json b/src/containers/Tenant/Query/i18n/en.json index 33ccc7d99..55f2c661b 100644 --- a/src/containers/Tenant/Query/i18n/en.json +++ b/src/containers/Tenant/Query/i18n/en.json @@ -46,5 +46,8 @@ "action.send-selected-query": "Send selected query", "action.previous-query": "Previous query in history", "action.next-query": "Next query in history", - "action.save-query": "Save query" + "action.save-query": "Save query", + + "gear.tooltip": "Query execution settings have been changed for ", + "banner.query-settings.message": "Query results are displayed for " } diff --git a/src/containers/Tenant/Schema/SchemaTree/SchemaTree.tsx b/src/containers/Tenant/Schema/SchemaTree/SchemaTree.tsx index bc0c20655..c2242a0f7 100644 --- a/src/containers/Tenant/Schema/SchemaTree/SchemaTree.tsx +++ b/src/containers/Tenant/Schema/SchemaTree/SchemaTree.tsx @@ -8,7 +8,7 @@ import {NavigationTree} from 'ydb-ui-components'; import {USE_DIRECTORY_OPERATIONS} from '../../../../lib'; import {schemaApi} from '../../../../store/reducers/schema/schema'; import type {EPathType, TEvDescribeSchemeResult} from '../../../../types/api/schema'; -import {useQueryModes, useSetting, useTypedDispatch} from '../../../../utils/hooks'; +import {useQueryExecutionSettings, useSetting, useTypedDispatch} from '../../../../utils/hooks'; import {isChildlessPathType, mapPathTypeToNavigationTreeType} from '../../utils/schema'; import {getActions} from '../../utils/schemaActions'; import {getControls} from '../../utils/schemaControls'; @@ -27,7 +27,7 @@ export function SchemaTree(props: SchemaTreeProps) { const {rootPath, rootName, rootType, currentPath, onActivePathUpdate} = props; const dispatch = useTypedDispatch(); - const [_, setQueryMode] = useQueryModes(); + const [querySettings, setQueryExecutionSettings] = useQueryExecutionSettings(); const [createDirectoryOpen, setCreateDirectoryOpen] = React.useState(false); const [parentPath, setParentPath] = React.useState(''); const [schemaTreeKey, setSchemaTreeKey] = React.useState(''); @@ -112,7 +112,8 @@ export function SchemaTree(props: SchemaTreeProps) { fetchPath={fetchPath} getActions={getActions(dispatch, { setActivePath: onActivePathUpdate, - setQueryMode, + updateQueryExecutionSettings: (settings) => + setQueryExecutionSettings({...querySettings, ...settings}), showCreateDirectoryDialog: useDirectoryActions ? handleOpenCreateDirectoryDialog : undefined, diff --git a/src/containers/Tenant/utils/schemaActions.ts b/src/containers/Tenant/utils/schemaActions.ts index 35ccddb53..75db21f83 100644 --- a/src/containers/Tenant/utils/schemaActions.ts +++ b/src/containers/Tenant/utils/schemaActions.ts @@ -4,7 +4,7 @@ import type {NavigationTreeNodeType, NavigationTreeProps} from 'ydb-ui-component import {changeUserInput} from '../../../store/reducers/executeQuery'; import {TENANT_PAGES_IDS, TENANT_QUERY_TABS_ID} from '../../../store/reducers/tenant/constants'; import {setQueryTab, setTenantPage} from '../../../store/reducers/tenant/tenant'; -import type {QueryMode} from '../../../types/store/query'; +import type {QueryMode, QuerySettings} from '../../../types/store/query'; import createToast from '../../../utils/createToast'; import i18n from '../i18n'; @@ -27,7 +27,7 @@ import { } from './queryTemplates'; interface ActionsAdditionalEffects { - setQueryMode: (mode: QueryMode) => void; + updateQueryExecutionSettings: (settings?: Partial) => void; setActivePath: (path: string) => void; showCreateDirectoryDialog?: (path: string) => void; } @@ -37,11 +37,12 @@ const bindActions = ( dispatch: React.Dispatch, additionalEffects: ActionsAdditionalEffects, ) => { - const {setActivePath, setQueryMode, showCreateDirectoryDialog} = additionalEffects; + const {setActivePath, updateQueryExecutionSettings, showCreateDirectoryDialog} = + additionalEffects; const inputQuery = (tmpl: (path: string) => string, mode?: QueryMode) => () => { if (mode) { - setQueryMode(mode); + updateQueryExecutionSettings({queryMode: mode}); } dispatch(changeUserInput({input: tmpl(path)})); diff --git a/src/services/api.ts b/src/services/api.ts index 28c33de0e..3c0ca51bf 100644 --- a/src/services/api.ts +++ b/src/services/api.ts @@ -20,7 +20,16 @@ import type {TNetInfo} from '../types/api/netInfo'; import type {TNodesInfo} from '../types/api/nodes'; import type {TEvNodesInfo} from '../types/api/nodesList'; import type {TEvPDiskStateResponse, TPDiskInfoResponse} from '../types/api/pdisk'; -import type {Actions, ErrorResponse, QueryAPIResponse, Schemas} from '../types/api/query'; +import type { + Actions, + ErrorResponse, + QueryAPIResponse, + Schemas, + Stats, + Timeout, + TracingLevel, + TransactionMode, +} from '../types/api/query'; import type {JsonRenderRequestParams, JsonRenderResponse} from '../types/api/render'; import type {TEvDescribeSchemeResult} from '../types/api/schema'; import type {TStorageInfo} from '../types/api/storage'; @@ -427,23 +436,19 @@ export class YdbEmbeddedAPI extends AxiosWrapper { ); } sendQuery( - { - schema, - ...params - }: { + params: { query?: string; database?: string; action?: Action; - stats?: string; schema?: Schema; syntax?: QuerySyntax; + stats?: Stats; + tracingLevel?: TracingLevel; + transaction_mode?: TransactionMode; + timeout?: Timeout; }, {concurrentId, signal, withRetries}: AxiosOptions = {}, ) { - // Time difference to ensure that timeout from ui will be shown rather than backend error - const uiTimeout = 9 * 60 * 1000; - const backendTimeout = 10 * 60 * 1000; - /** * Return strings using base64 encoding. * @link https://github.com/ydb-platform/ydb/pull/647 @@ -454,20 +459,21 @@ export class YdbEmbeddedAPI extends AxiosWrapper { ); return this.post | ErrorResponse>( - this.getPath( - `/viewer/json/query?timeout=${backendTimeout}&base64=${base64}${ - schema ? `&schema=${schema}` : '' - }`, - ), - params, + this.getPath('/viewer/json/query'), + {...params, base64}, {}, { concurrentId, - timeout: uiTimeout, + timeout: params.timeout, requestConfig: { signal, 'axios-retry': {retries: withRetries ? this.DEFAULT_RETRIES_COUNT : 0}, }, + headers: params.tracingLevel + ? { + 'X-Trace-Verbosity': params.tracingLevel, + } + : undefined, }, ); } diff --git a/src/services/settings.ts b/src/services/settings.ts index a929d917d..37a9a9a50 100644 --- a/src/services/settings.ts +++ b/src/services/settings.ts @@ -4,14 +4,17 @@ import { AUTOCOMPLETE_ON_ENTER, AUTO_REFRESH_INTERVAL, BINARY_DATA_IN_PLAIN_TEXT_DISPLAY, + DEFAULT_QUERY_SETTINGS, ENABLE_AUTOCOMPLETE, INVERTED_DISKS_KEY, IS_HOTKEYS_HELP_HIDDEN_KEY, LANGUAGE_KEY, + LAST_QUERY_EXECUTION_SETTINGS_KEY, LAST_USED_QUERY_ACTION_KEY, PARTITIONS_HIDDEN_COLUMNS_KEY, - QUERY_INITIAL_MODE_KEY, + QUERY_EXECUTION_SETTINGS_KEY, QUERY_SETTINGS, + QUERY_SETTINGS_BANNER_LAST_CLOSED_KEY, QUERY_USE_MULTI_SCHEMA_KEY, SAVED_QUERIES_KEY, SHOW_DOMAIN_DATABASE_KEY, @@ -23,7 +26,7 @@ import { USE_PAGINATED_TABLES_KEY, USE_SEPARATE_DISKS_PAGES_KEY, } from '../utils/constants'; -import {QUERY_ACTIONS, QUERY_MODES} from '../utils/query'; +import {QUERY_ACTIONS} from '../utils/query'; import {parseJson} from '../utils/utils'; export type SettingsObject = Record; @@ -38,7 +41,6 @@ export const DEFAULT_USER_SETTINGS = { [BINARY_DATA_IN_PLAIN_TEXT_DISPLAY]: true, [SAVED_QUERIES_KEY]: [], [TENANT_INITIAL_PAGE_KEY]: TENANT_PAGES_IDS.query, - [QUERY_INITIAL_MODE_KEY]: QUERY_MODES.script, [LAST_USED_QUERY_ACTION_KEY]: QUERY_ACTIONS.execute, [ASIDE_HEADER_COMPACT_KEY]: true, [PARTITIONS_HIDDEN_COLUMNS_KEY]: [], @@ -52,6 +54,9 @@ export const DEFAULT_USER_SETTINGS = { [USE_DIRECTORY_OPERATIONS]: false, [QUERY_SETTINGS]: false, [SHOW_DOMAIN_DATABASE_KEY]: false, + [LAST_QUERY_EXECUTION_SETTINGS_KEY]: undefined, + [QUERY_SETTINGS_BANNER_LAST_CLOSED_KEY]: undefined, + [QUERY_EXECUTION_SETTINGS_KEY]: DEFAULT_QUERY_SETTINGS, } as const satisfies SettingsObject; class SettingsManager { diff --git a/src/store/reducers/executeQuery.ts b/src/store/reducers/executeQuery.ts index bb19f32ed..2c9bc25dc 100644 --- a/src/store/reducers/executeQuery.ts +++ b/src/store/reducers/executeQuery.ts @@ -1,6 +1,7 @@ import type {Reducer} from '@reduxjs/toolkit'; import {settingsManager} from '../../services/settings'; +import {TracingLevelNumber} from '../../types/api/query'; import type {ExecuteActions, Schemas} from '../../types/api/query'; import type { ExecuteQueryAction, @@ -12,6 +13,7 @@ import type { IQueryResult, QueryMode, QueryRequestParams, + QuerySettings, QuerySyntax, } from '../../types/store/query'; import {QUERIES_HISTORY_KEY} from '../../utils/constants'; @@ -21,6 +23,7 @@ import { isQueryErrorResponse, parseQueryAPIExecuteResponse, } from '../../utils/query'; +import {isNumeric} from '../../utils/utils'; import {createRequestActionTypes} from '../utils'; import {api} from './api'; @@ -139,22 +142,22 @@ const executeQuery: Reducer = ( }; interface SendQueryParams extends QueryRequestParams { - mode?: QueryMode; + querySettings?: Partial; schema?: Schemas; } export const executeQueryApi = api.injectEndpoints({ endpoints: (build) => ({ executeQuery: build.mutation({ - queryFn: async ({query, database, mode, schema = 'modern'}) => { + queryFn: async ({query, database, querySettings, schema = 'modern'}) => { let action: ExecuteActions = 'execute'; let syntax: QuerySyntax = QUERY_SYNTAX.yql; - if (mode === 'pg') { + if (querySettings?.queryMode === 'pg') { action = 'execute-query'; syntax = QUERY_SYNTAX.pg; - } else if (mode) { - action = `execute-${mode}`; + } else if (querySettings?.queryMode) { + action = `execute-${querySettings?.queryMode}`; } try { @@ -164,7 +167,14 @@ export const executeQueryApi = api.injectEndpoints({ database, action, syntax, - stats: 'full', + stats: querySettings?.statisticsMode, + tracingLevel: querySettings?.tracingLevel + ? TracingLevelNumber[querySettings?.tracingLevel] + : undefined, + transaction_mode: querySettings?.isolationLevel, + timeout: isNumeric(querySettings?.timeout) + ? Number(querySettings?.timeout) * 1000 + : undefined, }); if (isQueryErrorResponse(response)) { diff --git a/src/store/reducers/explainQuery/explainQuery.ts b/src/store/reducers/explainQuery/explainQuery.ts index 766234954..3078122b1 100644 --- a/src/store/reducers/explainQuery/explainQuery.ts +++ b/src/store/reducers/explainQuery/explainQuery.ts @@ -1,27 +1,29 @@ +import {TracingLevelNumber} from '../../../types/api/query'; import type {ExplainActions} from '../../../types/api/query'; -import type {QueryMode, QueryRequestParams, QuerySyntax} from '../../../types/store/query'; +import type {QueryRequestParams, QuerySettings, QuerySyntax} from '../../../types/store/query'; import {QUERY_SYNTAX, isQueryErrorResponse} from '../../../utils/query'; +import {isNumeric} from '../../../utils/utils'; import {api} from '../api'; import type {PreparedExplainResponse} from './types'; import {prepareExplainResponse} from './utils'; interface ExplainQueryParams extends QueryRequestParams { - mode?: QueryMode; + querySettings?: Partial; } export const explainQueryApi = api.injectEndpoints({ endpoints: (build) => ({ explainQuery: build.mutation({ - queryFn: async ({query, database, mode}) => { + queryFn: async ({query, database, querySettings}) => { let action: ExplainActions = 'explain'; let syntax: QuerySyntax = QUERY_SYNTAX.yql; - if (mode === 'pg') { + if (querySettings?.queryMode === 'pg') { action = 'explain-query'; syntax = QUERY_SYNTAX.pg; - } else if (mode) { - action = `explain-${mode}`; + } else if (querySettings?.queryMode) { + action = `explain-${querySettings?.queryMode}`; } try { @@ -30,6 +32,14 @@ export const explainQueryApi = api.injectEndpoints({ database, action, syntax, + stats: querySettings?.statisticsMode, + tracingLevel: querySettings?.tracingLevel + ? TracingLevelNumber[querySettings?.tracingLevel] + : undefined, + transaction_mode: querySettings?.isolationLevel, + timeout: isNumeric(querySettings?.timeout) + ? Number(querySettings?.timeout) * 1000 + : undefined, }); if (isQueryErrorResponse(response)) { diff --git a/src/types/api/query.ts b/src/types/api/query.ts index 155bb43b2..aca9a1d64 100644 --- a/src/types/api/query.ts +++ b/src/types/api/query.ts @@ -1,3 +1,6 @@ +import {TRACING_LEVELS} from '../../utils/query'; +import type {IsolationLevel, StatisticsMode} from '../store/query'; + // ==== types from backend protos ==== interface Position { row?: number; @@ -200,6 +203,27 @@ export interface ColumnType { type: string; } +export const TracingLevelNumber = { + [TRACING_LEVELS.off]: 0, + [TRACING_LEVELS.toplevel]: 4, + [TRACING_LEVELS.basic]: 9, + [TRACING_LEVELS.detailed]: 13, + [TRACING_LEVELS.diagnostic]: 14, + [TRACING_LEVELS.trace]: 15, +}; + +/** undefined = 'none' */ +export type Stats = StatisticsMode; + +/** undefined = '60000' */ +export type Timeout = number; + +/** undefined = 'serializable-read-write' */ +export type TransactionMode = IsolationLevel; + +/** undefined = '15' */ +export type TracingLevel = number; + /** undefined = 'classic' */ export type Schemas = 'classic' | 'modern' | 'ydb' | 'multi' | undefined; diff --git a/src/types/store/query.ts b/src/types/store/query.ts index 7279b795b..8325f45cd 100644 --- a/src/types/store/query.ts +++ b/src/types/store/query.ts @@ -36,6 +36,14 @@ export interface QueryRequestParams { query: string; } +export interface QuerySettings { + queryMode: QueryMode; + isolationLevel: IsolationLevel; + timeout?: string; + statisticsMode?: StatisticsMode; + tracingLevel?: TracingLevel; +} + export type QueryErrorResponse = IResponseError; export type QueryError = NetworkError | QueryErrorResponse; diff --git a/src/utils/constants.ts b/src/utils/constants.ts index 99dcc0eb7..3c9a75701 100644 --- a/src/utils/constants.ts +++ b/src/utils/constants.ts @@ -2,6 +2,9 @@ import DataTable from '@gravity-ui/react-data-table'; import type {Settings} from '@gravity-ui/react-data-table'; import {EType} from '../types/api/tablet'; +import type {QuerySettings} from '../types/store/query'; + +import {ISOLATION_LEVELS, QUERY_MODES, STATISTICS_MODES, TRACING_LEVELS} from './query'; const SECOND = 1000; @@ -18,6 +21,7 @@ export const TERABYTE = 1_000_000_000_000; export const MINUTE_IN_SECONDS = 60; export const HOUR_IN_SECONDS = 60 * MINUTE_IN_SECONDS; export const DAY_IN_SECONDS = 24 * HOUR_IN_SECONDS; +export const WEEK_IN_SECONDS = 7 * DAY_IN_SECONDS; export const MS_IN_NANOSECONDS = 1000000; @@ -118,7 +122,18 @@ export const TENANT_OVERVIEW_TABLES_SETTINGS = { dynamicRender: false, } as const; -export const QUERY_INITIAL_MODE_KEY = 'query_initial_mode'; +export const DEFAULT_QUERY_SETTINGS: QuerySettings = { + queryMode: QUERY_MODES.script, + isolationLevel: ISOLATION_LEVELS.serializable, + timeout: '60', + statisticsMode: STATISTICS_MODES.none, + tracingLevel: TRACING_LEVELS.detailed, +}; + +export const QUERY_EXECUTION_SETTINGS_KEY = 'queryExecutionSettings'; +export const LAST_QUERY_EXECUTION_SETTINGS_KEY = 'last_query_execution_settings'; +export const QUERY_SETTINGS_BANNER_LAST_CLOSED_KEY = 'querySettingsBannerLastClosed'; + export const LAST_USED_QUERY_ACTION_KEY = 'last_used_query_action'; export const PARTITIONS_HIDDEN_COLUMNS_KEY = 'partitionsHiddenColumns'; diff --git a/src/utils/hooks/index.ts b/src/utils/hooks/index.ts index 1f951947b..0aa4f3ab0 100644 --- a/src/utils/hooks/index.ts +++ b/src/utils/hooks/index.ts @@ -1,7 +1,7 @@ export * from './useTypedSelector'; export * from './useTypedDispatch'; export * from './useSetting'; -export * from './useQueryModes'; +export * from './useQueryExecutionSettings'; export * from './useTableSort'; export * from './useSearchQuery'; export * from './useAutoRefreshInterval'; diff --git a/src/utils/hooks/useChangedQuerySettings.ts b/src/utils/hooks/useChangedQuerySettings.ts new file mode 100644 index 000000000..0e25c64c1 --- /dev/null +++ b/src/utils/hooks/useChangedQuerySettings.ts @@ -0,0 +1,89 @@ +import React from 'react'; + +import getChangedQueryExecutionSettings from '../../containers/Tenant/Query/QueryEditorControls/utils/getChangedQueryExecutionSettings'; +import getChangedQueryExecutionSettingsDescription from '../../containers/Tenant/Query/QueryEditorControls/utils/getChangedQueryExecutionSettingsDescription'; +import { + DEFAULT_QUERY_SETTINGS, + QUERY_SETTINGS_BANNER_LAST_CLOSED_KEY, + WEEK_IN_SECONDS, +} from '../constants'; + +import {useLastQueryExecutionSettings} from './useLastQueryExecutionSettings'; +import {useQueryExecutionSettings} from './useQueryExecutionSettings'; +import {useSetting} from './useSetting'; + +export const useChangedQuerySettings = () => { + const [bannerLastClosedTimestamp, setBannerLastClosedTimestamp] = useSetting< + number | undefined + >(QUERY_SETTINGS_BANNER_LAST_CLOSED_KEY); + const [lastQuerySettings] = useLastQueryExecutionSettings(); + const [currentQuerySettings] = useQueryExecutionSettings(); + + const changedLastExucutionSettings = React.useMemo( + () => + lastQuerySettings + ? getChangedQueryExecutionSettings(lastQuerySettings, DEFAULT_QUERY_SETTINGS) + : [], + [lastQuerySettings], + ); + + const changedCurrentSettings = React.useMemo( + () => + currentQuerySettings + ? getChangedQueryExecutionSettings(currentQuerySettings, DEFAULT_QUERY_SETTINGS) + : [], + [currentQuerySettings], + ); + + const hasChangedLastExucutionSettings = changedLastExucutionSettings.length > 0; + + const changedLastExecutionSettingsDescriptions = React.useMemo( + () => + lastQuerySettings + ? getChangedQueryExecutionSettingsDescription({ + currentSettings: lastQuerySettings, + defaultSettings: DEFAULT_QUERY_SETTINGS, + }) + : [], + [lastQuerySettings], + ); + + const changedCurrentSettingsDescriptions = React.useMemo( + () => + currentQuerySettings + ? getChangedQueryExecutionSettingsDescription({ + currentSettings: currentQuerySettings, + defaultSettings: DEFAULT_QUERY_SETTINGS, + }) + : [], + [currentQuerySettings], + ); + + const isClosedRecently = + bannerLastClosedTimestamp && + Date.now() - bannerLastClosedTimestamp < WEEK_IN_SECONDS * 1000; + + const isBannerShown = hasChangedLastExucutionSettings && !isClosedRecently; + const isIndicatorShown = hasChangedLastExucutionSettings && isClosedRecently; + + const closeBanner = React.useCallback(() => { + setBannerLastClosedTimestamp(Date.now()); + }, [setBannerLastClosedTimestamp]); + + const resetBanner = React.useCallback(() => { + setBannerLastClosedTimestamp(undefined); + }, [setBannerLastClosedTimestamp]); + + return { + isBannerShown, + isIndicatorShown, + closeBanner, + resetBanner, + + changedCurrentSettings, + changedCurrentSettingsDescriptions, + + changedLastExucutionSettings, + changedLastExecutionSettingsDescriptions, + }; +}; diff --git a/src/utils/hooks/useLastQueryExecutionSettings.ts b/src/utils/hooks/useLastQueryExecutionSettings.ts new file mode 100644 index 000000000..872441feb --- /dev/null +++ b/src/utils/hooks/useLastQueryExecutionSettings.ts @@ -0,0 +1,8 @@ +import type {QuerySettings} from '../../types/store/query'; +import {LAST_QUERY_EXECUTION_SETTINGS_KEY} from '../constants'; + +import {useSetting} from './useSetting'; + +export const useLastQueryExecutionSettings = () => { + return useSetting(LAST_QUERY_EXECUTION_SETTINGS_KEY); +}; diff --git a/src/utils/hooks/useQueryExecutionSettings.ts b/src/utils/hooks/useQueryExecutionSettings.ts new file mode 100644 index 000000000..107b529c5 --- /dev/null +++ b/src/utils/hooks/useQueryExecutionSettings.ts @@ -0,0 +1,8 @@ +import type {QuerySettings} from '../../types/store/query'; +import {QUERY_EXECUTION_SETTINGS_KEY} from '../constants'; + +import {useSetting} from './useSetting'; + +export const useQueryExecutionSettings = () => { + return useSetting(QUERY_EXECUTION_SETTINGS_KEY); +}; diff --git a/src/utils/hooks/useQueryModes.ts b/src/utils/hooks/useQueryModes.ts deleted file mode 100644 index 165c1a8d2..000000000 --- a/src/utils/hooks/useQueryModes.ts +++ /dev/null @@ -1,8 +0,0 @@ -import type {QueryMode} from '../../types/store/query'; -import {QUERY_INITIAL_MODE_KEY} from '../constants'; - -import {useSetting} from './useSetting'; - -export const useQueryModes = () => { - return useSetting(QUERY_INITIAL_MODE_KEY); -}; From c8a259cca4ce1fc12ff3b58d0fdcd2c9db7afa3f Mon Sep 17 00:00:00 2001 From: Anton Standrik Date: Wed, 31 Jul 2024 13:33:39 +0300 Subject: [PATCH 2/6] fix: align banner --- .../Tenant/Query/QuerySettingsBanner/QuerySettingsBanner.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/containers/Tenant/Query/QuerySettingsBanner/QuerySettingsBanner.tsx b/src/containers/Tenant/Query/QuerySettingsBanner/QuerySettingsBanner.tsx index e43f061c1..4ad1c330b 100644 --- a/src/containers/Tenant/Query/QuerySettingsBanner/QuerySettingsBanner.tsx +++ b/src/containers/Tenant/Query/QuerySettingsBanner/QuerySettingsBanner.tsx @@ -15,6 +15,7 @@ export function QuerySettingsBanner() { {i18n('banner.query-settings.message')} From db1750e07b4c807345d18c6d255d7ff07b8983a8 Mon Sep 17 00:00:00 2001 From: Anton Standrik Date: Wed, 31 Jul 2024 17:34:18 +0300 Subject: [PATCH 3/6] fix: align banner baseline --- .../Tenant/Query/QuerySettingsBanner/QuerySettingsBanner.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/containers/Tenant/Query/QuerySettingsBanner/QuerySettingsBanner.tsx b/src/containers/Tenant/Query/QuerySettingsBanner/QuerySettingsBanner.tsx index 4ad1c330b..648eef007 100644 --- a/src/containers/Tenant/Query/QuerySettingsBanner/QuerySettingsBanner.tsx +++ b/src/containers/Tenant/Query/QuerySettingsBanner/QuerySettingsBanner.tsx @@ -15,7 +15,7 @@ export function QuerySettingsBanner() { {i18n('banner.query-settings.message')} From c8ac3cd736a3226293c499bcf743725c95a06a82 Mon Sep 17 00:00:00 2001 From: Anton Standrik Date: Wed, 31 Jul 2024 18:47:50 +0300 Subject: [PATCH 4/6] fix: review fixes --- .../QueryExecutionStatus.scss | 7 - .../QueryExecutionStatus.tsx | 14 +- .../QuerySettingsDescription.scss | 8 + .../QuerySettingsDescription.tsx | 26 ++ .../QuerySettingsDescription/index.ts | 1 + .../QueryEditorControls.scss | 7 - .../QueryEditorControls.tsx | 14 +- ...dQueryExecutionSettingsDescription.test.ts | 44 ++- ...hangedQueryExecutionSettingsDescription.ts | 13 +- .../QuerySettingsBanner.scss | 7 - .../QuerySettingsBanner.tsx | 14 +- .../QuerySettingsDialog.tsx | 295 +++++++++--------- src/utils/hooks/useChangedQuerySettings.ts | 62 ++-- 13 files changed, 266 insertions(+), 246 deletions(-) create mode 100644 src/components/QuerySettingsDescription/QuerySettingsDescription.scss create mode 100644 src/components/QuerySettingsDescription/QuerySettingsDescription.tsx create mode 100644 src/components/QuerySettingsDescription/index.ts diff --git a/src/components/QueryExecutionStatus/QueryExecutionStatus.scss b/src/components/QueryExecutionStatus/QueryExecutionStatus.scss index 191c35916..6acf9cb1f 100644 --- a/src/components/QueryExecutionStatus/QueryExecutionStatus.scss +++ b/src/components/QueryExecutionStatus/QueryExecutionStatus.scss @@ -14,11 +14,4 @@ &__query-settings-icon { color: var(--g-color-text-hint); } - - &__message { - display: flex; - flex-wrap: wrap; - - white-space: pre; - } } diff --git a/src/components/QueryExecutionStatus/QueryExecutionStatus.tsx b/src/components/QueryExecutionStatus/QueryExecutionStatus.tsx index 861c8d887..f6a202ccb 100644 --- a/src/components/QueryExecutionStatus/QueryExecutionStatus.tsx +++ b/src/components/QueryExecutionStatus/QueryExecutionStatus.tsx @@ -8,6 +8,7 @@ import i18n from '../../containers/Tenant/Query/i18n'; import {QUERY_SETTINGS, useSetting} from '../../lib'; import {cn} from '../../utils/cn'; import {useChangedQuerySettings} from '../../utils/hooks/useChangedQuerySettings'; +import QuerySettingsDescription from '../QuerySettingsDescription/QuerySettingsDescription'; import './QueryExecutionStatus.scss'; @@ -30,15 +31,10 @@ const QuerySettingsIndicator = () => { - {i18n('banner.query-settings.message')} - {changedLastExecutionSettingsDescriptions.map((description, index, arr) => ( - - {description} - {index < arr.length - 1 ? ', ' : null} - - ))} -
+ } > diff --git a/src/components/QuerySettingsDescription/QuerySettingsDescription.scss b/src/components/QuerySettingsDescription/QuerySettingsDescription.scss new file mode 100644 index 000000000..89cecc6c9 --- /dev/null +++ b/src/components/QuerySettingsDescription/QuerySettingsDescription.scss @@ -0,0 +1,8 @@ +.ydb-query-settings-description { + &__message { + display: flex; + flex-wrap: wrap; + + white-space: pre; + } +} diff --git a/src/components/QuerySettingsDescription/QuerySettingsDescription.tsx b/src/components/QuerySettingsDescription/QuerySettingsDescription.tsx new file mode 100644 index 000000000..db42f1d18 --- /dev/null +++ b/src/components/QuerySettingsDescription/QuerySettingsDescription.tsx @@ -0,0 +1,26 @@ +import {cn} from '../../utils/cn'; + +import './QuerySettingsDescription.scss'; + +const b = cn('ydb-query-settings-description'); + +interface QuerySettingsDescriptionProps { + prefix: string; + querySettings: Record[]; +} + +const QuerySettingsDescription = ({querySettings, prefix}: QuerySettingsDescriptionProps) => { + return ( +
+ {prefix} + {Object.entries(querySettings).map(([key, value], index, arr) => ( + + {`${key}: ${value}`} + {index < arr.length - 1 ? ', ' : null} + + ))} +
+ ); +}; + +export default QuerySettingsDescription; diff --git a/src/components/QuerySettingsDescription/index.ts b/src/components/QuerySettingsDescription/index.ts new file mode 100644 index 000000000..bb0a5028c --- /dev/null +++ b/src/components/QuerySettingsDescription/index.ts @@ -0,0 +1 @@ +export * from './QuerySettingsDescription'; diff --git a/src/containers/Tenant/Query/QueryEditorControls/QueryEditorControls.scss b/src/containers/Tenant/Query/QueryEditorControls/QueryEditorControls.scss index 80ac6a8c9..4241d69f9 100644 --- a/src/containers/Tenant/Query/QueryEditorControls/QueryEditorControls.scss +++ b/src/containers/Tenant/Query/QueryEditorControls/QueryEditorControls.scss @@ -48,11 +48,4 @@ white-space: pre-wrap; } - - &__message { - display: flex; - flex-wrap: wrap; - - white-space: pre; - } } diff --git a/src/containers/Tenant/Query/QueryEditorControls/QueryEditorControls.tsx b/src/containers/Tenant/Query/QueryEditorControls/QueryEditorControls.tsx index 4fa3dcd13..42f2382d6 100644 --- a/src/containers/Tenant/Query/QueryEditorControls/QueryEditorControls.tsx +++ b/src/containers/Tenant/Query/QueryEditorControls/QueryEditorControls.tsx @@ -5,6 +5,7 @@ import type {ButtonView} from '@gravity-ui/uikit'; import {Button, DropdownMenu, Icon, Tooltip} from '@gravity-ui/uikit'; import {LabelWithPopover} from '../../../../components/LabelWithPopover'; +import QuerySettingsDescription from '../../../../components/QuerySettingsDescription/QuerySettingsDescription'; import {QUERY_SETTINGS, useSetting} from '../../../../lib'; import type {QueryAction, QueryMode, QuerySettings} from '../../../../types/store/query'; import {cn} from '../../../../utils/cn'; @@ -60,15 +61,10 @@ const SettingsButton = ({onClick, runIsLoading}: SettingsButtonProps) => { - {i18n('gear.tooltip')} - {changedCurrentSettingsDescriptions.map((description, index, arr) => ( - - {description} - {index < arr.length - 1 ? ', ' : null} - - ))} -
+ } openDelay={0} placement={['top-start']} diff --git a/src/containers/Tenant/Query/QueryEditorControls/utils/getChangedQueryExecutionSettingsDescription.test.ts b/src/containers/Tenant/Query/QueryEditorControls/utils/getChangedQueryExecutionSettingsDescription.test.ts index 98ed9d5ce..adb13aeb7 100644 --- a/src/containers/Tenant/Query/QueryEditorControls/utils/getChangedQueryExecutionSettingsDescription.test.ts +++ b/src/containers/Tenant/Query/QueryEditorControls/utils/getChangedQueryExecutionSettingsDescription.test.ts @@ -1,9 +1,13 @@ import type {QuerySettings} from '../../../../../types/store/query'; import { ISOLATION_LEVELS, + ISOLATION_LEVELS_TITLES, QUERY_MODES, + QUERY_MODES_TITLES, STATISTICS_MODES, + STATISTICS_MODES_TITLES, TRACING_LEVELS, + TRACING_LEVELS_TITLES, } from '../../../../../utils/query'; import {QUERY_SETTINGS_FIELD_SETTINGS} from '../../QuerySettingsDialog/constants'; @@ -19,12 +23,13 @@ const DEFAULT_QUERY_SETTINGS: QuerySettings = { describe('getChangedQueryExecutionSettingsDescription', () => { it('should return an empty array if no settings changed', () => { - const currentSettings: QuerySettings = DEFAULT_QUERY_SETTINGS; + const currentSettings: QuerySettings = {...DEFAULT_QUERY_SETTINGS}; const result = getChangedQueryExecutionSettingsDescription({ currentSettings, defaultSettings: DEFAULT_QUERY_SETTINGS, }); + expect(result).toEqual([]); }); @@ -39,9 +44,17 @@ describe('getChangedQueryExecutionSettingsDescription', () => { currentSettings, defaultSettings: DEFAULT_QUERY_SETTINGS, }); + expect(result).toEqual([ - `${QUERY_SETTINGS_FIELD_SETTINGS.queryMode.title}: ${QUERY_SETTINGS_FIELD_SETTINGS.queryMode.options.find((option) => option.value === QUERY_MODES.pg)?.content}`, - `${QUERY_SETTINGS_FIELD_SETTINGS.timeout.title}: 63`, + { + [QUERY_SETTINGS_FIELD_SETTINGS.queryMode.title]: + QUERY_SETTINGS_FIELD_SETTINGS.queryMode.options.find( + (option) => option.value === QUERY_MODES.pg, + )?.content, + }, + { + [QUERY_SETTINGS_FIELD_SETTINGS.timeout.title]: '63', + }, ]); }); @@ -58,12 +71,27 @@ describe('getChangedQueryExecutionSettingsDescription', () => { currentSettings, defaultSettings: DEFAULT_QUERY_SETTINGS, }); + expect(result).toEqual([ - `${QUERY_SETTINGS_FIELD_SETTINGS.queryMode.title}: ${QUERY_SETTINGS_FIELD_SETTINGS.queryMode.options.find((option) => option.value === QUERY_MODES.data)?.content}`, - `${QUERY_SETTINGS_FIELD_SETTINGS.isolationLevel.title}: ${QUERY_SETTINGS_FIELD_SETTINGS.isolationLevel.options.find((option) => option.value === ISOLATION_LEVELS.snapshot)?.content}`, - `${QUERY_SETTINGS_FIELD_SETTINGS.timeout.title}: 120`, - `${QUERY_SETTINGS_FIELD_SETTINGS.statisticsMode.title}: ${QUERY_SETTINGS_FIELD_SETTINGS.statisticsMode.options.find((option) => option.value === STATISTICS_MODES.profile)?.content}`, - `${QUERY_SETTINGS_FIELD_SETTINGS.tracingLevel.title}: ${QUERY_SETTINGS_FIELD_SETTINGS.tracingLevel.options.find((option) => option.value === TRACING_LEVELS.diagnostic)?.content}`, + { + [QUERY_SETTINGS_FIELD_SETTINGS.queryMode.title]: + QUERY_MODES_TITLES[QUERY_MODES.data], + }, + { + [QUERY_SETTINGS_FIELD_SETTINGS.isolationLevel.title]: + ISOLATION_LEVELS_TITLES[ISOLATION_LEVELS.snapshot], + }, + { + [QUERY_SETTINGS_FIELD_SETTINGS.timeout.title]: '120', + }, + { + [QUERY_SETTINGS_FIELD_SETTINGS.statisticsMode.title]: + STATISTICS_MODES_TITLES[STATISTICS_MODES.profile], + }, + { + [QUERY_SETTINGS_FIELD_SETTINGS.tracingLevel.title]: + TRACING_LEVELS_TITLES[TRACING_LEVELS.diagnostic], + }, ]); }); }); diff --git a/src/containers/Tenant/Query/QueryEditorControls/utils/getChangedQueryExecutionSettingsDescription.ts b/src/containers/Tenant/Query/QueryEditorControls/utils/getChangedQueryExecutionSettingsDescription.ts index b81cc6100..9738a45f1 100644 --- a/src/containers/Tenant/Query/QueryEditorControls/utils/getChangedQueryExecutionSettingsDescription.ts +++ b/src/containers/Tenant/Query/QueryEditorControls/utils/getChangedQueryExecutionSettingsDescription.ts @@ -9,20 +9,19 @@ export default function getChangedQueryExecutionSettingsDescription({ }: { currentSettings: QuerySettings; defaultSettings: QuerySettings; -}): string[] { +}): Record[] { const keys = getChangedQueryExecutionSettings(currentSettings, defaultSettings); return keys.map((key) => { const settings = QUERY_SETTINGS_FIELD_SETTINGS[key]; - const currentValue = currentSettings[key]; + const currentValue = currentSettings[key] as string; if ('options' in settings) { - const content = settings.options.find( - (option) => option.value === currentValue, - )?.content; + const content = settings.options.find((option) => option.value === currentValue) + ?.content as string; - return `${settings.title}: ${content}`; + return {[settings.title]: content}; } - return `${settings.title}: ${currentValue}`; + return {[settings.title]: currentValue}; }); } diff --git a/src/containers/Tenant/Query/QuerySettingsBanner/QuerySettingsBanner.scss b/src/containers/Tenant/Query/QuerySettingsBanner/QuerySettingsBanner.scss index e9b74aef0..a68b86c90 100644 --- a/src/containers/Tenant/Query/QuerySettingsBanner/QuerySettingsBanner.scss +++ b/src/containers/Tenant/Query/QuerySettingsBanner/QuerySettingsBanner.scss @@ -1,10 +1,3 @@ .ydb-query-settings-banner { margin: var(--g-spacing-1) var(--g-spacing-5); - - &__message { - display: flex; - flex-wrap: wrap; - - white-space: pre; - } } diff --git a/src/containers/Tenant/Query/QuerySettingsBanner/QuerySettingsBanner.tsx b/src/containers/Tenant/Query/QuerySettingsBanner/QuerySettingsBanner.tsx index 648eef007..3a73dcf73 100644 --- a/src/containers/Tenant/Query/QuerySettingsBanner/QuerySettingsBanner.tsx +++ b/src/containers/Tenant/Query/QuerySettingsBanner/QuerySettingsBanner.tsx @@ -1,5 +1,6 @@ import {Alert} from '@gravity-ui/uikit'; +import QuerySettingsDescription from '../../../../components/QuerySettingsDescription/QuerySettingsDescription'; import {cn} from '../../../../utils/cn'; import {useChangedQuerySettings} from '../../../../utils/hooks/useChangedQuerySettings'; import i18n from '../i18n'; @@ -17,15 +18,10 @@ export function QuerySettingsBanner() { theme="info" align="baseline" message={ -
- {i18n('banner.query-settings.message')} - {changedLastExecutionSettingsDescriptions.map((description, index, arr) => ( - - {description} - {index < arr.length - 1 ? ', ' : null} - - ))} -
+ } onClose={closeBanner} /> diff --git a/src/containers/Tenant/Query/QuerySettingsDialog/QuerySettingsDialog.tsx b/src/containers/Tenant/Query/QuerySettingsDialog/QuerySettingsDialog.tsx index 419796a11..fc9c21d46 100644 --- a/src/containers/Tenant/Query/QuerySettingsDialog/QuerySettingsDialog.tsx +++ b/src/containers/Tenant/Query/QuerySettingsDialog/QuerySettingsDialog.tsx @@ -27,28 +27,22 @@ export function QuerySettingsDialog() { const dispatch = useTypedDispatch(); const queryAction = useTypedSelector(selectQueryAction); const [querySettings, setQuerySettings] = useQueryExecutionSettings(); - const {control, handleSubmit, reset} = useForm({ - defaultValues: querySettings, - }); - - React.useEffect(() => { - reset(querySettings); - }, [querySettings, reset]); const onCloseDialog = React.useCallback(() => { dispatch(setQueryAction('idle')); - reset(); - }, [dispatch, reset]); + }, [dispatch]); const onSaveClick = React.useCallback( (data: QuerySettings) => { setQuerySettings(data); - reset(data); onCloseDialog(); }, - [onCloseDialog, reset, setQuerySettings], + [onCloseDialog, setQuerySettings], ); + // key to reinitialize dialog on open + const formKey = React.useMemo(() => JSON.stringify(querySettings), [querySettings]); + return ( -
- - - -
- ( - - )} - /> -
-
- - -
- ( - - - - {i18n('form.timeout.seconds')} - - - )} - /> -
-
- - -
- ( - - )} - /> -
-
- - -
- ( - - )} - /> -
-
- - -
- ( - +
+ ); +} + +interface QuerySettingsFormProps { + initialValues: QuerySettings; + onSubmit: (data: QuerySettings) => void; + onCancel: () => void; +} + +function QuerySettingsForm({initialValues, onSubmit, onCancel}: QuerySettingsFormProps) { + const {control, handleSubmit} = useForm({ + defaultValues: initialValues, + }); + + return ( + + + + +
+ ( + + )} + /> +
+
+ + +
+ ( + + - )} - /> + + {i18n('form.timeout.seconds')} + + + )} + /> +
+
+ + +
+ ( + + )} + /> +
+
+ + +
+ ( + + )} + /> +
+
+ + +
+ ( + + )} + /> +
+
+
+ ( +
+ + {i18n('docs')} + +
+ {buttonCancel} + {buttonApply}
- - - ( -
- - {i18n('docs')} - -
- {buttonCancel} - {buttonApply} -
-
- )} - /> - -
+
+ )} + /> + ); } diff --git a/src/utils/hooks/useChangedQuerySettings.ts b/src/utils/hooks/useChangedQuerySettings.ts index 0e25c64c1..4d94e44a7 100644 --- a/src/utils/hooks/useChangedQuerySettings.ts +++ b/src/utils/hooks/useChangedQuerySettings.ts @@ -1,5 +1,3 @@ -import React from 'react'; - import getChangedQueryExecutionSettings from '../../containers/Tenant/Query/QueryEditorControls/utils/getChangedQueryExecutionSettings'; import getChangedQueryExecutionSettingsDescription from '../../containers/Tenant/Query/QueryEditorControls/utils/getChangedQueryExecutionSettingsDescription'; import { @@ -19,45 +17,29 @@ export const useChangedQuerySettings = () => { const [lastQuerySettings] = useLastQueryExecutionSettings(); const [currentQuerySettings] = useQueryExecutionSettings(); - const changedLastExucutionSettings = React.useMemo( - () => - lastQuerySettings - ? getChangedQueryExecutionSettings(lastQuerySettings, DEFAULT_QUERY_SETTINGS) - : [], - [lastQuerySettings], - ); + const changedLastExucutionSettings = lastQuerySettings + ? getChangedQueryExecutionSettings(lastQuerySettings, DEFAULT_QUERY_SETTINGS) + : []; - const changedCurrentSettings = React.useMemo( - () => - currentQuerySettings - ? getChangedQueryExecutionSettings(currentQuerySettings, DEFAULT_QUERY_SETTINGS) - : [], - [currentQuerySettings], - ); + const changedCurrentSettings = currentQuerySettings + ? getChangedQueryExecutionSettings(currentQuerySettings, DEFAULT_QUERY_SETTINGS) + : []; const hasChangedLastExucutionSettings = changedLastExucutionSettings.length > 0; - const changedLastExecutionSettingsDescriptions = React.useMemo( - () => - lastQuerySettings - ? getChangedQueryExecutionSettingsDescription({ - currentSettings: lastQuerySettings, - defaultSettings: DEFAULT_QUERY_SETTINGS, - }) - : [], - [lastQuerySettings], - ); + const changedLastExecutionSettingsDescriptions = lastQuerySettings + ? getChangedQueryExecutionSettingsDescription({ + currentSettings: lastQuerySettings, + defaultSettings: DEFAULT_QUERY_SETTINGS, + }) + : []; - const changedCurrentSettingsDescriptions = React.useMemo( - () => - currentQuerySettings - ? getChangedQueryExecutionSettingsDescription({ - currentSettings: currentQuerySettings, - defaultSettings: DEFAULT_QUERY_SETTINGS, - }) - : [], - [currentQuerySettings], - ); + const changedCurrentSettingsDescriptions = currentQuerySettings + ? getChangedQueryExecutionSettingsDescription({ + currentSettings: currentQuerySettings, + defaultSettings: DEFAULT_QUERY_SETTINGS, + }) + : []; const isClosedRecently = bannerLastClosedTimestamp && @@ -66,13 +48,9 @@ export const useChangedQuerySettings = () => { const isBannerShown = hasChangedLastExucutionSettings && !isClosedRecently; const isIndicatorShown = hasChangedLastExucutionSettings && isClosedRecently; - const closeBanner = React.useCallback(() => { - setBannerLastClosedTimestamp(Date.now()); - }, [setBannerLastClosedTimestamp]); + const closeBanner = () => setBannerLastClosedTimestamp(Date.now()); - const resetBanner = React.useCallback(() => { - setBannerLastClosedTimestamp(undefined); - }, [setBannerLastClosedTimestamp]); + const resetBanner = () => setBannerLastClosedTimestamp(undefined); return { isBannerShown, From 3926c096881dd1bec28570131732d672988f72e8 Mon Sep 17 00:00:00 2001 From: Anton Standrik Date: Wed, 31 Jul 2024 19:01:39 +0300 Subject: [PATCH 5/6] fix: test --- .../QuerySettingsDescription.tsx | 2 +- ...dQueryExecutionSettingsDescription.test.ts | 66 ++++++++----------- ...hangedQueryExecutionSettingsDescription.ts | 14 ++-- src/utils/hooks/useChangedQuerySettings.ts | 4 +- 4 files changed, 40 insertions(+), 46 deletions(-) diff --git a/src/components/QuerySettingsDescription/QuerySettingsDescription.tsx b/src/components/QuerySettingsDescription/QuerySettingsDescription.tsx index db42f1d18..0ea81973d 100644 --- a/src/components/QuerySettingsDescription/QuerySettingsDescription.tsx +++ b/src/components/QuerySettingsDescription/QuerySettingsDescription.tsx @@ -6,7 +6,7 @@ const b = cn('ydb-query-settings-description'); interface QuerySettingsDescriptionProps { prefix: string; - querySettings: Record[]; + querySettings: Record; } const QuerySettingsDescription = ({querySettings, prefix}: QuerySettingsDescriptionProps) => { diff --git a/src/containers/Tenant/Query/QueryEditorControls/utils/getChangedQueryExecutionSettingsDescription.test.ts b/src/containers/Tenant/Query/QueryEditorControls/utils/getChangedQueryExecutionSettingsDescription.test.ts index adb13aeb7..cc2dc1f4e 100644 --- a/src/containers/Tenant/Query/QueryEditorControls/utils/getChangedQueryExecutionSettingsDescription.test.ts +++ b/src/containers/Tenant/Query/QueryEditorControls/utils/getChangedQueryExecutionSettingsDescription.test.ts @@ -1,13 +1,9 @@ import type {QuerySettings} from '../../../../../types/store/query'; import { ISOLATION_LEVELS, - ISOLATION_LEVELS_TITLES, QUERY_MODES, - QUERY_MODES_TITLES, STATISTICS_MODES, - STATISTICS_MODES_TITLES, TRACING_LEVELS, - TRACING_LEVELS_TITLES, } from '../../../../../utils/query'; import {QUERY_SETTINGS_FIELD_SETTINGS} from '../../QuerySettingsDialog/constants'; @@ -22,7 +18,7 @@ const DEFAULT_QUERY_SETTINGS: QuerySettings = { }; describe('getChangedQueryExecutionSettingsDescription', () => { - it('should return an empty array if no settings changed', () => { + it('should return an empty object if no settings changed', () => { const currentSettings: QuerySettings = {...DEFAULT_QUERY_SETTINGS}; const result = getChangedQueryExecutionSettingsDescription({ @@ -30,7 +26,7 @@ describe('getChangedQueryExecutionSettingsDescription', () => { defaultSettings: DEFAULT_QUERY_SETTINGS, }); - expect(result).toEqual([]); + expect(result).toEqual({}); }); it('should return the description for changed settings', () => { @@ -45,17 +41,13 @@ describe('getChangedQueryExecutionSettingsDescription', () => { defaultSettings: DEFAULT_QUERY_SETTINGS, }); - expect(result).toEqual([ - { - [QUERY_SETTINGS_FIELD_SETTINGS.queryMode.title]: - QUERY_SETTINGS_FIELD_SETTINGS.queryMode.options.find( - (option) => option.value === QUERY_MODES.pg, - )?.content, - }, - { - [QUERY_SETTINGS_FIELD_SETTINGS.timeout.title]: '63', - }, - ]); + expect(result).toEqual({ + [QUERY_SETTINGS_FIELD_SETTINGS.queryMode.title]: + QUERY_SETTINGS_FIELD_SETTINGS.queryMode.options.find( + (option) => option.value === QUERY_MODES.pg, + )?.content, + [QUERY_SETTINGS_FIELD_SETTINGS.timeout.title]: '63', + }); }); it('should return the correct description for all changed settings', () => { @@ -72,26 +64,24 @@ describe('getChangedQueryExecutionSettingsDescription', () => { defaultSettings: DEFAULT_QUERY_SETTINGS, }); - expect(result).toEqual([ - { - [QUERY_SETTINGS_FIELD_SETTINGS.queryMode.title]: - QUERY_MODES_TITLES[QUERY_MODES.data], - }, - { - [QUERY_SETTINGS_FIELD_SETTINGS.isolationLevel.title]: - ISOLATION_LEVELS_TITLES[ISOLATION_LEVELS.snapshot], - }, - { - [QUERY_SETTINGS_FIELD_SETTINGS.timeout.title]: '120', - }, - { - [QUERY_SETTINGS_FIELD_SETTINGS.statisticsMode.title]: - STATISTICS_MODES_TITLES[STATISTICS_MODES.profile], - }, - { - [QUERY_SETTINGS_FIELD_SETTINGS.tracingLevel.title]: - TRACING_LEVELS_TITLES[TRACING_LEVELS.diagnostic], - }, - ]); + expect(result).toEqual({ + [QUERY_SETTINGS_FIELD_SETTINGS.queryMode.title]: + QUERY_SETTINGS_FIELD_SETTINGS.queryMode.options.find( + (option) => option.value === QUERY_MODES.data, + )?.content, + [QUERY_SETTINGS_FIELD_SETTINGS.isolationLevel.title]: + QUERY_SETTINGS_FIELD_SETTINGS.isolationLevel.options.find( + (option) => option.value === ISOLATION_LEVELS.snapshot, + )?.content, + [QUERY_SETTINGS_FIELD_SETTINGS.timeout.title]: '120', + [QUERY_SETTINGS_FIELD_SETTINGS.statisticsMode.title]: + QUERY_SETTINGS_FIELD_SETTINGS.statisticsMode.options.find( + (option) => option.value === STATISTICS_MODES.profile, + )?.content, + [QUERY_SETTINGS_FIELD_SETTINGS.tracingLevel.title]: + QUERY_SETTINGS_FIELD_SETTINGS.tracingLevel.options.find( + (option) => option.value === TRACING_LEVELS.diagnostic, + )?.content, + }); }); }); diff --git a/src/containers/Tenant/Query/QueryEditorControls/utils/getChangedQueryExecutionSettingsDescription.ts b/src/containers/Tenant/Query/QueryEditorControls/utils/getChangedQueryExecutionSettingsDescription.ts index 9738a45f1..cbd97c29b 100644 --- a/src/containers/Tenant/Query/QueryEditorControls/utils/getChangedQueryExecutionSettingsDescription.ts +++ b/src/containers/Tenant/Query/QueryEditorControls/utils/getChangedQueryExecutionSettingsDescription.ts @@ -9,9 +9,11 @@ export default function getChangedQueryExecutionSettingsDescription({ }: { currentSettings: QuerySettings; defaultSettings: QuerySettings; -}): Record[] { +}): Record { const keys = getChangedQueryExecutionSettings(currentSettings, defaultSettings); - return keys.map((key) => { + const result: Record = {}; + + keys.forEach((key) => { const settings = QUERY_SETTINGS_FIELD_SETTINGS[key]; const currentValue = currentSettings[key] as string; @@ -19,9 +21,11 @@ export default function getChangedQueryExecutionSettingsDescription({ const content = settings.options.find((option) => option.value === currentValue) ?.content as string; - return {[settings.title]: content}; + result[settings.title] = content; + } else { + result[settings.title] = currentValue; } - - return {[settings.title]: currentValue}; }); + + return result; } diff --git a/src/utils/hooks/useChangedQuerySettings.ts b/src/utils/hooks/useChangedQuerySettings.ts index 4d94e44a7..869fc0814 100644 --- a/src/utils/hooks/useChangedQuerySettings.ts +++ b/src/utils/hooks/useChangedQuerySettings.ts @@ -32,14 +32,14 @@ export const useChangedQuerySettings = () => { currentSettings: lastQuerySettings, defaultSettings: DEFAULT_QUERY_SETTINGS, }) - : []; + : {}; const changedCurrentSettingsDescriptions = currentQuerySettings ? getChangedQueryExecutionSettingsDescription({ currentSettings: currentQuerySettings, defaultSettings: DEFAULT_QUERY_SETTINGS, }) - : []; + : {}; const isClosedRecently = bannerLastClosedTimestamp && From 724eb77899f3bfae2350867d0a1e55e0de5cfc83 Mon Sep 17 00:00:00 2001 From: Anton Standrik Date: Wed, 31 Jul 2024 19:36:29 +0300 Subject: [PATCH 6/6] fix: useEffect --- ...dQueryExecutionSettingsDescription.test.ts | 23 +++++--------- .../QuerySettingsDialog.tsx | 30 +++++++++---------- 2 files changed, 23 insertions(+), 30 deletions(-) diff --git a/src/containers/Tenant/Query/QueryEditorControls/utils/getChangedQueryExecutionSettingsDescription.test.ts b/src/containers/Tenant/Query/QueryEditorControls/utils/getChangedQueryExecutionSettingsDescription.test.ts index cc2dc1f4e..956f5575a 100644 --- a/src/containers/Tenant/Query/QueryEditorControls/utils/getChangedQueryExecutionSettingsDescription.test.ts +++ b/src/containers/Tenant/Query/QueryEditorControls/utils/getChangedQueryExecutionSettingsDescription.test.ts @@ -1,9 +1,13 @@ import type {QuerySettings} from '../../../../../types/store/query'; import { ISOLATION_LEVELS, + ISOLATION_LEVELS_TITLES, QUERY_MODES, + QUERY_MODES_TITLES, STATISTICS_MODES, + STATISTICS_MODES_TITLES, TRACING_LEVELS, + TRACING_LEVELS_TITLES, } from '../../../../../utils/query'; import {QUERY_SETTINGS_FIELD_SETTINGS} from '../../QuerySettingsDialog/constants'; @@ -65,23 +69,12 @@ describe('getChangedQueryExecutionSettingsDescription', () => { }); expect(result).toEqual({ - [QUERY_SETTINGS_FIELD_SETTINGS.queryMode.title]: - QUERY_SETTINGS_FIELD_SETTINGS.queryMode.options.find( - (option) => option.value === QUERY_MODES.data, - )?.content, + [QUERY_SETTINGS_FIELD_SETTINGS.queryMode.title]: QUERY_MODES_TITLES.data, [QUERY_SETTINGS_FIELD_SETTINGS.isolationLevel.title]: - QUERY_SETTINGS_FIELD_SETTINGS.isolationLevel.options.find( - (option) => option.value === ISOLATION_LEVELS.snapshot, - )?.content, + ISOLATION_LEVELS_TITLES['snapshot-read-only'], [QUERY_SETTINGS_FIELD_SETTINGS.timeout.title]: '120', - [QUERY_SETTINGS_FIELD_SETTINGS.statisticsMode.title]: - QUERY_SETTINGS_FIELD_SETTINGS.statisticsMode.options.find( - (option) => option.value === STATISTICS_MODES.profile, - )?.content, - [QUERY_SETTINGS_FIELD_SETTINGS.tracingLevel.title]: - QUERY_SETTINGS_FIELD_SETTINGS.tracingLevel.options.find( - (option) => option.value === TRACING_LEVELS.diagnostic, - )?.content, + [QUERY_SETTINGS_FIELD_SETTINGS.statisticsMode.title]: STATISTICS_MODES_TITLES.profile, + [QUERY_SETTINGS_FIELD_SETTINGS.tracingLevel.title]: TRACING_LEVELS_TITLES.diagnostic, }); }); }); diff --git a/src/containers/Tenant/Query/QuerySettingsDialog/QuerySettingsDialog.tsx b/src/containers/Tenant/Query/QuerySettingsDialog/QuerySettingsDialog.tsx index fc9c21d46..4af9fe789 100644 --- a/src/containers/Tenant/Query/QuerySettingsDialog/QuerySettingsDialog.tsx +++ b/src/containers/Tenant/Query/QuerySettingsDialog/QuerySettingsDialog.tsx @@ -28,35 +28,31 @@ export function QuerySettingsDialog() { const queryAction = useTypedSelector(selectQueryAction); const [querySettings, setQuerySettings] = useQueryExecutionSettings(); - const onCloseDialog = React.useCallback(() => { + const onClose = React.useCallback(() => { dispatch(setQueryAction('idle')); }, [dispatch]); - const onSaveClick = React.useCallback( + const onSubmit = React.useCallback( (data: QuerySettings) => { setQuerySettings(data); - onCloseDialog(); + onClose(); }, - [onCloseDialog, setQuerySettings], + [onClose, setQuerySettings], ); - // key to reinitialize dialog on open - const formKey = React.useMemo(() => JSON.stringify(querySettings), [querySettings]); - return ( ); @@ -65,14 +61,18 @@ export function QuerySettingsDialog() { interface QuerySettingsFormProps { initialValues: QuerySettings; onSubmit: (data: QuerySettings) => void; - onCancel: () => void; + onClose: () => void; } -function QuerySettingsForm({initialValues, onSubmit, onCancel}: QuerySettingsFormProps) { - const {control, handleSubmit} = useForm({ +function QuerySettingsForm({initialValues, onSubmit, onClose}: QuerySettingsFormProps) { + const {control, handleSubmit, reset} = useForm({ defaultValues: initialValues, }); + React.useEffect(() => { + reset(initialValues); + }, [initialValues, reset]); + return (
@@ -182,7 +182,7 @@ function QuerySettingsForm({initialValues, onSubmit, onCancel}: QuerySettingsFor