Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Envopts #33

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ Confita is a library that loads configuration from multiple backends and stores
## Install

```sh
go get -u github.com/heetch/confita
go get -u github.com/karantin2020/confita
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks like a leftover from another fork.

```

## Usage
Expand Down Expand Up @@ -95,7 +95,10 @@ A loader can take other configured backends as parameters.

```go
loader := confita.NewLoader(
env.NewBackend(),
env.NewBackend(
env.WithPrefix("PREFIX"),
env.ToUpper(),
),
file.NewBackend("/path/to/config.json"),
file.NewBackend("/path/to/config.yaml"),
flags.NewBackend(),
Expand Down Expand Up @@ -124,7 +127,6 @@ err := loader.Load(ctx, &cfg)
If a key is not found, Confita won't change the respective struct field. With that in mind, default values can simply be implemented by filling the structure before passing it to Confita.

```go

type Config struct {
Host string `config:"host"`
Port uint32 `config:"port"`
Expand Down
37 changes: 30 additions & 7 deletions backend/env/env.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,43 @@ import (
// NewBackend creates a configuration loader that loads from the environment.
// If the key is not found, this backend tries again by turning any kebabcase key to snakecase and
// lowercase letters to uppercase.
func NewBackend() backend.Backend {
func NewBackend(fns ...opt) backend.Backend {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Technically I'm afraid this is a backwardly incompatible change, as someone might have assigned the function to a variable of type func() backend.Backend.

Also, I don't think it's a good idea in general to have unexported functions that use unexported types (it makes the godoc hard to understand and means you can't declare variables of those types).

return backend.Func("env", func(ctx context.Context, key string) ([]byte, error) {
val, ok := os.LookupEnv(key)
key = strings.Replace(key, "-", "_", -1)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a backwardly incompatible change. The original logic would succeed if an environment variable exists with exactly the same name, but it won't any longer (I'm surprised the tests pass actually - we should fix that).

val, ok := os.LookupEnv(opts(key, fns...))
if ok {
return []byte(val), nil
}

key = strings.Replace(strings.ToUpper(key), "-", "_", -1)

val, ok = os.LookupEnv(key)
val, ok = os.LookupEnv(opts(strings.ToUpper(key), fns...))
if ok {
return []byte(val), nil
}

return nil, backend.ErrNotFound
})
}

// WithPrefix adds preffix for searching env variable
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

// WithPrefix is a backend option that causes the given prefix to be added to all
// configuration names before looking them up with os.Getenv.

?

func WithPrefix(preffix string) opt {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

typo:
s/preffix/prefix/

if !strings.HasSuffix(preffix, "_") {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think it's worth bothering with this. It makes the logic more complex and harder to explain, and it's quite possible someone might want to add a prefix without any intervening underscore.

preffix = preffix + "_"
}
return func(key string) string {
return preffix + key
}
}

// ToUpper uppercases searching env variable
func ToUpper() opt {
return func(key string) string {
return strings.ToUpper(key)
}
}

type opt func(key string) string

func opts(key string, fns ...opt) string {
for i := range fns {
key = fns[i](key)
}
return key
}
97 changes: 97 additions & 0 deletions backend/env/env_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package env
import (
"context"
"os"
"reflect"
"testing"

"github.com/heetch/confita/backend"
Expand Down Expand Up @@ -35,3 +36,99 @@ func TestEnvBackend(t *testing.T) {
require.Equal(t, "ok", string(val))
})
}

func TestOpts(t *testing.T) {
type args struct {
fns []opt
key string
}
tests := []struct {
name string
args args
want string
}{
{
name: "Test opts positive",
args: args{
fns: []opt{
ToUpper(),
WithPrefix("TEST_"),
},
key: "var",
},
want: "TEST_VAR",
},
{
name: "Test upper after prefix with lower key",
args: args{
fns: []opt{
WithPrefix("test"),
ToUpper(),
},
key: "var",
},
want: "TEST_VAR",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := opts(tt.args.key, tt.args.fns...); !reflect.DeepEqual(got, tt.want) {
t.Errorf("NewBackend() = %v, want %v", got, tt.want)
}
})
}
}

func TestWithPrefix(t *testing.T) {
type args struct {
preffix string
key string
}
tests := []struct {
name string
args args
want string
}{
{
name: "Test add preffix positive",
args: args{
preffix: "TEST",
key: "VAR",
},
want: "TEST_VAR",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := WithPrefix(tt.args.preffix)(tt.args.key); !reflect.DeepEqual(got, tt.want) {
t.Errorf("WithPrefix() = %v, want %v", got, tt.want)
}
})
}
}

func TestToUpper(t *testing.T) {
type args struct {
key string
}
tests := []struct {
name string
args args
want string
}{
{
name: "Test add preffix positive",
args: args{
"test",
},
want: "TEST",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := ToUpper()(tt.args.key); !reflect.DeepEqual(got, tt.want) {
t.Errorf("ToUpper() = %v, want %v", got, tt.want)
}
})
}
}