ydb-embedded-ui 4.18.0 → 4.19.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 (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
+ }