ydb-embedded-ui 1.10.0 → 1.10.3
Sign up to get free protection for your applications and to get access to all the features.
- package/CHANGELOG.md +28 -0
- package/dist/components/IndexInfoViewer/IndexInfoViewer.tsx +10 -7
- package/dist/components/InfoViewer/InfoViewer.scss +1 -2
- package/dist/components/InfoViewer/utils.ts +18 -10
- package/dist/containers/Tenant/Diagnostics/DetailedOverview/DetailedOverview.tsx +15 -14
- package/dist/containers/Tenant/QueryEditor/QueryEditor.js +12 -2
- package/dist/containers/Tenant/Schema/SchemaInfoViewer/SchemaInfoViewer.js +164 -42
- package/dist/containers/Tenant/Schema/SchemaInfoViewer/SchemaInfoViewer.scss +18 -0
- package/dist/containers/Tenant/utils/schemaActions.ts +4 -1
- package/dist/services/api.js +0 -1
- package/dist/store/reducers/executeQuery.js +3 -2
- package/dist/store/reducers/settings.js +20 -13
- package/dist/types/api/schema.ts +117 -4
- package/dist/utils/constants.js +4 -0
- package/dist/utils/index.js +28 -4
- package/dist/utils/pdisk.ts +2 -2
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
@@ -1,5 +1,33 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
## [1.10.3](https://github.com/ydb-platform/ydb-embedded-ui/compare/v1.10.2...v1.10.3) (2022-08-23)
|
4
|
+
|
5
|
+
|
6
|
+
### Bug Fixes
|
7
|
+
|
8
|
+
* **Overview:** format undefined values to empty string, not 0 ([1a37c27](https://github.com/ydb-platform/ydb-embedded-ui/commit/1a37c278328ad8eb4397d9507566829f01a9c872))
|
9
|
+
|
10
|
+
## [1.10.2](https://github.com/ydb-platform/ydb-embedded-ui/compare/v1.10.1...v1.10.2) (2022-08-17)
|
11
|
+
|
12
|
+
|
13
|
+
### Bug Fixes
|
14
|
+
|
15
|
+
* convert bytes on decimal scale ([db9b0a7](https://github.com/ydb-platform/ydb-embedded-ui/commit/db9b0a71fc5334f5a40992cc6abc0688782ad5d2))
|
16
|
+
* display HDD instead of ROT as pdisk type ([bd9e5ba](https://github.com/ydb-platform/ydb-embedded-ui/commit/bd9e5ba4e594cb3a1f6a964f619f9824e083ae7c))
|
17
|
+
* **InfoViewer:** accept default value formatter ([e03d8cc](https://github.com/ydb-platform/ydb-embedded-ui/commit/e03d8cc5de76e4ac00b05586ae6f6522a9708fb0))
|
18
|
+
* **InfoViewer:** allow longer labels ([89060a3](https://github.com/ydb-platform/ydb-embedded-ui/commit/89060a381858b5beaa3c3cf3402c13c917705676))
|
19
|
+
* **Overview:** display table r/o replicas ([6dbe0b4](https://github.com/ydb-platform/ydb-embedded-ui/commit/6dbe0b45fc5e3867f9d6141d270c15508a693e35))
|
20
|
+
* **Overview:** format & group table info in overview ([1a35cfc](https://github.com/ydb-platform/ydb-embedded-ui/commit/1a35cfcd2075454c4a1f1fc4961a4b3106b6d225))
|
21
|
+
* **QueryEditor:** save chosen run action ([b0fb436](https://github.com/ydb-platform/ydb-embedded-ui/commit/b0fb43651e0c6d1dc5d6a25f92716703402b556d))
|
22
|
+
* use current i18n lang for numeral formatting ([5d58fcf](https://github.com/ydb-platform/ydb-embedded-ui/commit/5d58fcffde21924f3cbe6c28946c7a9f755a8490))
|
23
|
+
|
24
|
+
## [1.10.1](https://github.com/ydb-platform/ydb-embedded-ui/compare/v1.10.0...v1.10.1) (2022-08-10)
|
25
|
+
|
26
|
+
|
27
|
+
### Bug Fixes
|
28
|
+
|
29
|
+
* **Tenant:** fix actions set for topics ([0c75bf4](https://github.com/ydb-platform/ydb-embedded-ui/commit/0c75bf4561966dd663ab1cd7c7b81ef6b4632e50))
|
30
|
+
|
3
31
|
## [1.10.0](https://github.com/ydb-platform/ydb-embedded-ui/compare/v1.9.0...v1.10.0) (2022-08-10)
|
4
32
|
|
5
33
|
|
@@ -10,13 +10,16 @@ const DISPLAYED_FIELDS: Set<keyof TIndexDescription> = new Set([
|
|
10
10
|
]);
|
11
11
|
|
12
12
|
const formatItem = createInfoFormatter<TIndexDescription>({
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
13
|
+
values: {
|
14
|
+
Type: (value) => value?.substring(10), // trims EIndexType prefix
|
15
|
+
State: (value) => value?.substring(11), // trims EIndexState prefix
|
16
|
+
KeyColumnNames: (value) => value?.join(', '),
|
17
|
+
DataColumnNames: (value) => value?.join(', '),
|
18
|
+
},
|
19
|
+
labels: {
|
20
|
+
KeyColumnNames: 'Columns',
|
21
|
+
DataColumnNames: 'Includes',
|
22
|
+
},
|
20
23
|
});
|
21
24
|
|
22
25
|
interface IndexInfoViewerProps {
|
@@ -2,7 +2,7 @@ type LabelMap<T> = {
|
|
2
2
|
[label in keyof T]?: string;
|
3
3
|
}
|
4
4
|
|
5
|
-
type
|
5
|
+
type ValueFormatters<T> = {
|
6
6
|
[label in keyof T]?: (value: T[label]) => string | undefined;
|
7
7
|
}
|
8
8
|
|
@@ -13,20 +13,28 @@ function formatLabel<Shape>(label: keyof Shape, map: LabelMap<Shape>) {
|
|
13
13
|
function formatValue<Shape, Key extends keyof Shape>(
|
14
14
|
label: Key,
|
15
15
|
value: Shape[Key],
|
16
|
-
|
16
|
+
formatters: ValueFormatters<Shape>,
|
17
|
+
defaultFormatter?: (value: Shape[Key]) => string | undefined,
|
17
18
|
) {
|
18
|
-
const
|
19
|
-
const
|
19
|
+
const formatter = formatters[label] || defaultFormatter;
|
20
|
+
const formattedValue = formatter ? formatter(value) : value;
|
20
21
|
|
21
|
-
return String(
|
22
|
+
return String(formattedValue ?? '');
|
22
23
|
}
|
23
24
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
)
|
25
|
+
interface CreateInfoFormatterOptions<Shape> {
|
26
|
+
values?: ValueFormatters<Shape>,
|
27
|
+
labels?: LabelMap<Shape>,
|
28
|
+
defaultValueFormatter?: (value: Shape[keyof Shape]) => string | undefined,
|
29
|
+
}
|
30
|
+
|
31
|
+
export function createInfoFormatter<Shape extends Record<string, any>>({
|
32
|
+
values: valueFormatters,
|
33
|
+
labels: labelMap,
|
34
|
+
defaultValueFormatter,
|
35
|
+
}: CreateInfoFormatterOptions<Shape>) {
|
28
36
|
return <Key extends keyof Shape>(label: Key, value: Shape[Key]) => ({
|
29
37
|
label: formatLabel(label, labelMap || {}),
|
30
|
-
value: formatValue(label, value,
|
38
|
+
value: formatValue(label, value, valueFormatters || {}, defaultValueFormatter),
|
31
39
|
});
|
32
40
|
}
|
@@ -60,20 +60,21 @@ function DetailedOverview(props: DetailedOverviewProps) {
|
|
60
60
|
const isTenant = tenantName === currentSchemaPath;
|
61
61
|
return (
|
62
62
|
<div className={b()}>
|
63
|
-
|
64
|
-
|
65
|
-
<
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
63
|
+
{isTenant ? (
|
64
|
+
<>
|
65
|
+
<div className={b('section')}>
|
66
|
+
<TenantOverview tenantName={tenantName} additionalTenantInfo={additionalTenantInfo} />
|
67
|
+
</div>
|
68
|
+
<div className={b('section')}>
|
69
|
+
<Healthcheck
|
70
|
+
tenant={tenantName}
|
71
|
+
preview={true}
|
72
|
+
showMoreHandler={openModalHandler}
|
73
|
+
/>
|
74
|
+
</div>
|
75
|
+
</>
|
76
|
+
) : (
|
77
|
+
<Overview type={type} tenantName={tenantName} />
|
77
78
|
)}
|
78
79
|
</div>
|
79
80
|
);
|
@@ -33,6 +33,7 @@ import {
|
|
33
33
|
DEFAULT_SIZE_RESULT_PANE_KEY,
|
34
34
|
DEFAULT_TABLE_SETTINGS,
|
35
35
|
SAVED_QUERIES_KEY,
|
36
|
+
QUERY_INITIAL_RUN_ACTION_KEY,
|
36
37
|
} from '../../../utils/constants';
|
37
38
|
import {prepareQueryResponse} from '../../../utils/index';
|
38
39
|
|
@@ -538,7 +539,13 @@ function QueryEditor(props) {
|
|
538
539
|
};
|
539
540
|
|
540
541
|
const renderControls = () => {
|
541
|
-
const {
|
542
|
+
const {
|
543
|
+
executeQuery,
|
544
|
+
explainQuery,
|
545
|
+
savedQueries,
|
546
|
+
selectRunAction,
|
547
|
+
setSettingValue,
|
548
|
+
} = props;
|
542
549
|
const {runAction} = executeQuery;
|
543
550
|
const runIsDisabled = !executeQuery.input || executeQuery.loading;
|
544
551
|
const runText = _.find(RUN_ACTIONS, {value: runAction}).content;
|
@@ -546,7 +553,10 @@ function QueryEditor(props) {
|
|
546
553
|
const menuItems = RUN_ACTIONS.map((action) => {
|
547
554
|
return {
|
548
555
|
text: action.content,
|
549
|
-
action: () =>
|
556
|
+
action: () => {
|
557
|
+
selectRunAction(action.value);
|
558
|
+
setSettingValue(QUERY_INITIAL_RUN_ACTION_KEY, action.value);
|
559
|
+
},
|
550
560
|
};
|
551
561
|
});
|
552
562
|
|
@@ -3,63 +3,185 @@ import PropTypes from 'prop-types';
|
|
3
3
|
import cn from 'bem-cn-lite';
|
4
4
|
import './SchemaInfoViewer.scss';
|
5
5
|
|
6
|
-
import {formatCPU, formatBytes} from '../../../../utils';
|
6
|
+
import {formatCPU, formatBytes, formatNumber, formatBps, formatDateTime} from '../../../../utils';
|
7
7
|
|
8
|
-
import InfoViewer from '../../../../components/InfoViewer
|
8
|
+
import {InfoViewer, createInfoFormatter} from '../../../../components/InfoViewer';
|
9
9
|
|
10
10
|
const b = cn('schema-info-viewer');
|
11
11
|
|
12
|
+
const formatTabletMetricsItem = createInfoFormatter({
|
13
|
+
values: {
|
14
|
+
CPU: formatCPU,
|
15
|
+
Memory: formatBytes,
|
16
|
+
Storage: formatBytes,
|
17
|
+
Network: formatBps,
|
18
|
+
ReadThroughput: formatBps,
|
19
|
+
WriteThroughput: formatBps,
|
20
|
+
},
|
21
|
+
defaultValueFormatter: formatNumber,
|
22
|
+
});
|
23
|
+
|
24
|
+
const formatFollowerGroupItem = createInfoFormatter({
|
25
|
+
values: {
|
26
|
+
FollowerCount: formatNumber,
|
27
|
+
},
|
28
|
+
});
|
29
|
+
|
30
|
+
const formatPartitionConfigItem = createInfoFormatter({
|
31
|
+
values: {
|
32
|
+
FollowerCount: formatNumber,
|
33
|
+
CrossDataCenterFollowerCount: formatNumber,
|
34
|
+
},
|
35
|
+
});
|
36
|
+
|
37
|
+
const formatTableStatsItem = createInfoFormatter({
|
38
|
+
values: {
|
39
|
+
DataSize: formatBytes,
|
40
|
+
IndexSize: formatBytes,
|
41
|
+
LastAccessTime: formatDateTime,
|
42
|
+
LastUpdateTime: formatDateTime,
|
43
|
+
},
|
44
|
+
defaultValueFormatter: formatNumber,
|
45
|
+
});
|
46
|
+
|
47
|
+
const formatTableStats = (fields) => Object.entries(fields)
|
48
|
+
.map(([label, value]) => formatTableStatsItem(label, value))
|
49
|
+
.filter(({value}) => Boolean(value));
|
50
|
+
|
12
51
|
class SchemaInfoViewer extends React.Component {
|
13
52
|
static propTypes = {
|
14
53
|
data: PropTypes.object.isRequired,
|
15
54
|
};
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
return formatBytes(value);
|
21
|
-
} else {
|
22
|
-
return value;
|
55
|
+
|
56
|
+
renderItem(itemData, title) {
|
57
|
+
if (!Array.isArray(itemData) || !itemData.length) {
|
58
|
+
return null;
|
23
59
|
}
|
24
|
-
|
60
|
+
|
61
|
+
return (
|
62
|
+
<div className={b('item')}>
|
63
|
+
<InfoViewer
|
64
|
+
title={title}
|
65
|
+
info={itemData}
|
66
|
+
/>
|
67
|
+
</div>
|
68
|
+
);
|
69
|
+
}
|
70
|
+
|
71
|
+
renderContent(data) {
|
72
|
+
const {PathDescription = {}} = data;
|
73
|
+
const {TableStats = {}, TabletMetrics = {}, Table: {PartitionConfig = {}} = {}} = PathDescription;
|
74
|
+
const {
|
75
|
+
PartCount,
|
76
|
+
RowCount,
|
77
|
+
DataSize,
|
78
|
+
IndexSize,
|
79
|
+
|
80
|
+
LastAccessTime,
|
81
|
+
LastUpdateTime,
|
82
|
+
|
83
|
+
ImmediateTxCompleted,
|
84
|
+
PlannedTxCompleted,
|
85
|
+
TxRejectedByOverload,
|
86
|
+
TxRejectedBySpace,
|
87
|
+
TxCompleteLagMsec,
|
88
|
+
InFlightTxCount,
|
89
|
+
|
90
|
+
RowUpdates,
|
91
|
+
RowDeletes,
|
92
|
+
RowReads,
|
93
|
+
RangeReads,
|
94
|
+
RangeReadRows,
|
95
|
+
|
96
|
+
...restTableStats
|
97
|
+
} = TableStats;
|
98
|
+
const {FollowerGroups, FollowerCount, CrossDataCenterFollowerCount} = PartitionConfig;
|
99
|
+
|
100
|
+
const tableStatsInfo = [
|
101
|
+
formatTableStats({
|
102
|
+
PartCount,
|
103
|
+
RowCount,
|
104
|
+
DataSize,
|
105
|
+
IndexSize,
|
106
|
+
}),
|
107
|
+
formatTableStats({
|
108
|
+
LastAccessTime,
|
109
|
+
LastUpdateTime,
|
110
|
+
}),
|
111
|
+
formatTableStats({
|
112
|
+
ImmediateTxCompleted,
|
113
|
+
PlannedTxCompleted,
|
114
|
+
TxRejectedByOverload,
|
115
|
+
TxRejectedBySpace,
|
116
|
+
TxCompleteLagMsec,
|
117
|
+
InFlightTxCount,
|
118
|
+
}),
|
119
|
+
formatTableStats({
|
120
|
+
RowUpdates,
|
121
|
+
RowDeletes,
|
122
|
+
RowReads,
|
123
|
+
RangeReads,
|
124
|
+
RangeReadRows,
|
125
|
+
}),
|
126
|
+
formatTableStats(restTableStats),
|
127
|
+
];
|
128
|
+
|
129
|
+
const tabletMetricsInfo = Object.keys(TabletMetrics).map((key) =>
|
130
|
+
formatTabletMetricsItem(key, TabletMetrics[key])
|
131
|
+
);
|
132
|
+
|
133
|
+
const partitionConfigInfo = [];
|
134
|
+
|
135
|
+
if (Array.isArray(FollowerGroups) && FollowerGroups.length > 0) {
|
136
|
+
partitionConfigInfo.push(...Object.keys(FollowerGroups[0]).map((key) =>
|
137
|
+
formatFollowerGroupItem(key, FollowerGroups[0][key])
|
138
|
+
));
|
139
|
+
} else if (FollowerCount !== undefined) {
|
140
|
+
partitionConfigInfo.push(
|
141
|
+
formatPartitionConfigItem('FollowerCount', FollowerCount)
|
142
|
+
);
|
143
|
+
} else if (CrossDataCenterFollowerCount !== undefined) {
|
144
|
+
partitionConfigInfo.push(
|
145
|
+
formatPartitionConfigItem('CrossDataCenterFollowerCount', CrossDataCenterFollowerCount)
|
146
|
+
);
|
147
|
+
}
|
148
|
+
|
149
|
+
if ([
|
150
|
+
tabletMetricsInfo,
|
151
|
+
partitionConfigInfo,
|
152
|
+
tableStatsInfo.flat(),
|
153
|
+
].flat().length === 0) {
|
154
|
+
return (
|
155
|
+
<div className={b('item')}>Empty</div>
|
156
|
+
);
|
157
|
+
}
|
158
|
+
|
159
|
+
return (
|
160
|
+
<div className={b('row')}>
|
161
|
+
{tabletMetricsInfo.length > 0 || partitionConfigInfo.length > 0 ? (
|
162
|
+
<div className={b('col')}>
|
163
|
+
{this.renderItem(tabletMetricsInfo, 'Tablet Metrics')}
|
164
|
+
{this.renderItem(partitionConfigInfo, 'Partition Config')}
|
165
|
+
</div>
|
166
|
+
) : null}
|
167
|
+
<div className={b('col')}>
|
168
|
+
{tableStatsInfo.map((info, index) => (
|
169
|
+
<React.Fragment key={index}>
|
170
|
+
{this.renderItem(info, index === 0 ? 'Table Stats' : undefined)}
|
171
|
+
</React.Fragment>
|
172
|
+
))}
|
173
|
+
</div>
|
174
|
+
</div>
|
175
|
+
);
|
176
|
+
}
|
177
|
+
|
25
178
|
render() {
|
26
179
|
const {data} = this.props;
|
27
180
|
|
28
181
|
if (data) {
|
29
|
-
const {PathDescription = {}} = data;
|
30
|
-
const {TableStats = {}, TabletMetrics = {}} = PathDescription;
|
31
|
-
const {PartCount, ...restTableStats} = TableStats;
|
32
|
-
|
33
|
-
const priorityInfo = [{
|
34
|
-
label: 'PartCount',
|
35
|
-
value: PartCount,
|
36
|
-
}].filter(({value}) => value !== undefined);
|
37
|
-
|
38
|
-
const tableStatsInfo = Object.keys(restTableStats).map((key) => ({
|
39
|
-
label: key,
|
40
|
-
value: TableStats[key].toString(),
|
41
|
-
}));
|
42
|
-
|
43
|
-
const tabletMetricsInfo = Object.keys(TabletMetrics).map((key) => ({
|
44
|
-
label: key,
|
45
|
-
value: this.formatTabletMetricsValue(key, TabletMetrics[key].toString()),
|
46
|
-
}));
|
47
|
-
|
48
|
-
const generalInfo = [
|
49
|
-
...priorityInfo,
|
50
|
-
...tabletMetricsInfo,
|
51
|
-
...tableStatsInfo,
|
52
|
-
];
|
53
|
-
|
54
182
|
return (
|
55
183
|
<div className={b()}>
|
56
|
-
|
57
|
-
{generalInfo.length ? (
|
58
|
-
<InfoViewer info={generalInfo}></InfoViewer>
|
59
|
-
) : (
|
60
|
-
<div>Empty</div>
|
61
|
-
)}
|
62
|
-
</div>
|
184
|
+
{this.renderContent(data)}
|
63
185
|
</div>
|
64
186
|
);
|
65
187
|
} else {
|
@@ -1,6 +1,24 @@
|
|
1
1
|
.schema-info-viewer {
|
2
2
|
overflow: auto;
|
3
3
|
|
4
|
+
&__row {
|
5
|
+
display: flex;
|
6
|
+
flex-wrap: wrap;
|
7
|
+
justify-content: flex-start;
|
8
|
+
align-items: flex-start;
|
9
|
+
}
|
10
|
+
|
11
|
+
&__col {
|
12
|
+
display: flex;
|
13
|
+
flex-direction: column;
|
14
|
+
justify-content: flex-start;
|
15
|
+
align-items: flex-start;
|
16
|
+
|
17
|
+
& + & {
|
18
|
+
margin-left: 50px;
|
19
|
+
}
|
20
|
+
}
|
21
|
+
|
4
22
|
&__item {
|
5
23
|
margin-bottom: 20px;
|
6
24
|
|
@@ -114,11 +114,14 @@ export const getActions = (
|
|
114
114
|
const nodeTypeToActions: Record<NavigationTreeNodeType, ActionsSet> = {
|
115
115
|
database: DIR_SET,
|
116
116
|
directory: DIR_SET,
|
117
|
+
|
117
118
|
table: TABLE_SET,
|
118
119
|
column_table: TABLE_SET,
|
120
|
+
|
119
121
|
index_table: JUST_COPY,
|
122
|
+
topic: JUST_COPY,
|
123
|
+
|
120
124
|
index: EMPTY_SET,
|
121
|
-
topic: DIR_SET,
|
122
125
|
};
|
123
126
|
|
124
127
|
return nodeTypeToActions[type];
|
package/dist/services/api.js
CHANGED
@@ -1,7 +1,8 @@
|
|
1
1
|
import {createRequestActionTypes, createApiRequest} from '../utils';
|
2
2
|
import '../../services/api';
|
3
3
|
import {getValueFromLS, parseJson} from '../../utils/utils';
|
4
|
-
import {QUERIES_HISTORY_KEY} from '../../utils/constants';
|
4
|
+
import {QUERIES_HISTORY_KEY, QUERY_INITIAL_RUN_ACTION_KEY} from '../../utils/constants';
|
5
|
+
import {readSavedSettingsValue} from './settings';
|
5
6
|
|
6
7
|
const MAXIMUM_QUERIES_IN_HISTORY = 20;
|
7
8
|
|
@@ -39,7 +40,7 @@ const initialState = {
|
|
39
40
|
? MAXIMUM_QUERIES_IN_HISTORY - 1
|
40
41
|
: queriesHistoryInitial.length - 1,
|
41
42
|
},
|
42
|
-
runAction: RUN_ACTIONS_VALUES.script,
|
43
|
+
runAction: readSavedSettingsValue(QUERY_INITIAL_RUN_ACTION_KEY, RUN_ACTIONS_VALUES.script),
|
43
44
|
monacoHotKey: null,
|
44
45
|
};
|
45
46
|
|
@@ -1,4 +1,11 @@
|
|
1
|
-
import {
|
1
|
+
import {
|
2
|
+
defaultUserSettings,
|
3
|
+
ALL,
|
4
|
+
SAVED_QUERIES_KEY,
|
5
|
+
THEME_KEY,
|
6
|
+
TENANT_INITIAL_TAB_KEY,
|
7
|
+
QUERY_INITIAL_RUN_ACTION_KEY,
|
8
|
+
} from '../../utils/constants';
|
2
9
|
import '../../services/api';
|
3
10
|
import {getValueFromLS} from '../../utils/utils';
|
4
11
|
|
@@ -7,24 +14,24 @@ const SET_SETTING_VALUE = 'settings/SET_VALUE';
|
|
7
14
|
|
8
15
|
const userSettings = window.userSettings || {};
|
9
16
|
const systemSettings = window.systemSettings || {};
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
: getValueFromLS(TENANT_INITIAL_TAB_KEY);
|
17
|
+
|
18
|
+
export function readSavedSettingsValue(key, defaultValue) {
|
19
|
+
const savedValue = window.web_version
|
20
|
+
? userSettings[key]
|
21
|
+
: getValueFromLS(key);
|
22
|
+
|
23
|
+
return savedValue ?? defaultValue;
|
24
|
+
}
|
19
25
|
|
20
26
|
export const initialState = {
|
21
27
|
problemFilter: ALL,
|
22
28
|
userSettings: {
|
23
29
|
...defaultUserSettings,
|
24
30
|
...userSettings,
|
25
|
-
theme,
|
26
|
-
[SAVED_QUERIES_KEY]:
|
27
|
-
[TENANT_INITIAL_TAB_KEY]:
|
31
|
+
theme: readSavedSettingsValue(THEME_KEY, 'light'),
|
32
|
+
[SAVED_QUERIES_KEY]: readSavedSettingsValue(SAVED_QUERIES_KEY, '[]'),
|
33
|
+
[TENANT_INITIAL_TAB_KEY]: readSavedSettingsValue(TENANT_INITIAL_TAB_KEY),
|
34
|
+
[QUERY_INITIAL_RUN_ACTION_KEY]: readSavedSettingsValue(QUERY_INITIAL_RUN_ACTION_KEY),
|
28
35
|
},
|
29
36
|
systemSettings,
|
30
37
|
};
|
package/dist/types/api/schema.ts
CHANGED
@@ -16,7 +16,7 @@ export interface TEvDescribeSchemeResult {
|
|
16
16
|
PathOwnerId?: string;
|
17
17
|
}
|
18
18
|
|
19
|
-
enum EStatus
|
19
|
+
enum EStatus {
|
20
20
|
StatusSuccess = 'StatusSuccess',
|
21
21
|
StatusAccepted = 'StatusAccepted',
|
22
22
|
StatusPathDoesNotExist = 'StatusPathDoesNotExist',
|
@@ -47,8 +47,8 @@ interface TPathDescription {
|
|
47
47
|
Children?: TDirEntry[];
|
48
48
|
|
49
49
|
// for table
|
50
|
-
Table?:
|
51
|
-
TableStats?:
|
50
|
+
Table?: TTableDescription;
|
51
|
+
TableStats?: TTableStats;
|
52
52
|
TabletMetrics?: unknown;
|
53
53
|
TablePartitions?: unknown[];
|
54
54
|
|
@@ -82,6 +82,119 @@ interface TDirEntry {
|
|
82
82
|
Version?: TPathVersion;
|
83
83
|
}
|
84
84
|
|
85
|
+
// incomplete
|
86
|
+
export interface TTableDescription {
|
87
|
+
PartitionConfig?: TPartitionConfig;
|
88
|
+
}
|
89
|
+
|
90
|
+
// incomplete
|
91
|
+
export interface TPartitionConfig {
|
92
|
+
/** uint64 */
|
93
|
+
FollowerCount?: string;
|
94
|
+
/**
|
95
|
+
* uint32
|
96
|
+
* @deprecated use FollowerGroups
|
97
|
+
*/
|
98
|
+
CrossDataCenterFollowerCount?: string;
|
99
|
+
/** 0 or 1 items */
|
100
|
+
FollowerGroups?: TFollowerGroup[];
|
101
|
+
}
|
102
|
+
|
103
|
+
export interface TFollowerGroup {
|
104
|
+
/** uint32 */
|
105
|
+
FollowerCount?: string;
|
106
|
+
AllowLeaderPromotion?: boolean;
|
107
|
+
AllowClientRead?: boolean;
|
108
|
+
/** uint32[] */
|
109
|
+
AllowedNodeIDs?: string[];
|
110
|
+
/**
|
111
|
+
* uint32[]
|
112
|
+
* @deprecated use AllowedDataCenters
|
113
|
+
*/
|
114
|
+
AllowedDataCenterNumIDs?: string[];
|
115
|
+
RequireAllDataCenters?: boolean;
|
116
|
+
LocalNodeOnly?: boolean;
|
117
|
+
RequireDifferentNodes?: boolean;
|
118
|
+
FollowerCountPerDataCenter?: boolean; // multiplies FollowerCount by number of DataCenters
|
119
|
+
AllowedDataCenters?: string[];
|
120
|
+
}
|
121
|
+
|
122
|
+
interface TTableStats {
|
123
|
+
/** uint64 */
|
124
|
+
DataSize?: string;
|
125
|
+
/** uint64 */
|
126
|
+
RowCount?: string;
|
127
|
+
/** uint64 */
|
128
|
+
IndexSize?: string;
|
129
|
+
/** uint64 */
|
130
|
+
InMemSize?: string;
|
131
|
+
|
132
|
+
/**
|
133
|
+
* uint64
|
134
|
+
* unix time in millisec
|
135
|
+
*/
|
136
|
+
LastAccessTime?: string;
|
137
|
+
/**
|
138
|
+
* uint64
|
139
|
+
* unix time in millisec
|
140
|
+
*/
|
141
|
+
LastUpdateTime?: string;
|
142
|
+
|
143
|
+
RowCountHistogram?: THistogram;
|
144
|
+
DataSizeHistogram?: THistogram;
|
145
|
+
|
146
|
+
/** uint64 */
|
147
|
+
ImmediateTxCompleted?: string;
|
148
|
+
/** uint64 */
|
149
|
+
PlannedTxCompleted?: string;
|
150
|
+
/** uint64 */
|
151
|
+
TxRejectedByOverload?: string;
|
152
|
+
/** uint64 */
|
153
|
+
TxRejectedBySpace?: string;
|
154
|
+
/** uint64 */
|
155
|
+
TxCompleteLagMsec?: string;
|
156
|
+
/** uint64 */
|
157
|
+
InFlightTxCount?: string;
|
158
|
+
|
159
|
+
/** uint64 */
|
160
|
+
RowUpdates?: string;
|
161
|
+
/** uint64 */
|
162
|
+
RowDeletes?: string;
|
163
|
+
/** uint64 */
|
164
|
+
RowReads?: string;
|
165
|
+
/** uint64 */
|
166
|
+
RangeReads?: string;
|
167
|
+
/** uint64 */
|
168
|
+
RangeReadRows?: string;
|
169
|
+
|
170
|
+
/** uint64 */
|
171
|
+
PartCount?: string;
|
172
|
+
|
173
|
+
KeyAccessSample?: THistogram;
|
174
|
+
|
175
|
+
/** uint64 */
|
176
|
+
SearchHeight?: string;
|
177
|
+
|
178
|
+
/**
|
179
|
+
* uint64
|
180
|
+
* seconds since epoch
|
181
|
+
*/
|
182
|
+
LastFullCompactionTs?: string;
|
183
|
+
|
184
|
+
// i.e. this shard lent to other shards
|
185
|
+
HasLoanedParts?: boolean;
|
186
|
+
}
|
187
|
+
|
188
|
+
interface THistogram {
|
189
|
+
Buckets?: THistogramBucket[];
|
190
|
+
}
|
191
|
+
|
192
|
+
interface THistogramBucket {
|
193
|
+
Key?: string;
|
194
|
+
/** uint64 */
|
195
|
+
Value?: string;
|
196
|
+
}
|
197
|
+
|
85
198
|
export interface TIndexDescription {
|
86
199
|
Name?: string;
|
87
200
|
/** uint64 */
|
@@ -111,7 +224,7 @@ export enum EPathType {
|
|
111
224
|
|
112
225
|
EPathTypeSubDomain = 'EPathTypeSubDomain',
|
113
226
|
|
114
|
-
EPathTypeTableIndex = 'EPathTypeTableIndex',
|
227
|
+
EPathTypeTableIndex = 'EPathTypeTableIndex',
|
115
228
|
EPathTypeExtSubDomain = 'EPathTypeExtSubDomain',
|
116
229
|
|
117
230
|
EPathTypeColumnStore = 'EPathTypeColumnStore',
|
package/dist/utils/constants.js
CHANGED
@@ -6,6 +6,9 @@ export const GROUP_AUTO_RELOAD_INTERVAL = 10 * SECOND;
|
|
6
6
|
export const PDISK_AUTO_RELOAD_INTERVAL = 10 * SECOND;
|
7
7
|
export const VDISK_AUTO_RELOAD_INTERVAL = 10 * SECOND;
|
8
8
|
export const AUTO_RELOAD_INTERVAL = 10 * SECOND;
|
9
|
+
// by agreement, display all byte values in decimal scale
|
10
|
+
// values in data are always in bytes, never in higher units,
|
11
|
+
// therefore there is no issue arbitrary converting them in UI
|
9
12
|
export const MEGABYTE = 1_000_000;
|
10
13
|
export const GIGABYTE = 1_000_000_000;
|
11
14
|
export const TERABYTE = 1_000_000_000_000;
|
@@ -139,3 +142,4 @@ export const DEFAULT_TABLE_SETTINGS = {
|
|
139
142
|
};
|
140
143
|
|
141
144
|
export const TENANT_INITIAL_TAB_KEY = 'saved_tenant_initial_tab';
|
145
|
+
export const QUERY_INITIAL_RUN_ACTION_KEY = 'query_initial_run_action';
|
package/dist/utils/index.js
CHANGED
@@ -1,16 +1,24 @@
|
|
1
1
|
import numeral from 'numeral';
|
2
|
+
import locales from 'numeral/locales'; // eslint-disable-line no-unused-vars
|
2
3
|
import _ from 'lodash';
|
3
4
|
|
5
|
+
import {i18n} from './i18n';
|
4
6
|
import {MEGABYTE, TERABYTE, DAY_IN_SECONDS, GIGABYTE} from './constants';
|
7
|
+
import {isNumeric} from './utils';
|
5
8
|
|
6
|
-
|
7
|
-
numeral.locale('ru');
|
8
|
-
numeral.localeData().delimiters.decimal = '.';
|
9
|
+
numeral.locale(i18n.lang);
|
9
10
|
|
10
11
|
export const formatBytes = (bytes) => {
|
11
|
-
|
12
|
+
if (!isNumeric(bytes)) {
|
13
|
+
return '';
|
14
|
+
}
|
15
|
+
|
16
|
+
// by agreement, display byte values in decimal scale
|
17
|
+
return numeral(bytes).format('0 b');
|
12
18
|
};
|
13
19
|
|
20
|
+
export const formatBps = (bytes) => formatBytes(bytes) + '/s';
|
21
|
+
|
14
22
|
export const formatBytesToGigabyte = (bytes) => {
|
15
23
|
return `${Math.floor(bytes / GIGABYTE)} GB`;
|
16
24
|
};
|
@@ -49,13 +57,29 @@ export const formatThroughput = (value, total) => {
|
|
49
57
|
};
|
50
58
|
|
51
59
|
export const formatNumber = (number) => {
|
60
|
+
if (!isNumeric(number)) {
|
61
|
+
return '';
|
62
|
+
}
|
63
|
+
|
52
64
|
return numeral(number).format();
|
53
65
|
};
|
54
66
|
|
55
67
|
export const formatCPU = (value) => {
|
68
|
+
if (!isNumeric(value)) {
|
69
|
+
return '';
|
70
|
+
}
|
71
|
+
|
56
72
|
return numeral(value / 1000000).format('0.00');
|
57
73
|
};
|
58
74
|
|
75
|
+
export const formatDateTime = (value) => {
|
76
|
+
if (!isNumeric(value)) {
|
77
|
+
return '';
|
78
|
+
}
|
79
|
+
|
80
|
+
return value > 0 ? new Date(Number(value)).toUTCString() : 'N/A';
|
81
|
+
};
|
82
|
+
|
59
83
|
export const calcUptime = (milliseconds) => {
|
60
84
|
const currentDate = new Date();
|
61
85
|
return formatUptime((currentDate - Number(milliseconds)) / 1000);
|
package/dist/utils/pdisk.ts
CHANGED
@@ -28,7 +28,7 @@ export const parseBitField = <T extends Record<string, number>>(
|
|
28
28
|
};
|
29
29
|
|
30
30
|
export enum IPDiskType {
|
31
|
-
|
31
|
+
HDD = 'HDD', // ROT (Rotation?) = HDD
|
32
32
|
SSD = 'SSD',
|
33
33
|
MVME = 'NVME',
|
34
34
|
}
|
@@ -67,7 +67,7 @@ export const getPDiskType = (data: TPDiskStateInfo): IPDiskType | undefined => {
|
|
67
67
|
return IPDiskType.MVME;
|
68
68
|
}
|
69
69
|
} else if (categoryBitField.typeExt === '0') {
|
70
|
-
return IPDiskType.
|
70
|
+
return IPDiskType.HDD;
|
71
71
|
}
|
72
72
|
|
73
73
|
return undefined;
|