ydb-embedded-ui 1.8.8 → 1.10.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. package/CHANGELOG.md +51 -0
  2. package/dist/components/BasicNodeViewer/BasicNodeViewer.scss +43 -0
  3. package/dist/components/BasicNodeViewer/BasicNodeViewer.tsx +53 -0
  4. package/dist/components/BasicNodeViewer/index.ts +1 -0
  5. package/dist/components/EntityStatus/EntityStatus.js +15 -3
  6. package/dist/components/FullNodeViewer/FullNodeViewer.js +29 -48
  7. package/dist/components/FullNodeViewer/FullNodeViewer.scss +0 -45
  8. package/dist/components/IndexInfoViewer/IndexInfoViewer.tsx +52 -0
  9. package/dist/components/InfoViewer/index.ts +4 -0
  10. package/dist/components/InfoViewer/utils.ts +32 -0
  11. package/dist/components/ProgressViewer/ProgressViewer.js +1 -1
  12. package/dist/containers/Node/Node.scss +5 -1
  13. package/dist/containers/Node/Node.tsx +7 -1
  14. package/dist/containers/Node/NodeOverview/NodeOverview.tsx +1 -3
  15. package/dist/containers/Node/NodeStructure/NodeStructure.scss +30 -1
  16. package/dist/containers/Node/NodeStructure/PDiskTitleBadge.tsx +25 -0
  17. package/dist/containers/Node/NodeStructure/Pdisk.tsx +24 -2
  18. package/dist/containers/Nodes/Nodes.js +1 -0
  19. package/dist/containers/Storage/Pdisk/Pdisk.tsx +25 -33
  20. package/dist/containers/Storage/Vdisk/Vdisk.js +2 -0
  21. package/dist/containers/Tablet/Tablet.js +2 -2
  22. package/dist/containers/Tenant/Diagnostics/DiagnosticsPages.ts +24 -14
  23. package/dist/containers/Tenant/Diagnostics/HotKeys/HotKeys.js +3 -3
  24. package/dist/containers/Tenant/Diagnostics/Overview/Overview.tsx +24 -3
  25. package/dist/containers/Tenant/Diagnostics/TopShards/TopShards.js +80 -10
  26. package/dist/containers/Tenant/Schema/SchemaInfoViewer/SchemaInfoViewer.js +20 -16
  27. package/dist/containers/Tenant/Schema/SchemaTree/SchemaTree.tsx +1 -0
  28. package/dist/containers/Tenant/utils/schema.ts +73 -28
  29. package/dist/containers/Tenant/utils/schemaActions.ts +45 -32
  30. package/dist/services/api.js +13 -8
  31. package/dist/store/reducers/executeQuery.js +1 -1
  32. package/dist/store/reducers/executeTopQueries.js +1 -1
  33. package/dist/store/reducers/olapStats.js +5 -1
  34. package/dist/store/reducers/preview.js +1 -1
  35. package/dist/store/reducers/shardsWorkload.js +32 -4
  36. package/dist/types/api/schema.ts +43 -1
  37. package/dist/types/api/storage.ts +54 -0
  38. package/dist/utils/getNodesColumns.js +2 -0
  39. package/dist/utils/pdisk.ts +74 -0
  40. package/dist/utils/tooltip.js +27 -0
  41. package/package.json +2 -2
@@ -8,6 +8,8 @@ import {bytesToGB} from '../../../utils/utils';
8
8
  import routes, {createHref} from '../../../routes';
9
9
  //@ts-ignore
10
10
  import {getPDiskId} from '../../../utils';
11
+ import {getPDiskType} from '../../../utils/pdisk';
12
+ import {TPDiskStateInfo, TPDiskState} from '../../../types/api/storage';
11
13
  import DiskStateProgressBar, {
12
14
  diskProgressColors,
13
15
  } from '../DiskStateProgressBar/DiskStateProgressBar';
@@ -20,38 +22,29 @@ import './Pdisk.scss';
20
22
  const b = cn('pdisk-storage');
