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;