Skip to content

Commit 65e25f8

Browse files
authored
feat(healthcheck): use rtk query (#800)
1 parent 876510c commit 65e25f8

File tree

9 files changed

+76
-169
lines changed

9 files changed

+76
-169
lines changed

src/containers/Tenant/Diagnostics/TenantOverview/Healthcheck/HealthcheckDetails.tsx

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ import React from 'react';
33
import {ResponseError} from '../../../../../components/Errors/ResponseError';
44
import {Loader} from '../../../../../components/Loader';
55
import type {IssuesTree} from '../../../../../store/reducers/healthcheckInfo/types';
6-
import type {IResponseError} from '../../../../../types/api/error';
76
import {cn} from '../../../../../utils/cn';
87

98
import IssueTree from './IssuesViewer/IssueTree';
@@ -16,19 +15,18 @@ const b = cn('healthcheck');
1615
interface HealthcheckDetailsProps {
1716
issueTrees?: IssuesTree[];
1817
loading?: boolean;
19-
wasLoaded?: boolean;
20-
error?: IResponseError;
18+
error?: unknown;
2119
}
2220

2321
export function HealthcheckDetails(props: HealthcheckDetailsProps) {
24-
const {issueTrees, loading, wasLoaded, error} = props;
22+
const {issueTrees, loading, error} = props;
2523

2624
const renderContent = () => {
2725
if (error) {
2826
return <ResponseError error={error} defaultMessage={i18n('no-data')} />;
2927
}
3028

31-
if (loading && !wasLoaded) {
29+
if (loading) {
3230
return <Loader size="m" />;
3331
}
3432

src/containers/Tenant/Diagnostics/TenantOverview/Healthcheck/HealthcheckPreview.tsx

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import {EntityStatus} from '../../../../../components/EntityStatus/EntityStatus'
77
import {ResponseError} from '../../../../../components/Errors/ResponseError';
88
import {Loader} from '../../../../../components/Loader';
99
import {hcStatusToColorFlag} from '../../../../../store/reducers/healthcheckInfo/utils';
10-
import type {IResponseError} from '../../../../../types/api/error';
1110
import type {SelfCheckResult, StatusFlag} from '../../../../../types/api/healthcheck';
1211
import {cn} from '../../../../../utils/cn';
1312

@@ -23,19 +22,18 @@ interface HealthcheckPreviewProps {
2322
selfCheckResult: SelfCheckResult;
2423
issuesStatistics?: [StatusFlag, number][];
2524
loading?: boolean;
26-
wasLoaded?: boolean;
2725
onUpdate: VoidFunction;
28-
error?: IResponseError;
26+
error?: unknown;
2927
active?: boolean;
3028
}
3129

3230
export function HealthcheckPreview(props: HealthcheckPreviewProps) {
33-
const {selfCheckResult, issuesStatistics, loading, wasLoaded, onUpdate, error, active} = props;
31+
const {selfCheckResult, issuesStatistics, loading, onUpdate, error, active} = props;
3432

3533
const renderHeader = () => {
3634
const modifier = selfCheckResult.toLowerCase();
3735

38-
if (loading && !wasLoaded) {
36+
if (loading) {
3937
return null;
4038
}
4139

@@ -59,7 +57,7 @@ export function HealthcheckPreview(props: HealthcheckPreviewProps) {
5957
return <ResponseError error={error} defaultMessage={i18n('no-data')} />;
6058
}
6159

62-
if (loading && !wasLoaded) {
60+
if (loading) {
6361
return <Loader size="m" />;
6462
}
6563

src/containers/Tenant/Diagnostics/TenantOverview/MetricsCards/MetricsCards.tsx

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ import {
2424
memoryUsageToStatus,
2525
storageUsageToStatus,
2626
} from '../../../../../store/reducers/tenants/utils';
27-
import type {IResponseError} from '../../../../../types/api/error';
2827
import type {SelfCheckResult, StatusFlag} from '../../../../../types/api/healthcheck';
2928
import {cn} from '../../../../../utils/cn';
3029
import {formatStorageValues} from '../../../../../utils/dataFormatters/dataFormatters';
@@ -60,7 +59,7 @@ interface MetricsCardsProps {
6059
fetchHealthcheck: VoidFunction;
6160
healthcheckLoading?: boolean;
6261
healthCheckWasLoaded?: boolean;
63-
healthcheckError?: IResponseError;
62+
healthcheckError?: unknown;
6463
}
6564

6665
export function MetricsCards({
@@ -72,7 +71,6 @@ export function MetricsCards({
7271
selfCheckResult,
7372
fetchHealthcheck,
7473
healthcheckLoading,
75-
healthCheckWasLoaded,
7674
healthcheckError,
7775
}: MetricsCardsProps) {
7876
const location = useLocation();
@@ -136,7 +134,6 @@ export function MetricsCards({
136134
issuesStatistics={issuesStatistics}
137135
onUpdate={fetchHealthcheck}
138136
loading={healthcheckLoading}
139-
wasLoaded={healthCheckWasLoaded}
140137
error={healthcheckError}
141138
active={metricsTab === TENANT_METRICS_TABS_IDS.healthcheck}
142139
/>

src/containers/Tenant/Diagnostics/TenantOverview/TenantOverview.tsx

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -47,11 +47,10 @@ export function TenantOverview({
4747
issueTrees,
4848
issuesStatistics,
4949
selfCheckResult,
50-
fetchHealthcheck,
5150
loading: healthcheckLoading,
52-
wasLoaded: healthCheckWasLoaded,
5351
error: healthcheckError,
54-
} = useHealthcheck(tenantName);
52+
refetch: fetchHealthcheck,
53+
} = useHealthcheck(tenantName, {autorefresh});
5554

5655
const fetchTenant = React.useCallback(
5756
(isBackground = true) => {
@@ -66,9 +65,8 @@ export function TenantOverview({
6665
useAutofetcher(
6766
(isBackground) => {
6867
fetchTenant(isBackground);
69-
fetchHealthcheck(isBackground);
7068
},
71-
[fetchTenant, fetchHealthcheck],
69+
[fetchTenant],
7270
autorefresh,
7371
);
7472

@@ -125,7 +123,6 @@ export function TenantOverview({
125123
<HealthcheckDetails
126124
issueTrees={issueTrees}
127125
loading={healthcheckLoading}
128-
wasLoaded={healthCheckWasLoaded}
129126
error={healthcheckError}
130127
/>
131128
);
@@ -161,7 +158,6 @@ export function TenantOverview({
161158
selfCheckResult={selfCheckResult}
162159
fetchHealthcheck={fetchHealthcheck}
163160
healthcheckLoading={healthcheckLoading}
164-
healthCheckWasLoaded={healthCheckWasLoaded}
165161
healthcheckError={healthcheckError}
166162
/>
167163
</div>
Lines changed: 21 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,53 +1,45 @@
1-
import React from 'react';
2-
1+
import {DEFAULT_POLLING_INTERVAL} from '../../../../lib';
32
import {
4-
getHealthcheckInfo,
3+
healthcheckApi,
54
selectIssuesStatistics,
65
selectIssuesTrees,
7-
setDataWasNotLoaded,
86
} from '../../../../store/reducers/healthcheckInfo/healthcheckInfo';
97
import type {IssuesTree} from '../../../../store/reducers/healthcheckInfo/types';
10-
import type {IResponseError} from '../../../../types/api/error';
118
import {SelfCheckResult} from '../../../../types/api/healthcheck';
129
import type {StatusFlag} from '../../../../types/api/healthcheck';
13-
import {useTypedDispatch, useTypedSelector} from '../../../../utils/hooks';
10+
import {useTypedSelector} from '../../../../utils/hooks';
1411

1512
interface HealthcheckParams {
1613
issueTrees: IssuesTree[];
1714
issuesStatistics: [StatusFlag, number][];
18-
fetchHealthcheck: (isBackground?: boolean) => void;
1915
loading: boolean;
20-
wasLoaded: boolean;
21-
error?: IResponseError;
16+
error?: unknown;
17+
refetch: () => void;
2218
selfCheckResult: SelfCheckResult;
2319
}
2420

25-
export const useHealthcheck = (tenantName: string): HealthcheckParams => {
26-
const dispatch = useTypedDispatch();
27-
28-
const {data, loading, wasLoaded, error} = useTypedSelector((state) => state.healthcheckInfo);
21+
export const useHealthcheck = (
22+
tenantName: string,
23+
{autorefresh}: {autorefresh?: boolean} = {},
24+
): HealthcheckParams => {
25+
const {
26+
currentData: data,
27+
isFetching,
28+
error,
29+
refetch,
30+
} = healthcheckApi.useGetHealthcheckInfoQuery(tenantName, {
31+
pollingInterval: autorefresh ? DEFAULT_POLLING_INTERVAL : 0,
32+
});
2933
const selfCheckResult = data?.self_check_result || SelfCheckResult.UNSPECIFIED;
30-
const issuesStatistics = useTypedSelector(selectIssuesStatistics);
31-
const issueTrees = useTypedSelector(selectIssuesTrees);
32-
33-
const fetchHealthcheck = React.useCallback(
34-
(isBackground = true) => {
35-
if (!isBackground) {
36-
dispatch(setDataWasNotLoaded());
37-
}
38-
39-
dispatch(getHealthcheckInfo(tenantName));
40-
},
41-
[dispatch, tenantName],
42-
);
34+
const issuesStatistics = useTypedSelector((state) => selectIssuesStatistics(state, tenantName));
35+
const issueTrees = useTypedSelector((state) => selectIssuesTrees(state, tenantName));
4336

4437
return {
4538
issueTrees,
4639
issuesStatistics,
47-
fetchHealthcheck,
48-
loading,
49-
wasLoaded,
40+
loading: data === undefined && isFetching,
5041
error,
42+
refetch,
5143
selfCheckResult,
5244
};
5345
};

src/services/api.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -385,11 +385,11 @@ export class YdbEmbeddedAPI extends AxiosWrapper {
385385
{concurrentId: concurrentId || 'getHotKeys'},
386386
);
387387
}
388-
getHealthcheckInfo(database: string, {concurrentId}: AxiosOptions = {}) {
388+
getHealthcheckInfo(database: string, {concurrentId, signal}: AxiosOptions = {}) {
389389
return this.get<HealthCheckAPIResponse>(
390390
this.getPath('/viewer/json/healthcheck?merge_records=true'),
391391
{tenant: database},
392-
{concurrentId},
392+
{concurrentId, requestConfig: {signal}},
393393
);
394394
}
395395
evictVDisk({
Lines changed: 41 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -1,68 +1,26 @@
11
import {createSelector} from '@reduxjs/toolkit';
2-
import type {Reducer, Selector} from '@reduxjs/toolkit';
32

43
import type {IssueLog, StatusFlag} from '../../../types/api/healthcheck';
5-
import {createApiRequest, createRequestActionTypes} from '../../utils';
6-
7-
import type {
8-
HealthCheckInfoAction,
9-
HealthcheckInfoRootStateSlice,
10-
HealthcheckInfoState,
11-
IssuesTree,
12-
} from './types';
13-
14-
export const FETCH_HEALTHCHECK = createRequestActionTypes('cluster', 'FETCH_HEALTHCHECK');
15-
16-
const SET_DATA_WAS_NOT_LOADED = 'healthcheckInfo/SET_DATA_WAS_NOT_LOADED';
17-
18-
const initialState = {loading: false, wasLoaded: false};
19-
20-
const healthcheckInfo: Reducer<HealthcheckInfoState, HealthCheckInfoAction> = function (
21-
state = initialState,
22-
action,
23-
) {
24-
switch (action.type) {
25-
case FETCH_HEALTHCHECK.REQUEST: {
26-
return {
27-
...state,
28-
loading: true,
29-
};
30-
}
31-
case FETCH_HEALTHCHECK.SUCCESS: {
32-
const {data} = action;
33-
34-
return {
35-
...state,
36-
data,
37-
wasLoaded: true,
38-
loading: false,
39-
error: undefined,
40-
};
41-
}
42-
case FETCH_HEALTHCHECK.FAILURE: {
43-
if (action.error?.isCancelled) {
44-
return state;
45-
}
46-
47-
return {
48-
...state,
49-
error: action.error,
50-
loading: false,
51-
wasLoaded: true,
52-
data: undefined,
53-
};
54-
}
55-
56-
case SET_DATA_WAS_NOT_LOADED: {
57-
return {
58-
...state,
59-
wasLoaded: false,
60-
};
61-
}
62-
default:
63-
return state;
64-
}
65-
};
4+
import type {RootState} from '../../defaultStore';
5+
import {api} from '../api';
6+
7+
import type {IssuesTree} from './types';
8+
9+
export const healthcheckApi = api.injectEndpoints({
10+
endpoints: (builder) => ({
11+
getHealthcheckInfo: builder.query({
12+
queryFn: async (database: string, {signal}) => {
13+
try {
14+
const data = await window.api.getHealthcheckInfo(database, {signal});
15+
return {data};
16+
} catch (error) {
17+
return {error};
18+
}
19+
},
20+
providesTags: ['All'],
21+
}),
22+
}),
23+
});
6624

6725
const mapStatusToPriority: Partial<Record<StatusFlag, number>> = {
6826
RED: 0,
@@ -133,33 +91,28 @@ const getIssuesStatistics = (data: IssueLog[]): [StatusFlag, number][] => {
13391
});
13492
};
13593

136-
const getIssuesLog = (state: HealthcheckInfoRootStateSlice) =>
137-
state.healthcheckInfo.data?.issue_log;
94+
const createGetHealthcheckInfoSelector = createSelector(
95+
(database: string) => database,
96+
(database) => healthcheckApi.endpoints.getHealthcheckInfo.select(database),
97+
);
13898

139-
export const selectIssuesTreesRoots: Selector<HealthcheckInfoRootStateSlice, IssueLog[]> =
140-
createSelector(getIssuesLog, (issues = []) => getRoots(issues));
99+
const getIssuesLog = createSelector(
100+
(state: RootState) => state,
101+
(_state: RootState, database: string) => createGetHealthcheckInfoSelector(database),
102+
(state: RootState, selectGetPost) => selectGetPost(state).data?.issue_log || [],
103+
);
141104

142-
export const selectIssuesTrees: Selector<HealthcheckInfoRootStateSlice, IssuesTree[]> =
143-
createSelector([getIssuesLog, selectIssuesTreesRoots], (data = [], roots = []) => {
144-
return getInvertedConsequencesTree({data, roots});
145-
});
105+
export const selectIssuesTreesRoots = createSelector(getIssuesLog, (issues = []) =>
106+
getRoots(issues),
107+
);
146108

147-
export const selectIssuesStatistics: Selector<
148-
HealthcheckInfoRootStateSlice,
149-
[StatusFlag, number][]
150-
> = createSelector(getIssuesLog, (issues = []) => getIssuesStatistics(issues));
151-
152-
export function getHealthcheckInfo(database: string) {
153-
return createApiRequest({
154-
request: window.api.getHealthcheckInfo(database, {concurrentId: 'getHealthcheckInfo'}),
155-
actions: FETCH_HEALTHCHECK,
156-
});
157-
}
158-
159-
export const setDataWasNotLoaded = () => {
160-
return {
161-
type: SET_DATA_WAS_NOT_LOADED,
162-
} as const;
163-
};
109+
export const selectIssuesTrees = createSelector(
110+
[getIssuesLog, selectIssuesTreesRoots],
111+
(data = [], roots = []) => {
112+
return getInvertedConsequencesTree({data, roots});
113+
},
114+
);
164115

165-
export default healthcheckInfo;
116+
export const selectIssuesStatistics = createSelector(getIssuesLog, (issues = []) =>
117+
getIssuesStatistics(issues),
118+
);

0 commit comments

Comments
 (0)