21
23
 
22
24
  const stateSeverity = {
23
- Initial: 0,
24
- Normal: 1,
25
- InitialFormatRead: 3,
26
- InitialSysLogRead: 3,
27
- InitialCommonLogRead: 3,
28
- InitialFormatReadError: 5,
29
- InitialSysLogReadError: 5,
30
- InitialSysLogParseError: 5,
31
- InitialCommonLogReadError: 5,
32
- InitialCommonLogParseError: 5,
33
- CommonLoggerInitError: 5,
34
- OpenFileError: 5,
35
- ChunkQuotaError: 5,
36
- DeviceIoError: 5,
25
+ [TPDiskState.Initial]: 0,
26
+ [TPDiskState.Normal]: 1,
27
+ [TPDiskState.InitialFormatRead]: 3,
28
+ [TPDiskState.InitialSysLogRead]: 3,
29
+ [TPDiskState.InitialCommonLogRead]: 3,
30
+ [TPDiskState.InitialFormatReadError]: 5,
31
+ [TPDiskState.InitialSysLogReadError]: 5,
32
+ [TPDiskState.InitialSysLogParseError]: 5,
33
+ [TPDiskState.InitialCommonLogReadError]: 5,
34
+ [TPDiskState.InitialCommonLogParseError]: 5,
35
+ [TPDiskState.CommonLoggerInitError]: 5,
36
+ [TPDiskState.OpenFileError]: 5,
37
+ [TPDiskState.ChunkQuotaError]: 5,
38
+ [TPDiskState.DeviceIoError]: 5,
37
39
  };
38
40
 
39
- type PDiskState = keyof typeof stateSeverity;
40
-
41
- interface PDiskProps {
42
- NodeId: number;
43
- Host?: string;
44
- Path?: string;
45
- Realtime?: string;
46
- Device?: string;
47
- AvailableSize?: string;
48
- TotalSize?: string;
49
- State?: PDiskState;
50
- PDiskId: number;
51
- }
41
+ type PDiskProps = TPDiskStateInfo;
42
+
43
+ const isSeverityKey = (key?: TPDiskState): key is keyof typeof stateSeverity =>
44
+ key !== undefined && key in stateSeverity;
52
45
 
