ydb-embedded-ui 4.10.0 → 4.11.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (110) hide show
  1. package/CHANGELOG.md +30 -0
  2. package/dist/components/InfoViewer/formatters/schema.ts +3 -1
  3. package/dist/components/QueryResultTable/Cell/Cell.tsx +8 -8
  4. package/dist/components/QueryResultTable/i18n/en.json +1 -1
  5. package/dist/components/QueryResultTable/i18n/ru.json +1 -1
  6. package/dist/components/ShortyString/ShortyString.tsx +3 -6
  7. package/dist/components/ShortyString/i18n/en.json +8 -8
  8. package/dist/components/ShortyString/i18n/ru.json +8 -8
  9. package/dist/components/SpeedMultiMeter/i18n/index.ts +0 -2
  10. package/dist/components/Stack/Stack.tsx +16 -16
  11. package/dist/components/TableSkeleton/TableSkeleton.tsx +3 -3
  12. package/dist/components/TableWithControlsLayout/TableWithControlsLayout.scss +32 -0
  13. package/dist/components/TableWithControlsLayout/TableWithControlsLayout.tsx +43 -0
  14. package/dist/containers/AsideNavigation/AsideNavigation.tsx +2 -2
  15. package/dist/containers/Cluster/Cluster.scss +4 -5
  16. package/dist/containers/Cluster/Cluster.tsx +3 -22
  17. package/dist/containers/Cluster/ClusterInfo/ClusterInfo.scss +4 -0
  18. package/dist/containers/Cluster/ClusterInfo/ClusterInfo.tsx +14 -3
  19. package/dist/containers/Cluster/ClusterInfoSkeleton/ClusterInfoSkeleton.tsx +1 -1
  20. package/dist/containers/Cluster/utils.tsx +0 -11
  21. package/dist/containers/Header/Header.scss +1 -5
  22. package/dist/containers/Header/Header.tsx +2 -2
  23. package/dist/containers/Header/breadcrumbs.ts +2 -1
  24. package/dist/containers/Heatmap/Heatmap.tsx +4 -3
  25. package/dist/containers/Node/NodeStructure/PDiskTitleBadge.tsx +2 -8
  26. package/dist/containers/Nodes/Nodes.scss +1 -24
  27. package/dist/containers/Nodes/Nodes.tsx +29 -39
  28. package/dist/containers/Storage/EmptyFilter/i18n/en.json +2 -2
  29. package/dist/containers/Storage/EmptyFilter/i18n/ru.json +2 -2
  30. package/dist/containers/Storage/Storage.scss +1 -14
  31. package/dist/containers/Storage/Storage.tsx +15 -18
  32. package/dist/containers/Storage/StorageGroups/i18n/en.json +5 -5
  33. package/dist/containers/Storage/StorageGroups/i18n/ru.json +5 -5
  34. package/dist/containers/Storage/StorageTypeFilter/StorageTypeFilter.tsx +3 -1
  35. package/dist/containers/Storage/{StorageVisibleEntityFilter/StorageVisibleEntityFilter.tsx → StorageVisibleEntitiesFilter/StorageVisibleEntitiesFilter.tsx} +4 -2
  36. package/dist/containers/Storage/UsageFilter/i18n/en.json +3 -8
  37. package/dist/containers/Storage/UsageFilter/i18n/ru.json +3 -8
  38. package/dist/containers/Tablet/Tablet.tsx +2 -2
  39. package/dist/containers/Tenant/Acl/Acl.scss +1 -9
  40. package/dist/containers/Tenant/Acl/Acl.tsx +137 -0
  41. package/dist/containers/Tenant/Diagnostics/Describe/Describe.tsx +2 -2
  42. package/dist/containers/Tenant/Diagnostics/Diagnostics.scss +6 -2
  43. package/dist/containers/Tenant/Diagnostics/DiagnosticsPages.ts +6 -0
  44. package/dist/containers/Tenant/Diagnostics/HotKeys/HotKeys.js +3 -3
  45. package/dist/containers/Tenant/Diagnostics/Overview/Overview.tsx +2 -0
  46. package/dist/containers/Tenant/Diagnostics/Overview/utils/prepareTopicSchemaInfo.ts +2 -3
  47. package/dist/containers/Tenant/ObjectSummary/ObjectSummary.scss +3 -18
  48. package/dist/containers/Tenant/ObjectSummary/ObjectSummary.tsx +95 -88
  49. package/dist/containers/Tenant/Query/Issues/Issues.tsx +27 -23
  50. package/dist/containers/Tenant/Query/Issues/models.ts +0 -11
  51. package/dist/containers/Tenant/Query/Preview/Preview.tsx +3 -3
  52. package/dist/containers/Tenant/Query/i18n/en.json +1 -1
  53. package/dist/containers/Tenant/Query/i18n/ru.json +1 -1
  54. package/dist/containers/Tenant/Schema/SchemaTree/SchemaTree.tsx +2 -2
  55. package/dist/containers/Tenant/Schema/SchemaViewer/SchemaViewer.tsx +99 -0
  56. package/dist/containers/Tenant/Tenant.tsx +1 -5
  57. package/dist/containers/Tenant/TenantPages.tsx +9 -14
  58. package/dist/containers/Tenant/i18n/en.json +11 -0
  59. package/dist/containers/Tenant/i18n/index.ts +11 -0
  60. package/dist/containers/Tenant/i18n/ru.json +11 -0
  61. package/dist/containers/Tenant/utils/schema.ts +24 -0
  62. package/dist/containers/Tenant/utils/schemaActions.ts +28 -24
  63. package/dist/containers/Tenants/Tenants.scss +1 -13
  64. package/dist/containers/Tenants/Tenants.tsx +18 -28
  65. package/dist/services/api.ts +6 -7
  66. package/dist/store/index.js +1 -1
  67. package/dist/store/reducers/nodes/nodes.ts +12 -111
  68. package/dist/store/reducers/nodes/selectors.ts +74 -0
  69. package/dist/store/reducers/nodes/types.ts +22 -3
  70. package/dist/store/reducers/nodes/utils.ts +59 -0
  71. package/dist/store/reducers/preview.ts +6 -4
  72. package/dist/store/reducers/schemaAcl/schemaAcl.ts +17 -0
  73. package/dist/store/reducers/schemaAcl/types.ts +9 -7
  74. package/dist/store/reducers/storage/selectors.ts +1 -1
  75. package/dist/store/reducers/tenant/constants.ts +6 -0
  76. package/dist/store/reducers/tenant/tenant.ts +15 -0
  77. package/dist/store/reducers/tenant/types.ts +18 -3
  78. package/dist/store/state-url-mapping.js +3 -0
  79. package/dist/types/api/cluster.ts +1 -1
  80. package/dist/types/api/compute.ts +27 -2
  81. package/dist/types/api/error.ts +2 -2
  82. package/dist/types/api/netInfo.ts +3 -3
  83. package/dist/types/api/nodes.ts +13 -1
  84. package/dist/types/api/query.ts +1 -1
  85. package/dist/types/api/schema/cdcStream.ts +32 -0
  86. package/dist/types/api/schema/columnEntity.ts +138 -0
  87. package/dist/types/api/schema/externalDataSource.ts +24 -0
  88. package/dist/types/api/schema/externalTable.ts +14 -0
  89. package/dist/types/api/schema/index.ts +10 -0
  90. package/dist/types/api/schema/persQueueGroup.ts +191 -0
  91. package/dist/types/api/schema/schema.ts +302 -0
  92. package/dist/types/api/schema/shared.ts +42 -0
  93. package/dist/types/api/schema/table.ts +616 -0
  94. package/dist/types/api/schema/tableIndex.ts +33 -0
  95. package/dist/types/api/storage.ts +1 -1
  96. package/dist/types/assets.d.ts +1 -2
  97. package/dist/types/store/executeQuery.ts +2 -3
  98. package/dist/types/store/executeTopQueries.ts +8 -5
  99. package/dist/types/store/explainQuery.ts +4 -4
  100. package/dist/types/store/query.ts +4 -3
  101. package/dist/types/store/shardsWorkload.ts +8 -5
  102. package/dist/utils/constants.ts +4 -1
  103. package/dist/utils/error.ts +2 -3
  104. package/dist/utils/query.ts +3 -9
  105. package/dist/utils/tests/providers.tsx +6 -9
  106. package/package.json +6 -2
  107. package/dist/assets/icons/versions.svg +0 -3
  108. package/dist/containers/Tenant/Acl/Acl.js +0 -153
  109. package/dist/containers/Tenant/Schema/SchemaViewer/SchemaViewer.js +0 -94
  110. package/dist/types/api/schema.ts +0 -1326
