3434import com .optimizely .ab .optimizelyconfig .OptimizelyConfig ;
3535import com .optimizely .ab .optimizelyconfig .OptimizelyConfigManager ;
3636import 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 ;
3839import com .optimizely .ab .optimizelydecision .OptimizelyDecideOption ;
40+ import com .optimizely .ab .optimizelydecision .OptimizelyDecision ;
41+ import com .optimizely .ab .optimizelyjson .OptimizelyJSON ;
3942import org .slf4j .Logger ;
4043import org .slf4j .LoggerFactory ;
4144
@@ -1089,6 +1092,8 @@ public OptimizelyConfig getOptimizelyConfig() {
10891092 return new OptimizelyConfigService (projectConfig ).getConfig ();
10901093 }
10911094
1095+ //============ decide ============//
1096+
10921097 /**
10931098 * Create a context of the user for which decision APIs will be called.
10941099 *
@@ -1107,6 +1112,173 @@ public OptimizelyUserContext createUserContext(@Nonnull String userId) {
11071112 return new OptimizelyUserContext (this , userId );
11081113 }
11091114
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+
11101282 /**
11111283 * Helper method which makes separate copy of attributesMap variable and returns it
11121284 *
0 commit comments