Skip to content

Commit 9c3176f

Browse files
Anton Standrikgithub-actions[bot]
authored andcommitted
feat: indicate schema description loading
1 parent 7302a4a commit 9c3176f

File tree

14 files changed

+216
-81
lines changed

14 files changed

+216
-81
lines changed

package-lock.json

Lines changed: 5 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
},
1212
"dependencies": {
1313
"@bem-react/classname": "^1.6.0",
14+
"@ebay/nice-modal-react": "^1.2.13",
1415
"@gravity-ui/axios-wrapper": "^1.4.1",
1516
"@gravity-ui/chartkit": "^5.9.0",
1617
"@gravity-ui/components": "^3.7.0",
@@ -27,7 +28,6 @@
2728
"@hookform/resolvers": "^3.9.0",
2829
"@reduxjs/toolkit": "^2.2.3",
2930
"@tanstack/react-table": "^8.19.3",
30-
"@ebay/nice-modal-react": "^1.2.13",
3131
"axios": "^1.7.3",
3232
"axios-retry": "^4.4.2",
3333
"colord": "^2.9.3",
@@ -53,7 +53,7 @@
5353
"use-query-params": "^2.2.1",
5454
"uuid": "^10.0.0",
5555
"web-vitals": "^1.1.2",
56-
"ydb-ui-components": "^4.2.0",
56+
"ydb-ui-components": "^4.3.2",
5757
"zod": "^3.23.8"
5858
},
5959
"scripts": {

src/containers/Tenant/ObjectSummary/SchemaTree/SchemaTree.tsx

Lines changed: 48 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -9,18 +9,19 @@ import {useCreateDirectoryFeatureAvailable} from '../../../../store/reducers/cap
99
import {selectUserInput} from '../../../../store/reducers/query/query';
1010
import {schemaApi} from '../../../../store/reducers/schema/schema';
1111
import {tableSchemaDataApi} from '../../../../store/reducers/tableSchemaData';
12-
import type {GetTableSchemaDataParams} from '../../../../store/reducers/tableSchemaData';
1312
import type {EPathType, TEvDescribeSchemeResult} from '../../../../types/api/schema';
14-
import {wait} from '../../../../utils';
15-
import {SECOND_IN_MS} from '../../../../utils/constants';
1613
import {
1714
useQueryExecutionSettings,
1815
useTypedDispatch,
1916
useTypedSelector,
2017
} from '../../../../utils/hooks';
2118
import {getConfirmation} from '../../../../utils/hooks/withConfirmation/useChangeInputWithConfirmation';
2219
import {getSchemaControls} from '../../utils/controls';
23-
import {isChildlessPathType, mapPathTypeToNavigationTreeType} from '../../utils/schema';
20+
import {
21+
isChildlessPathType,
22+
mapPathTypeToNavigationTreeType,
23+
nodeTableTypeToPathType,
24+
} from '../../utils/schema';
2425
import {getActions} from '../../utils/schemaActions';
2526
import {CreateDirectoryDialog} from '../CreateDirectoryDialog/CreateDirectoryDialog';
2627
import {useDispatchTreeKey, useTreeKey} from '../UpdateTreeContext';
@@ -33,29 +34,15 @@ interface SchemaTreeProps {
3334
onActivePathUpdate: (path: string) => void;
3435
}
3536

36-
const TABLE_SCHEMA_TIMEOUT = SECOND_IN_MS * 2;
37-
3837
export function SchemaTree(props: SchemaTreeProps) {
3938
const createDirectoryFeatureAvailable = useCreateDirectoryFeatureAvailable();
4039
const {rootPath, rootName, rootType, currentPath, onActivePathUpdate} = props;
4140
const dispatch = useTypedDispatch();
4241
const input = useTypedSelector(selectUserInput);
43-
const [getTableSchemaDataMutation] = tableSchemaDataApi.useGetTableSchemaDataMutation();
44-
45-
const getTableSchemaDataPromise = React.useCallback(
46-
async (args: GetTableSchemaDataParams) => {
47-
try {
48-
const result = await Promise.race([
49-
getTableSchemaDataMutation(args).unwrap(),
50-
wait<undefined>(TABLE_SCHEMA_TIMEOUT),
51-
]);
52-
return result;
53-
} catch (e) {
54-
return undefined;
55-
}
56-
},
57-
[getTableSchemaDataMutation],
58-
);
42+
const [
43+
getTableSchemaDataQuery,
44+
{currentData: actionsSchemaData, isFetching: isActionsDataFetching},
45+
] = tableSchemaDataApi.useLazyGetTableSchemaDataQuery();
5946

6047
const [querySettings, setQueryExecutionSettings] = useQueryExecutionSettings();
6148
const [createDirectoryOpen, setCreateDirectoryOpen] = React.useState(false);
@@ -123,6 +110,36 @@ export function SchemaTree(props: SchemaTreeProps) {
123110
setParentPath(value);
124111
setCreateDirectoryOpen(true);
125112
};
113+
114+
const getTreeNodeActions = React.useMemo(() => {
115+
return getActions(
116+
dispatch,
117+
{
118+
setActivePath: onActivePathUpdate,
119+
updateQueryExecutionSettings: (settings) =>
120+
setQueryExecutionSettings({...querySettings, ...settings}),
121+
showCreateDirectoryDialog: createDirectoryFeatureAvailable
122+
? handleOpenCreateDirectoryDialog
123+
: undefined,
124+
getConfirmation: input ? getConfirmation : undefined,
125+
126+
schemaData: actionsSchemaData,
127+
isSchemaDataLoading: isActionsDataFetching,
128+
},
129+
rootPath,
130+
);
131+
}, [
132+
actionsSchemaData,
133+
createDirectoryFeatureAvailable,
134+
dispatch,
135+
input,
136+
isActionsDataFetching,
137+
onActivePathUpdate,
138+
querySettings,
139+
rootPath,
140+
setQueryExecutionSettings,
141+
]);
142+
126143
return (
127144
<React.Fragment>
128145
<CreateDirectoryDialog
@@ -141,20 +158,15 @@ export function SchemaTree(props: SchemaTreeProps) {
141158
collapsed: false,
142159
}}
143160
fetchPath={fetchPath}
144-
getActions={getActions(
145-
dispatch,
146-
{
147-
setActivePath: onActivePathUpdate,
148-
updateQueryExecutionSettings: (settings) =>
149-
setQueryExecutionSettings({...querySettings, ...settings}),
150-
showCreateDirectoryDialog: createDirectoryFeatureAvailable
151-
? handleOpenCreateDirectoryDialog
152-
: undefined,
153-
getTableSchemaDataPromise,
154-
getConfirmation: input ? getConfirmation : undefined,
155-
},
156-
rootPath,
157-
)}
161+
getActions={getTreeNodeActions}
162+
onActionsOpenToggle={({path, type, isOpen}) => {
163+
const pathType = nodeTableTypeToPathType[type];
164+
if (isOpen && pathType) {
165+
getTableSchemaDataQuery({path, tenantName: rootPath, type: pathType});
166+
}
167+
168+
return [];
169+
}}
158170
renderAdditionalNodeElements={getSchemaControls(dispatch, {
159171
setActivePath: onActivePathUpdate,
160172
})}

src/containers/Tenant/utils/schemaActions.ts renamed to src/containers/Tenant/utils/schemaActions.tsx

Lines changed: 35 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1+
import {Flex, Spin} from '@gravity-ui/uikit';
12
import copy from 'copy-to-clipboard';
23
import type {NavigationTreeNodeType, NavigationTreeProps} from 'ydb-ui-components';
34

45
import type {AppDispatch} from '../../../store';
5-
import type {GetTableSchemaDataParams} from '../../../store/reducers/tableSchemaData';
66
import {TENANT_PAGES_IDS, TENANT_QUERY_TABS_ID} from '../../../store/reducers/tenant/constants';
77
import {setQueryTab, setTenantPage} from '../../../store/reducers/tenant/tenant';
88
import type {QuerySettings} from '../../../types/store/query';
@@ -12,7 +12,6 @@ import {transformPath} from '../ObjectSummary/transformPath';
1212
import type {SchemaData} from '../Schema/SchemaViewer/types';
1313
import i18n from '../i18n';
1414

15-
import {nodeTableTypeToPathType} from './schema';
1615
import type {TemplateFn} from './schemaQueryTemplates';
1716
import {
1817
addTableIndex,
@@ -36,14 +35,13 @@ import {
3635
upsertQueryTemplate,
3736
} from './schemaQueryTemplates';
3837

39-
interface ActionsAdditionalEffects {
38+
interface ActionsAdditionalParams {
4039
updateQueryExecutionSettings: (settings?: Partial<QuerySettings>) => void;
4140
setActivePath: (path: string) => void;
4241
showCreateDirectoryDialog?: (path: string) => void;
43-
getTableSchemaDataPromise?: (
44-
params: GetTableSchemaDataParams,
45-
) => Promise<SchemaData[] | undefined>;
4642
getConfirmation?: () => Promise<boolean>;
43+
schemaData?: SchemaData[];
44+
isSchemaDataLoading?: boolean;
4745
}
4846

4947
interface BindActionParams {
@@ -56,32 +54,18 @@ interface BindActionParams {
5654
const bindActions = (
5755
params: BindActionParams,
5856
dispatch: AppDispatch,
59-
additionalEffects: ActionsAdditionalEffects,
57+
additionalEffects: ActionsAdditionalParams,
6058
) => {
61-
const {setActivePath, showCreateDirectoryDialog, getTableSchemaDataPromise, getConfirmation} =
59+
const {setActivePath, showCreateDirectoryDialog, getConfirmation, schemaData} =
6260
additionalEffects;
6361

6462
const inputQuery = (tmpl: TemplateFn) => () => {
6563
const applyInsert = () => {
66-
const pathType = nodeTableTypeToPathType[params.type];
67-
const withTableData = [selectQueryTemplate, upsertQueryTemplate].includes(tmpl);
68-
69-
const userInputDataPromise =
70-
withTableData && pathType && getTableSchemaDataPromise
71-
? getTableSchemaDataPromise({
72-
path: params.path,
73-
tenantName: params.tenantName,
74-
type: pathType,
75-
})
76-
: Promise.resolve(undefined);
77-
7864
//order is important here: firstly we should open query tab and initialize editor (it will be set to window.ydbEditor), after that it is possible to insert snippet
7965
dispatch(setTenantPage(TENANT_PAGES_IDS.query));
8066
dispatch(setQueryTab(TENANT_QUERY_TABS_ID.newQuery));
8167
setActivePath(params.path);
82-
userInputDataPromise.then((tableData) => {
83-
insertSnippetToEditor(tmpl({...params, tableData}));
84-
});
68+
insertSnippetToEditor(tmpl({...params, schemaData}));
8569
};
8670
if (getConfirmation) {
8771
const confirmedPromise = getConfirmation();
@@ -142,8 +126,25 @@ const bindActions = (
142126

143127
type ActionsSet = ReturnType<Required<NavigationTreeProps>['getActions']>;
144128

129+
interface ActionConfig {
130+
text: string;
131+
action: () => void;
132+
isLoading?: boolean;
133+
}
134+
135+
const getActionWithLoader = ({text, action, isLoading}: ActionConfig) => ({
136+
text: (
137+
<Flex justifyContent="space-between" alignItems="center">
138+
{text}
139+
{isLoading && <Spin size="xs" />}
140+
</Flex>
141+
),
142+
action,
143+
disabled: isLoading,
144+
});
145+
145146
export const getActions =
146-
(dispatch: AppDispatch, additionalEffects: ActionsAdditionalEffects, rootPath = '') =>
147+
(dispatch: AppDispatch, additionalEffects: ActionsAdditionalParams, rootPath = '') =>
147148
(path: string, type: NavigationTreeNodeType) => {
148149
const relativePath = transformPath(path, rootPath);
149150
const actions = bindActions(
@@ -176,8 +177,16 @@ export const getActions =
176177
[
177178
{text: i18n('actions.alterTable'), action: actions.alterTable},
178179
{text: i18n('actions.dropTable'), action: actions.dropTable},
179-
{text: i18n('actions.selectQuery'), action: actions.selectQuery},
180-
{text: i18n('actions.upsertQuery'), action: actions.upsertQuery},
180+
getActionWithLoader({
181+
text: i18n('actions.selectQuery'),
182+
action: actions.selectQuery,
183+
isLoading: additionalEffects.isSchemaDataLoading,
184+
}),
185+
getActionWithLoader({
186+
text: i18n('actions.upsertQuery'),
187+
action: actions.upsertQuery,
188+
isLoading: additionalEffects.isSchemaDataLoading,
189+
}),
181190
{text: i18n('actions.addTableIndex'), action: actions.addTableIndex},
182191
{text: i18n('actions.createCdcStream'), action: actions.createCdcStream},
183192
],

src/containers/Tenant/utils/schemaQueryTemplates.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import type {SchemaData} from '../Schema/SchemaViewer/types';
33
export interface SchemaQueryParams {
44
path: string;
55
relativePath: string;
6-
tableData?: SchemaData[];
6+
schemaData?: SchemaData[];
77
}
88

99
export type TemplateFn = (params?: SchemaQueryParams) => string;
@@ -87,7 +87,7 @@ ALTER TABLE ${path}
8787
export const selectQueryTemplate = (params?: SchemaQueryParams) => {
8888
const path = params?.relativePath ? `\`${params?.relativePath}\`` : '${2:<my_table>}';
8989
const columns =
90-
params?.tableData?.map((column) => '`' + column.name + '`').join(', ') || '${1:*}';
90+
params?.schemaData?.map((column) => '`' + column.name + '`').join(', ') || '${1:*}';
9191
const filters = params?.relativePath ? '' : 'WHERE ${3:Key1 = 1}\nORDER BY ${4:Key1}\n';
9292
return `SELECT ${columns}
9393
FROM ${path}
@@ -96,8 +96,8 @@ ${filters}LIMIT \${5:10};`;
9696
export const upsertQueryTemplate = (params?: SchemaQueryParams) => {
9797
const path = params?.relativePath ? `\`${params?.relativePath}\`` : '${1:<my_table>}';
9898
const columns =
99-
params?.tableData?.map((column) => `\`${column.name}\``).join(', ') || '${2:id, name}';
100-
const values = params?.tableData ? '${3: }' : '${3:1, "foo"}';
99+
params?.schemaData?.map((column) => `\`${column.name}\``).join(', ') || '${2:id, name}';
100+
const values = params?.schemaData ? '${3: }' : '${3:1, "foo"}';
101101
return `UPSERT INTO ${path}
102102
( ${columns} )
103103
VALUES ( ${values} );`;

src/services/api.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -384,7 +384,7 @@ export class YdbEmbeddedAPI extends AxiosWrapper {
384384
);
385385
}
386386
getDescribe(
387-
{path, database}: {path: string; database: string},
387+
{path, database, timeout}: {path: string; database: string; timeout?: number},
388388
{concurrentId, signal}: AxiosOptions = {},
389389
) {
390390
return this.get<Nullable<TEvDescribeSchemeResult>>(
@@ -396,7 +396,7 @@ export class YdbEmbeddedAPI extends AxiosWrapper {
396396
partition_stats: true,
397397
subs: 0,
398398
},
399-
{concurrentId: concurrentId || `getDescribe|${path}`, requestConfig: {signal}},
399+
{concurrentId: concurrentId || `getDescribe|${path}`, requestConfig: {signal}, timeout},
400400
);
401401
}
402402
getSchemaAcl(

src/store/reducers/overview/overview.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,12 +32,16 @@ export const overviewApi = api.injectEndpoints({
3232
providesTags: ['All'],
3333
}),
3434
getOverview: build.query({
35-
queryFn: async ({path, database}: {path: string; database: string}, {signal}) => {
35+
queryFn: async (
36+
{path, database, timeout}: {path: string; database: string; timeout?: number},
37+
{signal},
38+
) => {
3639
try {
3740
const data = await window.api.getDescribe(
3841
{
3942
path,
4043
database,
44+
timeout,
4145
},
4246
{signal},
4347
);

src/store/reducers/tableSchemaData.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import {
55
import type {SchemaData} from '../../containers/Tenant/Schema/SchemaViewer/types';
66
import {isViewType} from '../../containers/Tenant/utils/schema';
77
import type {EPathType} from '../../types/api/schema';
8+
import {SECOND_IN_MS} from '../../utils/constants';
89
import {isQueryErrorResponse} from '../../utils/query';
910

1011
import {api} from './api';
@@ -17,16 +18,19 @@ export interface GetTableSchemaDataParams {
1718
type: EPathType;
1819
}
1920

21+
const TABLE_SCHEMA_TIMEOUT = SECOND_IN_MS * 5;
22+
2023
export const tableSchemaDataApi = api.injectEndpoints({
2124
endpoints: (build) => ({
22-
getTableSchemaData: build.mutation<SchemaData[], GetTableSchemaDataParams>({
25+
getTableSchemaData: build.query<SchemaData[], GetTableSchemaDataParams>({
2326
queryFn: async ({path, tenantName, type}, {dispatch}) => {
2427
try {
2528
if (isViewType(type)) {
2629
const response = await dispatch(
2730
viewSchemaApi.endpoints.getViewSchema.initiate({
2831
database: tenantName,
2932
path,
33+
timeout: TABLE_SCHEMA_TIMEOUT,
3034
}),
3135
);
3236

@@ -42,6 +46,7 @@ export const tableSchemaDataApi = api.injectEndpoints({
4246
overviewApi.endpoints.getOverview.initiate({
4347
path,
4448
database: tenantName,
49+
timeout: TABLE_SCHEMA_TIMEOUT,
4550
}),
4651
);
4752
const result = prepareSchemaData(type, schemaData.data);

0 commit comments

Comments
 (0)