diff --git a/core-api/src/main/java/com/optimizely/ab/Optimizely.java b/core-api/src/main/java/com/optimizely/ab/Optimizely.java index c53095692..7eae1a1d0 100644 --- a/core-api/src/main/java/com/optimizely/ab/Optimizely.java +++ b/core-api/src/main/java/com/optimizely/ab/Optimizely.java @@ -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. * @@ -1185,7 +1185,7 @@ OptimizelyDecision decide(@Nonnull OptimizelyUserContext user, // Check Forced Decision OptimizelyDecisionContext optimizelyDecisionContext = new OptimizelyDecisionContext(flag.getKey(), null); - DecisionResponse forcedDecisionVariation = user.findValidatedForcedDecision(optimizelyDecisionContext); + DecisionResponse forcedDecisionVariation = decisionService.validatedForcedDecision(optimizelyDecisionContext, projectConfig, user); decisionReasons.merge(forcedDecisionVariation.getReasons()); if (forcedDecisionVariation.getResult() != null) { flagDecision = new FeatureDecision(null, forcedDecisionVariation.getResult(), FeatureDecision.DecisionSource.FEATURE_TEST); @@ -1344,26 +1344,6 @@ private DecisionResponse> 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> flagVariationsMap = getProjectConfig().getFlagVariationsMap(); - if (flagVariationsMap.containsKey(flagKey)) { - List 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 * diff --git a/core-api/src/main/java/com/optimizely/ab/OptimizelyUserContext.java b/core-api/src/main/java/com/optimizely/ab/OptimizelyUserContext.java index 53eb26cf6..d05df3bbb 100644 --- a/core-api/src/main/java/com/optimizely/ab/OptimizelyUserContext.java +++ b/core-api/src/main/java/com/optimizely/ab/OptimizelyUserContext.java @@ -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. @@ -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 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 diff --git a/core-api/src/main/java/com/optimizely/ab/bucketing/DecisionService.java b/core-api/src/main/java/com/optimizely/ab/bucketing/DecisionService.java index e386c360d..c7ee0b3f3 100644 --- a/core-api/src/main/java/com/optimizely/ab/bucketing/DecisionService.java +++ b/core-api/src/main/java/com/optimizely/ab/bucketing/DecisionService.java @@ -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. * @@ -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.*; @@ -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 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> getForcedVariationMapping() { return forcedVariationMapping; } @@ -591,7 +625,7 @@ public DecisionResponse getVariationFromExperimentRule(@Nonnull Proje String ruleKey = rule != null ? rule.getKey() : null; // Check Forced-Decision OptimizelyDecisionContext optimizelyDecisionContext = new OptimizelyDecisionContext(flagKey, ruleKey); - DecisionResponse forcedDecisionResponse = user.findValidatedForcedDecision(optimizelyDecisionContext); + DecisionResponse forcedDecisionResponse = validatedForcedDecision(optimizelyDecisionContext, projectConfig, user); reasons.merge(forcedDecisionResponse.getReasons()); @@ -640,7 +674,7 @@ DecisionResponse getVariationFromDeliveryRule(@Nonnull // Check forced-decisions first Experiment rule = rules.get(ruleIndex); OptimizelyDecisionContext optimizelyDecisionContext = new OptimizelyDecisionContext(flagKey, rule.getKey()); - DecisionResponse forcedDecisionResponse = user.findValidatedForcedDecision(optimizelyDecisionContext); + DecisionResponse forcedDecisionResponse = validatedForcedDecision(optimizelyDecisionContext, projectConfig, user); reasons.merge(forcedDecisionResponse.getReasons()); Variation variation = forcedDecisionResponse.getResult(); diff --git a/core-api/src/main/java/com/optimizely/ab/config/DatafileProjectConfig.java b/core-api/src/main/java/com/optimizely/ab/config/DatafileProjectConfig.java index 831563826..9620f5cbf 100644 --- a/core-api/src/main/java/com/optimizely/ab/config/DatafileProjectConfig.java +++ b/core-api/src/main/java/com/optimizely/ab/config/DatafileProjectConfig.java @@ -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. @@ -503,6 +503,27 @@ public Map> 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> flagVariationsMap = getFlagVariationsMap(); + if (flagVariationsMap.containsKey(flagKey)) { + List variations = flagVariationsMap.get(flagKey); + for (Variation variation : variations) { + if (variation.getKey().equals(variationKey)) { + return variation; + } + } + } + return null; + } + @Override public String toString() { return "ProjectConfig{" + diff --git a/core-api/src/main/java/com/optimizely/ab/config/ProjectConfig.java b/core-api/src/main/java/com/optimizely/ab/config/ProjectConfig.java index 9c3321708..10ebdc832 100644 --- a/core-api/src/main/java/com/optimizely/ab/config/ProjectConfig.java +++ b/core-api/src/main/java/com/optimizely/ab/config/ProjectConfig.java @@ -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. @@ -105,6 +105,8 @@ Experiment getExperimentForKey(@Nonnull String experimentKey, Map> getFlagVariationsMap(); + Variation getFlagVariationByKey(String flagKey, String variationKey); + @Override String toString(); diff --git a/core-api/src/test/java/com/optimizely/ab/OptimizelyTest.java b/core-api/src/test/java/com/optimizely/ab/OptimizelyTest.java index 0a9c334f8..2cab4a01e 100644 --- a/core-api/src/test/java/com/optimizely/ab/OptimizelyTest.java +++ b/core-api/src/test/java/com/optimizely/ab/OptimizelyTest.java @@ -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. * @@ -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()); diff --git a/core-api/src/test/java/com/optimizely/ab/OptimizelyUserContextTest.java b/core-api/src/test/java/com/optimizely/ab/OptimizelyUserContextTest.java index ef91ca596..c196938c4 100644 --- a/core-api/src/test/java/com/optimizely/ab/OptimizelyUserContextTest.java +++ b/core-api/src/test/java/com/optimizely/ab/OptimizelyUserContextTest.java @@ -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. @@ -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 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 response = optimizelyUserContext.findValidatedForcedDecision(optimizelyDecisionContext); - Variation variation = response.getResult(); - assertEquals(variationKey, variation.getKey()); - } - @Test public void setForcedDecisionsAndCallDecide() { String flagKey = "feature_2"; diff --git a/core-api/src/test/java/com/optimizely/ab/bucketing/DecisionServiceTest.java b/core-api/src/test/java/com/optimizely/ab/bucketing/DecisionServiceTest.java index eddbf0178..6057b43cf 100644 --- a/core-api/src/test/java/com/optimizely/ab/bucketing/DecisionServiceTest.java +++ b/core-api/src/test/java/com/optimizely/ab/bucketing/DecisionServiceTest.java @@ -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. * @@ -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; @@ -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.*; @@ -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 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 response = decisionService.validatedForcedDecision(optimizelyDecisionContext, v4ProjectConfig, optimizelyUserContext); + Variation variation = response.getResult(); + assertEquals(variationKey, variation.getKey()); + } + //========= white list tests ==========/ /**