Repository: open-feature/go-sdk
Pull request #74 (2022-09-22 12:43:30)

refactor!: defined type for provider interface evaluation context
Signed-off-by: James-Milligan <james@omnant.co.uk>

james-milligan: Intention is to reduice confusion and ensure the change looks more deliberate
codecov-commenter: # [Codecov](https://codecov.io/gh/open-feature/go-sdk/pull/74?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=open-feature) Report
> Merging [#74](https://codecov.io/gh/open-feature/go-sdk/pull/74?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=open-feature) (7bed02b) into [main](https://codecov.io/gh/open-feature/go-sdk/commit/f0822b6ce9522cbbb10ed5168cecad2df6c29e40?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=open-feature) (f0822b6) will **not change** coverage.
> The diff coverage is `100.00%`.

```diff
@@           Coverage Diff           @@
##             main      #74   +/-   ##
=======================================
  Coverage   84.30%   84.30%           
=======================================
  Files           6        6           
  Lines         376      376           
=======================================
  Hits          317      317           
  Misses         54       54           
  Partials        5        5           
```


| [Impacted Files](https://codecov.io/gh/open-feature/go-sdk/pull/74?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=open-feature) | Coverage Δ | |
|---|---|---|
| [pkg/openfeature/provider.go](https://codecov.io/gh/open-feature/go-sdk/pull/74/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=open-feature#diff-cGtnL29wZW5mZWF0dXJlL3Byb3ZpZGVyLmdv) | `100.00% <ø> (ø)` | |
| [pkg/openfeature/noop\_provider.go](https://codecov.io/gh/open-feature/go-sdk/pull/74/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=open-feature#diff-cGtnL29wZW5mZWF0dXJlL25vb3BfcHJvdmlkZXIuZ28=) | `100.00% <100.00%> (ø)` | |

:mega: We’re building smart automated test selection to slash your CI/CD build times. [Learn more](https://about.codecov.io/iterative-testing/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=open-feature)


Patch:
diff --git a/go.mod b/go.mod
index 878de32..a647e31 100644
--- a/go.mod
+++ b/go.mod
@@ -7,4 +7,4 @@ require (
 	golang.org/x/text v0.3.7
 )
 
-require github.com/go-logr/logr v1.2.3 // indirect
+require github.com/go-logr/logr v1.2.3
diff --git a/pkg/openfeature/client.go b/pkg/openfeature/client.go
index 99b79ea..f762473 100644
--- a/pkg/openfeature/client.go
+++ b/pkg/openfeature/client.go
@@ -461,8 +461,8 @@ func (c Client) evaluate(
 	return evalDetails, nil
 }
 
-func flattenContext(evalCtx EvaluationContext) map[string]interface{} {
-	flatCtx := map[string]interface{}{}
+func flattenContext(evalCtx EvaluationContext) FlattenedContext {
+	flatCtx := FlattenedContext{}
 	if evalCtx.Attributes != nil {
 		flatCtx = evalCtx.Attributes
 	}
diff --git a/pkg/openfeature/noop_provider.go b/pkg/openfeature/noop_provider.go
index 90500f4..5cd5561 100644
--- a/pkg/openfeature/noop_provider.go
+++ b/pkg/openfeature/noop_provider.go
@@ -11,7 +11,7 @@ func (e NoopProvider) Metadata() Metadata {
 }
 
 // BooleanEvaluation returns a boolean flag.
-func (e NoopProvider) BooleanEvaluation(ctx context.Context, flag string, defaultValue bool, evalCtx map[string]interface{}) BoolResolutionDetail {
+func (e NoopProvider) BooleanEvaluation(ctx context.Context, flag string, defaultValue bool, evalCtx FlattenedContext) BoolResolutionDetail {
 	return BoolResolutionDetail{
 		Value: defaultValue,
 		ResolutionDetail: ResolutionDetail{
@@ -22,7 +22,7 @@ func (e NoopProvider) BooleanEvaluation(ctx context.Context, flag string, defaul
 }
 
 // StringEvaluation returns a string flag.
-func (e NoopProvider) StringEvaluation(ctx context.Context, flag string, defaultValue string, evalCtx map[string]interface{}) StringResolutionDetail {
+func (e NoopProvider) StringEvaluation(ctx context.Context, flag string, defaultValue string, evalCtx FlattenedContext) StringResolutionDetail {
 	return StringResolutionDetail{
 		Value: defaultValue,
 		ResolutionDetail: ResolutionDetail{
@@ -33,7 +33,7 @@ func (e NoopProvider) StringEvaluation(ctx context.Context, flag string, default
 }
 
 // FloatEvaluation returns a float flag.
-func (e NoopProvider) FloatEvaluation(ctx context.Context, flag string, defaultValue float64, evalCtx map[string]interface{}) FloatResolutionDetail {
+func (e NoopProvider) FloatEvaluation(ctx context.Context, flag string, defaultValue float64, evalCtx FlattenedContext) FloatResolutionDetail {
 	return FloatResolutionDetail{
 		Value: defaultValue,
 		ResolutionDetail: ResolutionDetail{
@@ -44,7 +44,7 @@ func (e NoopProvider) FloatEvaluation(ctx context.Context, flag string, defaultV
 }
 
 // IntEvaluation returns an int flag.
-func (e NoopProvider) IntEvaluation(ctx context.Context, flag string, defaultValue int64, evalCtx map[string]interface{}) IntResolutionDetail {
+func (e NoopProvider) IntEvaluation(ctx context.Context, flag string, defaultValue int64, evalCtx FlattenedContext) IntResolutionDetail {
 	return IntResolutionDetail{
 		Value: defaultValue,
 		ResolutionDetail: ResolutionDetail{
@@ -55,7 +55,7 @@ func (e NoopProvider) IntEvaluation(ctx context.Context, flag string, defaultVal
 }
 
 // ObjectEvaluation returns an object flag
-func (e NoopProvider) ObjectEvaluation(ctx context.Context, flag string, defaultValue interface{}, evalCtx map[string]interface{}) InterfaceResolutionDetail {
+func (e NoopProvider) ObjectEvaluation(ctx context.Context, flag string, defaultValue interface{}, evalCtx FlattenedContext) InterfaceResolutionDetail {
 	return InterfaceResolutionDetail{
 		Value: defaultValue,
 		ResolutionDetail: ResolutionDetail{
diff --git a/pkg/openfeature/provider.go b/pkg/openfeature/provider.go
index a5ff81f..de85a71 100644
--- a/pkg/openfeature/provider.go
+++ b/pkg/openfeature/provider.go
@@ -16,15 +16,19 @@ const (
 	TargetingKey string = "targetingKey" // evaluation context map key. The targeting key uniquely identifies the subject (end-user, or client service) of a flag evaluation.
 )
 
+// FlattenedContext contains metadata for a given flag evaluation in a flattened structure.
+// TargetingKey ("targetingKey") is stored as a string value if provided in the evaluation context.
+type FlattenedContext map[string]interface{}
+
 // FeatureProvider interface defines a set of functions that can be called in order to evaluate a flag.
 // vendors should implement
 type FeatureProvider interface {
 	Metadata() Metadata
-	BooleanEvaluation(ctx context.Context, flag string, defaultValue bool, evalCtx map[string]interface{}) BoolResolutionDetail
-	StringEvaluation(ctx context.Context, flag string, defaultValue string, evalCtx map[string]interface{}) StringResolutionDetail
-	FloatEvaluation(ctx context.Context, flag string, defaultValue float64, evalCtx map[string]interface{}) FloatResolutionDetail
-	IntEvaluation(ctx context.Context, flag string, defaultValue int64, evalCtx map[string]interface{}) IntResolutionDetail
-	ObjectEvaluation(ctx context.Context, flag string, defaultValue interface{}, evalCtx map[string]interface{}) InterfaceResolutionDetail
+	BooleanEvaluation(ctx context.Context, flag string, defaultValue bool, evalCtx FlattenedContext) BoolResolutionDetail
+	StringEvaluation(ctx context.Context, flag string, defaultValue string, evalCtx FlattenedContext) StringResolutionDetail
+	FloatEvaluation(ctx context.Context, flag string, defaultValue float64, evalCtx FlattenedContext) FloatResolutionDetail
+	IntEvaluation(ctx context.Context, flag string, defaultValue int64, evalCtx FlattenedContext) IntResolutionDetail
+	ObjectEvaluation(ctx context.Context, flag string, defaultValue interface{}, evalCtx FlattenedContext) InterfaceResolutionDetail
 	Hooks() []Hook
 }
 


Test patch:
diff --git a/pkg/openfeature/client_test.go b/pkg/openfeature/client_test.go
index 7bd49da..c5d521e 100644
--- a/pkg/openfeature/client_test.go
+++ b/pkg/openfeature/client_test.go
@@ -416,7 +416,7 @@ func TestRequirement_1_4_9(t *testing.T) {
 func TestFlattenContext(t *testing.T) {
 	tests := map[string]struct {
 		inCtx  EvaluationContext
-		outCtx map[string]interface{}
+		outCtx FlattenedContext
 	}{
 		"happy path": {
 			inCtx: EvaluationContext{
@@ -427,7 +427,7 @@ func TestFlattenContext(t *testing.T) {
 				},
 				TargetingKey: "user",
 			},
-			outCtx: map[string]interface{}{
+			outCtx: FlattenedContext{
 				TargetingKey: "user",
 				"1":          "string",
 				"2":          0.01,
@@ -442,7 +442,7 @@ func TestFlattenContext(t *testing.T) {
 					"3": false,
 				},
 			},
-			outCtx: map[string]interface{}{
+			outCtx: FlattenedContext{
 				"1": "string",
 				"2": 0.01,
 				"3": false,
@@ -458,7 +458,7 @@ func TestFlattenContext(t *testing.T) {
 					"3":          false,
 				},
 			},
-			outCtx: map[string]interface{}{
+			outCtx: FlattenedContext{
 				TargetingKey: "user",
 				"1":          "string",
 				"2":          0.01,
@@ -469,7 +469,7 @@ func TestFlattenContext(t *testing.T) {
 			inCtx: EvaluationContext{
 				TargetingKey: "user",
 			},
-			outCtx: map[string]interface{}{
+			outCtx: FlattenedContext{
 				TargetingKey: "user",
 			},
 		},
diff --git a/pkg/openfeature/provider_mock_test.go b/pkg/openfeature/provider_mock_test.go
index bcdb2b1..b68fc1f 100644
--- a/pkg/openfeature/provider_mock_test.go
+++ b/pkg/openfeature/provider_mock_test.go
@@ -35,7 +35,7 @@ func (m *MockFeatureProvider) EXPECT() *MockFeatureProviderMockRecorder {
 }
 
 // BooleanEvaluation mocks base method.
-func (m *MockFeatureProvider) BooleanEvaluation(ctx context.Context, flag string, defaultValue bool, evalCtx map[string]interface{}) BoolResolutionDetail {
+func (m *MockFeatureProvider) BooleanEvaluation(ctx context.Context, flag string, defaultValue bool, evalCtx FlattenedContext) BoolResolutionDetail {
 	m.ctrl.T.Helper()
 	ret := m.ctrl.Call(m, "BooleanEvaluation", ctx, flag, defaultValue, evalCtx)
 	ret0, _ := ret[0].(BoolResolutionDetail)
@@ -49,7 +49,7 @@ func (mr *MockFeatureProviderMockRecorder) BooleanEvaluation(ctx, flag, defaultV
 }
 
 // FloatEvaluation mocks base method.
-func (m *MockFeatureProvider) FloatEvaluation(ctx context.Context, flag string, defaultValue float64, evalCtx map[string]interface{}) FloatResolutionDetail {
+func (m *MockFeatureProvider) FloatEvaluation(ctx context.Context, flag string, defaultValue float64, evalCtx FlattenedContext) FloatResolutionDetail {
 	m.ctrl.T.Helper()
 	ret := m.ctrl.Call(m, "FloatEvaluation", ctx, flag, defaultValue, evalCtx)
 	ret0, _ := ret[0].(FloatResolutionDetail)
@@ -77,7 +77,7 @@ func (mr *MockFeatureProviderMockRecorder) Hooks() *gomock.Call {
 }
 
 // IntEvaluation mocks base method.
-func (m *MockFeatureProvider) IntEvaluation(ctx context.Context, flag string, defaultValue int64, evalCtx map[string]interface{}) IntResolutionDetail {
+func (m *MockFeatureProvider) IntEvaluation(ctx context.Context, flag string, defaultValue int64, evalCtx FlattenedContext) IntResolutionDetail {
 	m.ctrl.T.Helper()
 	ret := m.ctrl.Call(m, "IntEvaluation", ctx, flag, defaultValue, evalCtx)
 	ret0, _ := ret[0].(IntResolutionDetail)
@@ -105,7 +105,7 @@ func (mr *MockFeatureProviderMockRecorder) Metadata() *gomock.Call {
 }
 
 // ObjectEvaluation mocks base method.
-func (m *MockFeatureProvider) ObjectEvaluation(ctx context.Context, flag string, defaultValue interface{}, evalCtx map[string]interface{}) InterfaceResolutionDetail {
+func (m *MockFeatureProvider) ObjectEvaluation(ctx context.Context, flag string, defaultValue interface{}, evalCtx FlattenedContext) InterfaceResolutionDetail {
 	m.ctrl.T.Helper()
 	ret := m.ctrl.Call(m, "ObjectEvaluation", ctx, flag, defaultValue, evalCtx)
 	ret0, _ := ret[0].(InterfaceResolutionDetail)
@@ -119,7 +119,7 @@ func (mr *MockFeatureProviderMockRecorder) ObjectEvaluation(ctx, flag, defaultVa
 }
 
 // StringEvaluation mocks base method.
-func (m *MockFeatureProvider) StringEvaluation(ctx context.Context, flag, defaultValue string, evalCtx map[string]interface{}) StringResolutionDetail {
+func (m *MockFeatureProvider) StringEvaluation(ctx context.Context, flag, defaultValue string, evalCtx FlattenedContext) StringResolutionDetail {
 	m.ctrl.T.Helper()
 	ret := m.ctrl.Call(m, "StringEvaluation", ctx, flag, defaultValue, evalCtx)
 	ret0, _ := ret[0].(StringResolutionDetail)
diff --git a/pkg/openfeature/provider_test.go b/pkg/openfeature/provider_test.go
index 5c1983e..9662d5a 100644
--- a/pkg/openfeature/provider_test.go
+++ b/pkg/openfeature/provider_test.go
@@ -42,11 +42,11 @@ func TestRequirement_2_2(t *testing.T) {
 	mockProvider := NewMockFeatureProvider(ctrl)
 
 	type requirements interface {
-		BooleanEvaluation(ctx context.Context, flag string, defaultValue bool, evalCtx map[string]interface{}) BoolResolutionDetail
-		StringEvaluation(ctx context.Context, flag string, defaultValue string, evalCtx map[string]interface{}) StringResolutionDetail
-		FloatEvaluation(ctx context.Context, flag string, defaultValue float64, evalCtx map[string]interface{}) FloatResolutionDetail
-		IntEvaluation(ctx context.Context, flag string, defaultValue int64, evalCtx map[string]interface{}) IntResolutionDetail
-		ObjectEvaluation(ctx context.Context, flag string, defaultValue interface{}, evalCtx map[string]interface{}) InterfaceResolutionDetail
+		BooleanEvaluation(ctx context.Context, flag string, defaultValue bool, evalCtx FlattenedContext) BoolResolutionDetail
+		StringEvaluation(ctx context.Context, flag string, defaultValue string, evalCtx FlattenedContext) StringResolutionDetail
+		FloatEvaluation(ctx context.Context, flag string, defaultValue float64, evalCtx FlattenedContext) FloatResolutionDetail
+		IntEvaluation(ctx context.Context, flag string, defaultValue int64, evalCtx FlattenedContext) IntResolutionDetail
+		ObjectEvaluation(ctx context.Context, flag string, defaultValue interface{}, evalCtx FlattenedContext) InterfaceResolutionDetail
 	}
 
 	var mockProviderI interface{} = mockProvider