ydb-embedded-ui 3.3.2 → 3.3.4
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 +27 -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 +12 -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/Diagnostics.tsx +3 -3
- package/dist/containers/Tenant/Diagnostics/DiagnosticsPages.ts +7 -7
- package/dist/containers/Tenant/Diagnostics/HotKeys/HotKeys.js +1 -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/Diagnostics/TopShards/Filters/Filters.scss +8 -0
- package/dist/containers/Tenant/Diagnostics/TopShards/Filters/Filters.tsx +56 -0
- package/dist/containers/Tenant/Diagnostics/TopShards/Filters/index.ts +1 -0
- package/dist/containers/Tenant/Diagnostics/{OverloadedShards/OverloadedShards.scss → TopShards/TopShards.scss} +2 -10
- package/dist/containers/Tenant/Diagnostics/{OverloadedShards/OverloadedShards.tsx → TopShards/TopShards.tsx} +64 -29
- package/dist/containers/Tenant/Diagnostics/TopShards/i18n/en.json +6 -0
- package/dist/containers/Tenant/Diagnostics/{OverloadedShards → TopShards}/i18n/index.ts +1 -1
- package/dist/containers/Tenant/Diagnostics/TopShards/i18n/ru.json +6 -0
- package/dist/containers/Tenant/Diagnostics/TopShards/index.ts +1 -0
- 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/authentication.js +0 -15
- package/dist/store/reducers/shardsWorkload.ts +30 -3
- package/dist/store/reducers/storage.js +1 -1
- package/dist/store/state-url-mapping.js +3 -0
- package/dist/types/store/shardsWorkload.ts +6 -0
- package/dist/utils/constants.ts +1 -1
- package/dist/utils/index.js +3 -1
- package/dist/utils/prepareQueryExplain.ts +1 -1
- package/dist/utils/typecheckers.ts +5 -0
- package/package.json +5 -3
- package/dist/containers/Tenant/Diagnostics/OverloadedShards/i18n/en.json +0 -4
- package/dist/containers/Tenant/Diagnostics/OverloadedShards/i18n/ru.json +0 -4
- package/dist/containers/Tenant/Diagnostics/OverloadedShards/index.ts +0 -1
- 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';
|
@@ -28,6 +28,7 @@ import './StorageGroups.scss';
|
|
28
28
|
enum TableColumnsIds {
|
29
29
|
PoolName = 'PoolName',
|
30
30
|
Type = 'Type',
|
31
|
+
ErasureSpecies = 'ErasureSpecies',
|
31
32
|
GroupID = 'GroupID',
|
32
33
|
Used = 'Used',
|
33
34
|
Limit = 'Limit',
|
@@ -53,6 +54,7 @@ interface StorageGroupsProps {
|
|
53
54
|
const tableColumnsNames: Record<TableColumnsIdsValues, string> = {
|
54
55
|
PoolName: 'Pool Name',
|
55
56
|
Type: 'Type',
|
57
|
+
ErasureSpecies: 'Erasure',
|
56
58
|
GroupID: 'Group ID',
|
57
59
|
Used: 'Used',
|
58
60
|
Limit: 'Limit',
|
@@ -127,6 +129,7 @@ function StorageGroups({
|
|
127
129
|
{
|
128
130
|
name: TableColumnsIds.Type,
|
129
131
|
header: tableColumnsNames[TableColumnsIds.Type],
|
132
|
+
// prettier-ignore
|
130
133
|
render: ({value, row}) => (
|
131
134
|
<>
|
132
135
|
<Label>{(value as string) || '—'}</Label>
|
@@ -145,6 +148,12 @@ function StorageGroups({
|
|
145
148
|
</>
|
146
149
|
),
|
147
150
|
},
|
151
|
+
{
|
152
|
+
name: TableColumnsIds.ErasureSpecies,
|
153
|
+
header: tableColumnsNames[TableColumnsIds.ErasureSpecies],
|
154
|
+
render: ({row}) => (row.ErasureSpecies ? row.ErasureSpecies : '-'),
|
155
|
+
align: DataTable.LEFT,
|
156
|
+
},
|
148
157
|
{
|
149
158
|
name: TableColumnsIds.Missing,
|
150
159
|
header: tableColumnsNames[TableColumnsIds.Missing],
|
@@ -164,7 +173,7 @@ function StorageGroups({
|
|
164
173
|
// but the absence of a value is more clear
|
165
174
|
return row.Limit ? (
|
166
175
|
<Label
|
167
|
-
theme={
|
176
|
+
theme={getUsageSeverityForStorageGroup(usage)}
|
168
177
|
className={b('usage-label', {overload: usage >= 90})}
|
169
178
|
>
|
170
179
|
{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';
|
@@ -12,7 +12,7 @@ import {Loader} from '../../../components/Loader';
|
|
12
12
|
import {TopQueries} from './TopQueries';
|
13
13
|
//@ts-ignore
|
14
14
|
import DetailedOverview from './DetailedOverview/DetailedOverview';
|
15
|
-
import {
|
15
|
+
import {TopShards} from './TopShards';
|
16
16
|
//@ts-ignore
|
17
17
|
import Storage from '../../Storage/Storage';
|
18
18
|
//@ts-ignore
|
@@ -124,8 +124,8 @@ function Diagnostics(props: DiagnosticsProps) {
|
|
124
124
|
/>
|
125
125
|
);
|
126
126
|
}
|
127
|
-
case GeneralPagesIds.
|
128
|
-
return <
|
127
|
+
case GeneralPagesIds.topShards: {
|
128
|
+
return <TopShards tenantPath={tenantNameString} type={type} />;
|
129
129
|
}
|
130
130
|
case GeneralPagesIds.nodes: {
|
131
131
|
return (
|
@@ -3,7 +3,7 @@ import {EPathType} from '../../../types/api/schema';
|
|
3
3
|
export enum GeneralPagesIds {
|
4
4
|
'overview' = 'Overview',
|
5
5
|
'topQueries' = 'topQueries',
|
6
|
-
'
|
6
|
+
'topShards' = 'topShards',
|
7
7
|
'nodes' = 'Nodes',
|
8
8
|
'tablets' = 'Tablets',
|
9
9
|
'storage' = 'Storage',
|
@@ -29,9 +29,9 @@ const topQueries = {
|
|
29
29
|
title: 'Top queries',
|
30
30
|
};
|
31
31
|
|
32
|
-
const
|
33
|
-
id: GeneralPagesIds.
|
34
|
-
title: '
|
32
|
+
const topShards = {
|
33
|
+
id: GeneralPagesIds.topShards,
|
34
|
+
title: 'Top shards',
|
35
35
|
};
|
36
36
|
|
37
37
|
const nodes = {
|
@@ -75,7 +75,7 @@ const consumers = {
|
|
75
75
|
export const DATABASE_PAGES = [
|
76
76
|
overview,
|
77
77
|
topQueries,
|
78
|
-
|
78
|
+
topShards,
|
79
79
|
nodes,
|
80
80
|
tablets,
|
81
81
|
storage,
|
@@ -83,9 +83,9 @@ export const DATABASE_PAGES = [
|
|
83
83
|
describe,
|
84
84
|
];
|
85
85
|
|
86
|
-
export const TABLE_PAGES = [overview,
|
86
|
+
export const TABLE_PAGES = [overview, topShards, graph, tablets, hotKeys, describe];
|
87
87
|
|
88
|
-
export const DIR_PAGES = [overview,
|
88
|
+
export const DIR_PAGES = [overview, topShards, describe];
|
89
89
|
|
90
90
|
export const CDC_STREAM_PAGES = [overview, consumers, describe];
|
91
91
|
export const TOPIC_PAGES = [overview, consumers, describe];
|
@@ -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
|
|
@@ -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 {
|