Skip to content

Commit 78a8b02

Browse files
Merge branch 'master' into pawel/fix_requester_despatcher
# Conflicts: # optimizely/client/client_test.go
2 parents a01cdc4 + 2d89df4 commit 78a8b02

File tree

4 files changed

+772
-153
lines changed

4 files changed

+772
-153
lines changed

optimizely/client/client.go

Lines changed: 80 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -45,34 +45,7 @@ type OptimizelyClient struct {
4545
// IsFeatureEnabled returns true if the feature is enabled for the given user
4646
func (o *OptimizelyClient) IsFeatureEnabled(featureKey string, userContext entities.UserContext) (result bool, err error) {
4747

48-
defer func() {
49-
if r := recover(); r != nil {
50-
errorMessage := fmt.Sprintf(`Optimizely SDK is panicking with the error "%s"`, string(debug.Stack()))
51-
err = errors.New(errorMessage)
52-
logger.Error(errorMessage, err)
53-
}
54-
}()
55-
56-
projectConfig, err := o.GetProjectConfig()
57-
if err != nil {
58-
logger.Error("Error retrieving feature", err)
59-
return false, err
60-
}
61-
62-
feature, err := projectConfig.GetFeatureByKey(featureKey)
63-
if err != nil {
64-
logger.Error("Error retrieving feature", err)
65-
return result, err
66-
}
67-
featureDecisionContext := decision.FeatureDecisionContext{
68-
Feature: &feature,
69-
ProjectConfig: projectConfig,
70-
}
71-
72-
userID := userContext.ID
73-
logger.Debug(fmt.Sprintf(`Evaluating feature "%s" for user "%s".`, featureKey, userID))
74-
featureDecision, err := o.decisionService.GetFeatureDecision(featureDecisionContext, userContext)
75-
48+
context, featureDecision, err := o.getFeatureDecision(featureKey, userContext)
7649
if err != nil {
7750
logger.Error("received an error while computing feature decision", err)
7851
return result, err
@@ -85,17 +58,17 @@ func (o *OptimizelyClient) IsFeatureEnabled(featureKey string, userContext entit
8558
}
8659

8760
if result {
88-
logger.Info(fmt.Sprintf(`Feature "%s" is enabled for user "%s".`, featureKey, userID))
61+
logger.Info(fmt.Sprintf(`Feature "%s" is enabled for user "%s".`, featureKey, userContext.ID))
8962
} else {
90-
logger.Info(fmt.Sprintf(`Feature "%s" is not enabled for user "%s".`, featureKey, userID))
63+
logger.Info(fmt.Sprintf(`Feature "%s" is not enabled for user "%s".`, featureKey, userContext.ID))
9164
}
9265

9366
if featureDecision.Source == decision.FeatureTest {
9467
// send impression event for feature tests
95-
impressionEvent := event.CreateImpressionUserEvent(projectConfig, featureDecision.Experiment, *featureDecision.Variation, userContext)
68+
impressionEvent := event.CreateImpressionUserEvent(context.ProjectConfig, featureDecision.Experiment, *featureDecision.Variation, userContext)
9669
o.eventProcessor.ProcessEvent(impressionEvent)
9770
}
98-
return result, nil
71+
return result, err
9972
}
10073

10174
// GetEnabledFeatures returns an array containing the keys of all features in the project that are enabled for the given user.
@@ -124,7 +97,7 @@ func (o *OptimizelyClient) GetEnabledFeatures(userContext entities.UserContext)
12497
}
12598
}
12699

127-
return enabledFeatures, nil
100+
return enabledFeatures, err
128101
}
129102

130103
// Track take and event key with event tags and if the event is part of the config, send to events backend.
@@ -155,12 +128,12 @@ func (o *OptimizelyClient) Track(eventKey string, userContext entities.UserConte
155128
return err
156129
}
157130

158-
return nil
131+
return err
159132
}
160133