@@ -26,7 +26,7 @@
26
26
 
27
27
  &__action-button {
28
28
  position: absolute;
29
- top: 8px; // centered relative to the heading
29
+ top: 19px; // centered relative to the heading
30
30
  right: 5px; // centered relative to the collapsed panel
31
31
 
32
32
  background-color: var(--yc-color-base-background);
@@ -35,12 +35,6 @@
35
35
  }
36
36
  }
37
37
 
38
- &__loader {
39
- display: flex;
40
- justify-content: center;
41
- align-items: center;
42
- }
43
-
44
38
  &__tree-wrapper {
45
39
  display: flex;
46
40
  flex-direction: column;
@@ -51,20 +45,11 @@
51
45
  flex: 1 1 auto;
52
46
 
53
47
  height: 100%;
54
- padding: 0 12px 12px;
48
+ padding: 0 12px 12px 16px;
55
49
  }
56
50
 
57
51
  &__tree-header {
58
- display: flex;
59
- flex: 0 0 auto;
60
- justify-content: space-between;
61
- align-items: center;
62
-
63
- padding: 12px 12px 8px;
64
- }
65
-
66
- &__tree-title {
67
- font-weight: 600;
52
+ padding: 23px 12px 17px 20px;
68
53
  }
69
54
 
70
55
  &__sticky-top {
@@ -1,17 +1,13 @@
1
1
  import React, {ReactNode, useEffect, useReducer} from 'react';
2
- import {useDispatch, useSelector} from 'react-redux';
2
+ import {useDispatch} from 'react-redux';
3
+ import {useLocation} from 'react-router';
3
4
  import {Link} from 'react-router-dom';
4
- import cn from 'bem-cn-lite';
5
- import {useHistory, useLocation} from 'react-router';
6
5
  import qs from 'qs';
7
- import _ from 'lodash';
6
+ import cn from 'bem-cn-lite';
8
7
 
9
- import {Button, HelpPopover, Loader, Tabs} from '@gravity-ui/uikit';
8
+ import {Button, HelpPopover, Tabs} from '@gravity-ui/uikit';
10
9
 
11
10
  import SplitPane from '../../../components/SplitPane';
12
- import {SchemaTree} from '../Schema/SchemaTree/SchemaTree';
13
- import Acl from '../Acl/Acl';
14
- import SchemaViewer from '../Schema/SchemaViewer/SchemaViewer';
15
11
  import CopyToClipboard from '../../../components/CopyToClipboard/CopyToClipboard';
16
12
  import InfoViewer from '../../../components/InfoViewer/InfoViewer';
17
13
  import {
@@ -19,41 +15,43 @@ import {
19
15
  PersQueueGroupOverview,
20
16
  } from '../../../components/InfoViewer/schemaOverview';
21
17
  import {Icon} from '../../../components/Icon';
18
+ import {Loader} from '../../../components/Loader';
22
19
 
23
20
  import {
24
21
  EPathSubType,
25
22
  EPathType,
23
+ TColumnDescription,
26
24
  TColumnTableDescription,
27
- TDirEntry,
28
25
  } from '../../../types/api/schema';
29
-
26
+ import routes, {createHref} from '../../../routes';
30
27
  import {formatDateTime} from '../../../utils';
31
- import {isColumnEntityType, isIndexTable, isTableType} from '../utils/schema';
32
-
28
+ import {useTypedSelector} from '../../../utils/hooks';
33
29
  import {
34
30
  DEFAULT_IS_TENANT_COMMON_INFO_COLLAPSED,
35
31
  DEFAULT_SIZE_TENANT_SUMMARY_KEY,
36
32
  } from '../../../utils/constants';
33
+ import {setShowPreview} from '../../../store/reducers/schema/schema';
34
+ import {setQueryTab, setSummaryTab, setTenantPage} from '../../../store/reducers/tenant/tenant';
37
35
  import {
38
- TenantInfoTabsIds,
39
- TenantTabsGroups,
40
- TENANT_INFO_TABS,
41
- TENANT_SCHEMA_TAB,
42
- } from '../TenantPages';
43
- import routes, {createHref} from '../../../routes';
36
+ TENANT_PAGES_IDS,
37
+ TENANT_QUERY_TABS_ID,
38
+ TENANT_SUMMARY_TABS_IDS,
39
+ } from '../../../store/reducers/tenant/constants';
40
+
41
+ import {SchemaTree} from '../Schema/SchemaTree/SchemaTree';
42
+ import {SchemaViewer} from '../Schema/SchemaViewer/SchemaViewer';
43
+ import {Acl} from '../Acl/Acl';
44
+
45
+ import {TenantTabsGroups, TENANT_INFO_TABS, TENANT_SCHEMA_TAB} from '../TenantPages';
44
46
  import {
45
47
  PaneVisibilityActionTypes,
46
48
  paneVisibilityToggleReducerCreator,
47
49
  PaneVisibilityToggleButtons,
48
50
  } from '../utils/paneVisibilityToggleHelpers';
49
- import {setShowPreview} from '../../../store/reducers/schema/schema';
50
- import {setQueryTab, setTenantPage} from '../../../store/reducers/tenant/tenant';
51
- import {
52
- TENANT_PAGES_IDS,
53
- TENANT_QUERY_TABS_ID,
54
- } from '../../../store/reducers/tenant/constants';
51
+ import {isColumnEntityType, isExternalTable, isIndexTable, isTableType} from '../utils/schema';
55
52
 
56
53
  import './ObjectSummary.scss';
54
+ import i18n from '../i18n';
57
55
 
58
56
  const b = cn('object-summary');
59
57
 
@@ -75,7 +73,7 @@ function prepareOlapTableSchema(tableSchema: TColumnTableDescription = {}) {
75
73
  const KeyColumnIds = KeyColumnNames?.map((name: string) => {
76
74
  const column = Columns?.find((el) => el.Name === name);
77
75
  return column?.Id;
78
- });
76
+ }).filter((id): id is number => id !== undefined);
79
77
 
