34
34
import com .optimizely .ab .optimizelyconfig .OptimizelyConfig ;
35
35
import com .optimizely .ab .optimizelyconfig .OptimizelyConfigManager ;
36
36
import com .optimizely .ab .optimizelyconfig .OptimizelyConfigService ;
37
- import com .optimizely .ab .optimizelyjson .OptimizelyJSON ;
37
+ import com .optimizely .ab .optimizelydecision .DecisionMessage ;
38
+ import com .optimizely .ab .optimizelydecision .DecisionReasons ;
38
39
import com .optimizely .ab .optimizelydecision .OptimizelyDecideOption ;
40
+ import com .optimizely .ab .optimizelydecision .OptimizelyDecision ;
41
+ import com .optimizely .ab .optimizelyjson .OptimizelyJSON ;
39
42
import org .slf4j .Logger ;
40
43
import org .slf4j .LoggerFactory ;
41
44
@@ -1089,6 +1092,8 @@ public OptimizelyConfig getOptimizelyConfig() {
1089
1092
return new OptimizelyConfigService (projectConfig ).getConfig ();
1090
1093
}
1091
1094
1095
+ //============ decide ============//
1096
+
1092
1097
/**
1093
1098
* Create a context of the user for which decision APIs will be called.
1094
1099
*
@@ -1107,6 +1112,173 @@ public OptimizelyUserContext createUserContext(@Nonnull String userId) {
1107
1112
return new OptimizelyUserContext (this , userId );
1108
1113
}
1109
1114
1115
+ OptimizelyDecision decide (@ Nonnull OptimizelyUserContext user ,
1116
+ @ Nonnull String key ,
1117
+ @ Nonnull List <OptimizelyDecideOption > options ) {
1118
+
1119
+ ProjectConfig projectConfig = getProjectConfig ();
1120
+ if (projectConfig == null ) {
1121
+ return OptimizelyDecision .createErrorDecision (key , user , DecisionMessage .SDK_NOT_READY .reason ());
1122
+ }
1123
+
1124
+ FeatureFlag flag = projectConfig .getFeatureKeyMapping ().get (key );
1125
+ if (flag == null ) {
1126
+ return OptimizelyDecision .createErrorDecision (key , user , DecisionMessage .FLAG_KEY_INVALID .reason (key ));
1127
+ }
1128
+
1129
+ String userId = user .getUserId ();
1130
+ Map <String , Object > attributes = user .getAttributes ();
1131
+ Boolean sentEvent = false ;
1132
+ Boolean flagEnabled = false ;
1133
+ List <OptimizelyDecideOption > allOptions = getAllOptions (options );
1134
+ DecisionReasons decisionReasons = new DecisionReasons (allOptions );
1135
+
1136
+ Map <String , ?> copiedAttributes = new HashMap <>(attributes );
1137
+ FeatureDecision flagDecision = decisionService .getVariationForFeature (
1138
+ flag ,
1139
+ userId ,
1140
+ copiedAttributes ,
1141
+ projectConfig ,
1142
+ allOptions ,
1143
+ decisionReasons );
1144
+
1145
+ if (flagDecision .variation != null ) {
1146
+ if (flagDecision .decisionSource .equals (FeatureDecision .DecisionSource .FEATURE_TEST )) {
1147
+ if (!allOptions .contains (OptimizelyDecideOption .DISABLE_DECISION_EVENT )) {
1148
+ sendImpression (
1149
+ projectConfig ,
1150
+ flagDecision .experiment ,
1151
+ userId ,
1152
+ copiedAttributes ,
1153
+ flagDecision .variation );
1154
+ sentEvent = true ;
1155
+ }
1156
+ } else {
1157
+ String message = String .format ("The user \" %s\" is not included in an experiment for flag \" %s\" ." , userId , key );
1158
+ logger .info (message );
1159
+ decisionReasons .addInfo (message );
1160
+ }
1161
+ if (flagDecision .variation .getFeatureEnabled ()) {
1162
+ flagEnabled = true ;
1163
+ }
1164
+ }
1165
+
1166
+ Map <String , Object > variableMap = new HashMap <>();
1167
+ if (!allOptions .contains (OptimizelyDecideOption .EXCLUDE_VARIABLES )) {
1168
+ variableMap = getDecisionVariableMap (
1169
+ flag ,
1170
+ flagDecision .variation ,
1171
+ flagEnabled ,
1172
+ decisionReasons );
1173
+ }
1174
+
1175
+ OptimizelyJSON optimizelyJSON = new OptimizelyJSON (variableMap );
1176
+
1177
+ List <String > reasonsToReport = decisionReasons .toReport ();
1178
+ String variationKey = flagDecision .variation != null ? flagDecision .variation .getKey () : null ;
1179
+ // TODO: add ruleKey values when available later. use a copy of experimentKey until then.
1180
+ String ruleKey = flagDecision .experiment != null ? flagDecision .experiment .getKey () : null ;
1181
+
1182
+ DecisionNotification decisionNotification = DecisionNotification .newFlagDecisionNotificationBuilder ()
1183
+ .withUserId (userId )
1184
+ .withAttributes (copiedAttributes )
1185
+ .withFlagKey (key )
1186
+ .withEnabled (flagEnabled )
1187
+ .withVariables (variableMap )
1188
+ .withVariationKey (variationKey )
1189
+ .withRuleKey (ruleKey )
1190
+ .withReasons (reasonsToReport )
1191
+ .withDecisionEventDispatched (sentEvent )
1192
+ .build ();
1193
+ notificationCenter .send (decisionNotification );
1194
+
1195
+ logger .info ("Feature \" {}\" is enabled for user \" {}\" ? {}" , key , userId , flagEnabled );
1196
+
1197
+ return new OptimizelyDecision (
1198
+ variationKey ,
1199
+ flagEnabled ,
1200
+ optimizelyJSON ,
1201
+ ruleKey ,
1202
+ key ,
1203
+ user ,
1204
+ reasonsToReport );
1205
+ }
1206
+
1207
+ Map <String , OptimizelyDecision > decideForKeys (@ Nonnull OptimizelyUserContext user ,
1208
+ @ Nonnull List <String > keys ,
1209
+ @ Nonnull List <OptimizelyDecideOption > options ) {
1210
+ Map <String , OptimizelyDecision > decisionMap = new HashMap <>();
1211
+
1212
+ ProjectConfig projectConfig = getProjectConfig ();
1213
+ if (projectConfig == null ) {
1214
+ logger .error ("Optimizely instance is not valid, failing isFeatureEnabled call." );
1215
+ return decisionMap ;
1216
+ }
1217
+
1218
+ if (keys .isEmpty ()) return decisionMap ;
1219
+
1220
+ List <OptimizelyDecideOption > allOptions = getAllOptions (options );
1221
+
1222
+ for (String key : keys ) {
1223
+ OptimizelyDecision decision = decide (user , key , options );
1224
+ if (!allOptions .contains (OptimizelyDecideOption .ENABLED_FLAGS_ONLY ) || decision .getEnabled ()) {
1225
+ decisionMap .put (key , decision );
1226
+ }
1227
+ }
1228
+
1229
+ return decisionMap ;
1230
+ }
1231
+
1232
+ Map <String , OptimizelyDecision > decideAll (@ Nonnull OptimizelyUserContext user ,
1233
+ @ Nonnull List <OptimizelyDecideOption > options ) {
1234
+ Map <String , OptimizelyDecision > decisionMap = new HashMap <>();
1235
+
1236
+ ProjectConfig projectConfig = getProjectConfig ();
1237
+ if (projectConfig == null ) {
1238
+ logger .error ("Optimizely instance is not valid, failing isFeatureEnabled call." );
1239
+ return decisionMap ;
1240
+ }
1241
+
1242
+ List <FeatureFlag > allFlags = projectConfig .getFeatureFlags ();
1243
+ List <String > allFlagKeys = new ArrayList <>();
1244
+ for (int i = 0 ; i < allFlags .size (); i ++) allFlagKeys .add (allFlags .get (i ).getKey ());
1245
+
1246
+ return decideForKeys (user , allFlagKeys , options );
1247
+ }
1248
+
1249
+ private List <OptimizelyDecideOption > getAllOptions (List <OptimizelyDecideOption > options ) {
1250
+ List <OptimizelyDecideOption > copiedOptions = new ArrayList (defaultDecideOptions );
1251
+ copiedOptions .addAll (options );
1252
+ return copiedOptions ;
1253
+ }
1254
+
1255
+ private Map <String , Object > getDecisionVariableMap (@ Nonnull FeatureFlag flag ,
1256
+ @ Nonnull Variation variation ,
1257
+ @ Nonnull Boolean featureEnabled ,
1258
+ @ Nonnull DecisionReasons decisionReasons ) {
1259
+ Map <String , Object > valuesMap = new HashMap <String , Object >();
1260
+ for (FeatureVariable variable : flag .getVariables ()) {
1261
+ String value = variable .getDefaultValue ();
1262
+ if (featureEnabled ) {
1263
+ FeatureVariableUsageInstance instance = variation .getVariableIdToFeatureVariableUsageInstanceMap ().get (variable .getId ());
1264
+ if (instance != null ) {
1265
+ value = instance .getValue ();
1266
+ }
1267
+ }
1268
+
1269
+ Object convertedValue = convertStringToType (value , variable .getType ());
1270
+ if (convertedValue == null ) {
1271
+ decisionReasons .addError (DecisionMessage .VARIABLE_VALUE_INVALID .reason (variable .getKey ()));
1272
+ } else if (convertedValue instanceof OptimizelyJSON ) {
1273
+ convertedValue = ((OptimizelyJSON ) convertedValue ).toMap ();
1274
+ }
1275
+
1276
+ valuesMap .put (variable .getKey (), convertedValue );
1277
+ }
1278
+
1279
+ return valuesMap ;
1280
+ }
1281
+
1110
1282
/**
1111
1283
* Helper method which makes separate copy of attributesMap variable and returns it
1112
1284
*
0 commit comments