Skip to content

Commit

Permalink
move validate forced decision to decision service (#457)
Browse files Browse the repository at this point in the history
* Moved validated forced decision to decision service and fixed tests

* comment fix

* Updated Header

Co-authored-by: mnoman09 <[email protected]>
  • Loading branch information
mnoman09 and NomanShoaib authored Jan 5, 2022
1 parent ca3690d commit 7298cf5
Show file tree
Hide file tree
Showing 8 changed files with 112 additions and 99 deletions.
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

0 comments on commit 7298cf5

Please sign in to comment.