ydb-embedded-ui 3.3.2 → 3.3.3
Sign up to get free protection for your applications and to get access to all the features.
- package/CHANGELOG.md +18 -0
- package/dist/components/Errors/ResponseError/ResponseError.tsx +17 -0
- package/dist/components/Errors/ResponseError/index.ts +1 -0
- package/dist/components/Errors/i18n/en.json +2 -1
- package/dist/components/Errors/i18n/ru.json +2 -1
- package/dist/components/FullGroupViewer/FullGroupViewer.js +1 -1
- package/dist/components/InfoViewer/InfoViewer.scss +1 -1
- package/dist/components/InfoViewer/InfoViewer.tsx +29 -21
- package/dist/components/InfoViewer/formatters/index.ts +1 -0
- package/dist/components/InfoViewer/formatters/table.ts +40 -0
- package/dist/components/QueryExecutionStatus/QueryExecutionStatus.tsx +26 -8
- package/dist/components/QueryExecutionStatus/index.ts +1 -0
- package/dist/components/QueryResultTable/QueryResultTable.tsx +2 -2
- package/dist/containers/App/Content.js +12 -5
- package/dist/containers/AsideNavigation/AsideNavigation.tsx +10 -13
- package/dist/containers/Authentication/Authentication.scss +6 -0
- package/dist/containers/Authentication/Authentication.tsx +34 -15
- package/dist/containers/Node/NodeStructure/Pdisk.tsx +7 -10
- package/dist/containers/Nodes/Nodes.tsx +1 -1
- package/dist/containers/Nodes/getNodesColumns.tsx +4 -4
- package/dist/containers/Storage/PDisk/PDisk.tsx +25 -17
- package/dist/containers/Storage/PDisk/__tests__/colors.tsx +64 -1
- package/dist/containers/Storage/Storage.js +1 -1
- package/dist/containers/Storage/StorageGroups/StorageGroups.tsx +4 -3
- package/dist/containers/Storage/StorageNodes/StorageNodes.tsx +1 -1
- package/dist/containers/Storage/utils/index.ts +26 -10
- package/dist/containers/Tablet/Tablet.js +1 -1
- package/dist/containers/Tenant/Acl/Acl.js +1 -1
- package/dist/containers/Tenant/Diagnostics/Consumers/Consumers.tsx +2 -2
- package/dist/containers/Tenant/Diagnostics/HotKeys/HotKeys.js +1 -1
- package/dist/containers/Tenant/Diagnostics/OverloadedShards/OverloadedShards.tsx +6 -1
- package/dist/containers/Tenant/Diagnostics/Overview/Overview.tsx +10 -21
- package/dist/containers/Tenant/{Schema/SchemaInfoViewer/SchemaInfoViewer.scss → Diagnostics/Overview/TableInfo/TableInfo.scss} +8 -10
- package/dist/containers/Tenant/Diagnostics/Overview/TableInfo/TableInfo.tsx +71 -0
- package/dist/containers/Tenant/Diagnostics/Overview/TableInfo/i18n/en.json +5 -0
- package/dist/containers/Tenant/Diagnostics/Overview/TableInfo/i18n/index.ts +11 -0
- package/dist/containers/Tenant/Diagnostics/Overview/TableInfo/i18n/ru.json +5 -0
- package/dist/containers/Tenant/Diagnostics/Overview/TableInfo/index.ts +1 -0
- package/dist/containers/Tenant/Diagnostics/Overview/TableInfo/prepareTableInfo.ts +96 -0
- package/dist/containers/Tenant/Diagnostics/TopQueries/TopQueries.tsx +7 -1
- package/dist/containers/Tenant/ObjectSummary/ObjectSummary.tsx +3 -1
- package/dist/containers/Tenant/QueryEditor/QueryExplain/QueryExplain.js +16 -11
- package/dist/containers/Tenant/QueryEditor/QueryResult/QueryResult.js +37 -23
- package/dist/containers/Tenant/QueryEditor/QueryResult/QueryResult.scss +4 -0
- package/dist/containers/Tenant/Schema/SchemaViewer/SchemaViewer.js +1 -1
- package/dist/containers/Tenants/Tenants.js +4 -3
- package/dist/routes.ts +1 -0
- package/dist/services/api.js +4 -1
- package/dist/store/reducers/shardsWorkload.ts +2 -1
- package/dist/store/reducers/storage.js +1 -1
- package/dist/utils/constants.ts +1 -1
- package/dist/utils/index.js +3 -1
- package/dist/utils/prepareQueryExplain.ts +1 -1
- package/package.json +5 -3
- package/dist/containers/Tenant/Diagnostics/Overview/Overview.scss +0 -13
- package/dist/containers/Tenant/Schema/SchemaInfoViewer/SchemaInfoViewer.js +0 -201
@@ -7,7 +7,7 @@ import {TPDiskState} from '../../../../types/api/pdisk';
|
|
7
7
|
import {PDisk} from '../PDisk';
|
8
8
|
|
9
9
|
describe('PDisk state', () => {
|
10
|
-
it('Should determine severity based on State', () => {
|
10
|
+
it('Should determine severity based on State if space severity is OK', () => {
|
11
11
|
const {getAllByRole} = renderWithStore(
|
12
12
|
<MemoryRouter>
|
13
13
|
<PDisk nodeId={1} data={{State: TPDiskState.Normal}} />
|
@@ -20,6 +20,55 @@ describe('PDisk state', () => {
|
|
20
20
|
expect(normalDisk.className).not.toBe(erroredDisk.className);
|
21
21
|
});
|
22
22
|
|
23
|
+
it('Should determine severity based on space utilization if state severity is OK', () => {
|
24
|
+
const {getAllByRole} = renderWithStore(
|
25
|
+
<MemoryRouter>
|
26
|
+
<PDisk
|
27
|
+
nodeId={1}
|
28
|
+
data={{State: TPDiskState.Normal, AvailableSize: '100', TotalSize: '100'}}
|
29
|
+
/>
|
30
|
+
<PDisk
|
31
|
+
nodeId={2}
|
32
|
+
data={{State: TPDiskState.Normal, AvailableSize: '14', TotalSize: '100'}}
|
33
|
+
/>
|
34
|
+
<PDisk
|
35
|
+
nodeId={3}
|
36
|
+
data={{State: TPDiskState.Normal, AvailableSize: '4', TotalSize: '100'}}
|
37
|
+
/>
|
38
|
+
</MemoryRouter>,
|
39
|
+
);
|
40
|
+
|
41
|
+
const [disk1, disk2, disk3] = getAllByRole('meter');
|
42
|
+
|
43
|
+
expect(disk1.className).toMatch(/_green\b/i);
|
44
|
+
expect(disk2.className).toMatch(/_yellow\b/i);
|
45
|
+
expect(disk3.className).toMatch(/_red\b/i);
|
46
|
+
});
|
47
|
+
|
48
|
+
it('Should determine severity based on max severity of state and space utilization ', () => {
|
49
|
+
const {getAllByRole} = renderWithStore(
|
50
|
+
<MemoryRouter>
|
51
|
+
<PDisk
|
52
|
+
nodeId={1}
|
53
|
+
data={{
|
54
|
+
State: TPDiskState.ChunkQuotaError,
|
55
|
+
AvailableSize: '100',
|
56
|
+
TotalSize: '100',
|
57
|
+
}}
|
58
|
+
/>
|
59
|
+
<PDisk
|
60
|
+
nodeId={2}
|
61
|
+
data={{State: TPDiskState.Normal, AvailableSize: '4', TotalSize: '100'}}
|
62
|
+
/>
|
63
|
+
</MemoryRouter>,
|
64
|
+
);
|
65
|
+
|
66
|
+
const [disk1, disk2] = getAllByRole('meter');
|
67
|
+
|
68
|
+
expect(disk1.className).toMatch(/_red\b/i);
|
69
|
+
expect(disk2.className).toMatch(/_red\b/i);
|
70
|
+
});
|
71
|
+
|
23
72
|
it('Should display as unavailabe when no State is provided', () => {
|
24
73
|
const {getAllByRole} = renderWithStore(
|
25
74
|
<MemoryRouter>
|
@@ -34,4 +83,18 @@ describe('PDisk state', () => {
|
|
34
83
|
expect(disk1.className).toMatch(/_grey\b/i);
|
35
84
|
expect(disk2.className).not.toMatch(/_grey\b/i);
|
36
85
|
});
|
86
|
+
|
87
|
+
it('Should display as unavailabe when no State is provided event if space severity is not OK', () => {
|
88
|
+
const {getAllByRole} = renderWithStore(
|
89
|
+
<MemoryRouter>
|
90
|
+
<PDisk nodeId={1} data={{AvailableSize: '14', TotalSize: '100'}} />
|
91
|
+
<PDisk nodeId={2} data={{AvailableSize: '4', TotalSize: '100'}} />
|
92
|
+
</MemoryRouter>,
|
93
|
+
);
|
94
|
+
|
95
|
+
const [disk1, disk2] = getAllByRole('meter');
|
96
|
+
|
97
|
+
expect(disk1.className).toMatch(/_grey\b/i);
|
98
|
+
expect(disk2.className).toMatch(/_grey\b/i);
|
99
|
+
});
|
37
100
|
});
|
@@ -2,7 +2,7 @@ import React from 'react';
|
|
2
2
|
import PropTypes from 'prop-types';
|
3
3
|
import {connect} from 'react-redux';
|
4
4
|
import cn from 'bem-cn-lite';
|
5
|
-
import DataTable from '@
|
5
|
+
import DataTable from '@gravity-ui/react-data-table';
|
6
6
|
import {RadioButton} from '@gravity-ui/uikit';
|
7
7
|
|
8
8
|
import {Search} from '../../components/Search';
|
@@ -1,6 +1,6 @@
|
|
1
1
|
import _ from 'lodash';
|
2
2
|
import cn from 'bem-cn-lite';
|
3
|
-
import DataTable, {Column, Settings, SortOrder} from '@
|
3
|
+
import DataTable, {Column, Settings, SortOrder} from '@gravity-ui/react-data-table';
|
4
4
|
import {Icon, Label, Popover, PopoverBehavior} from '@gravity-ui/uikit';
|
5
5
|
|
6
6
|
import shieldIcon from '../../../assets/icons/shield.svg';
|
@@ -20,7 +20,7 @@ import {getUsage, isFullDonorData} from '../../../utils/storage';
|
|
20
20
|
|
21
21
|
import {EmptyFilter} from '../EmptyFilter/EmptyFilter';
|
22
22
|
import {VDisk} from '../VDisk';
|
23
|
-
import {getDegradedSeverity,
|
23
|
+
import {getDegradedSeverity, getUsageSeverityForStorageGroup} from '../utils';
|
24
24
|
|
25
25
|
import i18n from './i18n';
|
26
26
|
import './StorageGroups.scss';
|
@@ -127,6 +127,7 @@ function StorageGroups({
|
|
127
127
|
{
|
128
128
|
name: TableColumnsIds.Type,
|
129
129
|
header: tableColumnsNames[TableColumnsIds.Type],
|
130
|
+
// prettier-ignore
|
130
131
|
render: ({value, row}) => (
|
131
132
|
<>
|
132
133
|
<Label>{(value as string) || '—'}</Label>
|
@@ -164,7 +165,7 @@ function StorageGroups({
|
|
164
165
|
// but the absence of a value is more clear
|
165
166
|
return row.Limit ? (
|
166
167
|
<Label
|
167
|
-
theme={
|
168
|
+
theme={getUsageSeverityForStorageGroup(usage)}
|
168
169
|
className={b('usage-label', {overload: usage >= 90})}
|
169
170
|
>
|
170
171
|
{usage}%
|
@@ -1,7 +1,7 @@
|
|
1
1
|
import _ from 'lodash';
|
2
2
|
import cn from 'bem-cn-lite';
|
3
3
|
|
4
|
-
import DataTable, {Column, Settings, SortOrder} from '@
|
4
|
+
import DataTable, {Column, Settings, SortOrder} from '@gravity-ui/react-data-table';
|
5
5
|
import {Popover, PopoverBehavior} from '@gravity-ui/uikit';
|
6
6
|
|
7
7
|
import {VisibleEntities} from '../../../store/reducers/storage';
|
@@ -1,12 +1,14 @@
|
|
1
1
|
import type {IStoragePoolGroup} from '../../../types/store/storage';
|
2
|
+
import {EFlag} from '../../../types/api/enums';
|
2
3
|
|
3
4
|
export * from './constants';
|
4
5
|
|
5
|
-
const generateEvaluator =
|
6
|
-
OkLevel extends string,
|
7
|
-
|
8
|
-
|
9
|
-
|
6
|
+
const generateEvaluator =
|
7
|
+
<OkLevel extends string, WarnLevel extends string, CritLevel extends string>(
|
8
|
+
warn: number,
|
9
|
+
crit: number,
|
10
|
+
levels: [OkLevel, WarnLevel, CritLevel],
|
11
|
+
) =>
|
10
12
|
(value: number) => {
|
11
13
|
if (0 <= value && value < warn) {
|
12
14
|
return levels[0];
|
@@ -34,12 +36,26 @@ const canEvaluateErasureSpecies = (value?: string): value is keyof typeof degrad
|
|
34
36
|
value !== undefined && value in degradationEvaluators;
|
35
37
|
|
36
38
|
export const getDegradedSeverity = (group: IStoragePoolGroup) => {
|
37
|
-
const evaluate = canEvaluateErasureSpecies(group.ErasureSpecies)
|
38
|
-
degradationEvaluators[group.ErasureSpecies]
|
39
|
-
defaultDegradationEvaluator;
|
39
|
+
const evaluate = canEvaluateErasureSpecies(group.ErasureSpecies)
|
40
|
+
? degradationEvaluators[group.ErasureSpecies]
|
41
|
+
: defaultDegradationEvaluator;
|
40
42
|
|
41
43
|
return evaluate(group.Missing);
|
42
44
|
};
|
43
45
|
|
44
|
-
export const
|
45
|
-
|
46
|
+
export const getUsageSeverityForStorageGroup = generateEvaluator(80, 85, [
|
47
|
+
'success',
|
48
|
+
'warning',
|
49
|
+
'danger',
|
50
|
+
]);
|
51
|
+
export const getUsageSeverityForEntityStatus = generateEvaluator(80, 85, [
|
52
|
+
'Green',
|
53
|
+
'Yellow',
|
54
|
+
'Red',
|
55
|
+
]);
|
56
|
+
|
57
|
+
export const getUsageSeverityForPDisk = generateEvaluator(85, 95, [
|
58
|
+
EFlag.Green,
|
59
|
+
EFlag.Yellow,
|
60
|
+
EFlag.Red,
|
61
|
+
]);
|
@@ -16,7 +16,7 @@ import {Tag} from '../../components/Tag/Tag';
|
|
16
16
|
import Icon from '../../components/Icon/Icon';
|
17
17
|
import EmptyState from '../../components/EmptyState/EmptyState';
|
18
18
|
import {Link as ExternalLink, Button, Loader} from '@gravity-ui/uikit';
|
19
|
-
import DataTable from '@
|
19
|
+
import DataTable from '@gravity-ui/react-data-table';
|
20
20
|
import CriticalActionDialog from '../../components/CriticalActionDialog/CriticalActionDialog';
|
21
21
|
import routes, {createHref} from '../../routes';
|
22
22
|
import {getDefaultNodePath} from '../Node/NodePages';
|
@@ -4,7 +4,7 @@ import cn from 'bem-cn-lite';
|
|
4
4
|
import _ from 'lodash';
|
5
5
|
import {connect} from 'react-redux';
|
6
6
|
import {Loader} from '@gravity-ui/uikit';
|
7
|
-
import DataTable from '@
|
7
|
+
import DataTable from '@gravity-ui/react-data-table';
|
8
8
|
import {DEFAULT_TABLE_SETTINGS} from '../../../utils/constants';
|
9
9
|
|
10
10
|
import './Acl.scss';
|
@@ -1,9 +1,9 @@
|
|
1
1
|
import {useCallback, useEffect, useState} from 'react';
|
2
2
|
import {useDispatch} from 'react-redux';
|
3
3
|
import block from 'bem-cn-lite';
|
4
|
-
import {
|
4
|
+
import {escapeRegExp} from 'lodash/fp';
|
5
5
|
|
6
|
-
import DataTable, {Column} from '@
|
6
|
+
import DataTable, {Column} from '@gravity-ui/react-data-table';
|
7
7
|
|
8
8
|
import type {EPathType} from '../../../../types/api/schema';
|
9
9
|
import {Loader} from '../../../../components/Loader';
|
@@ -2,7 +2,7 @@ import {useEffect, useMemo} from 'react';
|
|
2
2
|
import cn from 'bem-cn-lite';
|
3
3
|
import {connect} from 'react-redux';
|
4
4
|
import {Loader} from '@gravity-ui/uikit';
|
5
|
-
import DataTable from '@
|
5
|
+
import DataTable from '@gravity-ui/react-data-table';
|
6
6
|
|
7
7
|
import Icon from '../../../../components/Icon/Icon';
|
8
8
|
|
@@ -2,7 +2,7 @@ import {useState, useContext, useEffect, useMemo} from 'react';
|
|
2
2
|
import {useDispatch} from 'react-redux';
|
3
3
|
import cn from 'bem-cn-lite';
|
4
4
|
|
5
|
-
import DataTable, {Column, Settings, SortOrder} from '@
|
5
|
+
import DataTable, {Column, Settings, SortOrder} from '@gravity-ui/react-data-table';
|
6
6
|
import {Loader} from '@gravity-ui/uikit';
|
7
7
|
|
8
8
|
import {DateRange, DateRangeValues} from '../../../../components/DateRange';
|
@@ -53,6 +53,7 @@ const tableColumnsNames = {
|
|
53
53
|
NodeId: 'NodeId',
|
54
54
|
PeakTime: 'PeakTime',
|
55
55
|
InFlightTxCount: 'InFlightTxCount',
|
56
|
+
IntervalEnd: 'IntervalEnd',
|
56
57
|
};
|
57
58
|
|
58
59
|
function prepareCPUWorkloadValue(value: string) {
|
@@ -227,6 +228,10 @@ export const OverloadedShards = ({tenantPath, type}: OverloadedShardsProps) => {
|
|
227
228
|
align: DataTable.RIGHT,
|
228
229
|
sortable: false,
|
229
230
|
},
|
231
|
+
{
|
232
|
+
name: tableColumnsNames.IntervalEnd,
|
233
|
+
render: ({value}) => formatDateTime(new Date(value as string).getTime()),
|
234
|
+
}
|
230
235
|
];
|
231
236
|
}, [dispatch, history, tenantPath]);
|
232
237
|
|
@@ -1,15 +1,15 @@
|
|
1
1
|
import {ReactNode, useCallback, useMemo} from 'react';
|
2
2
|
import {shallowEqual, useDispatch, useSelector} from 'react-redux';
|
3
|
-
import cn from 'bem-cn-lite';
|
4
3
|
|
5
|
-
import {Loader} from '
|
4
|
+
import {Loader} from '../../../../components/Loader';
|
6
5
|
|
7
6
|
//@ts-ignore
|
8
|
-
import SchemaInfoViewer from '../../Schema/SchemaInfoViewer/SchemaInfoViewer';
|
9
7
|
import {TableIndexInfo} from '../../../../components/InfoViewer/schemaInfo';
|
8
|
+
import {ResponseError} from '../../../../components/Errors/ResponseError';
|
10
9
|
|
11
10
|
import {TopicInfo} from './TopicInfo';
|
12
11
|
import {ChangefeedInfo} from './ChangefeedInfo';
|
12
|
+
import {TableInfo} from './TableInfo';
|
13
13
|
|
14
14
|
import {EPathType, TEvDescribeSchemeResult} from '../../../../types/api/schema';
|
15
15
|
import {
|
@@ -33,8 +33,6 @@ import {
|
|
33
33
|
} from '../../../../store/reducers/olapStats';
|
34
34
|
import {useAutofetcher, useTypedSelector} from '../../../../utils/hooks';
|
35
35
|
|
36
|
-
import './Overview.scss';
|
37
|
-
|
38
36
|
function prepareOlapTableGeneral(item?: TEvDescribeSchemeResult, olapStats?: any[]) {
|
39
37
|
const tableData = item?.PathDescription?.ColumnTableDescription;
|
40
38
|
|
@@ -70,8 +68,6 @@ interface OverviewProps {
|
|
70
68
|
tenantName?: string;
|
71
69
|
}
|
72
70
|
|
73
|
-
const b = cn('kv-tenant-overview');
|
74
|
-
|
75
71
|
function Overview({type, tenantName, className}: OverviewProps) {
|
76
72
|
const dispatch = useDispatch();
|
77
73
|
|
@@ -81,6 +77,7 @@ function Overview({type, tenantName, className}: OverviewProps) {
|
|
81
77
|
wasLoaded,
|
82
78
|
autorefresh,
|
83
79
|
currentSchemaPath,
|
80
|
+
error,
|
84
81
|
} = useSelector((state: any) => state.schema);
|
85
82
|
|
86
83
|
const {data: {result: olapStats} = {result: undefined}, loading: olapStatsLoading} =
|
@@ -144,14 +141,6 @@ function Overview({type, tenantName, className}: OverviewProps) {
|
|
144
141
|
: currentItem;
|
145
142
|
}, [type, olapStats, currentItem]);
|
146
143
|
|
147
|
-
const renderLoader = () => {
|
148
|
-
return (
|
149
|
-
<div className={b('loader')}>
|
150
|
-
<Loader size="m" />
|
151
|
-
</div>
|
152
|
-
);
|
153
|
-
};
|
154
|
-
|
155
144
|
const renderContent = () => {
|
156
145
|
// verbose mapping to guarantee a correct render for new path types
|
157
146
|
// TS will error when a new type is added but not mapped here
|
@@ -170,15 +159,15 @@ function Overview({type, tenantName, className}: OverviewProps) {
|
|
170
159
|
[EPathType.EPathTypePersQueueGroup]: () => <TopicInfo data={schemaData} />,
|
171
160
|
};
|
172
161
|
|
173
|
-
return (
|
174
|
-
(type && pathTypeToComponent[type]?.()) || (
|
175
|
-
<SchemaInfoViewer fullPath={currentItem.Path} data={schemaData} />
|
176
|
-
)
|
177
|
-
);
|
162
|
+
return (type && pathTypeToComponent[type]?.()) || <TableInfo data={schemaData} />;
|
178
163
|
};
|
179
164
|
|
180
165
|
if ((loading && !wasLoaded) || (isEntityWithMergedImpl && !mergedChildrenPaths)) {
|
181
|
-
return
|
166
|
+
return <Loader size="m" />;
|
167
|
+
}
|
168
|
+
|
169
|
+
if (error) {
|
170
|
+
return <ResponseError error={error} />;
|
182
171
|
}
|
183
172
|
|
184
173
|
return <div className={className}>{renderContent()}</div>;
|
@@ -1,6 +1,12 @@
|
|
1
|
-
.
|
1
|
+
@import '../../../../../styles/mixins.scss';
|
2
|
+
|
3
|
+
.ydb-diagnostics-table-info {
|
2
4
|
overflow: auto;
|
3
5
|
|
6
|
+
&__title {
|
7
|
+
@include info-viewer-title();
|
8
|
+
}
|
9
|
+
|
4
10
|
&__row {
|
5
11
|
display: flex;
|
6
12
|
flex-wrap: wrap;
|
@@ -19,19 +25,11 @@
|
|
19
25
|
}
|
20
26
|
}
|
21
27
|
|
22
|
-
&
|
28
|
+
&__info-block {
|
23
29
|
margin-bottom: 20px;
|
24
30
|
|
25
31
|
.info-viewer__items {
|
26
32
|
grid-template-columns: minmax(max-content, 280px);
|
27
33
|
}
|
28
34
|
}
|
29
|
-
|
30
|
-
&__title {
|
31
|
-
margin: 15px 0 10px;
|
32
|
-
|
33
|
-
font-size: var(--yc-text-body-2-font-size);
|
34
|
-
font-weight: 600;
|
35
|
-
line-height: var(--yc-text-body-2-line-height);
|
36
|
-
}
|
37
35
|
}
|
@@ -0,0 +1,71 @@
|
|
1
|
+
import {useMemo} from 'react';
|
2
|
+
import cn from 'bem-cn-lite';
|
3
|
+
|
4
|
+
import type {TEvDescribeSchemeResult} from '../../../../../types/api/schema';
|
5
|
+
|
6
|
+
import {InfoViewer} from '../../../../../components/InfoViewer';
|
7
|
+
|
8
|
+
import {getEntityName} from '../../../utils';
|
9
|
+
|
10
|
+
import {prepareTableInfo} from './prepareTableInfo';
|
11
|
+
|
12
|
+
import i18n from './i18n';
|
13
|
+
|
14
|
+
import './TableInfo.scss';
|
15
|
+
|
16
|
+
const b = cn('ydb-diagnostics-table-info');
|
17
|
+
|
18
|
+
interface TableInfoProps {
|
19
|
+
data?: TEvDescribeSchemeResult;
|
20
|
+
}
|
21
|
+
|
22
|
+
export const TableInfo = ({data}: TableInfoProps) => {
|
23
|
+
const entityName = getEntityName(data?.PathDescription);
|
24
|
+
|
25
|
+
const {
|
26
|
+
generalTableInfo = [],
|
27
|
+
tableStatsInfo = [],
|
28
|
+
tabletMetricsInfo = [],
|
29
|
+
partitionConfigInfo = [],
|
30
|
+
} = useMemo(() => prepareTableInfo(data), [data]);
|
31
|
+
|
32
|
+
return (
|
33
|
+
<div className={b()}>
|
34
|
+
<InfoViewer
|
35
|
+
info={generalTableInfo}
|
36
|
+
title={entityName}
|
37
|
+
className={b('info-block')}
|
38
|
+
renderEmptyState={() => <div className={b('title')}>{entityName}</div>}
|
39
|
+
/>
|
40
|
+
<div className={b('row')}>
|
41
|
+
{tabletMetricsInfo.length > 0 || partitionConfigInfo.length > 0 ? (
|
42
|
+
<div className={b('col')}>
|
43
|
+
<InfoViewer
|
44
|
+
info={tabletMetricsInfo}
|
45
|
+
title={i18n('tabletMetrics')}
|
46
|
+
className={b('info-block')}
|
47
|
+
renderEmptyState={() => null}
|
48
|
+
/>
|
49
|
+
<InfoViewer
|
50
|
+
info={partitionConfigInfo}
|
51
|
+
title={i18n('partitionConfig')}
|
52
|
+
className={b('info-block')}
|
53
|
+
renderEmptyState={() => null}
|
54
|
+
/>
|
55
|
+
</div>
|
56
|
+
) : null}
|
57
|
+
<div className={b('col')}>
|
58
|
+
{tableStatsInfo.map((info, index) => (
|
59
|
+
<InfoViewer
|
60
|
+
key={index}
|
61
|
+
info={info}
|
62
|
+
title={index === 0 ? i18n('tableStats') : undefined}
|
63
|
+
className={b('info-block')}
|
64
|
+
renderEmptyState={() => null}
|
65
|
+
/>
|
66
|
+
))}
|
67
|
+
</div>
|
68
|
+
</div>
|
69
|
+
</div>
|
70
|
+
);
|
71
|
+
};
|
@@ -0,0 +1,11 @@
|
|
1
|
+
import {i18n, Lang} from '../../../../../../utils/i18n';
|
2
|
+
|
3
|
+
import en from './en.json';
|
4
|
+
import ru from './ru.json';
|
5
|
+
|
6
|
+
const COMPONENT = 'ydb-diagnostics-overview-table-info';
|
7
|
+
|
8
|
+
i18n.registerKeyset(Lang.En, COMPONENT, en);
|
9
|
+
i18n.registerKeyset(Lang.Ru, COMPONENT, ru);
|
10
|
+
|
11
|
+
export default i18n.keyset(COMPONENT);
|
@@ -0,0 +1 @@
|
|
1
|
+
export * from './TableInfo';
|
@@ -0,0 +1,96 @@
|
|
1
|
+
import type {TEvDescribeSchemeResult} from '../../../../../types/api/schema';
|
2
|
+
|
3
|
+
import {formatObject} from '../../../../../components/InfoViewer';
|
4
|
+
import {
|
5
|
+
formatFollowerGroupItem,
|
6
|
+
formatPartitionConfigItem,
|
7
|
+
formatTableStatsItem,
|
8
|
+
formatTabletMetricsItem,
|
9
|
+
} from '../../../../../components/InfoViewer/formatters';
|
10
|
+
|
11
|
+
export const prepareTableInfo = (data?: TEvDescribeSchemeResult) => {
|
12
|
+
if (!data) {
|
13
|
+
return {};
|
14
|
+
}
|
15
|
+
|
16
|
+
const {PathDescription = {}} = data;
|
17
|
+
|
18
|
+
const {
|
19
|
+
TableStats = {},
|
20
|
+
TabletMetrics = {},
|
21
|
+
Table: {PartitionConfig = {}} = {},
|
22
|
+
} = PathDescription;
|
23
|
+
|
24
|
+
const {
|
25
|
+
PartCount,
|
26
|
+
RowCount,
|
27
|
+
DataSize,
|
28
|
+
IndexSize,
|
29
|
+
|
30
|
+
LastAccessTime,
|
31
|
+
LastUpdateTime,
|
32
|
+
|
33
|
+
ImmediateTxCompleted,
|
34
|
+
PlannedTxCompleted,
|
35
|
+
TxRejectedByOverload,
|
36
|
+
TxRejectedBySpace,
|
37
|
+
TxCompleteLagMsec,
|
38
|
+
InFlightTxCount,
|
39
|
+
|
40
|
+
RowUpdates,
|
41
|
+
RowDeletes,
|
42
|
+
RowReads,
|
43
|
+
RangeReads,
|
44
|
+
RangeReadRows,
|
45
|
+
|
46
|
+
...restTableStats
|
47
|
+
} = TableStats;
|
48
|
+
|
49
|
+
const {FollowerGroups, FollowerCount, CrossDataCenterFollowerCount} = PartitionConfig;
|
50
|
+
|
51
|
+
const generalTableInfo = formatObject(formatTableStatsItem, {
|
52
|
+
PartCount,
|
53
|
+
RowCount,
|
54
|
+
DataSize,
|
55
|
+
IndexSize,
|
56
|
+
...restTableStats,
|
57
|
+
});
|
58
|
+
|
59
|
+
const tableStatsInfo = [
|
60
|
+
formatObject(formatTableStatsItem, {
|
61
|
+
LastAccessTime,
|
62
|
+
LastUpdateTime,
|
63
|
+
}),
|
64
|
+
formatObject(formatTableStatsItem, {
|
65
|
+
ImmediateTxCompleted,
|
66
|
+
PlannedTxCompleted,
|
67
|
+
TxRejectedByOverload,
|
68
|
+
TxRejectedBySpace,
|
69
|
+
TxCompleteLagMsec,
|
70
|
+
InFlightTxCount,
|
71
|
+
}),
|
72
|
+
formatObject(formatTableStatsItem, {
|
73
|
+
RowUpdates,
|
74
|
+
RowDeletes,
|
75
|
+
RowReads,
|
76
|
+
RangeReads,
|
77
|
+
RangeReadRows,
|
78
|
+
}),
|
79
|
+
];
|
80
|
+
|
81
|
+
const tabletMetricsInfo = formatObject(formatTabletMetricsItem, TabletMetrics);
|
82
|
+
|
83
|
+
let partitionConfigInfo = [];
|
84
|
+
|
85
|
+
if (Array.isArray(FollowerGroups) && FollowerGroups.length > 0) {
|
86
|
+
partitionConfigInfo = formatObject(formatFollowerGroupItem, FollowerGroups[0]);
|
87
|
+
} else if (FollowerCount !== undefined) {
|
88
|
+
partitionConfigInfo.push(formatPartitionConfigItem('FollowerCount', FollowerCount));
|
89
|
+
} else if (CrossDataCenterFollowerCount !== undefined) {
|
90
|
+
partitionConfigInfo.push(
|
91
|
+
formatPartitionConfigItem('CrossDataCenterFollowerCount', CrossDataCenterFollowerCount),
|
92
|
+
);
|
93
|
+
}
|
94
|
+
|
95
|
+
return {generalTableInfo, tableStatsInfo, tabletMetricsInfo, partitionConfigInfo};
|
96
|
+
};
|
@@ -2,7 +2,7 @@ import {useCallback, useEffect, useRef, useState} from 'react';
|
|
2
2
|
import {useDispatch} from 'react-redux';
|
3
3
|
import cn from 'bem-cn-lite';
|
4
4
|
|
5
|
-
import DataTable, {Column, Settings} from '@
|
5
|
+
import DataTable, {Column, Settings} from '@gravity-ui/react-data-table';
|
6
6
|
import {Loader} from '@gravity-ui/uikit';
|
7
7
|
|
8
8
|
import {DateRange, DateRangeValues} from '../../../../components/DateRange';
|
@@ -21,6 +21,7 @@ import type {EPathType} from '../../../../types/api/schema';
|
|
21
21
|
import type {ITopQueriesFilters} from '../../../../types/store/executeTopQueries';
|
22
22
|
import type {IQueryResult} from '../../../../types/store/query';
|
23
23
|
|
24
|
+
import {formatDateTime} from '../../../../utils';
|
24
25
|
import {DEFAULT_TABLE_SETTINGS, HOUR_IN_SECONDS} from '../../../../utils/constants';
|
25
26
|
import {useAutofetcher, useTypedSelector} from '../../../../utils/hooks';
|
26
27
|
import {prepareQueryError} from '../../../../utils/query';
|
@@ -51,6 +52,11 @@ const COLUMNS: Column<KeyValueRow>[] = [
|
|
51
52
|
sortable: false,
|
52
53
|
render: ({value}) => <TruncatedQuery value={value} maxQueryHeight={MAX_QUERY_HEIGHT} />,
|
53
54
|
},
|
55
|
+
{
|
56
|
+
name: 'IntervalEnd',
|
57
|
+
width: 140,
|
58
|
+
render: ({value}) => formatDateTime(new Date(value as string).getTime()),
|
59
|
+
},
|
54
60
|
];
|
55
61
|
|
56
62
|
interface TopQueriesProps {
|
@@ -26,6 +26,8 @@ import {
|
|
26
26
|
TColumnTableDescription,
|
27
27
|
TDirEntry,
|
28
28
|
} from '../../../types/api/schema';
|
29
|
+
|
30
|
+
import {formatDateTime} from '../../../utils';
|
29
31
|
import {isColumnEntityType, isIndexTable, isTableType} from '../utils/schema';
|
30
32
|
|
31
33
|
import {
|
@@ -201,7 +203,7 @@ function ObjectSummary(props: ObjectSummaryProps) {
|
|
201
203
|
const startTimeInMilliseconds = Number(currentSchemaData?.CreateStep);
|
202
204
|
let createTime = '';
|
203
205
|
if (startTimeInMilliseconds) {
|
204
|
-
createTime =
|
206
|
+
createTime = formatDateTime(startTimeInMilliseconds);
|
205
207
|
}
|
206
208
|
|
207
209
|
component = <InfoViewer info={[{label: 'Create time', value: createTime}]} />;
|