ydb-embedded-ui 2.4.4 → 2.5.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 +20 -0
- package/dist/components/InfoViewer/formatters/schema.ts +2 -1
- package/dist/components/InfoViewer/schemaInfo/CDCStreamInfo.tsx +3 -16
- package/dist/components/InfoViewer/schemaInfo/PersQueueGroupInfo.tsx +8 -13
- package/dist/components/InfoViewer/schemaInfo/TableIndexInfo.tsx +2 -12
- package/dist/components/InfoViewer/schemaOverview/CDCStreamOverview.tsx +3 -16
- package/dist/components/InfoViewer/schemaOverview/PersQueueGroupOverview.tsx +8 -13
- package/dist/components/InfoViewer/utils.ts +6 -6
- package/dist/components/Loader/Loader.scss +6 -3
- package/dist/components/Loader/Loader.tsx +7 -5
- package/dist/components/Loader/index.ts +1 -0
- package/dist/components/UptimeFIlter/UptimeFilter.tsx +21 -0
- package/dist/components/UptimeFIlter/index.ts +1 -0
- package/dist/containers/Node/Node.scss +1 -0
- package/dist/containers/Node/Node.tsx +3 -8
- package/dist/containers/Node/NodeStructure/NodeStructure.scss +0 -6
- package/dist/containers/Node/NodeStructure/NodeStructure.tsx +1 -1
- package/dist/containers/Nodes/Nodes.js +22 -10
- package/dist/{components → containers}/NodesViewer/NodesViewer.js +49 -62
- package/dist/{components → containers}/NodesViewer/NodesViewer.scss +0 -0
- package/dist/containers/Storage/Pdisk/Pdisk.scss +1 -1
- package/dist/containers/Storage/Storage.js +35 -10
- package/dist/containers/Storage/StorageNodes/StorageNodes.scss +2 -2
- package/dist/containers/Storage/StorageNodes/StorageNodes.tsx +35 -17
- package/dist/containers/Storage/StorageNodes/i18n/en.json +6 -4
- package/dist/containers/Storage/StorageNodes/i18n/ru.json +6 -4
- package/dist/containers/Storage/UsageFilter/UsageFilter.scss +10 -5
- package/dist/containers/Tenant/Acl/Acl.js +1 -7
- package/dist/containers/Tenant/Diagnostics/Compute/Compute.js +1 -1
- package/dist/containers/Tenant/Diagnostics/Consumers/Consumers.tsx +34 -8
- package/dist/containers/Tenant/Diagnostics/Describe/Describe.scss +0 -8
- package/dist/containers/Tenant/Diagnostics/Describe/Describe.tsx +2 -6
- package/dist/containers/Tenant/Diagnostics/Diagnostics.scss +0 -7
- package/dist/containers/Tenant/Diagnostics/Diagnostics.tsx +5 -7
- package/dist/containers/Tenant/Diagnostics/TopShards/TopShards.js +21 -13
- package/dist/containers/Tenant/ObjectGeneral/ObjectGeneral.tsx +1 -5
- package/dist/containers/Tenant/QueryEditor/QueryExplain/QueryExplain.js +4 -4
- package/dist/containers/Tenant/QueryEditor/SaveQuery/SaveQuery.js +1 -4
- package/dist/containers/Tenant/Schema/SchemaInfoViewer/SchemaInfoViewer.js +23 -28
- package/dist/containers/Tenant/TenantPages.tsx +1 -1
- package/dist/containers/Tenant/utils/schemaActions.ts +9 -20
- package/dist/store/reducers/clusterNodes.js +29 -10
- package/dist/store/reducers/nodes.js +24 -3
- package/dist/store/reducers/{schema.js → schema.ts} +22 -14
- package/dist/store/reducers/storage.js +46 -5
- package/dist/types/store/schema.ts +46 -0
- package/dist/utils/index.js +6 -2
- package/dist/utils/nodes.ts +9 -0
- package/package.json +1 -1
@@ -5,7 +5,9 @@ import {Link} from 'react-router-dom';
|
|
5
5
|
import {useDispatch, useSelector} from 'react-redux';
|
6
6
|
import {useLocation} from 'react-router';
|
7
7
|
|
8
|
-
import {
|
8
|
+
import {Switch, Tabs} from '@gravity-ui/uikit';
|
9
|
+
|
10
|
+
import {Loader} from '../../../components/Loader';
|
9
11
|
|
10
12
|
//@ts-ignore
|
11
13
|
import TopQueries from './TopQueries/TopQueries';
|
@@ -153,7 +155,7 @@ function Diagnostics(props: DiagnosticsProps) {
|
|
153
155
|
return <Heatmap path={currentItem.Path} />;
|
154
156
|
}
|
155
157
|
case GeneralPagesIds.consumers: {
|
156
|
-
return <Consumers path={
|
158
|
+
return <Consumers path={currentSchemaPath} />;
|
157
159
|
}
|
158
160
|
default: {
|
159
161
|
return <div>No data...</div>;
|
@@ -196,11 +198,7 @@ function Diagnostics(props: DiagnosticsProps) {
|
|
196
198
|
// After tabs are initially loaded it is no longer needed
|
197
199
|
// Thus there is no also "loading" check as in other parts of the project
|
198
200
|
if (!wasLoaded) {
|
199
|
-
return
|
200
|
-
<div className={b('loader')}>
|
201
|
-
<Loader size="l" />
|
202
|
-
</div>
|
203
|
-
);
|
201
|
+
return <Loader size="l" />;
|
204
202
|
}
|
205
203
|
|
206
204
|
return (
|
@@ -47,17 +47,23 @@ function prepareDateSizeValue(value) {
|
|
47
47
|
}
|
48
48
|
|
49
49
|
function stringToDataTableSortOrder(value) {
|
50
|
-
return
|
51
|
-
|
52
|
-
|
53
|
-
|
50
|
+
return (
|
51
|
+
value &&
|
52
|
+
value.split(',').map((columnId) => ({
|
53
|
+
columnId,
|
54
|
+
order: DataTable.DESCENDING,
|
55
|
+
}))
|
56
|
+
);
|
54
57
|
}
|
55
58
|
|
56
59
|
function stringToQuerySortOrder(value) {
|
57
|
-
return
|
58
|
-
|
59
|
-
|
60
|
-
|
60
|
+
return (
|
61
|
+
value &&
|
62
|
+
value.split(',').map((columnId) => ({
|
63
|
+
columnId,
|
64
|
+
order: 'DESC',
|
65
|
+
}))
|
66
|
+
);
|
61
67
|
}
|
62
68
|
|
63
69
|
function dataTableToStringSortOrder(value = []) {
|
@@ -85,11 +91,13 @@ function TopShards({
|
|
85
91
|
|
86
92
|
if (autorefresh) {
|
87
93
|
autofetcher.start();
|
88
|
-
autofetcher.fetch(() =>
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
94
|
+
autofetcher.fetch(() =>
|
95
|
+
sendShardQuery({
|
96
|
+
database: path,
|
97
|
+
path: currentSchemaPath,
|
98
|
+
sortOrder: stringToQuerySortOrder(sortOrder),
|
99
|
+
}),
|
100
|
+
);
|
93
101
|
}
|
94
102
|
|
95
103
|
return () => {
|
@@ -54,11 +54,7 @@ function ObjectGeneral(props: ObjectGeneralProps) {
|
|
54
54
|
if (!tenantName) {
|
55
55
|
return null;
|
56
56
|
}
|
57
|
-
return (
|
58
|
-
<div className={b()}>
|
59
|
-
{renderTabContent()}
|
60
|
-
</div>
|
61
|
-
);
|
57
|
+
return <div className={b()}>{renderTabContent()}</div>;
|
62
58
|
};
|
63
59
|
|
64
60
|
return renderContent();
|
@@ -1,4 +1,4 @@
|
|
1
|
-
import React, {
|
1
|
+
import React, {useEffect, useRef, useState} from 'react';
|
2
2
|
import cn from 'bem-cn-lite';
|
3
3
|
import MonacoEditor from 'react-monaco-editor';
|
4
4
|
import {Loader, RadioButton} from '@gravity-ui/uikit';
|
@@ -58,7 +58,7 @@ function GraphRoot(props) {
|
|
58
58
|
updateComponentTheme(theme);
|
59
59
|
}, [theme]);
|
60
60
|
|
61
|
-
const render =
|
61
|
+
const render = () => {
|
62
62
|
if (version === explainVersions.v2) {
|
63
63
|
paranoid.current = getTopology('graphRoot', data, opts, shapes);
|
64
64
|
paranoid.current.render();
|
@@ -66,7 +66,7 @@ function GraphRoot(props) {
|
|
66
66
|
paranoid.current = getCompactTopology('graphRoot', data, opts);
|
67
67
|
paranoid.current.renderCompactTopology();
|
68
68
|
}
|
69
|
-
}
|
69
|
+
};
|
70
70
|
|
71
71
|
useEffect(() => {
|
72
72
|
render();
|
@@ -86,7 +86,7 @@ function GraphRoot(props) {
|
|
86
86
|
graphRoot.innerHTML = '';
|
87
87
|
|
88
88
|
render();
|
89
|
-
}, [componentTheme
|
89
|
+
}, [componentTheme]);
|
90
90
|
|
91
91
|
useEffect(() => {
|
92
92
|
paranoid.current?.updateData?.(props.data);
|
@@ -73,10 +73,7 @@ function SaveQuery({savedQueries, onSaveQuery, saveButtonDisabled}) {
|
|
73
73
|
</div>
|
74
74
|
)}
|
75
75
|
<div className={b('dialog-row')}>
|
76
|
-
<label
|
77
|
-
htmlFor="queryName"
|
78
|
-
className={b('field-title', 'required')}
|
79
|
-
>
|
76
|
+
<label htmlFor="queryName" className={b('field-title', 'required')}>
|
80
77
|
Query name
|
81
78
|
</label>
|
82
79
|
<div className={b('control-wrapper')}>
|
@@ -44,9 +44,10 @@ const formatTableStatsItem = createInfoFormatter({
|
|
44
44
|
defaultValueFormatter: formatNumber,
|
45
45
|
});
|
46
46
|
|
47
|
-
const formatTableStats = (fields) =>
|
48
|
-
.
|
49
|
-
|
47
|
+
const formatTableStats = (fields) =>
|
48
|
+
Object.entries(fields)
|
49
|
+
.map(([label, value]) => formatTableStatsItem(label, value))
|
50
|
+
.filter(({value}) => Boolean(value));
|
50
51
|
|
51
52
|
class SchemaInfoViewer extends React.Component {
|
52
53
|
static propTypes = {
|
@@ -60,17 +61,18 @@ class SchemaInfoViewer extends React.Component {
|
|
60
61
|
|
61
62
|
return (
|
62
63
|
<div className={b('item')}>
|
63
|
-
<InfoViewer
|
64
|
-
title={title}
|
65
|
-
info={itemData}
|
66
|
-
/>
|
64
|
+
<InfoViewer title={title} info={itemData} />
|
67
65
|
</div>
|
68
66
|
);
|
69
67
|
}
|
70
68
|
|
71
69
|
renderContent(data) {
|
72
70
|
const {PathDescription = {}} = data;
|
73
|
-
const {
|
71
|
+
const {
|
72
|
+
TableStats = {},
|
73
|
+
TabletMetrics = {},
|
74
|
+
Table: {PartitionConfig = {}} = {},
|
75
|
+
} = PathDescription;
|
74
76
|
const {
|
75
77
|
PartCount,
|
76
78
|
RowCount,
|
@@ -127,33 +129,30 @@ class SchemaInfoViewer extends React.Component {
|
|
127
129
|
];
|
128
130
|
|
129
131
|
const tabletMetricsInfo = Object.keys(TabletMetrics).map((key) =>
|
130
|
-
formatTabletMetricsItem(key, TabletMetrics[key])
|
132
|
+
formatTabletMetricsItem(key, TabletMetrics[key]),
|
131
133
|
);
|
132
134
|
|
133
135
|
const partitionConfigInfo = [];
|
134
136
|
|
135
137
|
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
138
|
partitionConfigInfo.push(
|
141
|
-
|
139
|
+
...Object.keys(FollowerGroups[0]).map((key) =>
|
140
|
+
formatFollowerGroupItem(key, FollowerGroups[0][key]),
|
141
|
+
),
|
142
142
|
);
|
143
|
+
} else if (FollowerCount !== undefined) {
|
144
|
+
partitionConfigInfo.push(formatPartitionConfigItem('FollowerCount', FollowerCount));
|
143
145
|
} else if (CrossDataCenterFollowerCount !== undefined) {
|
144
146
|
partitionConfigInfo.push(
|
145
|
-
formatPartitionConfigItem(
|
147
|
+
formatPartitionConfigItem(
|
148
|
+
'CrossDataCenterFollowerCount',
|
149
|
+
CrossDataCenterFollowerCount,
|
150
|
+
),
|
146
151
|
);
|
147
152
|
}
|
148
153
|
|
149
|
-
if ([
|
150
|
-
|
151
|
-
partitionConfigInfo,
|
152
|
-
tableStatsInfo.flat(),
|
153
|
-
].flat().length === 0) {
|
154
|
-
return (
|
155
|
-
<div className={b('item')}>Empty</div>
|
156
|
-
);
|
154
|
+
if ([tabletMetricsInfo, partitionConfigInfo, tableStatsInfo.flat()].flat().length === 0) {
|
155
|
+
return <div className={b('item')}>Empty</div>;
|
157
156
|
}
|
158
157
|
|
159
158
|
return (
|
@@ -179,11 +178,7 @@ class SchemaInfoViewer extends React.Component {
|
|
179
178
|
const {data} = this.props;
|
180
179
|
|
181
180
|
if (data) {
|
182
|
-
return (
|
183
|
-
<div className={b()}>
|
184
|
-
{this.renderContent(data)}
|
185
|
-
</div>
|
186
|
-
);
|
181
|
+
return <div className={b()}>{this.renderContent(data)}</div>;
|
187
182
|
} else {
|
188
183
|
return <div className="error">no schema data</div>;
|
189
184
|
}
|
@@ -37,9 +37,9 @@ const bindActions = (
|
|
37
37
|
) => {
|
38
38
|
const inputQuery = (tmpl: (path: string) => string) => () => {
|
39
39
|
dispatch(changeUserInput({input: tmpl(path)}));
|
40
|
-
dispatch(setTopLevelTab(TenantGeneralTabsIds.query))
|
40
|
+
dispatch(setTopLevelTab(TenantGeneralTabsIds.query));
|
41
41
|
setActivePath(path);
|
42
|
-
}
|
42
|
+
};
|
43
43
|
|
44
44
|
return {
|
45
45
|
createTable: inputQuery(createTableTemplate),
|
@@ -66,7 +66,7 @@ const bindActions = (
|
|
66
66
|
},
|
67
67
|
openPreview: () => {
|
68
68
|
dispatch(setShowPreview(true));
|
69
|
-
dispatch(setTopLevelTab(TenantGeneralTabsIds.query))
|
69
|
+
dispatch(setTopLevelTab(TenantGeneralTabsIds.query));
|
70
70
|
setActivePath(path);
|
71
71
|
},
|
72
72
|
};
|
@@ -74,27 +74,18 @@ const bindActions = (
|
|
74
74
|
|
75
75
|
type ActionsSet = ReturnType<Required<NavigationTreeProps>['getActions']>;
|
76
76
|
|
77
|
-
export const getActions =
|
78
|
-
dispatch: Dispatch<any>,
|
79
|
-
setActivePath: (path: string) => void,
|
80
|
-
) =>
|
77
|
+
export const getActions =
|
78
|
+
(dispatch: Dispatch<any>, setActivePath: (path: string) => void) =>
|
81
79
|
(path: string, type: NavigationTreeNodeType) => {
|
82
80
|
const actions = bindActions(path, dispatch, setActivePath);
|
83
81
|
const copyItem = {text: 'Copy path', action: actions.copyPath};
|
84
82
|
|
85
83
|
const DIR_SET: ActionsSet = [
|
86
|
-
[
|
87
|
-
|
88
|
-
],
|
89
|
-
[
|
90
|
-
{text: 'Create table...', action: actions.createTable},
|
91
|
-
],
|
84
|
+
[copyItem],
|
85
|
+
[{text: 'Create table...', action: actions.createTable}],
|
92
86
|
];
|
93
87
|
const TABLE_SET: ActionsSet = [
|
94
|
-
[
|
95
|
-
{text: 'Open preview', action: actions.openPreview},
|
96
|
-
copyItem,
|
97
|
-
],
|
88
|
+
[{text: 'Open preview', action: actions.openPreview}, copyItem],
|
98
89
|
[
|
99
90
|
{text: 'Alter table...', action: actions.alterTable},
|
100
91
|
{text: 'Select query...', action: actions.selectQuery},
|
@@ -102,9 +93,7 @@ export const getActions = (
|
|
102
93
|
],
|
103
94
|
];
|
104
95
|
|
105
|
-
const JUST_COPY: ActionsSet = [
|
106
|
-
copyItem,
|
107
|
-
];
|
96
|
+
const JUST_COPY: ActionsSet = [copyItem];
|
108
97
|
|
109
98
|
const EMPTY_SET: ActionsSet = [];
|
110
99
|
|
@@ -1,7 +1,11 @@
|
|
1
|
-
import {createRequestActionTypes, createApiRequest} from '../utils';
|
2
1
|
import {createSelector} from 'reselect';
|
2
|
+
|
3
3
|
import '../../services/api';
|
4
4
|
import {ALL} from '../../utils/constants';
|
5
|
+
import {createRequestActionTypes, createApiRequest} from '../utils';
|
6
|
+
|
7
|
+
import {filterByUptime} from './storage';
|
8
|
+
import {getNodesUptimeFilter} from './nodes';
|
5
9
|
|
6
10
|
const FETCH_NODES_LIST = createRequestActionTypes('tenants', 'FETCH_NODES_LIST');
|
7
11
|
|
@@ -47,23 +51,38 @@ export function getNodesList() {
|
|
47
51
|
});
|
48
52
|
}
|
49
53
|
|
54
|
+
const filterByProblemsStatus = (nodes = [], problemFilter) => {
|
55
|
+
if (problemFilter === ALL) {
|
56
|
+
return nodes;
|
57
|
+
}
|
58
|
+
|
59
|
+
return nodes.filter(({Overall}) => {
|
60
|
+
return Overall && Overall !== 'Green';
|
61
|
+
});
|
62
|
+
};
|
63
|
+
|
64
|
+
export const filterNodesByStatusAndUptime = (nodes = [], problemFilter, uptimeFilter) => {
|
65
|
+
let result = filterByProblemsStatus(nodes, problemFilter);
|
66
|
+
result = filterByUptime(result, uptimeFilter);
|
67
|
+
|
68
|
+
return result;
|
69
|
+
};
|
70
|
+
|
50
71
|
export const getFilteredNodes = createSelector(
|
51
|
-
|
52
|
-
|
53
|
-
|
72
|
+
[
|
73
|
+
(state) => state.nodes.data?.Tenants,
|
74
|
+
(state) => state.settings.problemFilter,
|
75
|
+
getNodesUptimeFilter,
|
76
|
+
],
|
77
|
+
(tenants, problemFilter, uptimeFilter) => {
|
54
78
|
const nodes = tenants?.reduce((acc, item) => {
|
55
79
|
if (Array.isArray(item.Nodes)) {
|
56
80
|
return [...acc, ...item.Nodes.map((node) => ({...node, TenantName: item.Name}))];
|
57
81
|
}
|
58
82
|
return acc;
|
59
83
|
}, []);
|
60
|
-
if (filter === ALL) {
|
61
|
-
return nodes;
|
62
|
-
}
|
63
84
|
|
64
|
-
return nodes
|
65
|
-
return Overall && Overall !== 'Green';
|
66
|
-
});
|
85
|
+
return filterNodesByStatusAndUptime(nodes, problemFilter, uptimeFilter);
|
67
86
|
},
|
68
87
|
);
|
69
88
|
|
@@ -1,9 +1,19 @@
|
|
1
1
|
import {createRequestActionTypes, createApiRequest} from '../utils';
|
2
2
|
import '../../services/api';
|
3
|
+
import {NodesUptimeFilterValues} from '../../utils/nodes';
|
3
4
|
|
4
5
|
const FETCH_NODES = createRequestActionTypes('nodes', 'FETCH_NODES');
|
5
6
|
|
6
|
-
const
|
7
|
+
const CLEAR_NODES = 'nodes/CLEAR_NODES';
|
8
|
+
const SET_NODES_UPTIME_FILTER = 'nodes/SET_NODES_UPTIME_FILTER';
|
9
|
+
|
10
|
+
const initialState = {
|
11
|
+
loading: true,
|
12
|
+
wasLoaded: false,
|
13
|
+
nodesUptimeFilter: NodesUptimeFilterValues.All,
|
14
|
+
};
|
15
|
+
|
16
|
+
const nodes = (state = initialState, action) => {
|
7
17
|
switch (action.type) {
|
8
18
|
case FETCH_NODES.REQUEST: {
|
9
19
|
return {
|
@@ -28,7 +38,7 @@ const nodes = function z(state = {loading: true, wasLoaded: false}, action) {
|
|
28
38
|
loading: false,
|
29
39
|
};
|
30
40
|
}
|
31
|
-
case
|
41
|
+
case CLEAR_NODES: {
|
32
42
|
return {
|
33
43
|
...state,
|
34
44
|
loading: true,
|
@@ -38,6 +48,10 @@ const nodes = function z(state = {loading: true, wasLoaded: false}, action) {
|
|
38
48
|
error: undefined,
|
39
49
|
};
|
40
50
|
}
|
51
|
+
|
52
|
+
case SET_NODES_UPTIME_FILTER: {
|
53
|
+
return {...state, nodesUptimeFilter: action.data};
|
54
|
+
}
|
41
55
|
default:
|
42
56
|
return state;
|
43
57
|
}
|
@@ -50,6 +64,13 @@ export function getNodes(path) {
|
|
50
64
|
});
|
51
65
|
}
|
52
66
|
|
53
|
-
export const clearNodes = () => ({type:
|
67
|
+
export const clearNodes = () => ({type: CLEAR_NODES});
|
68
|
+
|
69
|
+
export const setNodesUptimeFilter = (value) => ({
|
70
|
+
type: SET_NODES_UPTIME_FILTER,
|
71
|
+
data: value,
|
72
|
+
});
|
73
|
+
|
74
|
+
export const getNodesUptimeFilter = (state) => state.nodes.nodesUptimeFilter;
|
54
75
|
|
55
76
|
export default nodes;
|
@@ -1,7 +1,11 @@
|
|
1
|
-
import {
|
1
|
+
import {Reducer} from 'redux';
|
2
|
+
|
3
|
+
import {ISchemaAction, ISchemaData, ISchemaState} from '../../types/store/schema';
|
2
4
|
import '../../services/api';
|
3
5
|
|
4
|
-
|
6
|
+
import {createRequestActionTypes, createApiRequest} from '../utils';
|
7
|
+
|
8
|
+
export const FETCH_SCHEMA = createRequestActionTypes('schema', 'FETCH_SCHEMA');
|
5
9
|
const PRELOAD_SCHEMAS = 'schema/PRELOAD_SCHEMAS';
|
6
10
|
const SET_SCHEMA = 'schema/SET_SCHEMA';
|
7
11
|
const SET_SHOW_PREVIEW = 'schema/SET_SHOW_PREVIEW';
|
@@ -18,7 +22,7 @@ export const initialState = {
|
|
18
22
|
showPreview: false,
|
19
23
|
};
|
20
24
|
|
21
|
-
const schema = (state = initialState, action) => {
|
25
|
+
const schema: Reducer<ISchemaState, ISchemaAction> = (state = initialState, action) => {
|
22
26
|
switch (action.type) {
|
23
27
|
case FETCH_SCHEMA.REQUEST: {
|
24
28
|
return {
|
@@ -28,7 +32,11 @@ const schema = (state = initialState, action) => {
|
|
28
32
|
}
|
29
33
|
case FETCH_SCHEMA.SUCCESS: {
|
30
34
|
const newData = JSON.parse(JSON.stringify(state.data));
|
31
|
-
|
35
|
+
|
36
|
+
if (action.data.Path) {
|
37
|
+
newData[action.data.Path] = action.data;
|
38
|
+
}
|
39
|
+
|
32
40
|
const currentSchema = state.currentSchemaPath
|
33
41
|
? newData[state.currentSchemaPath]
|
34
42
|
: action.data;
|
@@ -100,49 +108,49 @@ const schema = (state = initialState, action) => {
|
|
100
108
|
}
|
101
109
|
};
|
102
110
|
|
103
|
-
export function getSchema({path}) {
|
111
|
+
export function getSchema({path}: {path: string}) {
|
104
112
|
return createApiRequest({
|
105
113
|
request: window.api.getSchema({path}),
|
106
114
|
actions: FETCH_SCHEMA,
|
107
115
|
});
|
108
116
|
}
|
109
117
|
|
110
|
-
export function setCurrentSchemaPath(currentSchemaPath) {
|
118
|
+
export function setCurrentSchemaPath(currentSchemaPath: string) {
|
111
119
|
return {
|
112
120
|
type: SET_SCHEMA,
|
113
121
|
data: currentSchemaPath,
|
114
|
-
};
|
122
|
+
} as const;
|
115
123
|
}
|
116
124
|
export function enableAutorefresh() {
|
117
125
|
return {
|
118
126
|
type: ENABLE_AUTOREFRESH,
|
119
|
-
};
|
127
|
+
} as const;
|
120
128
|
}
|
121
129
|
export function disableAutorefresh() {
|
122
130
|
return {
|
123
131
|
type: DISABLE_AUTOREFRESH,
|
124
|
-
};
|
132
|
+
} as const;
|
125
133
|
}
|
126
|
-
export function setShowPreview(value) {
|
134
|
+
export function setShowPreview(value: boolean) {
|
127
135
|
return {
|
128
136
|
type: SET_SHOW_PREVIEW,
|
129
137
|
data: value,
|
130
|
-
};
|
138
|
+
} as const;
|
131
139
|
}
|
132
140
|
|
133
141
|
// only stores data for paths that are not in the store yet
|
134
142
|
// existing paths are ignored
|
135
|
-
export function preloadSchemas(data) {
|
143
|
+
export function preloadSchemas(data: ISchemaData) {
|
136
144
|
return {
|
137
145
|
type: PRELOAD_SCHEMAS,
|
138
146
|
data,
|
139
|
-
};
|
147
|
+
} as const;
|
140
148
|
}
|
141
149
|
|
142
150
|
export function resetLoadingState() {
|
143
151
|
return {
|
144
152
|
type: RESET_LOADING_STATE,
|
145
|
-
};
|
153
|
+
} as const;
|
146
154
|
}
|
147
155
|
|
148
156
|
export default schema;
|
@@ -1,10 +1,14 @@
|
|
1
|
-
import {createRequestActionTypes, createApiRequest} from '../utils';
|
2
|
-
import '../../services/api';
|
3
1
|
import _ from 'lodash';
|
4
2
|
import {createSelector} from 'reselect';
|
5
|
-
|
3
|
+
|
4
|
+
import {calcUptime, calcUptimeInSeconds} from '../../utils';
|
6
5
|
import {getUsage} from '../../utils/storage';
|
6
|
+
import {NodesUptimeFilterValues} from '../../utils/nodes';
|
7
7
|
import {getPDiskType} from '../../utils/pdisk';
|
8
|
+
import {HOUR_IN_SECONDS} from '../../utils/constants';
|
9
|
+
import '../../services/api';
|
10
|
+
|
11
|
+
import {createRequestActionTypes, createApiRequest} from '../utils';
|
8
12
|
|
9
13
|
export const VisibleEntities = {
|
10
14
|
All: 'All',
|
@@ -29,6 +33,7 @@ const SET_FILTER = 'storage/SET_FILTER';
|
|
29
33
|
const SET_USAGE_FILTER = 'storage/SET_USAGE_FILTER';
|
30
34
|
const SET_VISIBLE_GROUPS = 'storage/SET_VISIBLE_GROUPS';
|
31
35
|
const SET_STORAGE_TYPE = 'storage/SET_STORAGE_TYPE';
|
36
|
+
const SET_NODES_UPTIME_FILTER = 'storage/SET_NODES_UPTIME_FILTER';
|
32
37
|
|
33
38
|
const initialState = {
|
34
39
|
loading: true,
|
@@ -36,6 +41,7 @@ const initialState = {
|
|
36
41
|
filter: '',
|
37
42
|
usageFilter: [],
|
38
43
|
visible: VisibleEntities.Missing,
|
44
|
+
nodesUptimeFilter: NodesUptimeFilterValues.All,
|
39
45
|
type: StorageTypes.groups,
|
40
46
|
};
|
41
47
|
|
@@ -93,6 +99,13 @@ const storage = (state = initialState, action) => {
|
|
93
99
|
error: undefined,
|
94
100
|
};
|
95
101
|
}
|
102
|
+
|
103
|
+
case SET_NODES_UPTIME_FILTER: {
|
104
|
+
return {
|
105
|
+
...state,
|
106
|
+
nodesUptimeFilter: action.data,
|
107
|
+
};
|
108
|
+
}
|
96
109
|
case SET_STORAGE_TYPE: {
|
97
110
|
return {
|
98
111
|
...state,
|
@@ -149,6 +162,13 @@ export function setVisibleEntities(value) {
|
|
149
162
|
};
|
150
163
|
}
|
151
164
|
|
165
|
+
export function setNodesUptimeFilter(value) {
|
166
|
+
return {
|
167
|
+
type: SET_NODES_UPTIME_FILTER,
|
168
|
+
data: value,
|
169
|
+
};
|
170
|
+
}
|
171
|
+
|
152
172
|
export const getStoragePools = (state) => state.storage.data?.StoragePools;
|
153
173
|
export const getStoragePoolsGroupsCount = (state) => ({
|
154
174
|
total: state.storage.data?.TotalGroups || 0,
|
@@ -162,6 +182,7 @@ export const getStorageNodesCount = (state) => ({
|
|
162
182
|
export const getStorageFilter = (state) => state.storage.filter;
|
163
183
|
export const getUsageFilter = (state) => state.storage.usageFilter;
|
164
184
|
export const getVisibleEntities = (state) => state.storage.visible;
|
185
|
+
export const getNodesUptimeFilter = (state) => state.storage.nodesUptimeFilter;
|
165
186
|
export const getStorageType = (state) => state.storage.type;
|
166
187
|
export const getNodesObject = (state) =>
|
167
188
|
_.reduce(
|
@@ -337,12 +358,32 @@ const filterByUsage = (entities, usage) => {
|
|
337
358
|
});
|
338
359
|
};
|
339
360
|
|
361
|
+
export const filterByUptime = (nodes = [], nodesUptimeFilter) => {
|
362
|
+
if (nodesUptimeFilter === NodesUptimeFilterValues.All) {
|
363
|
+
return nodes;
|
364
|
+
}
|
365
|
+
return nodes.filter(({StartTime}) => {
|
366
|
+
return !StartTime || calcUptimeInSeconds(StartTime) < HOUR_IN_SECONDS;
|
367
|
+
});
|
368
|
+
};
|
369
|
+
|
340
370
|
export const getFilteredEntities = createSelector(
|
341
|
-
[
|
342
|
-
|
371
|
+
[
|
372
|
+
getStorageFilter,
|
373
|
+
getUsageFilter,
|
374
|
+
getStorageType,
|
375
|
+
getNodesUptimeFilter,
|
376
|
+
getVisibleEntitiesList,
|
377
|
+
],
|
378
|
+
(textFilter, usageFilter, type, nodesUptimeFilter, entities) => {
|
343
379
|
let result = entities;
|
344
380
|
result = filterByText(result, type, textFilter);
|
345
381
|
result = filterByUsage(result, usageFilter);
|
382
|
+
|
383
|
+
if (type === StorageTypes.nodes) {
|
384
|
+
result = filterByUptime(result, nodesUptimeFilter);
|
385
|
+
}
|
386
|
+
|
346
387
|
return result;
|
347
388
|
},
|
348
389
|
);
|
@@ -0,0 +1,46 @@
|
|
1
|
+
import {
|
2
|
+
disableAutorefresh,
|
3
|
+
enableAutorefresh,
|
4
|
+
FETCH_SCHEMA,
|
5
|
+
preloadSchemas,
|
6
|
+
resetLoadingState,
|
7
|
+
setCurrentSchemaPath,
|
8
|
+
setShowPreview,
|
9
|
+
} from '../../store/reducers/schema';
|
10
|
+
import {ApiRequestAction} from '../../store/utils';
|
11
|
+
import {IResponseError} from '../api/error';
|
12
|
+
import {TEvDescribeSchemeResult} from '../api/schema';
|
13
|
+
|
14
|
+
export type ISchemaData = Record<string, TEvDescribeSchemeResult>;
|
15
|
+
|
16
|
+
export interface ISchemaState {
|
17
|
+
loading: boolean;
|
18
|
+
wasLoaded: boolean;
|
19
|
+
data: ISchemaData;
|
20
|
+
currentSchema?: TEvDescribeSchemeResult;
|
21
|
+
currentSchemaPath?: string;
|
22
|
+
autorefresh: boolean;
|
23
|
+
showPreview: boolean;
|
24
|
+
error?: IResponseError;
|
25
|
+
}
|
26
|
+
|
27
|
+
type ISchemaApiRequestAction = ApiRequestAction<
|
28
|
+
typeof FETCH_SCHEMA,
|
29
|
+
TEvDescribeSchemeResult,
|
30
|
+
IResponseError
|
31
|
+
>;
|
32
|
+
|
33
|
+
export type ISchemaAction =
|
34
|
+
| ISchemaApiRequestAction
|
35
|
+
| (
|
36
|
+
| ReturnType<typeof setCurrentSchemaPath>
|
37
|
+
| ReturnType<typeof enableAutorefresh>
|
38
|
+
| ReturnType<typeof disableAutorefresh>
|
39
|
+
| ReturnType<typeof setShowPreview>
|
40
|
+
| ReturnType<typeof preloadSchemas>
|
41
|
+
| ReturnType<typeof resetLoadingState>
|
42
|
+
);
|
43
|
+
|
44
|
+
export interface ISchemaRootStateSlice {
|
45
|
+
schema: ISchemaState;
|
46
|
+
}
|