ydb-embedded-ui 4.9.0 → 4.10.0
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 +12 -0
- package/dist/components/BasicNodeViewer/BasicNodeViewer.tsx +7 -4
- package/dist/components/EntityStatus/EntityStatus.js +3 -1
- package/dist/components/FormattedBytes/FormattedBytes.tsx +10 -0
- package/dist/components/FormattedBytes/utils.tsx +13 -0
- package/dist/components/FullNodeViewer/FullNodeViewer.tsx +73 -0
- package/dist/components/InfoViewer/formatters/table.ts +6 -5
- package/dist/components/ProblemFilter/ProblemFilter.tsx +2 -2
- package/dist/components/SpeedMultiMeter/SpeedMultiMeter.tsx +4 -4
- package/dist/components/TruncatedQuery/{TruncatedQuery.js → TruncatedQuery.tsx} +10 -8
- package/dist/containers/AsideNavigation/AsideNavigation.tsx +6 -6
- package/dist/containers/Cluster/Cluster.tsx +10 -6
- package/dist/containers/Node/Node.tsx +3 -3
- package/dist/containers/Nodes/Nodes.tsx +2 -2
- package/dist/containers/Storage/PDisk/PDisk.tsx +2 -7
- package/dist/containers/Storage/Storage.tsx +240 -0
- package/dist/containers/Storage/StorageGroups/StorageGroups.tsx +45 -40
- package/dist/containers/Storage/StorageNodes/StorageNodes.tsx +12 -16
- package/dist/containers/Storage/UsageFilter/UsageFilter.scss +1 -0
- package/dist/containers/Storage/UsageFilter/UsageFilter.tsx +17 -17
- package/dist/containers/Tenant/Diagnostics/Diagnostics.tsx +3 -3
- package/dist/containers/Tenant/Diagnostics/TenantOverview/TenantOverview.js +7 -4
- package/dist/containers/Tenant/Diagnostics/TenantOverview/TenantOverview.scss +0 -15
- package/dist/containers/Tenant/Diagnostics/TopQueries/TopQueries.tsx +10 -3
- package/dist/containers/Tenant/{Preview → Query/Preview}/Preview.scss +1 -1
- package/dist/containers/Tenant/Query/Preview/Preview.tsx +121 -0
- package/dist/containers/Tenant/Query/QueriesHistory/QueriesHistory.tsx +1 -1
- package/dist/containers/Tenant/Query/QueryEditor/QueryEditor.js +6 -8
- package/dist/containers/Tenant/Query/SavedQueries/SavedQueries.tsx +1 -1
- package/dist/containers/Tenant/Query/i18n/en.json +8 -1
- package/dist/containers/Tenant/Query/i18n/ru.json +8 -1
- package/dist/containers/Tenants/Tenants.tsx +269 -0
- package/dist/services/api.ts +8 -3
- package/dist/store/reducers/nodes/nodes.ts +4 -4
- package/dist/store/reducers/partitions/types.ts +3 -3
- package/dist/store/reducers/settings/settings.ts +4 -2
- package/dist/store/reducers/settings/types.ts +3 -1
- package/dist/store/reducers/storage/selectors.ts +279 -0
- package/dist/store/reducers/storage/storage.ts +191 -0
- package/dist/store/reducers/storage/types.ts +80 -0
- package/dist/store/reducers/tenants/selectors.ts +46 -0
- package/dist/store/reducers/tenants/tenants.ts +21 -14
- package/dist/store/reducers/tenants/types.ts +20 -5
- package/dist/store/reducers/tenants/utils.ts +68 -0
- package/dist/types/additionalProps.ts +8 -0
- package/dist/types/api/storage.ts +1 -1
- package/dist/types/store/topic.ts +3 -3
- package/dist/utils/bytesParsers/__test__/formatBytes.test.ts +38 -0
- package/dist/utils/bytesParsers/convertBytesObjectToSpeed.ts +2 -2
- package/dist/utils/bytesParsers/formatBytes.ts +132 -0
- package/dist/utils/bytesParsers/i18n/en.json +1 -0
- package/dist/utils/bytesParsers/i18n/ru.json +1 -0
- package/dist/utils/bytesParsers/index.ts +1 -1
- package/dist/utils/index.js +5 -10
- package/dist/utils/numeral.ts +8 -0
- package/package.json +1 -1
- package/dist/components/FullNodeViewer/FullNodeViewer.js +0 -89
- package/dist/containers/Node/NodeOverview/NodeOverview.scss +0 -0
- package/dist/containers/Node/NodeOverview/NodeOverview.tsx +0 -21
- package/dist/containers/Storage/Storage.js +0 -350
- package/dist/containers/Tenant/Preview/Preview.js +0 -168
- package/dist/containers/Tenants/Tenants.js +0 -363
- package/dist/store/reducers/storage/storage.js +0 -404
- package/dist/utils/bytesParsers/formatBytesCustom.ts +0 -57
@@ -9,7 +9,7 @@ import {Loader} from '@gravity-ui/uikit';
|
|
9
9
|
|
10
10
|
import {DateRange, DateRangeValues} from '../../../../components/DateRange';
|
11
11
|
import {Search} from '../../../../components/Search';
|
12
|
-
import TruncatedQuery from '../../../../components/TruncatedQuery/TruncatedQuery';
|
12
|
+
import {TruncatedQuery} from '../../../../components/TruncatedQuery/TruncatedQuery';
|
13
13
|
|
14
14
|
import {changeUserInput} from '../../../../store/reducers/executeQuery';
|
15
15
|
import {
|
@@ -23,7 +23,11 @@ import type {EPathType} from '../../../../types/api/schema';
|
|
23
23
|
import type {ITopQueriesFilters} from '../../../../types/store/executeTopQueries';
|
24
24
|
import type {IQueryResult} from '../../../../types/store/query';
|
25
25
|
|
26
|
-
import {
|
26
|
+
import {
|
27
|
+
TENANT_PAGE,
|
28
|
+
TENANT_PAGES_IDS,
|
29
|
+
TENANT_QUERY_TABS_ID,
|
30
|
+
} from '../../../../store/reducers/tenant/constants';
|
27
31
|
import {formatDateTime, formatNumber} from '../../../../utils';
|
28
32
|
import {HOUR_IN_SECONDS} from '../../../../utils/constants';
|
29
33
|
import {useAutofetcher, useTypedSelector} from '../../../../utils/hooks';
|
@@ -50,7 +54,10 @@ const COLUMNS: Column<KeyValueRow>[] = [
|
|
50
54
|
sortable: false,
|
51
55
|
render: ({row}) => (
|
52
56
|
<div className={b('query')}>
|
53
|
-
<TruncatedQuery
|
57
|
+
<TruncatedQuery
|
58
|
+
value={row.QueryText?.toString()}
|
59
|
+
maxQueryHeight={MAX_QUERY_HEIGHT}
|
60
|
+
/>
|
54
61
|
</div>
|
55
62
|
),
|
56
63
|
},
|
@@ -0,0 +1,121 @@
|
|
1
|
+
import {useCallback} from 'react';
|
2
|
+
import {useDispatch} from 'react-redux';
|
3
|
+
import cn from 'bem-cn-lite';
|
4
|
+
|
5
|
+
import {Loader, Button} from '@gravity-ui/uikit';
|
6
|
+
|
7
|
+
import type {EPathType} from '../../../../types/api/schema';
|
8
|
+
import {sendQuery, setQueryOptions} from '../../../../store/reducers/preview';
|
9
|
+
import {setShowPreview} from '../../../../store/reducers/schema/schema';
|
10
|
+
import {prepareQueryError} from '../../../../utils/query';
|
11
|
+
import {useAutofetcher, useTypedSelector} from '../../../../utils/hooks';
|
12
|
+
|
13
|
+
import {Icon} from '../../../../components/Icon';
|
14
|
+
import Fullscreen from '../../../../components/Fullscreen/Fullscreen';
|
15
|
+
import {QueryResultTable} from '../../../../components/QueryResultTable';
|
16
|
+
import EnableFullscreenButton from '../../../../components/EnableFullscreenButton/EnableFullscreenButton';
|
17
|
+
|
18
|
+
import {isTableType} from '../../utils/schema';
|
19
|
+
|
20
|
+
import i18n from '../i18n';
|
21
|
+
|
22
|
+
import './Preview.scss';
|
23
|
+
|
24
|
+
const b = cn('kv-preview');
|
25
|
+
|
26
|
+
interface PreviewProps {
|
27
|
+
database: string;
|
28
|
+
type: EPathType | undefined;
|
29
|
+
}
|
30
|
+
|
31
|
+
export const Preview = ({database, type}: PreviewProps) => {
|
32
|
+
const dispatch = useDispatch();
|
33
|
+
|
34
|
+
const {data = {}, loading, error, wasLoaded} = useTypedSelector((state) => state.preview);
|
35
|
+
const {autorefresh, currentSchemaPath} = useTypedSelector((state) => state.schema);
|
36
|
+
const isFullscreen = useTypedSelector((state) => state.fullscreen);
|
37
|
+
|
38
|
+
const sendQueryForPreview = useCallback(
|
39
|
+
(isBackground) => {
|
40
|
+
if (!isTableType(type)) {
|
41
|
+
return;
|
42
|
+
}
|
43
|
+
|
44
|
+
if (!isBackground) {
|
45
|
+
dispatch(
|
46
|
+
setQueryOptions({
|
47
|
+
wasLoaded: false,
|
48
|
+
data: undefined,
|
49
|
+
}),
|
50
|
+
);
|
51
|
+
}
|
52
|
+
|
53
|
+
const query = `--!syntax_v1\nselect * from \`${currentSchemaPath}\` limit 32`;
|
54
|
+
|
55
|
+
dispatch(
|
56
|
+
sendQuery({
|
57
|
+
query,
|
58
|
+
database,
|
59
|
+
action: 'execute-scan',
|
60
|
+
}),
|
61
|
+
);
|
62
|
+
},
|
63
|
+
[dispatch, database, currentSchemaPath, type],
|
64
|
+
);
|
65
|
+
|
66
|
+
useAutofetcher(sendQueryForPreview, [sendQueryForPreview], autorefresh);
|
67
|
+
|
68
|
+
const handleClosePreview = () => {
|
69
|
+
dispatch(setShowPreview(false));
|
70
|
+
};
|
71
|
+
|
72
|
+
const renderHeader = () => {
|
73
|
+
return (
|
74
|
+
<div className={b('header')}>
|
75
|
+
<div className={b('title')}>
|
76
|
+
{i18n('preview.title')}{' '}
|
77
|
+
<div className={b('table-name')}>{currentSchemaPath}</div>
|
78
|
+
</div>
|
79
|
+
<div className={b('controls-left')}>
|
80
|
+
<EnableFullscreenButton disabled={Boolean(error)} />
|
81
|
+
<Button
|
82
|
+
view="flat-secondary"
|
83
|
+
onClick={handleClosePreview}
|
84
|
+
title={i18n('preview.close')}
|
85
|
+
>
|
86
|
+
<Icon name="close" viewBox={'0 0 16 16'} width={16} height={16} />
|
87
|
+
</Button>
|
88
|
+
</div>
|
89
|
+
</div>
|
90
|
+
);
|
91
|
+
};
|
92
|
+
|
93
|
+
if (loading && !wasLoaded) {
|
94
|
+
return (
|
95
|
+
<div className={b('loader-container')}>
|
96
|
+
<Loader size="m" />
|
97
|
+
</div>
|
98
|
+
);
|
99
|
+
}
|
100
|
+
|
101
|
+
let message;
|
102
|
+
|
103
|
+
if (!isTableType(type)) {
|
104
|
+
message = <div className={b('message-container')}>{i18n('preview.not-available')}</div>;
|
105
|
+
} else if (error) {
|
106
|
+
message = <div className={b('message-container')}>{prepareQueryError(error)}</div>;
|
107
|
+
}
|
108
|
+
|
109
|
+
const content = message ?? (
|
110
|
+
<div className={b('result')}>
|
111
|
+
<QueryResultTable data={data.result} columns={data.columns} />
|
112
|
+
</div>
|
113
|
+
);
|
114
|
+
|
115
|
+
return (
|
116
|
+
<div className={b()}>
|
117
|
+
{renderHeader()}
|
118
|
+
{isFullscreen ? <Fullscreen>{content}</Fullscreen> : content}
|
119
|
+
</div>
|
120
|
+
);
|
121
|
+
};
|
@@ -3,7 +3,7 @@ import block from 'bem-cn-lite';
|
|
3
3
|
|
4
4
|
import DataTable, {Column} from '@gravity-ui/react-data-table';
|
5
5
|
|
6
|
-
import TruncatedQuery from '../../../../components/TruncatedQuery/TruncatedQuery';
|
6
|
+
import {TruncatedQuery} from '../../../../components/TruncatedQuery/TruncatedQuery';
|
7
7
|
import {setQueryTab} from '../../../../store/reducers/tenant/tenant';
|
8
8
|
import {TENANT_QUERY_TABS_ID} from '../../../../store/reducers/tenant/constants';
|
9
9
|
import {useTypedSelector} from '../../../../utils/hooks';
|
@@ -35,7 +35,7 @@ import {
|
|
35
35
|
PaneVisibilityActionTypes,
|
36
36
|
paneVisibilityToggleReducerCreator,
|
37
37
|
} from '../../utils/paneVisibilityToggleHelpers';
|
38
|
-
import Preview from '
|
38
|
+
import {Preview} from '../Preview/Preview';
|
39
39
|
|
40
40
|
import {ExecuteResult} from '../ExecuteResult/ExecuteResult';
|
41
41
|
import {ExplainResult} from '../ExplainResult/ExplainResult';
|
@@ -104,8 +104,10 @@ function QueryEditor(props) {
|
|
104
104
|
|
105
105
|
useEffect(() => {
|
106
106
|
if (savedPath !== path) {
|
107
|
+
if (savedPath) {
|
108
|
+
changeUserInput({input: ''});
|
109
|
+
}
|
107
110
|
setTenantPath(path);
|
108
|
-
changeUserInput({input: ''});
|
109
111
|
}
|
110
112
|
}, [changeUserInput, setTenantPath, path, savedPath]);
|
111
113
|
|
@@ -380,12 +382,8 @@ function QueryEditor(props) {
|
|
380
382
|
};
|
381
383
|
|
382
384
|
const renderPreview = () => {
|
383
|
-
const {path, type
|
384
|
-
|
385
|
-
// onExpandResultHandler();
|
386
|
-
return (
|
387
|
-
<Preview database={path} table={currentSchema.Path} type={type} partCount={partCount} />
|
388
|
-
);
|
385
|
+
const {path, type} = props;
|
386
|
+
return <Preview database={path} type={type} />;
|
389
387
|
};
|
390
388
|
|
391
389
|
const handlePreviousHistoryClick = () => {
|
@@ -10,7 +10,7 @@ import {setQueryNameToEdit} from '../../../../store/reducers/saveQuery';
|
|
10
10
|
import {setQueryTab} from '../../../../store/reducers/tenant/tenant';
|
11
11
|
import {TENANT_QUERY_TABS_ID} from '../../../../store/reducers/tenant/constants';
|
12
12
|
|
13
|
-
import TruncatedQuery from '../../../../components/TruncatedQuery/TruncatedQuery';
|
13
|
+
import {TruncatedQuery} from '../../../../components/TruncatedQuery/TruncatedQuery';
|
14
14
|
import {IconWrapper} from '../../../../components/Icon';
|
15
15
|
|
16
16
|
import {MAX_QUERY_HEIGHT, QUERY_TABLE_SETTINGS} from '../../utils/constants';
|
@@ -1,12 +1,19 @@
|
|
1
1
|
{
|
2
2
|
"controls.query-mode-selector_type": "Query type:",
|
3
|
+
|
3
4
|
"tabs.newQuery": "New query",
|
4
5
|
"tabs.history": "History",
|
5
6
|
"tabs.saved": "Saved",
|
7
|
+
|
6
8
|
"history.empty": "History is empty",
|
7
9
|
"saved.empty": "There are no saved queries",
|
10
|
+
|
8
11
|
"delete-dialog.header": "Delete query",
|
9
12
|
"delete-dialog.question": "Are you sure you want to delete query",
|
10
13
|
"delete-dialog.delete": "Delete",
|
11
|
-
"delete-dialog.cancel": "Cancel"
|
14
|
+
"delete-dialog.cancel": "Cancel",
|
15
|
+
|
16
|
+
"preview.title": "Preview",
|
17
|
+
"preview.not-available": "Preview is not available",
|
18
|
+
"preview.close": "Close preview"
|
12
19
|
}
|
@@ -1,12 +1,19 @@
|
|
1
1
|
{
|
2
2
|
"controls.query-mode-selector_type": "Тип запроса:",
|
3
|
+
|
3
4
|
"tabs.newQuery": "Новый запрос",
|
4
5
|
"tabs.history": "История",
|
5
6
|
"tabs.saved": "Сохраненные",
|
7
|
+
|
6
8
|
"history.empty": "История пуста",
|
7
9
|
"saved.empty": "Нет сохраненных запросов",
|
10
|
+
|
8
11
|
"delete-dialog.header": "Удалить запрос",
|
9
12
|
"delete-dialog.question": "Вы уверены что хотите удалить запрос",
|
10
13
|
"delete-dialog.delete": "Удалить",
|
11
|
-
"delete-dialog.cancel": "Отменить"
|
14
|
+
"delete-dialog.cancel": "Отменить",
|
15
|
+
|
16
|
+
"preview.title": "Предпросмотр",
|
17
|
+
"preview.not-available": "Предпросмотр недоступен",
|
18
|
+
"preview.close": "Закрыть предпросмотр"
|
12
19
|
}
|
@@ -0,0 +1,269 @@
|
|
1
|
+
import cn from 'bem-cn-lite';
|
2
|
+
import {useDispatch} from 'react-redux';
|
3
|
+
|
4
|
+
import DataTable, {Column} from '@gravity-ui/react-data-table';
|
5
|
+
import {Loader, Button} from '@gravity-ui/uikit';
|
6
|
+
|
7
|
+
import EntityStatus from '../../components/EntityStatus/EntityStatus';
|
8
|
+
import {PoolsGraph} from '../../components/PoolsGraph/PoolsGraph';
|
9
|
+
import {TabletsStatistic} from '../../components/TabletsStatistic';
|
10
|
+
import {ProblemFilter} from '../../components/ProblemFilter';
|
11
|
+
import {Illustration} from '../../components/Illustration';
|
12
|
+
import {Search} from '../../components/Search';
|
13
|
+
import {ResponseError} from '../../components/Errors/ResponseError';
|
14
|
+
|
15
|
+
import type {AdditionalTenantsProps} from '../../types/additionalProps';
|
16
|
+
import type {ProblemFilterValue} from '../../store/reducers/settings/types';
|
17
|
+
import type {PreparedTenant} from '../../store/reducers/tenants/types';
|
18
|
+
import {getTenantsInfo, setSearchValue} from '../../store/reducers/tenants/tenants';
|
19
|
+
import {
|
20
|
+
selectFilteredTenants,
|
21
|
+
selectTenantsSearchValue,
|
22
|
+
} from '../../store/reducers/tenants/selectors';
|
23
|
+
import {
|
24
|
+
changeFilter,
|
25
|
+
ProblemFilterValues,
|
26
|
+
selectProblemFilter,
|
27
|
+
} from '../../store/reducers/settings/settings';
|
28
|
+
import {formatCPU, formatBytesToGigabyte, formatNumber} from '../../utils';
|
29
|
+
import {DEFAULT_TABLE_SETTINGS} from '../../utils/constants';
|
30
|
+
import {useAutofetcher, useTypedSelector} from '../../utils/hooks';
|
31
|
+
import {clusterName} from '../../store';
|
32
|
+
|
33
|
+
import {TenantTabsGroups, TENANT_INFO_TABS, getTenantPath} from '../Tenant/TenantPages';
|
34
|
+
|
35
|
+
import './Tenants.scss';
|
36
|
+
|
37
|
+
const b = cn('tenants');
|
38
|
+
|
39
|
+
interface TenantsProps {
|
40
|
+
additionalTenantsProps?: AdditionalTenantsProps;
|
41
|
+
}
|
42
|
+
|
43
|
+
export const Tenants = ({additionalTenantsProps}: TenantsProps) => {
|
44
|
+
const dispatch = useDispatch();
|
45
|
+
|
46
|
+
const {error, loading, wasLoaded} = useTypedSelector((state) => state.tenants);
|
47
|
+
const searchValue = useTypedSelector(selectTenantsSearchValue);
|
48
|
+
const filteredTenants = useTypedSelector(selectFilteredTenants);
|
49
|
+
const problemFilter = useTypedSelector(selectProblemFilter);
|
50
|
+
|
51
|
+
useAutofetcher(
|
52
|
+
() => {
|
53
|
+
dispatch(getTenantsInfo(clusterName));
|
54
|
+
},
|
55
|
+
[dispatch],
|
56
|
+
true,
|
57
|
+
);
|
58
|
+
|
59
|
+
const handleProblemFilterChange = (value: string) => {
|
60
|
+
dispatch(changeFilter(value as ProblemFilterValue));
|
61
|
+
};
|
62
|
+
|
63
|
+
const handleSearchChange = (value: string) => {
|
64
|
+
dispatch(setSearchValue(value));
|
65
|
+
};
|
66
|
+
|
67
|
+
const renderControls = () => {
|
68
|
+
return (
|
69
|
+
<div className={b('controls')}>
|
70
|
+
<Search
|
71
|
+
value={searchValue}
|
72
|
+
onChange={handleSearchChange}
|
73
|
+
placeholder="Database name"
|
74
|
+
className={b('search')}
|
75
|
+
/>
|
76
|
+
<ProblemFilter value={problemFilter} onChange={handleProblemFilterChange} />
|
77
|
+
</div>
|
78
|
+
);
|
79
|
+
};
|
80
|
+
|
81
|
+
const renderTable = () => {
|
82
|
+
const initialTenantInfoTab = TENANT_INFO_TABS[0].id;
|
83
|
+
|
84
|
+
const getTenantBackend = (tenant: PreparedTenant) => {
|
85
|
+
const backend = tenant.MonitoringEndpoint ?? tenant.backend;
|
86
|
+
return additionalTenantsProps?.prepareTenantBackend?.(backend);
|
87
|
+
};
|
88
|
+
|
89
|
+
const columns: Column<PreparedTenant>[] = [
|
90
|
+
{
|
91
|
+
name: 'Name',
|
92
|
+
header: 'Database',
|
93
|
+
render: ({row}) => {
|
94
|
+
const backend = getTenantBackend(row);
|
95
|
+
const isExternalLink = Boolean(backend);
|
96
|
+
return (
|
97
|
+
<div className={b('name-wrapper')}>
|
98
|
+
<EntityStatus
|
99
|
+
externalLink={isExternalLink}
|
100
|
+
className={b('name')}
|
101
|
+
name={row.Name || 'unknown database'}
|
102
|
+
withLeftTrim={true}
|
103
|
+
status={row.Overall}
|
104
|
+
hasClipboardButton
|
105
|
+
path={getTenantPath({
|
106
|
+
name: row.Name,
|
107
|
+
backend,
|
108
|
+
[TenantTabsGroups.info]: initialTenantInfoTab,
|
109
|
+
})}
|
110
|
+
/>
|
111
|
+
{additionalTenantsProps?.getMonitoringLink?.(row.Name, row.Type)}
|
112
|
+
</div>
|
113
|
+
);
|
114
|
+
},
|
115
|
+
width: 440,
|
116
|
+
sortable: true,
|
117
|
+
defaultOrder: DataTable.DESCENDING,
|
118
|
+
},
|
119
|
+
{
|
120
|
+
name: 'controlPlaneName',
|
121
|
+
header: 'Name',
|
122
|
+
render: ({row}) => {
|
123
|
+
return row.controlPlaneName;
|
124
|
+
},
|
125
|
+
width: 200,
|
126
|
+
sortable: true,
|
127
|
+
defaultOrder: DataTable.DESCENDING,
|
128
|
+
},
|
129
|
+
{
|
130
|
+
name: 'Type',
|
131
|
+
width: 200,
|
132
|
+
render: ({row}) => {
|
133
|
+
if (row.Type !== 'Serverless') {
|
134
|
+
return row.Type;
|
135
|
+
}
|
136
|
+
|
137
|
+
return (
|
138
|
+
<div className={b('type')}>
|
139
|
+
<span className={b('type-value')}>{row.Type}</span>
|
140
|
+
<Button
|
141
|
+
className={b('type-button')}
|
142
|
+
onClick={() => handleSearchChange(row.sharedTenantName || '')}
|
143
|
+
>
|
144
|
+
Show shared
|
145
|
+
</Button>
|
146
|
+
</div>
|
147
|
+
);
|
148
|
+
},
|
149
|
+
},
|
150
|
+
{
|
151
|
+
name: 'State',
|
152
|
+
width: 90,
|
153
|
+
render: ({row}) => (row.State ? row.State.toLowerCase() : '—'),
|
154
|
+
customStyle: () => ({textTransform: 'capitalize'}),
|
155
|
+
},
|
156
|
+
{
|
157
|
+
name: 'cpu',
|
158
|
+
header: 'CPU',
|
159
|
+
width: 80,
|
160
|
+
render: ({row}) => {
|
161
|
+
// Don't show values below 0.01 when formatted
|
162
|
+
if (row.cpu > 10_000) {
|
163
|
+
return formatCPU(row.cpu);
|
164
|
+
}
|
165
|
+
return '—';
|
166
|
+
},
|
167
|
+
align: DataTable.RIGHT,
|
168
|
+
defaultOrder: DataTable.DESCENDING,
|
169
|
+
},
|
170
|
+
{
|
171
|
+
name: 'memory',
|
172
|
+
header: 'Memory',
|
173
|
+
width: 120,
|
174
|
+
render: ({row}) => (row.memory ? formatBytesToGigabyte(row.memory) : '—'),
|
175
|
+
align: DataTable.RIGHT,
|
176
|
+
defaultOrder: DataTable.DESCENDING,
|
177
|
+
},
|
178
|
+
{
|
179
|
+
name: 'storage',
|
180
|
+
header: 'Storage',
|
181
|
+
width: 120,
|
182
|
+
render: ({row}) => (row.storage ? formatBytesToGigabyte(row.storage) : '—'),
|
183
|
+
align: DataTable.RIGHT,
|
184
|
+
defaultOrder: DataTable.DESCENDING,
|
185
|
+
},
|
186
|
+
{
|
187
|
+
name: 'nodesCount',
|
188
|
+
header: 'Nodes',
|
189
|
+
width: 100,
|
190
|
+
render: ({row}) => (row.nodesCount ? formatNumber(row.nodesCount) : '—'),
|
191
|
+
align: DataTable.RIGHT,
|
192
|
+
defaultOrder: DataTable.DESCENDING,
|
193
|
+
},
|
194
|
+
{
|
195
|
+
name: 'groupsCount',
|
196
|
+
header: 'Groups',
|
197
|
+
width: 100,
|
198
|
+
render: ({row}) => (row.groupsCount ? formatNumber(row.groupsCount) : '—'),
|
199
|
+
align: DataTable.RIGHT,
|
200
|
+
defaultOrder: DataTable.DESCENDING,
|
201
|
+
},
|
202
|
+
{
|
203
|
+
name: 'PoolStats',
|
204
|
+
header: 'Pools',
|
205
|
+
width: 100,
|
206
|
+
sortAccessor: ({PoolStats = []}) =>
|
207
|
+
PoolStats.reduce((acc, item) => acc + (item.Usage || 0), 0),
|
208
|
+
defaultOrder: DataTable.DESCENDING,
|
209
|
+
align: DataTable.LEFT,
|
210
|
+
render: ({row}) => <PoolsGraph pools={row.PoolStats} />,
|
211
|
+
},
|
212
|
+
{
|
213
|
+
name: 'Tablets',
|
214
|
+
header: 'Tablets States',
|
215
|
+
sortable: false,
|
216
|
+
width: 430,
|
217
|
+
render: ({row}) => {
|
218
|
+
const backend = getTenantBackend(row);
|
219
|
+
|
220
|
+
return row.Tablets ? (
|
221
|
+
<TabletsStatistic
|
222
|
+
path={row.Name}
|
223
|
+
tablets={row.Tablets}
|
224
|
+
nodeIds={row.NodeIds || []}
|
225
|
+
backend={backend}
|
226
|
+
/>
|
227
|
+
) : (
|
228
|
+
'—'
|
229
|
+
);
|
230
|
+
},
|
231
|
+
},
|
232
|
+
];
|
233
|
+
|
234
|
+
if (filteredTenants.length === 0 && problemFilter !== ProblemFilterValues.ALL) {
|
235
|
+
return <Illustration name="thumbsUp" width="200" />;
|
236
|
+
}
|
237
|
+
|
238
|
+
return (
|
239
|
+
<div className={b('table-wrapper')}>
|
240
|
+
<DataTable
|
241
|
+
theme="yandex-cloud"
|
242
|
+
data={filteredTenants}
|
243
|
+
columns={columns}
|
244
|
+
settings={DEFAULT_TABLE_SETTINGS}
|
245
|
+
emptyDataMessage="No such tenants"
|
246
|
+
/>
|
247
|
+
</div>
|
248
|
+
);
|
249
|
+
};
|
250
|
+
|
251
|
+
if (loading && !wasLoaded) {
|
252
|
+
return (
|
253
|
+
<div className={'loader'}>
|
254
|
+
<Loader size="l" />
|
255
|
+
</div>
|
256
|
+
);
|
257
|
+
}
|
258
|
+
|
259
|
+
if (error) {
|
260
|
+
return <ResponseError error={error} />;
|
261
|
+
}
|
262
|
+
|
263
|
+
return (
|
264
|
+
<div className={b()}>
|
265
|
+
{renderControls()}
|
266
|
+
{renderTable()}
|
267
|
+
</div>
|
268
|
+
);
|
269
|
+
};
|
package/dist/services/api.ts
CHANGED
@@ -282,19 +282,24 @@ export class YdbEmbeddedAPI extends AxiosWrapper {
|
|
282
282
|
},
|
283
283
|
{concurrentId}: AxiosOptions = {},
|
284
284
|
) {
|
285
|
+
// Time difference to ensure that timeout from ui will be shown rather than backend error
|
286
|
+
const uiTimeout = 9 * 60 * 1000;
|
287
|
+
const backendTimeout = 10 * 60 * 1000;
|
288
|
+
|
285
289
|
return this.post<QueryAPIResponse<Action, Schema>>(
|
286
|
-
this.getPath(
|
290
|
+
this.getPath(
|
291
|
+
`/viewer/json/query?timeout=${backendTimeout}${schema ? `&schema=${schema}` : ''}`,
|
292
|
+
),
|
287
293
|
{
|
288
294
|
query,
|
289
295
|
database,
|
290
296
|
action,
|
291
297
|
stats,
|
292
|
-
timeout: 600000,
|
293
298
|
},
|
294
299
|
{},
|
295
300
|
{
|
296
301
|
concurrentId,
|
297
|
-
timeout:
|
302
|
+
timeout: uiTimeout,
|
298
303
|
},
|
299
304
|
);
|
300
305
|
}
|
@@ -2,13 +2,13 @@ import type {Reducer} from 'redux';
|
|
2
2
|
import {createSelector, Selector} from 'reselect';
|
3
3
|
import {escapeRegExp} from 'lodash/fp';
|
4
4
|
|
5
|
-
import type {ValueOf} from '../../../types/common';
|
6
5
|
import '../../../services/api';
|
7
6
|
import {HOUR_IN_SECONDS} from '../../../utils/constants';
|
8
7
|
import {calcUptime, calcUptimeInSeconds} from '../../../utils';
|
9
8
|
import {NodesUptimeFilterValues} from '../../../utils/nodes';
|
10
9
|
import {EFlag} from '../../../types/api/enums';
|
11
10
|
|
11
|
+
import type {ProblemFilterValue} from '../settings/types';
|
12
12
|
import {createRequestActionTypes, createApiRequest} from '../../utils';
|
13
13
|
import {ProblemFilterValues} from '../settings/settings';
|
14
14
|
|
@@ -181,7 +181,7 @@ const getNodesList = (state: NodesStateSlice) => state.nodes.data;
|
|
181
181
|
|
182
182
|
const filterNodesByProblemsStatus = (
|
183
183
|
nodesList: NodesPreparedEntity[] = [],
|
184
|
-
problemFilter:
|
184
|
+
problemFilter: ProblemFilterValue,
|
185
185
|
) => {
|
186
186
|
if (problemFilter === ProblemFilterValues.ALL) {
|
187
187
|
return nodesList;
|
@@ -192,8 +192,8 @@ const filterNodesByProblemsStatus = (
|
|
192
192
|
});
|
193
193
|
};
|
194
194
|
|
195
|
-
export const filterNodesByUptime = (
|
196
|
-
nodesList:
|
195
|
+
export const filterNodesByUptime = <T extends {StartTime?: string}>(
|
196
|
+
nodesList: T[] = [],
|
197
197
|
nodesUptimeFilter: NodesUptimeFilterValues,
|
198
198
|
) => {
|
199
199
|
if (nodesUptimeFilter === NodesUptimeFilterValues.All) {
|
@@ -1,5 +1,5 @@
|
|
1
1
|
import type {IResponseError} from '../../../types/api/error';
|
2
|
-
import type {
|
2
|
+
import type {ProcessSpeedStats} from '../../../utils/bytesParsers';
|
3
3
|
import type {ApiRequestAction} from '../../utils';
|
4
4
|
|
5
5
|
import {FETCH_PARTITIONS, setDataWasNotLoaded, setSelectedConsumer} from './partitions';
|
@@ -9,8 +9,8 @@ export interface PreparedPartitionData {
|
|
9
9
|
partitionId: string;
|
10
10
|
storeSize: string;
|
11
11
|
|
12
|
-
writeSpeed:
|
13
|
-
readSpeed?:
|
12
|
+
writeSpeed: ProcessSpeedStats;
|
13
|
+
readSpeed?: ProcessSpeedStats;
|
14
14
|
|
15
15
|
partitionWriteLag: number;
|
16
16
|
partitionWriteIdleTime: number;
|
@@ -1,7 +1,6 @@
|
|
1
1
|
import type {Reducer} from 'redux';
|
2
2
|
import type {ThunkAction} from 'redux-thunk';
|
3
3
|
|
4
|
-
import type {ValueOf} from '../../../types/common';
|
5
4
|
import {
|
6
5
|
SAVED_QUERIES_KEY,
|
7
6
|
THEME_KEY,
|
@@ -23,6 +22,7 @@ import {TENANT_PAGES_IDS} from '../tenant/constants';
|
|
23
22
|
|
24
23
|
import type {RootState} from '..';
|
25
24
|
import type {
|
25
|
+
ProblemFilterValue,
|
26
26
|
SetSettingValueAction,
|
27
27
|
SettingsAction,
|
28
28
|
SettingsRootStateSlice,
|
@@ -133,11 +133,13 @@ export const getParsedSettingValue = (state: SettingsRootStateSlice, name: strin
|
|
133
133
|
return parseJson(value);
|
134
134
|
};
|
135
135
|
|
136
|
-
export const changeFilter = (filter:
|
136
|
+
export const changeFilter = (filter: ProblemFilterValue) => {
|
137
137
|
return {
|
138
138
|
type: CHANGE_PROBLEM_FILTER,
|
139
139
|
data: filter,
|
140
140
|
} as const;
|
141
141
|
};
|
142
142
|
|
143
|
+
export const selectProblemFilter = (state: SettingsRootStateSlice) => state.settings.problemFilter;
|
144
|
+
|
143
145
|
export default settings;
|
@@ -1,8 +1,10 @@
|
|
1
1
|
import type {ValueOf} from '../../../types/common';
|
2
2
|
import {changeFilter, ProblemFilterValues, SET_SETTING_VALUE} from './settings';
|
3
3
|
|
4
|
+
export type ProblemFilterValue = ValueOf<typeof ProblemFilterValues>;
|
5
|
+
|
4
6
|
export interface SettingsState {
|
5
|
-
problemFilter:
|
7
|
+
problemFilter: ProblemFilterValue;
|
6
8
|
userSettings: Record<string, string | undefined>;
|
7
9
|
systemSettings: Record<string, string | undefined>;
|
8
10
|
}
|