Skip to content

Commit b8383e1

Browse files
refactor!: flatten evaluationContext object (#51)
* 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]>
1 parent bff098e commit b8383e1

File tree

8 files changed

+155
-52
lines changed

8 files changed

+155
-52
lines changed

pkg/openfeature/client.go

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -213,25 +213,26 @@ func (c Client) evaluate(
213213
return evalDetails, err
214214
}
215215

216+
flatCtx := flattenContext(evalCtx)
216217
var resolution ResolutionDetail
217218
switch flagType {
218219
case Object:
219-
resolution = api.provider.ObjectEvaluation(flag, defaultValue, evalCtx)
220+
resolution = api.provider.ObjectEvaluation(flag, defaultValue, flatCtx)
220221
case Boolean:
221222
defValue := defaultValue.(bool)
222-
res := api.provider.BooleanEvaluation(flag, defValue, evalCtx)
223+
res := api.provider.BooleanEvaluation(flag, defValue, flatCtx)
223224
resolution = res.ResolutionDetail
224225
case String:
225226
defValue := defaultValue.(string)
226-
res := api.provider.StringEvaluation(flag, defValue, evalCtx)
227+
res := api.provider.StringEvaluation(flag, defValue, flatCtx)
227228
resolution = res.ResolutionDetail
228229
case Float:
229230
defValue := defaultValue.(float64)
230-
res := api.provider.FloatEvaluation(flag, defValue, evalCtx)
231+
res := api.provider.FloatEvaluation(flag, defValue, flatCtx)
231232
resolution = res.ResolutionDetail
232233
case Int:
233234
defValue := defaultValue.(int64)
234-
res := api.provider.IntEvaluation(flag, defValue, evalCtx)
235+
res := api.provider.IntEvaluation(flag, defValue, flatCtx)
235236
resolution = res.ResolutionDetail
236237
}
237238

@@ -254,6 +255,17 @@ func (c Client) evaluate(
254255
return evalDetails, nil
255256
}
256257

