8000 feat(ForcedDecisions): add forced-decisions APIs to OptimizelyUserCon… · optimizely/java-sdk@04d379a · GitHub
[go: up one dir, main page]

Skip to content
8000

Commit 04d379a

Browse files
feat(ForcedDecisions): add forced-decisions APIs to OptimizelyUserContext (#451)
## Summary Add a set of new APIs for forced-decisions to OptimizelyUserContext: - setForcedDecision - getForcedDecision - removeForcedDecision - removeAllForcedDecisions ## Test plan - unit tests for the new APIs - FSC tests with new test cases
1 parent e25985f commit 04d379a

14 files changed

+1287
-257
lines changed

core-api/src/main/java/com/optimizely/ab/Optimizely.java

Lines changed: 49 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -376,7 +376,7 @@ public void track(@Nonnull String eventName,
376376
@Nonnull
377377
public Boolean isFeatureEnabled(@Nonnull String featureKey,
378378
@Nonnull String userId) {
379-
return isFeatureEnabled(featureKey, userId, Collections.<String, String>emptyMap());
379+
return isFeatureEnabled(featureKey, userId, Collections.emptyMap());
380380
}
381381

382382
/**
@@ -424,7 +424,7 @@ private Boolean isFeatureEnabled(@Nonnull ProjectConfig projectConfig,
424424

425425
Map<String, ?> copiedAttributes = copyAttributes(attributes);
426426
FeatureDecision.DecisionSource decisionSource = FeatureDecision.DecisionSource.ROLLOUT;
427-
FeatureDecision featureDecision = decisionService.getVariationForFeature(featureFlag, userId, copiedAttributes, projectConfig).getResult();
427+
FeatureDecision featureDecision = decisionService.getVariationForFeature(featureFlag, createUserContext(userId, copiedAttributes), projectConfig).getResult();
428428
Boolean featureEnabled = false;
429429
SourceInfo sourceInfo = new RolloutSourceInfo();
430430
if (featureDecision.decisionSource != null) {
@@ -733,7 +733,7 @@ <T> T getFeatureVariableValueForType(@Nonnull String featureKey,
733733

734734
String variableValue = variable.getDefaultValue();
735735
Map<String, ?> copiedAttributes = copyAttributes(attributes);
736-
FeatureDecision featureDecision = decisionService.getVariationForFeature(featureFlag, userId, copiedAttributes, projectConfig).getResult();
736+
FeatureDecision featureDecision = decisionService.getVariationForFeature(featureFlag, createUserContext(userId, copiedAttributes), projectConfig).getResult();
737737
Boolean featureEnabled = false;
738738
if (featureDecision.variation != null) {
739739
if (featureDecision.variation.getFeatureEnabled()) {
@@ -824,6 +824,7 @@ Object convertStringToType(String variableValue, String type) {
824824
* @param userId The ID of the user.
825825
* @return An OptimizelyJSON instance for all variable values.
826826
* Null if the feature could not be found.
827+
*
827828
*/
828829
@Nullable
829830
public OptimizelyJSON getAllFeatureVariables(@Nonnull String featureKey,
@@ -839,6 +840,7 @@ public OptimizelyJSON getAllFeatureVariables(@Nonnull String featureKey,
839840
* @param attributes The user's attributes.
840841
* @return An OptimizelyJSON instance for all variable values.
841842
* Null if the feature could not be found.
843+
*
842844
*/
843845
@Nullable
844846
public OptimizelyJSON getAllFeatureVariables(@Nonnull String featureKey,
@@ -866,7 +868,7 @@ public OptimizelyJSON getAllFeatureVariables(@Nonnull String featureKey,
866868
}
867869

868870
Map<String, ?> copiedAttributes = copyAttributes(attributes);
869-
FeatureDecision featureDecision = decisionService.getVariationForFeature(featureFlag, userId, copiedAttributes, projectConfig).getResult();
871+
FeatureDecision featureDecision = decisionService.getVariationForFeature(featureFlag, createUserContext(userId, copiedAttributes), projectConfig, Collections.emptyList()).getResult();
870872
Boolean featureEnabled = false;
871873
Variation variation = featureDecision.variation;
872874

@@ -922,9 +924,10 @@ public OptimizelyJSON getAllFeatureVariables(@Nonnull String featureKey,
922924
* @param attributes The user's attributes.
923925
* @return List of the feature keys that are enabled for the user if the userId is empty it will
924926
* return Empty List.
927+
*
925928
*/
926929
public List<String> getEnabledFeatures(@Nonnull String userId, @Nonnull Map<String, ?> attributes) {
927-
List<String> enabledFeaturesList = new ArrayList<String>();
930+
List<String> enabledFeaturesList = new ArrayList();
928931
if (!validateUserId(userId)) {
929932
return enabledFeaturesList;
930933
}
@@ -951,7 +954,7 @@ public List<String> getEnabledFeatures(@Nonnull String userId, @Nonnull Map<Stri
951954
public Variation getVariation(@Nonnull Experiment experiment,
952955
@Nonnull String userId) throws UnknownExperimentException {
953956

954-
return getVariation(experiment, userId, Collections.<String, String>emptyMap());
957+
return getVariation(experiment, userId, Collections.emptyMap());
955958
}
956959

957960
@Nullable
@@ -967,8 +970,7 @@ private Variation getVariation(@Nonnull ProjectConfig projectConfig,
967970
@Nonnull String userId,
968971
@Nonnull Map<String, ?> attributes) throws UnknownExperimentException {
969972
Map<String, ?> copiedAttributes = copyAttributes(attributes);
970-
Variation variation = decisionService.getVariation(experiment, userId, copiedAttributes, projectConfig).getResult();
971-
973+
Variation variation = decisionService.getVariation(experiment, createUserContext(userId, copiedAttributes), projectConfig).getResult();
972974
String notificationType = NotificationCenter.DecisionNotificationType.AB_TEST.toString();
973975

974976
if (projectConfig.getExperimentFeatureKeyMapping().get(experiment.getId()) != null) {
@@ -1145,7 +1147,7 @@ public OptimizelyConfig getOptimizelyConfig() {
11451147
* @return An OptimizelyUserContext associated with this OptimizelyClient.
11461148
*/
11471149
public OptimizelyUserContext createUserContext(@Nonnull String userId,
1148-
@Nonnull Map<String, Object> attributes) {
1150+
@Nonnull Map<String, ?> attributes) {
11491151
if (userId == null) {
11501152
logger.warn("The userId parameter must be nonnull.");
11511153
return null;
@@ -1179,14 +1181,24 @@ OptimizelyDecision decide(@Nonnull OptimizelyUserContext user,
11791181
DecisionReasons decisionReasons = DefaultDecisionReasons.newInstance(allOptions);
11801182

11811183
Map<String, ?> copiedAttributes = new HashMap<>(attributes);
1182-
DecisionResponse<FeatureDecision> decisionVariation = decisionService.getVariationForFeature(
1183-
flag,
1184-
userId,
1185-
copiedAttributes,
1186-
projectConfig,
1187-
allOptions);
1188-
FeatureDecision flagDecision = decisionVariation.getResult();
1189-
decisionReasons.merge(decisionVariation.getReasons());
1184+
FeatureDecision flagDecision;
1185+
1186+
// Check Forced Decision
1187+
OptimizelyDecisionContext optimizelyDecisionContext = new OptimizelyDecisionContext(flag.getKey(), null);
1188+
DecisionResponse<Variation> forcedDecisionVariation = user.findValidatedForcedDecision(optimizelyDecisionContext);
1189+
decisionReasons.merge(forcedDecisionVariation.getReasons());
1190+
if (forcedDecisionVariation.getResult() != null) {
1191+
flagDecision = new FeatureDecision(null, forcedDecisionVariation.getResult(), FeatureDecision.DecisionSource.FEATURE_TEST);
1192+
} else {
1193+
// Regular decision
1194+
DecisionResponse<FeatureDecision> decisionVariation = decisionService.getVariationForFeature(
1195+
flag,
1196+
user,
1197+
projectConfig,
1198+
allOptions);
1199+
flagDecision = decisionVariation.getResult();
1200+
decisionReasons.merge(decisionVariation.getReasons());
1201+
}
11901202

11911203
Boolean flagEnabled = false;
11921204
if (flagDecision.variation != null) {
@@ -1332,6 +1344,26 @@ private DecisionResponse<Map<String, Object>> getDecisionVariableMap(@Nonnull Fe
13321344
return new DecisionResponse(valuesMap, reasons);
13331345
}
13341346

1347+
/**
1348+
* Gets a variation based on flagKey and variationKey
1349+
*
1350+
* @param flagKey The flag key for the variation
1351+
* @param variationKey The variation key for the variation
1352+
* @return Returns a variation based on flagKey and variationKey, otherwise null
1353+
*/
1354+
public Variation getFlagVariationByKey(String flagKey, String variationKey) {
1355+
Map<String, List<Variation>> flagVariationsMap = getProjectConfig().getFlagVariationsMap();
1356+
if (flagVariationsMap.containsKey(flagKey)) {
1357+
List<Variation> variations = flagVariationsMap.get(flagKey);
1358+
for (Variation variation : variations) {
1359+
if (variation.getKey().equals(variationKey)) {
1360+
return variation;
1361+
}
1362+
}
1363+
}
1364+
return null;
1365+
}
1366+
13351367
/**
13361368
* Helper method which makes separate copy of attributesMap variable and returns it
13371369
*
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
/**
2+
*
3+
* Copyright 2021, Optimizely and contributors
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
package com.optimizely.ab;
18+
19+
import javax.annotation.Nonnull;
20+
import javax.annotation.Nullable;
21+
22+
public class OptimizelyDecisionContext {
23+
public static final String OPTI_NULL_RULE_KEY = "$opt-null-rule-key";
24+
public static final String OPTI_KEY_DIVIDER = "-$opt$-";
25+
26+
private String flagKey;
27+
private String ruleKey;
28+
29+
public OptimizelyDecisionContext(@Nonnull String flagKey, @Nullable String ruleKey) {
30+
this.flagKey = flagKey;
31+
this.ruleKey = ruleKey;
32+
}
33+
34+
public String getFlagKey() {
35+
return flagKey;
36+
}
37+
38+
public String getRuleKey() {
39+
return ruleKey != null ? ruleKey : OPTI_NULL_RULE_KEY;
40+
}
41+
42+
public String getKey() {
43+
StringBuilder keyBuilder = new StringBuilder();
44+
keyBuilder.append(flagKey);
45+
keyBuilder.append(OPTI_KEY_DIVIDER);
46+
keyBuilder.append(getRuleKey());
47+
return keyBuilder.toString();
48+
}
49+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/**
2+
*
3+
* Copyright 2021, Optimizely and contributors
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
package com.optimizely.ab;
18+
19+
import javax.annotation.Nonnull;
20+
21+
public class OptimizelyForcedDecision {
22+
private String variationKey;
23+
24+
public OptimizelyForcedDecision(@Nonnull String variationKey) {
25+
this.variationKey = variationKey;
26+
}
27+
28+
public String getVariationKey() {
29+
return variationKey;
30+
}
31+
}

0 commit comments

Comments
 (0)
0