Skip to content

Commit 95768e6

Browse files
committed
update
1 parent e813bfe commit 95768e6

File tree

3 files changed

+397
-68
lines changed

3 files changed

+397
-68
lines changed

.vscode/settings.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"java.compile.nullAnalysis.mode": "automatic"
3+
}

core-api/src/main/java/com/optimizely/ab/Optimizely.java

Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@
4242
import javax.annotation.Nonnull;
4343
import javax.annotation.Nullable;
4444
import javax.annotation.concurrent.ThreadSafe;
45+
import javax.xml.catalog.CatalogFeatures.Feature;
46+
4547
import java.io.Closeable;
4648
import java.util.*;
4749

@@ -1296,6 +1298,174 @@ OptimizelyDecision decide(@Nonnull OptimizelyUserContext user,
12961298
reasonsToReport);
12971299
}
12981300

1301+
Optional<FeatureDecision> getForcedDecision(@Nonnull String flagKey,
1302+
@Nonnull DecisionReasons decisionReasons,
1303+
@Nonnull ProjectConfig projectConfig,
1304+
@Nonnull OptimizelyUserContext user) {
1305+
1306+
OptimizelyDecisionContext optimizelyDecisionContext = new OptimizelyDecisionContext(flagKey, null);
1307+
DecisionResponse<Variation> forcedDecisionVariation = decisionService.validatedForcedDecision(optimizelyDecisionContext, projectConfig, user);
1308+
decisionReasons.merge(forcedDecisionVariation.getReasons());
1309+
if (forcedDecisionVariation.getResult() != null) {
1310+
return Optional.of(new FeatureDecision(null, forcedDecisionVariation.getResult(), FeatureDecision.DecisionSource.FEATURE_TEST));
1311+
}
1312+
1313+
return Optional.empty();
1314+
}
1315+
1316+
OptimizelyDecision decideInternal(@Nonnull OptimizelyUserContext user,
1317+
@Nonnull String key,
1318+
@Nonnull List<OptimizelyDecideOption> options) {
1319+
1320+
ProjectConfig projectConfig = getProjectConfig();
1321+
if (projectConfig == null) {
1322+
return OptimizelyDecision.newErrorDecision(key, user, DecisionMessage.SDK_NOT_READY.reason());
1323+
}
1324+
1325+
FeatureFlag flag = projectConfig.getFeatureKeyMapping().get(key);
1326+
if (flag == null) {
1327+
return OptimizelyDecision.newErrorDecision(key, user, DecisionMessage.FLAG_KEY_INVALID.reason(key));
1328+
}
1329+
1330+
String userId = user.getUserId();
1331+
Map<String, Object> attributes = user.getAttributes();
1332+
Boolean decisionEventDispatched = false;
1333+
List<OptimizelyDecideOption> allOptions = getAllOptions(options);
1334+
DecisionReasons decisionReasons = DefaultDecisionReasons.newInstance(allOptions);
1335+
1336+
Map<String, ?> copiedAttributes = new HashMap<>(attributes);
1337+
FeatureDecision flagDecision;
1338+
1339+
// Check Forced Decision
1340+
OptimizelyDecisionContext optimizelyDecisionContext = new OptimizelyDecisionContext(flag.getKey(), null);
1341+
DecisionResponse<Variation> forcedDecisionVariation = decisionService.validatedForcedDecision(optimizelyDecisionContext, projectConfig, user);
1342+
decisionReasons.merge(forcedDecisionVariation.getReasons());
1343+
if (forcedDecisionVariation.getResult() != null) {
1344+
flagDecision = new FeatureDecision(null, forcedDecisionVariation.getResult(), FeatureDecision.DecisionSource.FEATURE_TEST);
1345+
} else {
1346+
// Regular decision
1347+
DecisionResponse<FeatureDecision> decisionVariation = decisionService.getVariationForFeature(
1348+
flag,
1349+
user,
1350+
projectConfig,
1351+
allOptions);
1352+
flagDecision = decisionVariation.getResult();
1353+
decisionReasons.merge(decisionVariation.getReasons());
1354+
}
1355+
1356+
Boolean flagEnabled = false;
1357+
if (flagDecision.variation != null) {
1358+
if (flagDecision.variation.getFeatureEnabled()) {
1359+
flagEnabled = true;
1360+
}
1361+
}
1362+
logger.info("Feature \"{}\" is enabled for user \"{}\"? {}", key, userId, flagEnabled);
1363+
1364+
Map<String, Object> variableMap = new HashMap<>();
1365+
if (!allOptions.contains(OptimizelyDecideOption.EXCLUDE_VARIABLES)) {
1366+
DecisionResponse<Map<String, Object>> decisionVariables = getDecisionVariableMap(
1367+
flag,
1368+
flagDecision.variation,
1369+
flagEnabled);
1370+
variableMap = decisionVariables.getResult();
1371+
decisionReasons.merge(decisionVariables.getReasons());
1372+
}
1373+
OptimizelyJSON optimizelyJSON = new OptimizelyJSON(variableMap);
1374+
1375+
FeatureDecision.DecisionSource decisionSource = FeatureDecision.DecisionSource.ROLLOUT;
1376+
if (flagDecision.decisionSource != null) {
1377+
decisionSource = flagDecision.decisionSource;
1378+
}
1379+
1380+
List<String> reasonsToReport = decisionReasons.toReport();
1381+
String variationKey = flagDecision.variation != null ? flagDecision.variation.getKey() : null;
1382+
// TODO: add ruleKey values when available later. use a copy of experimentKey until then.
1383+
// add to event metadata as well (currently set to experimentKey)
1384+
String ruleKey = flagDecision.experiment != null ? flagDecision.experiment.getKey() : null;
1385+
1386+
if (!allOptions.contains(OptimizelyDecideOption.DISABLE_DECISION_EVENT)) {
1387+
decisionEventDispatched = sendImpression(
1388+
projectConfig,
1389+
flagDecision.experiment,
1390+
userId,
1391+
copiedAttributes,
1392+
flagDecision.variation,
1393+
key,
1394+
decisionSource.toString(),
1395+
flagEnabled);
1396+
}
1397+
1398+
DecisionNotification decisionNotification = DecisionNotification.newFlagDecisionNotificationBuilder()
1399+
.withUserId(userId)
1400+
.withAttributes(copiedAttributes)
1401+
.withFlagKey(key)
1402+
.withEnabled(flagEnabled)
1403+
.withVariables(variableMap)
1404+
.withVariationKey(variationKey)
1405+
.withRuleKey(ruleKey)
1406+
.withReasons(reasonsToReport)
1407+
.withDecisionEventDispatched(decisionEventDispatched)
1408+
.build();
1409+
notificationCenter.send(decisionNotification);
1410+
1411+
return new OptimizelyDecision(
1412+
variationKey,
1413+
flagEnabled,
1414+
optimizelyJSON,
1415+
ruleKey,
1416+
key,
1417+
user,
1418+
reasonsToReport);
1419+
}
1420+
1421+
Map<String, OptimizelyDecision> decideForKeysInternal(@Nonnull OptimizelyUserContext user,
1422+
@Nonnull List<String> keys,
1423+
@Nonnull List<OptimizelyDecideOption> options) {
1424+
Map<String, OptimizelyDecision> decisionMap = new HashMap<>();
1425+
1426+
ProjectConfig projectConfig = getProjectConfig();
1427+
if (projectConfig == null) {
1428+
logger.error("Optimizely instance is not valid, failing isFeatureEnabled call.");
1429+
return decisionMap;
1430+
}
1431+
1432+
if (keys.isEmpty()) return decisionMap;
1433+
1434+
String userId = user.getUserId();
1435+
Map<String, Object> attributes = user.getAttributes();
1436+
Boolean decisionEventDispatched = false;
1437+
List<OptimizelyDecideOption> allOptions = getAllOptions(options);
1438+
1439+
Map<String, FeatureDecision> flagDecisions = new HashMap<>();
1440+
Map<String, DecisionReasons> decisionReasonsMap = new HashMap<>();
1441+
1442+
List<String> keysWithoutForcedDecision = new ArrayList<>();
1443+
1444+
for (String key : keys) {
1445+
FeatureFlag flag = projectConfig.getFeatureKeyMapping().get(key);
1446+
if (flag == null) {
1447+
decisionMap.put(key, OptimizelyDecision.newErrorDecision(key, user, DecisionMessage.FLAG_KEY_INVALID.reason(key)));
1448+
continue;
1449+
}
1450+
1451+
DecisionReasons decisionReasons = DefaultDecisionReasons.newInstance(allOptions);
1452+
Optional<FeatureDecision> forcedDecision = getForcedDecision(key, decisionReasons, projectConfig, user);
1453+
decisionReasonsMap.put(key, decisionReasons);
1454+
1455+
if (forcedDecision.isPresent()) {
1456+
flagDecisions.put(key, forcedDecision.get());
1457+
} else {
1458+
keysWithoutForcedDecision.add(key);
1459+
}
1460+
// OptimizelyDecision decision = decide(user, key, options);
1461+
// if (!allOptions.contains(OptimizelyDecideOption.ENABLED_FLAGS_ONLY) || decision.getEnabled()) {
1462+
// decisionMap.put(key, decision);
1463+
// }
1464+
}
1465+
1466+
return decisionMap;
1467+
}
1468+
12991469
Map<String, OptimizelyDecision> decideForKeys(@Nonnull OptimizelyUserContext user,
13001470
@Nonnull List<String> keys,
13011471
@Nonnull List<OptimizelyDecideOption> options) {

0 commit comments

Comments
 (0)