use of com.optimizely.ab.optimizelydecision.DecisionReasons in project java-sdk by optimizely.
the class Bucketer method bucket.
/**
* Assign a {@link Variation} of an {@link Experiment} to a user based on hashed value from murmurhash3.
*
* @param experiment The Experiment in which the user is to be bucketed.
* @param bucketingId string A customer-assigned value used to create the key for the murmur hash.
* @param projectConfig The current projectConfig
* @return A {@link DecisionResponse} including the {@link Variation} that user is bucketed into (or null) and the decision reasons
*/
@Nonnull
public DecisionResponse<Variation> bucket(@Nonnull Experiment experiment, @Nonnull String bucketingId, @Nonnull ProjectConfig projectConfig) {
DecisionReasons reasons = DefaultDecisionReasons.newInstance();
// ---------- Bucket User ----------
String groupId = experiment.getGroupId();
// check whether the experiment belongs to a group
if (!groupId.isEmpty()) {
Group experimentGroup = projectConfig.getGroupIdMapping().get(groupId);
// bucket to an experiment only if group entities are to be mutually exclusive
if (experimentGroup.getPolicy().equals(Group.RANDOM_POLICY)) {
Experiment bucketedExperiment = bucketToExperiment(experimentGroup, bucketingId, projectConfig);
if (bucketedExperiment == null) {
String message = reasons.addInfo("User with bucketingId \"%s\" is not in any experiment of group %s.", bucketingId, experimentGroup.getId());
logger.info(message);
return new DecisionResponse(null, reasons);
} else {
}
// don't perform further bucketing within the experiment
if (!bucketedExperiment.getId().equals(experiment.getId())) {
String message = reasons.addInfo("User with bucketingId \"%s\" is not in experiment \"%s\" of group %s.", bucketingId, experiment.getKey(), experimentGroup.getId());
logger.info(message);
return new DecisionResponse(null, reasons);
}
String message = reasons.addInfo("User with bucketingId \"%s\" is in experiment \"%s\" of group %s.", bucketingId, experiment.getKey(), experimentGroup.getId());
logger.info(message);
}
}
DecisionResponse<Variation> decisionResponse = bucketToVariation(experiment, bucketingId);
reasons.merge(decisionResponse.getReasons());
return new DecisionResponse<>(decisionResponse.getResult(), reasons);
}
use of com.optimizely.ab.optimizelydecision.DecisionReasons in project java-sdk by optimizely.
the class DecisionService method getVariationFromDeliveryRule.
/**
* @param projectConfig The Project config
* @param flagKey The flag key for the feature flag
* @param rules The experiments belonging to a rollout
* @param ruleIndex The index of the rule
* @param user The OptimizelyUserContext
* @return Returns a DecisionResponse Object containing a AbstractMap.SimpleEntry<Variation, Boolean>
* where the Variation is the result and the Boolean is the skipToEveryoneElse.
*/
DecisionResponse<AbstractMap.SimpleEntry> getVariationFromDeliveryRule(@Nonnull ProjectConfig projectConfig, @Nonnull String flagKey, @Nonnull List<Experiment> rules, @Nonnull int ruleIndex, @Nonnull OptimizelyUserContext user) {
DecisionReasons reasons = DefaultDecisionReasons.newInstance();
Boolean skipToEveryoneElse = false;
AbstractMap.SimpleEntry<Variation, Boolean> variationToSkipToEveryoneElsePair;
// Check forced-decisions first
Experiment rule = rules.get(ruleIndex);
OptimizelyDecisionContext optimizelyDecisionContext = new OptimizelyDecisionContext(flagKey, rule.getKey());
DecisionResponse<Variation> forcedDecisionResponse = validatedForcedDecision(optimizelyDecisionContext, projectConfig, user);
reasons.merge(forcedDecisionResponse.getReasons());
Variation variation = forcedDecisionResponse.getResult();
if (variation != null) {
variationToSkipToEveryoneElsePair = new AbstractMap.SimpleEntry<>(variation, false);
return new DecisionResponse(variationToSkipToEveryoneElsePair, reasons);
}
// Handle a regular decision
String bucketingId = getBucketingId(user.getUserId(), user.getAttributes());
Boolean everyoneElse = (ruleIndex == rules.size() - 1);
String loggingKey = everyoneElse ? "Everyone Else" : String.valueOf(ruleIndex + 1);
Variation bucketedVariation = null;
DecisionResponse<Boolean> audienceDecisionResponse = ExperimentUtils.doesUserMeetAudienceConditions(projectConfig, rule, user.getAttributes(), RULE, String.valueOf(ruleIndex + 1));
reasons.merge(audienceDecisionResponse.getReasons());
String message;
if (audienceDecisionResponse.getResult()) {
message = reasons.addInfo("User \"%s\" meets conditions for targeting rule \"%s\".", user.getUserId(), loggingKey);
reasons.addInfo(message);
logger.debug(message);
DecisionResponse<Variation> decisionResponse = bucketer.bucket(rule, bucketingId, projectConfig);
reasons.merge(decisionResponse.getReasons());
bucketedVariation = decisionResponse.getResult();
if (bucketedVariation != null) {
message = reasons.addInfo("User \"%s\" bucketed for targeting rule \"%s\".", user.getUserId(), loggingKey);
logger.debug(message);
reasons.addInfo(message);
} else if (!everyoneElse) {
message = reasons.addInfo("User \"%s\" is not bucketed for targeting rule \"%s\".", user.getUserId(), loggingKey);
logger.debug(message);
reasons.addInfo(message);
// Skip the rest of rollout rules to the everyone-else rule if audience matches but not bucketed.
skipToEveryoneElse = true;
}
} else {
message = reasons.addInfo("User \"%s\" does not meet conditions for targeting rule \"%d\".", user.getUserId(), ruleIndex + 1);
reasons.addInfo(message);
logger.debug(message);
}
variationToSkipToEveryoneElsePair = new AbstractMap.SimpleEntry<>(bucketedVariation, skipToEveryoneElse);
return new DecisionResponse(variationToSkipToEveryoneElsePair, reasons);
}
use of com.optimizely.ab.optimizelydecision.DecisionReasons in project java-sdk by optimizely.
the class DecisionService method getVariationFromExperimentRule.
public DecisionResponse<Variation> getVariationFromExperimentRule(@Nonnull ProjectConfig projectConfig, @Nonnull String flagKey, @Nonnull Experiment rule, @Nonnull OptimizelyUserContext user, @Nonnull List<OptimizelyDecideOption> options) {
DecisionReasons reasons = DefaultDecisionReasons.newInstance();
String ruleKey = rule != null ? rule.getKey() : null;
// Check Forced-Decision
OptimizelyDecisionContext optimizelyDecisionContext = new OptimizelyDecisionContext(flagKey, ruleKey);
DecisionResponse<Variation> forcedDecisionResponse = validatedForcedDecision(optimizelyDecisionContext, projectConfig, user);
reasons.merge(forcedDecisionResponse.getReasons());
Variation variation = forcedDecisionResponse.getResult();
if (variation != null) {
return new DecisionResponse(variation, reasons);
}
// regular decision
DecisionResponse<Variation> decisionResponse = getVariation(rule, user, projectConfig, options);
reasons.merge(decisionResponse.getReasons());
variation = decisionResponse.getResult();
return new DecisionResponse(variation, reasons);
}
use of com.optimizely.ab.optimizelydecision.DecisionReasons in project java-sdk by optimizely.
the class DecisionService method validatedForcedDecision.
/**
* Find a validated forced decision
*
* @param optimizelyDecisionContext The OptimizelyDecisionContext containing flagKey and ruleKey
* @param projectConfig The Project config
* @param user The OptimizelyUserContext
* @return Returns a DecisionResponse structure of type Variation, otherwise null result with reasons
*/
public DecisionResponse<Variation> validatedForcedDecision(@Nonnull OptimizelyDecisionContext optimizelyDecisionContext, @Nonnull ProjectConfig projectConfig, @Nonnull OptimizelyUserContext user) {
DecisionReasons reasons = DefaultDecisionReasons.newInstance();
String userId = user.getUserId();
OptimizelyForcedDecision optimizelyForcedDecision = user.findForcedDecision(optimizelyDecisionContext);
String variationKey = optimizelyForcedDecision != null ? optimizelyForcedDecision.getVariationKey() : null;
if (projectConfig != null && variationKey != null) {
Variation variation = projectConfig.getFlagVariationByKey(optimizelyDecisionContext.getFlagKey(), variationKey);
String ruleKey = optimizelyDecisionContext.getRuleKey();
String flagKey = optimizelyDecisionContext.getFlagKey();
String info;
String target = ruleKey != OptimizelyDecisionContext.OPTI_NULL_RULE_KEY ? String.format("flag (%s), rule (%s)", flagKey, ruleKey) : String.format("flag (%s)", flagKey);
if (variation != null) {
info = String.format("Variation (%s) is mapped to %s and user (%s) in the forced decision map.", variationKey, target, userId);
logger.debug(info);
reasons.addInfo(info);
return new DecisionResponse(variation, reasons);
} else {
info = String.format("Invalid variation is mapped to %s and user (%s) in the forced decision map.", target, userId);
logger.debug(info);
reasons.addInfo(info);
}
}
return new DecisionResponse<>(null, reasons);
}
use of com.optimizely.ab.optimizelydecision.DecisionReasons in project java-sdk by optimizely.
the class DecisionService method getStoredVariation.
/**
* Get the {@link Variation} that has been stored for the user in the {@link UserProfileService} implementation.
*
* @param experiment {@link Experiment} in which the user was bucketed.
* @param userProfile {@link UserProfile} of the user.
* @param projectConfig The current projectConfig
* @return A {@link DecisionResponse} including the {@link Variation} that user was previously bucketed into (or null)
* and the decision reasons. The variation can be null if the {@link UserProfileService} implementation is null or the user was not previously bucketed.
*/
@Nonnull
DecisionResponse<Variation> getStoredVariation(@Nonnull Experiment experiment, @Nonnull UserProfile userProfile, @Nonnull ProjectConfig projectConfig) {
DecisionReasons reasons = DefaultDecisionReasons.newInstance();
// ---------- Check User Profile for Sticky Bucketing ----------
// If a user profile instance is present then check it for a saved variation
String experimentId = experiment.getId();
String experimentKey = experiment.getKey();
Decision decision = userProfile.experimentBucketMap.get(experimentId);
if (decision != null) {
String variationId = decision.variationId;
Variation savedVariation = projectConfig.getExperimentIdMapping().get(experimentId).getVariationIdToVariationMap().get(variationId);
if (savedVariation != null) {
String message = reasons.addInfo("Returning previously activated variation \"%s\" of experiment \"%s\" for user \"%s\" from user profile.", savedVariation.getKey(), experimentKey, userProfile.userId);
logger.info(message);
// A variation is stored for this combined bucket id
return new DecisionResponse(savedVariation, reasons);
} else {
String message = reasons.addInfo("User \"%s\" was previously bucketed into variation with ID \"%s\" for experiment \"%s\", but no matching variation was found for that user. We will re-bucket the user.", userProfile.userId, variationId, experimentKey);
logger.info(message);
return new DecisionResponse(null, reasons);
}
} else {
String message = reasons.addInfo("No previously activated variation of experiment \"%s\" for user \"%s\" found in user profile.", experimentKey, userProfile.userId);
logger.info(message);
return new DecisionResponse(null, reasons);
}
}
Aggregations