github.com/minio/console@v1.4.1/web-app/src/screens/Console/Buckets/BucketDetails/EditReplicationModal.tsx (about)

     1  // This file is part of MinIO Console Server
     2  // Copyright (c) 2021 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 React, { useEffect, useState } from "react";
    18  import {
    19    BucketReplicationIcon,
    20    Button,
    21    FormLayout,
    22    InputBox,
    23    ReadBox,
    24    Switch,
    25    Grid,
    26  } from "mds";
    27  import ModalWrapper from "../../Common/ModalWrapper/ModalWrapper";
    28  import QueryMultiSelector from "../../Common/FormComponents/QueryMultiSelector/QueryMultiSelector";
    29  import { modalStyleUtils } from "../../Common/FormComponents/common/styleLibrary";
    30  import { setModalErrorSnackMessage } from "../../../../systemSlice";
    31  import { useAppDispatch } from "../../../../store";
    32  import { api } from "api";
    33  import { errorToHandler } from "api/errors";
    34  
    35  interface IEditReplicationModal {
    36    closeModalAndRefresh: (refresh: boolean) => void;
    37    open: boolean;
    38    bucketName: string;
    39    ruleID: string;
    40  }
    41  
    42  const EditReplicationModal = ({
    43    closeModalAndRefresh,
    44    open,
    45    bucketName,
    46    ruleID,
    47  }: IEditReplicationModal) => {
    48    const dispatch = useAppDispatch();
    49    const [editLoading, setEditLoading] = useState<boolean>(true);
    50    const [saveEdit, setSaveEdit] = useState<boolean>(false);
    51    const [priority, setPriority] = useState<string>("1");
    52    const [destination, setDestination] = useState<string>("");
    53    const [prefix, setPrefix] = useState<string>("");
    54    const [repDeleteMarker, setRepDeleteMarker] = useState<boolean>(false);
    55    const [metadataSync, setMetadataSync] = useState<boolean>(false);
    56    const [initialTags, setInitialTags] = useState<string>("");
    57    const [tags, setTags] = useState<string>("");
    58    const [targetStorageClass, setTargetStorageClass] = useState<string>("");
    59    const [repExisting, setRepExisting] = useState<boolean>(false);
    60    const [repDelete, setRepDelete] = useState<boolean>(false);
    61    const [ruleState, setRuleState] = useState<boolean>(false);
    62  
    63    useEffect(() => {
    64      if (editLoading) {
    65        api.buckets
    66          .getBucketReplicationRule(bucketName, ruleID)
    67          .then((res) => {
    68            setPriority(res.data.priority ? res.data.priority.toString() : "");
    69            const pref = res.data.prefix || "";
    70            const tag = res.data.tags || "";
    71            setPrefix(pref);
    72            setInitialTags(tag);
    73            setTags(tag);
    74            setDestination(res.data.destination?.bucket || "");
    75            setRepDeleteMarker(res.data.delete_marker_replication || false);
    76            setTargetStorageClass(res.data.storageClass || "");
    77            setRepExisting(!!res.data.existingObjects);
    78            setRepDelete(!!res.data.deletes_replication);
    79            setRuleState(res.data.status === "Enabled");
    80            setMetadataSync(!!res.data.metadata_replication);
    81  
    82            setEditLoading(false);
    83          })
    84          .catch((err) => {
    85            dispatch(setModalErrorSnackMessage(errorToHandler(err.error)));
    86            setEditLoading(false);
    87          });
    88      }
    89    }, [editLoading, dispatch, bucketName, ruleID]);
    90  
    91    useEffect(() => {
    92      if (saveEdit) {
    93        const remoteBucketsInfo = {
    94          arn: destination,
    95          ruleState: ruleState,
    96          prefix: prefix,
    97          tags: tags,
    98          replicateDeleteMarkers: repDeleteMarker,
    99          replicateDeletes: repDelete,
   100          replicateExistingObjects: repExisting,
   101          replicateMetadata: metadataSync,
   102          priority: parseInt(priority),
   103          storageClass: targetStorageClass,
   104        };
   105  
   106        api.buckets
   107          .updateMultiBucketReplication(bucketName, ruleID, remoteBucketsInfo)
   108          .then(() => {
   109            setSaveEdit(false);
   110            closeModalAndRefresh(true);
   111          })
   112          .catch((err) => {
   113            dispatch(setModalErrorSnackMessage(errorToHandler(err.error)));
   114            setSaveEdit(false);
   115          });
   116      }
   117    }, [
   118      saveEdit,
   119      bucketName,
   120      ruleID,
   121      destination,
   122      prefix,
   123      tags,
   124      repDeleteMarker,
   125      priority,
   126      repDelete,
   127      repExisting,
   128      ruleState,
   129      metadataSync,
   130      targetStorageClass,
   131      closeModalAndRefresh,
   132      dispatch,
   133    ]);
   134  
   135    return (
   136      <ModalWrapper
   137        modalOpen={open}
   138        onClose={() => {
   139          closeModalAndRefresh(false);
   140        }}
   141        title="Edit Bucket Replication"
   142        titleIcon={<BucketReplicationIcon />}
   143      >
   144        <form
   145          noValidate
   146          autoComplete="off"
   147          onSubmit={(e: React.FormEvent<HTMLFormElement>) => {
   148            e.preventDefault();
   149            setSaveEdit(true);
   150          }}
   151        >
   152          <FormLayout containerPadding={false} withBorders={false}>
   153            <Switch
   154              checked={ruleState}
   155              id="ruleState"
   156              name="ruleState"
   157              label="Rule State"
   158              onChange={(e) => {
   159                setRuleState(e.target.checked);
   160              }}
   161            />
   162            <ReadBox label={"Destination"} sx={{ width: "100%" }}>
   163              {destination}
   164            </ReadBox>
   165  
   166            <InputBox
   167              id="priority"
   168              name="priority"
   169              onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
   170                if (e.target.validity.valid) {
   171                  setPriority(e.target.value);
   172                }
   173              }}
   174              label="Priority"
   175              value={priority}
   176              pattern={"[0-9]*"}
   177            />
   178            <InputBox
   179              id="storageClass"
   180              name="storageClass"
   181              onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
   182                setTargetStorageClass(e.target.value);
   183              }}
   184              placeholder="STANDARD_IA,REDUCED_REDUNDANCY etc"
   185              label="Storage Class"
   186              value={targetStorageClass}
   187            />
   188            <fieldset className={"inputItem"}>
   189              <legend>Object Filters</legend>
   190              <InputBox
   191                id="prefix"
   192                name="prefix"
   193                onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
   194                  setPrefix(e.target.value);
   195                }}
   196                placeholder="prefix"
   197                label="Prefix"
   198                value={prefix}
   199              />
   200              <QueryMultiSelector
   201                name="tags"
   202                label="Tags"
   203                elements={initialTags}
   204                onChange={(vl: string) => {
   205                  setTags(vl);
   206                }}
   207                keyPlaceholder="Tag Key"
   208                valuePlaceholder="Tag Value"
   209                withBorder
   210              />
   211            </fieldset>
   212            <fieldset className={"inputItem"}>
   213              <legend>Replication Options</legend>
   214  
   215              <Switch
   216                checked={repExisting}
   217                id="repExisting"
   218                name="repExisting"
   219                label="Existing Objects"
   220                onChange={(e) => {
   221                  setRepExisting(e.target.checked);
   222                }}
   223                description={"Replicate existing objects"}
   224              />
   225  
   226              <Switch
   227                checked={metadataSync}
   228                id="metadatataSync"
   229                name="metadatataSync"
   230                label="Metadata Sync"
   231                onChange={(e) => {
   232                  setMetadataSync(e.target.checked);
   233                }}
   234                description={"Metadata Sync"}
   235              />
   236  
   237              <Switch
   238                checked={repDeleteMarker}
   239                id="deleteMarker"
   240                name="deleteMarker"
   241                label="Delete Marker"
   242                onChange={(e) => {
   243                  setRepDeleteMarker(e.target.checked);
   244                }}
   245                description={"Replicate soft deletes"}
   246              />
   247              <Switch
   248                checked={repDelete}
   249                id="repDelete"
   250                name="repDelete"
   251                label="Deletes"
   252                onChange={(e) => {
   253                  setRepDelete(e.target.checked);
   254                }}
   255                description={"Replicate versioned deletes"}
   256              />
   257            </fieldset>
   258            <Grid item xs={12} sx={modalStyleUtils.modalButtonBar}>
   259              <Button
   260                id={"cancel-edit-replication"}
   261                type="button"
   262                variant="regular"
   263                disabled={editLoading || saveEdit}
   264                onClick={() => {
   265                  closeModalAndRefresh(false);
   266                }}
   267                label={"Cancel"}
   268              />
   269              <Button
   270                id={"save-replication"}
   271                type="submit"
   272                variant="callAction"
   273                disabled={editLoading || saveEdit}
   274                label={"Save"}
   275              />
   276            </Grid>
   277          </FormLayout>
   278        </form>
   279      </ModalWrapper>
   280    );
   281  };
   282  
   283  export default EditReplicationModal;