github.com/minio/console@v1.4.1/web-app/src/screens/Console/EventDestinations/WebhookSettings/AddEndpointModal.tsx (about)

     1  // This file is part of MinIO Console Server
     2  // Copyright (c) 2023 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, { Fragment, useState } from "react";
    18  import {
    19    Button,
    20    ConsoleIcon,
    21    FormLayout,
    22    Grid,
    23    InputBox,
    24    PendingItemsIcon,
    25    ProgressBar,
    26    WebhookIcon,
    27  } from "mds";
    28  import { api } from "api";
    29  import { errorToHandler } from "api/errors";
    30  import ModalWrapper from "../../Common/ModalWrapper/ModalWrapper";
    31  import {
    32    configurationIsLoading,
    33    setErrorSnackMessage,
    34    setServerNeedsRestart,
    35    setSnackBarMessage,
    36  } from "../../../../systemSlice";
    37  import { useAppDispatch } from "../../../../store";
    38  
    39  import { modalStyleUtils } from "../../Common/FormComponents/common/styleLibrary";
    40  
    41  interface IEndpointModal {
    42    open: boolean;
    43    type: string;
    44    onCloseEndpoint: () => void;
    45  }
    46  
    47  const AddEndpointModal = ({ open, type, onCloseEndpoint }: IEndpointModal) => {
    48    const [name, setName] = useState<string>("");
    49    const [endpoint, setEndpoint] = useState<string>("");
    50    const [authToken, setAuthToken] = useState<string>("");
    51    const [saving, setSaving] = useState<boolean>(false);
    52    const [invalidInputs, setInvalidInput] = useState<string[]>([
    53      "name",
    54      "endpoint",
    55    ]);
    56    const [initialInputs, setInitialInputs] = useState<string[]>([
    57      "name",
    58      "endpoint",
    59      "auth-token",
    60    ]);
    61  
    62    const dispatch = useAppDispatch();
    63  
    64    const saveWebhook = () => {
    65      if (saving) {
    66        return;
    67      }
    68  
    69      if (invalidInputs.length !== 0) {
    70        return;
    71      }
    72  
    73      if (name.trim() === "") {
    74        setInvalidInput([...invalidInputs, "name"]);
    75  
    76        return;
    77      }
    78  
    79      if (endpoint.trim() === "") {
    80        setInvalidInput([...invalidInputs, "endpoint"]);
    81  
    82        return;
    83      }
    84  
    85      setSaving(true);
    86  
    87      const payload = {
    88        key_values: [
    89          {
    90            key: "endpoint",
    91            value: endpoint,
    92          },
    93          {
    94            key: "auth_token",
    95            value: authToken,
    96          },
    97        ],
    98        arn_resource_id: name,
    99      };
   100  
   101      api.configs
   102        .setConfig(type, payload)
   103        .then((res) => {
   104          setSaving(false);
   105          dispatch(setServerNeedsRestart(res.data.restart || false));
   106          if (!res.data.restart) {
   107            dispatch(setSnackBarMessage("Configuration saved successfully"));
   108          }
   109  
   110          onCloseEndpoint();
   111          dispatch(configurationIsLoading(true));
   112        })
   113        .catch((err) => {
   114          setSaving(false);
   115          dispatch(setErrorSnackMessage(errorToHandler(err.error)));
   116        });
   117    };
   118  
   119    const initializeInput = (name: string) => {
   120      setInitialInputs(initialInputs.filter((item) => item !== name));
   121    };
   122  
   123    const validateInput = (name: string, valid: boolean) => {
   124      if (invalidInputs.includes(name) && valid) {
   125        setInvalidInput(invalidInputs.filter((item) => item !== name));
   126        return;
   127      }
   128  
   129      if (!valid && !invalidInputs.includes(name)) {
   130        setInvalidInput([...invalidInputs, name]);
   131      }
   132    };
   133  
   134    let title = "Add new Webhook";
   135    let icon = <WebhookIcon />;
   136  
   137    switch (type) {
   138      case "logger_webhook":
   139        title = "New Logger Webhook";
   140        icon = <ConsoleIcon />;
   141        break;
   142      case "audit_webhook":
   143        title = "New Audit Webhook";
   144        icon = <PendingItemsIcon />;
   145        break;
   146    }
   147  
   148    return (
   149      <Fragment>
   150        <ModalWrapper
   151          modalOpen={open}
   152          title={title}
   153          onClose={onCloseEndpoint}
   154          titleIcon={icon}
   155        >
   156          <FormLayout containerPadding={false} withBorders={false}>
   157            <InputBox
   158              id="name"
   159              name="name"
   160              onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
   161                initializeInput("name");
   162                setName(event.target.value);
   163                validateInput("name", event.target.validity.valid);
   164              }}
   165              error={
   166                invalidInputs.includes("name") && !initialInputs.includes("name")
   167                  ? "Invalid Name"
   168                  : ""
   169              }
   170              label="Name"
   171              value={name}
   172              pattern={"^(?=.*[a-zA-Z0-9]).{1,}$"}
   173              required
   174            />
   175            <InputBox
   176              id="endpoint"
   177              name="endpoint"
   178              onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
   179                initializeInput("endpoint");
   180                setEndpoint(event.target.value);
   181                validateInput("endpoint", event.target.validity.valid);
   182              }}
   183              error={
   184                invalidInputs.includes("endpoint") &&
   185                !initialInputs.includes("endpoint")
   186                  ? "Invalid Endpoint set"
   187                  : ""
   188              }
   189              label="Endpoint"
   190              value={endpoint}
   191              pattern={
   192                "^(https?):\\/\\/([a-zA-Z0-9\\-.]+)(:[0-9]+)?(\\/[a-zA-Z0-9\\-.\\/]*)?$"
   193              }
   194              required
   195            />
   196            <InputBox
   197              id="auth-token"
   198              name="auth-token"
   199              onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
   200                initializeInput("auth-token");
   201                setAuthToken(event.target.value);
   202              }}
   203              label="Auth Token"
   204              value={authToken}
   205            />
   206          </FormLayout>
   207          {saving && (
   208            <Grid
   209              item
   210              xs={12}
   211              sx={{
   212                marginBottom: 10,
   213              }}
   214            >
   215              <ProgressBar />
   216            </Grid>
   217          )}
   218          <Grid item xs={12} sx={modalStyleUtils.modalButtonBar}>
   219            <Button
   220              id={"reset"}
   221              type="button"
   222              variant="regular"
   223              disabled={saving}
   224              onClick={onCloseEndpoint}
   225              label={"Cancel"}
   226              sx={{
   227                marginRight: 10,
   228              }}
   229            />
   230            <Button
   231              id={"save-lifecycle"}
   232              type="submit"
   233              variant="callAction"
   234              color="primary"
   235              disabled={saving || invalidInputs.length !== 0}
   236              label={"Save"}
   237              onClick={saveWebhook}
   238            />
   239          </Grid>
   240        </ModalWrapper>
   241      </Fragment>
   242    );
   243  };
   244  
   245  export default AddEndpointModal;