github.com/minio/console@v1.4.1/web-app/src/screens/Console/EventDestinations/CustomForms/ConfMySql.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, { useCallback, useEffect, useState } from "react";
    18  import { IElementValue } from "../../Configurations/types";
    19  import {
    20    Box,
    21    CommentBox,
    22    FormLayout,
    23    Grid,
    24    InputBox,
    25    RadioGroup,
    26    ReadBox,
    27    Switch,
    28  } from "mds";
    29  
    30  interface IConfMySqlProps {
    31    onChange: (newValue: IElementValue[]) => void;
    32  }
    33  
    34  const ConfMySql = ({ onChange }: IConfMySqlProps) => {
    35    //Local States
    36    const [useDsnString, setUseDsnString] = useState<boolean>(false);
    37    const [dsnString, setDsnString] = useState<string>("");
    38    const [host, setHostname] = useState<string>("");
    39    const [dbName, setDbName] = useState<string>("");
    40    const [port, setPort] = useState<string>("");
    41    const [user, setUser] = useState<string>("");
    42    const [password, setPassword] = useState<string>("");
    43  
    44    const [table, setTable] = useState<string>("");
    45    const [format, setFormat] = useState<string>("namespace");
    46    const [queueDir, setQueueDir] = useState<string>("");
    47    const [queueLimit, setQueueLimit] = useState<string>("");
    48    const [comment, setComment] = useState<string>("");
    49  
    50    // dsn_string*  (string)             MySQL data-source-name connection string e.g. "<user>:<password>@tcp(<host>:<port>)/<database>"
    51    // table*       (string)             DB table name to store/update events, table is auto-created
    52    // format*      (namespace*|access)  'namespace' reflects current bucket/object list and 'access' reflects a journal of object operations, defaults to 'namespace'
    53    // queue_dir    (path)               staging dir for undelivered messages e.g. '/home/events'
    54    // queue_limit  (number)             maximum limit for undelivered messages, defaults to '100000'
    55    // comment      (sentence)           optionally add a comment to this setting
    56  
    57    const parseDsnString = (
    58      input: string,
    59      keys: string[],
    60    ): Map<string, string> => {
    61      let kvFields: Map<string, string> = new Map();
    62      const regex = /(.*?):(.*?)@tcp\((.*?):(.*?)\)\/(.*?)$/gm;
    63      let m;
    64  
    65      while ((m = regex.exec(input)) !== null) {
    66        // This is necessary to avoid infinite loops with zero-width matches
    67        if (m.index === regex.lastIndex) {
    68          regex.lastIndex++;
    69        }
    70  
    71        kvFields.set("user", m[1]);
    72        kvFields.set("password", m[2]);
    73        kvFields.set("host", m[3]);
    74        kvFields.set("port", m[4]);
    75        kvFields.set("dbname", m[5]);
    76      }
    77  
    78      return kvFields;
    79    };
    80  
    81    const configToDsnString = useCallback((): string => {
    82      return `${user}:${password}@tcp(${host}:${port})/${dbName}`;
    83    }, [user, password, host, port, dbName]);
    84  
    85    useEffect(() => {
    86      if (dsnString !== "") {
    87        const formValues = [
    88          { key: "dsn_string", value: dsnString },
    89          { key: "table", value: table },
    90          { key: "format", value: format },
    91          { key: "queue_dir", value: queueDir },
    92          { key: "queue_limit", value: queueLimit },
    93          { key: "comment", value: comment },
    94        ];
    95  
    96        onChange(formValues);
    97      }
    98    }, [dsnString, table, format, queueDir, queueLimit, comment, onChange]);
    99  
   100    useEffect(() => {
   101      const cs = configToDsnString();
   102      setDsnString(cs);
   103    }, [user, dbName, password, port, host, setDsnString, configToDsnString]);
   104  
   105    const switcherChangeEvt = (event: React.ChangeEvent<HTMLInputElement>) => {
   106      if (event.target.checked) {
   107        // build dsn_string
   108        const cs = configToDsnString();
   109        setDsnString(cs);
   110      } else {
   111        // parse dsn_string
   112        const kv = parseDsnString(dsnString, [
   113          "host",
   114          "port",
   115          "dbname",
   116          "user",
   117          "password",
   118        ]);
   119        setHostname(kv.get("host") ? kv.get("host") + "" : "");
   120        setPort(kv.get("port") ? kv.get("port") + "" : "");
   121        setDbName(kv.get("dbname") ? kv.get("dbname") + "" : "");
   122        setUser(kv.get("user") ? kv.get("user") + "" : "");
   123        setPassword(kv.get("password") ? kv.get("password") + "" : "");
   124      }
   125  
   126      setUseDsnString(event.target.checked);
   127    };
   128  
   129    return (
   130      <FormLayout withBorders={false} containerPadding={false}>
   131        <Switch
   132          label={"Enter DNS String"}
   133          checked={useDsnString}
   134          id="checkedB"
   135          name="checkedB"
   136          onChange={switcherChangeEvt}
   137          value={"dnsString"}
   138        />
   139        {useDsnString ? (
   140          <React.Fragment>
   141            <Box className={"inputItem"}>
   142              <InputBox
   143                id="dsn-string"
   144                name="dsn_string"
   145                label="DSN String"
   146                value={dsnString}
   147                onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
   148                  setDsnString(e.target.value);
   149                }}
   150              />
   151            </Box>
   152          </React.Fragment>
   153        ) : (
   154          <React.Fragment>
   155            <Box>
   156              <Box
   157                withBorders
   158                useBackground
   159                sx={{
   160                  overflowY: "auto",
   161                  height: 170,
   162                  marginBottom: 12,
   163                }}
   164              >
   165                <InputBox
   166                  id="host"
   167                  name="host"
   168                  label=""
   169                  placeholder="Enter Host"
   170                  value={host}
   171                  onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
   172                    setHostname(e.target.value);
   173                  }}
   174                />
   175                <InputBox
   176                  id="db-name"
   177                  name="db-name"
   178                  label=""
   179                  placeholder="Enter DB Name"
   180                  value={dbName}
   181                  onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
   182                    setDbName(e.target.value);
   183                  }}
   184                />
   185                <InputBox
   186                  id="port"
   187                  name="port"
   188                  label=""
   189                  placeholder="Enter Port"
   190                  value={port}
   191                  onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
   192                    setPort(e.target.value);
   193                  }}
   194                />
   195                <InputBox
   196                  id="user"
   197                  name="user"
   198                  label=""
   199                  placeholder="Enter User"
   200                  value={user}
   201                  onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
   202                    setUser(e.target.value);
   203                  }}
   204                />
   205                <InputBox
   206                  id="password"
   207                  name="password"
   208                  label=""
   209                  placeholder="Enter Password"
   210                  type="password"
   211                  value={password}
   212                  onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
   213                    setPassword(e.target.value);
   214                  }}
   215                />
   216              </Box>
   217            </Box>
   218            <Grid item xs={12} sx={{ margin: "12px 0" }}>
   219              <ReadBox label={"Connection String"} multiLine>
   220                {dsnString}
   221              </ReadBox>
   222            </Grid>
   223          </React.Fragment>
   224        )}
   225        <InputBox
   226          id="table"
   227          name="table"
   228          label="Table"
   229          placeholder="Enter Table Name"
   230          value={table}
   231          tooltip="DB table name to store/update events, table is auto-created"
   232          onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
   233            setTable(e.target.value);
   234          }}
   235        />
   236        <RadioGroup
   237          currentValue={format}
   238          id="format"
   239          name="format"
   240          label="Format"
   241          onChange={(e) => {
   242            setFormat(e.target.value);
   243          }}
   244          tooltip="'namespace' reflects current bucket/object list and 'access' reflects a journal of object operations, defaults to 'namespace'"
   245          selectorOptions={[
   246            { label: "Namespace", value: "namespace" },
   247            { label: "Access", value: "access" },
   248          ]}
   249        />
   250        <InputBox
   251          id="queue-dir"
   252          name="queue_dir"
   253          label="Queue Dir"
   254          placeholder="Enter Queue Dir"
   255          value={queueDir}
   256          tooltip="Staging directory for undelivered messages e.g. '/home/events'"
   257          onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
   258            setQueueDir(e.target.value);
   259          }}
   260        />
   261        <InputBox
   262          id="queue-limit"
   263          name="queue_limit"
   264          label="Queue Limit"
   265          placeholder="Enter Queue Limit"
   266          type="number"
   267          value={queueLimit}
   268          tooltip="Maximum limit for undelivered messages, defaults to '10000'"
   269          onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
   270            setQueueLimit(e.target.value);
   271          }}
   272        />
   273        <CommentBox
   274          id="comment"
   275          name="comment"
   276          label="Comment"
   277          placeholder="Enter custom notes if any"
   278          value={comment}
   279          onChange={(e) => {
   280            setComment(e.target.value);
   281          }}
   282        />
   283      </FormLayout>
   284    );
   285  };
   286  
   287  export default ConfMySql;