-
Notifications
You must be signed in to change notification settings - Fork 18
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Browse the repository at this point in the history
- Loading branch information
Showing
8 changed files
with
640 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
|
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
package config | ||
|
||
type Config struct{} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,149 @@ | ||
package config_test | ||
|
||
import ( | ||
"os" | ||
"reflect" | ||
"strings" | ||
"testing" | ||
|
||
"github.com/knadh/koanf" | ||
"github.com/knadh/koanf/parsers/yaml" | ||
"github.com/knadh/koanf/providers/env" | ||
"github.com/knadh/koanf/providers/file" | ||
"github.com/knadh/koanf/providers/structs" | ||
) | ||
|
||
type structConfig struct { | ||
Debug bool `koanf:"debug"` | ||
MultiWordVar string `koanf:"multi_word_var"` | ||
DB db `koanf:"db"` | ||
} | ||
|
||
type db struct { | ||
Host string `koanf:"host"` | ||
Username string `koanf:"username"` | ||
Password string `koanf:"password"` | ||
MultiWordNestedVar string `koanf:"multi_word_nested_var"` | ||
NestedMultiWordConfig nestedMultiWordConfig `koanf:"nested_multi_word_config"` | ||
} | ||
|
||
type nestedMultiWordConfig struct { | ||
DownHere string `koanf:"down_here"` | ||
} | ||
|
||
const ( | ||
prefix = "ORMUS_" | ||
delimiter = "." | ||
separator = "__" | ||
) | ||
|
||
func callbackEnv(source string) string { | ||
base := strings.ToLower(strings.TrimPrefix(source, prefix)) | ||
|
||
return strings.ReplaceAll(base, separator, delimiter) | ||
} | ||
|
||
func TestLoadingDefaultConfigFromStruct(t *testing.T) { | ||
k := koanf.New(".") | ||
|
||
testStruct := structConfig{ | ||
Debug: false, | ||
MultiWordVar: "Im complex in default", | ||
DB: db{ | ||
Host: "localhost", | ||
Username: "hossein", | ||
Password: "1234", | ||
MultiWordNestedVar: "Oh this is too long", | ||
}, | ||
} | ||
|
||
if err := k.Load(structs.Provider(testStruct, "koanf"), nil); err != nil { | ||
t.Fatalf("error loading default config: %s", err) | ||
} | ||
|
||
var instance structConfig | ||
if err := k.Unmarshal("", &instance); err != nil { | ||
t.Fatalf("error unmarshaling config: %s", err) | ||
} | ||
|
||
if !reflect.DeepEqual(instance, testStruct) { | ||
t.Fatalf("expected: %+v, got: %+v", testStruct, instance) | ||
} | ||
} | ||
|
||
func TestLoadingConfigFromYamlFile(t *testing.T) { | ||
k := koanf.New(".") | ||
|
||
ymlConfigTest := []byte(`debug: false | ||
multi_word_var: "I'm complex in config.yml" | ||
db: | ||
host: "localhost" | ||
username: "ali" | ||
password: "passwd" | ||
multi_word_nested_var: "WHAT??"`) | ||
|
||
ymlFile, _ := os.Create("test.yml") | ||
defer ymlFile.Close() | ||
defer os.Remove("test.yml") | ||
ymlFile.Write(ymlConfigTest) | ||
// load configuration from yaml file | ||
if err := k.Load(file.Provider("test.yml"), yaml.Parser()); err != nil { | ||
t.Logf("error loading config from `config.yml` file: %s", err) | ||
} | ||
|
||
want := structConfig{ | ||
Debug: false, | ||
MultiWordVar: "I'm complex in config.yml", | ||
DB: db{ | ||
Host: "localhost", | ||
Username: "ali", | ||
Password: "passwd", | ||
MultiWordNestedVar: "WHAT??", | ||
}, | ||
} | ||
|
||
var instance structConfig | ||
if err := k.Unmarshal("", &instance); err != nil { | ||
t.Fatalf("error unmarshaling config: %s", err) | ||
} | ||
|
||
if !reflect.DeepEqual(want, instance) { | ||
t.Fatalf("expected: %+v, got: %+v", want, instance) | ||
} | ||
} | ||
|
||
func TestLoadConfigFromEnvironmentVariable(t *testing.T) { | ||
k := koanf.New(".") | ||
|
||
os.Setenv("ORMUS_DEBUG", "false") | ||
os.Setenv("ORMUS_MULTI_WORD_VAR", "this is multi word var") | ||
os.Setenv("ORMUS_DB__HOST", "localhost") | ||
os.Setenv("ORMUS_DB__USERNAME", "hossein") | ||
os.Setenv("ORMUS_DB__PASSWORD", "1234") | ||
os.Setenv("ORMUS_DB__MULTI_WORD_NESTED_VAR", "testing make it easy (:") | ||
os.Setenv("ORMUS_DB__NESTED_MULTI_WORD_CONFIG__DOWN_HERE", "im here") | ||
|
||
if err := k.Load(env.Provider(prefix, delimiter, callbackEnv), nil); err != nil { | ||
t.Logf("error loading environment variables: %s", err) | ||
} | ||
|
||
var instance structConfig | ||
if err := k.Unmarshal("", &instance); err != nil { | ||
t.Fatalf("error unmarshaling config: %s", err) | ||
} | ||
|
||
want := structConfig{ | ||
Debug: false, | ||
MultiWordVar: "this is multi word var", | ||
DB: db{ | ||
Host: "localhost", | ||
Username: "hossein", | ||
Password: "1234", | ||
MultiWordNestedVar: "testing make it easy (:", | ||
NestedMultiWordConfig: nestedMultiWordConfig{DownHere: "im here"}, | ||
}, | ||
} | ||
if !reflect.DeepEqual(instance, want) { | ||
t.Fatalf("expected: %+v, got: %+v", want, instance) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
package config | ||
|
||
func Default() Config { | ||
return Config{} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,86 @@ | ||
package config | ||
|
||
// TODO: use Viper/Koanf - the config loader should support default values, json/yaml config files and finally overwrite them with environment variables | ||
import ( | ||
"log" | ||
"strings" | ||
|
||
"github.com/knadh/koanf" | ||
"github.com/knadh/koanf/parsers/yaml" | ||
"github.com/knadh/koanf/providers/env" | ||
"github.com/knadh/koanf/providers/file" | ||
"github.com/knadh/koanf/providers/structs" | ||
) | ||
|
||
const ( | ||
defaultPrefix = "ORMUS_" | ||
defaultDelimiter = "." | ||
defaultSeparator = "__" | ||
defaultYamlFilePath = "config.yml" | ||
) | ||
|
||
var c Config | ||
|
||
type Option struct { | ||
Prefix string | ||
Delimiter string | ||
Separator string | ||
YamlFilePath string | ||
CallbackEnv func(string) string | ||
} | ||
|
||
// our environment variables must prefix with `ORMUS_` | ||
// for nested env should use `__` aka: ORMUS_DB__HOST. | ||
func defaultCallbackEnv(source string) string { | ||
base := strings.ToLower(strings.TrimPrefix(source, defaultPrefix)) | ||
|
||
return strings.ReplaceAll(base, defaultSeparator, defaultDelimiter) | ||
} | ||
|
||
func init() { | ||
k := koanf.New(defaultDelimiter) | ||
|
||
// load default configuration from Default function | ||
if err := k.Load(structs.Provider(Default(), "koanf"), nil); err != nil { | ||
log.Fatalf("error loading default config: %s", err) | ||
} | ||
|
||
// load configuration from yaml file | ||
if err := k.Load(file.Provider(defaultYamlFilePath), yaml.Parser()); err != nil { | ||
log.Printf("error loading config from `config.yml` file: %s", err) | ||
} | ||
|
||
// load from environment variable | ||
if err := k.Load(env.Provider(defaultPrefix, defaultDelimiter, defaultCallbackEnv), nil); err != nil { | ||
log.Printf("error loading environment variables: %s", err) | ||
} | ||
|
||
if err := k.Unmarshal("", &c); err != nil { | ||
log.Fatalf("error unmarshaling config: %s", err) | ||
} | ||
} | ||
|
||
func C() Config { | ||
return c | ||
} | ||
|
||
func New(opt Option) Config { | ||
k := koanf.New(opt.Separator) | ||
|
||
if err := k.Load(structs.Provider(Default(), "koanf"), nil); err != nil { | ||
log.Fatalf("error loading default config: %s", err) | ||
} | ||
|
||
if err := k.Load(file.Provider(opt.YamlFilePath), yaml.Parser()); err != nil { | ||
log.Printf("error loading config from `config.yml` file: %s", err) | ||
} | ||
|
||
if err := k.Load(env.Provider(opt.Prefix, opt.Delimiter, opt.CallbackEnv), nil); err != nil { | ||
log.Printf("error loading environment variables: %s", err) | ||
} | ||
|
||
if err := k.Unmarshal("", &c); err != nil { | ||
log.Fatalf("error unmarshaling config: %s", err) | ||
} | ||
|
||
return c | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.