0

I am working on a utility method lets say GetConfig(), which reads the config struct and return it to the caller. GetConfig() does not know what config it is going to read, but the caller knows the struct what it is going to receive.

In this regard, I have written a below utility program:

=========== yaml file data ==========
apiRouting:
  enableThrottling: true
  formFactor: 4
leasing:
  periodInSecs: 10
  preemptionEnable: false
=========== yaml file data ==========

func GetConfig() (interface{}, error) {
    fmt.Println("reading generic service config")
    viper.SetConfigName("service_config")
    viper.AddConfigPath("config/default")
    if err := viper.ReadInConfig(); err != nil {
        return nil, err
    }
    var appConfig interface{}
    if err := viper.Unmarshal(&appConfig); err != nil {
        return nil, err
    }
    return appConfig, nil
}

Caller uses GetConfig() like this: (I have tried 2 options, nothing is working)

type ApiRouting struct {
    EnableThrottling bool  `json:"enableThrottling"`
    FormFactor       int32 `json:"formFactor"`
}

type Leasing struct {
    PeriodInSecs     int32 `json:"periodInSecs"`
    PreemptionEnable bool  `json:"preemptionEnable"`
}

type ServiceConfig struct {
    ApiRouting ApiRouting `json:"apiRouting"`
    Leasing    Leasing    `json:"leasing"`
}

// code snipped [option 1]
    tmpinterface := GetConfig()
    myconfig, ok := tmpinterface.(ServiceConfig)
    if !ok {
        log.Fatal()
    } else {
        println(myconfig)
    }

// code snipped [option 2]
    tmpinterface := GetConfig()
    // Convert map to json string
    jsonStr, err := json.Marshal(tmpinterface)
    if err != nil {
        fmt.Println(err)
    }
    
    // Convert json string to struct
    var sc ServiceConfig
    if err := json.Unmarshal(jsonStr, &sc); err != nil {
        fmt.Println(err)
    }

I have verified that tmpinterface is getting the values correctly in both the cases, but finally myconfig{} struct is empty.

tmpinterface value is:

map[%!f(string=apirouting):map[%!f(string=enablethrottling):%!f(bool=true) %!f(string=formfactor):%!f(int=4)] %!f(string=leasing):map[%!f(string=periodinsecs):%!f(int=10) %!f(string=preemptionenable):%!f(bool=false)]]
6
  • 2
    GetConfig should accept an instance of the concrete config type, i.e. var sc ServiceConfig; GetConfig(&sc). Commented Dec 5, 2022 at 6:23
  • @mkopriva: I cannot use the concrete type here. Because GetConfig code lies in the framework and various services will call it. Framework doesn't really need to know the concrete type. I am very close to my solution, its just I am not able to unmarshall it into the required struct. Commented Dec 5, 2022 at 7:34
  • GetConfig doesn't need to know the concrete type, it can accept any as argument, just like json.Unmarshal or viper.Unmarshal. Commented Dec 5, 2022 at 8:39
  • 1
    Something like this: go.dev/play/p/jshA4lk3Ero Commented Dec 5, 2022 at 8:43
  • 1
    @mkopriva: Really thanks, Any works like charm and it looks clean also. Learnt a good thing today. Commented Dec 5, 2022 at 8:51

1 Answer 1

1

@mkopriva, thanks for a cleaner solution.

func GetConfig(appConfig any) error {
    fmt.Println("reading generic service config")
    viper.SetConfigName("service_config")
    viper.AddConfigPath("config/default")
    if err := viper.ReadInConfig(); err != nil {
        return err
    }
    if err := viper.Unmarshal(appConfig); err != nil {
        return err
    }
    return nil
}

func main() {
    var sc ServiceConfig
    if err := GetConfig(&sc); err != nil {
        panic(err)
    }
    fmt.Println(sc)
}
Sign up to request clarification or add additional context in comments.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.