ydb-embedded-ui 4.5.1 → 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 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;