Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion core-api/src/main/java/com/optimizely/ab/Optimizely.java
Original file line number Diff line number Diff line change
Expand Up @@ -390,7 +390,7 @@ public Boolean isFeatureEnabled(@Nonnull String featureKey,
FeatureDecision featureDecision = decisionService.getVariationForFeature(featureFlag, userId, copiedAttributes);

if (featureDecision.variation != null) {
if (featureDecision.decisionSource.equals(FeatureDecision.DecisionSource.EXPERIMENT)) {
if (featureDecision.decisionSource.equals(FeatureDecision.DecisionSource.FEATURE_TEST)) {
sendImpression(
projectConfig,
featureDecision.experiment,
Expand Down Expand Up @@ -721,11 +721,18 @@ public Variation getVariation(@Nonnull Experiment experiment,
Map<String, ?> copiedAttributes = copyAttributes(attributes);
Variation variation = decisionService.getVariation(experiment, userId, copiedAttributes);

String notificationType = NotificationCenter.DecisionNotificationType.AB_TEST.toString();

if (getProjectConfig().getExperimentFeatureKeyMapping().get(experiment.getId()) != null) {
notificationType = NotificationCenter.DecisionNotificationType.FEATURE_TEST.toString();
}

DecisionNotification decisionNotification = DecisionNotification.newExperimentDecisionNotificationBuilder()
.withUserId(userId)
.withAttributes(copiedAttributes)
.withExperimentKey(experiment.getKey())
.withVariation(variation)
.withType(notificationType)
.build();

notificationCenter.sendNotifications(decisionNotification);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ public FeatureDecision getVariationForFeature(@Nonnull FeatureFlag featureFlag,
Variation variation = this.getVariation(experiment, userId, filteredAttributes);
if (variation != null) {
return new FeatureDecision(experiment, variation,
FeatureDecision.DecisionSource.EXPERIMENT);
FeatureDecision.DecisionSource.FEATURE_TEST);
}
}
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,19 @@ public class FeatureDecision {
public DecisionSource decisionSource;

public enum DecisionSource {
EXPERIMENT,
ROLLOUT
FEATURE_TEST("feature-test"),
ROLLOUT("rollout");

private final String key;

DecisionSource(String key) {
this.key = key;
}

@Override
public String toString() {
return key;
}
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ public String toString() {
private final Map<String, EventType> eventNameMapping;
private final Map<String, Experiment> experimentKeyMapping;
private final Map<String, FeatureFlag> featureKeyMapping;
private final Map<String, List<String>> experimentFeatureKeyMapping;

// id to entity mappings
private final Map<String, Audience> audienceIdMapping;
Expand Down Expand Up @@ -216,6 +217,9 @@ public ProjectConfig(String accountId,
this.experimentIdMapping = ProjectConfigUtils.generateIdMapping(this.experiments);
this.groupIdMapping = ProjectConfigUtils.generateIdMapping(groups);
this.rolloutIdMapping = ProjectConfigUtils.generateIdMapping(this.rollouts);

// Generate experiment to featureFlag list mapping to identify if experiment is AB-Test experiment or Feature-Test Experiment.
this.experimentFeatureKeyMapping = ProjectConfigUtils.generateExperimentFeatureMapping(this.featureFlags);
}

/**
Expand Down Expand Up @@ -419,6 +423,10 @@ public Map<String, FeatureFlag> getFeatureKeyMapping() {
return featureKeyMapping;
}

public Map<String, List<String>> getExperimentFeatureKeyMapping() {
return experimentFeatureKeyMapping;
}

public ConcurrentHashMap<String, ConcurrentHashMap<String, String>> getForcedVariationMapping() {
return forcedVariationMapping;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,4 +48,22 @@ public static <T extends IdMapped> Map<String, T> generateIdMapping(List<T> name
return Collections.unmodifiableMap(nameMapping);
}

/**
* Helper method for creating convenience mappings of ExperimentID to featureFlags it is included in.
*/
public static Map<String, List<String>> generateExperimentFeatureMapping(List<FeatureFlag> featureFlags) {
Map<String, List<String>> experimentFeatureMap = new HashMap<>();
for (FeatureFlag featureFlag : featureFlags) {
for (String experimentId : featureFlag.getExperimentIds()) {
if (experimentFeatureMap.containsKey(experimentId)) {
experimentFeatureMap.get(experimentId).add(featureFlag.getKey());
} else {
ArrayList<String> featureFlagKeysList = new ArrayList<>();
featureFlagKeysList.add(featureFlag.getKey());
experimentFeatureMap.put(experimentId, featureFlagKeysList);
}
}
}
return Collections.unmodifiableMap(experimentFeatureMap);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ public static class ExperimentDecisionNotificationBuilder {
public final static String EXPERIMENT_KEY = "experiment_key";
public final static String VARIATION_KEY = "variation_key";

private String type;
private String experimentKey;
private Variation variation;
private String userId;
Expand All @@ -93,6 +94,11 @@ public ExperimentDecisionNotificationBuilder withExperimentKey(String experiment
return this;
}

public ExperimentDecisionNotificationBuilder withType(String type) {
this.type = type;
return this;
}

public ExperimentDecisionNotificationBuilder withVariation(Variation variation) {
this.variation = variation;
return this;
Expand All @@ -104,7 +110,7 @@ public DecisionNotification build() {
decisionInfo.put(VARIATION_KEY, variation != null ? variation.getKey() : null);

return new DecisionNotification(
NotificationCenter.DecisionNotificationType.EXPERIMENT.toString(),
type,
userId,
attributes,
decisionInfo);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,10 @@
*/
public class NotificationCenter {
public enum DecisionNotificationType {
AB_TEST("ab-test"),
FEATURE("feature"),
FEATURE_VARIABLE("feature_variable"),
EXPERIMENT("experiment");
FEATURE_TEST("feature-test"),
FEATURE_VARIABLE("feature-variable");

private final String key;

Expand Down
12 changes: 6 additions & 6 deletions core-api/src/test/java/com/optimizely/ab/OptimizelyTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -2687,7 +2687,7 @@ public void activateEndToEndWithDecisionListener() throws Exception {
testDecisionInfoMap.put(VARIATION_KEY, "Gred");

int notificationId = optimizely.notificationCenter.addDecisionNotificationListener(
getDecisionListener(NotificationCenter.DecisionNotificationType.EXPERIMENT.toString(),
getDecisionListener(NotificationCenter.DecisionNotificationType.FEATURE_TEST.toString(),
userId,
testUserAttributes,
testDecisionInfoMap));
Expand Down Expand Up @@ -2726,7 +2726,7 @@ public void activateUserNullWithListener() throws Exception {
testDecisionInfoMap.put(VARIATION_KEY, null);

int notificationId = optimizely.notificationCenter.addDecisionNotificationListener(
getDecisionListener(NotificationCenter.DecisionNotificationType.EXPERIMENT.toString(),
getDecisionListener(NotificationCenter.DecisionNotificationType.AB_TEST.toString(),
null,
Collections.<String, Object>emptyMap(),
testDecisionInfoMap));
Expand Down Expand Up @@ -2766,7 +2766,7 @@ public void activateUserNotInAudienceWithListener() throws Exception {
testDecisionInfoMap.put(VARIATION_KEY, null);

int notificationId = optimizely.notificationCenter.addDecisionNotificationListener(
getDecisionListener(NotificationCenter.DecisionNotificationType.EXPERIMENT.toString(),
getDecisionListener(NotificationCenter.DecisionNotificationType.FEATURE_TEST.toString(),
genericUserId,
testUserAttributes,
testDecisionInfoMap));
Expand Down Expand Up @@ -3516,7 +3516,7 @@ public void getFeatureVariableValueReturnsDefaultValueWhenFeatureEnabledIsFalse(
.withDecisionService(mockDecisionService)
.build();

FeatureDecision featureDecision = new FeatureDecision(multivariateExperiment, VARIATION_MULTIVARIATE_EXPERIMENT_GRED, FeatureDecision.DecisionSource.EXPERIMENT);
FeatureDecision featureDecision = new FeatureDecision(multivariateExperiment, VARIATION_MULTIVARIATE_EXPERIMENT_GRED, FeatureDecision.DecisionSource.FEATURE_TEST);
doReturn(featureDecision).when(mockDecisionService).getVariationForFeature(
FEATURE_FLAG_MULTI_VARIATE_FEATURE,
genericUserId,
Expand Down Expand Up @@ -4062,7 +4062,7 @@ public void isFeatureEnabledReturnsFalseAndDispatchesWhenUserIsBucketedIntoAnExp
Experiment activatedExperiment = validProjectConfig.getExperimentKeyMapping().get(EXPERIMENT_MULTIVARIATE_EXPERIMENT_KEY);
Variation variation = new Variation("2", "variation_toggled_off", false, null);

FeatureDecision featureDecision = new FeatureDecision(activatedExperiment, variation, FeatureDecision.DecisionSource.EXPERIMENT);
FeatureDecision featureDecision = new FeatureDecision(activatedExperiment, variation, FeatureDecision.DecisionSource.FEATURE_TEST);
doReturn(featureDecision).when(mockDecisionService).getVariationForFeature(
any(FeatureFlag.class),
anyString(),
Expand Down Expand Up @@ -5135,7 +5135,7 @@ public void getVariationBucketingIdAttribute() throws Exception {
testDecisionInfoMap.put(VARIATION_KEY, bucketedVariation.getKey());

int notificationId = optimizely.notificationCenter.addDecisionNotificationListener(
getDecisionListener(NotificationCenter.DecisionNotificationType.EXPERIMENT.toString(),
getDecisionListener(NotificationCenter.DecisionNotificationType.AB_TEST.toString(),
testUserId,
testUserAttributes,
testDecisionInfoMap));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;

import static com.optimizely.ab.config.ProjectConfigTestUtils.noAudienceProjectConfigV3;
import static com.optimizely.ab.config.ProjectConfigTestUtils.validProjectConfigV2;
import static com.optimizely.ab.config.ProjectConfigTestUtils.validProjectConfigV3;
import static com.optimizely.ab.config.ProjectConfigTestUtils.validProjectConfigV4;
import static com.optimizely.ab.config.ValidProjectConfigV4.ATTRIBUTE_HOUSE_KEY;
Expand Down Expand Up @@ -406,7 +405,7 @@ public void getVariationForFeatureReturnsVariationReturnedFromGetVariation() {
Collections.<String, String>emptyMap()
);
assertEquals(ValidProjectConfigV4.VARIATION_MUTEX_GROUP_EXP_2_VAR_1, featureDecision.variation);
assertEquals(FeatureDecision.DecisionSource.EXPERIMENT, featureDecision.decisionSource);
assertEquals(FeatureDecision.DecisionSource.FEATURE_TEST, featureDecision.decisionSource);

verify(spyFeatureFlag, times(2)).getExperimentIds();
verify(spyFeatureFlag, never()).getKey();
Expand Down Expand Up @@ -460,7 +459,7 @@ public void getVariationForFeatureReturnsVariationFromExperimentBeforeRollout()
Collections.<String, String>emptyMap()
);
assertEquals(experimentVariation, featureDecision.variation);
assertEquals(FeatureDecision.DecisionSource.EXPERIMENT, featureDecision.decisionSource);
assertEquals(FeatureDecision.DecisionSource.FEATURE_TEST, featureDecision.decisionSource);

// make sure we do not even check for rollout bucketing
verify(decisionService, never()).getVariationForFeatureInRollout(
Expand Down