Skip to content

Commit 1c0b4f7

Browse files
committed
feat: UI link to Plan2Svg
1 parent 3e4a04b commit 1c0b4f7

File tree

5 files changed

+96
-1
lines changed

5 files changed

+96
-1
lines changed

src/containers/Tenant/Query/ExecuteResult/ExecuteResult.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import {QuerySettingsBanner} from '../QuerySettingsBanner/QuerySettingsBanner';
3030
import {getPreparedResult} from '../utils/getPreparedResult';
3131
import {isQueryCancelledError} from '../utils/isQueryCancelledError';
3232

33+
import {PlanToSvgButton} from './PlanToSvgButton';
3334
import {TraceButton} from './TraceButton';
3435
import i18n from './i18n';
3536
import {getPlan} from './utils';
@@ -273,6 +274,9 @@ export function ExecuteResult({
273274
{data?.traceId ? (
274275
<TraceButton traceId={data.traceId} isTraceReady={result.isTraceReady} />
275276
) : null}
277+
{data?.plan ? (
278+
<PlanToSvgButton plan={data?.plan} database={tenantName} />
279+
) : null}
276280
</div>
277281
<div className={b('controls-left')}>
278282
{renderClipboardButton()}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import React from 'react';
2+
3+
import {ArrowUpRightFromSquare} from '@gravity-ui/icons';
4+
import {Button} from '@gravity-ui/uikit';
5+
6+
import {planToSvgQueryApi} from '../../../../store/reducers/planToSvgQuery';
7+
import type {QueryPlan, ScriptPlan} from '../../../../types/api/query';
8+
9+
import i18n from './i18n';
10+
11+
interface PlanToSvgButtonProps {
12+
plan?: QueryPlan | ScriptPlan;
13+
database: string;
14+
}
15+
16+
export function PlanToSvgButton({plan, database}: PlanToSvgButtonProps) {
17+
const [checkPlanToSvg, {isLoading, isUninitialized}] =
18+
planToSvgQueryApi.usePlanToSvgQueryMutation();
19+
20+
React.useEffect(() => {
21+
let checkPlanToSvgMutation: {abort: () => void} | null;
22+
if (plan) {
23+
checkPlanToSvgMutation = checkPlanToSvg({plan, database});
24+
}
25+
26+
return () => checkPlanToSvgMutation?.abort();
27+
}, [checkPlanToSvg, database, plan]);
28+
29+
if (isUninitialized) {
30+
return null;
31+
}
32+
33+
return (
34+
<Button
35+
view={isLoading ? 'flat-secondary' : 'flat-info'}
36+
loading={isLoading}
37+
target="_blank"
38+
>
39+
{i18n('text_plan-svg')}
40+
<Button.Icon>
41+
<ArrowUpRightFromSquare />
42+
</Button.Icon>
43+
</Button>
44+
);
45+
}

src/containers/Tenant/Query/ExecuteResult/i18n/en.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,6 @@
77
"action.copy": "Copy {{activeSection}}",
88
"trace": "Trace",
99
"title.truncated": "Truncated",
10-
"title.result": "Result"
10+
"title.result": "Result",
11+
"text_plan-svg": "Plan SVG"
1112
}

src/services/api.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import type {AxiosRequestConfig} from 'axios';
44
import axiosRetry from 'axios-retry';
55

66
import {backend as BACKEND, metaBackend as META_BACKEND} from '../store';
7+
import type {PlanToSvgQueryParams} from '../store/reducers/planToSvgQuery';
78
import type {TMetaInfo} from '../types/api/acl';
89
import type {TQueryAutocomplete} from '../types/api/autocomplete';
910
import type {CapabilitiesResponse} from '../types/api/capabilities';
@@ -578,6 +579,16 @@ export class YdbEmbeddedAPI extends AxiosWrapper {
578579
},
579580
);
580581
}
582+
planToSvg({database, plan}: PlanToSvgQueryParams, {signal}: {signal?: AbortSignal} = {}) {
583+
return this.post<{test: string}>(
584+
this.getPath('/viewer/plan2svg'),
585+
plan,
586+
{database},
587+
{
588+
requestConfig: {signal},
589+
},
590+
);
591+
}
581592
getHotKeys(
582593
{path, database, enableSampling}: {path: string; database: string; enableSampling: boolean},
583594
{concurrentId, signal}: AxiosOptions = {},
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import type {QueryPlan, ScriptPlan} from '../../types/api/query';
2+
import type {IQueryResult} from '../../types/store/query';
3+
4+
import {api} from './api';
5+
6+
export interface PlanToSvgQueryParams {
7+
plan: ScriptPlan | QueryPlan;
8+
database: string;
9+
}
10+
11+
export const planToSvgQueryApi = api.injectEndpoints({
12+
endpoints: (build) => ({
13+
planToSvgQuery: build.mutation<IQueryResult, PlanToSvgQueryParams>({
14+
queryFn: async ({plan, database}, {signal}) => {
15+
try {
16+
const response = await window.api.planToSvg(
17+
{
18+
database,
19+
plan,
20+
},
21+
{signal},
22+
);
23+
24+
console.log(response);
25+
26+
return {data: response};
27+
} catch (error) {
28+
return {error};
29+
}
30+
},
31+
}),
32+
}),
33+
overrideExisting: 'throw',
34+
});

0 commit comments

Comments
 (0)