ydb-embedded-ui 4.18.0 → 4.19.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (35) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/dist/components/FullNodeViewer/FullNodeViewer.scss +1 -1
  3. package/dist/components/FullNodeViewer/FullNodeViewer.tsx +13 -4
  4. package/dist/components/ProgressViewer/ProgressViewer.tsx +11 -5
  5. package/dist/containers/Nodes/getNodesColumns.tsx +1 -0
  6. package/dist/containers/Storage/PDiskPopup/PDiskPopup.tsx +1 -1
  7. package/dist/containers/Storage/StorageGroups/StorageGroups.tsx +3 -232
  8. package/dist/containers/Storage/StorageGroups/getStorageGroupsColumns.tsx +278 -0
  9. package/dist/containers/Tenant/Diagnostics/TenantOverview/MetricsCards/MetricsCards.tsx +2 -3
  10. package/dist/containers/Tenant/Diagnostics/TenantOverview/TenantOverview.tsx +22 -6
  11. package/dist/containers/Tenant/Diagnostics/TenantOverview/TenantStorage/TenantStorage.scss +41 -0
  12. package/dist/containers/Tenant/Diagnostics/TenantOverview/TenantStorage/TenantStorage.tsx +68 -0
  13. package/dist/containers/Tenant/Diagnostics/TenantOverview/TenantStorage/TopGroups.tsx +76 -0
  14. package/dist/containers/Tenant/Diagnostics/TenantOverview/TenantStorage/TopTables.tsx +105 -0
  15. package/dist/containers/Tenant/Diagnostics/TenantOverview/i18n/en.json +2 -0
  16. package/dist/containers/Tenant/Diagnostics/TenantOverview/i18n/ru.json +2 -0
  17. package/dist/containers/Versions/NodesTable/NodesTable.tsx +1 -0
  18. package/dist/store/reducers/index.ts +4 -0
  19. package/dist/store/reducers/storage/utils.ts +20 -11
  20. package/dist/store/reducers/tenantOverview/executeTopTables/executeTopTables.ts +93 -0
  21. package/dist/store/reducers/tenantOverview/executeTopTables/types.ts +14 -0
  22. package/dist/store/reducers/tenantOverview/topStorageGroups/topStorageGroups.ts +98 -0
  23. package/dist/store/reducers/tenantOverview/topStorageGroups/types.ts +29 -0
  24. package/dist/store/reducers/tenantOverview/topStorageGroups/utils.ts +20 -0
  25. package/dist/store/reducers/tenants/utils.ts +28 -18
  26. package/dist/styles/constants.scss +4 -0
  27. package/dist/types/additionalProps.ts +1 -1
  28. package/dist/types/api/storage.ts +1 -1
  29. package/dist/types/api/tenant.ts +21 -8
  30. package/dist/utils/bytesParsers/__test__/formatBytes.test.ts +10 -1
  31. package/dist/utils/bytesParsers/formatBytes.ts +3 -3
  32. package/dist/utils/constants.ts +8 -0
  33. package/dist/utils/dataFormatters/__test__/roundToSignificant.test.ts +22 -0
  34. package/dist/utils/dataFormatters/dataFormatters.ts +42 -20
  35. package/package.json +1 -1
@@ -14,6 +14,7 @@ import {getTenantInfo, setDataWasNotLoaded} from '../../../../store/reducers/ten
14
14
  import {calculateTenantMetrics} from '../../../../store/reducers/tenants/utils';
15
15
  import {HealthcheckDetails} from './Healthcheck/HealthcheckDetails';
16
16
  import {MetricsCards, type TenantMetrics} from './MetricsCards/MetricsCards';
17
+ import {TenantStorage} from './TenantStorage/TenantStorage';
17
18
  import {useHealthcheck} from './useHealthcheck';
18
19
 
19
20
  import i18n from './i18n';
