Skip to content

Commit 51ead18

Browse files
authored
Redesign API for v4 (#20)
* ignore unexported * ignore fields w/o tags? * improve test coverage * remove useless logging * add provider name method * check provider names' collision * use Go 1.18 * remove dependency on 'github.com/stretchr/testify/assert lib' * split JSON and YAML providers * remove dependency on 'gopkg.in/yaml.v2'
1 parent 16fefe8 commit 51ead18

27 files changed

+732
-737
lines changed

.github/workflows/go.yml

+2-2
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,10 @@ jobs:
55
name: Build
66
runs-on: ubuntu-latest
77
steps:
8-
- name: Set up Go 1.14
8+
- name: Set up Go 1.18
99
uses: actions/setup-go@v1
1010
with:
11-
go-version: 1.14
11+
go-version: 1.18
1212
id: go
1313

1414
- name: Check out code into the Go module directory

Makefile

+2-3
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
1-
APP_NAME="PlayersProfile"
21
COVERAGE_FILE="coverage.out"
32

43
test:
5-
go test -v -cover -coverprofile=$(COVERAGE_FILE) -covermode=atomic ./...
4+
go test -v -cover -coverprofile=$(COVERAGE_FILE) -covermode=atomic -count 1 ./...
65

76
coverage: test
8-
go tool cover -html=$(COVERAGE_FILE)
7+
go tool cover -html=$(COVERAGE_FILE)

README.md

+87-50
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ Available features:
1010
- setting *default* values for struct fields - `NewDefaultProvider()`
1111
- setting values from *environment* variables - `NewEnvProvider()`
1212
- setting values from command line *flags* - `NewFlagProvider(&cfg)`
13-
- setting values from *files* (JSON or YAML) - `NewFileProvider("./testdata/input.yml")`
13+
- setting values from a JSON *file* - `NewJSONFileProvider("./testdata/input.json")`
1414

1515
Supported types:
1616
- `string`, `*string`, `[]string`
@@ -24,57 +24,59 @@ Supported types:
2424
- `time.Duration` from strings like `12ms`, `2s` etc.
2525
- embedded structs and pointers to structs
2626

27+
2728
# Why?
2829
- your entire configuration can be defined in one model
2930
- all metadata is in your model (defined with `tags`)
3031
- easy to set/change a source of data for your configuration
3132
- easy to set a priority of sources to fetch data from (e.g., 1.`flags`, 2.`env`, 3.`default` or another order)
3233
- you can implement your custom provider
33-
- only `2` external dependencies
34+
- no external dependencies
3435
- complies with `12-factor app`
3536

37+
3638
# Quick start
37-
Import path `github.com/BoRuDar/configuration/v3`
39+
Import path `github.com/BoRuDar/configuration/v4`
3840
```go
39-
// define a configuration object
41+
// defining a struct
4042
cfg := struct {
4143
Name string `flag:"name"`
4244
LastName string `default:"defaultLastName"`
43-
Age byte `env:"AGE_ENV"`
45+
Age byte `env:"AGE_ENV" default:"-1"`
4446
BoolPtr *bool `default:"false"`
45-
47+
4648
ObjPtr *struct {
4749
F32 float32 `default:"32"`
4850
StrPtr *string `default:"str_ptr_test"`
4951
HundredMS time.Duration `default:"100ms"`
5052
}
5153

5254
Obj struct {
53-
IntPtr *int16 `default:"123"`
54-
NameYML int `default:"24"`
55-
StrSlice []string `default:"one;two"`
56-
IntSlice []int64 `default:"3; 4"`
55+
IntPtr *int16 `default:"123"`
56+
Beta int `file_json:"inside.beta" default:"24"`
57+
StrSlice []string `default:"one;two"`
58+
IntSlice []int64 `default:"3; 4"`
59+
unexported string `xml:"ignored"`
5760
}
5861
}{}
5962

60-
fileProvider, err := NewFileProvider("./testdata/input.yml")
61-
if err != nil {
62-
t.Fatalf("unexpected error: %v", err)
63-
}
64-
65-
configurator, err := New(
66-
&cfg, // pointer to the object for configuration
67-
NewFlagProvider(&cfg), // 1. flag provider expects pointer to the object to initialize flags
68-
NewEnvProvider(), // 2.
69-
fileProvider, // 3.
70-
NewDefaultProvider(), // 4.
71-
// providers are executed in order of the declaration from 1 to 4
63+
configurator := configuration.New(
64+
&cfg,
65+
// order of execution will be preserved:
66+
configuration.NewFlagProvider(), // 1st
67+
configuration.NewEnvProvider(), // 2nd
68+
configuration.NewJSONFileProvider(fileName), // 3rd
69+
configuration.NewDefaultProvider(), // 4th
7270
)
73-
if err != nil {
74-
t.Fatalf("unexpected error: %v", err)
71+
72+
if err := configurator.InitValues(); err != nil {
73+
log.Fatalf("unexpected error: ", err)
7574
}
75+
```
7676

77-
configurator.InitValues()
77+
If you need only ENV variables and default values you can use a shorter form:
78+
```go
79+
err := configuration.FromEnvAndDefault(&cfg)
7880
```
7981

8082

@@ -83,54 +85,72 @@ You can specify one or more providers. They will be executed in order of definit
8385
```go
8486
[]Provider{
8587
NewFlagProvider(&cfg), // 1
86-
NewEnvProvider(), // 2
87-
NewDefaultProvider(), // 3
88+
NewEnvProvider(), // 2
89+
NewDefaultProvider(), // 3
8890
}
8991
```
9092
If provider set value successfully next ones will not be executed (if flag provider from the sample above found a value env and default providers are skipped).
9193
The value of first successfully executed provider will be set.
9294
If none of providers found value - an application will be terminated.
93-
This behavior can be changed with `configurator.SetOnFailFn` method.
95+
This behavior can be changed with `configurator.OnFailFnOpt` option:
96+
```go
97+
err := configuration.New(
98+
&cfg,
99+
configuration.NewEnvProvider(),
100+
configuration.NewDefaultProvider()).
101+
SetOptions(
102+
configuration.OnFailFnOpt(func(err error) {
103+
log.Println(err)
104+
}),
105+
).InitValues()
106+
```
107+
94108

95109
### Custom provider
96110
You can define a custom provider which should satisfy next interface:
97111
```go
98112
type Provider interface {
99-
Provide(field reflect.StructField, v reflect.Value, pathToField ...string) error
113+
Name() string
114+
Init(ptr any) error
115+
Provide(field reflect.StructField, v reflect.Value) error
100116
}
101117
```
102118

103119
### Default provider
104120
Looks for `default` tag and set value from it:
105121
```go
106-
struct {
107-
// ...
108-
Name string `default:"defaultName"`
109-
// ...
110-
}
122+
struct {
123+
// ...
124+
Name string `default:"defaultName"`
125+
// ...
126+
}
111127
```
112128

113129

114130
### Env provider
115131
Looks for `env` tag and tries to find an ENV variable with the name from the tag (`AGE` in this example):
116132
```go
117-
struct {
118-
// ...
119-
Age byte `env:"AGE"`
120-
// ...
121-
}
133+
struct {
134+
// ...
135+
Age byte `env:"AGE"`
136+
// ...
137+
}
138+
```
139+
Name inside tag `env:"<name>"` must be unique for each field. Only UPPER register for ENV vars is accepted:
140+
```bash
141+
bad_env_var_name=bad
142+
GOOD_ENV_VAR_NAME=good
122143
```
123-
Name inside tag `env:"<name>"` must be unique for each field.
124144

125145

126146
### Flag provider
127147
Looks for `flag` tag and tries to set value from the command line flag `-first_name`
128148
```go
129-
struct {
130-
// ...
131-
Name string `flag:"first_name|default_value|Description"`
132-
// ...
133-
}
149+
struct {
150+
// ...
151+
Name string `flag:"first_name|default_value|Description"`
152+
// ...
153+
}
134154
```
135155
Name inside tag `flag:"<name>"` must be unique for each field. `default_value` and `description` sections are `optional` and can be omitted.
136156
`NewFlagProvider(&cfg)` expects a pointer to the same object for initialization.
@@ -142,11 +162,28 @@ Flags:
142162
```
143163
And program execution will be terminated.
144164
#### Options for _NewFlagProvider_
145-
* WithFlagSet - set a custom FlagSet
146-
* WithErrorHandler - to catch and handle errors from the init phase (before actually getting data from flags)
165+
* WithFlagSet - sets a custom FlagSet
166+
147167

148-
### File provider
149-
Doesn't require any specific tags. JSON and YAML formats of files are supported.
168+
### JSON File provider
169+
Requires `file_json:"<path_to_json_field>"` tag.
150170
```go
151-
NewFileProvider("./testdata/input.yml")
171+
NewJSONFileProvider("./testdata/input.json")
152172
```
173+
For example, tag `file_json:"cache.retention"` will assume that you have this structure of your JSON file:
174+
```json
175+
{
176+
"cache": {
177+
"retention": 1
178+
}
179+
}
180+
```
181+
182+
183+
### Additional providers
184+
* [YAML files](https://github.com/BoRuDar/configuration-yaml-file)
185+
186+
187+
# Contribution
188+
1. Open a feature request or a bug report in [issues](https://github.com/BoRuDar/configuration/issues)
189+
2. Fork and create a PR into `dev` branch

0 commit comments

Comments
 (0)