Added quota metric to buckets list & objects list (#1595)

Signed-off-by: Benjamin Perez <benjamin@bexsoft.net>

Co-authored-by: Benjamin Perez <benjamin@bexsoft.net>
This commit is contained in:
Alex
2022-02-16 19:58:08 -07:00
committed by GitHub
parent 3606870565
commit 10539929e1
3 changed files with 39 additions and 47 deletions

View File

@@ -13,7 +13,8 @@
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
import React from "react";
import React, {Fragment} from "react";
import get from "lodash/get";
import { Theme } from "@mui/material/styles";
import createStyles from "@mui/styles/createStyles";
import withStyles from "@mui/styles/withStyles";
@@ -26,7 +27,7 @@ import {
} from "../../../../icons";
import { Bucket } from "../types";
import { Box, Grid, Typography } from "@mui/material";
import { niceBytes, prettyNumber } from "../../../../common/utils";
import {calculateBytes, niceBytes, prettyNumber} from "../../../../common/utils";
import CheckboxWrapper from "../../Common/FormComponents/CheckboxWrapper/CheckboxWrapper";
import { Link } from "react-router-dom";
import {
@@ -163,7 +164,6 @@ const styles = (theme: Theme) =>
interface IBucketListItem {
bucket: Bucket;
classes: any;
onDelete: (bucketName: string) => void;
onSelect: (e: React.ChangeEvent<HTMLInputElement>) => void;
selected: boolean;
bulkSelect: boolean;
@@ -172,7 +172,6 @@ interface IBucketListItem {
const BucketListItem = ({
classes,
bucket,
onDelete,
onSelect,
selected,
bulkSelect,
@@ -181,6 +180,10 @@ const BucketListItem = ({
const usageScalar = usage.split(" ")[0];
const usageUnit = usage.split(" ")[1];
const quota = get(bucket, "details.quota.quota", "0");
const quotaForString = calculateBytes(quota);
const accessToStr = (bucket: Bucket): string => {
if (bucket.rw_access?.read && !bucket.rw_access?.write) {
return "R";
@@ -287,6 +290,11 @@ const BucketListItem = ({
<div className={classes.metricText}>
{usageScalar}
<span className={classes.unit}>{usageUnit}</span>
{quota !== "0" && (
<Fragment>
{" "}/{" "}{quotaForString.total}<span className={classes.unit}>{quotaForString.unit}</span>
</Fragment>
)}
</div>
</Grid>
<Grid item textAlign={"left"} className={classes.metric}>

View File

@@ -23,7 +23,6 @@ import { LinearProgress } from "@mui/material";
import Grid from "@mui/material/Grid";
import { Bucket, BucketList } from "../types";
import { AddIcon, BucketsIcon, LifecycleConfigIcon } from "../../../../icons";
import { AppState } from "../../../../store";
import { setErrorSnackMessage } from "../../../../actions";
import {
containerForHeader,
@@ -31,12 +30,10 @@ import {
} from "../../Common/FormComponents/common/styleLibrary";
import { ErrorResponseHandler } from "../../../../common/types";
import api from "../../../../common/api";
import DeleteBucket from "./DeleteBucket";
import PageHeader from "../../Common/PageHeader/PageHeader";
import BucketListItem from "./BucketListItem";
import BulkReplicationModal from "./BulkReplicationModal";
import HelpBox from "../../../../common/HelpBox";
import { ISessionResponse } from "../../types";
import RefreshIcon from "../../../../icons/RefreshIcon";
import AButton from "../../Common/AButton/AButton";
import MultipleBucketsIcon from "../../../../icons/MultipleBucketsIcon";
@@ -85,19 +82,15 @@ interface IListBucketsProps {
classes: any;
history: any;
setErrorSnackMessage: typeof setErrorSnackMessage;
session: ISessionResponse;
}
const ListBuckets = ({
classes,
history,
setErrorSnackMessage,
session,
}: IListBucketsProps) => {
const [records, setRecords] = useState<Bucket[]>([]);
const [loading, setLoading] = useState<boolean>(true);
const [deleteOpen, setDeleteOpen] = useState<boolean>(false);
const [selectedBucket, setSelectedBucket] = useState<string>("");
const [filterBuckets, setFilterBuckets] = useState<string>("");
const [selectedBuckets, setSelectedBuckets] = useState<string[]>([]);
const [replicationModalOpen, setReplicationModalOpen] =
@@ -125,28 +118,11 @@ const ListBuckets = ({
}
}, [loading, setErrorSnackMessage]);
const closeDeleteModalAndRefresh = (refresh: boolean) => {
setDeleteOpen(false);
if (refresh) {
setLoading(true);
setSelectedBuckets([]);
}
};
const confirmDeleteBucket = (bucket: string) => {
setDeleteOpen(true);
setSelectedBucket(bucket);
};
const filteredRecords = records.filter((b: Bucket) => {
if (filterBuckets === "") {
return true;
} else {
if (b.name.indexOf(filterBuckets) >= 0) {
return true;
} else {
return false;
}
return b.name.indexOf(filterBuckets) >= 0;
}
});
@@ -191,7 +167,6 @@ const ListBuckets = ({
return (
<BucketListItem
bucket={bucket}
onDelete={confirmDeleteBucket}
onSelect={selectListBuckets}
selected={selectedBuckets.includes(bucket.name)}
bulkSelect={bulkSelect}
@@ -205,15 +180,6 @@ const ListBuckets = ({
return (
<Fragment>
{deleteOpen && (
<DeleteBucket
deleteOpen={deleteOpen}
selectedBucket={selectedBucket}
closeDeleteModalAndRefresh={(refresh: boolean) => {
closeDeleteModalAndRefresh(refresh);
}}
/>
)}
{replicationModalOpen && (
<BulkReplicationModal
open={replicationModalOpen}
@@ -378,11 +344,7 @@ const ListBuckets = ({
);
};
const mapState = (state: AppState) => ({
session: state.console.session,
});
const connector = connect(mapState, {
const connector = connect(null, {
setErrorSnackMessage,
});

View File

@@ -24,7 +24,6 @@ import React, {
} from "react";
import { connect } from "react-redux";
import { useDropzone } from "react-dropzone";
import { Theme } from "@mui/material/styles";
import createStyles from "@mui/styles/createStyles";
import withStyles from "@mui/styles/withStyles";
@@ -74,7 +73,7 @@ import {
setErrorSnackMessage,
setSnackBarMessage,
} from "../../../../../../actions";
import { BucketInfo, BucketVersioning } from "../../../types";
import { BucketInfo, BucketQuota, BucketVersioning } from "../../../types";
import { ErrorResponseHandler } from "../../../../../../common/types";
import ScreenTitle from "../../../../Common/ScreenTitle/ScreenTitle";
@@ -292,6 +291,7 @@ const ListObjects = ({
const [selectedInternalPaths, setSelectedInternalPaths] = useState<
string | null
>(null);
const [quota, setQuota] = useState<BucketQuota | null>(null);
const internalPaths = get(match.params, "subpaths", "");
const bucketName = match.params["bucketName"];
@@ -327,6 +327,25 @@ const ListObjects = ({
}
}, [selectedObjects]);
useEffect(() => {
if (!quota) {
api
.invoke("GET", `/api/v1/buckets/${bucketName}/quota`)
.then((res: BucketQuota) => {
let quotaVals = null;
if (res.quota) {
quotaVals = res;
}
setQuota(quotaVals);
})
.catch(() => {
setQuota(null);
});
}
}, [quota, bucketName]);
const displayDeleteObject = hasPermission(bucketName, [
IAM_SCOPES.S3_DELETE_OBJECT,
]);
@@ -1176,7 +1195,10 @@ const ListObjects = ({
{bucketInfo.size && (
<Fragment>{niceBytesInt(bucketInfo.size)}</Fragment>
)}
{bucketInfo.size && bucketInfo.objects ? " / " : ""}
{bucketInfo.size && quota && (
<Fragment> / {niceBytesInt(quota.quota)}</Fragment>
)}
{bucketInfo.size && bucketInfo.objects ? " - " : ""}
{bucketInfo.objects && (
<Fragment>
{bucketInfo.objects}&nbsp;Object