Skip to content

Commit

Permalink
refactor!: flatten evaluationContext object (#51)
Browse files Browse the repository at this point in the history
* flatten evaluationContext object

Signed-off-by: James-Milligan <[email protected]>

* added constant for targetingKey

Signed-off-by: James-Milligan <[email protected]>

* targetingKey casing

Signed-off-by: James-Milligan <[email protected]>

* reverting casing changes

Signed-off-by: James-Milligan <[email protected]>

* constant variable casing

Signed-off-by: James-Milligan <[email protected]>

* updated comment and added flattenContext unit test

Signed-off-by: James-Milligan <[email protected]>

Signed-off-by: James-Milligan <[email protected]>
Co-authored-by: Michael Beemer <[email protected]>
  • Loading branch information
james-milligan and beeme1mr authored Aug 31, 2022
1 parent bff098e commit b8383e1
Show file tree
Hide file tree
Showing 8 changed files with 155 additions and 52 deletions.
22 changes: 17 additions & 5 deletions pkg/openfeature/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -213,25 +213,26 @@ func (c Client) evaluate(
return evalDetails, err
}

flatCtx := flattenContext(evalCtx)
var resolution ResolutionDetail
switch flagType {
case Object:
resolution = api.provider.ObjectEvaluation(flag, defaultValue, evalCtx)
resolution = api.provider.ObjectEvaluation(flag, defaultValue, flatCtx)
case Boolean:
defValue := defaultValue.(bool)
res := api.provider.BooleanEvaluation(flag, defValue, evalCtx)
res := api.provider.BooleanEvaluation(flag, defValue, flatCtx)
resolution = res.ResolutionDetail
case String:
defValue := defaultValue.(string)
res := api.provider.StringEvaluation(flag, defValue, evalCtx)
res := api.provider.StringEvaluation(flag, defValue, flatCtx)
resolution = res.ResolutionDetail
case Float:
defValue := defaultValue.(float64)
res := api.provider.FloatEvaluation(flag, defValue, evalCtx)
res := api.provider.FloatEvaluation(flag, defValue, flatCtx)
resolution = res.ResolutionDetail
case Int:
defValue := defaultValue.(int64)
res := api.provider.IntEvaluation(flag, defValue, evalCtx)
res := api.provider.IntEvaluation(flag, defValue, flatCtx)
resolution = res.ResolutionDetail
}

Expand All @@ -254,6 +255,17 @@ func (c Client) evaluate(
return evalDetails, nil
}

func flattenContext(evalCtx EvaluationContext) map[string]interface{} {
flatCtx := map[string]interface{}{}
if evalCtx.Attributes != nil {
flatCtx = evalCtx.Attributes
}
if evalCtx.TargetingKey != "" {
flatCtx[TargetingKey] = evalCtx.TargetingKey
}
return flatCtx
}

func (c Client) beforeHooks(
hookCtx HookContext, hooks []Hook, evalCtx EvaluationContext, options EvaluationOptions,
) (EvaluationContext, error) {
Expand Down
110 changes: 95 additions & 15 deletions pkg/openfeature/client_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package openfeature

import (
"reflect"
"testing"

"github.com/golang/mock/gomock"
Expand Down Expand Up @@ -208,6 +209,8 @@ func TestRequirement_1_4_4(t *testing.T) {
func TestRequirement_1_4_9(t *testing.T) {
client := NewClient("test-client")
flagKey := "flag-key"
evalCtx := EvaluationContext{}
flatCtx := flattenContext(evalCtx)

ctrl := gomock.NewController(t)

Expand All @@ -217,7 +220,7 @@ func TestRequirement_1_4_9(t *testing.T) {
defaultValue := true
mockProvider.EXPECT().Metadata().Times(2)
mockProvider.EXPECT().Hooks().AnyTimes()
mockProvider.EXPECT().BooleanEvaluation(flagKey, defaultValue, EvaluationContext{}).
mockProvider.EXPECT().BooleanEvaluation(flagKey, defaultValue, flatCtx).
Return(BoolResolutionDetail{
Value: false,
ResolutionDetail: ResolutionDetail{
Expand All @@ -228,7 +231,7 @@ func TestRequirement_1_4_9(t *testing.T) {
}).Times(2)
SetProvider(mockProvider)

value, err := client.BooleanValue(flagKey, defaultValue, EvaluationContext{}, EvaluationOptions{})
value, err := client.BooleanValue(flagKey, defaultValue, evalCtx, EvaluationOptions{})
if err == nil {
t.Error("expected BooleanValue to return an error, got nil")
}
Expand All @@ -237,7 +240,7 @@ func TestRequirement_1_4_9(t *testing.T) {
t.Errorf("expected default value from BooleanValue, got %v", value)
}

valueDetails, err := client.BooleanValueDetails(flagKey, defaultValue, EvaluationContext{}, EvaluationOptions{})
valueDetails, err := client.BooleanValueDetails(flagKey, defaultValue, evalCtx, EvaluationOptions{})
if err == nil {
t.Error("expected BooleanValueDetails to return an error, got nil")
}
Expand All @@ -253,7 +256,7 @@ func TestRequirement_1_4_9(t *testing.T) {
defaultValue := "default"
mockProvider.EXPECT().Metadata().Times(2)
mockProvider.EXPECT().Hooks().AnyTimes()
mockProvider.EXPECT().StringEvaluation(flagKey, defaultValue, EvaluationContext{}).
mockProvider.EXPECT().StringEvaluation(flagKey, defaultValue, flatCtx).
Return(StringResolutionDetail{
Value: "foo",
ResolutionDetail: ResolutionDetail{
Expand All @@ -264,7 +267,7 @@ func TestRequirement_1_4_9(t *testing.T) {
}).Times(2)
SetProvider(mockProvider)

value, err := client.StringValue(flagKey, defaultValue, EvaluationContext{}, EvaluationOptions{})
value, err := client.StringValue(flagKey, defaultValue, evalCtx, EvaluationOptions{})
if err == nil {
t.Error("expected StringValue to return an error, got nil")
}
Expand All @@ -273,7 +276,7 @@ func TestRequirement_1_4_9(t *testing.T) {
t.Errorf("expected default value from StringValue, got %v", value)
}

valueDetails, err := client.StringValueDetails(flagKey, defaultValue, EvaluationContext{}, EvaluationOptions{})
valueDetails, err := client.StringValueDetails(flagKey, defaultValue, evalCtx, EvaluationOptions{})
if err == nil {
t.Error("expected StringValueDetails to return an error, got nil")
}
Expand All @@ -289,7 +292,7 @@ func TestRequirement_1_4_9(t *testing.T) {
defaultValue := 3.14159
mockProvider.EXPECT().Metadata().Times(2)
mockProvider.EXPECT().Hooks().AnyTimes()
mockProvider.EXPECT().FloatEvaluation(flagKey, defaultValue, EvaluationContext{}).
mockProvider.EXPECT().FloatEvaluation(flagKey, defaultValue, flatCtx).
Return(FloatResolutionDetail{
Value: 0,
ResolutionDetail: ResolutionDetail{
Expand All @@ -300,7 +303,7 @@ func TestRequirement_1_4_9(t *testing.T) {
}).Times(2)
SetProvider(mockProvider)

value, err := client.FloatValue(flagKey, defaultValue, EvaluationContext{}, EvaluationOptions{})
value, err := client.FloatValue(flagKey, defaultValue, evalCtx, EvaluationOptions{})
if err == nil {
t.Error("expected FloatValue to return an error, got nil")
}
Expand All @@ -309,7 +312,7 @@ func TestRequirement_1_4_9(t *testing.T) {
t.Errorf("expected default value from FloatValue, got %v", value)
}

valueDetails, err := client.FloatValueDetails(flagKey, defaultValue, EvaluationContext{}, EvaluationOptions{})
valueDetails, err := client.FloatValueDetails(flagKey, defaultValue, evalCtx, EvaluationOptions{})
if err == nil {
t.Error("expected FloatValueDetails to return an error, got nil")
}
Expand All @@ -325,7 +328,7 @@ func TestRequirement_1_4_9(t *testing.T) {
var defaultValue int64 = 3
mockProvider.EXPECT().Metadata().Times(2)
mockProvider.EXPECT().Hooks().AnyTimes()
mockProvider.EXPECT().IntEvaluation(flagKey, defaultValue, EvaluationContext{}).
mockProvider.EXPECT().IntEvaluation(flagKey, defaultValue, flatCtx).
Return(IntResolutionDetail{
Value: 0,
ResolutionDetail: ResolutionDetail{
Expand All @@ -336,7 +339,7 @@ func TestRequirement_1_4_9(t *testing.T) {
}).Times(2)
SetProvider(mockProvider)

value, err := client.IntValue(flagKey, defaultValue, EvaluationContext{}, EvaluationOptions{})
value, err := client.IntValue(flagKey, defaultValue, evalCtx, EvaluationOptions{})
if err == nil {
t.Error("expected IntValue to return an error, got nil")
}
Expand All @@ -345,7 +348,7 @@ func TestRequirement_1_4_9(t *testing.T) {
t.Errorf("expected default value from IntValue, got %v", value)
}

valueDetails, err := client.IntValueDetails(flagKey, defaultValue, EvaluationContext{}, EvaluationOptions{})
valueDetails, err := client.IntValueDetails(flagKey, defaultValue, evalCtx, EvaluationOptions{})
if err == nil {
t.Error("expected FloatValueDetails to return an error, got nil")
}
Expand All @@ -364,15 +367,15 @@ func TestRequirement_1_4_9(t *testing.T) {
defaultValue := obj{foo: "bar"}
mockProvider.EXPECT().Metadata().Times(2)
mockProvider.EXPECT().Hooks().AnyTimes()
mockProvider.EXPECT().ObjectEvaluation(flagKey, defaultValue, EvaluationContext{}).
mockProvider.EXPECT().ObjectEvaluation(flagKey, defaultValue, flatCtx).
Return(ResolutionDetail{
Value: obj{foo: "foo"},
ErrorCode: "GENERAL",
Reason: "forced test error",
}).Times(2)
SetProvider(mockProvider)

value, err := client.ObjectValue(flagKey, defaultValue, EvaluationContext{}, EvaluationOptions{})
value, err := client.ObjectValue(flagKey, defaultValue, evalCtx, EvaluationOptions{})
if err == nil {
t.Error("expected ObjectValue to return an error, got nil")
}
Expand All @@ -381,7 +384,7 @@ func TestRequirement_1_4_9(t *testing.T) {
t.Errorf("expected default value from ObjectValue, got %v", value)
}

valueDetails, err := client.ObjectValueDetails(flagKey, defaultValue, EvaluationContext{}, EvaluationOptions{})
valueDetails, err := client.ObjectValueDetails(flagKey, defaultValue, evalCtx, EvaluationOptions{})
if err == nil {
t.Error("expected ObjectValueDetails to return an error, got nil")
}
Expand Down Expand Up @@ -477,3 +480,80 @@ func TestClient_ProviderEvaluationReturnsUnexpectedType(t *testing.T) {
}
})
}

func TestFlattenContext(t *testing.T) {
tests := map[string]struct {
inCtx EvaluationContext
outCtx map[string]interface{}
}{
"happy path": {
inCtx: EvaluationContext{
Attributes: map[string]interface{}{
"1": "string",
"2": 0.01,
"3": false,
},
TargetingKey: "user",
},
outCtx: map[string]interface{}{
TargetingKey: "user",
"1": "string",
"2": 0.01,
"3": false,
},
},
"no targeting key": {
inCtx: EvaluationContext{
Attributes: map[string]interface{}{
"1": "string",
"2": 0.01,
"3": false,
},
},
outCtx: map[string]interface{}{
"1": "string",
"2": 0.01,
"3": false,
},
},
"duplicated key": {
inCtx: EvaluationContext{
TargetingKey: "user",
Attributes: map[string]interface{}{
TargetingKey: "not user",
"1": "string",
"2": 0.01,
"3": false,
},
},
outCtx: map[string]interface{}{
TargetingKey: "user",
"1": "string",
"2": 0.01,
"3": false,
},
},
"no attributes": {
inCtx: EvaluationContext{
TargetingKey: "user",
},
outCtx: map[string]interface{}{
TargetingKey: "user",
},
},
}

for name, test := range tests {
t.Run(name, func(t *testing.T) {
out := flattenContext(test.inCtx)
if !reflect.DeepEqual(test.outCtx, out) {
t.Fatalf(
"%s, unexpected value received from flatten context, expected %v got %v",
name,
test.outCtx,
out,
)
}
})
}
}
3 changes: 2 additions & 1 deletion pkg/openfeature/evaluation_context_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,8 @@ func TestRequirement_3_2_2(t *testing.T) {
"user": 1,
},
}
mockProvider.EXPECT().StringEvaluation(gomock.Any(), gomock.Any(), expectedMergedEvalCtx)
flatCtx := flattenContext(expectedMergedEvalCtx)
mockProvider.EXPECT().StringEvaluation(gomock.Any(), gomock.Any(), flatCtx)

_, err := client.StringValue("foo", "bar", invocationEvalCtx, EvaluationOptions{})
if err != nil {
Expand Down
Loading

0 comments on commit b8383e1

Please sign in to comment.