Search in sources :

Example 11 with DecisionReasons

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);
}
Also used : DecisionResponse(com.optimizely.ab.optimizelydecision.DecisionResponse) DefaultDecisionReasons(com.optimizely.ab.optimizelydecision.DefaultDecisionReasons) DecisionReasons(com.optimizely.ab.optimizelydecision.DecisionReasons) Nonnull(javax.annotation.Nonnull)

Example 12 with DecisionReasons

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);
}
Also used : DecisionResponse(com.optimizely.ab.optimizelydecision.DecisionResponse) ConcurrentHashMap(java.util.concurrent.ConcurrentHashMap) DefaultDecisionReasons(com.optimizely.ab.optimizelydecision.DefaultDecisionReasons) DecisionReasons(com.optimizely.ab.optimizelydecision.DecisionReasons) OptimizelyRuntimeException(com.optimizely.ab.OptimizelyRuntimeException) OptimizelyRuntimeException(com.optimizely.ab.OptimizelyRuntimeException) Nonnull(javax.annotation.Nonnull)

Example 13 with DecisionReasons

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);
}
Also used : DecisionResponse(com.optimizely.ab.optimizelydecision.DecisionResponse) DefaultDecisionReasons(com.optimizely.ab.optimizelydecision.DefaultDecisionReasons) DecisionReasons(com.optimizely.ab.optimizelydecision.DecisionReasons) Nonnull(javax.annotation.Nonnull)

Example 14 with DecisionReasons

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);
}
Also used : DecisionResponse(com.optimizely.ab.optimizelydecision.DecisionResponse) DefaultDecisionReasons(com.optimizely.ab.optimizelydecision.DefaultDecisionReasons) DecisionReasons(com.optimizely.ab.optimizelydecision.DecisionReasons) Nonnull(javax.annotation.Nonnull)

Example 15 with DecisionReasons

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);
}
Also used : Condition(com.optimizely.ab.config.audience.Condition) OrCondition(com.optimizely.ab.config.audience.OrCondition) AudienceIdCondition(com.optimizely.ab.config.audience.AudienceIdCondition) DecisionResponse(com.optimizely.ab.optimizelydecision.DecisionResponse) DefaultDecisionReasons(com.optimizely.ab.optimizelydecision.DefaultDecisionReasons) DecisionReasons(com.optimizely.ab.optimizelydecision.DecisionReasons) Nonnull(javax.annotation.Nonnull)

Aggregations

DecisionReasons (com.optimizely.ab.optimizelydecision.DecisionReasons)15 DecisionResponse (com.optimizely.ab.optimizelydecision.DecisionResponse)15 DefaultDecisionReasons (com.optimizely.ab.optimizelydecision.DefaultDecisionReasons)15 Nonnull (javax.annotation.Nonnull)12 OptimizelyDecisionContext (com.optimizely.ab.OptimizelyDecisionContext)2 OptimizelyForcedDecision (com.optimizely.ab.OptimizelyForcedDecision)2 AudienceIdCondition (com.optimizely.ab.config.audience.AudienceIdCondition)2 Condition (com.optimizely.ab.config.audience.Condition)2 OrCondition (com.optimizely.ab.config.audience.OrCondition)2 OptimizelyRuntimeException (com.optimizely.ab.OptimizelyRuntimeException)1 ArrayList (java.util.ArrayList)1 ConcurrentHashMap (java.util.concurrent.ConcurrentHashMap)1