Skip to content

Commit 33e35be

Browse files
Force to use only one retriever at the time (#49)
1 parent 74d825d commit 33e35be

File tree

4 files changed

+85
-116
lines changed

4 files changed

+85
-116
lines changed

README.md

+17-11
Original file line numberDiff line numberDiff line change
@@ -33,16 +33,16 @@ First, you need to initialize the `ffclient` with the location of your backend f
3333
```go
3434
err := ffclient.Init(ffclient.Config{
3535
PollInterval: 3,
36-
HTTPRetriever: &ffClient.HTTPRetriever{
36+
Retriever: &ffclient.HTTPRetriever{
3737
URL: "http://example.com/test.yaml",
3838
},
3939
})
4040
defer ffclient.Close()
4141
```
4242
*This example will load a file from an HTTP endpoint and will refresh the flags every 3 seconds (if you omit the
43-
PollInterval, default value is 60s).*
43+
PollInterval, the default value is 60 seconds).*
4444

45-
Now you can evalute your flags anywhere in your code.
45+
Now you can evaluate your flags anywhere in your code.
4646

4747
```go
4848
user := ffuser.NewUser("user-unique-key")
@@ -70,9 +70,10 @@ ffclient.Init(ffclient.Config{
7070

7171
| | |
7272
|---|---|
73-
|`PollInterval` | Number of seconds to wait before refreshing the flags. The default value is 60 seconds.|
74-
|`Logger` | Logger used to log what `go-feature-flag` is doing. If no logger provided no log will be output.|
75-
|`Context` | The context used by the retriever. The default value is `context.Background()`.|
73+
|`PollInterval` | Number of seconds to wait before refreshing the flags.<br />The default value is 60 seconds.|
74+
|`Logger` | Logger used to log what `go-feature-flag` is doing.<br />If no logger is provided the module will not log anything.|
75+
|`Context` | The context used by the retriever.<br />The default value is `context.Background()`.|
76+
|`Retriever` | The configuration retriever you want to use to get your flag file *(see [Where do I store my flags file](#where-do-i-store-my-flags-file) for the configuration details)*.|
7677

7778
## Where do I store my flags file
7879
`go-feature-flags` support different ways of retrieving the flag file.
@@ -83,7 +84,7 @@ consideration.
8384
```go
8485
err := ffclient.Init(ffclient.Config{
8586
PollInterval: 3,
86-
GithubRetriever: &ffClient.GithubRetriever{
87+
Retriever: &ffclient.GithubRetriever{
8788
RepositorySlug: "thomaspoignant/go-feature-flag",
8889
Branch: "main",
8990
FilePath: "testdata/test.yaml",
@@ -100,13 +101,13 @@ To configure the access to your GitHub file:
100101
- **GithubToken**: Github token is used to access a private repository, you need the `repo` permission *([how to create a GitHub token](https://docs.github.com/en/free-pro-team@latest/github/authenticating-to-github/creating-a-personal-access-token))*.
101102
- **Timeout**: Timeout for the HTTP call (default is 10 seconds).
102103

103-
**Warning**: GitHub has rate limits, so be sure to not reach them when setting your `PollInterval`.
104+
:warning: GitHub has rate limits, so be sure to not reach them when setting your `PollInterval`.
104105

105106
### From an HTTP endpoint
106107
```go
107108
err := ffclient.Init(ffclient.Config{
108109
PollInterval: 3,
109-
HTTPRetriever: &ffClient.HTTPRetriever{
110+
Retriever: &ffclient.HTTPRetriever{
110111
URL: "http://example.com/test.yaml",
111112
Timeout: 2 * time.Second,
112113
},
@@ -125,7 +126,7 @@ To configure your HTTP endpoint:
125126
```go
126127
err := ffclient.Init(ffclient.Config{
127128
PollInterval: 3,
128-
S3Retriever: &ffClient.S3Retriever{
129+
Retriever: &ffclient.S3Retriever{
129130
Bucket: "tpoi-test",
130131
Item: "test.yaml",
131132
AwsConfig: aws.Config{
@@ -145,11 +146,16 @@ To configure your S3 file location:
145146
```go
146147
err := ffclient.Init(ffclient.Config{
147148
PollInterval: 3,
148-
LocalFile: "file-example.yaml",
149+
Retriever: &ffclient.FileRetriever{
150+
Path: "file-example.yaml",
151+
},
149152
})
150153
defer ffclient.Close()
151154
```
152155

156+
To configure your File retriever:
157+
- **Path**: location of your file. **MANDATORY**
158+
153159
*I will not recommend using a file to store your flags except if it is in a shared folder for all your services.*
154160

155161

config.go

+58-56
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,28 @@ type Config struct {
2121
PollInterval int // Poll every X seconds
2222
Logger *log.Logger
2323
Context context.Context // default is context.Background()
24-
LocalFile string
25-
HTTPRetriever *HTTPRetriever
26-
S3Retriever *S3Retriever
27-
GithubRetriever *GithubRetriever
24+
Retriever Retriever
25+
}
26+
27+
// GetRetriever returns a retriever.FlagRetriever configure with the retriever available in the config.
28+
func (c *Config) GetRetriever() (retriever.FlagRetriever, error) {
29+
if c.Retriever == nil {
30+
return nil, errors.New("no retriever in the configuration, impossible to get the flags")
31+
}
32+
return c.Retriever.getFlagRetriever()
33+
}
34+
35+
type Retriever interface {
36+
getFlagRetriever() (retriever.FlagRetriever, error)
37+
}
38+
39+
// FileRetriever is a configuration struct for a local flat file.
40+
type FileRetriever struct {
41+
Path string
42+
}
43+
44+
func (r *FileRetriever) getFlagRetriever() (retriever.FlagRetriever, error) { // nolint: unparam
45+
return retriever.NewLocalRetriever(r.Path), nil
2846
}
2947

3048
// HTTPRetriever is a configuration struct for an HTTP endpoint retriever.
@@ -36,13 +54,46 @@ type HTTPRetriever struct {
3654
Timeout time.Duration
3755
}
3856

57+
func (r *HTTPRetriever) getFlagRetriever() (retriever.FlagRetriever, error) {
58+
timeout := r.Timeout
59+
if timeout <= 0 {
60+
timeout = 10 * time.Second
61+
}
62+
63+
return retriever.NewHTTPRetriever(
64+
&http.Client{
65+
Timeout: timeout,
66+
},
67+
r.URL,
68+
r.Method,
69+
r.Body,
70+
r.Header,
71+
), nil
72+
}
73+
3974
// S3Retriever is a configuration struct for a S3 retriever.
4075
type S3Retriever struct {
4176
Bucket string
4277
Item string
4378
AwsConfig aws.Config
4479
}
4580

81+
func (r *S3Retriever) getFlagRetriever() (retriever.FlagRetriever, error) {
82+
// Create an AWS session
83+
sess, err := session.NewSession(&r.AwsConfig)
84+
if err != nil {
85+
return nil, err
86+
}
87+
88+
// Create a new AWS S3 downloader
89+
downloader := s3manager.NewDownloader(sess)
90+
return retriever.NewS3Retriever(
91+
downloader,
92+
r.Bucket,
93+
r.Item,
94+
), nil
95+
}
96+
4697
// GithubRetriever is a configuration struct for a GitHub retriever.
4798
type GithubRetriever struct {
4899
RepositorySlug string
@@ -52,40 +103,7 @@ type GithubRetriever struct {
52103
Timeout time.Duration // default is 10 seconds
53104
}
54105

55-
// GetRetriever is used to get the retriever we will use to load the flags file.
56-
func (c *Config) GetRetriever() (retriever.FlagRetriever, error) {
57-
if c.GithubRetriever != nil {
58-
return initGithubRetriever(*c.GithubRetriever)
59-
}
60-
61-
if c.S3Retriever != nil {
62-
// Create an AWS session
63-
sess, err := session.NewSession(&c.S3Retriever.AwsConfig)
64-
if err != nil {
65-
return nil, err
66-
}
67-
68-
// Create a new AWS S3 downloader
69-
downloader := s3manager.NewDownloader(sess)
70-
return retriever.NewS3Retriever(
71-
downloader,
72-
c.S3Retriever.Bucket,
73-
c.S3Retriever.Item,
74-
), nil
75-
}
76-
77-
if c.HTTPRetriever != nil {
78-
return initHTTPRetriever(*c.HTTPRetriever)
79-
}
80-
81-
if c.LocalFile != "" {
82-
return retriever.NewLocalRetriever(c.LocalFile), nil
83-
}
84-
return nil, errors.New("please add a config to get the flag config file")
85-
}
86-
87-
// initGithubRetriever creates a HTTP retriever that allows to get changes from Github.
88-
func initGithubRetriever(r GithubRetriever) (retriever.FlagRetriever, error) {
106+
func (r *GithubRetriever) getFlagRetriever() (retriever.FlagRetriever, error) {
89107
// default branch is main
90108
branch := r.Branch
91109
if branch == "" {
@@ -104,28 +122,12 @@ func initGithubRetriever(r GithubRetriever) (retriever.FlagRetriever, error) {
104122
branch,
105123
r.FilePath)
106124

107-
return initHTTPRetriever(HTTPRetriever{
125+
httpRetriever := HTTPRetriever{
108126
URL: URL,
109127
Method: http.MethodGet,
110128
Header: header,
111129
Timeout: r.Timeout,
112-
})
113-
}
114-
115-
// initHttpRetriever creates a HTTP retriever
116-
func initHTTPRetriever(r HTTPRetriever) (retriever.FlagRetriever, error) {
117-
timeout := r.Timeout
118-
if timeout <= 0 {
119-
timeout = 10 * time.Second
120130
}
121131

122-
return retriever.NewHTTPRetriever(
123-
&http.Client{
124-
Timeout: timeout,
125-
},
126-
r.URL,
127-
r.Method,
128-
r.Body,
129-
r.Header,
130-
), nil
132+
return httpRetriever.getFlagRetriever()
131133
}

config_test.go

+8-47
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,8 @@ import (
1212

1313
func TestConfig_GetRetriever(t *testing.T) {
1414
type fields struct {
15-
PollInterval int
16-
LocalFile string
17-
HTTPRetriever *ffClient.HTTPRetriever
18-
S3Retriever *ffClient.S3Retriever
19-
GithubRetriever *ffClient.GithubRetriever
15+
PollInterval int
16+
Retriever ffClient.Retriever
2017
}
2118
tests := []struct {
2219
name string
@@ -28,7 +25,7 @@ func TestConfig_GetRetriever(t *testing.T) {
2825
name: "File retriever",
2926
fields: fields{
3027
PollInterval: 3,
31-
LocalFile: "file-example.yaml",
28+
Retriever: &ffClient.FileRetriever{Path: "file-example.yaml"},
3229
},
3330
want: "*retriever.localRetriever",
3431
wantErr: false,
@@ -37,7 +34,7 @@ func TestConfig_GetRetriever(t *testing.T) {
3734
name: "S3 retriever",
3835
fields: fields{
3936
PollInterval: 3,
40-
S3Retriever: &ffClient.S3Retriever{
37+
Retriever: &ffClient.S3Retriever{
4138
Bucket: "tpoi-test",
4239
Item: "test.yaml",
4340
AwsConfig: aws.Config{
@@ -52,7 +49,7 @@ func TestConfig_GetRetriever(t *testing.T) {
5249
name: "HTTP retriever",
5350
fields: fields{
5451
PollInterval: 3,
55-
HTTPRetriever: &ffClient.HTTPRetriever{
52+
Retriever: &ffClient.HTTPRetriever{
5653
URL: "http://example.com/test.yaml",
5754
Method: http.MethodGet,
5855
},
@@ -64,7 +61,7 @@ func TestConfig_GetRetriever(t *testing.T) {
6461
name: "Github retriever",
6562
fields: fields{
6663
PollInterval: 3,
67-
GithubRetriever: &ffClient.GithubRetriever{
64+
Retriever: &ffClient.GithubRetriever{
6865
RepositorySlug: "thomaspoignant/go-feature-flag",
6966
FilePath: "testdata/test.yaml",
7067
GithubToken: "XXX",
@@ -74,39 +71,6 @@ func TestConfig_GetRetriever(t *testing.T) {
7471
want: "*retriever.httpRetriever",
7572
wantErr: false,
7673
},
77-
{
78-
name: "Priority to S3",
79-
fields: fields{
80-
PollInterval: 3,
81-
HTTPRetriever: &ffClient.HTTPRetriever{
82-
URL: "http://example.com/test.yaml",
83-
Method: http.MethodGet,
84-
},
85-
S3Retriever: &ffClient.S3Retriever{
86-
Bucket: "tpoi-test",
87-
Item: "test.yaml",
88-
AwsConfig: aws.Config{
89-
Region: aws.String("eu-west-1"),
90-
},
91-
},
92-
LocalFile: "file-example.yaml",
93-
},
94-
want: "*retriever.s3Retriever",
95-
wantErr: false,
96-
},
97-
{
98-
name: "Priority to HTTP",
99-
fields: fields{
100-
PollInterval: 3,
101-
HTTPRetriever: &ffClient.HTTPRetriever{
102-
URL: "http://example.com/test.yaml",
103-
Method: http.MethodGet,
104-
},
105-
LocalFile: "file-example.yaml",
106-
},
107-
want: "*retriever.httpRetriever",
108-
wantErr: false,
109-
},
11074
{
11175
name: "No retriever",
11276
fields: fields{
@@ -118,11 +82,8 @@ func TestConfig_GetRetriever(t *testing.T) {
11882
for _, tt := range tests {
11983
t.Run(tt.name, func(t *testing.T) {
12084
c := &ffClient.Config{
121-
PollInterval: tt.fields.PollInterval,
122-
LocalFile: tt.fields.LocalFile,
123-
HTTPRetriever: tt.fields.HTTPRetriever,
124-
S3Retriever: tt.fields.S3Retriever,
125-
GithubRetriever: tt.fields.GithubRetriever,
85+
PollInterval: tt.fields.PollInterval,
86+
Retriever: tt.fields.Retriever,
12687
}
12788
got, err := c.GetRetriever()
12889
assert.Equal(t, tt.wantErr, err != nil)

feature_flag_test.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ func TestValidUseCase(t *testing.T) {
2121
// Valid use case
2222
err := ffclient.Init(ffclient.Config{
2323
PollInterval: 0,
24-
LocalFile: "testdata/test.yaml",
24+
Retriever: &ffclient.FileRetriever{Path: "testdata/test.yaml"},
2525
})
2626

2727
assert.NoError(t, err)
@@ -37,7 +37,7 @@ func TestS3RetrieverReturnError(t *testing.T) {
3737
// Valid use case
3838
err := ffclient.Init(ffclient.Config{
3939
PollInterval: 0,
40-
S3Retriever: &ffclient.S3Retriever{
40+
Retriever: &ffclient.S3Retriever{
4141
Bucket: "unknown-bucket",
4242
Item: "unknown-item",
4343
AwsConfig: aws.Config{},

0 commit comments

Comments
 (0)