Skip to content

Commit 39d86c9

Browse files
authored
feat: snippets for table (under tree dots in navigation tree) (#1476)
1 parent 82531a5 commit 39d86c9

File tree

6 files changed

+151
-17
lines changed

6 files changed

+151
-17
lines changed

src/containers/Tenant/Schema/SchemaTree/SchemaTree.tsx

+23
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,11 @@ import {NavigationTree} from 'ydb-ui-components';
77

88
import {useCreateDirectoryFeatureAvailable} from '../../../../store/reducers/capabilities/hooks';
99
import {schemaApi} from '../../../../store/reducers/schema/schema';
10+
import {tableSchemaDataApi} from '../../../../store/reducers/tableSchemaData';
11+
import type {GetTableSchemaDataParams} from '../../../../store/reducers/tableSchemaData';
1012
import type {EPathType, TEvDescribeSchemeResult} from '../../../../types/api/schema';
13+
import {wait} from '../../../../utils';
14+
import {SECOND_IN_MS} from '../../../../utils/constants';
1115
import {useQueryExecutionSettings, useTypedDispatch} from '../../../../utils/hooks';
1216
import {getSchemaControls} from '../../utils/controls';
1317
import {isChildlessPathType, mapPathTypeToNavigationTreeType} from '../../utils/schema';
@@ -22,10 +26,28 @@ interface SchemaTreeProps {
2226
onActivePathUpdate: (path: string) => void;
2327
}
2428

29+
const TABLE_SCHEMA_TIMEOUT = SECOND_IN_MS * 2;
30+
2531
export function SchemaTree(props: SchemaTreeProps) {
2632
const createDirectoryFeatureAvailable = useCreateDirectoryFeatureAvailable();
2733
const {rootPath, rootName, rootType, currentPath, onActivePathUpdate} = props;
2834
const dispatch = useTypedDispatch();
35+
const [getTableSchemaDataMutation] = tableSchemaDataApi.useGetTableSchemaDataMutation();
36+
37+
const getTableSchemaDataPromise = React.useCallback(
38+
async (args: GetTableSchemaDataParams) => {
39+
try {
40+
const result = await Promise.race([
41+
getTableSchemaDataMutation(args).unwrap(),
42+
wait<undefined>(TABLE_SCHEMA_TIMEOUT),
43+
]);
44+
return result;
45+
} catch (e) {
46+
return undefined;
47+
}
48+
},
49+
[getTableSchemaDataMutation],
50+
);
2951

3052
const [querySettings, setQueryExecutionSettings] = useQueryExecutionSettings();
3153
const [createDirectoryOpen, setCreateDirectoryOpen] = React.useState(false);
@@ -119,6 +141,7 @@ export function SchemaTree(props: SchemaTreeProps) {
119141
showCreateDirectoryDialog: createDirectoryFeatureAvailable
120142
? handleOpenCreateDirectoryDialog
121143
: undefined,
144+
getTableSchemaDataPromise,
122145
},
123146
rootPath,
124147
)}

src/containers/Tenant/utils/schema.ts

+8
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,14 @@ const pathTypeToNodeType: Record<EPathType, NavigationTreeNodeType | undefined>
4141
[EPathType.EPathTypeReplication]: 'async_replication',
4242
};
4343

