use of com.optimizely.ab.optimizelydecision.DecisionReasons in project java-sdk by optimizely.
the class DecisionService method getVariationFromExperiment.
/**
* @param projectConfig The ProjectConfig.
* @param featureFlag The feature flag the user wants to access.
* @param user The current OptimizelyUserContext.
* @param options An array of decision options
* @return A {@link DecisionResponse} including a {@link FeatureDecision} and the decision reasons
*/
@Nonnull
DecisionResponse<FeatureDecision> getVariationFromExperiment(@Nonnull ProjectConfig projectConfig, @Nonnull FeatureFlag featureFlag, @Nonnull OptimizelyUserContext user, @Nonnull List<OptimizelyDecideOption> options) {
DecisionReasons reasons = DefaultDecisionReasons.newInstance();
if (!featureFlag.getExperimentIds().isEmpty()) {
for (String experimentId : featureFlag.getExperimentIds()) {
Experiment experiment = projectConfig.getExperimentIdMapping().get(experimentId);
DecisionResponse<Variation> decisionVariation = getVariationFromExperimentRule(projectConfig, featureFlag.getKey(), experiment, user, options);
reasons.merge(decisionVariation.getReasons());
Variation variation = decisionVariation.getResult();
if (variation != null) {
return new DecisionResponse(new FeatureDecision(experiment, variation, FeatureDecision.DecisionSource.FEATURE_TEST), reasons);
}
}
} else {
String message = reasons.addInfo("The feature flag \"%s\" is not used in any experiments.", featureFlag.getKey());
logger.info(message);
}
return new DecisionResponse(null, reasons);
}
use of com.optimizely.ab.optimizelydecision.DecisionReasons in project java-sdk by optimizely.
the class DecisionService method getVariation.
/**
* Get a {@link Variation} of an {@link Experiment} for a user to be allocated into.
*
* @param experiment The Experiment the user will be bucketed into.
* @param user The current OptimizelyUserContext
* @param projectConfig The current projectConfig
* @param options An array of decision options
* @return A {@link DecisionResponse} including the {@link Variation} that user is bucketed into (or null) and the decision reasons
*/
@Nonnull
public DecisionResponse<Variation> getVariation(@Nonnull Experiment experiment, @Nonnull OptimizelyUserContext user, @Nonnull ProjectConfig projectConfig, @Nonnull List<OptimizelyDecideOption> options) {
DecisionReasons reasons = DefaultDecisionReasons.newInstance();
if (!ExperimentUtils.isExperimentActive(experiment)) {
String message = reasons.addInfo("Experiment \"%s\" is not running.", experiment.getKey());
logger.info(message);
return new DecisionResponse(null, reasons);
}
// look for forced bucketing first.
DecisionResponse<Variation> decisionVariation = getForcedVariation(experiment, user.getUserId());
reasons.merge(decisionVariation.getReasons());
Variation variation = decisionVariation.getResult();
// check for whitelisting
if (variation == null) {
decisionVariation = getWhitelistedVariation(experiment, user.getUserId());
reasons.merge(decisionVariation.getReasons());
variation = decisionVariation.getResult();
}
if (variation != null) {
return new DecisionResponse(variation, reasons);
}
// fetch the user profile map from the user profile service
boolean ignoreUPS = options.contains(OptimizelyDecideOption.IGNORE_USER_PROFILE_SERVICE);
UserProfile userProfile = null;
if (userProfileService != null && !ignoreUPS) {
try {
Map<String, Object> userProfileMap = userProfileService.lookup(user.getUserId());
if (userProfileMap == null) {
String message = reasons.addInfo("We were unable to get a user profile map from the UserProfileService.");
logger.info(message);
} else if (UserProfileUtils.isValidUserProfileMap(userProfileMap)) {
userProfile = UserProfileUtils.convertMapToUserProfile(userProfileMap);
} else {
String message = reasons.addInfo("The UserProfileService returned an invalid map.");
logger.warn(message);
}
} catch (Exception exception) {
String message = reasons.addInfo(exception.getMessage());
logger.error(message);
errorHandler.handleError(new OptimizelyRuntimeException(exception));
}
// check if user exists in user profile
if (userProfile != null) {
decisionVariation = getStoredVariation(experiment, userProfile, projectConfig);
reasons.merge(decisionVariation.getReasons());
variation = decisionVariation.getResult();
// return the stored variation if it exists
if (variation != null) {
return new DecisionResponse(variation, reasons);
}
} else {
// if we could not find a user profile, make a new one
userProfile = new UserProfile(user.getUserId(), new HashMap<String, Decision>());
}
}
DecisionResponse<Boolean> decisionMeetAudience = ExperimentUtils.doesUserMeetAudienceConditions(projectConfig, experiment, user.getAttributes(), EXPERIMENT, experiment.getKey());
reasons.merge(decisionMeetAudience.getReasons());
if (decisionMeetAudience.getResult()) {
String bucketingId = getBucketingId(user.getUserId(), user.getAttributes());
decisionVariation = bucketer.bucket(experiment, bucketingId, projectConfig);
reasons.merge(decisionVariation.getReasons());
variation = decisionVariation.getResult();
if (variation != null) {
if (userProfileService != null && !ignoreUPS) {
saveVariation(experiment, variation, userProfile);
} else {
logger.debug("This decision will not be saved since the UserProfileService is null.");
}
}
return new DecisionResponse(variation, reasons);
}
String message = reasons.addInfo("User \"%s\" does not meet conditions to be in experiment \"%s\".", user.getUserId(), experiment.getKey());
logger.info(message);
return new DecisionResponse(null, reasons);
}
use of com.optimizely.ab.optimizelydecision.DecisionReasons in project java-sdk by optimizely.
the class Bucketer method bucketToVariation.
@Nonnull
private DecisionResponse<Variation> bucketToVariation(@Nonnull Experiment experiment, @Nonnull String bucketingId) {
DecisionReasons reasons = DefaultDecisionReasons.newInstance();
// "salt" the bucket id using the experiment id
String experimentId = experiment.getId();
String experimentKey = experiment.getKey();
String combinedBucketId = bucketingId + experimentId;
List<TrafficAllocation> trafficAllocations = experiment.getTrafficAllocation();
int hashCode = MurmurHash3.murmurhash3_x86_32(combinedBucketId, 0, combinedBucketId.length(), MURMUR_HASH_SEED);
int bucketValue = generateBucketValue(hashCode);
logger.debug("Assigned bucket {} to user with bucketingId \"{}\" when bucketing to a variation.", bucketValue, bucketingId);
String bucketedVariationId = bucketToEntity(bucketValue, trafficAllocations);
if (bucketedVariationId != null) {
Variation bucketedVariation = experiment.getVariationIdToVariationMap().get(bucketedVariationId);
String variationKey = bucketedVariation.getKey();
String message = reasons.addInfo("User with bucketingId \"%s\" is in variation \"%s\" of experiment \"%s\".", bucketingId, variationKey, experimentKey);
logger.info(message);
return new DecisionResponse(bucketedVariation, reasons);
}
// user was not bucketed to a variation
String message = reasons.addInfo("User with bucketingId \"%s\" is not in any variation of experiment \"%s\".", bucketingId, experimentKey);
logger.info(message);
return new DecisionResponse(null, reasons);
}
use of com.optimizely.ab.optimizelydecision.DecisionReasons in project java-sdk by optimizely.
the class ExperimentUtils method doesUserMeetAudienceConditions.
/**
* Determines whether a user satisfies audience conditions for the experiment.
*
* @param projectConfig the current projectConfig
* @param experiment the experiment we are evaluating audiences for
* @param attributes the attributes of the user
* @param loggingEntityType It can be either experiment or rule.
* @param loggingKey In case of loggingEntityType is experiment it will be experiment key or else it will be rule number.
* @return whether the user meets the criteria for the experiment
*/
@Nonnull
public static DecisionResponse<Boolean> doesUserMeetAudienceConditions(@Nonnull ProjectConfig projectConfig, @Nonnull Experiment experiment, @Nonnull Map<String, ?> attributes, @Nonnull String loggingEntityType, @Nonnull String loggingKey) {
DecisionReasons reasons = DefaultDecisionReasons.newInstance();
DecisionResponse<Boolean> decisionResponse;
if (experiment.getAudienceConditions() != null) {
logger.debug("Evaluating audiences for {} \"{}\": {}.", loggingEntityType, loggingKey, experiment.getAudienceConditions());
decisionResponse = evaluateAudienceConditions(projectConfig, experiment, attributes, loggingEntityType, loggingKey);
} else {
decisionResponse = evaluateAudience(projectConfig, experiment, attributes, loggingEntityType, loggingKey);
}
Boolean resolveReturn = decisionResponse.getResult();
reasons.merge(decisionResponse.getReasons());
return new DecisionResponse(// make it Nonnull for if-evaluation
resolveReturn != null && resolveReturn, reasons);
}
use of com.optimizely.ab.optimizelydecision.DecisionReasons in project java-sdk by optimizely.
the class ExperimentUtils method evaluateAudienceConditions.
@Nonnull
public static DecisionResponse<Boolean> evaluateAudienceConditions(@Nonnull ProjectConfig projectConfig, @Nonnull Experiment experiment, @Nonnull Map<String, ?> attributes, @Nonnull String loggingEntityType, @Nonnull String loggingKey) {
DecisionReasons reasons = DefaultDecisionReasons.newInstance();
Condition conditions = experiment.getAudienceConditions();
if (conditions == null)
return new DecisionResponse(null, reasons);
Boolean result = null;
try {
result = conditions.evaluate(projectConfig, attributes);
String message = reasons.addInfo("Audiences for %s \"%s\" collectively evaluated to %s.", loggingEntityType, loggingKey, result);
logger.info(message);
} catch (Exception e) {
String message = reasons.addInfo("Condition invalid: %s", e.getMessage());
logger.error(message);
}
return new DecisionResponse(result, reasons);
}
Aggregations