53
- const getStateSeverity = (pDiskState?: PDiskState) => {
54
- return pDiskState ? stateSeverity[pDiskState] : NOT_AVAILABLE_SEVERITY;
46
+ const getStateSeverity = (pDiskState?: TPDiskState) => {
47
+ return isSeverityKey(pDiskState) ? stateSeverity[pDiskState] : NOT_AVAILABLE_SEVERITY;
55
48
  };
56
49
 
57
50
  function Pdisk(props: PDiskProps) {
@@ -76,8 +69,7 @@ function Pdisk(props: PDiskProps) {
76
69
  };
77
70
  /* eslint-disable */
78
71
  const preparePdiskData = () => {
79
- const {AvailableSize, TotalSize, State, PDiskId, NodeId, Host, Path, Realtime, Device} =
80
- props;
72
+ const {AvailableSize, TotalSize, State, PDiskId, NodeId, Path, Realtime, Device} = props;
81
73
  const errorColors = [
82
74
  diskProgressColors[colorSeverity.Orange as keyof typeof diskProgressColors],
83
75
  diskProgressColors[colorSeverity.Red as keyof typeof diskProgressColors],
@@ -89,9 +81,9 @@ function Pdisk(props: PDiskProps) {
89
81
  ];
90
82
 
91
83
  pdiskData.push({property: 'State', value: State || 'not available'});
84
+ pdiskData.push({property: 'Type', value: getPDiskType(props) || 'unknown'});
92
85
  NodeId && pdiskData.push({property: 'Node Id', value: NodeId});
93
86
 
94
- Host && pdiskData.push({property: 'Host', value: Host});
95
87
  Path && pdiskData.push({property: 'Path', value: Path});
96
88
  pdiskData.push({
97
89
  property: 'Available',
@@ -151,7 +143,7 @@ function Pdisk(props: PDiskProps) {
151
143
  href={createHref(
152
144
  routes.node,
153
145
  {id: props.NodeId, activeTab: STRUCTURE},
154
- {pdiskId: props.PDiskId},
146
+ {pdiskId: props.PDiskId || ''},
155
147
  )}
156
148
  />
157
149
  </div>
@@ -7,6 +7,7 @@ import {Popup} from '@yandex-cloud/uikit';
7
7
  import {bytesToGB, bytesToSpeed} from '../../../utils/utils';
8
8
  import routes, {createHref} from '../../../routes';
9
9
  import {stringifyVdiskId, getPDiskId} from '../../../utils';
10
+ import {getPDiskType} from '../../../utils/pdisk';
10
11
  import DiskStateProgressBar, {
11
12
  diskProgressColors,
12
13
  } from '../DiskStateProgressBar/DiskStateProgressBar';
@@ -169,6 +170,7 @@ function Vdisk(props) {
169
170
  property: 'State',
170
171
  value: PDisk.State || 'not available',
171
172
  });
173
+ pdiskData.push({property: 'Type', value: getPDiskType(PDisk) || 'unknown'});
172
174
  PDisk.NodeId && pdiskData.push({property: 'Node Id', value: PDisk.NodeId});
173
175
  PDisk.NodeId &&
174
176
  nodes[PDisk.NodeId] &&
@@ -199,7 +199,7 @@ class Tablet extends React.Component {
199
199
  return (
200
200
  <CriticalActionDialog
201
201
  visible={dialogVisible}
202
- text="The tablet will be killed. Do you want to proceed?"
202
+ text="The tablet will be restarted. Do you want to proceed?"
203
203
  onClose={this.hideDialog}
204
204
  onConfirm={this._onKillClick}
205
205
  />
@@ -363,7 +363,7 @@ class Tablet extends React.Component {
363
363
  disabled={this.isDisabledKill()}
364
364
  className={b('control')}
365
365
  >
366
- Kill
366
+ Restart
367
367
  </Button>
368
368
  {this.hasHiveId() ? (
369
369
  <React.Fragment>
@@ -13,6 +13,11 @@ export enum GeneralPagesIds {
13
13
  'graph' = 'graph',
14
14
  }
15
15
 
16
+ type Page = {
17
+ id: GeneralPagesIds,
18
+ title: string,
19
+ };
20
+
16
21
  const overview = {
17
22
  id: GeneralPagesIds.overview,
18
23
  title: 'Overview',
@@ -76,17 +81,22 @@ export const TABLE_PAGES = [overview, topShards, graph, tablets, hotKeys, descri
76
81
 
77
82
  export const DIR_PAGES = [overview, topShards, describe];
78
83
 
79
- export const getPagesByType = (type?: EPathType) => {
80
- switch (type) {
81
- case EPathType.EPathTypeColumnStore:
82
- case EPathType.EPathTypeSubDomain:
83
- return DATABASE_PAGES;
84
- case EPathType.EPathTypeColumnTable:
85
- case EPathType.EPathTypeTable:
86
- return TABLE_PAGES;
87
- case EPathType.EPathTypeDir:
88
- case EPathType.EPathTypeTableIndex:
89
- default:
90
- return DIR_PAGES;
91
- }
92
- }
84
+ // verbose mapping to guarantee correct tabs for new path types
85
+ // TS will error when a new type is added but not mapped here
86
+ const pathTypeToPages: Record<EPathType, Page[] | undefined> = {
87
+ [EPathType.EPathTypeInvalid]: undefined,
88
+
89
+ [EPathType.EPathTypeSubDomain]: DATABASE_PAGES,
90
+ [EPathType.EPathTypeExtSubDomain]: DATABASE_PAGES,
91
+ [EPathType.EPathTypeColumnStore]: DATABASE_PAGES,
92
+
93
+ [EPathType.EPathTypeTable]: TABLE_PAGES,
94
+ [EPathType.EPathTypeColumnTable]: TABLE_PAGES,
95
+
96
+ [EPathType.EPathTypeDir]: DIR_PAGES,
97
+ [EPathType.EPathTypeTableIndex]: DIR_PAGES,
98
+ [EPathType.EPathTypeCdcStream]: DIR_PAGES,
99
+ };
100
+
101
+ export const getPagesByType = (type?: EPathType) =>
102
+ (type && pathTypeToPages[type]) || DIR_PAGES;
@@ -8,9 +8,10 @@ import Icon from '../../../../components/Icon/Icon';
8
8
 
9
9
  import {AutoFetcher} from '../../../../utils/autofetcher';
10
10
  import {getHotKeys, setHotKeysOptions} from '../../../../store/reducers/hotKeys';
11
- import {EPathType} from '../../../../types/api/schema';
12
11
  import {prepareQueryError} from '../../../../utils';
13
12
 
13
+ import {isColumnEntityType, isTableType} from '../../utils/schema';
14
+
14
15
  import './HotKeys.scss';
15
16
 
16
17
  const b = cn('hot-keys');
@@ -42,8 +43,7 @@ function HotKeys({
42
43
  type,
43
44
  }) {
44
45
  const fetchData = () => {
45
- // ColumnTables excluded intentionally
46
- if (type === EPathType.EPathTypeTable) {
46
+ if (isTableType(type) && !isColumnEntityType(type)) {
47
47
  getHotKeys(currentSchemaPath);
48
48
  }
49
49
  };
@@ -1,4 +1,4 @@
1
- import {useEffect, useMemo} from 'react';
1
+ import {ReactNode, useEffect, useMemo} from 'react';
2
2
  import {useDispatch, useSelector} from 'react-redux';
3
3
  import cn from 'bem-cn-lite';
4
4
 
@@ -6,8 +6,9 @@ import {Loader} from '@yandex-cloud/uikit';
6
6
 
7
7
  //@ts-ignore
8
8
  import SchemaInfoViewer from '../../Schema/SchemaInfoViewer/SchemaInfoViewer';
9
+ import {IndexInfoViewer} from '../../../../components/IndexInfoViewer/IndexInfoViewer';
9
10
 
10
- import type {EPathType} from '../../../../types/api/schema';
11
+ import {EPathType} from '../../../../types/api/schema';
11
12
  import {isColumnEntityType, isTableType} from '../../utils/schema';
12
13
  import {AutoFetcher} from '../../../../utils/autofetcher';
13
14
  //@ts-ignore
@@ -112,11 +113,31 @@ function Overview(props: OverviewProps) {
112
113
  );
113
114
  };
114
115
 
116
+ const renderContent = () => {
117
+ // verbose mapping to guarantee a correct render for new path types
118
+ // TS will error when a new type is added but not mapped here
119
+ const pathTypeToComponent: Record<EPathType, (() => ReactNode) | undefined> = {
120
+ [EPathType.EPathTypeInvalid]: undefined,
121
+ [EPathType.EPathTypeDir]: undefined,
122
+ [EPathType.EPathTypeTable]: undefined,
123
+ [EPathType.EPathTypeSubDomain]: undefined,
124
+ [EPathType.EPathTypeTableIndex]: () => <IndexInfoViewer data={schemaData} />,
125
+ [EPathType.EPathTypeExtSubDomain]: undefined,
126
+ [EPathType.EPathTypeColumnStore]: undefined,
127
+ [EPathType.EPathTypeColumnTable]: undefined,
128
+ [EPathType.EPathTypeCdcStream]: undefined,
129
+ };
130
+
131
+ return (props.type && pathTypeToComponent[props.type]?.()) || (
132
+ <SchemaInfoViewer fullPath={currentItem.Path} data={schemaData} />
133
+ );
134
+ }
135
+
115
136
  return loading && !wasLoaded ? (
116
137
  renderLoader()
117
138
  ) : (
118
139
  <div className={props.className}>
119
- <SchemaInfoViewer fullPath={currentItem.Path} data={schemaData} />
140
+ {renderContent()}
120
141
  </div>
121
142
  );
122
143
  }
@@ -1,4 +1,4 @@
1
- import {useContext, useEffect, useMemo} from 'react';
1
+ import {useState, useContext, useEffect, useMemo} from 'react';
2
2
  import cn from 'bem-cn-lite';
3
3
  import {connect} from 'react-redux';
4
4
  import {Loader} from '@yandex-cloud/uikit';
@@ -14,15 +14,25 @@ import HistoryContext from '../../../../contexts/HistoryContext';
14
14
  import {DEFAULT_TABLE_SETTINGS} from '../../../../utils/constants';
15
15
  import {isColumnEntityType} from '../../utils/schema';
16
16
  import {prepareQueryError} from '../../../../utils';
17
+ import {i18n} from '../../../../utils/i18n';
17
18
 
18
19
  import './TopShards.scss';
19
20
 
20
21
  const b = cn('top-shards');
21
22
  const bLink = cn('yc-link');
22
23
 
24
+ const TABLE_SETTINGS = {
25
+ ...DEFAULT_TABLE_SETTINGS,
26
+ dynamicRender: false, // no more than 20 rows
27
+ externalSort: true,
28
+ disableSortReset: true,
29
+ defaultOrder: DataTable.DESCENDING,
30
+ };
31
+
23
32
  const tableColumnsNames = {
24
33
  TabletId: 'TabletId',
25
34
  CPUCores: 'CPUCores',
35
+ DataSize: 'DataSize',
26
36
  Path: 'Path',
27
37
  };
28
38
 
@@ -32,6 +42,28 @@ function prepareCPUWorkloadValue(value) {
32
42
  return `${(value * 100).toFixed(2)}%`;
33
43
  }
34
44
 
45
+ function prepareDateSizeValue(value) {
46
+ return new Intl.NumberFormat(i18n.lang).format(value);
47
+ }
48
+
49
+ function stringToDataTableSortOrder(value) {
50
+ return value && value.split(',').map((columnId) => ({
51
+ columnId,
52
+ order: DataTable.DESCENDING,
53
+ }));
54
+ }
55
+
56
+ function stringToQuerySortOrder(value) {
57
+ return value && value.split(',').map((columnId) => ({
58
+ columnId,
59
+ order: 'DESC',
60
+ }));
61
+ }
62
+
63
+ function dataTableToStringSortOrder(value = []) {
64
+ return value.map(({columnId}) => columnId).join(',');
65
+ }
66
+
35
67
  function TopShards({
36
68
  sendShardQuery,
37
69
  currentSchemaPath,
@@ -46,25 +78,40 @@ function TopShards({
46
78
  setShardQueryOptions,
47
79
  type,
48
80
  }) {
81
+ const [sortOrder, setSortOrder] = useState(tableColumnsNames.CPUCores);
82
+
49
83
  useEffect(() => {
84
+ autofetcher.stop();
85
+
50
86
  if (autorefresh) {
51
87
  autofetcher.start();
52
- autofetcher.fetch(() => sendShardQuery({database: path, path: currentSchemaPath}));
53
- } else {
54
- autofetcher.stop();
88
+ autofetcher.fetch(() => sendShardQuery({
89
+ database: path,
90
+ path: currentSchemaPath,
91
+ sortOrder: stringToQuerySortOrder(sortOrder),
92
+ }));
55
93
  }
94
+
56
95
  return () => {
57
96
  autofetcher.stop();
58
97
  };
59
- }, [autorefresh]);
98
+ }, [autorefresh, currentSchemaPath, path, sendShardQuery, sortOrder]);
60
99
 
100
+ // don't show loader for requests triggered by table sort, only for path change
61
101
  useEffect(() => {
62
- sendShardQuery({database: path, path: currentSchemaPath});
63
102
  setShardQueryOptions({
64
103
  wasLoaded: false,
65
104
  data: undefined,
66
105
  });
67
- }, [currentSchemaPath]);
106
+ }, [currentSchemaPath, path, setShardQueryOptions]);
107
+
108
+ useEffect(() => {
109
+ sendShardQuery({
110
+ database: path,
111
+ path: currentSchemaPath,
112
+ sortOrder: stringToQuerySortOrder(sortOrder),
113
+ });
114
+ }, [currentSchemaPath, path, sendShardQuery, sortOrder]);
68
115
 
69
116
  const history = useContext(HistoryContext);
70
117
 
@@ -76,6 +123,13 @@ function TopShards({
76
123
  };
77
124
  };
78
125
 
126
+ const onSort = (newSortOrder) => {
127
+ // omit information about sort order to disable ASC order, only DESC makes sense for top shards
128
+ // use a string (and not the DataTable default format) to prevent reference change,
129
+ // which would cause an excess state change, to avoid repeating requests
130
+ setSortOrder(dataTableToStringSortOrder(newSortOrder));
131
+ };
132
+
79
133
  const tableColumns = useMemo(() => {
80
134
  return [
81
135
  {
@@ -83,11 +137,16 @@ function TopShards({
83
137
  // eslint-disable-next-line
84
138
  render: ({value}) => {
85
139
  return (
86
- <span onClick={onSchemaClick(value)} className={bLink({view: 'normal'})}>
140
+ <span
141
+ // tenant name is substringed out in sql query but is needed here
142
+ onClick={onSchemaClick(path + value)}
143
+ className={bLink({view: 'normal'})}
144
+ >
87
145
  {value}
88
146
  </span>
89
147
  );
90
148
  },
149
+ sortable: false,
91
150
  },
92
151
  {
93
152
  name: tableColumnsNames.CPUCores,
@@ -97,6 +156,14 @@ function TopShards({
97
156
  },
98
157
  align: DataTable.RIGHT,
99
158
  },
159
+ {
160
+ name: tableColumnsNames.DataSize,
161
+ header: 'DataSize (B)',
162
+ render: ({value}) => {
163
+ return prepareDateSizeValue(value);
164
+ },
165
+ align: DataTable.RIGHT,
166
+ },
100
167
  {
101
168
  name: tableColumnsNames.TabletId,
102
169
  // eslint-disable-next-line
@@ -107,6 +174,7 @@ function TopShards({
107
174
  </InternalLink>
108
175
  );
109
176
  },
177
+ sortable: false,
110
178
  },
111
179
  ];
112
180
  }, []);
@@ -123,7 +191,7 @@ function TopShards({
123
191
  if (isColumnEntityType(type)) {
124
192
  return 'No data';
125
193
  }
126
- if (error) {
194
+ if (error && !error.isCancelled) {
127
195
  return prepareQueryError(error);
128
196
  }
129
197
 
@@ -132,9 +200,11 @@ function TopShards({
132
200
  <DataTable
133
201
  columns={tableColumns}
134
202
  data={data}
135
- settings={DEFAULT_TABLE_SETTINGS}
203
+ settings={TABLE_SETTINGS}
136
204
  className={b('table')}
137
205
  theme="yandex-cloud"
206
+ onSort={onSort}
207
+ sortOrder={stringToDataTableSortOrder(sortOrder)}
138
208
  />
139
209
  </div>
140
210
  ) : (
@@ -28,29 +28,33 @@ class SchemaInfoViewer extends React.Component {
28
28
  if (data) {
29
29
  const {PathDescription = {}} = data;
30
30
  const {TableStats = {}, TabletMetrics = {}} = PathDescription;
31
- const tableStatsInfo =
32
- TableStats &&
33
- Object.keys(TableStats).map((key) => ({
34
- label: key,
35
- value: TableStats[key].toString(),
36
- }));
31
+ const {PartCount, ...restTableStats} = TableStats;
37
32
 
38
- const tabletMetricsInfo =
39
- TableStats &&
40
- Object.keys(TabletMetrics).map((key) => ({
41
- label: key,
42
- value: this.formatTabletMetricsValue(key, TabletMetrics[key].toString()),
43
- }));
33
+ const priorityInfo = [{
34
+ label: 'PartCount',
35
+ value: PartCount,
36
+ }].filter(({value}) => value !== undefined);
44
37
 
45
- let generalInfo = Object.assign(tableStatsInfo, tabletMetricsInfo);
46
- generalInfo = Object.assign(generalInfo);
38
+ const tableStatsInfo = Object.keys(restTableStats).map((key) => ({
39
+ label: key,
40
+ value: TableStats[key].toString(),
41
+ }));
47
42
 
48
- const infoLength = Object.keys(generalInfo).length;
43
+ const tabletMetricsInfo = Object.keys(TabletMetrics).map((key) => ({
44
+ label: key,
45
+ value: this.formatTabletMetricsValue(key, TabletMetrics[key].toString()),
46
+ }));
49
47
 
48
+ const generalInfo = [
49
+ ...priorityInfo,
50
+ ...tabletMetricsInfo,
51
+ ...tableStatsInfo,
52
+ ];
53
+
50
54
  return (
51
55
  <div className={b()}>
52
56
  <div className={b('item')}>
53
- {infoLength ? (
57
+ {generalInfo.length ? (
54
58
  <InfoViewer info={generalInfo}></InfoViewer>
55
59
  ) : (
56
60
  <div>Empty</div>
@@ -61,6 +61,7 @@ export function SchemaTree(props: SchemaTreeProps) {
61
61
  activePath={currentPath}
62
62
  onActivePathUpdate={handleActivePathUpdate}
63
63
  cache={false}
64
+ virtualize
64
65
  />
65
66
  );
66
67
  }
@@ -1,43 +1,88 @@
1
1
  import type {NavigationTreeNodeType} from 'ydb-ui-components';
2
2
  import {EPathSubType, EPathType} from '../../../types/api/schema';
3
3
 
4
- const mapTablePathSubTypeToNavigationTreeType = (subType?: EPathSubType) => {
5
- switch (subType) {
6
- case EPathSubType.EPathSubTypeSyncIndexImplTable:
7
- case EPathSubType.EPathSubTypeAsyncIndexImplTable:
8
- return 'index_table';
9
- default:
10
- return 'table';
11
- }
4
+ // this file contains verbose mappings that are typed in a way that ensures
5
+ // correctness when a new node type or a new path type is added
6
+ // TS will error if a new entity is added but not mapped here
7
+
8
+ const pathSubTypeToNodeType: Record<EPathSubType, NavigationTreeNodeType | undefined> = {
9
+ [EPathSubType.EPathSubTypeSyncIndexImplTable]: 'index_table',
10
+ [EPathSubType.EPathSubTypeAsyncIndexImplTable]: 'index_table',
11
+
12
+ [EPathSubType.EPathSubTypeStreamImpl]: undefined,
13
+ [EPathSubType.EPathSubTypeEmpty]: undefined,
14
+ };
15
+
16
+ const pathTypeToNodeType: Record<EPathType, NavigationTreeNodeType | undefined> = {
17
+ [EPathType.EPathTypeInvalid]: undefined,
18
+
19
+ [EPathType.EPathTypeSubDomain]: 'database',
20
+ [EPathType.EPathTypeExtSubDomain]: 'database',
21
+
22
+ [EPathType.EPathTypeDir]: 'directory',
23
+ [EPathType.EPathTypeColumnStore]: 'directory',
24
+
25
+ [EPathType.EPathTypeTable]: 'table',
26
+
27
+ [EPathType.EPathTypeTableIndex]: 'index',
28
+
29
+ [EPathType.EPathTypeColumnTable]: 'column_table',
30
+
31
+ [EPathType.EPathTypeCdcStream]: 'topic',
12
32
  };
13
33
 
14
34
  export const mapPathTypeToNavigationTreeType = (
15
35
  type: EPathType = EPathType.EPathTypeDir,
16
36
  subType?: EPathSubType,
17
37
  defaultType: NavigationTreeNodeType = 'directory'
18
- ): NavigationTreeNodeType => {
19
- switch (type) {
20
- case EPathType.EPathTypeSubDomain:
21
- return 'database';
22
- case EPathType.EPathTypeTable:
23
- case EPathType.EPathTypeColumnTable:
24
- return mapTablePathSubTypeToNavigationTreeType(subType);
25
- case EPathType.EPathTypeDir:
26
- case EPathType.EPathTypeColumnStore:
27
- return 'directory';
28
- case EPathType.EPathTypeTableIndex:
29
- return 'index';
30
- default:
31
- return defaultType;
32
- }
38
+ ): NavigationTreeNodeType =>
39
+ (subType && pathSubTypeToNodeType[subType]) || pathTypeToNodeType[type] || defaultType;
40
+
41
+ // ====================
42
+
43
+ const pathTypeToIsTable: Record<EPathType, boolean> = {
44
+ [EPathType.EPathTypeTable]: true,
45
+ [EPathType.EPathTypeColumnTable]: true,
46
+
47
+ [EPathType.EPathTypeInvalid]: false,
48
+ [EPathType.EPathTypeDir]: false,
49
+ [EPathType.EPathTypeSubDomain]: false,
50
+ [EPathType.EPathTypeTableIndex]: false,
51
+ [EPathType.EPathTypeExtSubDomain]: false,
52
+ [EPathType.EPathTypeColumnStore]: false,
53
+ [EPathType.EPathTypeCdcStream]: false,
33
54
  };
34
55
 
35
- export const isTableType = (type?: EPathType) =>
36
- mapPathTypeToNavigationTreeType(type) === 'table';
56
+ export const isTableType = (pathType?: EPathType) =>
57
+ (pathType && pathTypeToIsTable[pathType]) ?? false;
58
+
59
+ // ====================
60
+
61
+ const pathSubTypeToIsIndexImpl: Record<EPathSubType, boolean> = {
62
+ [EPathSubType.EPathSubTypeSyncIndexImplTable]: true,
63
+ [EPathSubType.EPathSubTypeAsyncIndexImplTable]: true,
64
+
65
+ [EPathSubType.EPathSubTypeStreamImpl]: false,
66
+ [EPathSubType.EPathSubTypeEmpty]: false,
67
+ };
37
68
 
38
69
  export const isIndexTable = (subType?: EPathSubType) =>
39
- mapTablePathSubTypeToNavigationTreeType(subType) === 'index_table';
70
+ (subType && pathSubTypeToIsIndexImpl[subType]) ?? false;
71
+
72
+ // ====================
73
+
74
+ const pathTypeToIsColumn: Record<EPathType, boolean> = {
75
+ [EPathType.EPathTypeColumnStore]: true,
76
+ [EPathType.EPathTypeColumnTable]: true,
77
+
78
+ [EPathType.EPathTypeInvalid]: false,
79
+ [EPathType.EPathTypeDir]: false,
80
+ [EPathType.EPathTypeTable]: false,
81
+ [EPathType.EPathTypeSubDomain]: false,
82
+ [EPathType.EPathTypeTableIndex]: false,
83
+ [EPathType.EPathTypeExtSubDomain]: false,
84
+ [EPathType.EPathTypeCdcStream]: false,
85
+ };
40
86
 
41
87
  export const isColumnEntityType = (type?: EPathType) =>
42
- type === EPathType.EPathTypeColumnStore ||
43
- type === EPathType.EPathTypeColumnTable;
88
+ (type && pathTypeToIsColumn[type]) ?? false;