Skip to content

move validate forced decision to decision service #457

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jan 5, 2022
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
24 changes: 2 additions & 22 deletions core-api/src/main/java/com/optimizely/ab/Optimizely.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/****************************************************************************
* Copyright 2016-2021, Optimizely, Inc. and contributors *
* Copyright 2016-2022, Optimizely, Inc. and contributors *
* *
* Licensed under the Apache License, Version 2.0 (the "License"); *
* you may not use this file except in compliance with the License. *
Expand Down Expand Up @@ -1185,7 +1185,7 @@ OptimizelyDecision decide(@Nonnull OptimizelyUserContext user,

// Check Forced Decision
OptimizelyDecisionContext optimizelyDecisionContext = new OptimizelyDecisionContext(flag.getKey(), null);
DecisionResponse<Variation> forcedDecisionVariation = user.findValidatedForcedDecision(optimizelyDecisionContext);
DecisionResponse<Variation> forcedDecisionVariation = decisionService.validatedForcedDecision(optimizelyDecisionContext, projectConfig, user);
decisionReasons.merge(forcedDecisionVariation.getReasons());
if (forcedDecisionVariation.getResult() != null) {
flagDecision = new FeatureDecision(null, forcedDecisionVariation.getResult(), FeatureDecision.DecisionSource.FEATURE_TEST);
Expand Down Expand Up @@ -1344,26 +1344,6 @@ private DecisionResponse<Map<String, Object>> getDecisionVariableMap(@Nonnull Fe
return new DecisionResponse(valuesMap, reasons);
}

/**
* Gets a variation based on flagKey and variationKey
*
* @param flagKey The flag key for the variation
* @param variationKey The variation key for the variation
* @return Returns a variation based on flagKey and variationKey, otherwise null
*/
public Variation getFlagVariationByKey(String flagKey, String variationKey) {
Map<String, List<Variation>> flagVariationsMap = getProjectConfig().getFlagVariationsMap();
if (flagVariationsMap.containsKey(flagKey)) {
List<Variation> variations = flagVariationsMap.get(flagKey);
for (Variation variation : variations) {
if (variation.getKey().equals(variationKey)) {
return variation;
}
}
}
return null;
}

/**
* Helper method which makes separate copy of attributesMap variable and returns it
*
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/**
*
* Copyright 2020-2021, Optimizely and contributors
* Copyright 2020-2022, Optimizely and contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -265,35 +265,7 @@ public boolean removeAllForcedDecisions() {
return true;
}

/**
* Find a validated forced decision
*
* @param optimizelyDecisionContext The OptimizelyDecisionContext containing flagKey and ruleKey
* @return Returns a DecisionResponse structure of type Variation, otherwise null result with reasons
*/
public DecisionResponse<Variation> findValidatedForcedDecision(@Nonnull OptimizelyDecisionContext optimizelyDecisionContext) {
DecisionReasons reasons = DefaultDecisionReasons.newInstance();
OptimizelyForcedDecision optimizelyForcedDecision = findForcedDecision(optimizelyDecisionContext);
String variationKey = optimizelyForcedDecision != null ? optimizelyForcedDecision.getVariationKey() : null;
if (variationKey != null) {
Variation variation = optimizely.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);
}


