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  }