Skip to content

Commit 75cd907

Browse files
authoredAug 24, 2023
refactor: migrate TenantOverview to ts (#519)
1 parent c526471 commit 75cd907

File tree

14 files changed

+238
-248
lines changed

14 files changed

+238
-248
lines changed
 

‎src/containers/Tenant/Diagnostics/DetailedOverview/DetailedOverview.tsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,7 @@ import type {EPathType} from '../../../../types/api/schema';
88
import {Icon} from '../../../../components/Icon';
99
import Overview from '../Overview/Overview';
1010
import {Healthcheck} from '../Healthcheck';
11-
//@ts-ignore
12-
import TenantOverview from '../TenantOverview/TenantOverview';
11+
import {TenantOverview} from '../TenantOverview/TenantOverview';
1312

1413
import './DetailedOverview.scss';
1514

‎src/containers/Tenant/Diagnostics/TenantOverview/TenantOverview.js

Lines changed: 0 additions & 213 deletions
This file was deleted.
Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
import cn from 'bem-cn-lite';
2+
import {useCallback} from 'react';
3+
import {useDispatch} from 'react-redux';
4+
5+
import {Loader} from '@gravity-ui/uikit';
6+
7+
import {InfoViewer} from '../../../../components/InfoViewer';
8+
import {PoolUsage} from '../../../../components/PoolUsage/PoolUsage';
9+
import {Tablet} from '../../../../components/Tablet';
10+
import EntityStatus from '../../../../components/EntityStatus/EntityStatus';
11+
import {formatCPU} from '../../../../utils';
12+
import {TABLET_STATES, TENANT_DEFAULT_TITLE} from '../../../../utils/constants';
13+
import {bytesToGB} from '../../../../utils/utils';
14+
import {mapDatabaseTypeToDBName} from '../../utils/schema';
15+
import {useAutofetcher, useTypedSelector} from '../../../../utils/hooks';
16+
import {ETabletVolatileState} from '../../../../types/api/tenant';
17+
import {getTenantInfo, setDataWasNotLoaded} from '../../../../store/reducers/tenant/tenant';
18+
19+
import i18n from './i18n';
20+
import './TenantOverview.scss';
21+
22+
const b = cn('tenant-overview');
23+
24+
interface TenantOverviewProps {
25+
tenantName: string;
26+
additionalTenantInfo?: any;
27+
}
28+
29+
export function TenantOverview({tenantName, additionalTenantInfo}: TenantOverviewProps) {
30+
const {tenant, loading, wasLoaded} = useTypedSelector((state) => state.tenant);
31+
const {autorefresh} = useTypedSelector((state) => state.schema);
32+
const dispatch = useDispatch();
33+
const fetchTenant = useCallback(
34+
(isBackground = true) => {
35+
if (!isBackground) {
36+
dispatch(setDataWasNotLoaded());
37+
}
38+
dispatch(getTenantInfo({path: tenantName}));
39+
},
40+
[dispatch, tenantName],
41+
);
42+
43+
useAutofetcher(fetchTenant, [fetchTenant], autorefresh);
44+
45+
const {
46+
Metrics = {},
47+
PoolStats,
48+
StateStats = [],
49+
MemoryUsed,
50+
Name,
51+
State,
52+
CoresUsed,
53+
StorageGroups,
54+
StorageAllocatedSize,
55+
Type,
56+
SystemTablets,
57+
} = tenant || {};
58+
59+
const tenantType = mapDatabaseTypeToDBName(Type);
60+
const memoryRaw = MemoryUsed ?? Metrics.Memory;
61+
62+
const memory = (memoryRaw && bytesToGB(memoryRaw)) || i18n('no-data');
63+
const storage = (Metrics.Storage && bytesToGB(Metrics.Storage)) || i18n('no-data');
64+
const storageGroups = StorageGroups ?? i18n('no-data');
65+
const blobStorage =
66+
(StorageAllocatedSize && bytesToGB(StorageAllocatedSize)) || i18n('no-data');
67+
const storageEfficiency =
68+
Metrics.Storage && StorageAllocatedSize
69+
? `${((parseInt(Metrics.Storage) * 100) / parseInt(StorageAllocatedSize)).toFixed(2)}%`
70+
: i18n('no-data');
71+
72+
const cpuRaw = CoresUsed !== undefined ? Number(CoresUsed) * 1_000_000 : Metrics.CPU;
73+
74+
const cpu = formatCPU(cpuRaw);
75+
76+
const metricsInfo = [
77+
{label: 'Type', value: Type},
78+
{label: 'Memory', value: memory},
79+
{label: 'CPU', value: cpu},
80+
{label: 'Tablet storage', value: storage},
81+
{label: 'Storage groups', value: storageGroups},
82+
{label: 'Blob storage', value: blobStorage},
83+
{label: 'Storage efficiency', value: storageEfficiency},
84+
];
85+
86+
const tabletsInfo = StateStats.filter(
87+
(item): item is {VolatileState: ETabletVolatileState; Count: number} => {
88+
return item.VolatileState !== undefined && item.Count !== undefined;
89+
},
90+
).map((info) => {
91+
return {label: TABLET_STATES[info.VolatileState], value: info.Count};
92+
});
93+
94+
const renderName = () => {
95+
return (
96+
<div className={b('tenant-name-wrapper')}>
97+
<EntityStatus
98+
status={State}
99+
name={Name || TENANT_DEFAULT_TITLE}
100+
withLeftTrim
101+
hasClipboardButton={Boolean(tenant)}
102+
clipboardButtonAlwaysVisible
103+
/>
104+
</div>
105+
);
106+
};
107+
108+
if (loading && !wasLoaded) {
109+
return (
110+
<div className={b('loader')}>
111+
<Loader size="m" />
112+
</div>
113+
);
114+
}
115+
116+
return (
117+
<div className={b()}>
118+
<div className={b('top-label')}>{tenantType}</div>
119+
<div className={b('top')}>
120+
{renderName()}
121+
{tenant && additionalTenantInfo && additionalTenantInfo(tenant.Name, tenant.Type)}
122+
</div>
123+
<div className={b('system-tablets')}>
124+
{SystemTablets &&
125+
SystemTablets.map((tablet, tabletIndex) => (
126+
<Tablet key={tabletIndex} tablet={tablet} tenantName={Name} />
127+
))}
128+
</div>
129+
<div className={b('common-info')}>
130+
<div>
131+
<div className={b('section-title')}>{i18n('title.pools')}</div>
132+
{PoolStats ? (
133+
<div className={b('section', {pools: true})}>
134+
{PoolStats.map((pool, poolIndex) => (
135+
<PoolUsage key={poolIndex} data={pool} />
136+
))}
137+
</div>
138+
) : (
139+
<div className="error">{i18n('no-pools-data')}</div>
140+
)}
141+
</div>
142+
<InfoViewer
143+
title={i18n('title.metrics')}
144+
className={b('section', {metrics: true})}
145+
info={metricsInfo}
146+
/>
147+
148+
<div className={b('section')}>
149+
<InfoViewer info={tabletsInfo} title="Tablets" />
150+
</div>
151+
</div>
152+
</div>
153+
);
154+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"no-data": "No data",
3+
"no-pools-data": "No pools data",
4+
5+
"title.pools": "Pools",
6+
"title.metrics": "Metrics"
7+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import {i18n, Lang} from '../../../../../utils/i18n';
2+
3+
import en from './en.json';
4+
import ru from './ru.json';
5+
6+
const COMPONENT = 'ydb-diagnostics-tenant-overview';
7+
8+
i18n.registerKeyset(Lang.En, COMPONENT, en);
9+
i18n.registerKeyset(Lang.Ru, COMPONENT, ru);
10+
11+
export default i18n.keyset(COMPONENT);
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"no-data": "Нет данных",
3+
"no-pools-data": "Нет данных о пулах",
4+
5+
"title.pools": "Пулы",
6+
"title.metrics": "Метрики"
7+
}

‎src/containers/Tenants/Tenants.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,9 @@ export const Tenants = ({additionalTenantsProps}: TenantsProps) => {
106106
backend,
107107
})}
108108
/>
109-
{additionalTenantsProps?.getMonitoringLink?.(row.Name, row.Type)}
109+
{row.Name &&
110+
row.Type &&
111+
additionalTenantsProps?.getMonitoringLink?.(row.Name, row.Type)}
110112
</div>
111113
);
112114
},