44+
export const nodeTableTypeToPathType: Partial<Record<NavigationTreeNodeType, EPathType>> = {
45+
table: EPathType.EPathTypeTable,
46+
index: EPathType.EPathTypeTableIndex,
47+
column_table: EPathType.EPathTypeColumnTable,
48+
external_table: EPathType.EPathTypeExternalTable,
49+
view: EPathType.EPathTypeView,
50+
};
51+
4452
export const mapPathTypeToNavigationTreeType = (
4553
type: EPathType = EPathType.EPathTypeDir,
4654
subType?: EPathSubType,

src/containers/Tenant/utils/schemaActions.ts

+50-13
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,19 @@
11
import copy from 'copy-to-clipboard';
22
import type {NavigationTreeNodeType, NavigationTreeProps} from 'ydb-ui-components';
33

4+
import type {AppDispatch} from '../../../store';
45
import {changeUserInput} from '../../../store/reducers/executeQuery';
6+
import type {GetTableSchemaDataParams} from '../../../store/reducers/tableSchemaData';
57
import {TENANT_PAGES_IDS, TENANT_QUERY_TABS_ID} from '../../../store/reducers/tenant/constants';
68
import {setQueryTab, setTenantPage} from '../../../store/reducers/tenant/tenant';
79
import type {QueryMode, QuerySettings} from '../../../types/store/query';
810
import createToast from '../../../utils/createToast';
911
import {transformPath} from '../ObjectSummary/transformPath';
12+
import type {SchemaData} from '../Schema/SchemaViewer/types';
1013
import i18n from '../i18n';
1114

12-
import type {SchemaQueryParams} from './schemaQueryTemplates';
15+
import {nodeTableTypeToPathType} from './schema';
16+
import type {TemplateFn} from './schemaQueryTemplates';
1317
import {
1418
addTableIndex,
1519
alterAsyncReplicationTemplate,
@@ -34,31 +38,60 @@ interface ActionsAdditionalEffects {
3438
updateQueryExecutionSettings: (settings?: Partial<QuerySettings>) => void;
3539
setActivePath: (path: string) => void;
3640
showCreateDirectoryDialog?: (path: string) => void;
41+
getTableSchemaDataPromise?: (
42+
params: GetTableSchemaDataParams,
43+
) => Promise<SchemaData[] | undefined>;
44+
}
45+
46+
interface BindActionParams {
47+
tenantName: string;
48+
type: NavigationTreeNodeType;
49+
path: string;
50+
relativePath: string;
3751
}
3852

3953
const bindActions = (
40-
schemaQueryParams: SchemaQueryParams,
41-
dispatch: React.Dispatch<any>,
54+
params: BindActionParams,
55+
dispatch: AppDispatch,
4256
additionalEffects: ActionsAdditionalEffects,
4357
) => {
44-
const {setActivePath, updateQueryExecutionSettings, showCreateDirectoryDialog} =
45-
additionalEffects;
46-
47-
const inputQuery = (tmpl: (params?: SchemaQueryParams) => string, mode?: QueryMode) => () => {
58+
const {
59+
setActivePath,
60+
updateQueryExecutionSettings,
61+
showCreateDirectoryDialog,
62+
getTableSchemaDataPromise,
63+
} = additionalEffects;
64+
65+
const inputQuery = (tmpl: TemplateFn, mode?: QueryMode) => () => {
4866
if (mode) {
4967
updateQueryExecutionSettings({queryMode: mode});
5068
}
5169

52-
dispatch(changeUserInput({input: tmpl(schemaQueryParams)}));
70+
const pathType = nodeTableTypeToPathType[params.type];
71+
const withTableData = [selectQueryTemplate, upsertQueryTemplate].includes(tmpl);
72+
73+
const userInputDataPromise =
74+
withTableData && pathType && getTableSchemaDataPromise
75+
? getTableSchemaDataPromise({
76+
path: params.path,
77+
tenantName: params.tenantName,
78+
type: pathType,
79+
})
80+
: Promise.resolve(undefined);
81+
82+
userInputDataPromise.then((tableData) => {
83+
dispatch(changeUserInput({input: tmpl({...params, tableData})}));
84+
});
85+
5386
dispatch(setTenantPage(TENANT_PAGES_IDS.query));
5487
dispatch(setQueryTab(TENANT_QUERY_TABS_ID.newQuery));
55-
setActivePath(schemaQueryParams.path);
88+
setActivePath(params.path);
5689
};
5790

5891
return {
5992
createDirectory: showCreateDirectoryDialog
6093
? () => {
61-
showCreateDirectoryDialog(schemaQueryParams.path);
94+
showCreateDirectoryDialog(params.path);
6295
}
6396
: undefined,
6497
createTable: inputQuery(createTableTemplate, 'script'),
@@ -81,7 +114,7 @@ const bindActions = (
81114
addTableIndex: inputQuery(addTableIndex, 'script'),
82115
copyPath: () => {
83116
try {
84-
copy(schemaQueryParams.relativePath);
117+
copy(params.relativePath);
85118
createToast({
86119
name: 'Copied',
87120
title: i18n('actions.copied'),
@@ -101,10 +134,14 @@ const bindActions = (
101134
type ActionsSet = ReturnType<Required<NavigationTreeProps>['getActions']>;
102135

103136
export const getActions =
104-
(dispatch: React.Dispatch<any>, additionalEffects: ActionsAdditionalEffects, rootPath = '') =>
137+
(dispatch: AppDispatch, additionalEffects: ActionsAdditionalEffects, rootPath = '') =>
105138
(path: string, type: NavigationTreeNodeType) => {
106139
const relativePath = transformPath(path, rootPath);
107-
const actions = bindActions({path, relativePath}, dispatch, additionalEffects);
140+
const actions = bindActions(
141+
{path, relativePath, tenantName: rootPath, type},
142+
dispatch,
143+
additionalEffects,
144+
);
108145
const copyItem = {text: i18n('actions.copyPath'), action: actions.copyPath};
109146

110147
const DIR_SET: ActionsSet = [

src/containers/Tenant/utils/schemaQueryTemplates.ts

+12-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,13 @@
1+
import type {SchemaData} from '../Schema/SchemaViewer/types';
2+
13
export interface SchemaQueryParams {
24
path: string;
35
relativePath: string;
6+
tableData?: SchemaData[];
47
}
58

9+
export type TemplateFn = (params?: SchemaQueryParams) => string;
10+
611
export const createTableTemplate = (params?: SchemaQueryParams) => {
712
return `-- docs: https://ydb.tech/en/docs/yql/reference/syntax/create_table
813
CREATE TABLE \`${params?.relativePath || '$path'}/ydb_row_table\` (
@@ -67,13 +72,18 @@ export const alterTableTemplate = (params?: SchemaQueryParams) => {
6772
ADD COLUMN numeric_column Int32;`;
6873
};
6974
export const selectQueryTemplate = (params?: SchemaQueryParams) => {
70-
return `SELECT *
75+
const columns = params?.tableData?.map((column) => '`' + column.name + '`').join(', ') || '*';
76+
77+
return `SELECT ${columns}
7178
FROM \`${params?.relativePath || '$path'}\`
7279
LIMIT 10;`;
7380
};
7481
export const upsertQueryTemplate = (params?: SchemaQueryParams) => {
82+
const columns =
83+
params?.tableData?.map((column) => `\`${column.name}\``).join(', ') || `\`id\`, \`name\``;
84+
7585
return `UPSERT INTO \`${params?.relativePath || '$path'}\`
76-
( \`id\`, \`name\` )
86+
( ${columns} )
7787
VALUES ( );`;
7888
};
7989

src/store/reducers/tableSchemaData.ts

+56
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import {
2+
prepareSchemaData,
3+
prepareViewSchema,
4+
} from '../../containers/Tenant/Schema/SchemaViewer/prepareData';
5+
import type {SchemaData} from '../../containers/Tenant/Schema/SchemaViewer/types';
6+
import {isViewType} from '../../containers/Tenant/utils/schema';
7+
import type {EPathType} from '../../types/api/schema';
8+
import {isQueryErrorResponse} from '../../utils/query';
9+
10+
import {api} from './api';
11+
import {overviewApi} from './overview/overview';
12+
import {viewSchemaApi} from './viewSchema/viewSchema';
13+
14+
export interface GetTableSchemaDataParams {
15+
path: string;
16+
tenantName: string;
17+
type: EPathType;
18+
}
19+
20+
export const tableSchemaDataApi = api.injectEndpoints({
21+
endpoints: (build) => ({
22+
getTableSchemaData: build.mutation<SchemaData[], GetTableSchemaDataParams>({
23+
queryFn: async ({path, tenantName, type}, {dispatch}) => {
24+
try {
25+
if (isViewType(type)) {
26+
const response = await dispatch(
27+
viewSchemaApi.endpoints.getViewSchema.initiate({
28+
database: tenantName,
29+
path,
30+
}),
31+
);
32+
33+
if (isQueryErrorResponse(response)) {
34+
return {error: response};
35+
}
36+
37+
const result = prepareViewSchema(response.data);
38+
return {data: result};
39+
}
40+
41+
const schemaData = await dispatch(
42+
overviewApi.endpoints.getOverview.initiate({
43+
path,
44+
database: tenantName,
45+
}),
46+
);
47+
const result = prepareSchemaData(type, schemaData.data);
48+
49+
return {data: result};
50+
} catch (error) {
51+
return {error};
52+
}
53+
},
54+
}),
55+
}),
56+
});

src/utils/index.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ export function valueIsDefined<T>(value: T | null | undefined): value is T {
66
return value !== null && value !== undefined;
77
}
88

9-
export async function wait(time: number) {
9+
export async function wait<T = unknown>(time: number, value?: T): Promise<T | undefined> {
1010
return new Promise((resolve) => {
11-
setTimeout(resolve, time);
11+
setTimeout(() => resolve(value), time);
1212
});
1313
}

0 commit comments

Comments
 (0)