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

Add environment parameter in config #246

Merged
merged 13 commits into from
Jun 21, 2022
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ ffclient.Init(ffclient.Config{
},
},
StartWithRetrieverError: false,
Environment: os.Getenv("MYAPP_ENV"),
})
```
### Configuration fields
Expand All @@ -126,6 +127,7 @@ ffclient.Init(ffclient.Config{
|---------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `Retriever` | The configuration retriever you want to use to get your flag file.<br> *See [Store your flag file](https://thomaspoignant.github.io/go-feature-flag/latest/flag_file/) for the configuration details*. |
| `Context` | *(optional)*<br>The context used by the retriever.<br />Default: `context.Background()` |
| `Environment` | *(optional)*<br>The environment the app is running under, can be checked in feature flag rules.<br />Default: `""` |
| `DataExporter` | *(optional)*<br>DataExporter defines how to export data on how your flags are used.<br> *see [export data section](https://thomaspoignant.github.io/go-feature-flag/latest/data_collection/) for more details*. |
| `FileFormat` | *(optional)*<br>Format of your configuration file. Available formats are `yaml`, `toml` and `json`, if you omit the field it will try to unmarshal the file as a `yaml` file.<br>Default: `YAML` |
| `Logger` | *(optional)*<br>Logger used to log what `go-feature-flag` is doing.<br />If no logger is provided the module will not log anything.<br>Default: No log |
Expand Down
4 changes: 4 additions & 0 deletions config.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ type Config struct {
// Default: context.Background()
Context context.Context

// Environment (optional), can be checked in feature flag rules
// Default: ""
Environment string

// Retriever is the component in charge to retrieve your flag file
Retriever Retriever

Expand Down
2 changes: 2 additions & 0 deletions docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ During the initialization you must give a [`ffclient.Config{}`](https://pkg.go.d
|---|---|
|`Retriever` | The configuration retriever you want to use to get your flag file<br> *See [Store your flag file](flag_file/index.md) for the configuration details*.|
|`Context` | *(optional)*<br>The context used by the retriever.<br />Default: `context.Background()`|
|`Environment` | *(optional)*<br>The environment the app is running under, can be checked in feature flag rules.<br />Default: `""`|
|`DataExporter` | *(optional)*<br>DataExporter defines how to export data on how your flags are used.<br> *see [export data section](data_collection/index.md) for more details*.|
|`FileFormat`| *(optional)*<br>Format of your configuration file. Available formats are `yaml`, `toml` and `json`, if you omit the field it will try to unmarshal the file as a `yaml` file.<br>Default: `YAML`|
|`Logger` | *(optional)*<br>Logger used to log what `go-feature-flag` is doing.<br />If no logger is provided the module will not log anything.<br>Default: No log|
Expand All @@ -24,6 +25,7 @@ ffclient.Init(ffclient.Config{
PollingInterval: 3 * time.Second,
Logger: log.New(file, "/tmp/log", 0),
Context: context.Background(),
Environment: os.Getenv("MYAPP_ENV"),
Retriever: &ffclient.FileRetriever{Path: "testdata/flag-config.yaml"},
FileFormat: "yaml",
Notifiers: []ffclient.NotifierConfig{
Expand Down
4 changes: 2 additions & 2 deletions internal/flag/flag.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ import (

type Flag interface {
// Value is returning the Value associate to the flag (True / False / Default ) based
// if the flag apply to the user or not.
Value(flagName string, user ffuser.User) (interface{}, string)
// if the flag apply to the user and environment or not.
Value(flagName string, user ffuser.User, environment string) (interface{}, string)

// String display correctly a flag with the right formatting
String() string
Expand Down
12 changes: 8 additions & 4 deletions internal/flagv1/flag_data.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,14 +56,14 @@ type FlagData struct {

// Value is returning the Value associate to the flag (True / False / Default ) based
// if the toggle apply to the user or not.
func (f *FlagData) Value(flagName string, user ffuser.User) (interface{}, string) {
func (f *FlagData) Value(flagName string, user ffuser.User, environment string) (interface{}, string) {
f.updateFlagStage()
if f.isExperimentationOver() {
// if we have an experimentation that has not started or that is finished we use the default value.
return f.getDefault(), VariationDefault
}

if f.evaluateRule(user) {
if f.evaluateRule(user, environment) {
if f.isInPercentage(flagName, user) {
// Rule applied and user in the cohort.
return f.getTrue(), VariationTrue
Expand Down Expand Up @@ -102,7 +102,7 @@ func (f *FlagData) isInPercentage(flagName string, user ffuser.User) bool {
}

// evaluateRule is checking if the rule can apply to a specific user.
func (f *FlagData) evaluateRule(user ffuser.User) bool {
func (f *FlagData) evaluateRule(user ffuser.User, environment string) bool {
// Flag disable we cannot apply it.
if f.GetDisable() {
return false
Expand All @@ -114,7 +114,11 @@ func (f *FlagData) evaluateRule(user ffuser.User) bool {
}

// Evaluate the rule on the user.
return parser.Evaluate(f.getRule(), utils.UserToMap(user))
userMap := utils.UserToMap(user)
if environment != "" {
userMap["env"] = environment
}
return parser.Evaluate(f.getRule(), userMap)
}

// string display correctly a flag
Expand Down
25 changes: 24 additions & 1 deletion internal/flagv1/flag_priv_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ func TestFlag_evaluateRule(t *testing.T) {
}
type args struct {
user ffuser.User
env string
}
tests := []struct {
name string
Expand Down Expand Up @@ -77,6 +78,28 @@ func TestFlag_evaluateRule(t *testing.T) {
},
want: false,
},
{
name: "Rolled out to environment",
fields: fields{
Rule: "env == \"staging\"",
},
args: args{
user: ffuser.NewAnonymousUser(""),
env: "staging",
},
want: true,
},
{
name: "Not rolled out to environment",
fields: fields{
Rule: "env != \"production\"",
},
args: args{
user: ffuser.NewAnonymousUser(""),
env: "production",
},
want: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
Expand All @@ -88,7 +111,7 @@ func TestFlag_evaluateRule(t *testing.T) {
False: testconvert.Interface(tt.fields.False),
}

got := f.evaluateRule(tt.args.user)
got := f.evaluateRule(tt.args.user, tt.args.env)
assert.Equal(t, tt.want, got)
})
}
Expand Down
24 changes: 12 additions & 12 deletions internal/flagv1/flag_pub_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -337,7 +337,7 @@ func TestFlag_value(t *testing.T) {
Rollout: &tt.fields.Rollout,
}

got, variationType := f.Value(tt.args.flagName, tt.args.user)
got, variationType := f.Value(tt.args.flagName, tt.args.user, "")
assert.Equal(t, tt.want.value, got)
assert.Equal(t, tt.want.variationType, variationType)
})
Expand All @@ -362,15 +362,15 @@ func TestFlag_ProgressiveRollout(t *testing.T) {
flagName := "test-flag"

// We evaluate the same flag multiple time overtime.
v, _ := f.Value(flagName, user)
v, _ := f.Value(flagName, user, "")
assert.Equal(t, f.GetVariationValue(flagv1.VariationFalse), v)

time.Sleep(1 * time.Second)
v2, _ := f.Value(flagName, user)
v2, _ := f.Value(flagName, user, "")
assert.Equal(t, f.GetVariationValue(flagv1.VariationFalse), v2)

time.Sleep(1 * time.Second)
v3, _ := f.Value(flagName, user)
v3, _ := f.Value(flagName, user, "")
assert.Equal(t, f.GetVariationValue(flagv1.VariationTrue), v3)
}

Expand Down Expand Up @@ -447,43 +447,43 @@ func TestFlag_ScheduledRollout(t *testing.T) {
flagName := "test-flag"

// We evaluate the same flag multiple time overtime.
v, _ := f.Value(flagName, user)
v, _ := f.Value(flagName, user, "")
assert.Equal(t, f.GetVariationValue(flagv1.VariationFalse), v)

time.Sleep(1 * time.Second)

v, _ = f.Value(flagName, user)
v, _ = f.Value(flagName, user, "")
assert.Equal(t, "True", v)
assert.Equal(t, 1.1, f.GetVersion())

time.Sleep(1 * time.Second)

v, _ = f.Value(flagName, user)
v, _ = f.Value(flagName, user, "")
assert.Equal(t, "Default2", v)

time.Sleep(1 * time.Second)

v, _ = f.Value(flagName, user)
v, _ = f.Value(flagName, user, "")
assert.Equal(t, "True2", v)

time.Sleep(1 * time.Second)

v, _ = f.Value(flagName, user)
v, _ = f.Value(flagName, user, "")
assert.Equal(t, "Default2", v)

time.Sleep(1 * time.Second)

v, _ = f.Value(flagName, user)
v, _ = f.Value(flagName, user, "")
assert.Equal(t, "Default2", v)

time.Sleep(1 * time.Second)

v, _ = f.Value(flagName, user)
v, _ = f.Value(flagName, user, "")
assert.Equal(t, "True2", v)

time.Sleep(1 * time.Second)

v, _ = f.Value(flagName, user)
v, _ = f.Value(flagName, user, "")
assert.Equal(t, "Default2", v)
}

Expand Down
16 changes: 8 additions & 8 deletions variation.go
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ func (g *GoFeatureFlag) AllFlagsState(user ffuser.User) flagstate.AllFlags {

allFlags := flagstate.NewAllFlags()
for key, currentFlag := range flags {
flagValue, varType := currentFlag.Value(key, user)
flagValue, varType := currentFlag.Value(key, user, g.config.Environment)
switch v := flagValue; v.(type) {
case int, float64, bool, string, []interface{}, map[string]interface{}:
allFlags.AddFlag(key, flagstate.NewFlagState(currentFlag.GetTrackEvents(), v, varType, false))
Expand Down Expand Up @@ -191,7 +191,7 @@ func (g *GoFeatureFlag) boolVariation(flagKey string, user ffuser.User, sdkDefau
}, err
}

flagValue, variationType := f.Value(flagKey, user)
flagValue, variationType := f.Value(flagKey, user, g.config.Environment)
res, ok := flagValue.(bool)
if !ok {
return model.BoolVarResult{
Expand Down Expand Up @@ -221,7 +221,7 @@ func (g *GoFeatureFlag) intVariation(flagKey string, user ffuser.User, sdkDefaul
}, err
}

flagValue, variationType := f.Value(flagKey, user)
flagValue, variationType := f.Value(flagKey, user, g.config.Environment)
res, ok := flagValue.(int)
if !ok {
// if this is a float64 we convert it to int
Expand Down Expand Up @@ -259,7 +259,7 @@ func (g *GoFeatureFlag) float64Variation(flagKey string, user ffuser.User, sdkDe
}, err
}

flagValue, variationType := f.Value(flagKey, user)
flagValue, variationType := f.Value(flagKey, user, g.config.Environment)
res, ok := flagValue.(float64)
if !ok {
return model.Float64VarResult{
Expand Down Expand Up @@ -289,7 +289,7 @@ func (g *GoFeatureFlag) stringVariation(flagKey string, user ffuser.User, sdkDef
}, err
}

flagValue, variationType := f.Value(flagKey, user)
flagValue, variationType := f.Value(flagKey, user, g.config.Environment)
res, ok := flagValue.(string)
if !ok {
return model.StringVarResult{
Expand Down Expand Up @@ -319,7 +319,7 @@ func (g *GoFeatureFlag) jsonArrayVariation(flagKey string, user ffuser.User, sdk
}, err
}

flagValue, variationType := f.Value(flagKey, user)
flagValue, variationType := f.Value(flagKey, user, g.config.Environment)
res, ok := flagValue.([]interface{})
if !ok {
return model.JSONArrayVarResult{
Expand Down Expand Up @@ -349,7 +349,7 @@ func (g *GoFeatureFlag) jsonVariation(flagKey string, user ffuser.User, sdkDefau
}, err
}

flagValue, variationType := f.Value(flagKey, user)
flagValue, variationType := f.Value(flagKey, user, g.config.Environment)
res, ok := flagValue.(map[string]interface{})
if !ok {
return model.JSONVarResult{
Expand Down Expand Up @@ -399,7 +399,7 @@ func (g *GoFeatureFlag) RawVariation(flagKey string, user ffuser.User, sdkDefaul
return res, err
}

flagValue, variationType := f.Value(flagKey, user)
flagValue, variationType := f.Value(flagKey, user, g.config.Environment)
res := model.RawVarResult{
Value: flagValue,
VariationResult: computeVariationResult(f, variationType, false),
Expand Down