Skip to content

Commit 1c52366

Browse files
update: add useCmab parameter to decideForKeys methods for enhanced decision handling
1 parent 7363a2f commit 1c52366

File tree

2 files changed

+132
-103
lines changed

2 files changed

+132
-103
lines changed

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

Lines changed: 11 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -1408,7 +1408,8 @@ Map<String, OptimizelyDecision> decideForKeys(@Nonnull OptimizelyUserContext use
14081408
private Map<String, OptimizelyDecision> decideForKeys(@Nonnull OptimizelyUserContext user,
14091409
@Nonnull List<String> keys,
14101410
@Nonnull List<OptimizelyDecideOption> options,
1411-
boolean ignoreDefaultOptions) {
1411+
boolean ignoreDefaultOptions,
1412+
boolean useCmab) {
14121413
Map<String, OptimizelyDecision> decisionMap = new HashMap<>();
14131414

14141415
ProjectConfig projectConfig = getProjectConfig();
@@ -1453,7 +1454,7 @@ private Map<String, OptimizelyDecision> decideForKeys(@Nonnull OptimizelyUserCon
14531454
}
14541455

14551456
List<DecisionResponse<FeatureDecision>> decisionList =
1456-
decisionService.getVariationsForFeatureList(flagsWithoutForcedDecision, user, projectConfig, allOptions);
1457+
decisionService.getVariationsForFeatureList(flagsWithoutForcedDecision, user, projectConfig, allOptions, useCmab);
14571458

14581459
for (int i = 0; i < flagsWithoutForcedDecision.size(); i++) {
14591460
DecisionResponse<FeatureDecision> decision = decisionList.get(i);
@@ -1492,6 +1493,13 @@ private Map<String, OptimizelyDecision> decideForKeys(@Nonnull OptimizelyUserCon
14921493
return decisionMap;
14931494
}
14941495

1496+
private Map<String, OptimizelyDecision> decideForKeys(@Nonnull OptimizelyUserContext user,
1497+
@Nonnull List<String> keys,
1498+
@Nonnull List<OptimizelyDecideOption> options,
1499+
boolean ignoreDefaultOptions) {
1500+
return decideForKeys(user, keys, options, ignoreDefaultOptions, true);
1501+
}
1502+
14951503
Map<String, OptimizelyDecision> decideAll(@Nonnull OptimizelyUserContext user,
14961504
@Nonnull List<OptimizelyDecideOption> options) {
14971505
Map<String, OptimizelyDecision> decisionMap = new HashMap<>();
@@ -1577,73 +1585,7 @@ private Map<String, OptimizelyDecision> decideForKeysSync(@Nonnull OptimizelyUse
15771585
@Nonnull List<String> keys,
15781586
@Nonnull List<OptimizelyDecideOption> options,
15791587
boolean ignoreDefaultOptions) {
1580-
Map<String, OptimizelyDecision> decisionMap = new HashMap<>();
1581-
1582-
ProjectConfig projectConfig = getProjectConfig();
1583-
if (projectConfig == null) {
1584-
logger.error("Optimizely instance is not valid, failing decideForKeysSync call.");
1585-
return decisionMap;
1586-
}
1587-
1588-
if (keys.isEmpty()) return decisionMap;
1589-
1590-
List<OptimizelyDecideOption> allOptions = ignoreDefaultOptions ? options : getAllOptions(options);
1591-
1592-
Map<String, FeatureDecision> flagDecisions = new HashMap<>();
1593-
Map<String, DecisionReasons> decisionReasonsMap = new HashMap<>();
1594-
1595-
List<FeatureFlag> flagsWithoutForcedDecision = new ArrayList<>();
1596-
1597-
List<String> validKeys = new ArrayList<>();
1598-
1599-
for (String key : keys) {
1600-
FeatureFlag flag = projectConfig.getFeatureKeyMapping().get(key);
1601-
if (flag == null) {
1602-
decisionMap.put(key, OptimizelyDecision.newErrorDecision(key, user, DecisionMessage.FLAG_KEY_INVALID.reason(key)));
1603-
continue;
1604-
}
1605-
1606-
validKeys.add(key);
1607-
1608-
DecisionReasons decisionReasons = DefaultDecisionReasons.newInstance(allOptions);
1609-
decisionReasonsMap.put(key, decisionReasons);
1610-
1611-
OptimizelyDecisionContext optimizelyDecisionContext = new OptimizelyDecisionContext(key, null);
1612-
DecisionResponse<Variation> forcedDecisionVariation = decisionService.validatedForcedDecision(optimizelyDecisionContext, projectConfig, user);
1613-
decisionReasons.merge(forcedDecisionVariation.getReasons());
1614-
if (forcedDecisionVariation.getResult() != null) {
1615-
flagDecisions.put(key,
1616-
new FeatureDecision(null, forcedDecisionVariation.getResult(), FeatureDecision.DecisionSource.FEATURE_TEST));
1617-
} else {
1618-
flagsWithoutForcedDecision.add(flag);
1619-
}
1620-
}
1621-
1622-
// Use DecisionService method that skips CMAB logic
1623-
List<DecisionResponse<FeatureDecision>> decisionList =
1624-
decisionService.getVariationsForFeatureList(flagsWithoutForcedDecision, user, projectConfig, allOptions, false);
1625-
1626-
for (int i = 0; i < flagsWithoutForcedDecision.size(); i++) {
1627-
DecisionResponse<FeatureDecision> decision = decisionList.get(i);
1628-
String flagKey = flagsWithoutForcedDecision.get(i).getKey();
1629-
flagDecisions.put(flagKey, decision.getResult());
1630-
decisionReasonsMap.get(flagKey).merge(decision.getReasons());
1631-
}
1632-
1633-
for (String key : validKeys) {
1634-
FeatureDecision flagDecision = flagDecisions.get(key);
1635-
DecisionReasons decisionReasons = decisionReasonsMap.get((key));
1636-
1637-
OptimizelyDecision optimizelyDecision = createOptimizelyDecision(
1638-
user, key, flagDecision, decisionReasons, allOptions, projectConfig
1639-
);
1640-
1641-
if (!allOptions.contains(OptimizelyDecideOption.ENABLED_FLAGS_ONLY) || optimizelyDecision.getEnabled()) {
1642-
decisionMap.put(key, optimizelyDecision);
1643-
}
1644-
}
1645-
1646-
return decisionMap;
1588+
return decideForKeys(user, keys, options, ignoreDefaultOptions, false);
16471589
}
16481590

16491591

core-api/src/test/java/com/optimizely/ab/OptimizelyTest.java

Lines changed: 121 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -15,25 +15,139 @@
1515
***************************************************************************/
1616
package com.optimizely.ab;
1717

18-
import ch.qos.logback.classic.Level;
18+
import java.io.IOException;
19+
import java.util.Arrays;
20+
import static java.util.Arrays.asList;
21+
import java.util.Collection;
22+
import java.util.Collections;
23+
import java.util.HashMap;
24+
import java.util.List;
25+
import java.util.Map;
26+
import java.util.function.Function;
27+
28+
import static org.hamcrest.CoreMatchers.is;
29+
import static org.hamcrest.MatcherAssert.assertThat;
30+
import static org.junit.Assert.assertEquals;
31+
import static org.junit.Assert.assertFalse;
32+
import static org.junit.Assert.assertNotNull;
33+
import static org.junit.Assert.assertNull;
34+
import static org.junit.Assume.assumeTrue;
35+
import org.junit.Before;
36+
import org.junit.Rule;
37+
import org.junit.Test;
38+
import org.junit.rules.ExpectedException;
39+
import org.junit.rules.RuleChain;
40+
import org.junit.runner.RunWith;
41+
import org.junit.runners.Parameterized;
42+
import org.mockito.ArgumentCaptor;
43+
import static org.mockito.Matchers.any;
44+
import static org.mockito.Matchers.anyMapOf;
45+
import static org.mockito.Matchers.anyString;
46+
import static org.mockito.Matchers.eq;
47+
import static org.mockito.Matchers.isNull;
48+
import org.mockito.Mock;
49+
import org.mockito.Mockito;
50+
import static org.mockito.Mockito.doReturn;
51+
import static org.mockito.Mockito.mock;
52+
import static org.mockito.Mockito.never;
53+
import static org.mockito.Mockito.spy;
54+
import static org.mockito.Mockito.times;
55+
import static org.mockito.Mockito.verify;
56+
import static org.mockito.Mockito.when;
57+
import static org.mockito.Mockito.withSettings;
58+
import org.mockito.junit.MockitoJUnit;
59+
import org.mockito.junit.MockitoRule;
60+
1961
import com.google.common.collect.ImmutableMap;
2062
import com.google.gson.Gson;
2163
import com.google.gson.JsonElement;
2264
import com.google.gson.JsonParser;
2365
import com.optimizely.ab.bucketing.Bucketer;
2466
import com.optimizely.ab.bucketing.DecisionService;
2567
import com.optimizely.ab.bucketing.FeatureDecision;
26-
import com.optimizely.ab.config.*;
68+
import com.optimizely.ab.config.Attribute;
69+
import com.optimizely.ab.config.DatafileProjectConfig;
70+
import static com.optimizely.ab.config.DatafileProjectConfigTestUtils.invalidProjectConfigV5;
71+
import static com.optimizely.ab.config.DatafileProjectConfigTestUtils.noAudienceProjectConfigJsonV2;
72+
import static com.optimizely.ab.config.DatafileProjectConfigTestUtils.noAudienceProjectConfigJsonV3;
73+
import static com.optimizely.ab.config.DatafileProjectConfigTestUtils.validConfigJsonCMAB;
74+
import static com.optimizely.ab.config.DatafileProjectConfigTestUtils.validConfigJsonV2;
75+
import static com.optimizely.ab.config.DatafileProjectConfigTestUtils.validConfigJsonV3;
76+
import static com.optimizely.ab.config.DatafileProjectConfigTestUtils.validConfigJsonV4;
77+
import com.optimizely.ab.config.EventType;
78+
import com.optimizely.ab.config.Experiment;
79+
import com.optimizely.ab.config.FeatureFlag;
80+
import com.optimizely.ab.config.FeatureVariable;
81+
import com.optimizely.ab.config.FeatureVariableUsageInstance;
82+
import com.optimizely.ab.config.ProjectConfig;
83+
import com.optimizely.ab.config.ProjectConfigManager;
84+
import com.optimizely.ab.config.TrafficAllocation;
85+
import static com.optimizely.ab.config.ValidProjectConfigV4.ATTRIBUTE_BOOLEAN_KEY;
86+
import static com.optimizely.ab.config.ValidProjectConfigV4.ATTRIBUTE_DOUBLE_KEY;
87+
import static com.optimizely.ab.config.ValidProjectConfigV4.ATTRIBUTE_HOUSE_KEY;
88+
import static com.optimizely.ab.config.ValidProjectConfigV4.ATTRIBUTE_INTEGER_KEY;
89+
import static com.optimizely.ab.config.ValidProjectConfigV4.AUDIENCE_GRYFFINDOR_VALUE;
90+
import static com.optimizely.ab.config.ValidProjectConfigV4.AUDIENCE_SLYTHERIN_VALUE;
91+
import static com.optimizely.ab.config.ValidProjectConfigV4.EVENT_BASIC_EVENT_KEY;
92+
import static com.optimizely.ab.config.ValidProjectConfigV4.EVENT_LAUNCHED_EXPERIMENT_ONLY_KEY;
93+
import static com.optimizely.ab.config.ValidProjectConfigV4.EXPERIMENT_BASIC_EXPERIMENT_KEY;
94+
import static com.optimizely.ab.config.ValidProjectConfigV4.EXPERIMENT_DOUBLE_FEATURE_EXPERIMENT_KEY;
95+
import static com.optimizely.ab.config.ValidProjectConfigV4.EXPERIMENT_LAUNCHED_EXPERIMENT_KEY;
96+
import static com.optimizely.ab.config.ValidProjectConfigV4.EXPERIMENT_MULTIVARIATE_EXPERIMENT_KEY;
97+
import static com.optimizely.ab.config.ValidProjectConfigV4.EXPERIMENT_PAUSED_EXPERIMENT_KEY;
98+
import static com.optimizely.ab.config.ValidProjectConfigV4.EXPERIMENT_TYPEDAUDIENCE_EXPERIMENT_KEY;
99+
import static com.optimizely.ab.config.ValidProjectConfigV4.EXPERIMENT_TYPEDAUDIENCE_WITH_AND_EXPERIMENT_KEY;
100+
import static com.optimizely.ab.config.ValidProjectConfigV4.FEATURE_FLAG_MULTI_VARIATE_FEATURE;
101+
import static com.optimizely.ab.config.ValidProjectConfigV4.FEATURE_FLAG_SINGLE_VARIABLE_DOUBLE;
102+
import static com.optimizely.ab.config.ValidProjectConfigV4.FEATURE_FLAG_SINGLE_VARIABLE_INTEGER;
103+
import static com.optimizely.ab.config.ValidProjectConfigV4.FEATURE_MULTI_VARIATE_FEATURE_KEY;
104+
import static com.optimizely.ab.config.ValidProjectConfigV4.FEATURE_SINGLE_VARIABLE_BOOLEAN_KEY;
105+
import static com.optimizely.ab.config.ValidProjectConfigV4.FEATURE_SINGLE_VARIABLE_DOUBLE_KEY;
106+
import static com.optimizely.ab.config.ValidProjectConfigV4.FEATURE_SINGLE_VARIABLE_INTEGER_KEY;
107+
import static com.optimizely.ab.config.ValidProjectConfigV4.FEATURE_SINGLE_VARIABLE_STRING_KEY;
108+
import static com.optimizely.ab.config.ValidProjectConfigV4.MULTIVARIATE_EXPERIMENT_FORCED_VARIATION_USER_ID_GRED;
109+
import static com.optimizely.ab.config.ValidProjectConfigV4.PAUSED_EXPERIMENT_FORCED_VARIATION_USER_ID_CONTROL;
110+
import static com.optimizely.ab.config.ValidProjectConfigV4.ROLLOUT_2_ID;
111+
import static com.optimizely.ab.config.ValidProjectConfigV4.VARIABLE_BOOLEAN_VARIABLE_DEFAULT_VALUE;
112+
import static com.optimizely.ab.config.ValidProjectConfigV4.VARIABLE_BOOLEAN_VARIABLE_KEY;
113+
import static com.optimizely.ab.config.ValidProjectConfigV4.VARIABLE_DOUBLE_DEFAULT_VALUE;
114+
import static com.optimizely.ab.config.ValidProjectConfigV4.VARIABLE_DOUBLE_VARIABLE_KEY;
115+
import static com.optimizely.ab.config.ValidProjectConfigV4.VARIABLE_FIRST_LETTER_DEFAULT_VALUE;
116+
import static com.optimizely.ab.config.ValidProjectConfigV4.VARIABLE_FIRST_LETTER_KEY;
117+
import static com.optimizely.ab.config.ValidProjectConfigV4.VARIABLE_INTEGER_VARIABLE_KEY;
118+
import static com.optimizely.ab.config.ValidProjectConfigV4.VARIABLE_JSON_PATCHED_TYPE_KEY;
119+
import static com.optimizely.ab.config.ValidProjectConfigV4.VARIABLE_STRING_VARIABLE_KEY;
120+
import static com.optimizely.ab.config.ValidProjectConfigV4.VARIATION_MULTIVARIATE_EXPERIMENT_GRED;
121+
import static com.optimizely.ab.config.ValidProjectConfigV4.VARIATION_MULTIVARIATE_EXPERIMENT_GRED_KEY;
122+
import com.optimizely.ab.config.Variation;
27123
import com.optimizely.ab.error.NoOpErrorHandler;
28124
import com.optimizely.ab.error.RaiseExceptionErrorHandler;
29125
import com.optimizely.ab.event.BatchEventProcessor;
30126
import com.optimizely.ab.event.EventHandler;
31127
import com.optimizely.ab.event.EventProcessor;
32128
import com.optimizely.ab.event.LogEvent;
129+
import com.optimizely.ab.event.LogEvent.RequestMethod;
33130
import com.optimizely.ab.event.internal.UserEventFactory;
34131
import com.optimizely.ab.internal.ControlAttribute;
35132
import com.optimizely.ab.internal.LogbackVerifier;
36-
import com.optimizely.ab.notification.*;
133+
import com.optimizely.ab.notification.ActivateNotification;
134+
import com.optimizely.ab.notification.ActivateNotificationListener;
135+
import com.optimizely.ab.notification.DecisionNotification;
136+
import static com.optimizely.ab.notification.DecisionNotification.ExperimentDecisionNotificationBuilder.EXPERIMENT_KEY;
137+
import static com.optimizely.ab.notification.DecisionNotification.ExperimentDecisionNotificationBuilder.VARIATION_KEY;
138+
import static com.optimizely.ab.notification.DecisionNotification.FeatureVariableDecisionNotificationBuilder.FEATURE_ENABLED;
139+
import static com.optimizely.ab.notification.DecisionNotification.FeatureVariableDecisionNotificationBuilder.FEATURE_KEY;
140+
import static com.optimizely.ab.notification.DecisionNotification.FeatureVariableDecisionNotificationBuilder.SOURCE;
141+
import static com.optimizely.ab.notification.DecisionNotification.FeatureVariableDecisionNotificationBuilder.SOURCE_INFO;
142+
import static com.optimizely.ab.notification.DecisionNotification.FeatureVariableDecisionNotificationBuilder.VARIABLE_KEY;
143+
import static com.optimizely.ab.notification.DecisionNotification.FeatureVariableDecisionNotificationBuilder.VARIABLE_TYPE;
144+
import static com.optimizely.ab.notification.DecisionNotification.FeatureVariableDecisionNotificationBuilder.VARIABLE_VALUE;
145+
import static com.optimizely.ab.notification.DecisionNotification.FeatureVariableDecisionNotificationBuilder.VARIABLE_VALUES;
146+
import com.optimizely.ab.notification.NotificationCenter;
147+
import com.optimizely.ab.notification.NotificationHandler;
148+
import com.optimizely.ab.notification.NotificationManager;
149+
import com.optimizely.ab.notification.TrackNotification;
150+
import com.optimizely.ab.notification.UpdateConfigNotification;
37151
import com.optimizely.ab.odp.ODPEvent;
38152
import com.optimizely.ab.odp.ODPEventManager;
39153
import com.optimizely.ab.odp.ODPManager;
@@ -42,38 +156,10 @@
42156
import com.optimizely.ab.optimizelydecision.DefaultDecisionReasons;
43157
import com.optimizely.ab.optimizelydecision.OptimizelyDecision;
44158
import com.optimizely.ab.optimizelyjson.OptimizelyJSON;
45-
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
46-
import org.junit.Before;
47-
import org.junit.Rule;
48-
import org.junit.Test;
49-
import org.junit.rules.ExpectedException;
50-
import org.junit.rules.RuleChain;
51-
import org.junit.runner.RunWith;
52-
import org.junit.runners.Parameterized;
53-
import org.mockito.ArgumentCaptor;
54-
import org.mockito.Mock;
55-
import org.mockito.Mockito;
56-
import org.mockito.junit.MockitoJUnit;
57-
import org.mockito.junit.MockitoRule;
58159

59-
import java.io.IOException;
60-
import java.util.*;
61-
import java.util.function.Function;
62-
63-
import static com.optimizely.ab.config.DatafileProjectConfigTestUtils.*;
64-
import static com.optimizely.ab.config.ValidProjectConfigV4.*;
65-
import static com.optimizely.ab.event.LogEvent.RequestMethod;
66-
import static com.optimizely.ab.notification.DecisionNotification.ExperimentDecisionNotificationBuilder.EXPERIMENT_KEY;
67-
import static com.optimizely.ab.notification.DecisionNotification.ExperimentDecisionNotificationBuilder.VARIATION_KEY;
68-
import static com.optimizely.ab.notification.DecisionNotification.FeatureVariableDecisionNotificationBuilder.*;
69-
import static java.util.Arrays.asList;
160+
import ch.qos.logback.classic.Level;
161+
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
70162
import static junit.framework.TestCase.assertTrue;
71-
import static org.hamcrest.CoreMatchers.is;
72-
import static org.hamcrest.MatcherAssert.assertThat;
73-
import static org.junit.Assert.*;
74-
import static org.junit.Assume.assumeTrue;
75-
import static org.mockito.Matchers.*;
76-
import static org.mockito.Mockito.*;
77163

78164
/**
79165
* Tests for the top-level {@link Optimizely} class.
@@ -5030,7 +5116,8 @@ public void testDecideReturnsErrorDecisionWhenDecisionServiceFails() throws Exce
50305116
any(List.class),
50315117
any(OptimizelyUserContext.class),
50325118
any(ProjectConfig.class),
5033-
any(List.class)
5119+
any(List.class),
5120+
eq(true)
50345121
)).thenReturn(Arrays.asList(errorDecisionResponse));
50355122

50365123

0 commit comments

Comments
 (0)