258+
func flattenContext(evalCtx EvaluationContext) map[string]interface{} {
259+
flatCtx := map[string]interface{}{}
260+
if evalCtx.Attributes != nil {
261+
flatCtx = evalCtx.Attributes
262+
}
263+
if evalCtx.TargetingKey != "" {
264+
flatCtx[TargetingKey] = evalCtx.TargetingKey
265+
}
266+
return flatCtx
267+
}
268+
257269
func (c Client) beforeHooks(
258270
hookCtx HookContext, hooks []Hook, evalCtx EvaluationContext, options EvaluationOptions,
259271
) (EvaluationContext, error) {

pkg/openfeature/client_test.go

Lines changed: 95 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package openfeature
22

33
import (
4+
"reflect"
45
"testing"
56

67
"github.com/golang/mock/gomock"
@@ -208,6 +209,8 @@ func TestRequirement_1_4_4(t *testing.T) {
208209
func TestRequirement_1_4_9(t *testing.T) {
209210
client := NewClient("test-client")
210211
flagKey := "flag-key"
212+
evalCtx := EvaluationContext{}
213+
flatCtx := flattenContext(evalCtx)
211214

212215
ctrl := gomock.NewController(t)
213216

@@ -217,7 +220,7 @@ func TestRequirement_1_4_9(t *testing.T) {
217220
defaultValue := true
218221
mockProvider.EXPECT().Metadata().Times(2)
219222
mockProvider.EXPECT().Hooks().AnyTimes()
220-
mockProvider.EXPECT().BooleanEvaluation(flagKey, defaultValue, EvaluationContext{}).
223+
mockProvider.EXPECT().BooleanEvaluation(flagKey, defaultValue, flatCtx).
221224
Return(BoolResolutionDetail{
222225
Value: false,
223226
ResolutionDetail: ResolutionDetail{
@@ -228,7 +231,7 @@ func TestRequirement_1_4_9(t *testing.T) {
228231
}).Times(2)
229232
SetProvider(mockProvider)
230233

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

240-
valueDetails, err := client.BooleanValueDetails(flagKey, defaultValue, EvaluationContext{}, EvaluationOptions{})
243+
valueDetails, err := client.BooleanValueDetails(flagKey, defaultValue, evalCtx, EvaluationOptions{})
241244
if err == nil {
242245
t.Error("expected BooleanValueDetails to return an error, got nil")
243246
}
@@ -253,7 +256,7 @@ func TestRequirement_1_4_9(t *testing.T) {
253256
defaultValue := "default"
254257
mockProvider.EXPECT().Metadata().Times(2)
255258
mockProvider.EXPECT().Hooks().AnyTimes()
256-
mockProvider.EXPECT().StringEvaluation(flagKey, defaultValue, EvaluationContext{}).
259+
mockProvider.EXPECT().StringEvaluation(flagKey, defaultValue, flatCtx).
257260
Return(StringResolutionDetail{
258261
Value: "foo",
259262
ResolutionDetail: ResolutionDetail{
@@ -264,7 +267,7 @@ func TestRequirement_1_4_9(t *testing.T) {
264267
}).Times(2)
265268
SetProvider(mockProvider)
266269

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

276-
valueDetails, err := client.StringValueDetails(flagKey, defaultValue, EvaluationContext{}, EvaluationOptions{})
279+
valueDetails, err := client.StringValueDetails(flagKey, defaultValue, evalCtx, EvaluationOptions{})
277280
if err == nil {
278281
t.Error("expected StringValueDetails to return an error, got nil")
279282
}
@@ -289,7 +292,7 @@ func TestRequirement_1_4_9(t *testing.T) {
289292
defaultValue := 3.14159
290293
mockProvider.EXPECT().Metadata().Times(2)
291294
mockProvider.EXPECT().Hooks().AnyTimes()
292-
mockProvider.EXPECT().FloatEvaluation(flagKey, defaultValue, EvaluationContext{}).
295+
mockProvider.EXPECT().FloatEvaluation(flagKey, defaultValue, flatCtx).
293296
Return(FloatResolutionDetail{
294297
Value: 0,
295298
ResolutionDetail: ResolutionDetail{
@@ -300,7 +303,7 @@ func TestRequirement_1_4_9(t *testing.T) {
300303
}).Times(2)
301304
SetProvider(mockProvider)
302305

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

312-
valueDetails, err := client.FloatValueDetails(flagKey, defaultValue, EvaluationContext{}, EvaluationOptions{})
315+
valueDetails, err := client.FloatValueDetails(flagKey, defaultValue, evalCtx, EvaluationOptions{})
313316
if err == nil {
314317
t.Error("expected FloatValueDetails to return an error, got nil")
315318
}
@@ -325,7 +328,7 @@ func TestRequirement_1_4_9(t *testing.T) {
325328
var defaultValue int64 = 3
326329
mockProvider.EXPECT().Metadata().Times(2)
327330
mockProvider.EXPECT().Hooks().AnyTimes()
328-
mockProvider.EXPECT().IntEvaluation(flagKey, defaultValue, EvaluationContext{}).
331+
mockProvider.EXPECT().IntEvaluation(flagKey, defaultValue, flatCtx).
329332
Return(IntResolutionDetail{
330333
Value: 0,
331334
ResolutionDetail: ResolutionDetail{
@@ -336,7 +339,7 @@ func TestRequirement_1_4_9(t *testing.T) {
336339
}).Times(2)
337340
SetProvider(mockProvider)
338341

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

348-
valueDetails, err := client.IntValueDetails(flagKey, defaultValue, EvaluationContext{}, EvaluationOptions{})
351+
valueDetails, err := client.IntValueDetails(flagKey, defaultValue, evalCtx, EvaluationOptions{})
349352
if err == nil {
350353
t.Error("expected FloatValueDetails to return an error, got nil")
351354
}
@@ -364,15 +367,15 @@ func TestRequirement_1_4_9(t *testing.T) {
364367
defaultValue := obj{foo: "bar"}
365368
mockProvider.EXPECT().Metadata().Times(2)
366369
mockProvider.EXPECT().Hooks().AnyTimes()
367-
mockProvider.EXPECT().ObjectEvaluation(flagKey, defaultValue, EvaluationContext{}).
370+
mockProvider.EXPECT().ObjectEvaluation(flagKey, defaultValue, flatCtx).
368371
Return(ResolutionDetail{
369372
Value: obj{foo: "foo"},
370373
ErrorCode: "GENERAL",
371374
Reason: "forced test error",
372375
}).Times(2)
373376
SetProvider(mockProvider)
374377

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

384-
valueDetails, err := client.ObjectValueDetails(flagKey, defaultValue, EvaluationContext{}, EvaluationOptions{})
387+
valueDetails, err := client.ObjectValueDetails(flagKey, defaultValue, evalCtx, EvaluationOptions{})
385388
if err == nil {
386389
t.Error("expected ObjectValueDetails to return an error, got nil")
387390
}
@@ -477,3 +480,80 @@ func TestClient_ProviderEvaluationReturnsUnexpectedType(t *testing.T) {
477480
}
478481
})
479482
}
483+
484+
func TestFlattenContext(t *testing.T) {
485+
tests := map[string]struct {
486+
inCtx EvaluationContext
487+
outCtx map[string]interface{}
488+
}{
489+
"happy path": {
490+
inCtx: EvaluationContext{
491+
Attributes: map[string]interface{}{
492+
"1": "string",
493+
"2": 0.01,
494+
"3": false,
495+
},
496+
TargetingKey: "user",
497+
},
498+
outCtx: map[string]interface{}{
499+
TargetingKey: "user",
500+
"1": "string",
501+
"2": 0.01,
502+
"3": false,
503+
},
504+
},
505+
"no targeting key": {
506+
inCtx: EvaluationContext{
507+
Attributes: map[string]interface{}{
508+
"1": "string",
509+
"2": 0.01,
510+
"3": false,
511+
},
512+
},
513+
outCtx: map[string]interface{}{
514+
"1": "string",
515+
"2": 0.01,
516+
"3": false,
517+
},
518+
},
519+
"duplicated key": {
520+
inCtx: EvaluationContext{
521+
TargetingKey: "user",
522+
Attributes: map[string]interface{}{
523+
TargetingKey: "not user",
524+
"1": "string",
525+
"2": 0.01,
526+
"3": false,
527+
},
528+
},
529+
outCtx: map[string]interface{}{
530+
TargetingKey: "user",
531+
"1": "string",
532+
"2": 0.01,
533+
"3": false,
534+
},
535+
},
536+
"no attributes": {
537+
inCtx: EvaluationContext{
538+
TargetingKey: "user",
539+
},
540+
outCtx: map[string]interface{}{
541+
TargetingKey: "user",
542+
},
543+
},
544+
}
545+
546+
for name, test := range tests {
547+
t.Run(name, func(t *testing.T) {
548+
out := flattenContext(test.inCtx)
549+
if !reflect.DeepEqual(test.outCtx, out) {
550+
t.Fatalf(
551+
"%s, unexpected value received from flatten context, expected %v got %v",
552+
name,
553+
test.outCtx,
554+
out,
555+
)
556+
}
557+
})
558+
}
559+
}

pkg/openfeature/evaluation_context_test.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,8 @@ func TestRequirement_3_2_2(t *testing.T) {
132132
"user": 1,
133133
},
134134
}
135-
mockProvider.EXPECT().StringEvaluation(gomock.Any(), gomock.Any(), expectedMergedEvalCtx)
135+
flatCtx := flattenContext(expectedMergedEvalCtx)
136+
mockProvider.EXPECT().StringEvaluation(gomock.Any(), gomock.Any(), flatCtx)
136137

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

0 commit comments

Comments
 (0)