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.
- package/CHANGELOG.md +51 -0
- package/dist/components/BasicNodeViewer/BasicNodeViewer.scss +43 -0
- package/dist/components/BasicNodeViewer/BasicNodeViewer.tsx +53 -0
- package/dist/components/BasicNodeViewer/index.ts +1 -0
- package/dist/components/EntityStatus/EntityStatus.js +15 -3
- package/dist/components/FullNodeViewer/FullNodeViewer.js +29 -48
- package/dist/components/FullNodeViewer/FullNodeViewer.scss +0 -45
- package/dist/components/IndexInfoViewer/IndexInfoViewer.tsx +52 -0
- package/dist/components/InfoViewer/index.ts +4 -0
- package/dist/components/InfoViewer/utils.ts +32 -0
- package/dist/components/ProgressViewer/ProgressViewer.js +1 -1
- package/dist/containers/Node/Node.scss +5 -1
- package/dist/containers/Node/Node.tsx +7 -1
- package/dist/containers/Node/NodeOverview/NodeOverview.tsx +1 -3
- package/dist/containers/Node/NodeStructure/NodeStructure.scss +30 -1
- package/dist/containers/Node/NodeStructure/PDiskTitleBadge.tsx +25 -0
- package/dist/containers/Node/NodeStructure/Pdisk.tsx +24 -2
- package/dist/containers/Nodes/Nodes.js +1 -0
- package/dist/containers/Storage/Pdisk/Pdisk.tsx +25 -33
- package/dist/containers/Storage/Vdisk/Vdisk.js +2 -0
- package/dist/containers/Tablet/Tablet.js +2 -2
- package/dist/containers/Tenant/Diagnostics/DiagnosticsPages.ts +24 -14
- package/dist/containers/Tenant/Diagnostics/HotKeys/HotKeys.js +3 -3
- package/dist/containers/Tenant/Diagnostics/Overview/Overview.tsx +24 -3
- package/dist/containers/Tenant/Diagnostics/TopShards/TopShards.js +80 -10
- package/dist/containers/Tenant/Schema/SchemaInfoViewer/SchemaInfoViewer.js +20 -16
- package/dist/containers/Tenant/Schema/SchemaTree/SchemaTree.tsx +1 -0
- package/dist/containers/Tenant/utils/schema.ts +73 -28
- package/dist/containers/Tenant/utils/schemaActions.ts +45 -32
- package/dist/services/api.js +13 -8
- package/dist/store/reducers/executeQuery.js +1 -1
- package/dist/store/reducers/executeTopQueries.js +1 -1
- package/dist/store/reducers/olapStats.js +5 -1
- package/dist/store/reducers/preview.js +1 -1
- package/dist/store/reducers/shardsWorkload.js +32 -4
- package/dist/types/api/schema.ts +43 -1
- package/dist/types/api/storage.ts +54 -0
- package/dist/utils/getNodesColumns.js +2 -0
- package/dist/utils/pdisk.ts +74 -0
- package/dist/utils/tooltip.js +27 -0
- 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
|
40
|
-
|
41
|
-
|
42
|
-
|
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?:
|
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,
|
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
|
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
|
-
|
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
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
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
|
-
|
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
|
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
|
-
|
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({
|
53
|
-
|
54
|
-
|
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
|
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={
|
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
|
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
|
39
|
-
|
40
|
-
|
41
|
-
|
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
|
-
|
46
|
-
|
38
|
+
const tableStatsInfo = Object.keys(restTableStats).map((key) => ({
|
39
|
+
label: key,
|
40
|
+
value: TableStats[key].toString(),
|
41
|
+
}));
|
47
42
|
|
48
|
-
const
|
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
|
-
{
|
57
|
+
{generalInfo.length ? (
|
54
58
|
<InfoViewer info={generalInfo}></InfoViewer>
|
55
59
|
) : (
|
56
60
|
<div>Empty</div>
|
@@ -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
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
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
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
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 = (
|
36
|
-
|
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
|
-
|
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
|
43
|
-
type === EPathType.EPathTypeColumnTable;
|
88
|
+
(type && pathTypeToIsColumn[type]) ?? false;
|