Skip to content

Commit

Permalink
Allow reading from env file
Browse files Browse the repository at this point in the history
  • Loading branch information
huykingsofm committed Jan 8, 2023
1 parent a8d21a3 commit 8411e9c
Show file tree
Hide file tree
Showing 5 changed files with 81 additions and 2 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
# v1.3.0 (Jan 9, 2023)

1. Allow reading environment variables from file or os.

# v1.2.0 (Jan 8, 2023)

1. Allow converting config to map.
Expand Down
50 changes: 50 additions & 0 deletions config.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,11 @@ import (
"io/ioutil"
"os"
"strings"
"time"

"github.com/fsnotify/fsnotify"
"github.com/go-ini/ini"
"github.com/joho/godotenv"
"github.com/xybor-x/xyerror"
"github.com/xybor-x/xylock"
"github.com/xybor-x/xylog"
Expand All @@ -43,6 +45,7 @@ const (
UnknownFormat Format = iota
JSON
INI
ENV
)

var loggerName = "xybor.xyplatform.xyconfig"
Expand All @@ -51,6 +54,7 @@ var logger = xylog.GetLogger(loggerName)
var extensions = map[string]Format{
".json": JSON,
".ini": INI,
".env": ENV,
}

// Event represents for a changes in the config.
Expand Down Expand Up @@ -83,6 +87,9 @@ type Config struct {
// watcher tracks changes of files.
watcher *fsnotify.Watcher

// envWatcher tracks the waching of environment variables.
envWatcher *time.Timer

// lock avoids race condition.
lock *xylock.RWLock
}
Expand Down Expand Up @@ -143,6 +150,12 @@ func (c *Config) CloseWatcher() error {
err = c.watcher.Close()
c.watcher = nil
}

if c.envWatcher != nil {
c.envWatcher.Stop()
c.envWatcher = nil
}

return err
}

Expand Down Expand Up @@ -288,13 +301,29 @@ func (c *Config) ReadINI(b []byte) error {
return nil
}

// ReadENV reads the config values from a byte array under ENV format.
func (c *Config) ReadENV(b []byte) error {
var envmap, err = godotenv.Unmarshal(string(b))
if err != nil {
return ConfigError.New(err)
}

for k, v := range envmap {
c.Set(k, v, false)
}

return nil
}

// ReadBytes reads the config values from a bytes array under any format.
func (c *Config) ReadBytes(format Format, b []byte) error {
switch format {
case JSON:
return c.ReadJSON(b)
case INI:
return c.ReadINI(b)
case ENV:
return c.ReadENV(b)
default:
return FormatError.New("unsupported format")
}
Expand Down Expand Up @@ -337,6 +366,27 @@ func (c *Config) ReadFile(filename string, watch bool) error {
return nil
}

// LoadEnv loads all environment variables and watch for their changes every
// duration. Set the duration as zero if no need to watch the change.
func (c *Config) LoadEnv(d time.Duration) error {
var envs = os.Environ()
for i := range envs {
var key, value, found = strings.Cut(envs[i], "=")
if !found {
return FormatError.Newf("invalid environment variable %s", envs[i])
}
c.Set(key, value, false)
}

if d != 0 {
c.lock.Lock()
c.envWatcher = time.AfterFunc(d, func() { c.LoadEnv(d) })
c.lock.Unlock()
}

return nil
}

// Get returns the value assigned with the key. The latter returned value is
// false if they key doesn't exist.
func (c *Config) Get(key string) (Value, bool) {
Expand Down
24 changes: 24 additions & 0 deletions config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ package xyconfig_test

import (
"io/ioutil"
"os"
"testing"
"time"

Expand Down Expand Up @@ -150,6 +151,15 @@ func TestConfigReadByteINI(t *testing.T) {
xycond.ExpectTrue(cfg.MustGet("buzz.nil").IsNil()).Test(t)
}

func TestConfigReadByteENV(t *testing.T) {
var cfg = xyconfig.GetConfig(t.Name())
cfg.ReadBytes(xyconfig.ENV, []byte("fizz=bar\nbizz=bemm\nnil="))

xycond.ExpectEqual(cfg.MustGet("fizz").MustString(), "bar").Test(t)
xycond.ExpectEqual(cfg.MustGet("bizz").MustString(), "bemm").Test(t)
xycond.ExpectTrue(cfg.MustGet("nil").IsNil()).Test(t)
}

func TestConfigReadByteUnknown(t *testing.T) {
var cfg = xyconfig.GetConfig(t.Name())
var err = cfg.ReadBytes(xyconfig.UnknownFormat, []byte(""))
Expand Down Expand Up @@ -192,6 +202,20 @@ func TestConfigReadFileWithChange(t *testing.T) {
xycond.ExpectEqual(cfg.MustGet("foo").MustString(), "buzz").Test(t)
}

func TestConfigLoadEnvWithChange(t *testing.T) {
os.Setenv("foo", "bar")

var cfg = xyconfig.GetConfig(t.Name())
defer cfg.CloseWatcher()

cfg.LoadEnv(time.Millisecond)
xycond.ExpectEqual(cfg.MustGet("foo").MustString(), "bar").Test(t)

os.Setenv("foo", "buzz")
time.Sleep(2 * time.Millisecond)
xycond.ExpectEqual(cfg.MustGet("foo").MustString(), "buzz").Test(t)
}

func TestConfigReadFileWithErrorFileAfterChange(t *testing.T) {
ioutil.WriteFile(t.Name()+".json", []byte(`{"foo": "bar"}`), 0644)

Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ go 1.18
require (
github.com/fsnotify/fsnotify v1.6.0
github.com/go-ini/ini v1.67.0
github.com/joho/godotenv v1.4.0
github.com/xybor-x/xycond v1.0.0
github.com/xybor-x/xyerror v1.0.5
github.com/xybor-x/xylock v0.0.1
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4
github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
github.com/go-ini/ini v1.67.0 h1:z6ZrTEZqSWOTyH2FlglNbNgARyHG8oLW9gMELqKr06A=
github.com/go-ini/ini v1.67.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
github.com/joho/godotenv v1.4.0 h1:3l4+N6zfMWnkbPEXKng2o2/MR5mSwTrBih4ZEkkz1lg=
github.com/joho/godotenv v1.4.0/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
Expand All @@ -16,8 +18,6 @@ github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKs
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/xybor-x/xycond v1.0.0 h1:RJippg8hc0/HcEerJQrddHok7iaZUyLGrjN6Pbpe8u8=
github.com/xybor-x/xycond v1.0.0/go.mod h1:b/NXVIxFPWvGyHAAdkNooYSw9IpqF04VuW2+i7iE8sU=
github.com/xybor-x/xyerror v1.0.0 h1:tFqmi+SOnNrUaL1FDzp5qr/Ed0SY0tUfm1inmFMrt7g=
github.com/xybor-x/xyerror v1.0.0/go.mod h1:2P0SjxOPAkdllEpPhmlWYVOGKDVxzwl/RkmLcc2i9Os=
github.com/xybor-x/xyerror v1.0.5 h1:SiC0OWcVDvb3MwLMTFWCvnrACYln3KbYkhr9nehtfV0=
github.com/xybor-x/xyerror v1.0.5/go.mod h1:2P0SjxOPAkdllEpPhmlWYVOGKDVxzwl/RkmLcc2i9Os=
github.com/xybor-x/xylock v0.0.1 h1:/LhbJb6WJkUW+xE4/bBpIC76BKrbgnayiId6LdouqUw=
Expand Down

0 comments on commit 8411e9c

Please sign in to comment.