ydb-embedded-ui 2.4.4 → 2.6.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.
Files changed (77) hide show
  1. package/CHANGELOG.md +42 -0
  2. package/dist/components/InfoViewer/InfoViewer.scss +3 -3
  3. package/dist/components/InfoViewer/formatters/schema.ts +2 -1
  4. package/dist/components/InfoViewer/schemaInfo/CDCStreamInfo.tsx +23 -22
  5. package/dist/components/InfoViewer/schemaInfo/PersQueueGroupInfo.tsx +8 -13
  6. package/dist/components/InfoViewer/schemaInfo/TableIndexInfo.tsx +2 -12
  7. package/dist/components/InfoViewer/schemaOverview/CDCStreamOverview.tsx +3 -16
  8. package/dist/components/InfoViewer/schemaOverview/PersQueueGroupOverview.tsx +8 -13
  9. package/dist/components/InfoViewer/utils.ts +6 -6
  10. package/dist/components/Loader/Loader.scss +6 -3
  11. package/dist/components/Loader/Loader.tsx +7 -5
  12. package/dist/components/Loader/index.ts +1 -0
  13. package/dist/components/UptimeFIlter/UptimeFilter.tsx +21 -0
  14. package/dist/components/UptimeFIlter/index.ts +1 -0
  15. package/dist/containers/Node/Node.scss +1 -0
  16. package/dist/containers/Node/Node.tsx +3 -8
  17. package/dist/containers/Node/NodeStructure/NodeStructure.scss +0 -6
  18. package/dist/containers/Node/NodeStructure/NodeStructure.tsx +1 -1
  19. package/dist/containers/Nodes/Nodes.js +22 -10
  20. package/dist/{components → containers}/NodesViewer/NodesViewer.js +49 -62
  21. package/dist/{components → containers}/NodesViewer/NodesViewer.scss +0 -0
  22. package/dist/containers/Storage/Pdisk/Pdisk.scss +1 -1
  23. package/dist/containers/Storage/Pdisk/Pdisk.tsx +3 -9
  24. package/dist/containers/Storage/Pdisk/__tests__/colors.tsx +1 -1
  25. package/dist/containers/Storage/Storage.js +46 -11
  26. package/dist/containers/Storage/StorageGroups/StorageGroups.tsx +39 -32
  27. package/dist/containers/Storage/StorageNodes/StorageNodes.scss +2 -2
  28. package/dist/containers/Storage/StorageNodes/StorageNodes.tsx +35 -17
  29. package/dist/containers/Storage/StorageNodes/i18n/en.json +6 -4
  30. package/dist/containers/Storage/StorageNodes/i18n/ru.json +6 -4
  31. package/dist/containers/Storage/UsageFilter/UsageFilter.scss +10 -5
  32. package/dist/containers/Tenant/Acl/Acl.js +1 -7
  33. package/dist/containers/Tenant/Diagnostics/Compute/Compute.js +22 -14
  34. package/dist/containers/Tenant/Diagnostics/Consumers/Consumers.tsx +52 -10
  35. package/dist/containers/Tenant/Diagnostics/Describe/Describe.scss +0 -8
  36. package/dist/containers/Tenant/Diagnostics/Describe/Describe.tsx +42 -15
  37. package/dist/containers/Tenant/Diagnostics/Diagnostics.scss +0 -7
  38. package/dist/containers/Tenant/Diagnostics/Diagnostics.tsx +19 -15
  39. package/dist/containers/Tenant/Diagnostics/DiagnosticsPages.ts +1 -1
  40. package/dist/containers/Tenant/Diagnostics/Healthcheck/Healthcheck.tsx +13 -5
  41. package/dist/containers/Tenant/Diagnostics/Network/Network.js +17 -4
  42. package/dist/containers/Tenant/Diagnostics/Overview/Overview.tsx +50 -16
  43. package/dist/containers/Tenant/Diagnostics/TenantOverview/TenantOverview.js +16 -2
  44. package/dist/containers/Tenant/Diagnostics/TopQueries/TopQueries.js +1 -0
  45. package/dist/containers/Tenant/Diagnostics/TopShards/TopShards.js +21 -13
  46. package/dist/containers/Tenant/ObjectGeneral/ObjectGeneral.tsx +1 -5
  47. package/dist/containers/Tenant/QueryEditor/QueryExplain/QueryExplain.js +4 -4
  48. package/dist/containers/Tenant/QueryEditor/SaveQuery/SaveQuery.js +1 -4
  49. package/dist/containers/Tenant/Schema/SchemaInfoViewer/SchemaInfoViewer.js +23 -28
  50. package/dist/containers/Tenant/Schema/SchemaTree/SchemaTree.tsx +2 -2
  51. package/dist/containers/Tenant/TenantPages.tsx +1 -1
  52. package/dist/containers/Tenant/utils/schema.ts +84 -0
  53. package/dist/containers/Tenant/utils/schemaActions.ts +9 -20
  54. package/dist/services/api.d.ts +17 -11
  55. package/dist/store/reducers/clusterNodes.js +29 -10
  56. package/dist/store/reducers/describe.ts +56 -14
  57. package/dist/store/reducers/healthcheckInfo.ts +23 -8
  58. package/dist/store/reducers/network.js +22 -1
  59. package/dist/store/reducers/nodes.js +37 -3
  60. package/dist/store/reducers/schema.ts +229 -0
  61. package/dist/store/reducers/storage.js +59 -5
  62. package/dist/types/api/enums.ts +10 -0
  63. package/dist/types/api/nodes.ts +96 -0
  64. package/dist/types/api/pdisk.ts +48 -0
  65. package/dist/types/api/schema.ts +148 -9
  66. package/dist/types/api/storage.ts +3 -173
  67. package/dist/types/api/tablet.ts +97 -0
  68. package/dist/types/api/vdisk.ts +120 -0
  69. package/dist/types/store/describe.ts +8 -2
  70. package/dist/types/store/healthcheck.ts +12 -0
  71. package/dist/types/store/schema.ts +52 -0
  72. package/dist/utils/index.js +6 -2
  73. package/dist/utils/nodes.ts +9 -0
  74. package/dist/utils/pdisk.ts +1 -1
  75. package/dist/utils/storage.ts +1 -1
  76. package/package.json +1 -1
  77. package/dist/store/reducers/schema.js +0 -148