// Utils

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/****************************************************************************
* Copyright 2017-2021, Optimizely, Inc. and contributors *
* Copyright 2017-2022, Optimizely, Inc. and contributors *
* *
* Licensed under the Apache License, Version 2.0 (the "License"); *
* you may not use this file except in compliance with the License. *
Expand All @@ -16,6 +16,7 @@
package com.optimizely.ab.bucketing;

import com.optimizely.ab.OptimizelyDecisionContext;
import com.optimizely.ab.OptimizelyForcedDecision;
import com.optimizely.ab.OptimizelyRuntimeException;
import com.optimizely.ab.OptimizelyUserContext;
import com.optimizely.ab.config.*;
Expand Down Expand Up @@ -469,6 +470,39 @@ String getBucketingId(@Nonnull String userId,
return bucketingId;
}

/**
* 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);
}

public ConcurrentHashMap<String, ConcurrentHashMap<String, String>> getForcedVariationMapping() {
return forcedVariationMapping;
}
Expand Down Expand Up @@ -591,7 +625,7 @@ public DecisionResponse<Variation> getVariationFromExperimentRule(@Nonnull Proje
String ruleKey = rule != null ? rule.getKey() : null;
// Check Forced-Decision
OptimizelyDecisionContext optimizelyDecisionContext = new OptimizelyDecisionContext(flagKey, ruleKey);
DecisionResponse<Variation> forcedDecisionResponse = user.findValidatedForcedDecision(optimizelyDecisionContext);
DecisionResponse<Variation> forcedDecisionResponse = validatedForcedDecision(optimizelyDecisionContext, projectConfig, user);

reasons.merge(forcedDecisionResponse.getReasons());

Expand Down Expand Up @@ -640,7 +674,7 @@ DecisionResponse<AbstractMap.SimpleEntry> getVariationFromDeliveryRule(@Nonnull
// Check forced-decisions first
Experiment rule = rules.get(ruleIndex);
OptimizelyDecisionContext optimizelyDecisionContext = new OptimizelyDecisionContext(flagKey, rule.getKey());
DecisionResponse<Variation> forcedDecisionResponse = user.findValidatedForcedDecision(optimizelyDecisionContext);
DecisionResponse<Variation> forcedDecisionResponse = validatedForcedDecision(optimizelyDecisionContext, projectConfig, user);
reasons.merge(forcedDecisionResponse.getReasons());

Variation variation = forcedDecisionResponse.getResult();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/**
*
* Copyright 2016-2021, Optimizely and contributors
* Copyright 2016-2022, Optimizely and contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -503,6 +503,27 @@ public Map<String, List<Variation>> getFlagVariationsMap() {
return flagVariationsMap;
}

/**
* Gets a variation based on flagKey and variationKey
*
* @param flagKey The flag key for the variation
* @param variationKey The variation key for the variation
* @return Returns a variation based on flagKey and variationKey, otherwise null
*/
@Override
public Variation getFlagVariationByKey(String flagKey, String variationKey) {
Map<String, List<Variation>> flagVariationsMap = getFlagVariationsMap();
if (flagVariationsMap.containsKey(flagKey)) {
List<Variation> variations = flagVariationsMap.get(flagKey);
for (Variation variation : variations) {
if (variation.getKey().equals(variationKey)) {
return variation;
}
}
}
return null;
}

