|
16 | 16 | */ |
17 | 17 | package com.optimizely.ab; |
18 | 18 |
|
19 | | -import com.optimizely.ab.optimizelydecision.OptimizelyDecideOption; |
20 | | -import com.optimizely.ab.optimizelydecision.OptimizelyDecision; |
| 19 | +import com.optimizely.ab.config.Variation; |
| 20 | +import com.optimizely.ab.optimizelydecision.*; |
21 | 21 | import org.slf4j.Logger; |
22 | 22 | import org.slf4j.LoggerFactory; |
23 | 23 |
|
24 | 24 | import javax.annotation.Nonnull; |
25 | 25 | import javax.annotation.Nullable; |
26 | | -import java.util.Collections; |
27 | | -import java.util.HashMap; |
28 | | -import java.util.List; |
29 | | -import java.util.Map; |
| 26 | +import java.util.*; |
30 | 27 |
|
31 | 28 | public class OptimizelyUserContext { |
| 29 | + static class ForcedDecision { |
| 30 | + private String flagKey; |
| 31 | + private String ruleKey; |
| 32 | + private String variationKey; |
| 33 | + |
| 34 | + ForcedDecision(@Nonnull String flagKey, String ruleKey, @Nonnull String variationKey) { |
| 35 | + this.flagKey = flagKey; |
| 36 | + this.ruleKey = ruleKey; |
| 37 | + this.variationKey = variationKey; |
| 38 | + } |
| 39 | + |
| 40 | + public String getFlagKey() { return flagKey; } |
| 41 | + public String getRuleKey() { return ruleKey; } |
| 42 | + public String getVariationKey() { return variationKey; } |
| 43 | + } |
| 44 | + |
| 45 | + // flagKeys mapped to ruleKeys mapped to forcedDecisions |
| 46 | + Map<String, Map<String, ForcedDecision>> forcedDecisionsMap = new HashMap<>(); |
| 47 | + Map<String, ForcedDecision> forcedDecisionsMapWithNoRuleKey = new HashMap<>(); |
| 48 | + |
32 | 49 | @Nonnull |
33 | 50 | private final String userId; |
34 | 51 |
|
@@ -172,6 +189,206 @@ public void trackEvent(@Nonnull String eventName) throws UnknownEventTypeExcepti |
172 | 189 | trackEvent(eventName, Collections.emptyMap()); |
173 | 190 | } |
174 | 191 |
|
| 192 | + /** |
| 193 | + * |
| 194 | + * @param flagKey The flag key for the forced decision |
| 195 | + * @param variationKey The variation key for the forced decision |
| 196 | + * @return Returns a boolean, True if successfully set, otherwise false |
| 197 | + */ |
| 198 | + public Boolean setForcedDecision(@Nonnull String flagKey, @Nonnull String variationKey) { |
| 199 | + return setForcedDecision(flagKey, null, variationKey); |
| 200 | + } |
| 201 | + |
| 202 | + /** |
| 203 | + * Set a forced decision |
| 204 | + * |
| 205 | + * @param flagKey The flag key for the forced decision |
| 206 | + * @param ruleKey The rule key for the forced decision |
| 207 | + * @param variationKey The variation key for the forced decision |
| 208 | + * @return Returns a boolean, Ture if successfully set, otherwise false |
| 209 | + */ |
| 210 | + public Boolean setForcedDecision(@Nonnull String flagKey, String ruleKey, @Nonnull String variationKey) { |
| 211 | + if (optimizely.getOptimizelyConfig() == null) { |
| 212 | + logger.error("Optimizely SDK not ready."); |
| 213 | + return false; |
| 214 | + } |
| 215 | + |
| 216 | + if (ruleKey == null) { |
| 217 | + // If the ruleKey is null, we will populate/update the appropriate map |
| 218 | + if (forcedDecisionsMapWithNoRuleKey.get(flagKey) != null) { |
| 219 | + forcedDecisionsMapWithNoRuleKey.get(flagKey).variationKey = variationKey; |
| 220 | + } else { |
| 221 | + forcedDecisionsMapWithNoRuleKey.put(flagKey, new ForcedDecision(flagKey, null, variationKey)); |
| 222 | + } |
| 223 | + } else { |
| 224 | + // If the flagKey and ruleKey are already present, set the updated variationKey |
| 225 | + if (forcedDecisionsMap.containsKey(flagKey)) { |
| 226 | + if (forcedDecisionsMap.get(flagKey).containsKey(ruleKey)) { |
| 227 | + forcedDecisionsMap.get(flagKey).get(ruleKey).variationKey = variationKey; |
| 228 | + } else { |
| 229 | + forcedDecisionsMap.get(flagKey).put(ruleKey, new ForcedDecision(flagKey, ruleKey, variationKey)); |
| 230 | + } |
| 231 | + } else { |
| 232 | + Map<String, ForcedDecision> forcedDecision = new HashMap<>(); |
| 233 | + forcedDecision.put(ruleKey, new ForcedDecision(flagKey, ruleKey, variationKey)); |
| 234 | + forcedDecisionsMap.put(flagKey, forcedDecision); |
| 235 | + } |
| 236 | + } |
| 237 | + |
| 238 | + return true; |
| 239 | + } |
| 240 | + |
| 241 | + /** |
| 242 | + * |
| 243 | + * @param flagKey The flag key for the forced decision |
| 244 | + * @return Returns a variationKey for a given forced decision |
| 245 | + */ |
| 246 | + public String getForcedDecision(@Nonnull String flagKey) { |
| 247 | + return getForcedDecision(flagKey, null); |
| 248 | + } |
| 249 | + |
| 250 | + /** |
| 251 | + * Get a forced decision |
| 252 | + * |
| 253 | + * @param flagKey The flag key for the forced decision |
| 254 | + * @param ruleKey The rule key for the forced decision |
| 255 | + * @return Returns a variationKey for a given forced decision |
| 256 | + */ |
| 257 | + public String getForcedDecision(@Nonnull String flagKey, String ruleKey) { |
| 258 | + if (optimizely.getOptimizelyConfig() == null) { |
| 259 | + logger.error("Optimizely SDK not ready."); |
| 260 | + return null; |
| 261 | + } |
| 262 | + return findForcedDecision(flagKey, ruleKey); |
| 263 | + } |
| 264 | + |
| 265 | + /** |
| 266 | + * Finds a forced decision |
| 267 | + * |
| 268 | + * @param flagKey The flag key for the forced decision |
| 269 | + * @param ruleKey The rule key for the forced decision |
| 270 | + * @return Returns a variationKey relating to the found forced decision, otherwise null |
| 271 | + */ |
| 272 | + public String findForcedDecision(@Nonnull String flagKey, String ruleKey) { |
| 273 | + String variationKey = null; |
| 274 | + if (ruleKey != null) { |
| 275 | + if (forcedDecisionsMap.size() > 0 && forcedDecisionsMap.containsKey(flagKey)) { |
| 276 | + if (forcedDecisionsMap.get(flagKey).containsKey(ruleKey)) { |
| 277 | + variationKey = forcedDecisionsMap.get(flagKey).get(ruleKey).getVariationKey(); |
| 278 | + } |
| 279 | + } |
| 280 | + } else { |
| 281 | + if (forcedDecisionsMapWithNoRuleKey.size() > 0 && forcedDecisionsMapWithNoRuleKey.containsKey(flagKey)) { |
| 282 | + variationKey = forcedDecisionsMapWithNoRuleKey.get(flagKey).getVariationKey(); |
| 283 | + } |
| 284 | + } |
| 285 | + return variationKey; |
| 286 | + } |
| 287 | + |
| 288 | + /** |
| 289 | + * |
| 290 | + * @param flagKey The flag key in the forced decision |
| 291 | + * @return Returns a boolean of true if successful, otherwise false |
| 292 | + */ |
| 293 | + public boolean removeForcedDecision(@Nonnull String flagKey) { |
| 294 | + return removeForcedDecision(flagKey, null); |
| 295 | + } |
| 296 | + |
| 297 | + /** |
| 298 | + * Remove a forced decision |
| 299 | + * |
| 300 | + * @param flagKey The flag key for the forced decision |
| 301 | + * @param ruleKey The rule key for the forced decision |
| 302 | + * @return Returns a boolean, true if successfully removed, otherwise false |
| 303 | + */ |
| 304 | + public boolean removeForcedDecision(@Nonnull String flagKey, String ruleKey) { |
| 305 | + if (optimizely.getOptimizelyConfig() == null) { |
| 306 | + logger.error("Optimizely SDK not ready."); |
| 307 | + return false; |
| 308 | + } |
| 309 | + if (ruleKey != null) { |
| 310 | + try { |
| 311 | + forcedDecisionsMap.get(flagKey).remove(ruleKey); |
| 312 | + if (forcedDecisionsMap.get(flagKey).size() == 0) { |
| 313 | + forcedDecisionsMap.remove(flagKey); |
| 314 | + } |
| 315 | + return true; |
| 316 | + } catch (Exception e) { |
| 317 | + logger.error("Forced Decision does not exist to remove - " + e); |
| 318 | + } |
| 319 | + } else { |
| 320 | + try { |
| 321 | + forcedDecisionsMapWithNoRuleKey.remove(flagKey); |
| 322 | + return true; |
| 323 | + } catch (Exception e) { |
| 324 | + logger.error("Forced Decision does not exist to remove - " + e); |
| 325 | + } |
| 326 | + } |
| 327 | + |
| 328 | + return false; |
| 329 | + } |
| 330 | + |
| 331 | + /** |
| 332 | + * Remove all forced decisions |
| 333 | + * |
| 334 | + * @return Returns a boolean, True if successfully, otherwise false |
| 335 | + */ |
| 336 | + public boolean removeAllForcedDecisions() { |
| 337 | + if (optimizely.getProjectConfig() == null) { |
| 338 | + logger.error("Optimizely SDK not ready."); |
| 339 | + return false; |
| 340 | + } |
| 341 | + // Clear both maps for with and without ruleKey |
| 342 | + forcedDecisionsMap.clear(); |
| 343 | + forcedDecisionsMapWithNoRuleKey.clear(); |
| 344 | + return true; |
| 345 | + } |
| 346 | + |
| 347 | + /** |
| 348 | + * Find a validated forced decision |
| 349 | + * |
| 350 | + * @param flagKey The flag key for the forced decision |
| 351 | + * @return Returns a DecisionResponse structure of type Variation, otherwise null with reasons |
| 352 | + */ |
| 353 | + public DecisionResponse<Variation> findValidatedForcedDecision(@Nonnull String flagKey) { |
| 354 | + return findValidatedForcedDecision(flagKey, null); |
| 355 | + } |
| 356 | + |
| 357 | + /** |
| 358 | + * Find a validated forced decision |
| 359 | + * |
| 360 | + * @param flagKey The flag key for a forced decision |
| 361 | + * @param ruleKey The rule key for a forced decision |
| 362 | + * @return Returns a DecisionResponse structure of type Variation, otherwise null result with reasons |
| 363 | + */ |
| 364 | + public DecisionResponse<Variation> findValidatedForcedDecision(@Nonnull String flagKey, String ruleKey) { |
| 365 | + DecisionReasons reasons = DefaultDecisionReasons.newInstance(); |
| 366 | + // TODO - Move all info strings to a single class to be called rather than hardcoded in functions |
| 367 | + String variationKey = findForcedDecision(flagKey, ruleKey); |
| 368 | + if (variationKey != null) { |
| 369 | + Variation variation = optimizely.getFlagVariationByKey(flagKey, variationKey); |
| 370 | + String strRuleKey = ruleKey != null ? ruleKey : "null"; |
| 371 | + if (variation != null) { |
| 372 | + String info = "Variation " + variationKey |
| 373 | + + " is mapped to flag: " + flagKey |
| 374 | + + " and rule: " + strRuleKey |
| 375 | + + " and user: " + userId |
| 376 | + + " in the forced decision map."; |
| 377 | + logger.debug(info); |
| 378 | + reasons.addInfo(info); |
| 379 | + return new DecisionResponse(variation, reasons); |
| 380 | + } else { |
| 381 | + String info = "Invalid variation is mapped to flag: " + flagKey |
| 382 | + + " and rule: " + strRuleKey |
| 383 | + + " and user: " + userId |
| 384 | + + " forced decision map."; |
| 385 | + logger.debug(info); |
| 386 | + reasons.addInfo(info); |
| 387 | + } |
| 388 | + } |
| 389 | + return new DecisionResponse<>(null, reasons); |
| 390 | + } |
| 391 | + |
175 | 392 | // Utils |
176 | 393 |
|
177 | 394 | @Override |
|
0 commit comments