@@ -46,7 +46,7 @@ type OptimizelyClient struct {
46
46
notificationCenter notification.Center
47
47
execGroup * utils.ExecGroup
48
48
logger logging.OptimizelyLogProducer
49
- defaultDecideOptions decide.OptimizelyDecideOptions
49
+ defaultDecideOptions * decide.Options
50
50
}
51
51
52
52
// CreateUserContext creates a context of the user for which decision APIs will be called.
@@ -55,6 +55,156 @@ func (o *OptimizelyClient) CreateUserContext(userID string, attributes map[strin
55
55
return newOptimizelyUserContext (o , userID , attributes )
56
56
}
57
57
58
+ func (o * OptimizelyClient ) decide (userContext OptimizelyUserContext , key string , options * decide.Options ) OptimizelyDecision {
59
+ var err error
60
+ defer func () {
61
+ if r := recover (); r != nil {
62
+ switch t := r .(type ) {
63
+ case error :
64
+ err = t
65
+ case string :
66
+ err = errors .New (t )
67
+ default :
68
+ err = errors .New ("unexpected error" )
69
+ }
70
+ errorMessage := fmt .Sprintf ("decide call, optimizely SDK is panicking with the error:" )
71
+ o .logger .Error (errorMessage , err )
72
+ o .logger .Debug (string (debug .Stack ()))
73
+ }
74
+ }()
75
+
76
+ decisionContext := decision.FeatureDecisionContext {}
77
+ projectConfig , err := o .getProjectConfig ()
78
+ if err != nil {
79
+ return NewErrorDecision (key , userContext , decide .GetDecideError (decide .SDKNotReady ))
80
+ }
81
+ decisionContext .ProjectConfig = projectConfig
82
+
83
+ feature , err := projectConfig .GetFeatureByKey (key )
84
+ if err != nil {
85
+ return NewErrorDecision (key , userContext , decide .GetDecideError (decide .FlagKeyInvalid , key ))
86
+ }
87
+ decisionContext .Feature = & feature
88
+
89
+ usrContext := entities.UserContext {
90
+ ID : userContext .GetUserID (),
91
+ Attributes : userContext .GetUserAttributes (),
92
+ }
93
+ var variationKey string
94
+ var eventSent , flagEnabled bool
95
+ allOptions := o .getAllOptions (options )
96
+ decisionReasons := decide .NewDecisionReasons (& allOptions )
97
+ decisionContext .Variable = entities.Variable {}
98
+
99
+ featureDecision , err := o .DecisionService .GetFeatureDecision (decisionContext , usrContext , & allOptions , decisionReasons )
100
+ if err != nil {
101
+ o .logger .Warning (fmt .Sprintf (`Received error while making a decision for feature "%s": %s` , key , err ))
102
+ }
103
+
104
+ if featureDecision .Variation != nil {
105
+ variationKey = featureDecision .Variation .Key
106
+ flagEnabled = featureDecision .Variation .FeatureEnabled
107
+ }
108
+
109
+ if ! allOptions .DisableDecisionEvent {
110
+ if ue , ok := event .CreateImpressionUserEvent (decisionContext .ProjectConfig , featureDecision .Experiment ,
111
+ featureDecision .Variation , usrContext , key , featureDecision .Experiment .Key , featureDecision .Source , flagEnabled ); ok {
112
+ o .EventProcessor .ProcessEvent (ue )
113
+ eventSent = true
114
+ }
115
+ }
116
+
117
+ variableMap := map [string ]interface {}{}
118
+ if ! allOptions .ExcludeVariables {
119
+ variableMap = o .getDecisionVariableMap (feature , featureDecision .Variation , flagEnabled , decisionReasons )
120
+ }
121
+ optimizelyJSON := optimizelyjson .NewOptimizelyJSONfromMap (variableMap )
122
+ reasonsToReport := decisionReasons .ToReport ()
123
+ ruleKey := featureDecision .Experiment .Key
124
+
125
+ if o .notificationCenter != nil {
126
+ decisionNotification := decision .FlagNotification (key , variationKey , ruleKey , flagEnabled , eventSent , usrContext , variableMap , reasonsToReport )
127
+ o .logger .Info (fmt .Sprintf (`Feature "%s" is enabled for user "%s"? %v` , key , usrContext .ID , flagEnabled ))
128
+ if e := o .notificationCenter .Send (notification .Decision , * decisionNotification ); e != nil {
129
+ o .logger .Warning ("Problem with sending notification" )
130
+ }
131
+ }
132
+
133
+ return NewOptimizelyDecision (variationKey , ruleKey , key , flagEnabled , optimizelyJSON , userContext , reasonsToReport )
134
+ }
135
+
136
+ func (o * OptimizelyClient ) decideForKeys (userContext OptimizelyUserContext , keys []string , options * decide.Options ) map [string ]OptimizelyDecision {
137
+ var err error
138
+ defer func () {
139
+ if r := recover (); r != nil {
140
+ switch t := r .(type ) {
141
+ case error :
142
+ err = t
143
+ case string :
144
+ err = errors .New (t )
145
+ default :
146
+ err = errors .New ("unexpected error" )
147
+ }
148
+ errorMessage := fmt .Sprintf ("decideForKeys call, optimizely SDK is panicking with the error:" )
149
+ o .logger .Error (errorMessage , err )
150
+ o .logger .Debug (string (debug .Stack ()))
151
+ }
152
+ }()
153
+
154
+ decisionMap := map [string ]OptimizelyDecision {}
155
+ if _ , err = o .getProjectConfig (); err != nil {
156
+ o .logger .Error ("Optimizely instance is not valid, failing decideForKeys call." , err )
157
+ return decisionMap
158
+ }
159
+
160
+ if len (keys ) == 0 {
161
+ return decisionMap
162
+ }
163
+
164
+ enabledFlagsOnly := o .getAllOptions (options ).EnabledFlagsOnly
165
+ for _ , key := range keys {
166
+ optimizelyDecision := o .decide (userContext , key , options )
167
+ if ! enabledFlagsOnly || optimizelyDecision .GetEnabled () {
168
+ decisionMap [key ] = optimizelyDecision
169
+ }
170
+ }
171
+
172
+ return decisionMap
173
+ }
174
+
175
+ func (o * OptimizelyClient ) decideAll (userContext OptimizelyUserContext , options * decide.Options ) map [string ]OptimizelyDecision {
176
+
177
+ var err error
178
+ defer func () {
179
+ if r := recover (); r != nil {
180
+ switch t := r .(type ) {
181
+ case error :
182
+ err = t
183
+ case string :
184
+ err = errors .New (t )
185
+ default :
186
+ err = errors .New ("unexpected error" )
187
+ }
188
+ errorMessage := fmt .Sprintf ("decideAll call, optimizely SDK is panicking with the error:" )
189
+ o .logger .Error (errorMessage , err )
190
+ o .logger .Debug (string (debug .Stack ()))
191
+ }
192
+ }()
193
+
194
+ projectConfig , err := o .getProjectConfig ()
195
+ if err != nil {
196
+ o .logger .Error ("Optimizely instance is not valid, failing decideAll call." , err )
197
+ return map [string ]OptimizelyDecision {}
198
+ }
199
+
200
+ allFlagKeys := []string {}
201
+ for _ , flag := range projectConfig .GetFeatureList () {
202
+ allFlagKeys = append (allFlagKeys , flag .Key )
203
+ }
204
+
205
+ return o .decideForKeys (userContext , allFlagKeys , options )
206
+ }
207
+
58
208
// Activate returns the key of the variation the user is bucketed into and queues up an impression event to be sent to
59
209
// the Optimizely log endpoint for results processing.
60
210
func (o * OptimizelyClient ) Activate (experimentKey string , userContext entities.UserContext ) (result string , err error ) {
@@ -657,7 +807,7 @@ func (o *OptimizelyClient) getFeatureDecision(featureKey, variableKey string, us
657
807
}
658
808
659
809
decisionContext .Variable = variable
660
- options := decide.OptimizelyDecideOptions {}
810
+ options := & decide.Options {}
661
811
featureDecision , err = o .DecisionService .GetFeatureDecision (decisionContext , userContext , options , decide .NewDecisionReasons (options ))
662
812
if err != nil {
663
813
o .logger .Warning (fmt .Sprintf (`Received error while making a decision for feature "%s": %s` , featureKey , err ))
@@ -688,7 +838,7 @@ func (o *OptimizelyClient) getExperimentDecision(experimentKey string, userConte
688
838
ProjectConfig : projectConfig ,
689
839
}
690
840
691
- options := decide.OptimizelyDecideOptions {}
841
+ options := & decide.Options {}
692
842
experimentDecision , err = o .DecisionService .GetExperimentDecision (decisionContext , userContext , options , decide .NewDecisionReasons (options ))
693
843
if err != nil {
694
844
o .logger .Warning (fmt .Sprintf (`Received error while making a decision for experiment "%s": %s` , experimentKey , err ))
@@ -776,18 +926,49 @@ func (o *OptimizelyClient) getProjectConfig() (projectConfig config.ProjectConfi
776
926
return projectConfig , nil
777
927
}
778
928
929
+ func (o * OptimizelyClient ) getAllOptions (options * decide.Options ) decide.Options {
930
+ return decide.Options {
931
+ DisableDecisionEvent : o .defaultDecideOptions .DisableDecisionEvent || options .DisableDecisionEvent ,
932
+ EnabledFlagsOnly : o .defaultDecideOptions .EnabledFlagsOnly || options .EnabledFlagsOnly ,
933
+ ExcludeVariables : o .defaultDecideOptions .ExcludeVariables || options .ExcludeVariables ,
934
+ IgnoreUserProfileService : o .defaultDecideOptions .IgnoreUserProfileService || options .IgnoreUserProfileService ,
935
+ IncludeReasons : o .defaultDecideOptions .IncludeReasons || options .IncludeReasons ,
936
+ }
937
+ }
938
+
779
939
// GetOptimizelyConfig returns OptimizelyConfig object
780
940
func (o * OptimizelyClient ) GetOptimizelyConfig () (optimizelyConfig * config.OptimizelyConfig ) {
781
941
782
942
return o .ConfigManager .GetOptimizelyConfig ()
783
-
784
943
}
785
944
786
945
// Close closes the Optimizely instance and stops any ongoing tasks from its children components.
787
946
func (o * OptimizelyClient ) Close () {
788
947
o .execGroup .TerminateAndWait ()
789
948
}
790
949
950
+ func (o * OptimizelyClient ) getDecisionVariableMap (feature entities.Feature , variation * entities.Variation , featureEnabled bool , decisionReasons decide.DecisionReasons ) map [string ]interface {} {
951
+ valuesMap := map [string ]interface {}{}
952
+
953
+ for _ , v := range feature .VariableMap {
954
+ val := v .DefaultValue
955
+
956
+ if featureEnabled {
957
+ if variable , ok := variation .Variables [v .ID ]; ok {
958
+ val = variable .Value
959
+ }
960
+ }
961
+
962
+ typedValue , typedError := o .getTypedValue (val , v .Type )
963
+ if typedError != nil {
964
+ decisionReasons .AddError (decide .GetDecideMessage (decide .VariableValueInvalid , v .Key ))
965
+ }
966
+ valuesMap [v .Key ] = typedValue
967
+ }
968
+
969
+ return valuesMap
970
+ }
971
+
791
972
func isNil (v interface {}) bool {
792
973
return v == nil || (reflect .ValueOf (v ).Kind () == reflect .Ptr && reflect .ValueOf (v ).IsNil ())
793
974
}
0 commit comments