@@ -70,16 +71,31 @@ export function TenantOverview({tenantName, additionalTenantProps}: TenantOvervi
70
71
 
71
72
  const tenantType = mapDatabaseTypeToDBName(Type);
72
73
 
73
- const {cpu, storage, memory, cpuLimit, storageLimit, memoryLimit} =
74
- calculateTenantMetrics(tenant);
74
+ const {
75
+ cpu,
76
+ blobStorage,
77
+ tableStorage,
78
+ memory,
79
+ cpuUsage,
80
+ blobStorageLimit,
81
+ tableStorageLimit,
82
+ memoryLimit,
83
+ } = calculateTenantMetrics(tenant);
75
84
 
76
85
  const calculatedMetrics: TenantMetrics = {
77
86
  memoryUsed: memory,
78
87
  memoryLimit,
79
88
  cpuUsed: cpu,
80
- cpuLimit,
81
- storageUsed: storage,
82
- storageLimit,
89
+ cpuUsage,
90
+ storageUsed: blobStorage,
91
+ storageLimit: blobStorageLimit,
92
+ };
93
+
94
+ const storageMetrics = {
95
+ blobStorageUsed: blobStorage,
96
+ blobStorageLimit,
97
+ tableStorageUsed: tableStorage,
98
+ tableStorageLimit,
83
99
  };
84
100
 
85
101
  const renderName = () => {
@@ -102,7 +118,7 @@ export function TenantOverview({tenantName, additionalTenantProps}: TenantOvervi
102
118
  return i18n('label.under-development');
103
119
  }
104
120
  case TENANT_METRICS_TABS_IDS.storage: {
105
- return i18n('label.under-development');
121
+ return <TenantStorage tenantName={tenantName} metrics={storageMetrics} />;
106
122
  }
107
123
  case TENANT_METRICS_TABS_IDS.memory: {
108
124
  return i18n('label.under-development');
@@ -0,0 +1,41 @@
1
+ @import '../../../../../styles/mixins.scss';
2
+
3
+ .tenant-overview-storage {
4
+ &__info {
5
+ margin-bottom: 36px;
6
+ }
7
+
8
+ &__title {
9
+ margin-bottom: 10px;
10
+
11
+ font-size: var(--yc-text-body-2-font-size);
12
+ font-weight: 700;
13
+ line-height: var(--yc-text-body-2-line-height);
14
+ }
15
+
16
+ &__table {
17
+ width: var(--diagnostics-section-table-width);
18
+ @include table-styles;
19
+
20
+ &:not(:last-child) {
21
+ margin-bottom: var(--diagnostics-section-margin);
22
+ }
23
+
24
+ th {
25
+ height: 40px;
26
+
27
+ vertical-align: middle;
28
+ }
29
+ }
30
+
31
+ &__cell-with-popover-wrapper {
32
+ display: flex;
33
+ }
34
+
35
+ &__cell-with-popover {
36
+ overflow: hidden;
37
+
38
+ white-space: nowrap;
39
+ text-overflow: ellipsis;
40
+ }
41
+ }
@@ -0,0 +1,68 @@
1
+ import cn from 'bem-cn-lite';
2
+
3
+ import InfoViewer from '../../../../../components/InfoViewer/InfoViewer';
4
+ import {ProgressViewer} from '../../../../../components/ProgressViewer/ProgressViewer';
5
+ import {formatStorageValues} from '../../../../../utils/dataFormatters/dataFormatters';
6
+ import {getSizeWithSignificantDigits} from '../../../../../utils/bytesParsers';
7
+ import {TopTables} from './TopTables';
8
+ import {TopGroups} from './TopGroups';
9
+ import './TenantStorage.scss';
10
+
11
+ const b = cn('tenant-overview-storage');
12
+
13
+ export interface TenantStorageMetrics {
14
+ blobStorageUsed?: number;
15
+ blobStorageLimit?: number;
16
+ tableStorageUsed?: number;
17
+ tableStorageLimit?: number;
18
+ }
19
+
20
+ interface TenantStorageProps {
21
+ tenantName: string;
22
+ metrics: TenantStorageMetrics;
23
+ }
24
+
25
+ export function TenantStorage({tenantName, metrics}: TenantStorageProps) {
26
+ const {blobStorageUsed, tableStorageUsed, blobStorageLimit, tableStorageLimit} = metrics;
27
+ const formatValues = (value?: number, total?: number) => {
28
+ const size = getSizeWithSignificantDigits(Number(blobStorageLimit || blobStorageUsed), 0);
29
+
30
+ return formatStorageValues(value, total, size);
31
+ };
32
+
33
+ const info = [
34
+ {
35
+ label: 'Database storage',
36
+ value: (
37
+ <ProgressViewer
38
+ value={blobStorageUsed}
39
+ capacity={blobStorageLimit}
40
+ formatValues={formatValues}
41
+ colorizeProgress={true}
42
+ warningThreshold={75}
43
+ dangerThreshold={85}
44
+ />
45
+ ),
46
+ },
47
+ {
48
+ label: 'Table storage',
49
+ value: (
50
+ <ProgressViewer
51
+ value={tableStorageUsed}
52
+ capacity={tableStorageLimit}
53
+ formatValues={formatValues}
54
+ colorizeProgress={true}
55
+ warningThreshold={75}
56
+ dangerThreshold={85}
57
+ />
58
+ ),
59
+ },
60
+ ];
61
+ return (
62
+ <>
63
+ <InfoViewer className={b('info')} title="Storage details" info={info} />
64
+ <TopTables path={tenantName} />
65
+ <TopGroups tenant={tenantName} />
66
+ </>
67
+ );
68
+ }
@@ -0,0 +1,76 @@
1
+ import cn from 'bem-cn-lite';
2
+ import {useCallback} from 'react';
3
+ import {useDispatch} from 'react-redux';
4
+
5
+ import DataTable from '@gravity-ui/react-data-table';
6
+
7
+ import {useAutofetcher, useTypedSelector} from '../../../../../utils/hooks';
8
+ import {
9
+ TENANT_OVERVIEW_TABLES_LIMIT,
10
+ TENANT_OVERVIEW_TABLES_SETTINGS,
11
+ } from '../../../../../utils/constants';
12
+ import {
13
+ setDataWasNotLoaded,
14
+ getTopStorageGroups,
15
+ selectTopStorageGroups,
16
+ } from '../../../../../store/reducers/tenantOverview/topStorageGroups/topStorageGroups';
17
+ import {ResponseError} from '../../../../../components/Errors/ResponseError';
18
+ import {TableSkeleton} from '../../../../../components/TableSkeleton/TableSkeleton';
19
+ import {getStorageTopGroupsColumns} from '../../../../Storage/StorageGroups/getStorageGroupsColumns';
20
+ import i18n from '../i18n';
21
+
22
+ const b = cn('tenant-overview-storage');
23
+
24
+ interface TopGroupsProps {
25
+ tenant?: string;
26
+ }
27
+
28
+ export function TopGroups({tenant}: TopGroupsProps) {
29
+ const dispatch = useDispatch();
30
+
31
+ const {autorefresh} = useTypedSelector((state) => state.schema);
32
+ const {loading, wasLoaded, error} = useTypedSelector((state) => state.topStorageGroups);
33
+ const topGroups = useTypedSelector(selectTopStorageGroups);
34
+
35
+ const columns = getStorageTopGroupsColumns();
36
+
37
+ const fetchData = useCallback(
38
+ (isBackground: boolean) => {
39
+ if (!isBackground) {
40
+ dispatch(setDataWasNotLoaded());
41
+ }
42
+
43
+ dispatch(getTopStorageGroups({tenant}));
44
+ },
45
+ [dispatch, tenant],
46
+ );
47
+
48
+ useAutofetcher(fetchData, [fetchData], autorefresh);
49
+
50
+ const renderContent = () => {
51
+ if (error) {
52
+ return <ResponseError error={error} />;
53
+ }
54
+
55
+ if (loading && !wasLoaded) {
56
+ return <TableSkeleton rows={TENANT_OVERVIEW_TABLES_LIMIT} />;
57
+ }
58
+
59
+ return (
60
+ <DataTable
61
+ theme="yandex-cloud"
62
+ data={topGroups || []}
63
+ columns={columns}
64
+ settings={TENANT_OVERVIEW_TABLES_SETTINGS}
65
+ emptyDataMessage={i18n('top-groups.empty-data')}
66
+ />
67
+ );
68
+ };
69
+
70
+ return (
71
+ <>
72
+ <div className={b('title')}>Top groups by usage</div>
73
+ <div className={b('table')}>{renderContent()}</div>
74
+ </>
75
+ );
76
+ }
@@ -0,0 +1,105 @@
1
+ import {useDispatch} from 'react-redux';
2
+ import cn from 'bem-cn-lite';
3
+
4
+ import DataTable, {Column} from '@gravity-ui/react-data-table';
5
+ import {Popover} from '@gravity-ui/uikit';
6
+
7
+ import {useAutofetcher, useTypedSelector} from '../../../../../utils/hooks';
8
+ import {
9
+ fetchTopTables,
10
+ setDataWasNotLoaded,
11
+ } from '../../../../../store/reducers/tenantOverview/executeTopTables/executeTopTables';
12
+ import {
13
+ TENANT_OVERVIEW_TABLES_LIMIT,
14
+ TENANT_OVERVIEW_TABLES_SETTINGS,
15
+ } from '../../../../../utils/constants';
16
+ import type {KeyValueRow} from '../../../../../types/api/query';
17
+ import {formatBytes, getSizeWithSignificantDigits} from '../../../../../utils/bytesParsers';
18
+ import {TableSkeleton} from '../../../../../components/TableSkeleton/TableSkeleton';
19
+ import {ResponseError} from '../../../../../components/Errors/ResponseError';
20
+
21
+ import './TenantStorage.scss';
22
+
23
+ const b = cn('tenant-overview-storage');
24
+
25
+ interface TopTablesProps {
26
+ path: string;
27
+ }
28
+
29
+ export function TopTables({path}: TopTablesProps) {
30
+ const dispatch = useDispatch();
31
+
32
+ const {autorefresh} = useTypedSelector((state) => state.schema);
33
+
34
+ const {
35
+ loading,
36
+ wasLoaded,
37
+ error,
38
+ data: {result: data = undefined} = {},
39
+ } = useTypedSelector((state) => state.executeTopTables);
40
+
41
+ useAutofetcher(
42
+ (isBackground) => {
43
+ if (!isBackground) {
44
+ dispatch(setDataWasNotLoaded());
45
+ }
46
+
47
+ dispatch(fetchTopTables(path));
48
+ },
49
+ [dispatch, path],
50
+ autorefresh,
51
+ );
52
+
53
+ const formatSize = (value?: number) => {
54
+ const size = getSizeWithSignificantDigits(data?.length ? Number(data[0].Size) : 0, 0);
55
+
56
+ return formatBytes({value, size, precision: 1});
57
+ };
58
+
59
+ const columns: Column<KeyValueRow>[] = [
60
+ {
61
+ name: 'Size',
62
+ width: 80,
63
+ sortable: false,
64
+ render: ({row}) => formatSize(Number(row.Size)),
65
+ align: DataTable.RIGHT,
66
+ },
67
+ {
68
+ name: 'Path',
69
+ sortable: false,
70
+ render: ({row}) => (
71
+ <div className={b('cell-with-popover-wrapper')}>
72
+ <Popover className={b('cell-with-popover')} content={row.Path}>
73
+ {row.Path}
74
+ </Popover>
75
+ </div>
76
+ ),
77
+ },
78
+ ];
79
+
80
+ const renderContent = () => {
81
+ if (error) {
82
+ return <ResponseError error={error} />;
83
+ }
84
+
85
+ if (loading && !wasLoaded) {
86
+ return <TableSkeleton rows={TENANT_OVERVIEW_TABLES_LIMIT} />;
87
+ }
88
+
89
+ return (
90
+ <DataTable
91
+ theme="yandex-cloud"
92
+ columns={columns}
93
+ settings={TENANT_OVERVIEW_TABLES_SETTINGS}
94
+ data={data || []}
95
+ />
96
+ );
97
+ };
98
+
99
+ return (
100
+ <>
101
+ <div className={b('title')}>Top tables by size</div>
102
+ <div className={b('table')}>{renderContent()}</div>
103
+ </>
104
+ );
105
+ }
@@ -5,5 +5,7 @@
5
5
  "title.pools": "Pools",
6
6
  "title.metrics": "Metrics",
7
7
 
8
+ "top-groups.empty-data": "No such groups",
9
+
8
10
  "label.under-development": "This section is under development"
9
11
  }
@@ -5,5 +5,7 @@
5
5
  "title.pools": "Пулы",
6
6
  "title.metrics": "Метрики",
7
7
 
8
+ "top-groups.empty-data": "Нет групп",
9
+
8
10
  "label.under-development": "Этот раздел находится в разработке"
9
11
  }
@@ -94,6 +94,7 @@ const columns: Column<PreparedClusterNode>[] = [
94
94
  <ProgressViewer
95
95
  value={row.LoadAverage[0]}
96
96
  percents={true}
97
+ capacity={100}
97
98
  colorizeProgress={true}
98
99
  />
99
100
  ) : (
@@ -5,6 +5,7 @@ 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/storage';
8
+ import topStorageGroups from './tenantOverview/topStorageGroups/topStorageGroups';
8
9
  import node from './node/node';
9
10
  import tooltip from './tooltip';
10
11
  import tablets from './tablets';
@@ -26,6 +27,7 @@ import nodesList from './nodesList';
26
27
  import describe from './describe';
27
28
  import schemaAcl from './schemaAcl/schemaAcl';
28
29
  import executeTopQueries from './executeTopQueries';
30
+ import executeTopTables from './tenantOverview/executeTopTables/executeTopTables';
29
31
  import healthcheckInfo from './healthcheckInfo';
30
32
  import shardsWorkload from './shardsWorkload';
31
33
  import hotKeys from './hotKeys';
@@ -43,6 +45,7 @@ export const rootReducer = {
43
45
  clusterNodes,
44
46
  tenant,
45
47
  storage,
48
+ topStorageGroups,
46
49
  node,
47
50
  tooltip,
48
51
  tablets,
@@ -65,6 +68,7 @@ export const rootReducer = {
65
68
  describe,
66
69
  schemaAcl,
67
70
  executeTopQueries,
71
+ executeTopTables,
68
72
  healthcheckInfo,
69
73
  shardsWorkload,
70
74
  hotKeys,
@@ -3,6 +3,7 @@ import type {
3
3
  TStorageGroupInfo,
4
4
  TStorageGroupInfoV2,
5
5
  TStorageInfo,
6
+ TStoragePoolInfo,
6
7
  } from '../../../types/api/storage';
7
8
  import {EVDiskState, type TVDiskStateInfo} from '../../../types/api/vdisk';
8
9
  import {TPDiskState} from '../../../types/api/pdisk';
@@ -146,6 +147,24 @@ const prepareStorageGroupDataV2 = (group: TStorageGroupInfoV2): PreparedStorageG
146
147
  };
147
148
  };
148
149
 
150
+ export const prepareStorageGroups = (
151
+ StorageGroups?: TStorageGroupInfoV2[],
152
+ StoragePools?: TStoragePoolInfo[],
153
+ ) => {
154
+ let preparedGroups: PreparedStorageGroup[] = [];
155
+ if (StorageGroups) {
156
+ preparedGroups = StorageGroups.map(prepareStorageGroupDataV2);
157
+ } else {
158
+ StoragePools?.forEach((pool) => {
159
+ pool.Groups?.forEach((group) => {
160
+ preparedGroups.push(prepareStorageGroupData(group, pool.Name));
161
+ });
162
+ });
163
+ }
164
+
165
+ return preparedGroups;
166
+ };
167
+
149
168
  // ==== Prepare nodes ====
150
169
 
151
170
  const prepareStorageNodeData = (node: TNodeInfo): PreparedStorageNode => {
@@ -187,17 +206,7 @@ export const prepareStorageNodesResponse = (data: TNodesInfo): PreparedStorageRe
187
206
  export const prepareStorageGroupsResponse = (data: TStorageInfo): PreparedStorageResponse => {
188
207
  const {StoragePools, StorageGroups, TotalGroups, FoundGroups} = data;
189
208
 
190
- let preparedGroups: PreparedStorageGroup[] = [];
191
-
192
- if (StorageGroups) {
193
- preparedGroups = StorageGroups.map(prepareStorageGroupDataV2);
194
- } else {
195
- StoragePools?.forEach((pool) => {
196
- pool.Groups?.forEach((group) => {
197
- preparedGroups.push(prepareStorageGroupData(group, pool.Name));
198
- });
199
- });
200
- }
209
+ const preparedGroups = prepareStorageGroups(StorageGroups, StoragePools);
201
210
 
202
211
  return {
203
212
  groups: preparedGroups,
@@ -0,0 +1,93 @@
1
+ import type {Reducer} from 'redux';
2
+
3
+ import {TENANT_OVERVIEW_TABLES_LIMIT} from '../../../../utils/constants';
4
+ import {parseQueryAPIExecuteResponse} from '../../../../utils/query';
5
+ import {createApiRequest, createRequestActionTypes} from '../../../utils';
6
+ import type {TopTablesAction, TopTablesState} from './types';
7
+
8
+ export const FETCH_TOP_TABLES = createRequestActionTypes('top-tables', 'FETCH_TOP_TABLES');
9
+ const SET_DATA_WAS_NOT_LOADED = 'top-tables/SET_DATA_WAS_NOT_LOADED';
10
+
11
+ const initialState = {
12
+ loading: false,
13
+ wasLoaded: false,
14
+ };
15
+
16
+ const getQueryText = (path: string) => {
17
+ return `
18
+ SELECT
19
+ Path, SUM(DataSize) as Size
20
+ FROM \`${path}/.sys/partition_stats\`
21
+ GROUP BY Path
22
+ ORDER BY Size DESC
23
+ LIMIT ${TENANT_OVERVIEW_TABLES_LIMIT}
24
+ `;
25
+ };
26
+
27
+ const executeTopTables: Reducer<TopTablesState, TopTablesAction> = (
28
+ state = initialState,
29
+ action,
30
+ ) => {
31
+ switch (action.type) {
32
+ case FETCH_TOP_TABLES.REQUEST: {
33
+ return {
34
+ ...state,
35
+ loading: true,
36
+ error: undefined,
37
+ };
38
+ }
39
+ case FETCH_TOP_TABLES.SUCCESS: {
40
+ return {
41
+ ...state,
42
+ data: action.data,
43
+ loading: false,
44
+ error: undefined,
45
+ wasLoaded: true,
46
+ };
47
+ }
48
+ // 401 Unauthorized error is handled by GenericAPI
49
+ case FETCH_TOP_TABLES.FAILURE: {
50
+ if (action.error?.isCancelled) {
51
+ return state;
52
+ }
53
+ return {
54
+ ...state,
55
+ error: action.error || 'Unauthorized',
56
+ loading: false,
57
+ };
58
+ }
59
+ case SET_DATA_WAS_NOT_LOADED:
60
+ return {
61
+ ...state,
62
+ wasLoaded: false,
63
+ };
64
+ default:
65
+ return state;
66
+ }
67
+ };
68
+
69
+ export const fetchTopTables = (database: string) => {
70
+ return createApiRequest({
71
+ request: window.api.sendQuery(
72
+ {
73
+ schema: 'modern',
74
+ query: getQueryText(database),
75
+ database,
76
+ action: 'execute-scan',
77
+ },
78
+ {
79
+ concurrentId: 'executeTopTables',
80
+ },
81
+ ),
82
+ actions: FETCH_TOP_TABLES,
83
+ dataHandler: parseQueryAPIExecuteResponse,
84
+ });
85
+ };
86
+
87
+ export function setDataWasNotLoaded() {
88
+ return {
89
+ type: SET_DATA_WAS_NOT_LOADED,
90
+ } as const;
91
+ }
92
+
93
+ export default executeTopTables;
@@ -0,0 +1,14 @@
1
+ import type {IQueryResult, QueryErrorResponse} from '../../../../types/store/query';
2
+ import type {ApiRequestAction} from '../../../utils';
3
+ import {FETCH_TOP_TABLES, setDataWasNotLoaded} from './executeTopTables';
4
+
5
+ export interface TopTablesState {
6
+ loading: boolean;
7
+ wasLoaded: boolean;
8
+ data?: IQueryResult;
9
+ error?: QueryErrorResponse;
10
+ }
11
+
12
+ export type TopTablesAction =
13
+ | ApiRequestAction<typeof FETCH_TOP_TABLES, IQueryResult, QueryErrorResponse>
14
+ | ReturnType<typeof setDataWasNotLoaded>;
@@ -0,0 +1,98 @@
1
+ import type {Reducer} from 'redux';
2
+
3
+ import {TENANT_OVERVIEW_TABLES_LIMIT} from '../../../../utils/constants';
4
+ import {EVersion} from '../../../../types/api/storage';
5
+ import {createApiRequest, createRequestActionTypes} from '../../../utils';
6
+ import type {StorageApiRequestParams} from '../../storage/types';
7
+ import type {
8
+ TopStorageGroupsAction,
9
+ TopStorageGroupsState,
10
+ TopStorageGroupsStateSlice,
11
+ } from './types';
12
+ import {prepareTopStorageGroupsResponse} from './utils';
13
+
14
+ export const FETCH_TOP_STORAGE_GROUPS = createRequestActionTypes(
15
+ 'topStorageGroups',
16
+ 'FETCH_TOP_STORAGE_GROUPS',
17
+ );
18
+
19
+ const SET_DATA_WAS_NOT_LOADED = 'topStorageGroups/SET_DATA_WAS_NOT_LOADED';
20
+
21
+ const initialState = {
22
+ loading: true,
23
+ wasLoaded: false,
24
+ };
25
+
26
+ const topStorageGroups: Reducer<TopStorageGroupsState, TopStorageGroupsAction> = (
27
+ state = initialState,
28
+ action,
29
+ ) => {
30
+ switch (action.type) {
31
+ case FETCH_TOP_STORAGE_GROUPS.REQUEST: {
32
+ return {
33
+ ...state,
34
+ loading: true,
35
+ };
36
+ }
37
+ case FETCH_TOP_STORAGE_GROUPS.SUCCESS: {
38
+ return {
39
+ ...state,
40
+ data: action.data.groups,
41
+ loading: false,
42
+ wasLoaded: true,
43
+ error: undefined,
44
+ };
45
+ }
46
+ case FETCH_TOP_STORAGE_GROUPS.FAILURE: {
47
+ if (action.error?.isCancelled) {
48
+ return state;
49
+ }
50
+
51
+ return {
52
+ ...state,
53
+ error: action.error,
54
+ loading: false,
55
+ wasLoaded: true,
56
+ };
57
+ }
58
+ case SET_DATA_WAS_NOT_LOADED: {
59
+ return {
60
+ ...state,
61
+ wasLoaded: false,
62
+ };
63
+ }
64
+ default:
65
+ return state;
66
+ }
67
+ };
68
+
69
+ export const getTopStorageGroups = ({
70
+ tenant,
71
+ visibleEntities = 'all',
72
+ nodeId,
73
+ sortOrder = -1,
74
+ sortValue = 'Usage',
75
+ limit = TENANT_OVERVIEW_TABLES_LIMIT,
76
+ version = EVersion.v2,
77
+ ...params
78
+ }: StorageApiRequestParams) => {
79
+ return createApiRequest({
80
+ request: window.api.getStorageInfo(
81
+ {tenant, visibleEntities, nodeId, version, sortOrder, sortValue, limit, ...params},
82
+ {concurrentId: 'getTopStorageGroups'},
83
+ ),
84
+ actions: FETCH_TOP_STORAGE_GROUPS,
85
+ dataHandler: prepareTopStorageGroupsResponse,
86
+ });
87
+ };
88
+
89
+ export const selectTopStorageGroups = (state: TopStorageGroupsStateSlice) =>
90
+ state.topStorageGroups.data;
91
+
92
+ export const setDataWasNotLoaded = () => {
93
+ return {
94
+ type: SET_DATA_WAS_NOT_LOADED,
95
+ } as const;
96
+ };
97
+
98
+ export default topStorageGroups;
@@ -0,0 +1,29 @@
1
+ import type {IResponseError} from '../../../../types/api/error';
2
+ import type {ApiRequestAction} from '../../../utils';
3
+ import type {PreparedStorageGroup} from '../../storage/types';
4
+ import {FETCH_TOP_STORAGE_GROUPS, setDataWasNotLoaded} from './topStorageGroups';
5
+
6
+ export interface TopStorageGroupsState {
7
+ loading: boolean;
8
+ wasLoaded: boolean;
9
+ data?: PreparedStorageGroup[];
10
+ error?: IResponseError;
11
+ }
12
+
13
+ export interface PreparedTopStorageGroupsResponse {
14
+ groups?: PreparedStorageGroup[];
15
+ }
16
+
17
+ type GetTopStorageGroupApiRequestAction = ApiRequestAction<
18
+ typeof FETCH_TOP_STORAGE_GROUPS,
19
+ PreparedTopStorageGroupsResponse,
20
+ IResponseError
21
+ >;
22
+
23
+ export type TopStorageGroupsAction =
24
+ | GetTopStorageGroupApiRequestAction
25
+ | ReturnType<typeof setDataWasNotLoaded>;
26
+
27
+ export interface TopStorageGroupsStateSlice {
28
+ topStorageGroups: TopStorageGroupsState;
29
+ }