@@ -1,5 +1,5 @@
1
- import {ReactNode, useMemo} from 'react';
2
- import {useDispatch, useSelector} from 'react-redux';
1
+ import {ReactNode, useCallback, useMemo} from 'react';
2
+ import {shallowEqual, useDispatch, useSelector} from 'react-redux';
3
3
  import cn from 'bem-cn-lite';
4
4
 
5
5
  import {Loader} from '@gravity-ui/uikit';
@@ -13,15 +13,24 @@ import {
13
13
  } from '../../../../components/InfoViewer/schemaInfo';
14
14
 
15
15
  import {EPathType, TColumnTableDescription} from '../../../../types/api/schema';
16
- import {isColumnEntityType, isTableType} from '../../utils/schema';
16
+ import {
17
+ isEntityWithMergedImplementation,
18
+ isColumnEntityType,
19
+ isTableType,
20
+ } from '../../utils/schema';
17
21
  //@ts-ignore
18
- import {getSchema, resetLoadingState} from '../../../../store/reducers/schema';
22
+ import {
23
+ getSchema,
24
+ getSchemaBatched,
25
+ resetLoadingState,
26
+ selectSchemaMergedChildrenPaths,
27
+ } from '../../../../store/reducers/schema';
19
28
  //@ts-ignore
20
29
  import {
21
30
  getOlapStats,
22
31
  resetLoadingState as resetOlapLoadingState,
23
32
  } from '../../../../store/reducers/olapStats';
24
- import {useAutofetcher} from '../../../../utils/hooks';
33
+ import {useAutofetcher, useTypedSelector} from '../../../../utils/hooks';
25
34
 