‎src/services/api.ts

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -76,12 +76,16 @@ export class YdbEmbeddedAPI extends AxiosWrapper {
7676
cluster_name: clusterName,
7777
});
7878
}
79-
getTenantInfo({path}: {path: string}) {
80-
return this.get<TTenantInfo>(this.getPath('/viewer/json/tenantinfo'), {
81-
path,
82-
tablets: true,
83-
storage: true,
84-
});
79+
getTenantInfo({path}: {path: string}, {concurrentId}: AxiosOptions = {}) {
80+
return this.get<TTenantInfo>(
81+
this.getPath('/viewer/json/tenantinfo'),
82+
{
83+
path,
84+
tablets: true,
85+
storage: true,
86+
},
87+
{concurrentId: concurrentId || `getTenantInfo|${path}`},
88+
);
8589
}
8690
getNodes(
8791
{

‎src/store/reducers/tenant/tenant.ts

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ const SET_QUERY_TAB = 'tenant/SET_QUERY_TAB';
2020
const SET_DIAGNOSTICS_TAB = 'tenant/SET_DIAGNOSTICS_TAB';
2121
const SET_SUMMARY_TAB = 'tenant/SET_SUMMARY_TAB';
2222
const CLEAR_TENANT = 'tenant/CLEAR_TENANT';
23+
const SET_DATA_WAS_NOT_LOADED = 'tenant/SET_DATA_WAS_NOT_LOADED';
2324

2425
const initialState = {loading: false, wasLoaded: false};
2526

@@ -43,6 +44,10 @@ const tenantReducer: Reducer<TenantState, TenantAction> = (state = initialState,
4344
}
4445

4546
case FETCH_TENANT.FAILURE: {
47+
if (action.error?.isCancelled) {
48+
return state;
49+
}
50+
4651
return {
4752
...state,
4853
error: action.error,
@@ -84,14 +89,21 @@ const tenantReducer: Reducer<TenantState, TenantAction> = (state = initialState,
8489
};
8590
}
8691

92+
case SET_DATA_WAS_NOT_LOADED: {
93+
return {
94+
...state,
95+
wasLoaded: false,
96+
};
97+
}
98+
8799
default:
88100
return state;
89101
}
90102
};
91103

92104
export const getTenantInfo = ({path}: {path: string}) => {
93105
return createApiRequest({
94-
request: window.api.getTenantInfo({path}),
106+
request: window.api.getTenantInfo({path}, {concurrentId: 'getTenantInfo'}),
95107
actions: FETCH_TENANT,
96108
dataHandler: (tenantData): TTenant | undefined => {
97109
return tenantData.TenantInfo?.[0];
@@ -131,4 +143,10 @@ export function setSummaryTab(tab: TenantSummaryTab) {
131143
} as const;
132144
}
133145

146+
export const setDataWasNotLoaded = () => {
147+
return {
148+
type: SET_DATA_WAS_NOT_LOADED,
149+
} as const;
150+
};
151+
134152
export default tenantReducer;

‎src/store/reducers/tenant/types.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import {
1616
setQueryTab,
1717
setSummaryTab,
1818
setTenantPage,
19+
setDataWasNotLoaded,
1920
} from './tenant';
2021

2122
export type TenantPage = ValueOf<typeof TENANT_PAGES_IDS>;
@@ -41,4 +42,5 @@ export type TenantAction =
4142
| ReturnType<typeof setTenantPage>
4243
| ReturnType<typeof setQueryTab>
4344
| ReturnType<typeof setDiagnosticsTab>
44-
| ReturnType<typeof setSummaryTab>;
45+
| ReturnType<typeof setSummaryTab>
46+
| ReturnType<typeof setDataWasNotLoaded>;

‎src/store/reducers/tenants/selectors.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ const filterTenantsByProblems = (tenants: PreparedTenant[], problemFilter: Probl
2424
const filteredTenantsBySearch = (tenants: PreparedTenant[], searchQuery: string) => {
2525
return tenants.filter((item) => {
2626
const re = new RegExp(escapeRegExp(searchQuery), 'i');
27-
return re.test(item.Name) || re.test(item.controlPlaneName);
27+
return re.test(item.Name || '') || re.test(item.controlPlaneName);
2828
});
2929
};
3030

‎src/store/reducers/tenants/utils.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@ import type {TTenant} from '../../../types/api/tenant';
22
import {isNumeric} from '../../../utils/utils';
33

44
const getControlPlaneValue = (tenant: TTenant) => {
5-
const parts = tenant.Name.split('/');
6-
const defaultValue = parts.length ? parts[parts.length - 1] : '—';
5+
const parts = tenant.Name?.split('/');
6+
const defaultValue = parts?.length ? parts[parts.length - 1] : '—';
77
const controlPlaneName = tenant.ControlPlane?.name;
88

99
return controlPlaneName ?? defaultValue;

‎src/types/api/tenant.ts

Lines changed: 19 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -22,38 +22,38 @@ export interface TTenantInfo {
2222
}
2323

2424
export interface TTenant {
25-
Name: string;
26-
Id: string;
27-
Type: ETenantType;
28-
State: State;
25+
Name?: string;
26+
Id?: string;
27+
Type?: ETenantType;
28+
State?: State;
2929
StateStats?: THiveDomainStatsStateCount[];
30-
Metrics: TMetrics;
30+
Metrics?: TMetrics;
3131
NodeIds?: number[];
32-
AliveNodes: number;
33-
Resources: TTenantResources;
32+
AliveNodes?: number;
33+
Resources?: TTenantResources;
3434
/** uint64 */
35-
CreateTime: string;
36-
Owner: string;
35+
CreateTime?: string;
36+
Owner?: string;
3737
Users?: string[];
3838
PoolStats?: TPoolStats[];
39-
UserAttributes: Record<string, string>;
40-
Overall: EFlag;
39+
UserAttributes?: Record<string, string>;
40+
Overall?: EFlag;
4141
SystemTablets?: TTabletStateInfo[];
42-
ResourceId: string;
42+
ResourceId?: string;
4343
Tablets?: TTabletStateInfo[];
4444
/** uint64 */
45-
StorageAllocatedSize: string;
45+
StorageAllocatedSize?: string;
4646
/** uint64 */
47-
StorageMinAvailableSize: string;
47+
StorageMinAvailableSize?: string;
4848
Nodes?: TSystemStateInfo[];
4949
/** uint64 */
50-
MemoryUsed: string;
50+
MemoryUsed?: string;
5151
/** uint64 */
52-
MemoryLimit: string;
52+
MemoryLimit?: string;
5353
/** double */
54-
CoresUsed: number;
54+
CoresUsed?: number;
5555
/** uint64 */
56-
StorageGroups: string;
56+
StorageGroups?: string;
5757

5858
MonitoringEndpoint?: string; // additional
5959
ControlPlane?: ControlPlane; // additional
@@ -139,11 +139,10 @@ enum State {
139139
'CONFIGURING' = 'CONFIGURING',
140140
}
141141

142-
enum ETabletVolatileState {
142+
export enum ETabletVolatileState {
143143
'TABLET_VOLATILE_STATE_UNKNOWN' = 'TABLET_VOLATILE_STATE_UNKNOWN',
144144
'TABLET_VOLATILE_STATE_STOPPED' = 'TABLET_VOLATILE_STATE_STOPPED',
145145
'TABLET_VOLATILE_STATE_BOOTING' = 'TABLET_VOLATILE_STATE_BOOTING',
146146
'TABLET_VOLATILE_STATE_STARTING' = 'TABLET_VOLATILE_STATE_STARTING',
147147
'TABLET_VOLATILE_STATE_RUNNING' = 'TABLET_VOLATILE_STATE_RUNNING',
148-
'_TABLET_VOLATILE_STATE_BLOCKED' = '_TABLET_VOLATILE_STATE_BLOCKED',
149148
}

‎src/utils/constants.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ export const TABLET_STATES = {
2525
TABLET_VOLATILE_STATE_BOOTING: 'booting',
2626
TABLET_VOLATILE_STATE_STARTING: 'starting',
2727
TABLET_VOLATILE_STATE_RUNNING: 'running',
28-
TABLET_VOLATILE_STATE_BLOCKED: 'blocked',
2928
};
3029

3130
export const TABLET_COLORS = {
@@ -81,6 +80,7 @@ export const COLORS_PRIORITY = {
8180
// ==== Titles ====
8281
export const DEVELOPER_UI_TITLE = 'Developer UI';
8382
export const CLUSTER_DEFAULT_TITLE = 'Cluster';
83+
export const TENANT_DEFAULT_TITLE = 'Database';
8484

8585
// ==== Settings ====
8686
export const THEME_KEY = 'theme';

0 commit comments

Comments
 (0)
Please sign in to comment.