Skip to content

Commit 5e62fd6

Browse files
Merge branch 'master' into farhan-anjum/FSSDK-11179-update-impression-event
2 parents c4c07a6 + efbda89 commit 5e62fd6

File tree

14 files changed

+206
-68
lines changed

14 files changed

+206
-68
lines changed

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

Lines changed: 132 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,7 @@
1717

1818
import com.optimizely.ab.annotations.VisibleForTesting;
1919
import com.optimizely.ab.bucketing.*;
20-
import com.optimizely.ab.cmab.service.CmabCacheValue;
2120
import com.optimizely.ab.cmab.service.CmabService;
22-
import com.optimizely.ab.cmab.service.DefaultCmabService;
2321
import com.optimizely.ab.config.AtomicProjectConfigManager;
2422
import com.optimizely.ab.config.DatafileProjectConfig;
2523
import com.optimizely.ab.config.EventType;
@@ -156,7 +154,7 @@ private Optimizely(@Nonnull EventHandler eventHandler,
156154
@Nonnull List<OptimizelyDecideOption> defaultDecideOptions,
157155
@Nullable ODPManager odpManager,
158156
@Nonnull CmabService cmabService
159-
) {
157+
) {
160158
this.eventHandler = eventHandler;
161159
this.eventProcessor = eventProcessor;
162160
this.errorHandler = errorHandler;
@@ -355,14 +353,14 @@ private boolean sendImpression(@Nonnull ProjectConfig projectConfig,
355353

356354
// Legacy API methods only apply to the Experiment type and not to Holdout.
357355
boolean isExperimentType = experiment instanceof Experiment;
358-
356+
359357
// Kept For backwards compatibility.
360358
// This notification is deprecated and the new DecisionNotifications
361359
// are sent via their respective method calls.
362360
if (notificationCenter.getNotificationManager(ActivateNotification.class).size() > 0 && isExperimentType) {
363361
LogEvent impressionEvent = EventFactory.createLogEvent(userEvent);
364362
ActivateNotification activateNotification = new ActivateNotification(
365-
(Experiment)experiment, userId, filteredAttributes, variation, impressionEvent);
363+
(Experiment) experiment, userId, filteredAttributes, variation, impressionEvent);
366364
notificationCenter.send(activateNotification);
367365
}
368366
return true;
@@ -1293,20 +1291,6 @@ private OptimizelyUserContext createUserContextCopy(@Nonnull String userId, @Non
12931291
return new OptimizelyUserContext(this, userId, attributes, Collections.EMPTY_MAP, null, false);
12941292
}
12951293

1296-
OptimizelyDecision decide(@Nonnull OptimizelyUserContext user,
1297-
@Nonnull String key,
1298-
@Nonnull List<OptimizelyDecideOption> options) {
1299-
ProjectConfig projectConfig = getProjectConfig();
1300-
if (projectConfig == null) {
1301-
return OptimizelyDecision.newErrorDecision(key, user, DecisionMessage.SDK_NOT_READY.reason());
1302-
}
1303-
1304-
List<OptimizelyDecideOption> allOptions = getAllOptions(options);
1305-
allOptions.remove(OptimizelyDecideOption.ENABLED_FLAGS_ONLY);
1306-
1307-
return decideForKeys(user, Arrays.asList(key), allOptions, true).get(key);
1308-
}
1309-
13101294
private OptimizelyDecision createOptimizelyDecision(
13111295
OptimizelyUserContext user,
13121296
String flagKey,
@@ -1397,17 +1381,72 @@ private OptimizelyDecision createOptimizelyDecision(
13971381
reasonsToReport);
13981382
}
13991383

1384+
OptimizelyDecision decide(@Nonnull OptimizelyUserContext user,
1385+
@Nonnull String key,
1386+
@Nonnull List<OptimizelyDecideOption> options) {
1387+
return decideInternal(user, key, options, DecisionPath.WITH_CMAB);
1388+
}
1389+
14001390
Map<String, OptimizelyDecision> decideForKeys(@Nonnull OptimizelyUserContext user,
14011391
@Nonnull List<String> keys,
14021392
@Nonnull List<OptimizelyDecideOption> options) {
1403-
return decideForKeys(user, keys, options, false);
1393+
return decideForKeysInternal(user, keys, options, false, DecisionPath.WITH_CMAB);
1394+
}
1395+
1396+
Map<String, OptimizelyDecision> decideAll(@Nonnull OptimizelyUserContext user,
1397+
@Nonnull List<OptimizelyDecideOption> options) {
1398+
return decideAllInternal(user, options, DecisionPath.WITH_CMAB);
1399+
}
1400+
1401+
/**
1402+
* Returns a decision result ({@link OptimizelyDecision}) for a given flag key and a user context,
1403+
* skipping CMAB logic and using only traditional A/B testing.
1404+
* This will be called by mobile apps which will make synchronous decisions only (for backward compatibility with android-sdk)
1405+
*
1406+
* @param user An OptimizelyUserContext associated with this OptimizelyClient.
1407+
* @param key A flag key for which a decision will be made.
1408+
* @param options A list of options for decision-making.
1409+
* @return A decision result using traditional A/B testing logic only.
1410+
*/
1411+
OptimizelyDecision decideSync(@Nonnull OptimizelyUserContext user,
1412+
@Nonnull String key,
1413+
@Nonnull List<OptimizelyDecideOption> options) {
1414+
return decideInternal(user, key, options, DecisionPath.WITHOUT_CMAB);
1415+
}
1416+
1417+
/**
1418+
* Returns decision results for multiple flag keys, skipping CMAB logic and using only traditional A/B testing.
1419+
* This will be called by mobile apps which will make synchronous decisions only (for backward compatibility with android-sdk)
1420+
*
1421+
* @param user An OptimizelyUserContext associated with this OptimizelyClient.
1422+
* @param keys A list of flag keys for which decisions will be made.
1423+
* @param options A list of options for decision-making.
1424+
* @return All decision results mapped by flag keys, using traditional A/B testing logic only.
1425+
*/
1426+
Map<String, OptimizelyDecision> decideForKeysSync(@Nonnull OptimizelyUserContext user,
1427+
@Nonnull List<String> keys,
1428+
@Nonnull List<OptimizelyDecideOption> options) {
1429+
return decideForKeysInternal(user, keys, options, false, DecisionPath.WITHOUT_CMAB);
1430+
}
1431+
1432+
/**
1433+
* Returns decision results for all active flag keys, skipping CMAB logic and using only traditional A/B testing.
1434+
* This will be called by mobile apps which will make synchronous decisions only (for backward compatibility with android-sdk)
1435+
*
1436+
* @param user An OptimizelyUserContext associated with this OptimizelyClient.
1437+
* @param options A list of options for decision-making.
1438+
* @return All decision results mapped by flag keys, using traditional A/B testing logic only.
1439+
*/
1440+
Map<String, OptimizelyDecision> decideAllSync(@Nonnull OptimizelyUserContext user,
1441+
@Nonnull List<OptimizelyDecideOption> options) {
1442+
return decideAllInternal(user, options, DecisionPath.WITHOUT_CMAB);
14041443
}
14051444

1406-
private Map<String, OptimizelyDecision> decideForKeys(@Nonnull OptimizelyUserContext user,
1407-
@Nonnull List<String> keys,
1408-
@Nonnull List<OptimizelyDecideOption> options,
1409-
boolean ignoreDefaultOptions,
1410-
DecisionPath decisionPath) {
1445+
private Map<String, OptimizelyDecision> decideForKeysInternal(@Nonnull OptimizelyUserContext user,
1446+
@Nonnull List<String> keys,
1447+
@Nonnull List<OptimizelyDecideOption> options,
1448+
boolean ignoreDefaultOptions,
1449+
DecisionPath decisionPath) {
14111450
Map<String, OptimizelyDecision> decisionMap = new HashMap<>();
14121451

14131452
ProjectConfig projectConfig = getProjectConfig();
@@ -1491,28 +1530,85 @@ private Map<String, OptimizelyDecision> decideForKeys(@Nonnull OptimizelyUserCon
14911530
return decisionMap;
14921531
}
14931532

1494-
private Map<String, OptimizelyDecision> decideForKeys(@Nonnull OptimizelyUserContext user,
1495-
@Nonnull List<String> keys,
1496-
@Nonnull List<OptimizelyDecideOption> options,
1497-
boolean ignoreDefaultOptions) {
1498-
return decideForKeys(user, keys, options, ignoreDefaultOptions, DecisionPath.WITH_CMAB);
1499-
}
1500-
1501-
Map<String, OptimizelyDecision> decideAll(@Nonnull OptimizelyUserContext user,
1502-
@Nonnull List<OptimizelyDecideOption> options) {
1533+
private Map<String, OptimizelyDecision> decideAllInternal(@Nonnull OptimizelyUserContext user,
1534+
@Nonnull List<OptimizelyDecideOption> options,
1535+
@Nonnull DecisionPath decisionPath) {
15031536
Map<String, OptimizelyDecision> decisionMap = new HashMap<>();
15041537

15051538
ProjectConfig projectConfig = getProjectConfig();
15061539
if (projectConfig == null) {
1507-
logger.error("Optimizely instance is not valid, failing isFeatureEnabled call.");
1540+
logger.error("Optimizely instance is not valid, failing decideAllSync call.");
15081541
return decisionMap;
15091542
}
15101543

15111544
List<FeatureFlag> allFlags = projectConfig.getFeatureFlags();
15121545
List<String> allFlagKeys = new ArrayList<>();
15131546
for (int i = 0; i < allFlags.size(); i++) allFlagKeys.add(allFlags.get(i).getKey());
15141547

1515-
return decideForKeys(user, allFlagKeys, options);
1548+
return decideForKeysInternal(user, allFlagKeys, options, false, decisionPath);
1549+
}
1550+
1551+
private OptimizelyDecision decideInternal(@Nonnull OptimizelyUserContext user,
1552+
@Nonnull String key,
1553+
@Nonnull List<OptimizelyDecideOption> options,
1554+
@Nonnull DecisionPath decisionPath) {
1555+
ProjectConfig projectConfig = getProjectConfig();
1556+
if (projectConfig == null) {
1557+
return OptimizelyDecision.newErrorDecision(key, user, DecisionMessage.SDK_NOT_READY.reason());
1558+
}
1559+
1560+
List<OptimizelyDecideOption> allOptions = getAllOptions(options);
1561+
allOptions.remove(OptimizelyDecideOption.ENABLED_FLAGS_ONLY);
1562+
1563+
return decideForKeysInternal(user, Arrays.asList(key), allOptions, true, decisionPath).get(key);
1564+
}
1565+
1566+
//============ decide async ============//
1567+
1568+
/**
1569+
* Returns a decision result asynchronously for a given flag key and a user context.
1570+
*
1571+
* @param userContext The user context to make decisions for
1572+
* @param key A flag key for which a decision will be made
1573+
* @param callback A callback to invoke when the decision is available
1574+
* @param options A list of options for decision-making
1575+
*/
1576+
void decideAsync(@Nonnull OptimizelyUserContext userContext,
1577+
@Nonnull String key,
1578+
@Nonnull List<OptimizelyDecideOption> options,
1579+
@Nonnull OptimizelyDecisionCallback callback) {
1580+
AsyncDecisionFetcher fetcher = new AsyncDecisionFetcher(userContext, key, options, callback);
1581+
fetcher.start();
1582+
}
1583+
1584+
/**
1585+
* Returns decision results asynchronously for multiple flag keys.
1586+
*
1587+
* @param userContext The user context to make decisions for
1588+
* @param keys A list of flag keys for which decisions will be made
1589+
* @param callback A callback to invoke when decisions are available
1590+
* @param options A list of options for decision-making
1591+
*/
1592+
void decideForKeysAsync(@Nonnull OptimizelyUserContext userContext,
1593+
@Nonnull List<String> keys,
1594+
@Nonnull List<OptimizelyDecideOption> options,
1595+
@Nonnull OptimizelyDecisionsCallback callback) {
1596+
AsyncDecisionFetcher fetcher = new AsyncDecisionFetcher(userContext, keys, options, callback);
1597+
fetcher.start();
1598+
}
1599+
1600+
/**
1601+
* Returns decision results asynchronously for all active flag keys.
1602+
*
1603+
* @param userContext The user context to make decisions for
1604+
* @param callback A callback to invoke when decisions are available
1605+
* @param options A list of options for decision-making
1606+
*/
1607+
void decideAllAsync(@Nonnull OptimizelyUserContext userContext,
1608+
@Nonnull List<OptimizelyDecideOption> options,
1609+
@Nonnull OptimizelyDecisionsCallback callback) {
1610+
AsyncDecisionFetcher fetcher = new AsyncDecisionFetcher(userContext, options, callback);
1611+
fetcher.start();
15161612
}
15171613

15181614
/**

core-api/src/main/java/com/optimizely/ab/bucketing/DecisionService.java

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,19 @@ public class DecisionService {
7878
*/
7979
private transient ConcurrentHashMap<String, ConcurrentHashMap<String, String>> forcedVariationMapping = new ConcurrentHashMap<String, ConcurrentHashMap<String, String>>();
8080

81+
/**
82+
* Initialize a decision service for the Optimizely client.
83+
*
84+
* @param bucketer Base bucketer to allocate new users to an experiment.
85+
* @param errorHandler The error handler of the Optimizely client.
86+
* @param userProfileService UserProfileService implementation for storing user info.
87+
* @param cmabService Cmab Service for decision making.
88+
*/
89+
public DecisionService(@Nonnull Bucketer bucketer,
90+
@Nonnull ErrorHandler errorHandler,
91+
@Nullable UserProfileService userProfileService) {
92+
this(bucketer, errorHandler, userProfileService, null);
93+
}
8194

8295
/**
8396
* Initialize a decision service for the Optimizely client.
@@ -90,7 +103,7 @@ public class DecisionService {
90103
public DecisionService(@Nonnull Bucketer bucketer,
91104
@Nonnull ErrorHandler errorHandler,
92105
@Nullable UserProfileService userProfileService,
93-
@Nonnull CmabService cmabService) {
106+
@Nullable CmabService cmabService) {
94107
this.bucketer = bucketer;
95108
this.errorHandler = errorHandler;
96109
this.userProfileService = userProfileService;

core-api/src/main/java/com/optimizely/ab/cmab/client/CmabClientConfig.java

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,16 +23,31 @@
2323
*/
2424
public class CmabClientConfig {
2525
private final RetryConfig retryConfig;
26+
private String cmabEndpoint = null;
2627

2728
public CmabClientConfig(@Nullable RetryConfig retryConfig) {
2829
this.retryConfig = retryConfig;
2930
}
3031

32+
public CmabClientConfig(@Nullable RetryConfig retryConfig, @Nullable String cmabEndpoint) {
33+
this.retryConfig = retryConfig;
34+
this.cmabEndpoint = cmabEndpoint;
35+
}
36+
3137
@Nullable
3238
public RetryConfig getRetryConfig() {
3339
return retryConfig;
3440
}
3541

42+
@Nullable
43+
public String getCmabEndpoint() {
44+
return cmabEndpoint;
45+
}
46+
47+
public void setCmabEndpoint(@Nullable String cmabEndpoint){
48+
this.cmabEndpoint = cmabEndpoint;
49+
}
50+
3651
/**
3752
* Creates a config with default retry settings.
3853
*
@@ -50,4 +65,13 @@ public static CmabClientConfig withDefaultRetry() {
5065
public static CmabClientConfig withNoRetry() {
5166
return new CmabClientConfig(null);
5267
}
53-
}
68+
69+
/**
70+
* Creates a config with custom cmab endpoint.
71+
*
72+
* @return A cmab client config with custom cmab endpoint
73+
*/
74+
public static CmabClientConfig withCmabEndpoint(@Nullable String cmabEndpoint) {
75+
return new CmabClientConfig(RetryConfig.defaultConfig(), cmabEndpoint);
76+
}
77+
}

core-api/src/main/java/com/optimizely/ab/cmab/client/RetryConfig.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ public RetryConfig(int maxRetries) {
6161
}
6262

6363
/**
64-
* Creates a default RetryConfig with 3 retries and exponential backoff.
64+
* Creates a default RetryConfig with 1 retry and exponential backoff.
6565
*
6666
* @return Retry config with default settings
6767
*/

core-api/src/main/java/com/optimizely/ab/cmab/service/CmabCacheValue.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,4 +63,4 @@ public boolean equals(Object o) {
6363
public int hashCode() {
6464
return Objects.hash(attributesHash, variationId, cmabUUID);
6565
}
66-
}
66+
}

core-api/src/main/java/com/optimizely/ab/cmab/service/CmabDecision.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,4 +55,4 @@ public boolean equals(Object o) {
5555
public int hashCode() {
5656
return Objects.hash(variationId, cmabUUID);
5757
}
58-
}
58+
}

0 commit comments

Comments
 (0)