26
35
  import './Overview.scss';
27
36
 
@@ -60,9 +69,7 @@ interface OverviewProps {
60
69
 
61
70
  const b = cn('kv-tenant-overview');
62
71
 
63
- function Overview(props: OverviewProps) {
64
- const {tenantName, type} = props;
65
-
72
+ function Overview({type, tenantName, className}: OverviewProps) {
66
73
  const dispatch = useDispatch();
67
74
 
68
75
  const {
@@ -78,14 +85,31 @@ function Overview(props: OverviewProps) {
78
85
 
79
86
  const loading = schemaLoading || olapStatsLoading;
80
87
 
81
- useAutofetcher(
82
- (isBackground) => {
88
+ const isEntityWithMergedImpl = isEntityWithMergedImplementation(type);
89
+
90
+ // There is a circular dependency here. Fetch data depends on children paths
91
+ // When data in store updated on fetch request,
92
+ // new object is set there, so source children array is updated
93
+ // This updates selector, the selector returns a new array, and data is fetched again
94
+ // To prevent it, shallowEqual, which compares array content, was added
95
+ const mergedChildrenPaths = useTypedSelector(
96
+ (state) => selectSchemaMergedChildrenPaths(state, currentSchemaPath, type),
97
+ shallowEqual,
98
+ );
99
+
100
+ const fetchData = useCallback(
101
+ (isBackground: boolean) => {
83
102
  if (!isBackground) {
84
103
  dispatch(resetLoadingState());
85
104
  }
86
105
 
87
106
  const schemaPath = currentSchemaPath || tenantName;
88
- dispatch(getSchema({path: schemaPath}));
107
+
108
+ if (!isEntityWithMergedImpl) {
109
+ dispatch(getSchema({path: schemaPath}));
110
+ } else if (mergedChildrenPaths) {
111
+ dispatch(getSchemaBatched([schemaPath, ...mergedChildrenPaths]));
112
+ }
89
113
 
90
114
  if (isTableType(type) && isColumnEntityType(type)) {
91
115
  if (!isBackground) {
@@ -94,10 +118,18 @@ function Overview(props: OverviewProps) {
94
118
  dispatch(getOlapStats({path: schemaPath}));
95
119
  }
96
120
  },
97
- [currentSchemaPath, dispatch, tenantName, type],
98
- autorefresh,
121
+ [
122
+ tenantName,
123
+ currentSchemaPath,
124
+ type,
125
+ isEntityWithMergedImpl,
126
+ mergedChildrenPaths,
127
+ dispatch,
128
+ ],
99
129
  );
100
130
 
131
+ useAutofetcher(fetchData, [fetchData], autorefresh);
132
+
101
133
  const tableSchema =
102
134
  currentItem?.PathDescription?.Table || currentItem?.PathDescription?.ColumnTableDescription;
103
135
 
@@ -128,7 +160,9 @@ function Overview(props: OverviewProps) {
128
160
  [EPathType.EPathTypeExtSubDomain]: undefined,
129
161
  [EPathType.EPathTypeColumnStore]: undefined,
130
162
  [EPathType.EPathTypeColumnTable]: undefined,
131
- [EPathType.EPathTypeCdcStream]: () => <CDCStreamInfo data={schemaData} />,
163
+ [EPathType.EPathTypeCdcStream]: () => (
164
+ <CDCStreamInfo data={schemaData} childrenPaths={mergedChildrenPaths} />
165
+ ),
132
166
  [EPathType.EPathTypePersQueueGroup]: () => <PersQueueGroupInfo data={schemaData} />,
133
167
  };
134
168
 
@@ -139,10 +173,10 @@ function Overview(props: OverviewProps) {
139
173
  );
140
174
  };
141
175
 
142
- return loading && !wasLoaded ? (
176
+ return (loading && !wasLoaded) || (isEntityWithMergedImpl && !mergedChildrenPaths) ? (
143
177
  renderLoader()
144
178
  ) : (
145
- <div className={props.className}>{renderContent()}</div>
179
+ <div className={className}>{renderContent()}</div>
146
180
  );
147
181
  }
148
182
 
@@ -53,6 +53,7 @@ class TenantOverview extends React.Component {
53
53
 
54
54
  componentDidMount() {
55
55
  const {tenantName, autorefresh, getTenantInfo} = this.props;
56
+ getTenantInfo({path: tenantName});
56
57
  this.autofetcher = new AutoFetcher();
57
58
  if (autorefresh) {
58
59
  this.autofetcher.start();
@@ -62,12 +63,25 @@ class TenantOverview extends React.Component {
62
63
 
63
64
  componentDidUpdate(prevProps) {
64
65
  const {autorefresh, tenantName, getTenantInfo} = this.props;
65
- if (autorefresh && !prevProps.autorefresh) {
66
- getTenantInfo({path: tenantName});
66
+
67
+ const restartAutorefresh = () => {
67
68
  this.autofetcher.stop();
68
69
  this.autofetcher.start();
69
70
  this.autofetcher.fetch(() => getTenantInfo({path: tenantName}));
71
+ };
72
+
73
+ if (prevProps.tenantName !== this.props.tenantName) {
74
+ getTenantInfo({path: tenantName});
75
+ if (autorefresh) {
76
+ restartAutorefresh();
77
+ }
70
78
  }
79
+
80
+ if (autorefresh && !prevProps.autorefresh) {
81
+ getTenantInfo({path: tenantName});
82
+ restartAutorefresh();
83
+ }
84
+
71
85
  if (!autorefresh && prevProps.autorefresh) {
72
86
  this.autofetcher.stop();
73
87
  }
@@ -90,6 +90,7 @@ class TopQueries extends React.Component {
90
90
  wasLoaded: false,
91
91
  data: undefined,
92
92
  });
93
+ this.getTopQueries();
93
94
  }
94
95
  }
95
96
 
@@ -47,17 +47,23 @@ function prepareDateSizeValue(value) {
47
47
  }
48
48
 
49
49
  function stringToDataTableSortOrder(value) {
50
- return value && value.split(',').map((columnId) => ({
51
- columnId,
52
- order: DataTable.DESCENDING,
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 value && value.split(',').map((columnId) => ({
58
- columnId,
59
- order: 'DESC',
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(() => sendShardQuery({
89
- database: path,
90
- path: currentSchemaPath,
91
- sortOrder: stringToQuerySortOrder(sortOrder),
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, {useCallback, useEffect, useRef, useState} from '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 = useCallback(() => {
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
- }, [data, opts, shapes, version]);
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, render]);
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) => Object.entries(fields)
48
- .map(([label, value]) => formatTableStatsItem(label, value))
49
- .filter(({value}) => Boolean(value));
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 {TableStats = {}, TabletMetrics = {}, Table: {PartitionConfig = {}} = {}} = PathDescription;
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
- formatPartitionConfigItem('FollowerCount', FollowerCount)
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('CrossDataCenterFollowerCount', CrossDataCenterFollowerCount)
147
+ formatPartitionConfigItem(
148
+ 'CrossDataCenterFollowerCount',
149
+ CrossDataCenterFollowerCount,
150
+ ),
146
151
  );
147
152
  }
148
153
 
149
- if ([
150
- tabletMetricsInfo,
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
  }
@@ -6,7 +6,7 @@ import {NavigationTree} from 'ydb-ui-components';
6
6
  import {setCurrentSchemaPath, preloadSchemas} from '../../../../store/reducers/schema';
7
7
  import type {EPathType, TEvDescribeSchemeResult} from '../../../../types/api/schema';
8
8
 
9
- import {mapPathTypeToNavigationTreeType} from '../../utils/schema';
9
+ import {isChildlessPathType, mapPathTypeToNavigationTreeType} from '../../utils/schema';
10
10
  import {getActions} from '../../utils/schemaActions';
11
11
 
12
12
  interface SchemaTreeProps {
@@ -42,7 +42,7 @@ export function SchemaTree(props: SchemaTreeProps) {
42
42
  type: mapPathTypeToNavigationTreeType(PathType, PathSubType),
43
43
  // FIXME: should only be explicitly set to true for tables with indexes
44
44
  // at the moment of writing there is no property to determine this, fix later
45
- expandable: true,
45
+ expandable: !isChildlessPathType(PathType, PathSubType),
46
46
  };
47
47
  });
48
48
 
@@ -14,7 +14,7 @@ export enum TenantInfoTabsIds {
14
14
  export enum TenantTabsGroups {
15
15
  info = 'info',
16
16
  general = 'general',
17
- generalTab = 'generalTab'
17
+ generalTab = 'generalTab',
18
18
  }
19
19
 
20
20
  export const TENANT_GENERAL_TABS = [
@@ -88,3 +88,87 @@ const pathTypeToIsColumn: Record<EPathType, boolean> = {
88
88
  };
89
89
 
90
90
  export const isColumnEntityType = (type?: EPathType) => (type && pathTypeToIsColumn[type]) ?? false;
91
+
92
+ // ====================
93
+
94
+ const pathTypeToIsDatabase: Record<EPathType, boolean> = {
95
+ [EPathType.EPathTypeSubDomain]: true,
96
+ [EPathType.EPathTypeExtSubDomain]: true,
97
+
98
+ [EPathType.EPathTypeInvalid]: false,
99
+ [EPathType.EPathTypeDir]: false,
100
+ [EPathType.EPathTypeColumnStore]: false,
101
+ [EPathType.EPathTypeColumnTable]: false,
102
+ [EPathType.EPathTypeTable]: false,
103
+ [EPathType.EPathTypeTableIndex]: false,
104
+ [EPathType.EPathTypeCdcStream]: false,
105
+ [EPathType.EPathTypePersQueueGroup]: false,
106
+ };
107
+
108
+ export const isDatabaseEntityType = (type?: EPathType) =>
109
+ (type && pathTypeToIsDatabase[type]) ?? false;
110
+
111
+ // ====================
112
+
113
+ const pathTypeToIsCdcStream: Record<EPathType, boolean> = {
114
+ [EPathType.EPathTypeCdcStream]: true,
115
+
116
+ [EPathType.EPathTypeInvalid]: false,
117
+ [EPathType.EPathTypeColumnStore]: false,
118
+ [EPathType.EPathTypeColumnTable]: false,
119
+ [EPathType.EPathTypeDir]: false,
120
+ [EPathType.EPathTypeTable]: false,
121
+ [EPathType.EPathTypeSubDomain]: false,
122
+ [EPathType.EPathTypeTableIndex]: false,
123
+ [EPathType.EPathTypeExtSubDomain]: false,
124
+ [EPathType.EPathTypePersQueueGroup]: false,
125
+ };
126
+
127
+ export const isCdcStreamEntityType = (type?: EPathType) =>
128
+ (type && pathTypeToIsCdcStream[type]) ?? false;
129
+
130
+ // ====================
131
+
132
+ const pathTypeToEntityWithMergedImplementation: Record<EPathType, boolean> = {
133
+ [EPathType.EPathTypeCdcStream]: true,
134
+
135
+ [EPathType.EPathTypePersQueueGroup]: false,
136
+ [EPathType.EPathTypeInvalid]: false,
137
+ [EPathType.EPathTypeColumnStore]: false,
138
+ [EPathType.EPathTypeColumnTable]: false,
139
+ [EPathType.EPathTypeDir]: false,
140
+ [EPathType.EPathTypeTable]: false,
141
+ [EPathType.EPathTypeSubDomain]: false,
142
+ [EPathType.EPathTypeTableIndex]: false,
143
+ [EPathType.EPathTypeExtSubDomain]: false,
144
+ };
145
+
146
+ export const isEntityWithMergedImplementation = (type?: EPathType) =>
147
+ (type && pathTypeToEntityWithMergedImplementation[type]) ?? false;
148
+
149
+ // ====================
150
+
151
+ const pathSubTypeToChildless: Record<EPathSubType, boolean> = {
152
+ [EPathSubType.EPathSubTypeSyncIndexImplTable]: true,
153
+ [EPathSubType.EPathSubTypeAsyncIndexImplTable]: true,
154
+
155
+ [EPathSubType.EPathSubTypeStreamImpl]: false,
156
+ [EPathSubType.EPathSubTypeEmpty]: false,
157
+ };
158
+
159
+ const pathTypeToChildless: Record<EPathType, boolean> = {
160
+ [EPathType.EPathTypeCdcStream]: true,
161
+ [EPathType.EPathTypePersQueueGroup]: true,
162
+
163
+ [EPathType.EPathTypeInvalid]: false,
164
+ [EPathType.EPathTypeColumnStore]: false,
165
+ [EPathType.EPathTypeColumnTable]: false,
166
+ [EPathType.EPathTypeDir]: false,
167
+ [EPathType.EPathTypeTable]: false,
168
+ [EPathType.EPathTypeSubDomain]: false,
169
+ [EPathType.EPathTypeTableIndex]: false,
170
+ [EPathType.EPathTypeExtSubDomain]: false,
171
+ };
172
+
173
+ export const isChildlessPathType = (type?: EPathType, subType?: EPathSubType) =>
174
+ ((subType && pathSubTypeToChildless[subType]) || (type && pathTypeToChildless[type])) ?? false;
@@ -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
- copyItem,
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
 
@@ -8,25 +8,29 @@ interface Window {
8
8
  params: {path: string},
9
9
  axiosOptions?: AxiosOptions,
10
10
  ) => Promise<import('../types/api/schema').TEvDescribeSchemeResult>;
11
+ getDescribe: (
12
+ params: {path: string},
13
+ axiosOptions?: AxiosOptions,
14
+ ) => Promise<import('../types/api/schema').TEvDescribeSchemeResult>;
11
15
  getStorageInfo: (
12
16
  params: {
13
- tenant: string,
14
- filter: string,
15
- nodeId: string,
16
- type: 'Groups' | 'Nodes',
17
+ tenant: string;
18
+ filter: string;
19
+ nodeId: string;
20
+ type: 'Groups' | 'Nodes';
17
21
  },
18
22
  axiosOptions?: AxiosOptions,
19
23
  ) => Promise<import('../types/api/storage').TStorageInfo>;
20
24
  sendQuery: <
21
25
  Action extends import('../types/api/query').Actions,
22
- Schema extends import('../types/api/query').Schemas = undefined
26
+ Schema extends import('../types/api/query').Schemas = undefined,
23
27
  >(
24
28
  params: {
25
- query?: string,
26
- database?: string,
27
- action?: Action,
28
- stats?: string,
29
- schema?: Schema,
29
+ query?: string;
30
+ database?: string;
31
+ action?: Action;
32
+ stats?: string;
33
+ schema?: Schema;
30
34
  },
31
35
  axiosOptions?: AxiosOptions,
32
36
  ) => Promise<import('../types/api/query').QueryAPIResponse<Action, Schema>>;
@@ -38,7 +42,9 @@ interface Window {
38
42
  query: string,
39
43
  database: string,
40
44
  ) => Promise<import('../types/api/query').QueryAPIExplainResponse<'explain-ast'>>;
41
- getHealthcheckInfo: (database: string) => Promise<import('../types/api/healthcheck').HealthCheckAPIResponse>,
45
+ getHealthcheckInfo: (
46
+ database: string,
47
+ ) => Promise<import('../types/api/healthcheck').HealthCheckAPIResponse>;
42
48
  [method: string]: Function;
43
49
  };
44
50
  }
@@ -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
- (state) => state.nodes.data?.Tenants,
52
- (state) => state.settings.problemFilter,
53
- (tenants, filter) => {
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?.filter(({Overall}) => {
65
- return Overall && Overall !== 'Green';
66
- });
85
+ return filterNodesByStatusAndUptime(nodes, problemFilter, uptimeFilter);
67
86
  },
68
87
  );
69
88