goviper-go

Golang Viper: How to take a value from another field if the first one does not exist


I need to read data first of all from the fields inside the service, and only then look at the external fields.

There is a config of this type:

auth_service:
  log_level: debug
  port: 9999
mail_service:
  log_level: debug
  port: 9998
smtp:
  host: localhost
  port: 1025
  username: ""
  password: ""
  email_from: "mailservice@test.test"
  retries_count: 5
log_level: test

I tried to do it via viper.RegisterAlias("log_level", "auth_service.log_level"), but in this form the external field is not read.

The code for getting the config:

type Config struct  {
    LogLevel string `mapstructure:"log_level"`
    Port int `mapstructure:"port"`
    SMTP SMTPConfig `mapstructure:"smtp"`
}

type SMTPConfig struct {
    Host         string     `mapstructure:"host"`
    Port         int        `mapstructure:"port"`
    Username     string     `mapstructure:"username"`
    Password     string     `mapstructure:"password"`
    EmailFrom    string     `mapstructure:"email_from"`
    RetriesCount int        `mapstructure:"retries_count"`
}

func LoadConfig(path string) (*Config, error) {
    type ServiceConfig struct {
        Cfg Config `mapstructure:"auth_service"`
    }
    viper.RegisterAlias("log_level","auth_service.log_level")
    viper.AutomaticEnv()
    if path != "" {
        dir := p.Dir(path)
        file := p.Base(path)
        fileParts := strings.Split(file, ".")
        if len(fileParts) != 2 {
            return nil, fmt.Errorf("incorrect config file: %s", file)
        }
        viper.AddConfigPath(dir)
        viper.SetConfigName(fileParts[0])
        viper.SetConfigType(fileParts[1])
        err := viper.ReadInConfig()
        if err != nil {
            return nil, err
        }
    }
    fmt.Println(viper.AllKeys())
    var config ServiceConfig
    err := viper.Unmarshal(&config)
    if err != nil {
        return nil, err
    }
    return &config.Cfg, nil
}

ServiceConfig is needed so that you can get the value from service_name.field_name, because mapstructure:"service_name.field" is not working.


Solution

  • After digging around, I realized that there is no native way for this and I had to make a fork and add some code. Now you can configure the viper so that the data from the keys is read first and only then the aliases are checked. The possibility of step-by-step verification of each level of aliases has also been added.

    https://github.com/EwvwGeN/viper

    Of course, you can do this using the reflect package by specifying, for example, your new tag alias: "field_one, field_two" in the field, But it was more convenient for me to create a full-fledged fork

    Thank you all for rate this question