github.com/minio/console@v1.4.1/web-app/src/screens/Console/EventDestinations/CustomForms/EditConfiguration.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, { Fragment, useCallback, useEffect, useState } from "react"; 18 import get from "lodash/get"; 19 import { Box, Button, Grid, Loader } from "mds"; 20 import { useLocation, useNavigate } from "react-router-dom"; 21 import { useSelector } from "react-redux"; 22 import { api } from "api"; 23 import { Configuration, ConfigurationKV } from "api/consoleApi"; 24 import { errorToHandler } from "api/errors"; 25 import { 26 fieldsConfigurations, 27 overrideFields, 28 removeEmptyFields, 29 } from "../../Configurations/utils"; 30 import { 31 IConfigurationElement, 32 IElementValue, 33 IOverrideEnv, 34 KVField, 35 } from "../../Configurations/types"; 36 import { 37 configurationIsLoading, 38 setErrorSnackMessage, 39 setHelpName, 40 setServerNeedsRestart, 41 setSnackBarMessage, 42 } from "../../../../systemSlice"; 43 import { AppState, useAppDispatch } from "../../../../store"; 44 import WebhookSettings from "../WebhookSettings/WebhookSettings"; 45 import ConfTargetGeneric from "../ConfTargetGeneric"; 46 import ResetConfigurationModal from "./ResetConfigurationModal"; 47 48 interface IAddNotificationEndpointProps { 49 selectedConfiguration: IConfigurationElement; 50 className?: string; 51 } 52 53 const EditConfiguration = ({ 54 selectedConfiguration, 55 className = "", 56 }: IAddNotificationEndpointProps) => { 57 const dispatch = useAppDispatch(); 58 const navigate = useNavigate(); 59 const { pathname = "" } = useLocation(); 60 61 let selConfigTab = pathname.substring(pathname.lastIndexOf("/") + 1); 62 selConfigTab = selConfigTab === "settings" ? "region" : selConfigTab; 63 64 //Local States 65 const [valuesObj, setValueObj] = useState<IElementValue[]>([]); 66 const [saving, setSaving] = useState<boolean>(false); 67 const [configValues, setConfigValues] = useState<IElementValue[]>([]); 68 const [configSubsysList, setConfigSubsysList] = useState<Configuration[]>([]); 69 const [resetConfigurationOpen, setResetConfigurationOpen] = 70 useState<boolean>(false); 71 const [overrideEnvs, setOverrideEnvs] = useState<IOverrideEnv>({}); 72 73 const loadingConfig = useSelector( 74 (state: AppState) => state.system.loadingConfigurations, 75 ); 76 77 useEffect(() => { 78 dispatch(configurationIsLoading(true)); 79 }, [selConfigTab, dispatch]); 80 81 useEffect(() => { 82 if (loadingConfig) { 83 const configId = get(selectedConfiguration, "configuration_id", false); 84 85 if (configId) { 86 api.configs 87 .configInfo(configId) 88 .then((res) => { 89 setConfigSubsysList(res.data); 90 let values: ConfigurationKV[] = get(res.data[0], "key_values", []); 91 92 const fieldsConfig: KVField[] = fieldsConfigurations[configId]; 93 94 const keyVals: IElementValue[] = fieldsConfig.map((field) => { 95 const includedValue = values.find( 96 (element: ConfigurationKV) => element.key === field.name, 97 ); 98 const customValue = includedValue?.value || ""; 99 100 return { 101 key: field.name, 102 value: field.customValueProcess 103 ? field.customValueProcess(customValue) 104 : customValue, 105 env_override: includedValue?.env_override, 106 }; 107 }); 108 109 setConfigValues(keyVals); 110 setOverrideEnvs(overrideFields(keyVals)); 111 dispatch(configurationIsLoading(false)); 112 }) 113 .catch((err) => { 114 dispatch(configurationIsLoading(false)); 115 dispatch(setErrorSnackMessage(errorToHandler(err.error))); 116 }); 117 118 return; 119 } 120 dispatch(configurationIsLoading(false)); 121 } 122 }, [loadingConfig, selectedConfiguration, dispatch]); 123 124 useEffect(() => { 125 if (saving) { 126 const payload = { 127 key_values: removeEmptyFields(valuesObj), 128 }; 129 api.configs 130 .setConfig(selectedConfiguration.configuration_id, payload) 131 .then((res) => { 132 setSaving(false); 133 dispatch(setServerNeedsRestart(res.data.restart || false)); 134 dispatch(configurationIsLoading(true)); 135 if (!res.data.restart) { 136 dispatch(setSnackBarMessage("Configuration saved successfully")); 137 } 138 }) 139 .catch((err) => { 140 setSaving(false); 141 dispatch(setErrorSnackMessage(errorToHandler(err.error))); 142 }); 143 } 144 }, [saving, dispatch, selectedConfiguration, valuesObj, navigate]); 145 146 //Fetch Actions 147 const submitForm = (event: React.FormEvent) => { 148 event.preventDefault(); 149 setSaving(true); 150 }; 151 152 const onValueChange = useCallback( 153 (newValue: IElementValue[]) => { 154 setValueObj(newValue); 155 }, 156 [setValueObj], 157 ); 158 159 const continueReset = (restart: boolean) => { 160 setResetConfigurationOpen(false); 161 dispatch(setServerNeedsRestart(restart)); 162 if (restart) { 163 dispatch(configurationIsLoading(true)); 164 } 165 }; 166 167 const resetConfigurationMOpen = () => { 168 setResetConfigurationOpen(true); 169 }; 170 171 return ( 172 <Fragment> 173 <div 174 onMouseMove={() => { 175 dispatch( 176 setHelpName( 177 `settings_${selectedConfiguration.configuration_label}`, 178 ), 179 ); 180 }} 181 > 182 {resetConfigurationOpen && ( 183 <ResetConfigurationModal 184 configurationName={selectedConfiguration.configuration_id} 185 closeResetModalAndRefresh={continueReset} 186 resetOpen={resetConfigurationOpen} 187 /> 188 )} 189 {loadingConfig ? ( 190 <Grid item xs={12} sx={{ textAlign: "center", paddingTop: "15px" }}> 191 <Loader /> 192 </Grid> 193 ) : ( 194 <Box 195 sx={{ 196 padding: "15px", 197 height: "100%", 198 }} 199 > 200 {selectedConfiguration.configuration_id === "logger_webhook" || 201 selectedConfiguration.configuration_id === "audit_webhook" ? ( 202 <WebhookSettings 203 WebhookSettingslist={configSubsysList} 204 setResetConfigurationOpen={resetConfigurationMOpen} 205 type={selectedConfiguration.configuration_id} 206 /> 207 ) : ( 208 <Fragment> 209 <form 210 noValidate 211 onSubmit={submitForm} 212 className={className} 213 style={{ 214 height: "100%", 215 display: "flex", 216 flexFlow: "column", 217 }} 218 > 219 <Grid 220 item 221 xs={12} 222 sx={{ 223 display: "grid", 224 gridTemplateColumns: "1fr", 225 gap: "10px", 226 }} 227 > 228 <ConfTargetGeneric 229 fields={ 230 fieldsConfigurations[ 231 selectedConfiguration.configuration_id 232 ] 233 } 234 onChange={onValueChange} 235 defaultVals={configValues} 236 overrideEnv={overrideEnvs} 237 /> 238 </Grid> 239 <Grid 240 item 241 xs={12} 242 sx={{ 243 paddingTop: "15px ", 244 textAlign: "right" as const, 245 maxHeight: "60px", 246 display: "flex", 247 alignItems: "center", 248 justifyContent: "flex-end", 249 }} 250 > 251 <Button 252 type={"button"} 253 id={"restore-defaults"} 254 variant="secondary" 255 onClick={resetConfigurationMOpen} 256 label={"Restore Defaults"} 257 /> 258 259 <Button 260 id={"save"} 261 type="submit" 262 variant="callAction" 263 disabled={saving} 264 label={"Save"} 265 /> 266 </Grid> 267 </form> 268 </Fragment> 269 )} 270 </Box> 271 )} 272 </div> 273 </Fragment> 274 ); 275 }; 276 277 export default EditConfiguration;