@Override
public String toString() {
return "ProjectConfig{" +
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/**
*
* Copyright 2016-2021, Optimizely and contributors
* Copyright 2016-2022, Optimizely and contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -105,6 +105,8 @@ Experiment getExperimentForKey(@Nonnull String experimentKey,

Map<String, List<Variation>> getFlagVariationsMap();

Variation getFlagVariationByKey(String flagKey, String variationKey);

@Override
String toString();

Expand Down
4 changes: 2 additions & 2 deletions core-api/src/test/java/com/optimizely/ab/OptimizelyTest.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/****************************************************************************
* Copyright 2016-2020, Optimizely, Inc. and contributors *
* Copyright 2016-2022, Optimizely, Inc. and contributors *
* *
* Licensed under the Apache License, Version 2.0 (the "License"); *
* you may not use this file except in compliance with the License. *
Expand Down Expand Up @@ -4660,7 +4660,7 @@ public void getFlagVariationByKey() throws IOException {
String flagKey = "double_single_variable_feature";
String variationKey = "pi_variation";
Optimizely optimizely = Optimizely.builder().withDatafile(validConfigJsonV4()).build();
Variation variation = optimizely.getFlagVariationByKey(flagKey, variationKey);
Variation variation = optimizely.getProjectConfig().getFlagVariationByKey(flagKey, variationKey);

assertNotNull(variation);
assertEquals(variationKey, variation.getKey());
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/**
*
* Copyright 2021, Optimizely and contributors
* Copyright 2021-2022, Optimizely and contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -1442,44 +1442,6 @@ public void removeAllForcedDecisions() {
assertTrue(optimizelyUserContext.removeAllForcedDecisions());
}


@Test
public void findValidatedForcedDecisionWithRuleKey() {
String ruleKey = "77777";
String flagKey = "feature_2";
String variationKey = "variation_no_traffic";
OptimizelyUserContext optimizelyUserContext = new OptimizelyUserContext(
optimizely,
userId,
Collections.emptyMap());

OptimizelyDecisionContext optimizelyDecisionContext = new OptimizelyDecisionContext(flagKey, ruleKey);
OptimizelyForcedDecision optimizelyForcedDecision = new OptimizelyForcedDecision(variationKey);

optimizelyUserContext.setForcedDecision(optimizelyDecisionContext, optimizelyForcedDecision);
DecisionResponse<Variation> response = optimizelyUserContext.findValidatedForcedDecision(optimizelyDecisionContext);
Variation variation = response.getResult();
assertEquals(variationKey, variation.getKey());
}

@Test
public void findValidatedForcedDecisionWithoutRuleKey() {
String flagKey = "feature_2";
String variationKey = "variation_no_traffic";
OptimizelyUserContext optimizelyUserContext = new OptimizelyUserContext(
optimizely,
userId,
Collections.emptyMap());

OptimizelyDecisionContext optimizelyDecisionContext = new OptimizelyDecisionContext(flagKey, null);
OptimizelyForcedDecision optimizelyForcedDecision = new OptimizelyForcedDecision(variationKey);

optimizelyUserContext.setForcedDecision(optimizelyDecisionContext, optimizelyForcedDecision);
DecisionResponse<Variation> response = optimizelyUserContext.findValidatedForcedDecision(optimizelyDecisionContext);
Variation variation = response.getResult();
assertEquals(variationKey, variation.getKey());
}

@Test
public void setForcedDecisionsAndCallDecide() {
String flagKey = "feature_2";
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/****************************************************************************
* Copyright 2017-2020, Optimizely, Inc. and contributors *
* Copyright 2017-2022, Optimizely, Inc. and contributors *
* *
* Licensed under the Apache License, Version 2.0 (the "License"); *
* you may not use this file except in compliance with the License. *
Expand All @@ -17,6 +17,8 @@

import ch.qos.logback.classic.Level;
import com.optimizely.ab.Optimizely;
import com.optimizely.ab.OptimizelyDecisionContext;
import com.optimizely.ab.OptimizelyForcedDecision;
import com.optimizely.ab.OptimizelyUserContext;
import com.optimizely.ab.config.*;
import com.optimizely.ab.error.ErrorHandler;
Expand All @@ -35,6 +37,7 @@

import static com.optimizely.ab.config.DatafileProjectConfigTestUtils.*;
import static com.optimizely.ab.config.ValidProjectConfigV4.*;
import static junit.framework.TestCase.assertEquals;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.*;
Expand Down Expand Up @@ -761,6 +764,45 @@ public void getVariationFromExperimentRuleTest() {
assertEquals(expectedVariation, decisionResponse.getResult());
}

@Test
public void validatedForcedDecisionWithRuleKey() {
String userId = "testUser1";
String ruleKey = "2637642575";
String flagKey = "multi_variate_feature";
String variationKey = "2346257680";
OptimizelyUserContext optimizelyUserContext = new OptimizelyUserContext(
optimizely,
userId,
Collections.emptyMap());

OptimizelyDecisionContext optimizelyDecisionContext = new OptimizelyDecisionContext(flagKey, ruleKey);
OptimizelyForcedDecision optimizelyForcedDecision = new OptimizelyForcedDecision(variationKey);

optimizelyUserContext.setForcedDecision(optimizelyDecisionContext, optimizelyForcedDecision);
DecisionResponse<Variation> response = decisionService.validatedForcedDecision(optimizelyDecisionContext, v4ProjectConfig, optimizelyUserContext);
Variation variation = response.getResult();
assertEquals(variationKey, variation.getKey());
}

@Test
public void validatedForcedDecisionWithoutRuleKey() {
String userId = "testUser1";
String flagKey = "multi_variate_feature";
String variationKey = "521740985";
OptimizelyUserContext optimizelyUserContext = new OptimizelyUserContext(
optimizely,
userId,
Collections.emptyMap());

OptimizelyDecisionContext optimizelyDecisionContext = new OptimizelyDecisionContext(flagKey, null);
OptimizelyForcedDecision optimizelyForcedDecision = new OptimizelyForcedDecision(variationKey);

optimizelyUserContext.setForcedDecision(optimizelyDecisionContext, optimizelyForcedDecision);
DecisionResponse<Variation> response = decisionService.validatedForcedDecision(optimizelyDecisionContext, v4ProjectConfig, optimizelyUserContext);
Variation variation = response.getResult();
assertEquals(variationKey, variation.getKey());
}

//========= white list tests ==========/

/**
Expand Down