ydb-embedded-ui 4.5.0 → 4.5.2
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 +14 -0
- package/dist/components/CriticalActionDialog/CriticalActionDialog.tsx +3 -0
- package/dist/components/FullNodeViewer/FullNodeViewer.js +1 -1
- package/dist/components/PoolUsage/PoolUsage.scss +1 -1
- package/dist/components/PoolUsage/PoolUsage.tsx +50 -0
- package/dist/containers/Node/Node.tsx +1 -1
- package/dist/containers/Node/NodeStructure/NodeStructure.tsx +19 -17
- package/dist/containers/Tablet/Tablet.scss +4 -0
- package/dist/containers/Tablet/Tablet.tsx +2 -1
- package/dist/containers/Tablet/TabletControls/TabletControls.tsx +19 -27
- package/dist/containers/Tenant/Diagnostics/Consumers/columns/columns.tsx +2 -2
- package/dist/containers/Tenant/Diagnostics/Diagnostics.tsx +37 -38
- package/dist/containers/Tenant/Diagnostics/DiagnosticsPages.ts +15 -28
- package/dist/containers/Tenant/Diagnostics/Network/Network.js +2 -2
- package/dist/containers/Tenant/Diagnostics/TenantOverview/TenantOverview.js +3 -3
- package/dist/containers/Tenant/Diagnostics/TopQueries/TopQueries.tsx +5 -3
- package/dist/containers/Tenant/ObjectGeneral/ObjectGeneral.tsx +2 -3
- package/dist/containers/Tenant/ObjectSummary/ObjectSummary.tsx +3 -3
- package/dist/containers/Tenant/Tenant.tsx +14 -15
- package/dist/containers/Tenant/TenantPages.tsx +3 -6
- package/dist/containers/Tenant/utils/schemaActions.ts +4 -4
- package/dist/containers/Versions/GroupedNodesTree/GroupedNodesTree.scss +1 -0
- package/dist/containers/Versions/NodesTable/NodesTable.tsx +2 -3
- package/dist/routes.ts +1 -1
- package/dist/services/api.ts +2 -2
- package/dist/store/reducers/index.ts +4 -4
- package/dist/store/reducers/{network.js → network/network.ts} +11 -8
- package/dist/store/reducers/network/types.ts +16 -0
- package/dist/store/reducers/node/node.ts +102 -0
- package/dist/store/reducers/node/selectors.ts +59 -0
- package/dist/store/reducers/node/types.ts +44 -0
- package/dist/store/reducers/{schemaAcl.js → schemaAcl/schemaAcl.ts} +20 -9
- package/dist/store/reducers/schemaAcl/types.ts +15 -0
- package/dist/store/reducers/tenant/constants.ts +19 -0
- package/dist/store/reducers/{tenant.js → tenant/tenant.ts} +27 -36
- package/dist/store/reducers/tenant/types.ts +25 -0
- package/dist/types/api/acl.ts +1 -1
- package/dist/utils/index.js +2 -1
- package/package.json +2 -1
- package/dist/components/Breadcrumbs/Breadcrumbs.js +0 -25
- package/dist/components/Breadcrumbs/Breadcrumbs.scss +0 -5
- package/dist/components/Collapse/Collapse.js +0 -84
- package/dist/components/Collapse/Collapse.scss +0 -70
- package/dist/components/PoolUsage/PoolUsage.js +0 -54
- package/dist/store/reducers/node.js +0 -141
@@ -1,13 +1,20 @@
|
|
1
|
-
import {
|
2
|
-
|
3
|
-
import
|
1
|
+
import type {Reducer} from 'redux';
|
2
|
+
|
3
|
+
import type {TTenant} from '../../../types/api/tenant';
|
4
|
+
import type {TenantAction, TenantDiagnosticsTab, TenantGeneralTab, TenantState} from './types';
|
5
|
+
|
6
|
+
import '../../../services/api';
|
7
|
+
import {createRequestActionTypes, createApiRequest} from '../../utils';
|
8
|
+
|
9
|
+
export const FETCH_TENANT = createRequestActionTypes('tenant', 'FETCH_TENANT');
|
4
10
|
|
5
|
-
const FETCH_TENANT = createRequestActionTypes('tenant', 'FETCH_TENANT');
|
6
11
|
const SET_TOP_LEVEL_TAB = 'tenant/SET_TOP_LEVEL_TAB';
|
7
12
|
const SET_DIAGNOSTICS_TAB = 'tenant/SET_DIAGNOSTICS_TAB';
|
8
13
|
const CLEAR_TENANT = 'tenant/CLEAR_TENANT';
|
9
14
|
|
10
|
-
const
|
15
|
+
const initialState = {loading: false, wasLoaded: false};
|
16
|
+
|
17
|
+
const tenantReducer: Reducer<TenantState, TenantAction> = (state = initialState, action) => {
|
11
18
|
switch (action.type) {
|
12
19
|
case FETCH_TENANT.REQUEST: {
|
13
20
|
return {
|
@@ -17,12 +24,9 @@ const tenantReducer = (state = {loading: false, wasLoaded: false, tenant: {}}, a
|
|
17
24
|
}
|
18
25
|
|
19
26
|
case FETCH_TENANT.SUCCESS: {
|
20
|
-
const {tenant, tenantNodes} = action.data;
|
21
|
-
|
22
27
|
return {
|
23
28
|
...state,
|
24
|
-
tenant,
|
25
|
-
tenantNodes,
|
29
|
+
tenant: action.data,
|
26
30
|
loading: false,
|
27
31
|
wasLoaded: true,
|
28
32
|
error: undefined,
|
@@ -32,7 +36,7 @@ const tenantReducer = (state = {loading: false, wasLoaded: false, tenant: {}}, a
|
|
32
36
|
case FETCH_TENANT.FAILURE: {
|
33
37
|
return {
|
34
38
|
...state,
|
35
|
-
|
39
|
+
error: action.error,
|
36
40
|
loading: false,
|
37
41
|
wasLoaded: true,
|
38
42
|
};
|
@@ -41,7 +45,7 @@ const tenantReducer = (state = {loading: false, wasLoaded: false, tenant: {}}, a
|
|
41
45
|
case CLEAR_TENANT: {
|
42
46
|
return {
|
43
47
|
...state,
|
44
|
-
tenant:
|
48
|
+
tenant: undefined,
|
45
49
|
loading: true,
|
46
50
|
};
|
47
51
|
}
|
@@ -65,45 +69,32 @@ const tenantReducer = (state = {loading: false, wasLoaded: false, tenant: {}}, a
|
|
65
69
|
}
|
66
70
|
};
|
67
71
|
|
68
|
-
export const
|
69
|
-
return {type: CLEAR_TENANT};
|
70
|
-
};
|
71
|
-
|
72
|
-
export const getTenantInfo = ({path}) => {
|
72
|
+
export const getTenantInfo = ({path}: {path: string}) => {
|
73
73
|
return createApiRequest({
|
74
|
-
request:
|
74
|
+
request: window.api.getTenantInfo({path}),
|
75
75
|
actions: FETCH_TENANT,
|
76
|
-
dataHandler: (
|
77
|
-
|
78
|
-
const tenantNodes = _.map(nodesData?.Tenants[0]?.Nodes, (item) => {
|
79
|
-
if (item.Host && item.Endpoints) {
|
80
|
-
const address = _.find(item.Endpoints, {Name: 'http-mon'})?.Address;
|
81
|
-
return {
|
82
|
-
id: item.NodeId,
|
83
|
-
backend: `${item.Host}${address ? address : ''}`,
|
84
|
-
};
|
85
|
-
}
|
86
|
-
|
87
|
-
return undefined;
|
88
|
-
}).filter(Boolean);
|
89
|
-
|
90
|
-
return {tenant, tenantNodes};
|
76
|
+
dataHandler: (tenantData): TTenant | undefined => {
|
77
|
+
return tenantData.TenantInfo?.[0];
|
91
78
|
},
|
92
79
|
});
|
93
80
|
};
|
94
81
|
|
95
|
-
export
|
82
|
+
export const clearTenant = () => {
|
83
|
+
return {type: CLEAR_TENANT} as const;
|
84
|
+
};
|
85
|
+
|
86
|
+
export function setTopLevelTab(tab: TenantGeneralTab) {
|
96
87
|
return {
|
97
88
|
type: SET_TOP_LEVEL_TAB,
|
98
89
|
data: tab,
|
99
|
-
};
|
90
|
+
} as const;
|
100
91
|
}
|
101
92
|
|
102
|
-
export function setDiagnosticsTab(tab) {
|
93
|
+
export function setDiagnosticsTab(tab: TenantDiagnosticsTab) {
|
103
94
|
return {
|
104
95
|
type: SET_DIAGNOSTICS_TAB,
|
105
96
|
data: tab,
|
106
|
-
};
|
97
|
+
} as const;
|
107
98
|
}
|
108
99
|
|
109
100
|
export default tenantReducer;
|
@@ -0,0 +1,25 @@
|
|
1
|
+
import type {IResponseError} from '../../../types/api/error';
|
2
|
+
import type {TTenant} from '../../../types/api/tenant';
|
3
|
+
import type {ValueOf} from '../../../types/common';
|
4
|
+
import type {ApiRequestAction} from '../../utils';
|
5
|
+
|
6
|
+
import {TENANT_DIAGNOSTICS_TABS_IDS, TENANT_GENERAL_TABS_IDS} from './constants';
|
7
|
+
import {FETCH_TENANT, clearTenant, setDiagnosticsTab, setTopLevelTab} from './tenant';
|
8
|
+
|
9
|
+
export type TenantGeneralTab = ValueOf<typeof TENANT_GENERAL_TABS_IDS>;
|
10
|
+
export type TenantDiagnosticsTab = ValueOf<typeof TENANT_DIAGNOSTICS_TABS_IDS>;
|
11
|
+
|
12
|
+
export interface TenantState {
|
13
|
+
loading: boolean;
|
14
|
+
wasLoaded: boolean;
|
15
|
+
topLevelTab?: TenantGeneralTab;
|
16
|
+
diagnosticsTab?: TenantDiagnosticsTab;
|
17
|
+
tenant?: TTenant;
|
18
|
+
error?: IResponseError;
|
19
|
+
}
|
20
|
+
|
21
|
+
export type TenantAction =
|
22
|
+
| ApiRequestAction<typeof FETCH_TENANT, TTenant | undefined, IResponseError>
|
23
|
+
| ReturnType<typeof clearTenant>
|
24
|
+
| ReturnType<typeof setTopLevelTab>
|
25
|
+
| ReturnType<typeof setDiagnosticsTab>;
|
package/dist/types/api/acl.ts
CHANGED
package/dist/utils/index.js
CHANGED
@@ -97,7 +97,8 @@ export const formatDateTime = (value) => {
|
|
97
97
|
|
98
98
|
export const calcUptimeInSeconds = (milliseconds) => {
|
99
99
|
const currentDate = new Date();
|
100
|
-
|
100
|
+
const diff = currentDate - Number(milliseconds);
|
101
|
+
return diff <= 0 ? 0 : diff / 1000;
|
101
102
|
};
|
102
103
|
|
103
104
|
export const calcUptime = (milliseconds) => {
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "ydb-embedded-ui",
|
3
|
-
"version": "4.5.
|
3
|
+
"version": "4.5.2",
|
4
4
|
"files": [
|
5
5
|
"dist"
|
6
6
|
],
|
@@ -48,6 +48,7 @@
|
|
48
48
|
"//build:embedded": "echo 'PUBLIC_URL is a setting for create-react-app. Embedded version is built and hosted as is on ydb servers, with no way of knowing the final URL pattern. PUBLIC_URL=. keeps paths to all static relative, allowing servers to handle them as needed'",
|
49
49
|
"build:embedded": "rm -rf build && PUBLIC_URL=. REACT_APP_BACKEND=http://localhost:8765 npm run build",
|
50
50
|
"lint:styles": "stylelint 'src/**/*.scss'",
|
51
|
+
"unimported": "npx unimported --no-cache",
|
51
52
|
"package": "rm -rf dist && copyfiles -u 1 'src/**/*' dist",
|
52
53
|
"test": "react-app-rewired test",
|
53
54
|
"eject": "react-scripts eject",
|
@@ -1,25 +0,0 @@
|
|
1
|
-
import React from 'react';
|
2
|
-
import PropTypes from 'prop-types';
|
3
|
-
import cn from 'bem-cn-lite';
|
4
|
-
import {Breadcrumbs as BreadcrumbsUiKit} from '@gravity-ui/uikit';
|
5
|
-
|
6
|
-
import './Breadcrumbs.scss';
|
7
|
-
|
8
|
-
const b = cn('kv-breadcrumbs');
|
9
|
-
|
10
|
-
class Breadcrumbs extends React.Component {
|
11
|
-
static propTypes = {
|
12
|
-
items: PropTypes.array,
|
13
|
-
};
|
14
|
-
|
15
|
-
static defaultProps = {
|
16
|
-
items: [],
|
17
|
-
};
|
18
|
-
|
19
|
-
render() {
|
20
|
-
const {items} = this.props;
|
21
|
-
return <BreadcrumbsUiKit items={items} firstDisplayedItemsCount={1} className={b()} />;
|
22
|
-
}
|
23
|
-
}
|
24
|
-
|
25
|
-
export default Breadcrumbs;
|
@@ -1,84 +0,0 @@
|
|
1
|
-
import {useState, useEffect, useRef} from 'react';
|
2
|
-
import block from 'bem-cn-lite';
|
3
|
-
import {ArrowToggle, Button} from '@gravity-ui/uikit';
|
4
|
-
|
5
|
-
import './Collapse.scss';
|
6
|
-
|
7
|
-
const b = block('yc-collapse');
|
8
|
-
|
9
|
-
function useEffectSkipFirst(fn, arr) {
|
10
|
-
const isFirst = useRef(true);
|
11
|
-
|
12
|
-
useEffect(() => {
|
13
|
-
if (isFirst.current) {
|
14
|
-
isFirst.current = false;
|
15
|
-
return;
|
16
|
-
}
|
17
|
-
|
18
|
-
return fn();
|
19
|
-
}, arr);
|
20
|
-
}
|
21
|
-
|
22
|
-
export const Collapse = ({
|
23
|
-
title,
|
24
|
-
children,
|
25
|
-
arrowView = 'icon',
|
26
|
-
emptyState = 'No data',
|
27
|
-
titleSize = 'l',
|
28
|
-
contentMarginTop = 12,
|
29
|
-
defaultIsExpand,
|
30
|
-
onChange,
|
31
|
-
}) => {
|
32
|
-
const [isExpand, setIsExpand] = useState(defaultIsExpand);
|
33
|
-
|
34
|
-
const arrowDirection = isExpand ? 'top' : 'bottom';
|
35
|
-
|
36
|
-
const arrowComponent =
|
37
|
-
arrowView === 'button' ? (
|
38
|
-
<Button view="flat" className={b('arrow-button')}>
|
39
|
-
<ArrowToggle className={b('arrow')} direction={arrowDirection} size={20} />
|
40
|
-
</Button>
|
41
|
-
) : (
|
42
|
-
<ArrowToggle className={b('arrow')} direction={arrowDirection} size={20} />
|
43
|
-
);
|
44
|
-
|
45
|
-
useEffectSkipFirst(onChange, [isExpand]);
|
46
|
-
|
47
|
-
return (
|
48
|
-
<div className={b()}>
|
49
|
-
<div
|
50
|
-
className={b('panel', {
|
51
|
-
'no-data': !children,
|
52
|
-
})}
|
53
|
-
onClick={() => {
|
54
|
-
setIsExpand(!isExpand);
|
55
|
-
}}
|
56
|
-
>
|
57
|
-
{typeof title === 'string' ? (
|
58
|
-
<h2
|
59
|
-
className={b('title', {
|
60
|
-
size: titleSize,
|
61
|
-
})}
|
62
|
-
>
|
63
|
-
{title}
|
64
|
-
</h2>
|
65
|
-
) : (
|
66
|
-
title
|
67
|
-
)}
|
68
|
-
|
69
|
-
{children && <div className={b('arrow-wrapper')}>{arrowComponent}</div>}
|
70
|
-
</div>
|
71
|
-
|
72
|
-
{!children && <h4 className={b('empty-state-title')}>{emptyState}</h4>}
|
73
|
-
|
74
|
-
{children && (
|
75
|
-
<div
|
76
|
-
className={b('content', {visible: isExpand})}
|
77
|
-
style={{marginTop: contentMarginTop}}
|
78
|
-
>
|
79
|
-
{children}
|
80
|
-
</div>
|
81
|
-
)}
|
82
|
-
</div>
|
83
|
-
);
|
84
|
-
};
|
@@ -1,70 +0,0 @@
|
|
1
|
-
.yc-collapse {
|
2
|
-
&__panel {
|
3
|
-
display: inline-flex;
|
4
|
-
align-items: center;
|
5
|
-
|
6
|
-
cursor: pointer;
|
7
|
-
|
8
|
-
&_no-data {
|
9
|
-
cursor: default;
|
10
|
-
}
|
11
|
-
}
|
12
|
-
|
13
|
-
&__title {
|
14
|
-
margin: 0;
|
15
|
-
}
|
16
|
-
|
17
|
-
&__title_secondary {
|
18
|
-
color: var(--yc-color-text-secondary);
|
19
|
-
}
|
20
|
-
|
21
|
-
&__title_size {
|
22
|
-
&_s {
|
23
|
-
font-size: var(--yc-text-body-1-font-size);
|
24
|
-
line-height: var(--yc-text-body-1-line-height);
|
25
|
-
}
|
26
|
-
|
27
|
-
&_m {
|
28
|
-
font-size: var(--yc-text-body-2-font-size);
|
29
|
-
line-height: var(--yc-text-body-2-line-height);
|
30
|
-
}
|
31
|
-
|
32
|
-
&_l {
|
33
|
-
font-size: var(--yc-text-body-3-font-size);
|
34
|
-
line-height: var(--yc-text-body-3-line-height);
|
35
|
-
}
|
36
|
-
}
|
37
|
-
|
38
|
-
&__arrow-wrapper {
|
39
|
-
margin-left: 8px;
|
40
|
-
}
|
41
|
-
|
42
|
-
&__arrow-wrapper_secondary {
|
43
|
-
color: var(--yc-color-text-secondary);
|
44
|
-
.button2__text {
|
45
|
-
color: var(--yc-color-text-secondary);
|
46
|
-
}
|
47
|
-
}
|
48
|
-
|
49
|
-
&__content {
|
50
|
-
display: none;
|
51
|
-
|
52
|
-
&_visible {
|
53
|
-
display: block;
|
54
|
-
}
|
55
|
-
}
|
56
|
-
|
57
|
-
&__empty-state-title {
|
58
|
-
font-size: var(--yc-text-body-1-font-size);
|
59
|
-
font-weight: normal;
|
60
|
-
line-height: var(--yc-text-body-1-line-height);
|
61
|
-
|
62
|
-
opacity: 0.5;
|
63
|
-
}
|
64
|
-
|
65
|
-
&__arrow-button {
|
66
|
-
.button2__text {
|
67
|
-
margin: 0 4px;
|
68
|
-
}
|
69
|
-
}
|
70
|
-
}
|
@@ -1,54 +0,0 @@
|
|
1
|
-
import React from 'react';
|
2
|
-
import cn from 'bem-cn-lite';
|
3
|
-
import './PoolUsage.scss';
|
4
|
-
import PropTypes from 'prop-types';
|
5
|
-
const b = cn('pool-usage');
|
6
|
-
|
7
|
-
const formatUsage = (usage) => (typeof usage === 'undefined' ? '' : Math.floor(usage * 100));
|
8
|
-
const getLineType = (fillWidth) => {
|
9
|
-
let fillColor = 'green';
|
10
|
-
if (fillWidth > 60 && fillWidth <= 80) {
|
11
|
-
fillColor = 'yellow';
|
12
|
-
} else if (fillWidth > 80) {
|
13
|
-
fillColor = 'red';
|
14
|
-
}
|
15
|
-
|
16
|
-
return fillColor;
|
17
|
-
};
|
18
|
-
export class PoolUsage extends React.Component {
|
19
|
-
static propTypes = {
|
20
|
-
data: PropTypes.object.isRequired,
|
21
|
-
};
|
22
|
-
|
23
|
-
render() {
|
24
|
-
const {data: pool = {}} = this.props;
|
25
|
-
|
26
|
-
const {Threads, Name = 'Unknown', Usage} = pool;
|
27
|
-
const dataExist = Usage && Threads;
|
28
|
-
|
29
|
-
const value = formatUsage(pool.Usage);
|
30
|
-
const fillWidth = value > 100 ? 100 : value;
|
31
|
-
|
32
|
-
return (
|
33
|
-
<div className={b()}>
|
34
|
-
<div className={b('info')}>
|
35
|
-
<div className={b('pool-name')}>{Name}</div>
|
36
|
-
{dataExist && (
|
37
|
-
<div className={b('value')}>
|
38
|
-
<div className={b('percents')}>{value < 1 ? '<1' : value}%</div>
|
39
|
-
<div className={b('threads')}>(×{Threads})</div>
|
40
|
-
</div>
|
41
|
-
)}
|
42
|
-
</div>
|
43
|
-
<div className={b('visual')}>
|
44
|
-
<div
|
45
|
-
className={b('usage-line', {type: getLineType(fillWidth)})}
|
46
|
-
style={{width: `${fillWidth}%`}}
|
47
|
-
/>
|
48
|
-
</div>
|
49
|
-
</div>
|
50
|
-
);
|
51
|
-
}
|
52
|
-
}
|
53
|
-
|
54
|
-
export default PoolUsage;
|
@@ -1,141 +0,0 @@
|
|
1
|
-
import {createRequestActionTypes, createApiRequest} from '../utils';
|
2
|
-
import '../../services/api';
|
3
|
-
import {stringifyVdiskId} from '../../utils';
|
4
|
-
import {createSelector} from 'reselect';
|
5
|
-
|
6
|
-
const FETCH_NODE = createRequestActionTypes('node', 'FETCH_NODE');
|
7
|
-
const FETCH_NODE_STRUCTURE = createRequestActionTypes('node', 'FETCH_NODE_STRUCTURE');
|
8
|
-
const RESET_NODE = 'node/RESET_NODE';
|
9
|
-
|
10
|
-
const node = (
|
11
|
-
state = {
|
12
|
-
data: {},
|
13
|
-
loading: true,
|
14
|
-
wasLoaded: false,
|
15
|
-
nodeStructure: {},
|
16
|
-
loadingStructure: true,
|
17
|
-
wasLoadedStructure: false,
|
18
|
-
},
|
19
|
-
action,
|
20
|
-
) => {
|
21
|
-
switch (action.type) {
|
22
|
-
case FETCH_NODE.REQUEST: {
|
23
|
-
return {
|
24
|
-
...state,
|
25
|
-
loading: true,
|
26
|
-
};
|
27
|
-
}
|
28
|
-
case FETCH_NODE.SUCCESS: {
|
29
|
-
return {
|
30
|
-
...state,
|
31
|
-
data: action.data,
|
32
|
-
loading: false,
|
33
|
-
wasLoaded: true,
|
34
|
-
error: undefined,
|
35
|
-
};
|
36
|
-
}
|
37
|
-
case FETCH_NODE.FAILURE: {
|
38
|
-
return {
|
39
|
-
...state,
|
40
|
-
error: action.error,
|
41
|
-
loading: false,
|
42
|
-
};
|
43
|
-
}
|
44
|
-
case FETCH_NODE_STRUCTURE.REQUEST: {
|
45
|
-
return {
|
46
|
-
...state,
|
47
|
-
loadingStructure: true,
|
48
|
-
};
|
49
|
-
}
|
50
|
-
case FETCH_NODE_STRUCTURE.SUCCESS: {
|
51
|
-
return {
|
52
|
-
...state,
|
53
|
-
nodeStructure: action.data,
|
54
|
-
loadingStructure: false,
|
55
|
-
wasLoadedStructure: true,
|
56
|
-
errorStructure: undefined,
|
57
|
-
};
|
58
|
-
}
|
59
|
-
case FETCH_NODE_STRUCTURE.FAILURE: {
|
60
|
-
return {
|
61
|
-
...state,
|
62
|
-
errorStructure: action.error,
|
63
|
-
loadingStructure: false,
|
64
|
-
};
|
65
|
-
}
|
66
|
-
case RESET_NODE: {
|
67
|
-
return {
|
68
|
-
...state,
|
69
|
-
data: {},
|
70
|
-
wasLoaded: false,
|
71
|
-
nodeStructure: {},
|
72
|
-
wasLoadedStructure: false,
|
73
|
-
};
|
74
|
-
}
|
75
|
-
default:
|
76
|
-
return state;
|
77
|
-
}
|
78
|
-
};
|
79
|
-
|
80
|
-
export const getNodeInfo = (id) => {
|
81
|
-
return createApiRequest({
|
82
|
-
request: window.api.getNodeInfo(id),
|
83
|
-
actions: FETCH_NODE,
|
84
|
-
});
|
85
|
-
};
|
86
|
-
|
87
|
-
export const getNodeStructure = (nodeId) => {
|
88
|
-
return createApiRequest({
|
89
|
-
request: window.api.getStorageInfo({nodeId}, {concurrentId: 'getNodeStructure'}),
|
90
|
-
actions: FETCH_NODE_STRUCTURE,
|
91
|
-
});
|
92
|
-
};
|
93
|
-
|
94
|
-
export function resetNode() {
|
95
|
-
return {
|
96
|
-
type: RESET_NODE,
|
97
|
-
};
|
98
|
-
}
|
99
|
-
|
100
|
-
const getNodeId = (state) => state.node?.data?.SystemStateInfo?.[0].NodeId;
|
101
|
-
|
102
|
-
const getRawNodeStructure = (state) => state.node?.nodeStructure;
|
103
|
-
|
104
|
-
export const selectNodeStructure = createSelector(
|
105
|
-
[getNodeId, getRawNodeStructure],
|
106
|
-
(nodeId, rawNodeStructure) => {
|
107
|
-
const pools = rawNodeStructure?.StoragePools;
|
108
|
-
const structure = {};
|
109
|
-
pools?.forEach((pool) => {
|
110
|
-
const groups = pool.Groups;
|
111
|
-
groups?.forEach((group) => {
|
112
|
-
const vDisks = group.VDisks?.filter((el) => el.NodeId === nodeId);
|
113
|
-
vDisks?.forEach((vd) => {
|
114
|
-
const vDiskId = stringifyVdiskId(vd.VDiskId);
|
115
|
-
const pDiskId = vd.PDisk?.PDiskId;
|
116
|
-
if (!structure[String(pDiskId)]) {
|
117
|
-
structure[String(pDiskId)] = {vDisks: {}, ...vd.PDisk};
|
118
|
-
}
|
119
|
-
structure[String(pDiskId)].vDisks[vDiskId] = {
|
120
|
-
...vd,
|
121
|
-
// VDisk doesn't have its own StoragePoolName when located inside StoragePool data
|
122
|
-
StoragePoolName: pool.Name,
|
123
|
-
};
|
124
|
-
});
|
125
|
-
});
|
126
|
-
});
|
127
|
-
|
128
|
-
const structureWithVdisksArray = Object.keys(structure).reduce((acc, el) => {
|
129
|
-
const vDisks = structure[el].vDisks;
|
130
|
-
const vDisksArray = Object.keys(vDisks).reduce((acc, key, index) => {
|
131
|
-
acc.push({...vDisks[key], id: key, order: index});
|
132
|
-
return acc;
|
133
|
-
}, []);
|
134
|
-
acc[el] = {...structure[el], vDisks: vDisksArray};
|
135
|
-
return acc;
|
136
|
-
}, {});
|
137
|
-
return structureWithVdisksArray;
|
138
|
-
},
|
139
|
-
);
|
140
|
-
|
141
|
-
export default node;
|