ydb-embedded-ui 1.10.0 → 1.10.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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;
|