ydb-embedded-ui 4.2.0 → 4.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +19 -0
- package/dist/containers/Tenant/Diagnostics/Diagnostics.tsx +2 -2
- package/dist/containers/Tenant/Diagnostics/Partitions/Partitions.tsx +111 -193
- package/dist/containers/Tenant/Diagnostics/Partitions/PartitionsControls/PartitionsControls.tsx +182 -0
- package/dist/containers/Tenant/Diagnostics/Partitions/columns/Columns.scss +1 -1
- package/dist/containers/Tenant/Diagnostics/Partitions/columns/columns.tsx +15 -6
- package/dist/containers/Tenant/Diagnostics/Partitions/utils/constants.ts +13 -1
- package/dist/containers/Tenant/Diagnostics/Partitions/utils/index.ts +26 -0
- package/dist/containers/Tenant/Diagnostics/Partitions/utils/types.ts +2 -2
- package/dist/containers/Tenant/Diagnostics/Partitions/utils/useGetPartitionsColumns.ts +27 -0
- package/dist/services/api.ts +1 -1
- package/dist/store/reducers/index.ts +2 -2
- package/dist/store/reducers/partitions/partitions.ts +104 -0
- package/dist/store/reducers/partitions/types.ts +47 -0
- package/dist/store/reducers/partitions/utils.ts +99 -0
- package/dist/store/reducers/settings.js +2 -2
- package/dist/store/reducers/topic.ts +7 -0
- package/dist/store/state-url-mapping.js +1 -1
- package/dist/types/api/topic.ts +1 -1
- package/dist/utils/constants.ts +1 -1
- package/dist/utils/createToast.tsx +1 -1
- package/dist/utils/hooks/index.ts +1 -0
- package/dist/utils/hooks/useSetting.ts +23 -0
- package/dist/utils/tablet.ts +14 -13
- package/package.json +1 -1
- package/dist/containers/Tenant/Diagnostics/Partitions/PartitionsWrapper.tsx +0 -81
- package/dist/containers/Tenant/Diagnostics/Partitions/index.ts +0 -1
- package/dist/store/reducers/consumer.ts +0 -174
- package/dist/types/store/consumer.ts +0 -63
package/CHANGELOG.md
CHANGED
@@ -1,5 +1,24 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
## [4.3.0](https://github.com/ydb-platform/ydb-embedded-ui/compare/v4.2.1...v4.3.0) (2023-05-18)
|
4
|
+
|
5
|
+
|
6
|
+
### Features
|
7
|
+
|
8
|
+
* **Partitions:** display partitions for topic without consumers ([0843a49](https://github.com/ydb-platform/ydb-embedded-ui/commit/0843a49c46cb6765b376832a847c3ac0ce8b6b85))
|
9
|
+
|
10
|
+
|
11
|
+
### Bug Fixes
|
12
|
+
|
13
|
+
* **Tablet:** update state to color mapping ([7ccc8c7](https://github.com/ydb-platform/ydb-embedded-ui/commit/7ccc8c79225cd311a7a3674150335b58a94f293e))
|
14
|
+
|
15
|
+
## [4.2.1](https://github.com/ydb-platform/ydb-embedded-ui/compare/v4.2.0...v4.2.1) (2023-05-18)
|
16
|
+
|
17
|
+
|
18
|
+
### Bug Fixes
|
19
|
+
|
20
|
+
* export toaster ([b5d12c0](https://github.com/ydb-platform/ydb-embedded-ui/commit/b5d12c0aa39ea3877a9b74071e3124f89a309ca3))
|
21
|
+
|
3
22
|
## [4.2.0](https://github.com/ydb-platform/ydb-embedded-ui/compare/v4.1.0...v4.2.0) (2023-05-16)
|
4
23
|
|
5
24
|
|
@@ -27,7 +27,7 @@ import {Nodes} from '../../Nodes';
|
|
27
27
|
//@ts-ignore
|
28
28
|
import {Tablets} from '../../Tablets';
|
29
29
|
import {Consumers} from './Consumers';
|
30
|
-
import {
|
30
|
+
import {Partitions} from './Partitions/Partitions';
|
31
31
|
|
32
32
|
import routes, {createHref} from '../../../routes';
|
33
33
|
import type {EPathType} from '../../../types/api/schema';
|
@@ -159,7 +159,7 @@ function Diagnostics(props: DiagnosticsProps) {
|
|
159
159
|
return <Consumers path={currentSchemaPath} type={type} />;
|
160
160
|
}
|
161
161
|
case GeneralPagesIds.partitions: {
|
162
|
-
return <
|
162
|
+
return <Partitions path={currentSchemaPath} />;
|
163
163
|
}
|
164
164
|
default: {
|
165
165
|
return <div>No data...</div>;
|
@@ -1,39 +1,32 @@
|
|
1
1
|
import block from 'bem-cn-lite';
|
2
2
|
import {useCallback, useEffect, useMemo, useState} from 'react';
|
3
3
|
import {useDispatch} from 'react-redux';
|
4
|
-
import {escapeRegExp} from 'lodash/fp';
|
5
4
|
|
6
5
|
import DataTable from '@gravity-ui/react-data-table';
|
7
|
-
import {Select, TableColumnSetup} from '@gravity-ui/uikit';
|
8
|
-
import {TableColumnSetupItem} from '@gravity-ui/uikit/build/esm/components/Table/hoc/withTableSettings/withTableSettings';
|
9
6
|
|
10
|
-
import
|
7
|
+
import {useAutofetcher, useTypedSelector, useSetting} from '../../../../utils/hooks';
|
8
|
+
import {DEFAULT_TABLE_SETTINGS, PARTITIONS_HIDDEN_COLUMNS_KEY} from '../../../../utils/constants';
|
11
9
|
|
12
|
-
import {
|
13
|
-
import {
|
14
|
-
|
15
|
-
|
10
|
+
import {getNodesList, selectNodesMap} from '../../../../store/reducers/nodesList';
|
11
|
+
import {
|
12
|
+
cleanTopicData,
|
13
|
+
getTopic,
|
14
|
+
selectConsumersNames,
|
15
|
+
setDataWasNotLoaded as setTopicDataWasNotLoaded,
|
16
|
+
} from '../../../../store/reducers/topic';
|
16
17
|
import {
|
17
|
-
|
18
|
-
|
19
|
-
setDataWasNotLoaded,
|
18
|
+
getPartitions,
|
19
|
+
setDataWasNotLoaded as setPartitionsDataWasNotLoaded,
|
20
20
|
setSelectedConsumer,
|
21
|
-
} from '../../../../store/reducers/
|
21
|
+
} from '../../../../store/reducers/partitions/partitions';
|
22
22
|
|
23
23
|
import {TableSkeleton} from '../../../../components/TableSkeleton/TableSkeleton';
|
24
|
-
import {Search} from '../../../../components/Search';
|
25
24
|
import {ResponseError} from '../../../../components/Errors/ResponseError';
|
26
25
|
|
27
|
-
import {
|
28
|
-
|
29
|
-
import
|
30
|
-
import {
|
31
|
-
PARTITIONS_COLUMNS_IDS,
|
32
|
-
PARTITIONS_COLUMNS_TITILES,
|
33
|
-
PARTITIONS_DEFAULT_SELECTED_COLUMNS,
|
34
|
-
} from './utils/constants';
|
35
|
-
|
36
|
-
import {columns as partitionsColumns} from './columns';
|
26
|
+
import type {PreparedPartitionDataWithHosts} from './utils/types';
|
27
|
+
import {addHostToPartitions} from './utils';
|
28
|
+
import {PartitionsControls} from './PartitionsControls/PartitionsControls';
|
29
|
+
import {useGetPartitionsColumns} from './utils/useGetPartitionsColumns';
|
37
30
|
|
38
31
|
import i18n from './i18n';
|
39
32
|
|
@@ -43,215 +36,140 @@ export const b = block('ydb-diagnostics-partitions');
|
|
43
36
|
|
44
37
|
interface PartitionsProps {
|
45
38
|
path?: string;
|
46
|
-
type?: EPathType;
|
47
|
-
nodes?: Record<number, string>;
|
48
|
-
consumers?: string[];
|
49
39
|
}
|
50
40
|
|
51
|
-
export const Partitions = ({path
|
52
|
-
const isCdcStream = isCdcStreamEntityType(type);
|
53
|
-
|
41
|
+
export const Partitions = ({path}: PartitionsProps) => {
|
54
42
|
const dispatch = useDispatch();
|
55
43
|
|
56
|
-
|
57
|
-
|
58
|
-
|
44
|
+
// Manual path control to ensure that topic state will be reset before data fetch
|
45
|
+
// so no request with wrong params will be sent
|
59
46
|
const [componentCurrentPath, setComponentCurrentPath] = useState(path);
|
60
47
|
|
61
|
-
const
|
62
|
-
|
63
|
-
(state) => state.consumer,
|
48
|
+
const [partitionsToRender, setPartitionsToRender] = useState<PreparedPartitionDataWithHosts[]>(
|
49
|
+
[],
|
64
50
|
);
|
65
51
|
|
66
|
-
const
|
67
|
-
|
68
|
-
const
|
69
|
-
|
52
|
+
const consumers = useTypedSelector(selectConsumersNames);
|
53
|
+
const nodesMap = useTypedSelector(selectNodesMap);
|
54
|
+
const {autorefresh} = useTypedSelector((state) => state.schema);
|
55
|
+
const {
|
56
|
+
loading: partitionsLoading,
|
57
|
+
wasLoaded: partitionsWasLoaded,
|
58
|
+
error: partitionsError,
|
59
|
+
partitions: rawPartitions,
|
60
|
+
selectedConsumer,
|
61
|
+
} = useTypedSelector((state) => state.partitions);
|
62
|
+
const {
|
63
|
+
loading: topicLoading,
|
64
|
+
wasLoaded: topicWasLoaded,
|
65
|
+
error: topicError,
|
66
|
+
} = useTypedSelector((state) => state.topic);
|
67
|
+
const {
|
68
|
+
loading: nodesLoading,
|
69
|
+
wasLoaded: nodesWasLoaded,
|
70
|
+
error: nodesError,
|
71
|
+
} = useTypedSelector((state) => state.nodesList);
|
72
|
+
|
73
|
+
const [hiddenColumns, setHiddenColumns] = useSetting<string[]>(
|
74
|
+
PARTITIONS_HIDDEN_COLUMNS_KEY,
|
75
|
+
[],
|
70
76
|
);
|
71
77
|
|
78
|
+
const [columns, columnsIdsForSelector] = useGetPartitionsColumns(selectedConsumer);
|
79
|
+
|
72
80
|
useEffect(() => {
|
73
|
-
|
81
|
+
dispatch(cleanTopicData());
|
82
|
+
dispatch(setTopicDataWasNotLoaded());
|
83
|
+
|
84
|
+
dispatch(getNodesList());
|
85
|
+
dispatch(getTopic(path));
|
86
|
+
|
74
87
|
setComponentCurrentPath(path);
|
75
88
|
}, [dispatch, path]);
|
76
89
|
|
77
|
-
const
|
90
|
+
const partitionsWithHosts = useMemo(() => {
|
91
|
+
return addHostToPartitions(rawPartitions, nodesMap);
|
92
|
+
}, [rawPartitions, nodesMap]);
|
93
|
+
|
94
|
+
const fetchData = useCallback(
|
78
95
|
(isBackground: boolean) => {
|
79
96
|
if (!isBackground) {
|
80
|
-
dispatch(
|
97
|
+
dispatch(setPartitionsDataWasNotLoaded());
|
81
98
|
}
|
82
|
-
|
83
|
-
|
84
|
-
dispatch(getConsumer(componentCurrentPath, selectedConsumer));
|
99
|
+
if (topicWasLoaded && componentCurrentPath) {
|
100
|
+
dispatch(getPartitions(componentCurrentPath, selectedConsumer));
|
85
101
|
}
|
86
102
|
},
|
87
|
-
[dispatch, selectedConsumer, componentCurrentPath,
|
103
|
+
[dispatch, selectedConsumer, componentCurrentPath, topicWasLoaded],
|
88
104
|
);
|
89
105
|
|
90
|
-
useAutofetcher(
|
91
|
-
|
92
|
-
const consumersToSelect = useMemo(
|
93
|
-
() =>
|
94
|
-
consumers
|
95
|
-
? consumers.map((consumer) => ({
|
96
|
-
value: consumer,
|
97
|
-
content: consumer,
|
98
|
-
}))
|
99
|
-
: undefined,
|
100
|
-
[consumers],
|
101
|
-
);
|
106
|
+
useAutofetcher(fetchData, [fetchData], autorefresh);
|
102
107
|
|
108
|
+
// Wrong consumer could be passed in search query
|
109
|
+
// Reset consumer if it doesn't exist for current topic
|
103
110
|
useEffect(() => {
|
104
|
-
const
|
105
|
-
|
111
|
+
const isTopicWithoutConsumers = topicWasLoaded && !consumers;
|
112
|
+
const wrongSelectedConsumer =
|
113
|
+
selectedConsumer && consumers && !consumers.includes(selectedConsumer);
|
106
114
|
|
107
|
-
if (
|
108
|
-
dispatch(setSelectedConsumer(
|
115
|
+
if (isTopicWithoutConsumers || wrongSelectedConsumer) {
|
116
|
+
dispatch(setSelectedConsumer());
|
109
117
|
}
|
110
|
-
}, [dispatch,
|
111
|
-
|
112
|
-
const selectedColumns: string[] = useMemo(
|
113
|
-
() =>
|
114
|
-
savedSelectedColumns
|
115
|
-
? JSON.parse(savedSelectedColumns)
|
116
|
-
: PARTITIONS_DEFAULT_SELECTED_COLUMNS,
|
117
|
-
[savedSelectedColumns],
|
118
|
-
);
|
119
|
-
|
120
|
-
const columnsToSelect = useMemo(() => {
|
121
|
-
return Object.values(PARTITIONS_COLUMNS_IDS).map((id) => {
|
122
|
-
return {
|
123
|
-
title: PARTITIONS_COLUMNS_TITILES[id],
|
124
|
-
selected: Boolean(selectedColumns?.includes(id)),
|
125
|
-
id: id,
|
126
|
-
required: id === PARTITIONS_COLUMNS_IDS.PARTITION_ID,
|
127
|
-
};
|
128
|
-
});
|
129
|
-
}, [selectedColumns]);
|
118
|
+
}, [dispatch, topicWasLoaded, selectedConsumer, consumers]);
|
130
119
|
|
131
120
|
const columnsToShow = useMemo(() => {
|
132
|
-
return
|
133
|
-
}, [
|
134
|
-
|
135
|
-
const partitionsWithHosts: IPreparedPartitionDataWithHosts[] | undefined = useMemo(() => {
|
136
|
-
return partitions?.map((partition) => {
|
137
|
-
const partitionHost =
|
138
|
-
partition.partitionNodeId && nodes ? nodes[partition.partitionNodeId] : undefined;
|
139
|
-
|
140
|
-
const connectionHost =
|
141
|
-
partition.connectionNodeId && nodes ? nodes[partition.connectionNodeId] : undefined;
|
142
|
-
|
143
|
-
return {
|
144
|
-
...partition,
|
145
|
-
partitionHost,
|
146
|
-
connectionHost,
|
147
|
-
};
|
148
|
-
});
|
149
|
-
}, [partitions, nodes]);
|
150
|
-
|
151
|
-
const dataToRender = useMemo(() => {
|
152
|
-
if (!partitionsWithHosts) {
|
153
|
-
return [];
|
154
|
-
}
|
155
|
-
|
156
|
-
const partitionIdRe = new RegExp(escapeRegExp(partitionIdSearchValue), 'i');
|
157
|
-
const generalRe = new RegExp(escapeRegExp(generalSearchValue), 'i');
|
158
|
-
|
159
|
-
return partitionsWithHosts.filter((partition) => {
|
160
|
-
const {
|
161
|
-
partitionId,
|
162
|
-
readerName = '',
|
163
|
-
readSessionId = '',
|
164
|
-
partitionNodeId,
|
165
|
-
connectionNodeId,
|
166
|
-
partitionHost = '',
|
167
|
-
connectionHost = '',
|
168
|
-
} = partition;
|
121
|
+
return columns.filter((column) => !hiddenColumns.includes(column.name));
|
122
|
+
}, [columns, hiddenColumns]);
|
169
123
|
|
170
|
-
|
171
|
-
|
172
|
-
generalRe.test(readerName) ||
|
173
|
-
generalRe.test(readSessionId) ||
|
174
|
-
generalRe.test(String(partitionNodeId)) ||
|
175
|
-
generalRe.test(String(connectionNodeId)) ||
|
176
|
-
generalRe.test(partitionHost) ||
|
177
|
-
generalRe.test(connectionHost);
|
178
|
-
|
179
|
-
return isPartitionIdMatch && isOtherValuesMatch;
|
180
|
-
});
|
181
|
-
}, [partitionIdSearchValue, generalSearchValue, partitionsWithHosts]);
|
182
|
-
|
183
|
-
const hadleTableColumnsSetupChange = (value: TableColumnSetupItem[]) => {
|
184
|
-
const columns = value.filter((el) => el.selected).map((el) => el.id);
|
185
|
-
dispatch(setSettingValue(PARTITIONS_SELECTED_COLUMNS_KEY, JSON.stringify(columns)));
|
124
|
+
const hadleTableColumnsSetupChange = (newHiddenColumns: string[]) => {
|
125
|
+
setHiddenColumns(newHiddenColumns);
|
186
126
|
};
|
187
127
|
|
188
|
-
const
|
189
|
-
dispatch(setSelectedConsumer(value
|
128
|
+
const handleSelectedConsumerChange = (value?: string) => {
|
129
|
+
dispatch(setSelectedConsumer(value));
|
190
130
|
};
|
191
131
|
|
192
|
-
const
|
193
|
-
|
194
|
-
|
132
|
+
const loading =
|
133
|
+
(topicLoading && !topicWasLoaded) ||
|
134
|
+
(nodesLoading && !nodesWasLoaded) ||
|
135
|
+
(partitionsLoading && !partitionsWasLoaded);
|
195
136
|
|
196
|
-
const
|
197
|
-
setGeneralSearchValue(value);
|
198
|
-
};
|
137
|
+
const error = nodesError || topicError || partitionsError;
|
199
138
|
|
200
|
-
|
201
|
-
|
202
|
-
|
139
|
+
const getContent = () => {
|
140
|
+
if (loading) {
|
141
|
+
return <TableSkeleton className={b('loader')} />;
|
142
|
+
}
|
143
|
+
if (error) {
|
144
|
+
return <ResponseError error={error} />;
|
145
|
+
}
|
203
146
|
|
204
|
-
|
205
|
-
|
206
|
-
|
147
|
+
return (
|
148
|
+
<DataTable
|
149
|
+
theme="yandex-cloud"
|
150
|
+
data={partitionsToRender}
|
151
|
+
columns={columnsToShow}
|
152
|
+
settings={DEFAULT_TABLE_SETTINGS}
|
153
|
+
emptyDataMessage={i18n('table.emptyDataMessage')}
|
154
|
+
/>
|
155
|
+
);
|
156
|
+
};
|
207
157
|
|
208
158
|
return (
|
209
159
|
<div className={b()}>
|
210
|
-
<
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
onChange={handlePartitionIdSearchChange}
|
222
|
-
placeholder={i18n('controls.partitionSearch')}
|
223
|
-
className={b('search', {partition: true})}
|
224
|
-
value={partitionIdSearchValue}
|
225
|
-
/>
|
226
|
-
<Search
|
227
|
-
onChange={handleGeneralSearchChange}
|
228
|
-
placeholder={i18n('controls.generalSearch')}
|
229
|
-
className={b('search', {general: true})}
|
230
|
-
value={generalSearchValue}
|
231
|
-
/>
|
232
|
-
<TableColumnSetup
|
233
|
-
key="TableColumnSetup"
|
234
|
-
popupWidth="242px"
|
235
|
-
items={columnsToSelect}
|
236
|
-
showStatus
|
237
|
-
onUpdate={hadleTableColumnsSetupChange}
|
238
|
-
className={b('table-settings')}
|
239
|
-
/>
|
240
|
-
</div>
|
160
|
+
<PartitionsControls
|
161
|
+
consumers={consumers}
|
162
|
+
selectedConsumer={selectedConsumer}
|
163
|
+
onSelectedConsumerChange={handleSelectedConsumerChange}
|
164
|
+
selectDisabled={Boolean(error) || loading}
|
165
|
+
partitions={partitionsWithHosts}
|
166
|
+
onSearchChange={setPartitionsToRender}
|
167
|
+
hiddenColumns={hiddenColumns}
|
168
|
+
onHiddenColumnsChange={hadleTableColumnsSetupChange}
|
169
|
+
initialColumnsIds={columnsIdsForSelector}
|
170
|
+
/>
|
241
171
|
<div className={b('table-wrapper')}>
|
242
|
-
<div className={b('table-content')}>
|
243
|
-
{loading && !wasLoaded ? (
|
244
|
-
<TableSkeleton className={b('loader')} />
|
245
|
-
) : (
|
246
|
-
<DataTable
|
247
|
-
theme="yandex-cloud"
|
248
|
-
data={dataToRender}
|
249
|
-
columns={columnsToShow}
|
250
|
-
settings={DEFAULT_TABLE_SETTINGS}
|
251
|
-
emptyDataMessage={i18n('table.emptyDataMessage')}
|
252
|
-
/>
|
253
|
-
)}
|
254
|
-
</div>
|
172
|
+
<div className={b('table-content')}>{getContent()}</div>
|
255
173
|
</div>
|
256
174
|
</div>
|
257
175
|
);
|
package/dist/containers/Tenant/Diagnostics/Partitions/PartitionsControls/PartitionsControls.tsx
ADDED
@@ -0,0 +1,182 @@
|
|
1
|
+
import {useEffect, useMemo, useState} from 'react';
|
2
|
+
import {escapeRegExp} from 'lodash/fp';
|
3
|
+
|
4
|
+
import {TableColumnSetupItem} from '@gravity-ui/uikit/build/esm/components/Table/hoc/withTableSettings/withTableSettings';
|
5
|
+
import {Select, TableColumnSetup} from '@gravity-ui/uikit';
|
6
|
+
|
7
|
+
import type {ValueOf} from '../../../../../types/common';
|
8
|
+
|
9
|
+
import {Search} from '../../../../../components/Search/Search';
|
10
|
+
|
11
|
+
import type {PreparedPartitionDataWithHosts} from '../utils/types';
|
12
|
+
import {PARTITIONS_COLUMNS_IDS, PARTITIONS_COLUMNS_TITILES} from '../utils/constants';
|
13
|
+
import i18n from '../i18n';
|
14
|
+
import {b} from '../Partitions';
|
15
|
+
|
16
|
+
interface PartitionsControlsProps {
|
17
|
+
consumers: string[] | undefined;
|
18
|
+
selectedConsumer: string | undefined;
|
19
|
+
onSelectedConsumerChange: (consumer: string | undefined) => void;
|
20
|
+
selectDisabled: boolean;
|
21
|
+
partitions: PreparedPartitionDataWithHosts[] | undefined;
|
22
|
+
onSearchChange: (filteredPartitions: PreparedPartitionDataWithHosts[]) => void;
|
23
|
+
hiddenColumns: string[];
|
24
|
+
onHiddenColumnsChange: (newHiddenColumns: string[]) => void;
|
25
|
+
initialColumnsIds: string[];
|
26
|
+
}
|
27
|
+
|
28
|
+
export const PartitionsControls = ({
|
29
|
+
consumers,
|
30
|
+
selectedConsumer,
|
31
|
+
onSelectedConsumerChange,
|
32
|
+
selectDisabled,
|
33
|
+
partitions,
|
34
|
+
onSearchChange,
|
35
|
+
hiddenColumns,
|
36
|
+
onHiddenColumnsChange,
|
37
|
+
initialColumnsIds,
|
38
|
+
}: PartitionsControlsProps) => {
|
39
|
+
const [generalSearchValue, setGeneralSearchValue] = useState('');
|
40
|
+
const [partitionIdSearchValue, setPartitionIdSearchValue] = useState('');
|
41
|
+
|
42
|
+
// Manual select control to enforce single-select behaviour for multiple select type
|
43
|
+
const [consumerSelectOpen, setConsumerSelectOpen] = useState(false);
|
44
|
+
|
45
|
+
useEffect(() => {
|
46
|
+
if (!partitions) {
|
47
|
+
return;
|
48
|
+
}
|
49
|
+
|
50
|
+
const partitionIdRe = new RegExp(escapeRegExp(partitionIdSearchValue), 'i');
|
51
|
+
const generalRe = new RegExp(escapeRegExp(generalSearchValue), 'i');
|
52
|
+
|
53
|
+
const filteredPartitions = partitions.filter((partition) => {
|
54
|
+
const {
|
55
|
+
partitionId,
|
56
|
+
readerName,
|
57
|
+
readSessionId,
|
58
|
+
partitionNodeId,
|
59
|
+
connectionNodeId,
|
60
|
+
partitionHost,
|
61
|
+
connectionHost,
|
62
|
+
} = partition;
|
63
|
+
|
64
|
+
const isPartitionIdMatch = partitionIdRe.test(partitionId);
|
65
|
+
|
66
|
+
const otherValues = [
|
67
|
+
readerName,
|
68
|
+
readSessionId,
|
69
|
+
partitionNodeId,
|
70
|
+
connectionNodeId,
|
71
|
+
partitionHost,
|
72
|
+
connectionHost,
|
73
|
+
]
|
74
|
+
.filter(Boolean)
|
75
|
+
.map(String);
|
76
|
+
|
77
|
+
const isOtherValuesMatch =
|
78
|
+
otherValues.length === 0 || otherValues.some((value) => generalRe.test(value));
|
79
|
+
|
80
|
+
return isPartitionIdMatch && isOtherValuesMatch;
|
81
|
+
});
|
82
|
+
|
83
|
+
onSearchChange(filteredPartitions);
|
84
|
+
}, [partitionIdSearchValue, generalSearchValue, partitions, onSearchChange]);
|
85
|
+
|
86
|
+
const consumersToSelect = useMemo(
|
87
|
+
() =>
|
88
|
+
consumers
|
89
|
+
? consumers.map((consumer) => ({
|
90
|
+
value: consumer,
|
91
|
+
content: consumer,
|
92
|
+
}))
|
93
|
+
: undefined,
|
94
|
+
[consumers],
|
95
|
+
);
|
96
|
+
|
97
|
+
const columnsToSelect = useMemo(() => {
|
98
|
+
return initialColumnsIds.map((id) => {
|
99
|
+
return {
|
100
|
+
title: PARTITIONS_COLUMNS_TITILES[id as ValueOf<typeof PARTITIONS_COLUMNS_IDS>],
|
101
|
+
selected: Boolean(!hiddenColumns.includes(id)),
|
102
|
+
id: id,
|
103
|
+
required: id === PARTITIONS_COLUMNS_IDS.PARTITION_ID,
|
104
|
+
};
|
105
|
+
});
|
106
|
+
}, [initialColumnsIds, hiddenColumns]);
|
107
|
+
|
108
|
+
const handleConsumerSelectChange = (value: string[]) => {
|
109
|
+
// As we have selector with multiple options, the first value corresponds to previous value
|
110
|
+
// The second value is currently chosen consumer
|
111
|
+
onSelectedConsumerChange(value[1]);
|
112
|
+
setConsumerSelectOpen(false);
|
113
|
+
};
|
114
|
+
|
115
|
+
const handlePartitionIdSearchChange = (value: string) => {
|
116
|
+
setPartitionIdSearchValue(value);
|
117
|
+
};
|
118
|
+
|
119
|
+
const handleGeneralSearchChange = (value: string) => {
|
120
|
+
setGeneralSearchValue(value);
|
121
|
+
};
|
122
|
+
|
123
|
+
const hadleTableColumnsSetupChange = (value: TableColumnSetupItem[]) => {
|
124
|
+
const result = [...hiddenColumns];
|
125
|
+
|
126
|
+
// Process current set of columns
|
127
|
+
// This way we do not remove from hidden these columns, that are not displayed currently
|
128
|
+
// The reasons: set of columns differs for partitions with and without consumers
|
129
|
+
value.forEach((el) => {
|
130
|
+
if (!el.selected && !hiddenColumns.includes(el.id)) {
|
131
|
+
result.push(el.id);
|
132
|
+
} else if (el.selected && hiddenColumns.includes(el.id)) {
|
133
|
+
result.splice(hiddenColumns.indexOf(el.id));
|
134
|
+
}
|
135
|
+
});
|
136
|
+
|
137
|
+
onHiddenColumnsChange(result);
|
138
|
+
};
|
139
|
+
|
140
|
+
return (
|
141
|
+
<div className={b('controls')}>
|
142
|
+
<Select
|
143
|
+
className={b('consumer-select')}
|
144
|
+
placeholder={i18n('controls.consumerSelector.placeholder')}
|
145
|
+
label={i18n('controls.consumerSelector')}
|
146
|
+
options={consumersToSelect}
|
147
|
+
value={[selectedConsumer || '']}
|
148
|
+
onUpdate={handleConsumerSelectChange}
|
149
|
+
filterable={consumers && consumers.length > 5}
|
150
|
+
disabled={selectDisabled || !consumers || !consumers.length}
|
151
|
+
open={consumerSelectOpen}
|
152
|
+
onOpenChange={setConsumerSelectOpen}
|
153
|
+
// Although only one value could be selected
|
154
|
+
// Multiple type Select is used
|
155
|
+
// The reason - we need Select to be able to work with no value
|
156
|
+
// And it is easier to make multiple Select close on value change
|
157
|
+
// Than to enable single select to work with empty values
|
158
|
+
multiple
|
159
|
+
/>
|
160
|
+
<Search
|
161
|
+
onChange={handlePartitionIdSearchChange}
|
162
|
+
placeholder={i18n('controls.partitionSearch')}
|
163
|
+
className={b('search', {partition: true})}
|
164
|
+
value={partitionIdSearchValue}
|
165
|
+
/>
|
166
|
+
<Search
|
167
|
+
onChange={handleGeneralSearchChange}
|
168
|
+
placeholder={i18n('controls.generalSearch')}
|
169
|
+
className={b('search', {general: true})}
|
170
|
+
value={generalSearchValue}
|
171
|
+
/>
|
172
|
+
<TableColumnSetup
|
173
|
+
key="TableColumnSetup"
|
174
|
+
popupWidth="242px"
|
175
|
+
items={columnsToSelect}
|
176
|
+
showStatus
|
177
|
+
onUpdate={hadleTableColumnsSetupChange}
|
178
|
+
className={b('table-settings')}
|
179
|
+
/>
|
180
|
+
</div>
|
181
|
+
);
|
182
|
+
};
|
@@ -14,8 +14,9 @@ import {
|
|
14
14
|
PARTITIONS_READ_LAGS_SUB_COLUMNS_TITLES,
|
15
15
|
PARTITIONS_WRITE_LAGS_SUB_COLUMNS_IDS,
|
16
16
|
PARTITIONS_WRITE_LAGS_SUB_COLUMNS_TITLES,
|
17
|
+
generalPartitionColumnsIds,
|
17
18
|
} from '../utils/constants';
|
18
|
-
import type {
|
19
|
+
import type {PreparedPartitionDataWithHosts} from '../utils/types';
|
19
20
|
|
20
21
|
import {
|
21
22
|
MultilineHeader,
|
@@ -30,7 +31,7 @@ import './Columns.scss';
|
|
30
31
|
|
31
32
|
const b = block('ydb-diagnostics-partitions-columns');
|
32
33
|
|
33
|
-
export const
|
34
|
+
export const allColumns: Column<PreparedPartitionDataWithHosts>[] = [
|
34
35
|
{
|
35
36
|
name: PARTITIONS_COLUMNS_IDS.PARTITION_ID,
|
36
37
|
header: (
|
@@ -63,7 +64,7 @@ export const columns: Column<IPreparedPartitionDataWithHosts>[] = [
|
|
63
64
|
name: PARTITIONS_COLUMNS_IDS.READ_SPEED,
|
64
65
|
header: PARTITIONS_COLUMNS_TITILES[PARTITIONS_COLUMNS_IDS.READ_SPEED],
|
65
66
|
align: DataTable.LEFT,
|
66
|
-
sortAccessor: (row) => row.readSpeed
|
67
|
+
sortAccessor: (row) => row.readSpeed?.perMinute,
|
67
68
|
render: ({row}) => <SpeedMultiMeter data={row.readSpeed} />,
|
68
69
|
},
|
69
70
|
{
|
@@ -210,10 +211,10 @@ export const columns: Column<IPreparedPartitionDataWithHosts>[] = [
|
|
210
211
|
),
|
211
212
|
align: DataTable.LEFT,
|
212
213
|
render: ({row}) =>
|
213
|
-
row.partitionHost ? (
|
214
|
+
row.partitionNodeId && row.partitionHost ? (
|
214
215
|
<EntityStatus
|
215
216
|
name={row.partitionHost}
|
216
|
-
path={getDefaultNodePath(row.
|
217
|
+
path={getDefaultNodePath(row.partitionNodeId)}
|
217
218
|
showStatus={false}
|
218
219
|
hasClipboardButton
|
219
220
|
className={b('string-with-copy')}
|
@@ -231,7 +232,7 @@ export const columns: Column<IPreparedPartitionDataWithHosts>[] = [
|
|
231
232
|
),
|
232
233
|
align: DataTable.LEFT,
|
233
234
|
render: ({row}) =>
|
234
|
-
row.connectionHost ? (
|
235
|
+
row.connectionNodeId && row.connectionHost ? (
|
235
236
|
<EntityStatus
|
236
237
|
name={row.connectionHost}
|
237
238
|
path={getDefaultNodePath(row.connectionNodeId)}
|
@@ -244,3 +245,11 @@ export const columns: Column<IPreparedPartitionDataWithHosts>[] = [
|
|
244
245
|
),
|
245
246
|
},
|
246
247
|
];
|
248
|
+
|
249
|
+
// Topics without consumers have partitions data with no data corresponding to consumers
|
250
|
+
// These columns will be empty and should not be displayed
|
251
|
+
export const generalColumns = allColumns.filter((column) => {
|
252
|
+
return generalPartitionColumnsIds.includes(
|
253
|
+
column.name as typeof generalPartitionColumnsIds[number],
|
254
|
+
);
|
255
|
+
});
|