diff --git a/ffuser/user.go b/ffuser/user.go index ad0a07ad7ad..b083784d10a 100644 --- a/ffuser/user.go +++ b/ffuser/user.go @@ -39,3 +39,10 @@ func (u *User) IsAnonymous() bool { func (u *User) GetCustom() map[string]interface{} { return u.custom } + +// AddCustomAttribute allows to add a custom attribute into the user. +func (u *User) AddCustomAttribute(name string, value interface{}) { + if name != "" { + u.custom[name] = value + } +} diff --git a/ffuser/user_test.go b/ffuser/user_test.go new file mode 100644 index 00000000000..5c0ecd2bf83 --- /dev/null +++ b/ffuser/user_test.go @@ -0,0 +1,44 @@ +package ffuser_test + +import ( + "github.com/stretchr/testify/assert" + "github.com/thomaspoignant/go-feature-flag/ffuser" + "testing" +) + +func TestUser_AddCustomAttribute(t *testing.T) { + type args struct { + name string + value interface{} + } + tests := []struct { + name string + user ffuser.User + args args + want map[string]interface{} + }{ + { + name: "trying to add nil value", + user: ffuser.NewUser("123"), + args: args{}, + want: map[string]interface{}{}, + }, + { + name: "add valid element", + user: ffuser.NewUser("123"), + args: args{ + name: "test", + value: "test", + }, + want: map[string]interface{}{ + "test": "test", + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + tt.user.AddCustomAttribute(tt.args.name, tt.args.value) + assert.Equal(t, tt.want, tt.user.GetCustom()) + }) + } +} diff --git a/internal/flag/internal_flag.go b/internal/flag/internal_flag.go index de45b6b595e..36ba8503821 100644 --- a/internal/flag/internal_flag.go +++ b/internal/flag/internal_flag.go @@ -60,6 +60,10 @@ func (f *InternalFlag) Value( ) (interface{}, ResolutionDetails) { f.applyScheduledRolloutSteps() + if evaluationCtx.Environment != "" { + user.AddCustomAttribute("env", evaluationCtx.Environment) + } + if f.IsDisable() || f.isExperimentationOver() { return evaluationCtx.DefaultSdkValue, ResolutionDetails{ Variant: VariationSDKDefault, diff --git a/internal/flag/internal_flag_test.go b/internal/flag/internal_flag_test.go index 3051366f0d6..b40fcbc4ff0 100644 --- a/internal/flag/internal_flag_test.go +++ b/internal/flag/internal_flag_test.go @@ -1159,6 +1159,44 @@ func TestInternalFlag_Value(t *testing.T) { Cacheable: true, }, }, + { + name: "Should use the environment as a rule criteria", + flag: flag.InternalFlag{ + Variations: &map[string]*interface{}{ + "A": testconvert.Interface(false), + "B": testconvert.Interface(true), + }, + Rules: &[]flag.Rule{ + { + Query: testconvert.String("env == \"development\""), + VariationResult: testconvert.String("A"), + }, + { + Query: testconvert.String("(env == \"production\") " + + "or (env == \"staging\") or (env == \"qa\")"), + VariationResult: testconvert.String("B"), + }, + }, + DefaultRule: &flag.Rule{ + VariationResult: testconvert.String("B"), + }, + }, + args: args{ + flagName: "feature", + user: ffuser.NewUserBuilder("1").Build(), + evaluationCtx: flag.EvaluationContext{ + DefaultSdkValue: true, + Environment: "development", + }, + }, + want: false, + want1: flag.ResolutionDetails{ + Variant: "A", + Reason: flag.ReasonTargetingMatch, + Cacheable: true, + RuleIndex: testconvert.Int(0), + }, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) {