ydb-embedded-ui 4.5.1 → 4.5.2

Sign up to get free protection for your applications and to get access to all the features.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,12 @@
1
1
  # Changelog
2
2
 
3
+ ## [4.5.2](https://github.com/ydb-platform/ydb-embedded-ui/compare/v4.5.1...v4.5.2) (2023-06-06)
4
+
5
+
6
+ ### Bug Fixes
7
+
8
+ * **Versions:** enable table dynamic render ([#416](https://github.com/ydb-platform/ydb-embedded-ui/issues/416)) ([3c877ea](https://github.com/ydb-platform/ydb-embedded-ui/commit/3c877ea88a0c4036213b38099676f473cf3ac2d6))
9
+
3
10
  ## [4.5.1](https://github.com/ydb-platform/ydb-embedded-ui/compare/v4.5.0...v4.5.1) (2023-06-02)
4
11
 
5
12
 
@@ -4,7 +4,7 @@ import PropTypes from 'prop-types';
4
4
 
5
5
  import InfoViewer from '../InfoViewer/InfoViewer';
6
6
  import ProgressViewer from '../ProgressViewer/ProgressViewer';
7
- import PoolUsage from '../PoolUsage/PoolUsage';
7
+ import {PoolUsage} from '../PoolUsage/PoolUsage';
8
8
 
9
9
  import {LOAD_AVERAGE_TIME_INTERVALS} from '../../utils/constants';
10
10
  import {calcUptime} from '../../utils';
@@ -1,4 +1,4 @@
1
- .pool-usage {
1
+ .ydb-pool-usage {
2
2
  font-size: var(--yc-text-body-2-font-size);
3
3
  line-height: var(--yc-text-body-2-line-height);
4
4
  &__info {
@@ -0,0 +1,50 @@
1
+ import block from 'bem-cn-lite';
2
+
3
+ import type {TPoolStats} from '../../types/api/nodes';
4
+
5
+ import './PoolUsage.scss';
6
+
7
+ const b = block('ydb-pool-usage');
8
+
9
+ const getLineType = (fillWidth: number) => {
10
+ let fillColor = 'green';
11
+ if (fillWidth > 60 && fillWidth <= 80) {
12
+ fillColor = 'yellow';
13
+ } else if (fillWidth > 80) {
14
+ fillColor = 'red';
15
+ }
16
+
17
+ return fillColor;
18
+ };
19
+
20
+ interface PoolUsageProps {
21
+ data?: TPoolStats;
22
+ }
23
+
24
+ export const PoolUsage = ({data: pool = {}}: PoolUsageProps) => {
25
+ const {Threads, Name = 'Unknown', Usage = 0} = pool;
26
+ const dataExist = Usage && Threads;
27
+
28
+ const value = Math.floor(Usage * 100);
29
+ const fillWidth = value > 100 ? 100 : value;
30
+
31
+ return (
32
+ <div className={b()}>
33
+ <div className={b('info')}>
34
+ <div className={b('pool-name')}>{Name}</div>
35
+ {dataExist && (
36
+ <div className={b('value')}>
37
+ <div className={b('percents')}>{value < 1 ? '<1' : value}%</div>
38
+ <div className={b('threads')}>(×{Threads})</div>
39
+ </div>
40
+ )}
41
+ </div>
42
+ <div className={b('visual')}>
43
+ <div
44
+ className={b('usage-line', {type: getLineType(fillWidth)})}
45
+ style={{width: `${fillWidth}%`}}
46
+ />
47
+ </div>
48
+ </div>
49
+ );
50
+ };
@@ -15,7 +15,7 @@ import NodeStructure from './NodeStructure/NodeStructure';
15
15
  import {Loader} from '../../components/Loader';
16
16
  import {BasicNodeViewer} from '../../components/BasicNodeViewer';
17
17
 
18
- import {getNodeInfo, resetNode} from '../../store/reducers/node';
18
+ import {getNodeInfo, resetNode} from '../../store/reducers/node/node';
19
19
  import routes, {CLUSTER_PAGES, createHref} from '../../routes';
20
20
  import {setHeader} from '../../store/reducers/header';
21
21
  import {AutoFetcher} from '../../utils/autofetcher';
@@ -1,16 +1,19 @@
1
1
  import {useEffect, useRef, useMemo} from 'react';
2
- import {useDispatch, useSelector} from 'react-redux';
2
+ import {useDispatch} from 'react-redux';
3
3
  import url from 'url';
4
4
  import _ from 'lodash';
5
5
 
6
6
  import cn from 'bem-cn-lite';
7
7
 
8
- import {PDisk} from './Pdisk';
9
8
  import {Loader} from '../.././../components/Loader';
10
9
 
11
- import {getNodeStructure, selectNodeStructure} from '../../../store/reducers/node';
10
+ import {getNodeStructure} from '../../../store/reducers/node/node';
11
+ import {selectNodeStructure} from '../../../store/reducers/node/selectors';
12
12
 
13
13
  import {AutoFetcher} from '../../../utils/autofetcher';
14
+ import {useTypedSelector} from '../../../utils/hooks';
15
+
16
+ import {PDisk} from './Pdisk';
14
17
 
15
18
  import './NodeStructure.scss';
16
19
 
@@ -32,20 +35,19 @@ interface NodeStructureProps {
32
35
 
33
36
  const autofetcher = new AutoFetcher();
34
37
 
35
- function NodeStructure(props: NodeStructureProps) {
38
+ function NodeStructure({nodeId, className, additionalNodesInfo}: NodeStructureProps) {
36
39
  const dispatch = useDispatch();
37
40
 
38
- const nodeStructure: any = useSelector(selectNodeStructure);
41
+ const nodeStructure = useTypedSelector(selectNodeStructure);
39
42
 
40
- const loadingStructure = useSelector((state: any) => state.node.loadingStructure);
41
- const wasLoadedStructure = useSelector((state: any) => state.node.wasLoadedStructure);
42
- const nodeData = useSelector((state: any) => state.node?.data?.SystemStateInfo?.[0]);
43
+ const {loadingStructure, wasLoadedStructure} = useTypedSelector((state) => state.node);
44
+ const nodeData = useTypedSelector((state) => state.node?.data?.SystemStateInfo?.[0]);
43
45
 
44
46
  const nodeHref = useMemo(() => {
45
- return props.additionalNodesInfo?.getNodeRef
46
- ? props.additionalNodesInfo.getNodeRef(nodeData)
47
+ return additionalNodesInfo?.getNodeRef
48
+ ? additionalNodesInfo.getNodeRef(nodeData)
47
49
  : undefined;
48
- }, [nodeData, props.additionalNodesInfo]);
50
+ }, [nodeData, additionalNodesInfo]);
49
51
 
50
52
  const {pdiskId: pdiskIdFromUrl, vdiskId: vdiskIdFromUrl} = url.parse(
51
53
  window.location.href,
@@ -71,16 +73,16 @@ function NodeStructure(props: NodeStructureProps) {
71
73
  }, []);
72
74
 
73
75
  useEffect(() => {
74
- dispatch(getNodeStructure(props.nodeId));
76
+ dispatch(getNodeStructure(nodeId));
75
77
  autofetcher.start();
76
- autofetcher.fetch(() => dispatch(getNodeStructure(props.nodeId)));
78
+ autofetcher.fetch(() => dispatch(getNodeStructure(nodeId)));
77
79
 
78
80
  return () => {
79
81
  scrolled.current = false;
80
82
  isReady.current = false;
81
83
  autofetcher.stop();
82
84
  };
83
- }, [props.nodeId, dispatch]);
85
+ }, [nodeId, dispatch]);
84
86
 
85
87
  useEffect(() => {
86
88
  if (!_.isEmpty(nodeStructure) && scrollContainer) {
@@ -98,9 +100,9 @@ function NodeStructure(props: NodeStructureProps) {
98
100
 
99
101
  if (vdiskIdFromUrl) {
100
102
  const vDisks = nodeStructure[pdiskIdFromUrl as string]?.vDisks;
101
- const vDisk = vDisks?.find((el: any) => el.id === vdiskIdFromUrl);
103
+ const vDisk = vDisks?.find((el) => el.id === vdiskIdFromUrl);
102
104
  const dataTable = vDisk ? document.querySelector('.data-table') : undefined;
103
- const order = vDisk?.order;
105
+ const order = vDisk?.order || 0;
104
106
 
105
107
  if (dataTable) {
106
108
  scrollToVdisk += (dataTable as HTMLElement).offsetTop + 40 * order;
@@ -147,7 +149,7 @@ function NodeStructure(props: NodeStructureProps) {
147
149
 
148
150
  return (
149
151
  <div className={b()} ref={scrollContainerRef}>
150
- <div className={props.className}>{renderContent()}</div>
152
+ <div className={className}>{renderContent()}</div>
151
153
  </div>
152
154
  );
153
155
  }
@@ -12,7 +12,7 @@ import {Icon} from '../../../../components/Icon';
12
12
  import {ProblemFilter} from '../../../../components/ProblemFilter';
13
13
  import {Illustration} from '../../../../components/Illustration';
14
14
 
15
- import {getNetworkInfo, setDataWasNotLoaded} from '../../../../store/reducers/network';
15
+ import {getNetworkInfo, setDataWasNotLoaded} from '../../../../store/reducers/network/network';
16
16
  import {hideTooltip, showTooltip} from '../../../../store/reducers/tooltip';
17
17
  import {changeFilter, ProblemFilterValues} from '../../../../store/reducers/settings/settings';
18
18
  import {AutoFetcher} from '../../../../utils/autofetcher';
@@ -361,7 +361,7 @@ class Network extends React.Component {
361
361
  }
362
362
 
363
363
  const mapStateToProps = (state) => {
364
- const {wasLoaded, loading, data: netWorkInfo} = state.network;
364
+ const {wasLoaded, loading, data: netWorkInfo = {}} = state.network;
365
365
  const {autorefresh} = state.schema;
366
366
  return {
367
367
  netWorkInfo,
@@ -6,7 +6,7 @@ import {Loader} from '@gravity-ui/uikit';
6
6
 
7
7
  import EntityStatus from '../../../../components/EntityStatus/EntityStatus';
8
8
  import InfoViewer from '../../../../components/InfoViewer/InfoViewer';
9
- import PoolUsage from '../../../../components/PoolUsage/PoolUsage';
9
+ import {PoolUsage} from '../../../../components/PoolUsage/PoolUsage';
10
10
  import {Tablet} from '../../../../components/Tablet';
11
11
 
12
12
  import {getTenantInfo} from '../../../../store/reducers/tenant/tenant';
@@ -11,7 +11,7 @@ import {useTypedSelector} from '../../utils/hooks';
11
11
  import routes, {CLUSTER_PAGES, createHref} from '../../routes';
12
12
  import {setHeader} from '../../store/reducers/header';
13
13
  import {disableAutorefresh, getSchema, resetLoadingState} from '../../store/reducers/schema';
14
- import {getSchemaAcl} from '../../store/reducers/schemaAcl';
14
+ import {getSchemaAcl} from '../../store/reducers/schemaAcl/schemaAcl';
15
15
  import {getTenantInfo, clearTenant} from '../../store/reducers/tenant/tenant';
16
16
 
17
17
  import SplitPane from '../../components/SplitPane';
@@ -17,6 +17,7 @@
17
17
  z-index: 0;
18
18
 
19
19
  overflow-x: auto;
20
+ overflow-y: hidden;
20
21
 
21
22
  margin-right: $margin-size;
22
23
  margin-left: $margin-size;
@@ -2,6 +2,7 @@ import DataTable, {Column} from '@gravity-ui/react-data-table';
2
2
 
3
3
  import type {PreparedClusterNode} from '../../../store/reducers/clusterNodes/types';
4
4
  import {isUnavailableNode} from '../../../utils/nodes';
5
+ import {DEFAULT_TABLE_SETTINGS} from '../../../utils/constants';
5
6
  import {formatBytes} from '../../../utils';
6
7
  import {getDefaultNodePath} from '../../Node/NodePages';
7
8
 
@@ -112,9 +113,7 @@ export const NodesTable = ({nodes}: NodesTableProps) => {
112
113
  theme="yandex-cloud"
113
114
  data={nodes}
114
115
  columns={columns}
115
- settings={{
116
- displayIndices: false,
117
- }}
116
+ settings={DEFAULT_TABLE_SETTINGS}
118
117
  />
119
118
  );
120
119
  };
@@ -99,8 +99,8 @@ export class YdbEmbeddedAPI extends AxiosWrapper {
99
99
  filter,
100
100
  nodeId,
101
101
  }: {
102
- tenant: string;
103
- filter: string;
102
+ tenant?: string;
103
+ filter?: string;
104
104
  nodeId: string;
105
105
  },
106
106
  {concurrentId}: AxiosOptions = {},
@@ -5,13 +5,13 @@ import cluster from './cluster/cluster';
5
5
  import clusterNodes from './clusterNodes/clusterNodes';
6
6
  import tenant from './tenant/tenant';
7
7
  import storage from './storage';
8
- import node from './node';
8
+ import node from './node/node';
9
9
  import tooltip from './tooltip';
10
10
  import tablets from './tablets';
11
11
  import heatmap from './heatmap';
12
12
  import schema from './schema';
13
13
  import host from './host';
14
- import network from './network';
14
+ import network from './network/network';
15
15
  import tenants from './tenants/tenants';
16
16
  import tablet from './tablet';
17
17
  import topic from './topic';
@@ -23,7 +23,7 @@ import settings from './settings/settings';
23
23
  import preview from './preview';
24
24
  import nodesList from './nodesList';
25
25
  import describe from './describe';
26
- import schemaAcl from './schemaAcl';
26
+ import schemaAcl from './schemaAcl/schemaAcl';
27
27
  import executeTopQueries from './executeTopQueries';
28
28
  import healthcheckInfo from './healthcheckInfo';
29
29
  import shardsWorkload from './shardsWorkload';
@@ -1,20 +1,23 @@
1
- import {createRequestActionTypes, createApiRequest} from '../utils';
2
- import '../../services/api';
1
+ import {Reducer} from 'redux';
3
2
 
4
- const FETCH_ALL_NODES_NETWORK = createRequestActionTypes(
5
- 'ALL_NODES_NETWORK',
3
+ import '../../../services/api';
4
+ import {createRequestActionTypes, createApiRequest} from '../../utils';
5
+
6
+ import type {NetworkAction, NetworkState} from './types';
7
+
8
+ export const FETCH_ALL_NODES_NETWORK = createRequestActionTypes(
9
+ 'network',
6
10
  'FETCH_ALL_NODES_NETWORK',
7
11
  );
8
12
 
9
13
  const SET_DATA_WAS_NOT_LOADED = 'network/SET_DATA_WAS_NOT_LOADED';
10
14
 
11
15
  const initialState = {
12
- data: {},
13
16
  loading: false,
14
17
  wasLoaded: false,
15
18
  };
16
19
 
17
- const network = (state = initialState, action) => {
20
+ const network: Reducer<NetworkState, NetworkAction> = (state = initialState, action) => {
18
21
  switch (action.type) {
19
22
  case FETCH_ALL_NODES_NETWORK.REQUEST: {
20
23
  return {
@@ -53,10 +56,10 @@ const network = (state = initialState, action) => {
53
56
  export const setDataWasNotLoaded = () => {
54
57
  return {
55
58
  type: SET_DATA_WAS_NOT_LOADED,
56
- };
59
+ } as const;
57
60
  };
58
61
 
59
- export const getNetworkInfo = (tenant) => {
62
+ export const getNetworkInfo = (tenant: string) => {
60
63
  return createApiRequest({
61
64
  request: window.api.getNetwork(tenant),
62
65
  actions: FETCH_ALL_NODES_NETWORK,
@@ -0,0 +1,16 @@
1
+ import type {IResponseError} from '../../../types/api/error';
2
+ import type {TNetInfo} from '../../../types/api/netInfo';
3
+ import type {ApiRequestAction} from '../../utils';
4
+
5
+ import {FETCH_ALL_NODES_NETWORK, setDataWasNotLoaded} from './network';
6
+
7
+ export interface NetworkState {
8
+ loading: boolean;
9
+ wasLoaded: boolean;
10
+ data?: TNetInfo;
11
+ error?: IResponseError;
12
+ }
13
+
14
+ export type NetworkAction =
15
+ | ApiRequestAction<typeof FETCH_ALL_NODES_NETWORK, TNetInfo, IResponseError>
16
+ | ReturnType<typeof setDataWasNotLoaded>;
@@ -0,0 +1,102 @@
1
+ import {Reducer} from 'redux';
2
+
3
+ import '../../../services/api';
4
+ import {createRequestActionTypes, createApiRequest} from '../../utils';
5
+
6
+ import type {NodeAction, NodeState} from './types';
7
+
8
+ export const FETCH_NODE = createRequestActionTypes('node', 'FETCH_NODE');
9
+ export const FETCH_NODE_STRUCTURE = createRequestActionTypes('node', 'FETCH_NODE_STRUCTURE');
10
+
11
+ const RESET_NODE = 'node/RESET_NODE';
12
+
13
+ const initialState = {
14
+ data: {},
15
+ loading: true,
16
+ wasLoaded: false,
17
+ nodeStructure: {},
18
+ loadingStructure: true,
19
+ wasLoadedStructure: false,
20
+ };
21
+
22
+ const node: Reducer<NodeState, NodeAction> = (state = initialState, action) => {
23
+ switch (action.type) {
24
+ case FETCH_NODE.REQUEST: {
25
+ return {
26
+ ...state,
27
+ loading: true,
28
+ };
29
+ }
30
+ case FETCH_NODE.SUCCESS: {
31
+ return {
32
+ ...state,
33
+ data: action.data,
34
+ loading: false,
35
+ wasLoaded: true,
36
+ error: undefined,
37
+ };
38
+ }
39
+ case FETCH_NODE.FAILURE: {
40
+ return {
41
+ ...state,
42
+ error: action.error,
43
+ loading: false,
44
+ };
45
+ }
46
+ case FETCH_NODE_STRUCTURE.REQUEST: {
47
+ return {
48
+ ...state,
49
+ loadingStructure: true,
50
+ };
51
+ }
52
+ case FETCH_NODE_STRUCTURE.SUCCESS: {
53
+ return {
54
+ ...state,
55
+ nodeStructure: action.data,
56
+ loadingStructure: false,
57
+ wasLoadedStructure: true,
58
+ errorStructure: undefined,
59
+ };
60
+ }
61
+ case FETCH_NODE_STRUCTURE.FAILURE: {
62
+ return {
63
+ ...state,
64
+ errorStructure: action.error,
65
+ loadingStructure: false,
66
+ };
67
+ }
68
+ case RESET_NODE: {
69
+ return {
70
+ ...state,
71
+ data: {},
72
+ wasLoaded: false,
73
+ nodeStructure: {},
74
+ wasLoadedStructure: false,
75
+ };
76
+ }
77
+ default:
78
+ return state;
79
+ }
80
+ };
81
+
82
+ export const getNodeInfo = (id: string) => {
83
+ return createApiRequest({
84
+ request: window.api.getNodeInfo(id),
85
+ actions: FETCH_NODE,
86
+ });
87
+ };
88
+
89
+ export const getNodeStructure = (nodeId: string) => {
90
+ return createApiRequest({
91
+ request: window.api.getStorageInfo({nodeId}, {concurrentId: 'getNodeStructure'}),
92
+ actions: FETCH_NODE_STRUCTURE,
93
+ });
94
+ };
95
+
96
+ export function resetNode() {
97
+ return {
98
+ type: RESET_NODE,
99
+ } as const;
100
+ }
101
+
102
+ export default node;
@@ -0,0 +1,59 @@
1
+ import type {Selector} from 'reselect';
2
+ import {createSelector} from 'reselect';
3
+
4
+ import {stringifyVdiskId} from '../../../utils';
5
+
6
+ import type {
7
+ NodeStateSlice,
8
+ PreparedNodeStructure,
9
+ PreparedStructureVDisk,
10
+ RawNodeStructure,
11
+ } from './types';
12
+
13
+ const selectNodeId = (state: NodeStateSlice) => state.node?.data?.SystemStateInfo?.[0].NodeId;
14
+
15
+ const selectRawNodeStructure = (state: NodeStateSlice) => state.node?.nodeStructure;
16
+
17
+ export const selectNodeStructure: Selector<NodeStateSlice, PreparedNodeStructure> = createSelector(
18
+ [selectNodeId, selectRawNodeStructure],
19
+ (nodeId, storageInfo) => {
20
+ const pools = storageInfo?.StoragePools;
21
+ const structure: RawNodeStructure = {};
22
+
23
+ pools?.forEach((pool) => {
24
+ const groups = pool.Groups;
25
+ groups?.forEach((group) => {
26
+ const vDisks = group.VDisks?.filter((el) => el.NodeId === nodeId);
27
+ vDisks?.forEach((vd) => {
28
+ const vDiskId = stringifyVdiskId(vd.VDiskId);
29
+ const pDiskId = vd.PDisk?.PDiskId;
30
+ if (!structure[String(pDiskId)]) {
31
+ structure[String(pDiskId)] = {vDisks: {}, ...vd.PDisk};
32
+ }
33
+ structure[String(pDiskId)].vDisks[vDiskId] = {
34
+ ...vd,
35
+ // VDisk doesn't have its own StoragePoolName when located inside StoragePool data
36
+ StoragePoolName: pool.Name,
37
+ };
38
+ });
39
+ });
40
+ });
41
+
42
+ const structureWithVdisksArray = Object.keys(structure).reduce<PreparedNodeStructure>(
43
+ (preparedStructure, el) => {
44
+ const vDisks = structure[el].vDisks;
45
+ const vDisksArray = Object.keys(vDisks).reduce<PreparedStructureVDisk[]>(
46
+ (acc, key, index) => {
47
+ acc.push({...vDisks[key], id: key, order: index});
48
+ return acc;
49
+ },
50
+ [],
51
+ );
52
+ preparedStructure[el] = {...structure[el], vDisks: vDisksArray};
53
+ return preparedStructure;
54
+ },
55
+ {},
56
+ );
57
+ return structureWithVdisksArray;
58
+ },
59
+ );
@@ -0,0 +1,44 @@
1
+ import type {IResponseError} from '../../../types/api/error';
2
+ import type {TPDiskStateInfo} from '../../../types/api/pdisk';
3
+ import type {TStorageInfo} from '../../../types/api/storage';
4
+ import type {TEvSystemStateResponse} from '../../../types/api/systemState';
5
+ import type {TVDiskStateInfo} from '../../../types/api/vdisk';
6
+ import type {ApiRequestAction} from '../../utils';
7
+
8
+ import {FETCH_NODE, FETCH_NODE_STRUCTURE, resetNode} from './node';
9
+
10
+ interface RawStructurePDisk extends TPDiskStateInfo {
11
+ vDisks: Record<string, TVDiskStateInfo>;
12
+ }
13
+
14
+ export type RawNodeStructure = Record<string, RawStructurePDisk>;
15
+
16
+ export interface PreparedStructureVDisk extends TVDiskStateInfo {
17
+ id: string;
18
+ order: number;
19
+ }
20
+
21
+ export interface PreparedStructurePDisk extends TPDiskStateInfo {
22
+ vDisks: PreparedStructureVDisk[];
23
+ }
24
+
25
+ export type PreparedNodeStructure = Record<string, PreparedStructurePDisk>;
26
+
27
+ export interface NodeState {
28
+ data: TEvSystemStateResponse;
29
+ loading: boolean;
30
+ wasLoaded: boolean;
31
+
32
+ nodeStructure: TStorageInfo;
33
+ loadingStructure: boolean;
34
+ wasLoadedStructure: boolean;
35
+ }
36
+
37
+ export type NodeAction =
38
+ | ApiRequestAction<typeof FETCH_NODE, TEvSystemStateResponse, IResponseError>
39
+ | ApiRequestAction<typeof FETCH_NODE_STRUCTURE, TStorageInfo, IResponseError>
40
+ | ReturnType<typeof resetNode>;
41
+
42
+ export interface NodeStateSlice {
43
+ node: NodeState;
44
+ }
@@ -1,10 +1,18 @@
1
- import {createRequestActionTypes, createApiRequest} from '../utils';
2
- import '../../services/api';
3
- import _ from 'lodash';
1
+ import type {Reducer} from 'redux';
4
2
 
5
- const FETCH_SCHEMA_ACL = createRequestActionTypes('schemaAcl', 'FETCH_SCHEMA_ACL');
3
+ import '../../../services/api';
4
+ import {createRequestActionTypes, createApiRequest} from '../../utils';
6
5
 
7
- const schemaAcl = function z(state = {loading: false, wasLoaded: false, acl: undefined}, action) {
6
+ import type {SchemaAclAction, SchemaAclState} from './types';
7
+
8
+ export const FETCH_SCHEMA_ACL = createRequestActionTypes('schemaAcl', 'FETCH_SCHEMA_ACL');
9
+
10
+ const initialState = {
11
+ loading: false,
12
+ wasLoaded: false,
13
+ };
14
+
15
+ const schemaAcl: Reducer<SchemaAclState, SchemaAclAction> = (state = initialState, action) => {
8
16
  switch (action.type) {
9
17
  case FETCH_SCHEMA_ACL.REQUEST: {
10
18
  return {
@@ -13,13 +21,16 @@ const schemaAcl = function z(state = {loading: false, wasLoaded: false, acl: und
13
21
  };
14
22
  }
15
23
  case FETCH_SCHEMA_ACL.SUCCESS: {
24
+ const acl = action.data.Common?.ACL;
25
+ const owner = action.data.Common?.Owner;
26
+
16
27
  return {
17
28
  ...state,
18
- error: undefined,
19
- acl: _.get(action.data, 'Common.ACL'),
20
- owner: _.get(action.data, 'Common.Owner'),
29
+ acl,
30
+ owner,
21
31
  loading: false,
22
32
  wasLoaded: true,
33
+ error: undefined,
23
34
  };
24
35
  }
25
36
  case FETCH_SCHEMA_ACL.FAILURE: {
@@ -34,7 +45,7 @@ const schemaAcl = function z(state = {loading: false, wasLoaded: false, acl: und
34
45
  }
35
46
  };
36
47
 
37
- export function getSchemaAcl({path}) {
48
+ export function getSchemaAcl({path}: {path: string}) {
38
49
  return createApiRequest({
39
50
  request: window.api.getSchemaAcl({path}),
40
51
  actions: FETCH_SCHEMA_ACL,
@@ -0,0 +1,15 @@
1
+ import type {TACE, TMetaInfo} from '../../../types/api/acl';
2
+ import type {IResponseError} from '../../../types/api/error';
3
+ import type {ApiRequestAction} from '../../utils';
4
+
5
+ import {FETCH_SCHEMA_ACL} from './schemaAcl';
6
+
7
+ export interface SchemaAclState {
8
+ loading: boolean
9
+ wasLoaded: boolean
10
+ acl?: TACE[]
11
+ owner?: string
12
+ error?: IResponseError
13
+ }
14
+
15
+ export type SchemaAclAction = ApiRequestAction<typeof FETCH_SCHEMA_ACL, TMetaInfo, IResponseError>;
@@ -16,7 +16,7 @@ export interface TMetaCommonInfo {
16
16
  ACL?: TACE[];
17
17
  }
18
18
 
19
- interface TACE {
19
+ export interface TACE {
20
20
  AccessType: string;
21
21
  AccessRights?: string[];
22
22
  Subject: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ydb-embedded-ui",
3
- "version": "4.5.1",
3
+ "version": "4.5.2",
4
4
  "files": [
5
5
  "dist"
6
6
  ],
@@ -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;