Skip to content

Commit 6c5f693

Browse files
authored
Tenant Details Thunk (#2072)
Signed-off-by: Daniel Valdivia <18384552+dvaldivia@users.noreply.github.com>
1 parent 41155b3 commit 6c5f693

File tree

4 files changed

+117
-70
lines changed

4 files changed

+117
-70
lines changed

portal-ui/src/screens/Console/Tenants/ListTenants/TenantListItem.tsx

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@ import { niceBytes, niceBytesInt } from "../../../../common/utils";
2626
import InformationItem from "./InformationItem";
2727
import TenantCapacity from "./TenantCapacity";
2828
import { DrivesIcon } from "../../../../icons";
29+
import { setTenantName } from "../tenantsSlice";
30+
import { getTenantAsync } from "../thunks/tenantDetailsAsync";
31+
import { useDispatch } from "react-redux";
2932

3033
const styles = (theme: Theme) =>
3134
createStyles({
@@ -104,6 +107,7 @@ interface ITenantListItem {
104107
}
105108

106109
const TenantListItem = ({ tenant, classes }: ITenantListItem) => {
110+
const dispatch = useDispatch();
107111
const healthStatusToClass = (health_status: string) => {
108112
switch (health_status) {
109113
case "red":
@@ -174,6 +178,13 @@ const TenantListItem = ({ tenant, classes }: ITenantListItem) => {
174178
}
175179

176180
const openTenantDetails = () => {
181+
dispatch(
182+
setTenantName({
183+
name: tenant.name,
184+
namespace: tenant.namespace,
185+
})
186+
);
187+
dispatch(getTenantAsync());
177188
history.push(`/namespaces/${tenant.namespace}/tenants/${tenant.name}`);
178189
};
179190

portal-ui/src/screens/Console/Tenants/TenantDetails/TenantDetails.tsx

Lines changed: 22 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -22,16 +22,12 @@ import createStyles from "@mui/styles/createStyles";
2222
import withStyles from "@mui/styles/withStyles";
2323
import get from "lodash/get";
2424
import Grid from "@mui/material/Grid";
25-
26-
import { ITenant } from "../ListTenants/types";
2725
import {
2826
containerForHeader,
2927
pageContentStyles,
3028
tenantDetailsStyles,
3129
} from "../../Common/FormComponents/common/styleLibrary";
3230
import { AppState } from "../../../../store";
33-
import { ErrorResponseHandler } from "../../../../common/types";
34-
import api from "../../../../common/api";
3531
import PageHeader from "../../Common/PageHeader/PageHeader";
3632
import { CircleIcon, MinIOTierIconXs, TrashIcon } from "../../../../icons";
3733
import { niceBytes } from "../../../../common/utils";
@@ -46,15 +42,10 @@ import BoxIconButton from "../../Common/BoxIconButton/BoxIconButton";
4642
import withSuspense from "../../Common/Components/withSuspense";
4743
import { IAM_PAGES } from "../../../../common/SecureComponent/permissions";
4844
import { tenantIsOnline } from "../ListTenants/utils";
49-
import {
50-
setErrorSnackMessage,
51-
setSnackBarMessage,
52-
} from "../../../../systemSlice";
53-
import {
54-
setTenantDetailsLoad,
55-
setTenantInfo,
56-
setTenantName,
57-
} from "../tenantsSlice";
45+
import { setSnackBarMessage } from "../../../../systemSlice";
46+
import { setTenantDetailsLoad, setTenantName } from "../tenantsSlice";
47+
import { getTenantAsync } from "../thunks/tenantDetailsAsync";
48+
import { LinearProgress } from "@mui/material";
5849

5950
const TenantYAML = withSuspense(React.lazy(() => import("./TenantYAML")));
6051
const TenantSummary = withSuspense(React.lazy(() => import("./TenantSummary")));
@@ -188,72 +179,28 @@ const TenantDetails = ({ classes, match, history }: ITenantDetailsProps) => {
188179
const tenantNamespace = match.params["tenantNamespace"];
189180
const [deleteOpen, setDeleteOpen] = useState<boolean>(false);
190181

182+
// if the current tenant selected is not the one in the redux, reload it
191183
useEffect(() => {
192-
if (!loadingTenant) {
193-
if (
194-
tenantName !== selectedTenant ||
195-
tenantNamespace !== selectedNamespace
196-
) {
197-
dispatch(
198-
setTenantName({
199-
name: tenantName,
200-
namespace: tenantNamespace,
201-
})
202-
);
203-
dispatch(setTenantDetailsLoad(true));
204-
}
184+
if (
185+
selectedNamespace !== tenantNamespace ||
186+
selectedTenant !== tenantName
187+
) {
188+
dispatch(
189+
setTenantName({
190+
name: tenantName,
191+
namespace: tenantNamespace,
192+
})
193+
);
194+
dispatch(getTenantAsync());
205195
}
206196
}, [
207-
loadingTenant,
208197
selectedTenant,
209198
selectedNamespace,
210199
dispatch,
211200
tenantName,
212201
tenantNamespace,
213202
]);
214203

215-
useEffect(() => {
216-
if (loadingTenant) {
217-
api
218-
.invoke(
219-
"GET",
220-
`/api/v1/namespaces/${tenantNamespace}/tenants/${tenantName}`
221-
)
222-
.then((res: ITenant) => {
223-
// add computed fields
224-
const resPools = !res.pools ? [] : res.pools;
225-
226-
let totalInstances = 0;
227-
let totalVolumes = 0;
228-
let poolNamedIndex = 0;
229-
for (let pool of resPools) {
230-
const cap =
231-
pool.volumes_per_server *
232-
pool.servers *
233-
pool.volume_configuration.size;
234-
pool.label = `pool-${poolNamedIndex}`;
235-
if (pool.name === undefined || pool.name === "") {
236-
pool.name = pool.label;
237-
}
238-
pool.capacity = niceBytes(cap + "");
239-
pool.volumes = pool.servers * pool.volumes_per_server;
240-
totalInstances += pool.servers;
241-
totalVolumes += pool.volumes;
242-
poolNamedIndex += 1;
243-
}
244-
res.total_instances = totalInstances;
245-
res.total_volumes = totalVolumes;
246-
247-
dispatch(setTenantInfo(res));
248-
dispatch(setTenantDetailsLoad(false));
249-
})
250-
.catch((err: ErrorResponseHandler) => {
251-
dispatch(setErrorSnackMessage(err));
252-
dispatch(setTenantDetailsLoad(false));
253-
});
254-
}
255-
}, [loadingTenant, tenantNamespace, tenantName, dispatch]);
256-
257204
const path = get(match, "path", "/");
258205
const splitSections = path.split("/");
259206

@@ -332,6 +279,11 @@ const TenantDetails = ({ classes, match, history }: ITenantDetailsProps) => {
332279
/>
333280

334281
<PageLayout className={classes.pageContainer}>
282+
{loadingTenant && (
283+
<Grid item xs={12}>
284+
<LinearProgress />
285+
</Grid>
286+
)}
335287
<Grid item xs={12}>
336288
<ScreenTitle
337289
icon={
@@ -417,7 +369,7 @@ const TenantDetails = ({ classes, match, history }: ITenantDetailsProps) => {
417369
variant="outlined"
418370
aria-label="Refresh List"
419371
onClick={() => {
420-
dispatch(setTenantDetailsLoad(true));
372+
dispatch(getTenantAsync());
421373
}}
422374
>
423375
<span>Refresh</span> <RefreshIcon />

portal-ui/src/screens/Console/Tenants/tenantsSlice.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import get from "lodash/get";
2525
import { has } from "lodash";
2626
import { Opts } from "./ListTenants/utils";
2727
import { ITenant } from "./ListTenants/types";
28+
import { getTenantAsync } from "./thunks/tenantDetailsAsync";
2829

2930
export interface FileValue {
3031
fileName: string;
@@ -247,6 +248,19 @@ export const tenantSlice = createSlice({
247248
state.tenantDetails.poolDetailsOpen = action.payload;
248249
},
249250
},
251+
extraReducers: (builder) => {
252+
builder
253+
.addCase(getTenantAsync.pending, (state) => {
254+
state.tenantDetails.loadingTenant = true;
255+
})
256+
.addCase(getTenantAsync.rejected, (state) => {
257+
state.tenantDetails.loadingTenant = false;
258+
})
259+
.addCase(getTenantAsync.fulfilled, (state, action) => {
260+
state.tenantDetails.loadingTenant = false;
261+
state.tenantDetails.tenantInfo = action.payload;
262+
});
263+
},
250264
});
251265

252266
// Action creators are generated for each case reducer function
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
// This file is part of MinIO Console Server
2+
// Copyright (c) 2022 MinIO, Inc.
3+
//
4+
// This program is free software: you can redistribute it and/or modify
5+
// it under the terms of the GNU Affero General Public License as published by
6+
// the Free Software Foundation, either version 3 of the License, or
7+
// (at your option) any later version.
8+
//
9+
// This program is distributed in the hope that it will be useful,
10+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
// GNU Affero General Public License for more details.
13+
//
14+
// You should have received a copy of the GNU Affero General Public License
15+
// along with this program. If not, see <http://www.gnu.org/licenses/>.
16+
17+
import { createAsyncThunk } from "@reduxjs/toolkit";
18+
import { AppState } from "../../../../store";
19+
import { niceBytes } from "../../../../common/utils";
20+
import { ITenant } from "../ListTenants/types";
21+
import api from "../../../../common/api";
22+
import { ErrorResponseHandler } from "../../../../common/types";
23+
import { setErrorSnackMessage } from "../../../../systemSlice";
24+
25+
export const getTenantAsync = createAsyncThunk(
26+
"tenantDetails/getTenantAsync",
27+
async (_, { getState, rejectWithValue, dispatch }) => {
28+
const state = getState() as AppState;
29+
30+
const currentNamespace = state.tenants.tenantDetails.currentNamespace;
31+
const currentTenant = state.tenants.tenantDetails.currentTenant;
32+
33+
return api
34+
.invoke(
35+
"GET",
36+
`/api/v1/namespaces/${currentNamespace}/tenants/${currentTenant}`
37+
)
38+
.then((res: ITenant) => {
39+
// add computed fields
40+
const resPools = !res.pools ? [] : res.pools;
41+
42+
let totalInstances = 0;
43+
let totalVolumes = 0;
44+
let poolNamedIndex = 0;
45+
for (let pool of resPools) {
46+
const cap =
47+
pool.volumes_per_server *
48+
pool.servers *
49+
pool.volume_configuration.size;
50+
pool.label = `pool-${poolNamedIndex}`;
51+
if (pool.name === undefined || pool.name === "") {
52+
pool.name = pool.label;
53+
}
54+
pool.capacity = niceBytes(cap + "");
55+
pool.volumes = pool.servers * pool.volumes_per_server;
56+
totalInstances += pool.servers;
57+
totalVolumes += pool.volumes;
58+
poolNamedIndex += 1;
59+
}
60+
res.total_instances = totalInstances;
61+
res.total_volumes = totalVolumes;
62+
63+
return res;
64+
})
65+
.catch((err: ErrorResponseHandler) => {
66+
dispatch(setErrorSnackMessage(err));
67+
return rejectWithValue(err);
68+
});
69+
}
70+
);

0 commit comments

Comments
 (0)