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.
- package/CHANGELOG.md +15 -0
- package/dist/components/FullNodeViewer/FullNodeViewer.scss +1 -1
- package/dist/components/FullNodeViewer/FullNodeViewer.tsx +13 -4
- package/dist/components/ProgressViewer/ProgressViewer.tsx +11 -5
- package/dist/containers/Nodes/getNodesColumns.tsx +1 -0
- package/dist/containers/Storage/PDiskPopup/PDiskPopup.tsx +1 -1
- package/dist/containers/Storage/StorageGroups/StorageGroups.tsx +3 -232
- package/dist/containers/Storage/StorageGroups/getStorageGroupsColumns.tsx +278 -0
- package/dist/containers/Tenant/Diagnostics/TenantOverview/MetricsCards/MetricsCards.tsx +2 -3
- package/dist/containers/Tenant/Diagnostics/TenantOverview/TenantOverview.tsx +22 -6
- package/dist/containers/Tenant/Diagnostics/TenantOverview/TenantStorage/TenantStorage.scss +41 -0
- package/dist/containers/Tenant/Diagnostics/TenantOverview/TenantStorage/TenantStorage.tsx +68 -0
- package/dist/containers/Tenant/Diagnostics/TenantOverview/TenantStorage/TopGroups.tsx +76 -0
- package/dist/containers/Tenant/Diagnostics/TenantOverview/TenantStorage/TopTables.tsx +105 -0
- package/dist/containers/Tenant/Diagnostics/TenantOverview/i18n/en.json +2 -0
- package/dist/containers/Tenant/Diagnostics/TenantOverview/i18n/ru.json +2 -0
- package/dist/containers/Versions/NodesTable/NodesTable.tsx +1 -0
- package/dist/store/reducers/index.ts +4 -0
- package/dist/store/reducers/storage/utils.ts +20 -11
- package/dist/store/reducers/tenantOverview/executeTopTables/executeTopTables.ts +93 -0
- package/dist/store/reducers/tenantOverview/executeTopTables/types.ts +14 -0
- package/dist/store/reducers/tenantOverview/topStorageGroups/topStorageGroups.ts +98 -0
- package/dist/store/reducers/tenantOverview/topStorageGroups/types.ts +29 -0
- package/dist/store/reducers/tenantOverview/topStorageGroups/utils.ts +20 -0
- package/dist/store/reducers/tenants/utils.ts +28 -18
- package/dist/styles/constants.scss +4 -0
- package/dist/types/additionalProps.ts +1 -1
- package/dist/types/api/storage.ts +1 -1
- package/dist/types/api/tenant.ts +21 -8
- package/dist/utils/bytesParsers/__test__/formatBytes.test.ts +10 -1
- package/dist/utils/bytesParsers/formatBytes.ts +3 -3
- package/dist/utils/constants.ts +8 -0
- package/dist/utils/dataFormatters/__test__/roundToSignificant.test.ts +22 -0
- package/dist/utils/dataFormatters/dataFormatters.ts +42 -20
- 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 {
|
74
|
-
|
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
|
-
|
81
|
-
storageUsed:
|
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
|
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,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
|
-
|
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
|
+
}
|