github.com/aldelo/common@v1.5.1/wrapper/viper/viperconf.go (about) 1 package viper 2 3 /* 4 * Copyright 2020-2023 Aldelo, LP 5 * 6 * Licensed under the Apache License, Version 2.0 (the "License"); 7 * you may not use this file except in compliance with the License. 8 * You may obtain a copy of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, software 13 * distributed under the License is distributed on an "AS IS" BASIS, 14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 * See the License for the specific language governing permissions and 16 * limitations under the License. 17 */ 18 19 import ( 20 "errors" 21 util "github.com/aldelo/common" 22 "github.com/spf13/viper" 23 "strings" 24 "time" 25 ) 26 27 // ViperConf struct info, 28 // ConfigName or SpecificConfigFileFullPath = One of the field is required 29 // UseYAML = true uses YAML; false uses JSON 30 // UseAutomaticEnvVar = true will auto load environment variables that matches to key 31 // AppName = used by config path options, identifies the name of the app 32 // UseConfig... = indicates config file search pattern 33 type ViperConf struct { 34 // define config properties 35 ConfigName string 36 SpecificConfigFileFullPath string 37 38 UseYAML bool 39 UseAutomaticEnvVar bool 40 41 AppName string 42 UseConfigPathEtcAppName bool 43 UseConfigPathHomeAppName bool 44 CustomConfigPath string 45 46 // cache viper config object 47 viperClient *viper.Viper 48 } 49 50 // Init will initialize config and readInConfig 51 // if config file does not exist, false is returned 52 func (v *ViperConf) Init() (bool, error) { 53 // validate 54 if util.LenTrim(v.ConfigName) <= 0 && util.LenTrim(v.SpecificConfigFileFullPath) <= 0 { 55 return false, errors.New("Init Config Failed: " + "Either Config Name or Config Full Path is Required") 56 } 57 58 // create new viper client object if needed 59 if v.viperClient == nil { 60 v.viperClient = viper.New() 61 } 62 63 // set viper properties 64 if util.LenTrim(v.SpecificConfigFileFullPath) <= 0 { 65 v.viperClient.SetConfigName(v.ConfigName) 66 67 if util.LenTrim(v.AppName) > 0 { 68 if v.UseConfigPathEtcAppName { 69 v.viperClient.AddConfigPath("/etc/" + v.AppName + "/") 70 } 71 72 if v.UseConfigPathHomeAppName { 73 v.viperClient.AddConfigPath("$HOME/." + v.AppName) 74 } 75 } 76 77 if util.LenTrim(v.CustomConfigPath) > 0 && v.CustomConfigPath != "." { 78 v.viperClient.AddConfigPath(v.CustomConfigPath) 79 } 80 81 v.viperClient.AddConfigPath(".") 82 } else { 83 v.viperClient.SetConfigFile(v.SpecificConfigFileFullPath) 84 } 85 86 if v.UseAutomaticEnvVar { 87 v.viperClient.AutomaticEnv() 88 } 89 90 if v.UseYAML { 91 v.viperClient.SetConfigType("yaml") 92 } else { 93 v.viperClient.SetConfigType("json") 94 } 95 96 v.viperClient.SetTypeByDefaultValue(true) 97 98 // read in config data 99 if err := v.viperClient.ReadInConfig(); err != nil { 100 if _, ok := err.(viper.ConfigFileNotFoundError); ok { 101 // config file not found, ignore error 102 return false, nil 103 } else { 104 // config file found, but other error occurred 105 return false, errors.New("Init Config Failed: (ReadInConfig Action) " + err.Error()) 106 } 107 } else { 108 // read success 109 // v.viperClient.WatchConfig() 110 return true, nil 111 } 112 } 113 114 // WatchConfig watches if config file does not exist, this call will panic 115 func (v *ViperConf) WatchConfig() { 116 if v.viperClient != nil { 117 v.viperClient.WatchConfig() 118 } 119 } 120 121 // ConfigFileUsed returns the current config file full path in use 122 func (v *ViperConf) ConfigFileUsed() string { 123 if v.viperClient != nil { 124 return v.viperClient.ConfigFileUsed() 125 } else { 126 return "" 127 } 128 } 129 130 // Default will set default key value pairs, allows method chaining 131 func (v *ViperConf) Default(key string, value interface{}) *ViperConf { 132 if v.viperClient == nil { 133 v.viperClient = viper.New() 134 } 135 136 if util.LenTrim(key) > 0 { 137 v.viperClient.SetDefault(key, value) 138 } 139 140 return v 141 } 142 143 // Unmarshal serializes config content into output struct, optionally serializes a portion of config identified by key into a struct 144 // 145 // struct tag use `mapstructure:""` 146 // if struct tag refers to sub element of yaml, define struct to contain the sub elements 147 // 148 // for example: 149 // parentkey: 150 // childkey: abc 151 // childkey_2: xyz 152 // type Child struct { ChildKey string `mapstructure:"childkey"` ChildKey2 string `mapstructure:"childkey_2"` } 153 // type Parent struct { ChildData Child `mapstructure:"parentkey"`} 154 func (v *ViperConf) Unmarshal(outputStructPtr interface{}, key ...string) error { 155 if v.viperClient == nil { 156 return errors.New("Unmarshal Config To Struct Failed: " + "Config Object Needs Initialized") 157 } 158 159 if outputStructPtr == nil { 160 return errors.New("Unmarshal Config To Struct Failed: " + "Output Struct Ptr is Required") 161 } 162 163 var err error 164 165 if len(key) <= 0 { 166 err = v.viperClient.Unmarshal(outputStructPtr) 167 } else { 168 err = v.viperClient.UnmarshalKey(key[0], outputStructPtr) 169 } 170 171 if err != nil { 172 return errors.New("Unmarshal Config To Struct Failed: (Unmarshal Error) " + err.Error()) 173 } else { 174 return nil 175 } 176 } 177 178 // SubConf returns a sub config object based on the given key 179 func (v *ViperConf) SubConf(key string) *ViperConf { 180 if v.viperClient != nil { 181 subViper := v.viperClient.Sub(key) 182 183 if subViper != nil { 184 return &ViperConf{ 185 ConfigName: v.ConfigName, 186 SpecificConfigFileFullPath: v.SpecificConfigFileFullPath, 187 UseYAML: v.UseYAML, 188 UseAutomaticEnvVar: v.UseAutomaticEnvVar, 189 AppName: v.AppName, 190 UseConfigPathEtcAppName: v.UseConfigPathEtcAppName, 191 UseConfigPathHomeAppName: v.UseConfigPathHomeAppName, 192 CustomConfigPath: v.CustomConfigPath, 193 viperClient: subViper, 194 } 195 } else { 196 return nil 197 } 198 } else { 199 return nil 200 } 201 } 202 203 // Set will set key value pair into config object 204 func (v *ViperConf) Set(key string, value interface{}) *ViperConf { 205 if v.viperClient != nil && util.LenTrim(key) > 0 { 206 v.viperClient.Set(key, value) 207 } 208 209 return v 210 } 211 212 // Save will save the current config object into target config disk file 213 func (v *ViperConf) Save() error { 214 if v.viperClient == nil { 215 return errors.New("Save Config Failed: " + "Config Client Not Initialized") 216 } 217 218 fileName := v.SpecificConfigFileFullPath 219 220 if util.LenTrim(fileName) <= 0 { 221 fileName = v.ConfigFileUsed() 222 223 if util.LenTrim(fileName) <= 0 { 224 fileName = "./" + v.ConfigName 225 226 if v.UseYAML { 227 fileName += ".yaml" 228 } else { 229 fileName += ".json" 230 } 231 } 232 } 233 234 var err error 235 236 if util.FileExists(fileName) { 237 err = v.viperClient.WriteConfig() 238 } else { 239 err = v.viperClient.WriteConfigAs(fileName) 240 } 241 242 if err != nil { 243 if strings.Contains(strings.ToLower(err.Error()), "invalid trailing UTF-8 octet") { 244 return errors.New("Save Config Failed: (WriteConfig Action) " + "Config File Name Must Not Be Same as Folder Name") 245 } else { 246 return errors.New("Save Config Failed: (WriteConfig Action) " + err.Error()) 247 } 248 } else { 249 return nil 250 } 251 } 252 253 // Alias will create an alias for the related key, 254 // this allows the alias name and key name both refer to the same stored config data 255 func (v *ViperConf) Alias(key string, alias string) *ViperConf { 256 if v.viperClient != nil && util.LenTrim(key) > 0 && util.LenTrim(alias) > 0 { 257 v.viperClient.RegisterAlias(alias, key) 258 } 259 260 return v 261 } 262 263 // IsDefined indicates if a key is defined within the config file 264 func (v *ViperConf) IsDefined(key string) bool { 265 if v.viperClient != nil && util.LenTrim(key) > 0 { 266 return v.viperClient.InConfig(key) 267 } else { 268 return false 269 } 270 } 271 272 // IsSet indicates if a key's value was set within the config file 273 func (v *ViperConf) IsSet(key string) bool { 274 if v.viperClient != nil && util.LenTrim(key) > 0 { 275 return v.viperClient.IsSet(key) 276 } else { 277 return false 278 } 279 } 280 281 // Size returns the given key's value in bytes 282 func (v *ViperConf) Size(key string) int64 { 283 if v.viperClient != nil && util.LenTrim(key) > 0 { 284 return int64(v.viperClient.GetSizeInBytes(key)) 285 } else { 286 return 0 287 } 288 } 289 290 // Get returns value by key 291 func (v *ViperConf) Get(key string) interface{} { 292 if v.viperClient != nil && util.LenTrim(key) > 0 { 293 return v.viperClient.Get(key) 294 } else { 295 return nil 296 } 297 } 298 299 // GetInt returns value by key 300 func (v *ViperConf) GetInt(key string) int { 301 if v.viperClient != nil && util.LenTrim(key) > 0 { 302 return v.viperClient.GetInt(key) 303 } else { 304 return 0 305 } 306 } 307 308 // GetIntSlice returns value by key 309 func (v *ViperConf) GetIntSlice(key string) []int { 310 if v.viperClient != nil && util.LenTrim(key) > 0 { 311 return v.viperClient.GetIntSlice(key) 312 } else { 313 return nil 314 } 315 } 316 317 // GetInt64 returns value by key 318 func (v *ViperConf) GetInt64(key string) int64 { 319 if v.viperClient != nil && util.LenTrim(key) > 0 { 320 return v.viperClient.GetInt64(key) 321 } else { 322 return 0 323 } 324 } 325 326 // GetFloat64 returns value by key 327 func (v *ViperConf) GetFloat64(key string) float64 { 328 if v.viperClient != nil && util.LenTrim(key) > 0 { 329 return v.viperClient.GetFloat64(key) 330 } else { 331 return 0.00 332 } 333 } 334 335 // GetBool returns value by key 336 func (v *ViperConf) GetBool(key string) bool { 337 if v.viperClient != nil && util.LenTrim(key) > 0 { 338 return v.viperClient.GetBool(key) 339 } else { 340 return false 341 } 342 } 343 344 // GetTime returns value by key 345 func (v *ViperConf) GetTime(key string) time.Time { 346 if v.viperClient != nil && util.LenTrim(key) > 0 { 347 return v.viperClient.GetTime(key) 348 } else { 349 return time.Time{} 350 } 351 } 352 353 // GetDuration returns value by key 354 func (v *ViperConf) GetDuration(key string) time.Duration { 355 if v.viperClient != nil && util.LenTrim(key) > 0 { 356 return v.viperClient.GetDuration(key) 357 } else { 358 return 0 359 } 360 } 361 362 // GetString returns value by key 363 func (v *ViperConf) GetString(key string) string { 364 if v.viperClient != nil && util.LenTrim(key) > 0 { 365 return v.viperClient.GetString(key) 366 } else { 367 return "" 368 } 369 } 370 371 // GetStringSlice returns value by key 372 func (v *ViperConf) GetStringSlice(key string) []string { 373 if v.viperClient != nil && util.LenTrim(key) > 0 { 374 return v.viperClient.GetStringSlice(key) 375 } else { 376 return nil 377 } 378 } 379 380 // GetStringMapInterface returns value by key 381 func (v *ViperConf) GetStringMapInterface(key string) map[string]interface{} { 382 if v.viperClient != nil && util.LenTrim(key) > 0 { 383 return v.viperClient.GetStringMap(key) 384 } else { 385 return nil 386 } 387 } 388 389 // GetStringMapString returns value by key 390 func (v *ViperConf) GetStringMapString(key string) map[string]string { 391 if v.viperClient != nil && util.LenTrim(key) > 0 { 392 return v.viperClient.GetStringMapString(key) 393 } else { 394 return nil 395 } 396 } 397 398 // GetStringMapStringSlice returns value by key 399 func (v *ViperConf) GetStringMapStringSlice(key string) map[string][]string { 400 if v.viperClient != nil && util.LenTrim(key) > 0 { 401 return v.viperClient.GetStringMapStringSlice(key) 402 } else { 403 return nil 404 } 405 } 406 407 // AllKeys returns all keys in config file 408 func (v *ViperConf) AllKeys() []string { 409 if v.viperClient != nil { 410 return v.viperClient.AllKeys() 411 } else { 412 return nil 413 } 414 } 415 416 // AllSettings returns map of all settings in config file 417 func (v *ViperConf) AllSettings() map[string]interface{} { 418 if v.viperClient != nil { 419 return v.viperClient.AllSettings() 420 } else { 421 return nil 422 } 423 }