80
78
  return {
81
79
  Columns,
@@ -96,10 +94,15 @@ interface ObjectSummaryProps {
96
94
  onCollapseSummary: VoidFunction;
97
95
  onExpandSummary: VoidFunction;
98
96
  isCollapsed: boolean;
99
- additionalTenantInfo?: any;
100
97
  }
101
98
 
102
- function ObjectSummary(props: ObjectSummaryProps) {
99
+ export function ObjectSummary({
100
+ type,
101
+ subType,
102
+ onCollapseSummary,
103
+ onExpandSummary,
104
+ isCollapsed,
105
+ }: ObjectSummaryProps) {
103
106
  const dispatch = useDispatch();
104
107
  const [commonInfoVisibilityState, dispatchCommonInfoVisibilityState] = useReducer(
105
108
  paneVisibilityToggleReducerCreator(DEFAULT_IS_TENANT_COMMON_INFO_COLLAPSED),
@@ -110,49 +113,48 @@ function ObjectSummary(props: ObjectSummaryProps) {
110
113
  currentSchemaPath,
111
114
  currentSchema: currentItem = {},
112
115
  loading: loadingSchema,
113
- } = useSelector((state: any) => state.schema);
116
+ } = useTypedSelector((state) => state.schema);
117
+ const {summaryTab = TENANT_SUMMARY_TABS_IDS.overview} = useTypedSelector(
118
+ (state) => state.tenant,
119
+ );
114
120
 
115
121
  const location = useLocation();
116
122
 
117
- const history = useHistory();
118
-
119
123
  const queryParams = qs.parse(location.search, {
120
124
  ignoreQueryPrefix: true,
121
125
  });
122
126
 
123
- const {name: tenantName, info: infoTab} = queryParams;
124
- const pathData: TDirEntry | undefined = _.get(
125
- data[tenantName as string],
126
- 'PathDescription.Self',
127
- );
128
- const currentSchemaData: TDirEntry | undefined = _.get(
129
- data[currentSchemaPath],
130
- 'PathDescription.Self',
131
- );
132
-
133
- const tableSchema =
134
- currentItem?.PathDescription?.Table || currentItem?.PathDescription?.ColumnTableDescription;
135
-
136
- const schema =
137
- isTableType(props.type) && isColumnEntityType(props.type)
138
- ? // process data for ColumnTable
139
- prepareOlapTableSchema(tableSchema)
140
- : tableSchema;
127
+ const {name: tenantName} = queryParams;
128
+
129
+ const pathData = tenantName ? data[tenantName.toString()]?.PathDescription?.Self : undefined;
130
+ const currentObjectData = currentSchemaPath ? data[currentSchemaPath] : undefined;
131
+ const currentSchemaData = currentObjectData?.PathDescription?.Self;
132
+
133
+ let keyColumnIds: number[] | undefined;
134
+ let columns: TColumnDescription[] | undefined;
135
+
136
+ if (isTableType(type) && isColumnEntityType(type)) {
137
+ const description = currentObjectData?.PathDescription?.ColumnTableDescription;
138
+ const columnTableSchema = prepareOlapTableSchema(description);
139
+ keyColumnIds = columnTableSchema.KeyColumnIds;
140
+ columns = columnTableSchema.Columns;
141
+ } else if (isExternalTable(type)) {
142
+ columns = currentObjectData?.PathDescription?.ExternalTableDescription?.Columns;
143
+ } else {
144
+ keyColumnIds = currentObjectData?.PathDescription?.Table?.KeyColumnIds;
145
+ columns = currentObjectData?.PathDescription?.Table?.Columns;
146
+ }
141
147
 
142
148
  useEffect(() => {
143
- const {type} = props;
144
149
  const isTable = isTableType(type);
145
150
 
146
- if (type && !isTable && !TENANT_INFO_TABS.find((el) => el.id === infoTab)) {
147
- history.push({
148
- pathname: location.pathname,
149
- search: qs.stringify({...queryParams, info: TenantInfoTabsIds.overview}),
150
- });
151
+ if (type && !isTable && !TENANT_INFO_TABS.find((el) => el.id === summaryTab)) {
152
+ dispatch(setSummaryTab(TENANT_SUMMARY_TABS_IDS.overview));
151
153
  }
152
- }, [props.type]);
154
+ }, [dispatch, type, summaryTab]);
153
155
 
154
156
  const renderTabs = () => {
155
- const isTable = isTableType(props.type);
157
+ const isTable = isTableType(type);
156
158
  const tabsItems = isTable ? [...TENANT_INFO_TABS, ...TENANT_SCHEMA_TAB] : TENANT_INFO_TABS;
157
159
 
158
160
  return (
@@ -160,12 +162,12 @@ function ObjectSummary(props: ObjectSummaryProps) {
160
162
  <Tabs
161
163
  size="l"
162
164
  items={tabsItems}
163
- activeTab={infoTab as string}
165
+ activeTab={summaryTab}
164
166
  wrapTo={({id}, node) => {
165
167
  const path = createHref(routes.tenant, undefined, {
166
168
  ...queryParams,
167
169
  name: tenantName as string,
168
- [TenantTabsGroups.info]: id,
170
+ [TenantTabsGroups.summaryTab]: id,
169
171
  });
170
172
  return (
171
173
  <Link to={path} key={id} className={b('tab')}>
@@ -191,12 +193,12 @@ function ObjectSummary(props: ObjectSummaryProps) {
191
193
  [EPathType.EPathTypeExtSubDomain]: undefined,
192
194
  [EPathType.EPathTypeColumnStore]: undefined,
193
195
  [EPathType.EPathTypeColumnTable]: undefined,
194
- [EPathType.EPathTypeCdcStream]: () => (
195
- <CDCStreamOverview data={data[currentSchemaPath]} />
196
- ),
196
+ [EPathType.EPathTypeCdcStream]: () => <CDCStreamOverview data={currentObjectData} />,
197
197
  [EPathType.EPathTypePersQueueGroup]: () => (
198
- <PersQueueGroupOverview data={data[currentSchemaPath]} />
198
+ <PersQueueGroupOverview data={currentObjectData} />
199
199
  ),
200
+ [EPathType.EPathTypeExternalTable]: undefined,
201
+ [EPathType.EPathTypeExternalDataSource]: undefined,
200
202
  };
201
203
 
202
204
  let component =
@@ -215,17 +217,26 @@ function ObjectSummary(props: ObjectSummaryProps) {
215
217
  return <div className={b('overview-wrapper')}>{component}</div>;
216
218
  };
217
219
 
220
+ const renderLoader = () => {
221
+ // If Loader isn't wrapped with div, SplitPane doesn't calculate panes height correctly
222
+ return (
223
+ <div>
224
+ <Loader />
225
+ </div>
226
+ );
227
+ };
228
+
218
229
  const renderTabContent = () => {
219
- switch (infoTab) {
220
- case TenantInfoTabsIds.acl: {
221
- return <Acl additionalTenantInfo={props.additionalTenantInfo} />;
230
+ switch (summaryTab) {
231
+ case TENANT_SUMMARY_TABS_IDS.acl: {
232
+ return <Acl />;
222
233
  }
223
- case TenantInfoTabsIds.schema: {
234
+ case TENANT_SUMMARY_TABS_IDS.schema: {
224
235
  return loadingSchema ? (
225
236
  renderLoader()
226
237
  ) : (
227
238
  <div className={b('schema')}>
228
- <SchemaViewer data={schema} />
239
+ <SchemaViewer keyColumnIds={keyColumnIds} columns={columns} type={type} />
229
240
  </div>
230
241
  );
231
242
  }
@@ -235,20 +246,10 @@ function ObjectSummary(props: ObjectSummaryProps) {
235
246
  }
236
247
  };
237
248
 
238
- const renderLoader = () => {
239
- return (
240
- <div className={b('loader')}>
241
- <Loader size="m" />
242
- </div>
243
- );
244
- };
245
-
246
249
  const renderTree = () => {
247
250
  return (
248
251
  <div className={b('tree-wrapper')}>
249
- <div className={b('tree-header')}>
250
- <div className={b('tree-title')}>Navigation</div>
251
- </div>
252
+ <div className={b('tree-header')}>{i18n('summary.navigation')}</div>
252
253
  <div className={b('tree')}>
253
254
  {pathData && (
254
255
  <SchemaTree
@@ -283,15 +284,24 @@ function ObjectSummary(props: ObjectSummaryProps) {
283
284
  };
284
285
 
285
286
  const renderCommonInfoControls = () => {
286
- const showPreview = isTableType(props.type) && !isIndexTable(props.subType);
287
+ const showPreview = isTableType(type) && !isIndexTable(subType);
287
288
  return (
288
289
  <React.Fragment>
289
290
  {showPreview && (
290
- <Button view="flat-secondary" onClick={onOpenPreview} title="Show preview">
291
+ <Button
292
+ view="flat-secondary"
293
+ onClick={onOpenPreview}
294
+ title={i18n('summary.showPreview')}
295
+ >
291
296
  <Icon name="tablePreview" viewBox={'0 0 16 16'} height={16} width={16} />
292
297
  </Button>
293
298
  )}
294
- <CopyToClipboard text={currentSchemaPath} title="Copy schema path" />
299
+ {currentSchemaPath && (
300
+ <CopyToClipboard
301
+ text={currentSchemaPath}
302
+ title={i18n('summary.copySchemaPath')}
303
+ />
304
+ )}
295
305
  <PaneVisibilityToggleButtons
296
306
  onCollapse={onCollapseInfoHandler}
297
307
  onExpand={onExpandInfoHandler}
@@ -303,7 +313,6 @@ function ObjectSummary(props: ObjectSummaryProps) {
303
313
  };
304
314
 
305
315
  const renderEntityTypeBadge = () => {
306
- const {type} = props;
307
316
  const {Status, Reason} = currentItem;
308
317
 
309
318
  let message;
@@ -326,7 +335,7 @@ function ObjectSummary(props: ObjectSummaryProps) {
326
335
  }
327
336
  return (
328
337
  <div className={b()}>
329
- <div className={b({hidden: props.isCollapsed})}>
338
+ <div className={b({hidden: isCollapsed})}>
330
339
  <SplitPane
331
340
  direction="vertical"
332
341
  defaultSizePaneKey={DEFAULT_SIZE_TENANT_SUMMARY_KEY}
@@ -355,9 +364,9 @@ function ObjectSummary(props: ObjectSummaryProps) {
355
364
  </SplitPane>
356
365
  </div>
357
366
  <PaneVisibilityToggleButtons
358
- onCollapse={props.onCollapseSummary}
359
- onExpand={props.onExpandSummary}
360
- isCollapsed={props.isCollapsed}
367
+ onCollapse={onCollapseSummary}
368
+ onExpand={onExpandSummary}
369
+ isCollapsed={isCollapsed}
361
370
  initialDirection="left"
362
371
  className={b('action-button')}
363
372
  />
@@ -367,5 +376,3 @@ function ObjectSummary(props: ObjectSummaryProps) {
367
376
 
368
377
  return renderContent();
369
378
  }
370
-
371
- export default ObjectSummary;
@@ -4,33 +4,31 @@ import cn from 'bem-cn-lite';
4
4
  import {Button, Icon, ArrowToggle} from '@gravity-ui/uikit';
5
5
  import ShortyString from '../../../../components/ShortyString/ShortyString';
6
6
 
7
- import {IssueType, SEVERITY, getSeverity} from './models';
7
+ import type {ErrorResponse, IssueMessage} from '../../../../types/api/query';
8
8
 
9
9
  import fatalIcon from '../../../../assets/icons/circle-xmark.svg';
10
10
  import errorIcon from '../../../../assets/icons/triangle-exclamation.svg';
11
11
  import warningIcon from '../../../../assets/icons/circle-exclamation.svg';
12
12
  import infoIcon from '../../../../assets/icons/circle-info.svg';
13
13
 
14
+ import {SEVERITY, getSeverity} from './models';
15
+
14
16
  import './Issues.scss';
15
17
 
16
18
  const blockWrapper = cn('kv-result-issues');
17
19
  const blockIssues = cn('kv-issues');
18
20
  const blockIssue = cn('kv-issue');
19
21
 
20
- type DataIssues = {
21
- error: IssueType;
22
- issues?: IssueType[];
23
- };
24
-
25
22
  interface ResultIssuesProps {
26
- data: DataIssues | string;
23
+ data: ErrorResponse | string;
27
24
  className: string;
28
25
  }
29
26
 
30
27
  export default function ResultIssues({data, className}: ResultIssuesProps) {
31
28
  const [showIssues, setShowIssues] = React.useState(false);
32
29
 
33
- const hasIssues = typeof data === 'string' ? false : Array.isArray(data?.issues);
30
+ const issues = typeof data === 'string' ? undefined : data?.issues;
31
+ const hasIssues = Array.isArray(issues) && issues.length > 0;
34
32
 
35
33
  const renderTitle = () => {
36
34
  let content;
@@ -61,37 +59,37 @@ export default function ResultIssues({data, className}: ResultIssuesProps) {
61
59
  </Button>
62
60
  )}
63
61
  </div>
64
- {hasIssues && showIssues && (
65
- <Issues issues={(data as DataIssues).issues!} className={className} />
66
- )}
62
+ {hasIssues && showIssues && <Issues issues={issues} className={className} />}
67
63
  </div>
68
64
  );
69
65
  }
70
66
 
71
67
  interface IssuesProps {
72
68
  className?: string;
73
- issues: IssueType[];
69
+ issues: IssueMessage[] | null | undefined;
74
70
  }
75
71
  export function Issues({issues, className}: IssuesProps) {
76
- const mostSevereIssue = issues.reduce((result, issue) => {
72
+ const mostSevereIssue = issues?.reduce((result, issue) => {
77
73
  const severity = issue.severity ?? 10;
78
74
  return Math.min(result, severity);
79
75
  }, 10);
80
76
  return (
81
77
  <div className={blockIssues(null, className)}>
82
- {issues.map((issue, index) => (
78
+ {issues?.map((issue, index) => (
83
79
  <Issue key={index} issue={issue} expanded={issue === mostSevereIssue} />
84
80
  ))}
85
81
  </div>
86
82
  );
87
83
  }
88
84
 
89
- function Issue({issue, level = 0}: {issue: IssueType; expanded?: boolean; level?: number}) {
85
+ function Issue({issue, level = 0}: {issue: IssueMessage; expanded?: boolean; level?: number}) {
90
86
  const [isExpand, setIsExpand] = React.useState(true);
91
87
  const severity = getSeverity(issue.severity);
92
- const hasIssues = Array.isArray(issue.issues) && issue.issues.length > 0;
93
88
  const position = getIssuePosition(issue);
94
89
 
90
+ const issues = issue.issues;
91
+ const hasIssues = Array.isArray(issues) && issues.length > 0;
92
+
95
93
  const arrowDirection = isExpand ? 'bottom' : 'right';
96
94
 
97
95
  return (
@@ -123,18 +121,20 @@ function Issue({issue, level = 0}: {issue: IssueType; expanded?: boolean; level?
123
121
  <ShortyString value={issue.message} expandLabel={'Show full message'} />
124
122
  </div>
125
123
  </span>
126
- {issue.code ? <span className={blockIssue('code')}>Code: {issue.code}</span> : null}
124
+ {issue.issue_code ? (
125
+ <span className={blockIssue('code')}>Code: {issue.issue_code}</span>
126
+ ) : null}
127
127
  </div>
128
128
  {hasIssues && isExpand && (
129
129
  <div className={blockIssue('issues')}>
130
- <IssueList issues={issue.issues!} level={level + 1} expanded={isExpand} />
130
+ <IssueList issues={issues} level={level + 1} expanded={isExpand} />
131
131
  </div>
132
132
  )}
133
133
  </div>
134
134
  );
135
135
  }
136
136
 
137
- function IssueList(props: {issues: IssueType[]; expanded: boolean; level: number}) {
137
+ function IssueList(props: {issues: IssueMessage[]; expanded: boolean; level: number}) {
138
138
  const {issues, level, expanded} = props;
139
139
  return (
140
140
  <div className={blockIssue('list')}>
@@ -145,7 +145,7 @@ function IssueList(props: {issues: IssueType[]; expanded: boolean; level: number
145
145
  );
146
146
  }
147
147
 
148
- const severityIcons: Record<SEVERITY, any> = {
148
+ const severityIcons: Record<SEVERITY, string> = {
149
149
  S_INFO: infoIcon,
150
150
  S_WARNING: warningIcon,
151
151
  S_ERROR: errorIcon,
@@ -162,10 +162,14 @@ function IssueSeverity({severity}: {severity: SEVERITY}) {
162
162
  );
163
163
  }
164
164
 
165
- function getIssuePosition(issue: IssueType) {
166
- const {file, position} = issue;
165
+ function getIssuePosition(issue: IssueMessage) {
166
+ const {position = {}} = issue;
167
+
167
168
  if (!position) {
168
169
  return false;
169
170
  }
170
- return `${file ? 'file:' : ''}${position.row}:${position.column}`;
171
+
172
+ const {file, row, column} = position;
173
+
174
+ return `${file ? 'file:' : ''}${row}:${column}`;
171
175
  }
@@ -1,14 +1,3 @@
1
- export interface IssueType {
2
- file?: string;
3
- position?: {row: number; column: number};
4
- // eslint-disable-next-line camelcase
5
- end_position?: {row: number; column: number};
6
- message?: string;
7
- code?: number;
8
- severity?: number;
9
- issues?: IssueType[];
10
- }
11
-
12
1
  export const SEVERITY_LIST = ['S_FATAL', 'S_ERROR', 'S_WARNING', 'S_INFO'] as const;
13
2
 
14
3
  export type SEVERITY = typeof SEVERITY_LIST[number];
@@ -15,7 +15,7 @@ import Fullscreen from '../../../../components/Fullscreen/Fullscreen';
15
15
  import {QueryResultTable} from '../../../../components/QueryResultTable';
16
16
  import EnableFullscreenButton from '../../../../components/EnableFullscreenButton/EnableFullscreenButton';
17
17
 
18
- import {isTableType} from '../../utils/schema';
18
+ import {isExternalTable, isTableType} from '../../utils/schema';
19
19
 
20
20
  import i18n from '../i18n';
21
21
 
@@ -56,7 +56,7 @@ export const Preview = ({database, type}: PreviewProps) => {
56
56
  sendQuery({
57
57
  query,
58
58
  database,
59
- action: 'execute-scan',
59
+ action: isExternalTable(type) ? 'execute-query' : 'execute-scan',
60
60
  }),
61
61
  );
62
62
  },
@@ -103,7 +103,7 @@ export const Preview = ({database, type}: PreviewProps) => {
103
103
  if (!isTableType(type)) {
104
104
  message = <div className={b('message-container')}>{i18n('preview.not-available')}</div>;
105
105
  } else if (error) {
106
- message = <div className={b('message-container')}>{prepareQueryError(error)}</div>;
106
+ message = <div className={b('message-container', 'error')}>{prepareQueryError(error)}</div>;
107
107
  }
108
108
 
109
109
  const content = message ?? (
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "controls.query-mode-selector_type": "Query type:",
3
3
 
4
- "tabs.newQuery": "New query",
4
+ "tabs.newQuery": "Query",
5
5
  "tabs.history": "History",
6
6
  "tabs.saved": "Saved",
7
7
 
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "controls.query-mode-selector_type": "Тип запроса:",
3
3
 
4
- "tabs.newQuery": "Новый запрос",
4
+ "tabs.newQuery": "Запрос",
5
5
  "tabs.history": "История",
6
6
  "tabs.saved": "Сохраненные",
7
7
 
@@ -13,7 +13,7 @@ interface SchemaTreeProps {
13
13
  rootPath: string;
14
14
  rootName: string;
15
15
  rootType?: EPathType;
16
- currentPath: string;
16
+ currentPath?: string;
17
17
  }
18
18
 
19
19
  export function SchemaTree(props: SchemaTreeProps) {
@@ -57,7 +57,7 @@ export function SchemaTree(props: SchemaTreeProps) {
57
57
 
58
58
  useEffect(() => {
59
59
  // if the cached path is not in the current tree, show root
60
- if (!currentPath.startsWith(rootPath)) {
60
+ if (!currentPath?.startsWith(rootPath)) {
61
61
  handleActivePathUpdate(rootPath);
62
62
  }
63
63
  }, []);