diff --git a/src/containers/Tenant/Diagnostics/TenantOverview/TenantCpu/TopQueries.tsx b/src/containers/Tenant/Diagnostics/TenantOverview/TenantCpu/TopQueries.tsx index be722ce39..0761e8a21 100644 --- a/src/containers/Tenant/Diagnostics/TenantOverview/TenantCpu/TopQueries.tsx +++ b/src/containers/Tenant/Diagnostics/TenantOverview/TenantCpu/TopQueries.tsx @@ -3,11 +3,11 @@ import React from 'react'; import {useHistory, useLocation} from 'react-router-dom'; import {parseQuery} from '../../../../../routes'; -import {changeUserInput} from '../../../../../store/reducers/executeQuery'; import { setTopQueriesFilters, topQueriesApi, } from '../../../../../store/reducers/executeTopQueries/executeTopQueries'; +import {changeUserInput} from '../../../../../store/reducers/query/query'; import { TENANT_DIAGNOSTICS_TABS_IDS, TENANT_PAGE, diff --git a/src/containers/Tenant/Diagnostics/TopQueries/TopQueries.tsx b/src/containers/Tenant/Diagnostics/TopQueries/TopQueries.tsx index 9d5fd19ad..3d1efe1fb 100644 --- a/src/containers/Tenant/Diagnostics/TopQueries/TopQueries.tsx +++ b/src/containers/Tenant/Diagnostics/TopQueries/TopQueries.tsx @@ -11,8 +11,8 @@ import {DateRange} from '../../../../components/DateRange'; import {Search} from '../../../../components/Search'; import {TableWithControlsLayout} from '../../../../components/TableWithControlsLayout/TableWithControlsLayout'; import {parseQuery} from '../../../../routes'; -import {changeUserInput} from '../../../../store/reducers/executeQuery'; import {setTopQueriesFilters} from '../../../../store/reducers/executeTopQueries/executeTopQueries'; +import {changeUserInput} from '../../../../store/reducers/query/query'; import { TENANT_PAGE, TENANT_PAGES_IDS, diff --git a/src/containers/Tenant/ObjectSummary/SchemaTree/SchemaTree.tsx b/src/containers/Tenant/ObjectSummary/SchemaTree/SchemaTree.tsx index 5a7214a70..c2416eb71 100644 --- a/src/containers/Tenant/ObjectSummary/SchemaTree/SchemaTree.tsx +++ b/src/containers/Tenant/ObjectSummary/SchemaTree/SchemaTree.tsx @@ -6,7 +6,7 @@ import React from 'react'; import {NavigationTree} from 'ydb-ui-components'; import {useCreateDirectoryFeatureAvailable} from '../../../../store/reducers/capabilities/hooks'; -import {selectUserInput} from '../../../../store/reducers/executeQuery'; +import {selectUserInput} from '../../../../store/reducers/query/query'; import {schemaApi} from '../../../../store/reducers/schema/schema'; import {tableSchemaDataApi} from '../../../../store/reducers/tableSchemaData'; import type {GetTableSchemaDataParams} from '../../../../store/reducers/tableSchemaData'; diff --git a/src/containers/Tenant/Query/ExecuteResult/ExecuteResult.tsx b/src/containers/Tenant/Query/ExecuteResult/ExecuteResult.tsx index a4a87a639..8ac6e4a50 100644 --- a/src/containers/Tenant/Query/ExecuteResult/ExecuteResult.tsx +++ b/src/containers/Tenant/Query/ExecuteResult/ExecuteResult.tsx @@ -13,9 +13,9 @@ import {LoaderWrapper} from '../../../../components/LoaderWrapper/LoaderWrapper' import {QueryExecutionStatus} from '../../../../components/QueryExecutionStatus'; import {QueryResultTable} from '../../../../components/QueryResultTable/QueryResultTable'; import {disableFullscreen} from '../../../../store/reducers/fullscreen'; +import type {QueryResult} from '../../../../store/reducers/query/types'; import type {TKqpStatsQuery} from '../../../../types/api/query'; import type {ValueOf} from '../../../../types/common'; -import type {ExecuteQueryResult} from '../../../../types/store/executeQuery'; import {getArray} from '../../../../utils'; import {cn} from '../../../../utils/cn'; import {USE_SHOW_PLAN_SVG_KEY} from '../../../../utils/constants'; @@ -50,7 +50,7 @@ const resultOptionsIds = { type SectionID = ValueOf; interface ExecuteResultProps { - result: ExecuteQueryResult; + result: QueryResult; isResultsCollapsed?: boolean; theme?: string; tenantName: string; diff --git a/src/containers/Tenant/Query/ExecuteResult/utils.ts b/src/containers/Tenant/Query/ExecuteResult/utils.ts index dfd7a787b..fd5ec2b02 100644 --- a/src/containers/Tenant/Query/ExecuteResult/utils.ts +++ b/src/containers/Tenant/Query/ExecuteResult/utils.ts @@ -1,4 +1,4 @@ -import {explainVersions} from '../../../../store/reducers/explainQuery/utils'; +import {explainVersions} from '../../../../store/reducers/query/prepareQueryData'; import type {IQueryResult} from '../../../../types/store/query'; import {preparePlan, prepareSimplifiedPlan} from '../../../../utils/prepareQueryExplain'; import {parseQueryExplainPlan} from '../../../../utils/query'; diff --git a/src/containers/Tenant/Query/ExplainResult/ExplainResult.tsx b/src/containers/Tenant/Query/ExplainResult/ExplainResult.tsx index f9dd488f1..4e18948ee 100644 --- a/src/containers/Tenant/Query/ExplainResult/ExplainResult.tsx +++ b/src/containers/Tenant/Query/ExplainResult/ExplainResult.tsx @@ -9,8 +9,8 @@ import Fullscreen from '../../../../components/Fullscreen/Fullscreen'; import {LoaderWrapper} from '../../../../components/LoaderWrapper/LoaderWrapper'; import {QueryExecutionStatus} from '../../../../components/QueryExecutionStatus'; import {disableFullscreen} from '../../../../store/reducers/fullscreen'; +import type {QueryResult} from '../../../../store/reducers/query/types'; import type {ValueOf} from '../../../../types/common'; -import type {ExplainQueryResult} from '../../../../types/store/executeQuery'; import {cn} from '../../../../utils/cn'; import {getStringifiedData} from '../../../../utils/dataFormatters/dataFormatters'; import {useTypedDispatch} from '../../../../utils/hooks'; @@ -59,7 +59,7 @@ const explainOptions = [ interface ExplainResultProps { theme: string; - result: ExplainQueryResult; + result: QueryResult; tenantName: string; isResultsCollapsed?: boolean; onCollapseResults: VoidFunction; @@ -81,7 +81,7 @@ export function ExplainResult({ const [isPending, startTransition] = React.useTransition(); const {error, isLoading, queryId} = result; - const {plan: explain, ast, simplifiedPlan} = result.data || {}; + const {preparedPlan: explain, ast, simplifiedPlan} = result.data || {}; React.useEffect(() => { return () => { diff --git a/src/containers/Tenant/Query/ExplainResult/components/Graph/Graph.tsx b/src/containers/Tenant/Query/ExplainResult/components/Graph/Graph.tsx index 3ebfb05be..645f30ea2 100644 --- a/src/containers/Tenant/Query/ExplainResult/components/Graph/Graph.tsx +++ b/src/containers/Tenant/Query/ExplainResult/components/Graph/Graph.tsx @@ -1,6 +1,6 @@ import {YDBGraph} from '../../../../../../components/Graph/Graph'; -import type {PreparedExplainResponse} from '../../../../../../store/reducers/explainQuery/types'; -import {explainVersions} from '../../../../../../store/reducers/explainQuery/utils'; +import {explainVersions} from '../../../../../../store/reducers/query/prepareQueryData'; +import type {PreparedPlan} from '../../../../../../store/reducers/query/types'; import {cn} from '../../../../../../utils/cn'; import i18n from '../../i18n'; @@ -9,7 +9,7 @@ import './Graph.scss'; const b = cn('ydb-query-explain-graph'); interface GraphProps { - explain: PreparedExplainResponse['plan']; + explain: PreparedPlan; theme: string; } diff --git a/src/containers/Tenant/Query/ExplainResult/components/SimplifiedPlan/SimplifiedPlan.tsx b/src/containers/Tenant/Query/ExplainResult/components/SimplifiedPlan/SimplifiedPlan.tsx index 2ab729d61..aca60ad28 100644 --- a/src/containers/Tenant/Query/ExplainResult/components/SimplifiedPlan/SimplifiedPlan.tsx +++ b/src/containers/Tenant/Query/ExplainResult/components/SimplifiedPlan/SimplifiedPlan.tsx @@ -3,7 +3,7 @@ import React from 'react'; import {Table, useTable} from '@gravity-ui/table'; import type {CellContext, ColumnDef, ExpandedState} from '@tanstack/react-table'; -import type {SimplifiedPlanItem} from '../../../../../../store/reducers/explainQuery/types'; +import type {SimplifiedPlanItem} from '../../../../../../store/reducers/query/types'; import {configuredNumeral} from '../../../../../../utils/numeral'; import {formatToMs} from '../../../../../../utils/timeParsers'; import {toExponential} from '../../../../../../utils/utils'; diff --git a/src/containers/Tenant/Query/ExplainResult/components/SimplifiedPlan/__tests__/utils.test.ts b/src/containers/Tenant/Query/ExplainResult/components/SimplifiedPlan/__tests__/utils.test.ts index 43079b3dc..15ad0502d 100644 --- a/src/containers/Tenant/Query/ExplainResult/components/SimplifiedPlan/__tests__/utils.test.ts +++ b/src/containers/Tenant/Query/ExplainResult/components/SimplifiedPlan/__tests__/utils.test.ts @@ -1,4 +1,4 @@ -import type {SimplifiedPlanItem} from '../../../../../../../store/reducers/explainQuery/types'; +import type {SimplifiedPlanItem} from '../../../../../../../store/reducers/query/types'; import type {ExtendedSimplifiesPlanItem} from '../types'; import {getExtendedTreeNodes} from '../utils'; diff --git a/src/containers/Tenant/Query/ExplainResult/components/SimplifiedPlan/types.ts b/src/containers/Tenant/Query/ExplainResult/components/SimplifiedPlan/types.ts index 9426d2e39..9de2b9c0c 100644 --- a/src/containers/Tenant/Query/ExplainResult/components/SimplifiedPlan/types.ts +++ b/src/containers/Tenant/Query/ExplainResult/components/SimplifiedPlan/types.ts @@ -1,4 +1,4 @@ -import type {SimplifiedPlanItem} from '../../../../../../store/reducers/explainQuery/types'; +import type {SimplifiedPlanItem} from '../../../../../../store/reducers/query/types'; export interface ExtendedSimplifiesPlanItem extends Omit { lines?: string; diff --git a/src/containers/Tenant/Query/ExplainResult/components/SimplifiedPlan/utils.ts b/src/containers/Tenant/Query/ExplainResult/components/SimplifiedPlan/utils.ts index b79f31605..399709ff0 100644 --- a/src/containers/Tenant/Query/ExplainResult/components/SimplifiedPlan/utils.ts +++ b/src/containers/Tenant/Query/ExplainResult/components/SimplifiedPlan/utils.ts @@ -1,4 +1,4 @@ -import type {SimplifiedPlanItem} from '../../../../../../store/reducers/explainQuery/types'; +import type {SimplifiedPlanItem} from '../../../../../../store/reducers/query/types'; import {cn} from '../../../../../../utils/cn'; import type {ExtendedSimplifiesPlanItem} from './types'; diff --git a/src/containers/Tenant/Query/QueriesHistory/QueriesHistory.tsx b/src/containers/Tenant/Query/QueriesHistory/QueriesHistory.tsx index fdf7d3271..5db6bc186 100644 --- a/src/containers/Tenant/Query/QueriesHistory/QueriesHistory.tsx +++ b/src/containers/Tenant/Query/QueriesHistory/QueriesHistory.tsx @@ -8,10 +8,10 @@ import { selectQueriesHistory, selectQueriesHistoryFilter, setQueryHistoryFilter, -} from '../../../../store/reducers/executeQuery'; +} from '../../../../store/reducers/query/query'; +import type {QueryInHistory} from '../../../../store/reducers/query/types'; 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 {formatDateTime} from '../../../../utils/dataFormatters/dataFormatters'; import {useTypedDispatch, useTypedSelector} from '../../../../utils/hooks'; diff --git a/src/containers/Tenant/Query/Query.tsx b/src/containers/Tenant/Query/Query.tsx index 1e78eae8e..549fa31f6 100644 --- a/src/containers/Tenant/Query/Query.tsx +++ b/src/containers/Tenant/Query/Query.tsx @@ -2,7 +2,7 @@ import React from 'react'; import {Helmet} from 'react-helmet-async'; -import {changeUserInput} from '../../../store/reducers/executeQuery'; +import {changeUserInput} from '../../../store/reducers/query/query'; import {TENANT_QUERY_TABS_ID} from '../../../store/reducers/tenant/constants'; import type {EPathType} from '../../../types/api/schema'; import {cn} from '../../../utils/cn'; diff --git a/src/containers/Tenant/Query/QueryEditor/QueryEditor.tsx b/src/containers/Tenant/Query/QueryEditor/QueryEditor.tsx index 289a351da..ec87c4422 100644 --- a/src/containers/Tenant/Query/QueryEditor/QueryEditor.tsx +++ b/src/containers/Tenant/Query/QueryEditor/QueryEditor.tsx @@ -10,9 +10,9 @@ import {MonacoEditor} from '../../../../components/MonacoEditor/MonacoEditor'; import SplitPane from '../../../../components/SplitPane'; import {useTracingLevelOptionAvailable} from '../../../../store/reducers/capabilities/hooks'; import { - executeQueryApi, goToNextQuery, goToPreviousQuery, + queryApi, saveQueryToHistory, selectQueriesHistory, selectQueriesHistoryCurrentIndex, @@ -20,13 +20,11 @@ import { selectTenantPath, selectUserInput, setTenantPath, -} from '../../../../store/reducers/executeQuery'; -import {explainQueryApi} from '../../../../store/reducers/explainQuery/explainQuery'; +} from '../../../../store/reducers/query/query'; +import type {QueryResult} from '../../../../store/reducers/query/types'; import {setQueryAction} from '../../../../store/reducers/queryActions/queryActions'; import {selectShowPreview, setShowPreview} from '../../../../store/reducers/schema/schema'; import type {EPathType} from '../../../../types/api/schema'; -import {ResultType} from '../../../../types/store/executeQuery'; -import type {QueryResult} from '../../../../types/store/executeQuery'; import type {QueryAction} from '../../../../types/store/query'; import {cn} from '../../../../utils/cn'; import { @@ -104,8 +102,7 @@ export default function QueryEditor(props: QueryEditorProps) { LAST_USED_QUERY_ACTION_KEY, ); - const [sendExecuteQuery] = executeQueryApi.useExecuteQueryMutation(); - const [sendExplainQuery] = explainQueryApi.useExplainQueryMutation(); + const [sendQuery] = queryApi.useUseSendQueryMutation(); React.useEffect(() => { if (savedPath !== tenantName) { @@ -147,7 +144,8 @@ export default function QueryEditor(props: QueryEditorProps) { } const queryId = uuidv4(); - sendExecuteQuery({ + sendQuery({ + actionType: 'execute', query, database: tenantName, querySettings, @@ -180,7 +178,8 @@ export default function QueryEditor(props: QueryEditorProps) { const queryId = uuidv4(); - sendExplainQuery({ + sendQuery({ + actionType: 'explain', query: input, database: tenantName, querySettings, @@ -400,7 +399,7 @@ function Result({ return ; } - if (result?.type === ResultType.EXECUTE) { + if (result?.type === 'execute') { return ( { - const parsedData = parseQueryAPIExecuteResponse(data).resultSets?.[0]?.result; + const parsedData = parseQueryAPIResponse(data).resultSets?.[0]?.result; return getGroupStats(parsedData); }; diff --git a/src/store/reducers/executeTopQueries/executeTopQueries.ts b/src/store/reducers/executeTopQueries/executeTopQueries.ts index b764eb173..05fa9a195 100644 --- a/src/store/reducers/executeTopQueries/executeTopQueries.ts +++ b/src/store/reducers/executeTopQueries/executeTopQueries.ts @@ -2,7 +2,7 @@ import {isLikeRelative} from '@gravity-ui/date-utils'; import {createSlice} from '@reduxjs/toolkit'; import type {PayloadAction} from '@reduxjs/toolkit'; -import {isQueryErrorResponse, parseQueryAPIExecuteResponse} from '../../../utils/query'; +import {isQueryErrorResponse, parseQueryAPIResponse} from '../../../utils/query'; import {api} from '../api'; import type {TopQueriesFilters} from './types'; @@ -70,7 +70,7 @@ export const topQueriesApi = api.injectEndpoints({ throw response; } - const data = parseQueryAPIExecuteResponse(response); + const data = parseQueryAPIResponse(response); return {data}; } catch (error) { return {error}; @@ -123,7 +123,7 @@ export const topQueriesApi = api.injectEndpoints({ throw response; } - const data = parseQueryAPIExecuteResponse(response); + const data = parseQueryAPIResponse(response); return {data}; } catch (error) { diff --git a/src/store/reducers/explainQuery/explainQuery.ts b/src/store/reducers/explainQuery/explainQuery.ts deleted file mode 100644 index c0f35ab54..000000000 --- a/src/store/reducers/explainQuery/explainQuery.ts +++ /dev/null @@ -1,100 +0,0 @@ -import {TracingLevelNumber} from '../../../types/api/query'; -import type {ExplainActions} from '../../../types/api/query'; -import {ResultType} from '../../../types/store/executeQuery'; -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 {setQueryResult} from '../executeQuery'; - -import {prepareExplainResponse} from './utils'; - -interface ExplainQueryParams extends QueryRequestParams { - queryId: string; - querySettings?: Partial; - // flag whether to send new tracing header or not - // default: not send - enableTracingLevel?: boolean; -} - -export const explainQueryApi = api.injectEndpoints({ - endpoints: (build) => ({ - explainQuery: build.mutation({ - queryFn: async ( - {query, database, querySettings, enableTracingLevel, queryId}, - {signal, dispatch}, - ) => { - let action: ExplainActions = 'explain'; - let syntax: QuerySyntax = QUERY_SYNTAX.yql; - - dispatch(setQueryResult({type: ResultType.EXPLAIN, queryId, isLoading: true})); - - if (querySettings?.queryMode === 'pg') { - action = 'explain-query'; - syntax = QUERY_SYNTAX.pg; - } else if (querySettings?.queryMode) { - action = `explain-${querySettings?.queryMode}`; - } - - try { - const response = await window.api.sendQuery( - { - query, - database, - action, - syntax, - stats: querySettings?.statisticsMode, - tracingLevel: - querySettings?.tracingLevel && enableTracingLevel - ? TracingLevelNumber[querySettings?.tracingLevel] - : undefined, - transaction_mode: - querySettings?.transactionMode === 'implicit' - ? undefined - : querySettings?.transactionMode, - timeout: isNumeric(querySettings?.timeout) - ? Number(querySettings?.timeout) * 1000 - : undefined, - query_id: queryId, - }, - {signal}, - ); - - if (isQueryErrorResponse(response)) { - dispatch( - setQueryResult({ - type: ResultType.EXPLAIN, - error: response, - queryId, - isLoading: false, - }), - ); - return {error: response}; - } - - const data = prepareExplainResponse(response); - dispatch( - setQueryResult({ - type: ResultType.EXPLAIN, - data, - queryId, - isLoading: false, - }), - ); - return {data: null}; - } catch (error) { - dispatch( - setQueryResult({ - type: ResultType.EXPLAIN, - error, - queryId, - isLoading: false, - }), - ); - return {error}; - } - }, - }), - }), - overrideExisting: 'throw', -}); diff --git a/src/store/reducers/explainQuery/types.ts b/src/store/reducers/explainQuery/types.ts deleted file mode 100644 index 29a3a3da2..000000000 --- a/src/store/reducers/explainQuery/types.ts +++ /dev/null @@ -1,37 +0,0 @@ -import type {ExplainPlanNodeData, GraphNode, Link} from '@gravity-ui/paranoid'; - -import type { - PlanTable, - QueryPlan, - ScriptPlan, - SimlifiedPlanOperatorOtherParams, - SimplifiedNode, -} from '../../../types/api/query'; - -export interface PreparedExplainResponse { - plan?: { - links?: Link[]; - nodes?: GraphNode[]; - tables?: PlanTable[]; - version?: string; - pristine?: QueryPlan | ScriptPlan; - DurationUs?: string | number; - }; - simplifiedPlan?: { - plan?: SimplifiedPlanItem[]; - pristine?: SimplifiedNode; - }; - ast?: string; -} - -export interface SimplifiedPlanItem { - parentId?: string; - name: string; - operationParams: SimlifiedPlanOperatorOtherParams; - aCpu?: number; - aRows?: number; - eCost?: string; - eRows?: string; - eSize?: string; - children?: SimplifiedPlanItem[]; -} diff --git a/src/store/reducers/index.ts b/src/store/reducers/index.ts index 4b1de873b..249f83a90 100644 --- a/src/store/reducers/index.ts +++ b/src/store/reducers/index.ts @@ -4,12 +4,12 @@ import {api} from './api'; import authentication from './authentication/authentication'; import cluster from './cluster/cluster'; import clusters from './clusters/clusters'; -import executeQuery from './executeQuery'; import executeTopQueries from './executeTopQueries/executeTopQueries'; import fullscreen from './fullscreen'; import header from './header/header'; import heatmap from './heatmap'; import partitions from './partitions/partitions'; +import query from './query/query'; import queryActions from './queryActions/queryActions'; import schema from './schema/schema'; import settings from './settings/settings'; @@ -28,7 +28,7 @@ export const rootReducer = { schema, tenants, partitions, - executeQuery, + query, heatmap, settings, executeTopQueries, diff --git a/src/store/reducers/preview.ts b/src/store/reducers/preview.ts index 8a2841e90..7eb511ce6 100644 --- a/src/store/reducers/preview.ts +++ b/src/store/reducers/preview.ts @@ -1,5 +1,5 @@ import type {ExecuteActions} from '../../types/api/query'; -import {isQueryErrorResponse, parseQueryAPIExecuteResponse} from '../../utils/query'; +import {isQueryErrorResponse, parseQueryAPIResponse} from '../../utils/query'; import {api} from './api'; @@ -23,7 +23,7 @@ export const previewApi = api.injectEndpoints({ return {error: response}; } - return {data: parseQueryAPIExecuteResponse(response)}; + return {data: parseQueryAPIResponse(response)}; } catch (error) { return {error: error || new Error('Unauthorized')}; } diff --git a/src/store/reducers/query/__test__/utils.test.ts b/src/store/reducers/query/__test__/utils.test.ts new file mode 100644 index 000000000..d215dec12 --- /dev/null +++ b/src/store/reducers/query/__test__/utils.test.ts @@ -0,0 +1,24 @@ +import {getActionAndSyntaxFromQueryMode} from '../utils'; + +describe('getActionAndSyntaxFromQueryMode', () => { + it('Correctly prepares execute action', () => { + const {action, syntax} = getActionAndSyntaxFromQueryMode('execute', 'script'); + expect(action).toBe('execute-script'); + expect(syntax).toBe('yql_v1'); + }); + it('Correctly prepares execute action with pg syntax', () => { + const {action, syntax} = getActionAndSyntaxFromQueryMode('execute', 'pg'); + expect(action).toBe('execute-query'); + expect(syntax).toBe('pg'); + }); + it('Correctly prepares explain action', () => { + const {action, syntax} = getActionAndSyntaxFromQueryMode('explain', 'script'); + expect(action).toBe('explain-script'); + expect(syntax).toBe('yql_v1'); + }); + it('Correctly prepares explain action with pg syntax', () => { + const {action, syntax} = getActionAndSyntaxFromQueryMode('explain', 'pg'); + expect(action).toBe('explain-query'); + expect(syntax).toBe('pg'); + }); +}); diff --git a/src/store/reducers/explainQuery/utils.ts b/src/store/reducers/query/prepareQueryData.ts similarity index 70% rename from src/store/reducers/explainQuery/utils.ts rename to src/store/reducers/query/prepareQueryData.ts index 0bfc999c6..b35751901 100644 --- a/src/store/reducers/explainQuery/utils.ts +++ b/src/store/reducers/query/prepareQueryData.ts @@ -1,10 +1,10 @@ import type {ExplainPlanNodeData, GraphNode, Link} from '@gravity-ui/paranoid'; -import type {ExplainQueryResponse, ExplainScriptResponse} from '../../../types/api/query'; +import type {ExecuteResponse, ExplainResponse} from '../../../types/api/query'; import {preparePlan, prepareSimplifiedPlan} from '../../../utils/prepareQueryExplain'; -import {parseQueryAPIExplainResponse, parseQueryExplainPlan} from '../../../utils/query'; +import {parseQueryAPIResponse, parseQueryExplainPlan} from '../../../utils/query'; -import type {PreparedExplainResponse} from './types'; +import type {PreparedQueryData} from './types'; export const explainVersions = { v2: '0.2', @@ -12,13 +12,14 @@ export const explainVersions = { const supportedExplainQueryVersions = Object.values(explainVersions); -export const prepareExplainResponse = ( - response: ExplainScriptResponse | ExplainQueryResponse | null, -): PreparedExplainResponse => { - const {plan: rawPlan, ast} = parseQueryAPIExplainResponse(response); +export function prepareQueryData( + response: ExplainResponse | ExecuteResponse | null, +): PreparedQueryData { + const result = parseQueryAPIResponse(response); + const {plan: rawPlan} = result; if (!rawPlan) { - return {ast}; + return result; } const {tables, meta, Plan, SimplifiedPlan} = parseQueryExplainPlan(rawPlan); @@ -26,11 +27,11 @@ export const prepareExplainResponse = ( if (supportedExplainQueryVersions.indexOf(meta.version) === -1) { // Do not prepare plan for not supported versions return { - plan: { + ...result, + preparedPlan: { pristine: rawPlan, version: meta.version, }, - ast, }; } @@ -48,7 +49,8 @@ export const prepareExplainResponse = ( } return { - plan: { + ...result, + preparedPlan: { links, nodes, tables, @@ -56,6 +58,5 @@ export const prepareExplainResponse = ( pristine: rawPlan, }, simplifiedPlan: {plan: preparedSimplifiedPlan, pristine: SimplifiedPlan}, - ast, }; -}; +} diff --git a/src/store/reducers/executeQuery.ts b/src/store/reducers/query/query.ts similarity index 76% rename from src/store/reducers/executeQuery.ts rename to src/store/reducers/query/query.ts index 4c92b919d..39c994a52 100644 --- a/src/store/reducers/executeQuery.ts +++ b/src/store/reducers/query/query.ts @@ -1,17 +1,17 @@ import {createSlice} from '@reduxjs/toolkit'; import type {PayloadAction} from '@reduxjs/toolkit'; -import {settingsManager} from '../../services/settings'; -import {TracingLevelNumber} from '../../types/api/query'; -import type {ExecuteActions} from '../../types/api/query'; -import {ResultType} from '../../types/store/executeQuery'; -import type {ExecuteQueryState, QueryInHistory, QueryResult} from '../../types/store/executeQuery'; -import type {QueryRequestParams, QuerySettings, QuerySyntax} from '../../types/store/query'; -import {QUERIES_HISTORY_KEY} from '../../utils/constants'; -import {QUERY_SYNTAX, isQueryErrorResponse, parseQueryAPIExecuteResponse} from '../../utils/query'; -import {isNumeric} from '../../utils/utils'; +import {settingsManager} from '../../../services/settings'; +import {TracingLevelNumber} from '../../../types/api/query'; +import type {QueryAction, QueryRequestParams, QuerySettings} from '../../../types/store/query'; +import {QUERIES_HISTORY_KEY} from '../../../utils/constants'; +import {isQueryErrorResponse} from '../../../utils/query'; +import {isNumeric} from '../../../utils/utils'; +import {api} from '../api'; -import {api} from './api'; +import {prepareQueryData} from './prepareQueryData'; +import type {QueryResult, QueryState} from './types'; +import {getActionAndSyntaxFromQueryMode, getQueryInHistory} from './utils'; const MAXIMUM_QUERIES_IN_HISTORY = 20; @@ -22,7 +22,7 @@ const queriesHistoryInitial = settingsManager.readUserSettingsValue( const sliceLimit = queriesHistoryInitial.length - MAXIMUM_QUERIES_IN_HISTORY; -const initialState: ExecuteQueryState = { +const initialState: QueryState = { input: '', history: { queries: queriesHistoryInitial @@ -37,7 +37,7 @@ const initialState: ExecuteQueryState = { }; const slice = createSlice({ - name: 'executeQuery', + name: 'query', initialState, reducers: { changeUserInput: (state, action: PayloadAction<{input: string}>) => { @@ -162,6 +162,7 @@ export const { } = slice.selectors; interface SendQueryParams extends QueryRequestParams { + actionType?: QueryAction; queryId: string; querySettings?: Partial; // flag whether to send new tracing header or not @@ -174,24 +175,26 @@ interface QueryStats { endTime?: string | number; } -export const executeQueryApi = api.injectEndpoints({ +export const queryApi = api.injectEndpoints({ endpoints: (build) => ({ - executeQuery: build.mutation({ + useSendQuery: build.mutation({ queryFn: async ( - {query, database, querySettings = {}, enableTracingLevel, queryId}, + { + actionType = 'execute', + query, + database, + querySettings = {}, + enableTracingLevel, + queryId, + }, {signal, dispatch}, ) => { - let action: ExecuteActions = 'execute'; - let syntax: QuerySyntax = QUERY_SYNTAX.yql; + dispatch(setQueryResult({type: actionType, queryId, isLoading: true})); - dispatch(setQueryResult({type: ResultType.EXECUTE, queryId, isLoading: true})); - - if (querySettings.queryMode === 'pg') { - action = 'execute-query'; - syntax = QUERY_SYNTAX.pg; - } else if (querySettings.queryMode) { - action = `execute-${querySettings.queryMode}`; - } + const {action, syntax} = getActionAndSyntaxFromQueryMode( + actionType, + querySettings?.queryMode, + ); try { const timeStart = Date.now(); @@ -224,7 +227,7 @@ export const executeQueryApi = api.injectEndpoints({ if (isQueryErrorResponse(response)) { dispatch( setQueryResult({ - type: ResultType.EXECUTE, + type: actionType, error: response, isLoading: false, queryId, @@ -233,24 +236,27 @@ export const executeQueryApi = api.injectEndpoints({ return {error: response}; } - const data = parseQueryAPIExecuteResponse(response); + const data = prepareQueryData(response); data.traceId = response?._meta?.traceId; - const queryStats: QueryStats = {}; - if (data.stats) { - const {DurationUs, Executions: [{FinishTimeMs}] = [{}]} = data.stats; - queryStats.durationUs = DurationUs; - queryStats.endTime = FinishTimeMs; - } else { - const now = Date.now(); - queryStats.durationUs = (now - timeStart) * 1000; - queryStats.endTime = now; + if (actionType === 'execute') { + const queryStats: QueryStats = {}; + if (data.stats) { + const {DurationUs, Executions: [{FinishTimeMs}] = [{}]} = data.stats; + queryStats.durationUs = DurationUs; + queryStats.endTime = FinishTimeMs; + } else { + const now = Date.now(); + queryStats.durationUs = (now - timeStart) * 1000; + queryStats.endTime = now; + } + + dispatch(updateQueryInHistory({stats: queryStats, queryId})); } - dispatch(updateQueryInHistory({stats: queryStats, queryId})); dispatch( setQueryResult({ - type: ResultType.EXECUTE, + type: actionType, data, isLoading: false, queryId, @@ -260,7 +266,7 @@ export const executeQueryApi = api.injectEndpoints({ } catch (error) { dispatch( setQueryResult({ - type: ResultType.EXECUTE, + type: actionType, error, isLoading: false, queryId, @@ -273,12 +279,3 @@ export const executeQueryApi = api.injectEndpoints({ }), overrideExisting: 'throw', }); - -function getQueryInHistory(rawQuery: string | QueryInHistory) { - if (typeof rawQuery === 'string') { - return { - queryText: rawQuery, - }; - } - return rawQuery; -} diff --git a/src/store/reducers/query/types.ts b/src/store/reducers/query/types.ts new file mode 100644 index 000000000..8c9a5df9e --- /dev/null +++ b/src/store/reducers/query/types.ts @@ -0,0 +1,69 @@ +import type {ExplainPlanNodeData, GraphNode, Link} from '@gravity-ui/paranoid'; + +import type { + PlanTable, + QueryPlan, + ScriptPlan, + SimlifiedPlanOperatorOtherParams, + SimplifiedNode, +} from '../../../types/api/query'; +import type {IQueryResult, QueryAction} from '../../../types/store/query'; + +export interface QueryInHistory { + queryId?: string; + queryText: string; + syntax?: string; + endTime?: string | number; + durationUs?: string | number; +} + +export interface PreparedPlan { + links?: Link[]; + nodes?: GraphNode[]; + tables?: PlanTable[]; + version?: string; + pristine?: QueryPlan | ScriptPlan; + DurationUs?: string | number; +} + +export interface SimplifiedPlan { + plan?: SimplifiedPlanItem[]; + pristine?: SimplifiedNode; +} + +export interface SimplifiedPlanItem { + parentId?: string; + name: string; + operationParams: SimlifiedPlanOperatorOtherParams; + aCpu?: number; + aRows?: number; + eCost?: string; + eRows?: string; + eSize?: string; + children?: SimplifiedPlanItem[]; +} + +export interface PreparedQueryData extends IQueryResult { + preparedPlan?: PreparedPlan; + simplifiedPlan?: SimplifiedPlan; +} + +export interface QueryResult { + type: QueryAction; + data?: PreparedQueryData; + error?: unknown; + isTraceReady?: true; + queryId: string; + isLoading: boolean; +} + +export interface QueryState { + input: string; + result?: QueryResult & {isTraceReady?: boolean}; + history: { + queries: QueryInHistory[]; + currentIndex: number; + filter?: string; + }; + tenantPath?: string; +} diff --git a/src/store/reducers/query/utils.ts b/src/store/reducers/query/utils.ts new file mode 100644 index 000000000..ae9b9f0f9 --- /dev/null +++ b/src/store/reducers/query/utils.ts @@ -0,0 +1,30 @@ +import type {Actions} from '../../../types/api/query'; +import type {QueryAction, QueryMode, QuerySyntax} from '../../../types/store/query'; + +import type {QueryInHistory} from './types'; + +export function getActionAndSyntaxFromQueryMode( + baseAction: QueryAction = 'execute', + queryMode: QueryMode = 'query', +) { + let action: Actions = baseAction; + let syntax: QuerySyntax = 'yql_v1'; + + if (queryMode === 'pg') { + action = `${baseAction}-query`; + syntax = 'pg'; + } else if (queryMode) { + action = `${baseAction}-${queryMode}`; + } + + return {action, syntax}; +} + +export function getQueryInHistory(rawQuery: string | QueryInHistory) { + if (typeof rawQuery === 'string') { + return { + queryText: rawQuery, + }; + } + return rawQuery; +} diff --git a/src/store/reducers/queryActions/queryActions.ts b/src/store/reducers/queryActions/queryActions.ts index dd661f8b2..fc16d2039 100644 --- a/src/store/reducers/queryActions/queryActions.ts +++ b/src/store/reducers/queryActions/queryActions.ts @@ -58,7 +58,7 @@ export function saveQuery(queryName: string | null) { return function saveQueryThunk(dispatch: AppDispatch, getState: GetState) { const state = getState(); const savedQueries = (getSettingValue(state, SAVED_QUERIES_KEY) as SavedQuery[]) ?? []; - const queryBody = state.executeQuery.input; + const queryBody = state.query.input; if (queryName === null) { return; } diff --git a/src/store/reducers/shardsWorkload/shardsWorkload.ts b/src/store/reducers/shardsWorkload/shardsWorkload.ts index 43a10debd..e59262bb1 100644 --- a/src/store/reducers/shardsWorkload/shardsWorkload.ts +++ b/src/store/reducers/shardsWorkload/shardsWorkload.ts @@ -2,7 +2,7 @@ import {dateTimeParse, isLikeRelative} from '@gravity-ui/date-utils'; import {createSlice} from '@reduxjs/toolkit'; import type {PayloadAction} from '@reduxjs/toolkit'; -import {isQueryErrorResponse, parseQueryAPIExecuteResponse} from '../../../utils/query'; +import {isQueryErrorResponse, parseQueryAPIResponse} from '../../../utils/query'; import {api} from '../api'; import type {ShardsWorkloadFilters} from './types'; @@ -155,7 +155,7 @@ export const shardApi = api.injectEndpoints({ return {error: response}; } - const data = parseQueryAPIExecuteResponse(response); + const data = parseQueryAPIResponse(response); return {data}; } catch (error) { return {error}; diff --git a/src/store/reducers/tenantOverview/executeTopTables/executeTopTables.ts b/src/store/reducers/tenantOverview/executeTopTables/executeTopTables.ts index ba3b1ab6a..4cc750b7f 100644 --- a/src/store/reducers/tenantOverview/executeTopTables/executeTopTables.ts +++ b/src/store/reducers/tenantOverview/executeTopTables/executeTopTables.ts @@ -1,5 +1,5 @@ import {TENANT_OVERVIEW_TABLES_LIMIT} from '../../../../utils/constants'; -import {isQueryErrorResponse, parseQueryAPIExecuteResponse} from '../../../../utils/query'; +import {isQueryErrorResponse, parseQueryAPIResponse} from '../../../../utils/query'; import {api} from '../../api'; const getQueryText = (path: string) => { @@ -31,7 +31,7 @@ export const topTablesApi = api.injectEndpoints({ return {error: response}; } - return {data: parseQueryAPIExecuteResponse(response)}; + return {data: parseQueryAPIResponse(response)}; } catch (error) { return {error: error || 'Unauthorized'}; } diff --git a/src/store/reducers/tenantOverview/topShards/tenantOverviewTopShards.ts b/src/store/reducers/tenantOverview/topShards/tenantOverviewTopShards.ts index a2374a994..ed4b84456 100644 --- a/src/store/reducers/tenantOverview/topShards/tenantOverviewTopShards.ts +++ b/src/store/reducers/tenantOverview/topShards/tenantOverviewTopShards.ts @@ -1,5 +1,5 @@ import {TENANT_OVERVIEW_TABLES_LIMIT} from '../../../../utils/constants'; -import {isQueryErrorResponse, parseQueryAPIExecuteResponse} from '../../../../utils/query'; +import {isQueryErrorResponse, parseQueryAPIResponse} from '../../../../utils/query'; import {api} from '../../api'; function createShardQuery(path: string, tenantName?: string) { @@ -39,7 +39,7 @@ export const topShardsApi = api.injectEndpoints({ return {error: response}; } - return {data: parseQueryAPIExecuteResponse(response)}; + return {data: parseQueryAPIResponse(response)}; } catch (error) { return {error: error || new Error('Unauthorized')}; } diff --git a/src/store/reducers/trace.ts b/src/store/reducers/trace.ts index fbfad100a..45969621c 100644 --- a/src/store/reducers/trace.ts +++ b/src/store/reducers/trace.ts @@ -1,5 +1,5 @@ import {api} from './api'; -import {setQueryTraceReady} from './executeQuery'; +import {setQueryTraceReady} from './query/query'; interface CheckTraceParams { url: string; diff --git a/src/types/store/executeQuery.ts b/src/types/store/executeQuery.ts deleted file mode 100644 index b8c3a0f63..000000000 --- a/src/types/store/executeQuery.ts +++ /dev/null @@ -1,48 +0,0 @@ -import type {PreparedExplainResponse} from '../../store/reducers/explainQuery/types'; - -import type {IQueryResult} from './query'; - -export interface QueryInHistory { - queryId?: string; - queryText: string; - syntax?: string; - endTime?: string | number; - durationUs?: string | number; -} - -export enum ResultType { - EXECUTE = 'execute', - EXPLAIN = 'explain', -} - -interface CommonResultParams { - queryId: string; - isLoading: boolean; -} - -export type ExecuteQueryResult = { - type: ResultType.EXECUTE; - data?: IQueryResult; - error?: unknown; - isTraceReady?: true; -} & CommonResultParams; - -export type ExplainQueryResult = { - type: ResultType.EXPLAIN; - data?: PreparedExplainResponse; - error?: unknown; -} & CommonResultParams; - -export type QueryResult = ExecuteQueryResult | ExplainQueryResult; - -export interface ExecuteQueryState { - input: string; - result?: QueryResult & {isTraceReady?: boolean}; - history: { - // String type for backward compatibility - queries: QueryInHistory[]; - currentIndex: number; - filter?: string; - }; - tenantPath?: string; -} diff --git a/src/utils/__test__/prepareQueryExplain.test.ts b/src/utils/__test__/prepareQueryExplain.test.ts index 82e753254..a9b4f762d 100644 --- a/src/utils/__test__/prepareQueryExplain.test.ts +++ b/src/utils/__test__/prepareQueryExplain.test.ts @@ -1,4 +1,4 @@ -import type {SimplifiedPlanItem} from '../../store/reducers/explainQuery/types'; +import type {SimplifiedPlanItem} from '../../store/reducers/query/types'; import type {SimplifiedNode} from '../../types/api/query'; import {prepareSimplifiedPlan} from '../prepareQueryExplain'; diff --git a/src/utils/hooks/withConfirmation/useChangeInputWithConfirmation.tsx b/src/utils/hooks/withConfirmation/useChangeInputWithConfirmation.tsx index d4c087174..00e6bfa8f 100644 --- a/src/utils/hooks/withConfirmation/useChangeInputWithConfirmation.tsx +++ b/src/utils/hooks/withConfirmation/useChangeInputWithConfirmation.tsx @@ -5,7 +5,7 @@ import NiceModal from '@ebay/nice-modal-react'; import {useTypedSelector} from '..'; import {CONFIRMATION_DIALOG} from '../../../components/ConfirmationDialog/ConfirmationDialog'; import {SaveQueryButton} from '../../../containers/Tenant/Query/SaveQuery/SaveQuery'; -import {selectUserInput} from '../../../store/reducers/executeQuery'; +import {selectUserInput} from '../../../store/reducers/query/query'; import i18n from './i18n'; diff --git a/src/utils/prepareQueryExplain.ts b/src/utils/prepareQueryExplain.ts index c1eb5c2ed..b4198b03a 100644 --- a/src/utils/prepareQueryExplain.ts +++ b/src/utils/prepareQueryExplain.ts @@ -7,7 +7,7 @@ import type { TopologyNodeDataStatsSection, } from '@gravity-ui/paranoid'; -import type {SimplifiedPlanItem} from '../store/reducers/explainQuery/types'; +import type {SimplifiedPlanItem} from '../store/reducers/query/types'; import type {PlanNode, SimplifiedNode} from '../types/api/query'; const CONNECTION_NODE_META_FIELDS = new Set(['PlanNodeId', 'PlanNodeType', 'Node Type', 'Plans']); diff --git a/src/utils/query.test.ts b/src/utils/query.test.ts index 44a812ae6..720dd9077 100644 --- a/src/utils/query.test.ts +++ b/src/utils/query.test.ts @@ -6,43 +6,39 @@ import type { TKqpStatsQuery, } from '../types/api/query'; -import { - parseQueryAPIExecuteResponse, - parseQueryAPIExplainResponse, - parseQueryExplainPlan, -} from './query'; +import {parseQueryAPIResponse, parseQueryExplainPlan} from './query'; describe('API utils', () => { describe('json/viewer/query', () => { - describe('parseQueryAPIExecuteResponse', () => { + describe('parseQueryAPIResponse', () => { describe('should handle responses with incorrect format', () => { it('should handle null response', () => { - expect(parseQueryAPIExecuteResponse(null)).toEqual({}); + expect(parseQueryAPIResponse(null)).toEqual({}); }); it('should handle undefined response', () => { - expect(parseQueryAPIExecuteResponse(undefined)).toEqual({}); + expect(parseQueryAPIResponse(undefined)).toEqual({}); }); it('should handle string response', () => { - expect(parseQueryAPIExecuteResponse('foo')).toEqual({}); + expect(parseQueryAPIResponse('foo')).toEqual({}); }); it('should handle array response', () => { - expect(parseQueryAPIExecuteResponse([{foo: 'bar'}])).toEqual({}); + expect(parseQueryAPIResponse([{foo: 'bar'}])).toEqual({}); }); it('should handle json string in the result field', () => { const json = {foo: 'bar'}; const response = {result: JSON.stringify(json)}; - expect(parseQueryAPIExecuteResponse(response)).toEqual({}); + expect(parseQueryAPIResponse(response)).toEqual({}); }); it('should handle object with request plan in the result field', () => { const response = {result: {queries: 'some queries'}}; - expect(parseQueryAPIExecuteResponse(response)).toEqual({}); + expect(parseQueryAPIResponse(response)).toEqual({}); }); }); describe('should correctly parse data', () => { it('should accept stats without a result', () => { const stats = {metric: 'good'} as TKqpStatsQuery; const response = {stats}; - const actual = parseQueryAPIExecuteResponse(response); + const actual = parseQueryAPIResponse(response); expect(actual.resultSets?.[0]?.result).toBeUndefined(); expect(actual.columns).toBeUndefined(); expect(actual.stats).toEqual(response.stats); @@ -102,7 +98,7 @@ describe('API utils', () => { stats: {DurationUs: '1000'}, }; - expect(parseQueryAPIExecuteResponse(input)).toEqual(expected); + expect(parseQueryAPIResponse(input)).toEqual(expected); }); it('should handle empty result array', () => { @@ -116,7 +112,7 @@ describe('API utils', () => { stats: {DurationUs: '1000'}, }; - expect(parseQueryAPIExecuteResponse(input)).toEqual(expected); + expect(parseQueryAPIResponse(input)).toEqual(expected); }); it('should handle result with columns but no rows', () => { @@ -142,7 +138,7 @@ describe('API utils', () => { stats: {DurationUs: '1000'}, }; - expect(parseQueryAPIExecuteResponse(input)).toEqual(expected); + expect(parseQueryAPIResponse(input)).toEqual(expected); }); it('should return empty object for unsupported format', () => { @@ -150,7 +146,7 @@ describe('API utils', () => { result: 'unsupported', }; - expect(parseQueryAPIExecuteResponse(input)).toEqual({}); + expect(parseQueryAPIResponse(input)).toEqual({}); }); it('should handle multiple result sets', () => { @@ -203,7 +199,7 @@ describe('API utils', () => { stats: {DurationUs: '1500'}, }; - expect(parseQueryAPIExecuteResponse(input)).toEqual(expected); + expect(parseQueryAPIResponse(input)).toEqual(expected); }); it('should handle null values in rows', () => { @@ -249,7 +245,7 @@ describe('API utils', () => { stats: {DurationUs: '1000'}, }; - expect(parseQueryAPIExecuteResponse(input)).toEqual(expected); + expect(parseQueryAPIResponse(input)).toEqual(expected); }); it('should handle truncated results', () => { @@ -264,7 +260,7 @@ describe('API utils', () => { stats: {DurationUs: '1000'}, }; - const result = parseQueryAPIExecuteResponse(input); + const result = parseQueryAPIResponse(input); expect(result.resultSets?.[0].truncated).toBe(true); }); @@ -291,33 +287,17 @@ describe('API utils', () => { stats: {DurationUs: '1000'}, }; - expect(parseQueryAPIExecuteResponse(input)).toEqual(expected); + expect(parseQueryAPIResponse(input)).toEqual(expected); }); }); - }); - - describe('parseQueryAPIExplainResponse', () => { - describe('should handle responses with incorrect format', () => { - it('should handle null response', () => { - expect(parseQueryAPIExecuteResponse(null)).toEqual({}); - }); - it('should handle undefined response', () => { - expect(parseQueryAPIExecuteResponse(undefined)).toEqual({}); - }); - it('should handle object with plan in the result field', () => { - const response = {result: {foo: 'bar'}}; - expect(parseQueryAPIExecuteResponse(response)).toEqual({}); - }); - }); - - describe('should correctly parse data', () => { + describe('should correctly parse plans', () => { it('should parse explain-scan', () => { const plan: PlanNode = {}; const tables: PlanTable[] = []; const meta: PlanMeta = {version: '0.2', type: 'script'}; const ast = 'ast'; const response = {plan: {Plan: plan, tables, meta}, ast}; - expect(parseQueryAPIExplainResponse(response)).toBe(response); + expect(parseQueryAPIResponse(response)).toBe(response); }); it('should parse explain-script', () => { const plan: PlanNode = {}; @@ -327,7 +307,7 @@ describe('API utils', () => { const response = { plan: {queries: [{Plan: plan, tables}], meta}, }; - expect(parseQueryAPIExplainResponse(response)).toBe(response); + expect(parseQueryAPIResponse(response)).toBe(response); }); }); }); diff --git a/src/utils/query.ts b/src/utils/query.ts index c28ed7479..f4deb9291 100644 --- a/src/utils/query.ts +++ b/src/utils/query.ts @@ -173,10 +173,13 @@ const parseExecuteResponse = (data: ExecuteResponse): IQueryResult => { }; }; -const isSupportedType = (response: ExecuteResponse): response is ExecuteResponse => +const isSupportedExecuteResponse = ( + response: ExecuteResponse | ExplainResponse, +): response is ExecuteResponse => Boolean( response && !Array.isArray(response) && + 'result' in response && Array.isArray(response.result) && typeof response.result[0] === 'object' && 'rows' in response.result[0] && @@ -207,29 +210,19 @@ export function isQueryErrorResponse(data: unknown): data is ErrorResponse { // Although schema is set in request, if schema is not supported default schema for the version will be used // So we should additionally parse response -export const parseQueryAPIExecuteResponse = ( - data: ExecuteResponse | UnsupportedQueryResponseFormat, -): IQueryResult => { +export function parseQueryAPIResponse( + data: ExecuteResponse | ExplainResponse | UnsupportedQueryResponseFormat, +): IQueryResult { if (isUnsupportedType(data)) { return {}; } - if (isSupportedType(data)) { + if (isSupportedExecuteResponse(data)) { return parseExecuteResponse(data); } return data; -}; - -export const parseQueryAPIExplainResponse = ( - data: ExplainResponse | UnsupportedQueryResponseFormat, -): IQueryResult => { - if (isUnsupportedType(data)) { - return {}; - } - - return data; -}; +} const isExplainScriptPlan = (plan: ScriptPlan | QueryPlan): plan is ScriptPlan => Boolean(plan && 'queries' in plan);