github.com/treeverse/lakefs@v1.24.1-0.20240520134607-95648127bfb0/webui/src/pages/repositories/repository/settings/retention.jsx (about)

     1  import React, {useEffect, useState} from "react";
     2  import { useOutletContext } from "react-router-dom";
     3  import {
     4      ActionGroup,
     5      ActionsBar,
     6      AlertError,
     7      Loading,
     8      RefreshButton,
     9      ToggleSwitch
    10  } from "../../../../lib/components/controls";
    11  import Button from "react-bootstrap/Button";
    12  import {NotFoundError, retention} from "../../../../lib/api";
    13  import {useAPI} from "../../../../lib/hooks/api";
    14  import {useRefs} from "../../../../lib/hooks/repo";
    15  import Card from "react-bootstrap/Card";
    16  import Table from "react-bootstrap/Table";
    17  import {PolicyEditor} from "../../../../lib/components/policy";
    18  import Alert from "react-bootstrap/Alert";
    19  
    20  const exampleJson = (defaultBranch) => {
    21      return {
    22          "default_retention_days": 21,
    23          "branches":
    24              [
    25                  {"branch_id": defaultBranch, "retention_days": 28},
    26              ]
    27      }
    28  }
    29  
    30  const GCPolicy = ({repo}) => {
    31      const [refresh, setRefresh] = useState(true);
    32      const [jsonView, setJsonView] = useState(false);
    33      const [showCreate, setShowCreate] = useState(false);
    34      const [isActionsDisabled, setIsActionsDisabled] = useState(false);
    35  
    36      const {response, error, loading} = useAPI(async () => {
    37          return await retention.getGCPolicy(repo.id)
    38      }, [repo, refresh])
    39  
    40      const doRefresh = () => setRefresh(!refresh);
    41      const onDelete = async () => {
    42          setIsActionsDisabled(true);
    43          try {
    44              await retention.deleteGCPolicy(repo.id);
    45          }
    46          catch (err) {
    47              setIsActionsDisabled(false);
    48              throw err;
    49          }
    50          setRefresh(!refresh);
    51          setIsActionsDisabled(false);
    52      }
    53  
    54      const onSubmit = async (policy) => {
    55          setIsActionsDisabled(true);
    56          try {
    57              await retention.setGCPolicy(repo.id, policy);
    58          }
    59          catch (err) {
    60              setIsActionsDisabled(false);
    61              throw err;
    62          }
    63          setShowCreate(false);
    64          setRefresh(!refresh);
    65          setIsActionsDisabled(false);
    66      };
    67  
    68      const jsonToggleBar = <ActionsBar>
    69          <ActionGroup orientation="right">
    70              <ToggleSwitch label={"JSON view"} id={"policy-json-switch"} onChange={setJsonView}
    71                            defaultChecked={jsonView}/>
    72          </ActionGroup>
    73      </ActionsBar>
    74      const isPolicyNotSet = error && error instanceof NotFoundError
    75      const policy = response
    76      let content;
    77      if (loading) {
    78          content = <Loading/>;
    79      } else if (error) {
    80          content = isPolicyNotSet ? <Alert variant="info" className={"mt-3"}>A garbage collection policy is not set yet.</Alert> : <AlertError error={error}/>;
    81      } else if (jsonView) {
    82          content = <>
    83              <pre className={"policy-body"}>{JSON.stringify(policy, null, 4)}</pre>
    84              {jsonToggleBar}
    85              </>
    86      } else {
    87          content = <>
    88              <Table borderless>
    89                  <tbody>
    90                  <tr key={'branch-default'}>
    91                      <td><code>Default retention days: {policy.default_retention_days}</code></td>
    92                  </tr>
    93                  </tbody>
    94              </Table>
    95              <Card className={"mb-3"}>
    96                  {policy.branches && <Table>
    97                      <thead>
    98                      <tr>
    99                          <th width={"80%"}>Branch</th>
   100                          <th>Retention Days</th>
   101                      </tr>
   102                      </thead>
   103                      <tbody>
   104                      {policy.branches.map((branch, i) => {
   105                          return (
   106                              <tr key={`branch-${i}`}>
   107                                  <td><code>{branch.branch_id}</code></td>
   108                                  <td><code>{branch.retention_days}</code></td>
   109                              </tr>
   110                          );
   111                      })}
   112                      </tbody>
   113                  </Table>}
   114              </Card>
   115              {jsonToggleBar}
   116          </>
   117      }
   118      let editorProps = {
   119          policy: policy
   120      };
   121      if (!!error  && error instanceof NotFoundError) {
   122          editorProps = {
   123              noID: true,
   124              isCreate: true,
   125              policy: exampleJson(repo.default_branch),
   126          }
   127      }
   128      return <div className="mt-3 mb-5">
   129          <div className={"section-title"}>
   130              <h4 className={"mb-0"}>
   131                  <div className={"ms-1 me-1 pl-0 d-flex"}>
   132                      <div className="flex-grow-1">Garbage collection policy</div>
   133                      <RefreshButton className={"ms-1"} onClick={doRefresh}/>
   134                      {!error && !loading && !isPolicyNotSet &&
   135                          <Button className={"ms-2 btn-secondary"} disabled={isActionsDisabled} onClick={onDelete}>Delete
   136                              Policy</Button>}
   137                      <Button className={"ms-2"} disabled={isActionsDisabled} onClick={() => setShowCreate(true)}>Edit Policy</Button>
   138                  </div>
   139              </h4>
   140          </div>
   141          <div>
   142              {/* eslint-disable-next-line react/jsx-no-target-blank */}
   143              This policy determines for how long objects are kept in the storage after they are deleted in lakeFS. <a
   144              href="https://docs.lakefs.io/howto/garbage-collection/" target="_blank">Learn more.</a>
   145          </div>
   146          <div className={"mt-3"}>
   147              {content}
   148          </div>
   149          <PolicyEditor
   150              onSubmit={onSubmit}
   151              onHide={() => setShowCreate(false)}
   152              show={showCreate}
   153              {...editorProps}
   154          />
   155      </div>
   156  };
   157  
   158  
   159  const RetentionContainer = () => {
   160      const {repo, loading, error} = useRefs();
   161      if (loading) return <Loading/>;
   162      if (error) return <AlertError error={error}/>;
   163  
   164      return (
   165          <GCPolicy repo={repo}/>
   166      );
   167  }
   168  
   169  const RepositoryRetentionPage = () => {
   170    const [setActiveTab] = useOutletContext();
   171    useEffect(() => setActiveTab("retention"), [setActiveTab]);
   172    return <RetentionContainer />;
   173  };
   174  
   175  export default RepositoryRetentionPage;