Added lifecycle rules to multiple buckets at once support (#1566)
Signed-off-by: Benjamin Perez <benjamin@bexsoft.net> Co-authored-by: Benjamin Perez <benjamin@bexsoft.net>
This commit is contained in:
@@ -20,69 +20,38 @@ import { SVGProps } from "react";
|
||||
const LifecycleConfigIcon = (props: SVGProps<SVGSVGElement>) => (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="284.616"
|
||||
height="49.568"
|
||||
className={"min-icon"}
|
||||
viewBox="0 0 256 256"
|
||||
fill={"currentcolor"}
|
||||
{...props}
|
||||
>
|
||||
<defs>
|
||||
<clipPath id="clip-path">
|
||||
<rect
|
||||
id="Rectángulo_1035"
|
||||
data-name="Rectángulo 1035"
|
||||
width="210.934"
|
||||
height="255.994"
|
||||
fill="none"
|
||||
/>
|
||||
</clipPath>
|
||||
<clipPath id="clip-Lifecycle_configuration">
|
||||
<rect width="256" height="256" />
|
||||
</clipPath>
|
||||
</defs>
|
||||
<g
|
||||
id="Lifecycle_configuration"
|
||||
data-name="Lifecycle configuration"
|
||||
clipPath="url(#clip-Lifecycle_configuration)"
|
||||
>
|
||||
<rect width="256" height="256" fill="#fff" />
|
||||
<g
|
||||
id="Lifecycle_configuration_Icon"
|
||||
data-name="Lifecycle configuration Icon"
|
||||
>
|
||||
<g
|
||||
id="Grupo_2424"
|
||||
data-name="Grupo 2424"
|
||||
transform="translate(23.344 0.006)"
|
||||
>
|
||||
<g id="Grupo_2423" data-name="Grupo 2423" clipPath="url(#clip-path)">
|
||||
<g transform="translate(23.344 0.006)">
|
||||
<g>
|
||||
<g>
|
||||
<g>
|
||||
<path
|
||||
id="Trazado_7180"
|
||||
data-name="Trazado 7180"
|
||||
d="M75.518,71.671a11.964,11.964,0,0,0,16.92,0L118.06,46.047a11.963,11.963,0,0,0,0-16.92L92.437,3.5a11.964,11.964,0,0,0-16.92,16.92L82.892,27.8A104.264,104.264,0,0,0,0,129.712a11.964,11.964,0,1,0,23.928,0A80.279,80.279,0,0,1,75.4,54.879a11.959,11.959,0,0,0,.116,16.791"
|
||||
transform="translate(0 0)"
|
||||
d="M76.7,73.6c4.6,4.6,11.9,4.6,16.5,0l0,0l25-25c4.6-4.6,4.6-11.9,0-16.5l0,0l-25-25
|
||||
c-4.6-4.6-11.9-4.6-16.5,0s-4.6,11.9,0,16.5l7.2,7.2c-47,9.9-80.8,51.3-80.8,99.4c0,6.4,5.2,11.7,11.7,11.7
|
||||
s11.7-5.2,11.7-11.7c0-32.4,20-61.4,50.2-73C72.2,61.8,72.2,69.1,76.7,73.6"
|
||||
/>
|
||||
<path
|
||||
id="Trazado_7181"
|
||||
data-name="Trazado 7181"
|
||||
d="M372.224,332.6a11.964,11.964,0,0,0-23.928,0,80.278,80.278,0,0,1-51.474,74.833,11.96,11.96,0,0,0-17.036-16.791l-25.623,25.623a11.914,11.914,0,0,0-3.011,5.053,12.017,12.017,0,0,0-.274,5.692,11.91,11.91,0,0,0,3.285,6.175l0,0,25.621,25.621a11.964,11.964,0,0,0,16.92-16.92l-7.374-7.375A104.262,104.262,0,0,0,372.224,332.6"
|
||||
transform="translate(-161.29 -206.318)"
|
||||
d="M208.8,126.8c0-6.4-5.2-11.7-11.7-11.7c-6.4,0-11.7,5.2-11.7,11.7c0,32.4-20,61.4-50.2,73
|
||||
c4.5-4.6,4.4-12-0.2-16.5c-4.6-4.5-11.9-4.4-16.4,0.1l-25,25c-1.4,1.4-2.4,3.1-2.9,4.9c-0.5,1.8-0.6,3.7-0.3,5.5
|
||||
c0.4,2.3,1.6,4.4,3.2,6l0,0l25,25c4.6,4.6,11.9,4.6,16.5,0s4.6-11.9,0-16.5l-7.2-7.2C175,216.3,208.7,174.9,208.8,126.8"
|
||||
/>
|
||||
<path
|
||||
id="Trazado_7182"
|
||||
data-name="Trazado 7182"
|
||||
d="M225.178,323.044l6.176-4.656a24.5,24.5,0,0,0,2.824,1.184l1.1,7.65a2.82,2.82,0,0,0,2.824,2.428H249a2.827,2.827,0,0,0,2.824-2.428l1.1-7.65a25.862,25.862,0,0,0,2.824-1.184l6.178,4.628a2.822,2.822,0,0,0,3.7-.252l7.705-7.705a2.824,2.824,0,0,0,.255-3.7l-4.6-6.154a25.662,25.662,0,0,0,1.184-2.822l7.65-1.1a2.826,2.826,0,0,0,2.428-2.822V287.536a2.826,2.826,0,0,0-2.4-2.792l-7.648-1.1A26.6,26.6,0,0,0,269,280.821l4.633-6.183a2.817,2.817,0,0,0-.258-3.7l-7.7-7.762a2.814,2.814,0,0,0-3.693-.254l-6.187,4.656a25.437,25.437,0,0,0-2.823-1.184l-1.1-7.648a2.822,2.822,0,0,0-2.822-2.428H238.126a2.819,2.819,0,0,0-2.82,2.428l-1.1,7.648a25.448,25.448,0,0,0-2.824,1.184l-6.2-4.656a2.83,2.83,0,0,0-3.705.254l-7.7,7.705a2.817,2.817,0,0,0-.258,3.7l4.661,6.183a24.842,24.842,0,0,0-1.193,2.822l-7.643,1.1a2.822,2.822,0,0,0-2.429,2.823V298.4a2.822,2.822,0,0,0,2.429,2.823l7.643,1.1a24.549,24.549,0,0,0,1.193,2.823l-4.661,6.238a2.821,2.821,0,0,0,.258,3.7l7.7,7.705a2.833,2.833,0,0,0,3.705.252m9.93-30.056a8.466,8.466,0,1,1,16.931-.007v.007a8.464,8.464,0,0,1-8.46,8.468h-.008a8.465,8.465,0,0,1-8.463-8.467Z"
|
||||
transform="translate(-133.14 -164.935)"
|
||||
d="M92.8,157.8l6-4.5c0.9,0.4,1.8,0.8,2.8,1.2l1.1,7.5c0.2,1.4,1.4,2.4,2.8,2.4h10.6
|
||||
c1.4,0,2.6-1,2.8-2.4l1.1-7.5c0.9-0.3,1.9-0.7,2.8-1.2l6,4.5c1.1,0.8,2.6,0.7,3.6-0.2l7.5-7.5c1-1,1.1-2.5,0.2-3.6l-4.5-6
|
||||
c0.4-0.9,0.8-1.8,1.2-2.8l7.5-1.1c1.4-0.2,2.4-1.4,2.4-2.8v-10.7c0-1.4-1-2.5-2.3-2.7l-7.5-1.1c-0.3-0.9-0.7-1.9-1.2-2.8
|
||||
l4.5-6c0.8-1.1,0.7-2.6-0.3-3.6l-7.5-7.6c-1-1-2.5-1.1-3.6-0.2l-6,4.5c-0.9-0.4-1.8-0.8-2.8-1.2l-1.1-7.5
|
||||
c-0.2-1.4-1.4-2.4-2.8-2.4h-10.7c-1.4,0-2.6,1-2.7,2.4l-1.1,7.5c-0.9,0.3-1.9,0.7-2.8,1.2l-6-4.5c-1.1-0.8-2.6-0.7-3.6,0.2
|
||||
l-7.5,7.5c-1,1-1.1,2.5-0.3,3.6l4.5,6c-0.4,0.9-0.8,1.8-1.2,2.8l-7.5,1.1c-1.4,0.2-2.4,1.4-2.4,2.8v10.6c0,1.4,1,2.6,2.4,2.8
|
||||
l7.5,1.1c0.3,0.9,0.7,1.9,1.2,2.8l-4.5,6.1c-0.8,1.1-0.7,2.6,0.3,3.6l7.5,7.5C90.2,158.6,91.7,158.7,92.8,157.8 M102.5,128.5
|
||||
c-0.1-4.6,3.6-8.3,8.2-8.3c4.6-0.1,8.3,3.6,8.3,8.2c0,0.1,0,0.1,0,0.2l0,0c0,4.6-3.7,8.3-8.2,8.3l0,0
|
||||
C106.2,136.8,102.5,133.1,102.5,128.5L102.5,128.5L102.5,128.5z"
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
<rect
|
||||
id="Rectángulo_1036"
|
||||
data-name="Rectángulo 1036"
|
||||
width="256"
|
||||
height="256"
|
||||
fill="none"
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
||||
@@ -287,6 +287,7 @@ const BucketLifecyclePanel = ({
|
||||
</Grid>
|
||||
{!loadingLifecycle && (
|
||||
<Grid item xs={12}>
|
||||
<br />
|
||||
<HelpBox
|
||||
title={"Lifecycle Rules"}
|
||||
iconComponent={<TiersIcon />}
|
||||
|
||||
@@ -0,0 +1,462 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2022 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// 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, { Fragment, useEffect, useState } from "react";
|
||||
import { connect } from "react-redux";
|
||||
import { Theme } from "@mui/material/styles";
|
||||
import createStyles from "@mui/styles/createStyles";
|
||||
import withStyles from "@mui/styles/withStyles";
|
||||
import { SelectChangeEvent, Tooltip } from "@mui/material";
|
||||
import get from "lodash/get";
|
||||
import Grid from "@mui/material/Grid";
|
||||
import ErrorOutlineIcon from "@mui/icons-material/ErrorOutline";
|
||||
import CheckCircleOutlineIcon from "@mui/icons-material/CheckCircleOutline";
|
||||
import {
|
||||
createTenantCommon,
|
||||
formFieldStyles,
|
||||
modalStyleUtils,
|
||||
spacingUtils,
|
||||
} from "../../Common/FormComponents/common/styleLibrary";
|
||||
import { setModalErrorSnackMessage } from "../../../../actions";
|
||||
import InputBoxWrapper from "../../Common/FormComponents/InputBoxWrapper/InputBoxWrapper";
|
||||
import ModalWrapper from "../../Common/ModalWrapper/ModalWrapper";
|
||||
import PredefinedList from "../../Common/FormComponents/PredefinedList/PredefinedList";
|
||||
import api from "../../../../common/api";
|
||||
import GenericWizard from "../../Common/GenericWizard/GenericWizard";
|
||||
import FormSwitchWrapper from "../../Common/FormComponents/FormSwitchWrapper/FormSwitchWrapper";
|
||||
import SelectWrapper from "../../Common/FormComponents/SelectWrapper/SelectWrapper";
|
||||
import RadioGroupSelector from "../../Common/FormComponents/RadioGroupSelector/RadioGroupSelector";
|
||||
import { ErrorResponseHandler } from "../../../../common/types";
|
||||
import QueryMultiSelector from "../../Common/FormComponents/QueryMultiSelector/QueryMultiSelector";
|
||||
import { ITiersDropDown } from "../BucketDetails/AddLifecycleModal";
|
||||
import {
|
||||
ITierElement,
|
||||
ITierResponse,
|
||||
} from "../../Configurations/TiersConfiguration/types";
|
||||
import { MultiBucketResult } from "../types";
|
||||
|
||||
interface IBulkReplicationModal {
|
||||
open: boolean;
|
||||
closeModalAndRefresh: (clearSelection: boolean) => any;
|
||||
classes: any;
|
||||
buckets: string[];
|
||||
setModalErrorSnackMessage: typeof setModalErrorSnackMessage;
|
||||
}
|
||||
|
||||
const styles = (theme: Theme) =>
|
||||
createStyles({
|
||||
resultGrid: {
|
||||
display: "grid",
|
||||
gridTemplateColumns: "45px auto",
|
||||
alignItems: "center",
|
||||
justifyContent: "stretch",
|
||||
},
|
||||
errorIcon: {
|
||||
paddingTop: 5,
|
||||
color: "#C72C48",
|
||||
},
|
||||
successIcon: {
|
||||
paddingTop: 5,
|
||||
color: "#42C91A",
|
||||
},
|
||||
hide: {
|
||||
opacity: 0,
|
||||
transitionDuration: "0.3s",
|
||||
},
|
||||
...spacingUtils,
|
||||
...modalStyleUtils,
|
||||
...formFieldStyles,
|
||||
...createTenantCommon,
|
||||
});
|
||||
|
||||
const AddBulkReplicationModal = ({
|
||||
open,
|
||||
closeModalAndRefresh,
|
||||
classes,
|
||||
buckets,
|
||||
setModalErrorSnackMessage,
|
||||
}: IBulkReplicationModal) => {
|
||||
const [addLoading, setAddLoading] = useState<boolean>(false);
|
||||
const [loadingTiers, setLoadingTiers] = useState<boolean>(true);
|
||||
const [tiersList, setTiersList] = useState<ITiersDropDown[]>([]);
|
||||
const [prefix, setPrefix] = useState("");
|
||||
const [tags, setTags] = useState<string>("");
|
||||
const [storageClass, setStorageClass] = useState("");
|
||||
const [NCTransitionSC, setNCTransitionSC] = useState("");
|
||||
const [expiredObjectDM, setExpiredObjectDM] = useState<boolean>(false);
|
||||
const [NCExpirationDays, setNCExpirationDays] = useState<string>("0");
|
||||
const [NCTransitionDays, setNCTransitionDays] = useState<string>("0");
|
||||
const [ilmType, setIlmType] = useState<string>("expiry");
|
||||
const [expiryDays, setExpiryDays] = useState<string>("0");
|
||||
const [transitionDays, setTransitionDays] = useState<string>("0");
|
||||
const [isFormValid, setIsFormValid] = useState<boolean>(false);
|
||||
const [results, setResults] = useState<MultiBucketResult | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (loadingTiers) {
|
||||
api
|
||||
.invoke("GET", `/api/v1/admin/tiers`)
|
||||
.then((res: ITierResponse) => {
|
||||
const tiersList: ITierElement[] | null = get(res, "items", []);
|
||||
|
||||
if (tiersList !== null && tiersList.length >= 1) {
|
||||
const objList = tiersList.map((tier: ITierElement) => {
|
||||
const tierType = tier.type;
|
||||
const value = get(tier, `${tierType}.name`, "");
|
||||
|
||||
return { label: value, value: value };
|
||||
});
|
||||
|
||||
setTiersList(objList);
|
||||
if (objList.length > 0) {
|
||||
setStorageClass(objList[0].value);
|
||||
}
|
||||
}
|
||||
setLoadingTiers(false);
|
||||
})
|
||||
.catch((err: ErrorResponseHandler) => {
|
||||
setLoadingTiers(false);
|
||||
setModalErrorSnackMessage(err);
|
||||
});
|
||||
}
|
||||
}, [loadingTiers, setModalErrorSnackMessage]);
|
||||
|
||||
useEffect(() => {
|
||||
let valid = true;
|
||||
|
||||
if (ilmType !== "expiry") {
|
||||
if (storageClass === "") {
|
||||
valid = false;
|
||||
}
|
||||
}
|
||||
setIsFormValid(valid);
|
||||
}, [ilmType, expiryDays, transitionDays, storageClass]);
|
||||
|
||||
const LogoToShow = ({ errString }: { errString: string }) => {
|
||||
switch (errString) {
|
||||
case "":
|
||||
return (
|
||||
<div className={classes.successIcon}>
|
||||
<CheckCircleOutlineIcon />
|
||||
</div>
|
||||
);
|
||||
case "n/a":
|
||||
return null;
|
||||
default:
|
||||
if (errString) {
|
||||
return (
|
||||
<div className={classes.errorIcon}>
|
||||
<Tooltip title={errString} placement="top-start">
|
||||
<ErrorOutlineIcon />
|
||||
</Tooltip>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
const createLifecycleRules = (to: any) => {
|
||||
let rules = {};
|
||||
|
||||
if (ilmType === "expiry") {
|
||||
let expiry = {
|
||||
expiry_days: parseInt(expiryDays),
|
||||
};
|
||||
|
||||
rules = {
|
||||
...expiry,
|
||||
noncurrentversion_expiration_days: parseInt(NCExpirationDays),
|
||||
};
|
||||
} else {
|
||||
let transition = {
|
||||
transition_days: parseInt(transitionDays),
|
||||
};
|
||||
|
||||
rules = {
|
||||
...transition,
|
||||
noncurrentversion_transition_days: parseInt(NCTransitionDays),
|
||||
noncurrentversion_transition_storage_class: NCTransitionSC,
|
||||
storage_class: storageClass,
|
||||
};
|
||||
}
|
||||
|
||||
const lifecycleInsert = {
|
||||
buckets,
|
||||
type: ilmType,
|
||||
prefix,
|
||||
tags,
|
||||
expired_object_delete_marker: expiredObjectDM,
|
||||
...rules,
|
||||
};
|
||||
|
||||
api
|
||||
.invoke("POST", `/api/v1/buckets/multi-lifecycle`, lifecycleInsert)
|
||||
.then((res: MultiBucketResult) => {
|
||||
setAddLoading(false);
|
||||
setResults(res);
|
||||
to("++");
|
||||
})
|
||||
.catch((err: ErrorResponseHandler) => {
|
||||
setAddLoading(false);
|
||||
setModalErrorSnackMessage(err);
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<ModalWrapper
|
||||
modalOpen={open}
|
||||
onClose={() => {
|
||||
closeModalAndRefresh(false);
|
||||
}}
|
||||
title="Set Lifecycle to multiple buckets"
|
||||
>
|
||||
<GenericWizard
|
||||
loadingStep={addLoading || loadingTiers}
|
||||
wizardSteps={[
|
||||
{
|
||||
label: "Lifecycle Configuration",
|
||||
componentRender: (
|
||||
<Fragment>
|
||||
<Grid item xs={12}>
|
||||
<PredefinedList
|
||||
label="Local Buckets to replicate"
|
||||
content={buckets.join(", ")}
|
||||
/>
|
||||
</Grid>
|
||||
<h4>Remote Endpoint Configuration</h4>
|
||||
<Grid container>
|
||||
<Grid item xs={12} className={classes.formScrollable}>
|
||||
<Grid item xs={12} className={classes.formFieldRow}>
|
||||
<fieldset className={classes.fieldGroup}>
|
||||
<legend className={classes.descriptionText}>
|
||||
Lifecycle Configuration
|
||||
</legend>
|
||||
|
||||
<Grid item xs={12}>
|
||||
<RadioGroupSelector
|
||||
currentSelection={ilmType}
|
||||
id="quota_type"
|
||||
name="quota_type"
|
||||
label="ILM Rule"
|
||||
onChange={(
|
||||
e: React.ChangeEvent<{ value: unknown }>
|
||||
) => {
|
||||
setIlmType(e.target.value as string);
|
||||
}}
|
||||
selectorOptions={[
|
||||
{ value: "expiry", label: "Expiry" },
|
||||
{ value: "transition", label: "Transition" },
|
||||
]}
|
||||
/>
|
||||
</Grid>
|
||||
{ilmType === "expiry" ? (
|
||||
<Fragment>
|
||||
<Grid item xs={12} className={classes.formFieldRow}>
|
||||
<InputBoxWrapper
|
||||
type="number"
|
||||
id="expiry_days"
|
||||
name="expiry_days"
|
||||
onChange={(
|
||||
e: React.ChangeEvent<HTMLInputElement>
|
||||
) => {
|
||||
setExpiryDays(e.target.value);
|
||||
}}
|
||||
label="Expiry Days"
|
||||
value={expiryDays}
|
||||
min="0"
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12} className={classes.formFieldRow}>
|
||||
<InputBoxWrapper
|
||||
type="number"
|
||||
id="noncurrentversion_expiration_days"
|
||||
name="noncurrentversion_expiration_days"
|
||||
onChange={(
|
||||
e: React.ChangeEvent<HTMLInputElement>
|
||||
) => {
|
||||
setNCExpirationDays(e.target.value);
|
||||
}}
|
||||
label="Non-current Expiration Days"
|
||||
value={NCExpirationDays}
|
||||
min="0"
|
||||
/>
|
||||
</Grid>
|
||||
</Fragment>
|
||||
) : (
|
||||
<Fragment>
|
||||
<Grid item xs={12} className={classes.formFieldRow}>
|
||||
<InputBoxWrapper
|
||||
type="number"
|
||||
id="transition_days"
|
||||
name="transition_days"
|
||||
onChange={(
|
||||
e: React.ChangeEvent<HTMLInputElement>
|
||||
) => {
|
||||
setTransitionDays(e.target.value);
|
||||
}}
|
||||
label="Transition Days"
|
||||
value={transitionDays}
|
||||
min="0"
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12} className={classes.formFieldRow}>
|
||||
<InputBoxWrapper
|
||||
type="number"
|
||||
id="noncurrentversion_transition_days"
|
||||
name="noncurrentversion_transition_days"
|
||||
onChange={(
|
||||
e: React.ChangeEvent<HTMLInputElement>
|
||||
) => {
|
||||
setNCTransitionDays(e.target.value);
|
||||
}}
|
||||
label="Non-current Transition Days"
|
||||
value={NCTransitionDays}
|
||||
min="0"
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12} className={classes.formFieldRow}>
|
||||
<InputBoxWrapper
|
||||
id="noncurrentversion_t_SC"
|
||||
name="noncurrentversion_t_SC"
|
||||
onChange={(
|
||||
e: React.ChangeEvent<HTMLInputElement>
|
||||
) => {
|
||||
setNCTransitionSC(e.target.value);
|
||||
}}
|
||||
placeholder="Set Non-current Version Transition Storage Class"
|
||||
label="Non-current Version Transition Storage Class"
|
||||
value={NCTransitionSC}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12} className={classes.formFieldRow}>
|
||||
<SelectWrapper
|
||||
label="Storage Class"
|
||||
id="storage_class"
|
||||
name="storage_class"
|
||||
value={storageClass}
|
||||
onChange={(e: SelectChangeEvent<string>) => {
|
||||
setStorageClass(e.target.value as string);
|
||||
}}
|
||||
options={tiersList}
|
||||
/>
|
||||
</Grid>
|
||||
</Fragment>
|
||||
)}
|
||||
</fieldset>
|
||||
</Grid>
|
||||
<Grid item xs={12} className={classes.formFieldRow}>
|
||||
<fieldset className={classes.fieldGroup}>
|
||||
<legend className={classes.descriptionText}>
|
||||
File Configuration
|
||||
</legend>
|
||||
|
||||
<Grid item xs={12}>
|
||||
<InputBoxWrapper
|
||||
id="prefix"
|
||||
name="prefix"
|
||||
onChange={(
|
||||
e: React.ChangeEvent<HTMLInputElement>
|
||||
) => {
|
||||
setPrefix(e.target.value);
|
||||
}}
|
||||
label="Prefix"
|
||||
value={prefix}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<QueryMultiSelector
|
||||
name="tags"
|
||||
label="Tags"
|
||||
elements={tags}
|
||||
onChange={(vl: string) => {
|
||||
setTags(vl);
|
||||
}}
|
||||
keyPlaceholder="Tag Key"
|
||||
valuePlaceholder="Tag Value"
|
||||
withBorder
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<FormSwitchWrapper
|
||||
value="expired_delete_marker"
|
||||
id="expired_delete_marker"
|
||||
name="expired_delete_marker"
|
||||
checked={expiredObjectDM}
|
||||
onChange={(
|
||||
event: React.ChangeEvent<HTMLInputElement>
|
||||
) => {
|
||||
setExpiredObjectDM(event.target.checked);
|
||||
}}
|
||||
label={"Expired Object Delete Marker"}
|
||||
/>
|
||||
</Grid>
|
||||
</fieldset>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Fragment>
|
||||
),
|
||||
buttons: [
|
||||
{
|
||||
type: "custom",
|
||||
label: "Create Rules",
|
||||
enabled: !loadingTiers && !addLoading && isFormValid,
|
||||
action: createLifecycleRules,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: "Results",
|
||||
componentRender: (
|
||||
<Fragment>
|
||||
<h3>Multi Bucket lifecycle Assignments Results</h3>
|
||||
<Grid container>
|
||||
<Grid item xs={12} className={classes.formScrollable}>
|
||||
<h4>Buckets Results</h4>
|
||||
{results?.results.map((resultItem) => {
|
||||
return (
|
||||
<div className={classes.resultGrid}>
|
||||
{LogoToShow({ errString: resultItem.error || "" })}
|
||||
<span>{resultItem.bucketName}</span>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Fragment>
|
||||
),
|
||||
buttons: [
|
||||
{
|
||||
type: "custom",
|
||||
label: "Done",
|
||||
enabled: !addLoading,
|
||||
action: () => closeModalAndRefresh(true),
|
||||
},
|
||||
],
|
||||
},
|
||||
]}
|
||||
forModal
|
||||
/>
|
||||
</ModalWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
const connector = connect(null, {
|
||||
setModalErrorSnackMessage,
|
||||
});
|
||||
|
||||
export default withStyles(styles)(connector(AddBulkReplicationModal));
|
||||
@@ -22,7 +22,7 @@ import withStyles from "@mui/styles/withStyles";
|
||||
import { LinearProgress } from "@mui/material";
|
||||
import Grid from "@mui/material/Grid";
|
||||
import { Bucket, BucketList } from "../types";
|
||||
import { AddIcon, BucketsIcon } from "../../../../icons";
|
||||
import {AddIcon, BucketsIcon, LifecycleConfigIcon} from "../../../../icons";
|
||||
import { AppState } from "../../../../store";
|
||||
import { setErrorSnackMessage } from "../../../../actions";
|
||||
import {
|
||||
@@ -50,6 +50,7 @@ import PageLayout from "../../Common/Layout/PageLayout";
|
||||
import SearchBox from "../../Common/SearchBox";
|
||||
import VirtualizedList from "../../Common/VirtualizedList/VirtualizedList";
|
||||
import RBIconButton from "../BucketDetails/SummaryItems/RBIconButton";
|
||||
import BulkLifecycleModal from "./BulkLifecycleModal";
|
||||
|
||||
const styles = (theme: Theme) =>
|
||||
createStyles({
|
||||
@@ -100,6 +101,7 @@ const ListBuckets = ({
|
||||
const [selectedBuckets, setSelectedBuckets] = useState<string[]>([]);
|
||||
const [replicationModalOpen, setReplicationModalOpen] =
|
||||
useState<boolean>(false);
|
||||
const [lifecycleModalOpen, setLifecycleModalOpen] = useState<boolean>(false);
|
||||
|
||||
const [bulkSelect, setBulkSelect] = useState<boolean>(false);
|
||||
|
||||
@@ -174,6 +176,14 @@ const ListBuckets = ({
|
||||
}
|
||||
};
|
||||
|
||||
const closeBulkLifecycleModal = (unselectAll: boolean) => {
|
||||
setLifecycleModalOpen(false);
|
||||
|
||||
if (unselectAll) {
|
||||
setSelectedBuckets([]);
|
||||
}
|
||||
};
|
||||
|
||||
const renderItemLine = (index: number) => {
|
||||
const bucket = filteredRecords[index] || null;
|
||||
if (bucket) {
|
||||
@@ -213,6 +223,13 @@ const ListBuckets = ({
|
||||
closeModalAndRefresh={closeBulkReplicationModal}
|
||||
/>
|
||||
)}
|
||||
{lifecycleModalOpen && (
|
||||
<BulkLifecycleModal
|
||||
buckets={selectedBuckets}
|
||||
closeModalAndRefresh={closeBulkLifecycleModal}
|
||||
open={lifecycleModalOpen}
|
||||
/>
|
||||
)}
|
||||
<PageHeader label={"Buckets"} />
|
||||
<PageLayout>
|
||||
<Grid item xs={12} className={classes.actionsTray} display="flex">
|
||||
@@ -241,6 +258,18 @@ const ListBuckets = ({
|
||||
variant={bulkSelect ? "contained" : "outlined"}
|
||||
/>
|
||||
|
||||
<RBIconButton
|
||||
tooltip={"Set Lifecycle"}
|
||||
onClick={() => {
|
||||
setLifecycleModalOpen(true);
|
||||
}}
|
||||
text={""}
|
||||
icon={<LifecycleConfigIcon />}
|
||||
disabled={selectedBuckets.length === 0}
|
||||
color={"primary"}
|
||||
variant={"outlined"}
|
||||
/>
|
||||
|
||||
<RBIconButton
|
||||
tooltip={"Set Replication"}
|
||||
onClick={() => {
|
||||
|
||||
@@ -201,3 +201,12 @@ export interface LifeCycleItem {
|
||||
tags?: any;
|
||||
status?: string;
|
||||
}
|
||||
|
||||
export interface MultiBucketResult {
|
||||
bucketName: string,
|
||||
error?: string,
|
||||
}
|
||||
|
||||
export interface MultiBucketResult {
|
||||
results: MultiBucketResult[],
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user