Skip to content

Commit 99f8802

Browse files
authored
feat(api): Hook up IsFeatureEnabled to the proj config and update the method signature (#36)
1 parent 1c29356 commit 99f8802

File tree

3 files changed

+178
-16
lines changed

3 files changed

+178
-16
lines changed

examples/main.go

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,26 @@ package main
33
import (
44
"fmt"
55

6+
"time"
7+
68
"github.com/optimizely/go-sdk/optimizely/client"
9+
"github.com/optimizely/go-sdk/optimizely/entities"
710
"github.com/optimizely/go-sdk/optimizely/event"
8-
"time"
911
)
1012

1113
func main() {
1214
optimizelyFactory := &client.OptimizelyFactory{
1315
SDKKey: "ABC",
1416
}
1517
client := optimizelyFactory.Client()
16-
fmt.Printf("Is feature enabled? %v", client.IsFeatureEnabled("go_sdk", "mike", nil))
18+
19+
user := entities.UserContext{
20+
ID: "mike ng",
21+
Attributes: entities.UserAttributes{},
22+
}
23+
24+
enabled, _ := client.IsFeatureEnabled("go_sdk", user)
25+
fmt.Printf("Is feature enabled? %v", enabled)
1726

1827
processor := event.NewEventProcessor(100, 100)
1928

optimizely/client/client.go

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
package client
1818

1919
import (
20+
"errors"
21+
2022
"github.com/optimizely/go-sdk/optimizely/config"
2123
"github.com/optimizely/go-sdk/optimizely/decision"
2224
"github.com/optimizely/go-sdk/optimizely/entities"
@@ -33,29 +35,31 @@ type OptimizelyClient struct {
3335
}
3436

3537
// IsFeatureEnabled returns true if the feature is enabled for the given user
36-
func (optly *OptimizelyClient) IsFeatureEnabled(featureKey string, userID string, attributes map[string]interface{}) bool {
37-
if !optly.isValid {
38+
func (o *OptimizelyClient) IsFeatureEnabled(featureKey string, userContext entities.UserContext) (bool, error) {
39+
if !o.isValid {
3840
errorMessage := "Optimizely instance is not valid. Failing IsFeatureEnabled."
41+
err := errors.New(errorMessage)
3942
logger.Error(errorMessage, nil)
40-
return false
43+
return false, err
4144
}
4245

43-
userContext := entities.UserContext{ID: userID, Attributes: entities.UserAttributes{Attributes: attributes}}
44-
45-
// @TODO(mng): we should fetch the Feature entity from the config service instead of manually creating it here
46-
featureExperiment := entities.Experiment{}
47-
feature := entities.Feature{
48-
Key: featureKey,
49-
FeatureExperiments: []entities.Experiment{featureExperiment},
46+
projectConfig := o.configManager.GetConfig()
47+
feature, err := projectConfig.GetFeatureByKey(featureKey)
48+
if err != nil {
49+
return false, err
5050
}
51+
52+
// @TODO(mng): Include assigned group for mutex support
5153
featureDecisionContext := decision.FeatureDecisionContext{
5254
Feature: feature,
5355
}
5456

55-
featureDecision, err := optly.decisionService.GetFeatureDecision(featureDecisionContext, userContext)
57+
featureDecision, err := o.decisionService.GetFeatureDecision(featureDecisionContext, userContext)
5658
if err != nil {
57-
// @TODO(mng): log error
58-
return false
59+
logger.Error("Received an error while computing feature decision", err)
60+
return false, err
5961
}
60-
return featureDecision.FeatureEnabled
62+
63+
// @TODO(mng): send impression event
64+
return featureDecision.FeatureEnabled, nil
6165
}

optimizely/client/client_test.go

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
/****************************************************************************
2+
* Copyright 2019, Optimizely, Inc. and contributors *
3+
* *
4+
* Licensed under the Apache License, Version 2.0 (the "License"); *
5+
* you may not use this file except in compliance with the License. *
6+
* You may obtain a copy of the License at *
7+
* *
8+
* http://www.apache.org/licenses/LICENSE-2.0 *
9+
* *
10+
* Unless required by applicable law or agreed to in writing, software *
11+
* distributed under the License is distributed on an "AS IS" BASIS, *
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
13+
* See the License for the specific language governing permissions and *
14+
* limitations under the License. *
15+
***************************************************************************/
16+
17+
package client
18+
19+
import (
20+
"errors"
21+
"testing"
22+
23+
"github.com/stretchr/testify/assert"
24+
25+
"github.com/optimizely/go-sdk/optimizely/config"
26+
"github.com/optimizely/go-sdk/optimizely/decision"
27+
"github.com/optimizely/go-sdk/optimizely/entities"
28+
"github.com/stretchr/testify/mock"
29+
)
30+
31+
type MockProjectConfig struct {
32+
config.ProjectConfig
33+
mock.Mock
34+
}
35+
36+
func (c *MockProjectConfig) GetFeatureByKey(featureKey string) (entities.Feature, error) {
37+
args := c.Called(featureKey)
38+
return args.Get(0).(entities.Feature), args.Error(1)
39+
}
40+
41+
type MockProjectConfigManager struct {
42+
mock.Mock
43+
}
44+
45+
func (p *MockProjectConfigManager) GetConfig() config.ProjectConfig {
46+
args := p.Called()
47+
return args.Get(0).(config.ProjectConfig)
48+
}
49+
50+
type MockDecisionService struct {
51+
mock.Mock
52+
}
53+
54+
func (m *MockDecisionService) GetFeatureDecision(decisionContext decision.FeatureDecisionContext, userContext entities.UserContext) (decision.FeatureDecision, error) {
55+
args := m.Called(decisionContext, userContext)
56+
return args.Get(0).(decision.FeatureDecision), args.Error(1)
57+
}
58+
59+
func TestIsFeatureEnabled(t *testing.T) {
60+
testUserContext := entities.UserContext{ID: "test_user_1"}
61+
testFeatureKey := "test_feature_key"
62+
testFeature := entities.Feature{
63+
Key: testFeatureKey,
64+
FeatureExperiments: []entities.Experiment{
65+
entities.Experiment{
66+
ID: "111111",
67+
Variations: map[string]entities.Variation{
68+
"22222": entities.Variation{
69+
ID: "22222",
70+
Key: "22222",
71+
FeatureEnabled: true,
72+
},
73+
},
74+
},
75+
},
76+
}
77+
78+
// Test happy path
79+
mockConfig := new(MockProjectConfig)
80+
mockConfig.On("GetFeatureByKey", testFeatureKey).Return(testFeature, nil)
81+
82+
mockConfigManager := new(MockProjectConfigManager)
83+
mockConfigManager.On("GetConfig").Return(mockConfig)
84+
85+
// Set up the mock decision service and its return value
86+
testDecisionContext := decision.FeatureDecisionContext{
87+
Feature: testFeature,
88+
}
89+
90+
expectedFeatureDecision := decision.FeatureDecision{
91+
FeatureEnabled: true,
92+
Decision: decision.Decision{
93+
DecisionMade: true,
94+
},
95+
}
96+
97+
mockDecisionService := new(MockDecisionService)
98+
mockDecisionService.On("GetFeatureDecision", testDecisionContext, testUserContext).Return(expectedFeatureDecision, nil)
99+
100+
client := OptimizelyClient{
101+
configManager: mockConfigManager,
102+
decisionService: mockDecisionService,
103+
isValid: true,
104+
}
105+
result, _ := client.IsFeatureEnabled(testFeatureKey, testUserContext)
106+
assert.True(t, result)
107+
mockConfig.AssertExpectations(t)
108+
mockConfigManager.AssertExpectations(t)
109+
mockDecisionService.AssertExpectations(t)
110+
}
111+
112+
func TestIsFeatureEnabledErrorCases(t *testing.T) {
113+
testUserContext := entities.UserContext{ID: "test_user_1"}
114+
testFeatureKey := "test_feature_key"
115+
116+
// Test instance invalid
117+
mockConfigManager := new(MockProjectConfigManager)
118+
mockDecisionService := new(MockDecisionService)
119+
120+
client := OptimizelyClient{
121+
configManager: mockConfigManager,
122+
decisionService: mockDecisionService,
123+
isValid: false,
124+
}
125+
result, _ := client.IsFeatureEnabled(testFeatureKey, testUserContext)
126+
assert.False(t, result)
127+
mockConfigManager.AssertNotCalled(t, "GetFeatureByKey")
128+
mockDecisionService.AssertNotCalled(t, "GetFeatureDecision")
129+
130+
// Test invalid feature key
131+
expectedError := errors.New("Invalid feature key")
132+
mockConfig := new(MockProjectConfig)
133+
mockConfig.On("GetFeatureByKey", testFeatureKey).Return(entities.Feature{}, expectedError)
134+
135+
mockConfigManager = new(MockProjectConfigManager)
136+
mockConfigManager.On("GetConfig").Return(mockConfig)
137+
mockDecisionService = new(MockDecisionService)
138+
139+
client = OptimizelyClient{
140+
configManager: mockConfigManager,
141+
decisionService: mockDecisionService,
142+
isValid: true,
143+
}
144+
result, err := client.IsFeatureEnabled(testFeatureKey, testUserContext)
145+
if assert.Error(t, err) {
146+
assert.Equal(t, expectedError, err)
147+
}
148+
assert.False(t, result)
149+
}

0 commit comments

Comments
 (0)