161134
// GetFeatureVariableBoolean returns boolean feature variable value
162135
func (o *OptimizelyClient) GetFeatureVariableBoolean(featureKey, variableKey string, userContext entities.UserContext) (value bool, err error) {
163-
val, valueType, err := o.getFeatureVariable(featureKey, variableKey, userContext)
136+
val, valueType, err := o.GetFeatureVariable(featureKey, variableKey, userContext)
164137
if err != nil {
165138
return false, err
166139
}
@@ -173,7 +146,7 @@ func (o *OptimizelyClient) GetFeatureVariableBoolean(featureKey, variableKey str
173146

174147
// GetFeatureVariableDouble returns double feature variable value
175148
func (o *OptimizelyClient) GetFeatureVariableDouble(featureKey, variableKey string, userContext entities.UserContext) (value float64, err error) {
176-
val, valueType, err := o.getFeatureVariable(featureKey, variableKey, userContext)
149+
val, valueType, err := o.GetFeatureVariable(featureKey, variableKey, userContext)
177150
if err != nil {
178151
return 0, err
179152
}
@@ -186,7 +159,7 @@ func (o *OptimizelyClient) GetFeatureVariableDouble(featureKey, variableKey stri
186159

187160
// GetFeatureVariableInteger returns integer feature variable value
188161
func (o *OptimizelyClient) GetFeatureVariableInteger(featureKey, variableKey string, userContext entities.UserContext) (value int, err error) {
189-
val, valueType, err := o.getFeatureVariable(featureKey, variableKey, userContext)
162+
val, valueType, err := o.GetFeatureVariable(featureKey, variableKey, userContext)
190163
if err != nil {
191164
return 0, err
192165
}
@@ -199,7 +172,7 @@ func (o *OptimizelyClient) GetFeatureVariableInteger(featureKey, variableKey str
199172

200173
// GetFeatureVariableString returns string feature variable value
201174
func (o *OptimizelyClient) GetFeatureVariableString(featureKey, variableKey string, userContext entities.UserContext) (value string, err error) {
202-
value, valueType, err := o.getFeatureVariable(featureKey, variableKey, userContext)
175+
value, valueType, err := o.GetFeatureVariable(featureKey, variableKey, userContext)
203176
if err != nil {
204177
return "", err
205178
}
@@ -209,50 +182,98 @@ func (o *OptimizelyClient) GetFeatureVariableString(featureKey, variableKey stri
209182
return value, err
210183
}
211184

212-
func (o *OptimizelyClient) getFeatureVariable(featureKey, variableKey string, userContext entities.UserContext) (value string, valueType entities.VariableType, err error) {
185+
// GetFeatureVariable returns feature as a string along with it's associated type
186+
func (o *OptimizelyClient) GetFeatureVariable(featureKey, variableKey string, userContext entities.UserContext) (value string, valueType entities.VariableType, err error) {
187+
188+
context, featureDecision, err := o.getFeatureDecision(featureKey, userContext)
189+
if err != nil {
190+
return "", "", err
191+
}
192+
193+
variable, err := context.ProjectConfig.GetVariableByKey(featureKey, variableKey)
194+
if err != nil {
195+
return "", "", err
196+
}
197+
198+
if featureDecision.Variation != nil {
199+
if v, ok := featureDecision.Variation.Variables[variable.ID]; ok && featureDecision.Variation.FeatureEnabled {
200+
return v.Value, variable.Type, err
201+
}
202+
}
203+
204+
return variable.DefaultValue, variable.Type, err
205+
}
206+
207+
// GetAllFeatureVariables returns all the variables for a given feature along with the enabled state
208+
func (o *OptimizelyClient) GetAllFeatureVariables(featureKey string, userContext entities.UserContext) (enabled bool, variableMap map[string]string, err error) {
209+
variableMap = make(map[string]string)
210+
decisionContext, featureDecision, err := o.getFeatureDecision(featureKey, userContext)
211+
if err != nil {
212+
logger.Error("Optimizely SDK tracking error", err)
213+
return enabled, variableMap, err
214+
}
215+
216+
feature := decisionContext.Feature
217+
if featureDecision.Variation != nil {
218+
enabled = featureDecision.Variation.FeatureEnabled
219+
}
220+
221+
for _, v := range feature.Variables {
222+
variableMap[v.Key] = v.DefaultValue
223+
224+
if enabled {
225+
if variable, ok := featureDecision.Variation.Variables[v.ID]; ok {
226+
variableMap[v.Key] = variable.Value
227+
}
228+
}
229+
}
230+
231+
return enabled, variableMap, err
232+
}
233+
234+
func (o *OptimizelyClient) getFeatureDecision(featureKey string, userContext entities.UserContext) (decisionContext decision.FeatureDecisionContext, featureDecision decision.FeatureDecision, err error) {
213235

214236
defer func() {
215237
if r := recover(); r != nil {
216238
errorMessage := fmt.Sprintf(`optimizely SDK is panicking with the error "%s"`, string(debug.Stack()))
217-
err = errors.New(errorMessage)
218239
logger.Error(errorMessage, err)
240+
241+
// If we have a feature, then we can recover w/o throwing
242+
if decisionContext.Feature == nil {
243+
err = errors.New(errorMessage)
244+
}
219245
}
220246
}()
221247

248+
userID := userContext.ID
249+
logger.Debug(fmt.Sprintf(`Evaluating feature "%s" for user "%s".`, featureKey, userID))
250+
222251
projectConfig, err := o.GetProjectConfig()
223252
if err != nil {
224-
logger.Error("Error calling getFeatureVariable", err)
225-
return "", "", err
253+
logger.Error("Error calling getFeatureDecision", err)
254+
return decisionContext, featureDecision, err
226255
}
227256

228257
feature, err := projectConfig.GetFeatureByKey(featureKey)
229258
if err != nil {
230-
logger.Error("Error calling getFeatureVariable", err)
231-
return "", "", err
232-
}
233-
234-
variable, err := projectConfig.GetVariableByKey(featureKey, variableKey)
235-
if err != nil {
236-
logger.Error("Error calling getFeatureVariable", err)
237-
return "", "", err
259+
logger.Error("Error calling getFeatureDecision", err)
260+
return decisionContext, featureDecision, err
238261
}
239262

240-
featureValue := variable.DefaultValue
241-
242-
featureDecisionContext := decision.FeatureDecisionContext{
263+
decisionContext = decision.FeatureDecisionContext{
243264
Feature: &feature,
244265
ProjectConfig: projectConfig,
245266
}
246267

247-
featureDecision, err := o.decisionService.GetFeatureDecision(featureDecisionContext, userContext)
248-
if err == nil && featureDecision.Variation != nil {
249-
if v, ok := featureDecision.Variation.Variables[variable.ID]; ok && featureDecision.Variation.FeatureEnabled {
250-
featureValue = v.Value
251-
}
268+
featureDecision, err = o.decisionService.GetFeatureDecision(decisionContext, userContext)
269+
if err != nil {
270+
err = nil
271+
logger.Warning("error making a decision")
272+
return decisionContext, featureDecision, err
252273
}
253274

254275
// @TODO(yasir): send decision notification
255-
return featureValue, variable.Type, nil
276+
return decisionContext, featureDecision, err
256277
}
257278

258279
// GetProjectConfig returns the current ProjectConfig or nil if the instance is not valid

0 commit comments

Comments
 (0)