1818
1919import com .optimizely .ab .annotations .VisibleForTesting ;
2020import com .optimizely .ab .bucketing .Bucketer ;
21- import com .optimizely .ab .bucketing .UserExperimentRecord ;
21+ import com .optimizely .ab .bucketing .UserProfile ;
2222import com .optimizely .ab .config .Attribute ;
2323import com .optimizely .ab .config .EventType ;
2424import com .optimizely .ab .config .Experiment ;
25+ import com .optimizely .ab .config .LiveVariable ;
26+ import com .optimizely .ab .config .LiveVariableUsageInstance ;
2527import com .optimizely .ab .config .ProjectConfig ;
2628import com .optimizely .ab .config .Variation ;
2729import com .optimizely .ab .config .parser .ConfigParseException ;
@@ -102,7 +104,7 @@ private Optimizely(@Nonnull ProjectConfig projectConfig,
102104
103105 // Do work here that should be done once per Optimizely lifecycle
104106 @ VisibleForTesting void initialize () {
105- bucketer .cleanUserExperimentRecords ();
107+ bucketer .cleanUserProfiles ();
106108 }
107109
108110 //======== activate calls ========//
@@ -253,12 +255,136 @@ private void track(@Nonnull String eventName,
253255 }
254256 }
255257
258+ //======== live variable getters ========//
259+
260+ public @ Nullable String getVariableString (@ Nonnull String variableKey ,
261+ boolean activateExperiment ,
262+ @ Nonnull String userId ) throws UnknownLiveVariableException {
263+ return getVariableString (variableKey , activateExperiment , userId , Collections .<String , String >emptyMap ());
264+ }
265+
266+ public @ Nullable String getVariableString (@ Nonnull String variableKey ,
267+ boolean activateExperiment ,
268+ @ Nonnull String userId ,
269+ @ Nonnull Map <String , String > attributes )
270+ throws UnknownLiveVariableException {
271+
272+ LiveVariable variable = getLiveVariableOrThrow (projectConfig , variableKey );
273+ if (variable == null ) {
274+ return null ;
275+ }
276+
277+ List <Experiment > experimentsUsingLiveVariable =
278+ projectConfig .getLiveVariableIdToExperimentsMapping ().get (variable .getId ());
279+ Map <String , Map <String , LiveVariableUsageInstance >> variationToLiveVariableUsageInstanceMapping =
280+ projectConfig .getVariationToLiveVariableUsageInstanceMapping ();
281+
282+ if (experimentsUsingLiveVariable == null ) {
283+ logger .warn ("No experiment is using variable \" {}\" ." , variable .getKey ());
284+ return variable .getDefaultValue ();
285+ }
286+
287+ for (Experiment experiment : experimentsUsingLiveVariable ) {
288+ Variation variation ;
289+ if (activateExperiment ) {
290+ variation = activate (experiment , userId , attributes );
291+ } else {
292+ variation = getVariation (experiment , userId , attributes );
293+ }
294+
295+ if (variation != null ) {
296+ LiveVariableUsageInstance usageInstance =
297+ variationToLiveVariableUsageInstanceMapping .get (variation .getId ()).get (variable .getId ());
298+ return usageInstance .getValue ();
299+ }
300+ }
301+
302+ return variable .getDefaultValue ();
303+ }
304+
305+ public @ Nullable Boolean getVariableBoolean (@ Nonnull String variableKey ,
306+ boolean activateExperiment ,
307+ @ Nonnull String userId ) throws UnknownLiveVariableException {
308+ return getVariableBoolean (variableKey , activateExperiment , userId , Collections .<String , String >emptyMap ());
309+ }
310+
311+ public @ Nullable Boolean getVariableBoolean (@ Nonnull String variableKey ,
312+ boolean activateExperiment ,
313+ @ Nonnull String userId ,
314+ @ Nonnull Map <String , String > attributes )
315+ throws UnknownLiveVariableException {
316+
317+ String variableValueString = getVariableString (variableKey , activateExperiment , userId , attributes );
318+ if (variableValueString != null ) {
319+ return Boolean .parseBoolean (variableValueString );
320+ }
321+
322+ return null ;
323+ }
324+
325+ public @ Nullable Integer getVariableInteger (@ Nonnull String variableKey ,
326+ boolean activateExperiment ,
327+ @ Nonnull String userId ) throws UnknownLiveVariableException {
328+ return getVariableInteger (variableKey , activateExperiment , userId , Collections .<String , String >emptyMap ());
329+ }
330+
331+ public @ Nullable Integer getVariableInteger (@ Nonnull String variableKey ,
332+ boolean activateExperiment ,
333+ @ Nonnull String userId ,
334+ @ Nonnull Map <String , String > attributes )
335+ throws UnknownLiveVariableException {
336+
337+ String variableValueString = getVariableString (variableKey , activateExperiment , userId , attributes );
338+ if (variableValueString != null ) {
339+ try {
340+ return Integer .parseInt (variableValueString );
341+ } catch (NumberFormatException e ) {
342+ logger .error ("Variable value \" {}\" for live variable \" {}\" is not an integer." , variableValueString ,
343+ variableKey );
344+ }
345+ }
346+
347+ return null ;
348+ }
349+
350+ public @ Nullable Float getVariableFloat (@ Nonnull String variableKey ,
351+ boolean activateExperiment ,
352+ @ Nonnull String userId ) throws UnknownLiveVariableException {
353+ return getVariableFloat (variableKey , activateExperiment , userId , Collections .<String , String >emptyMap ());
354+ }
355+
356+ public @ Nullable Float getVariableFloat (@ Nonnull String variableKey ,
357+ boolean activateExperiment ,
358+ @ Nonnull String userId ,
359+ @ Nonnull Map <String , String > attributes )
360+ throws UnknownLiveVariableException {
361+
362+ String variableValueString = getVariableString (variableKey , activateExperiment , userId , attributes );
363+ if (variableValueString != null ) {
364+ try {
365+ return Float .parseFloat (variableValueString );
366+ } catch (NumberFormatException e ) {
367+ logger .error ("Variable value \" {}\" for live variable \" {}\" is not a float." , variableValueString ,
368+ variableKey );
369+ }
370+ }
371+
372+ return null ;
373+ }
374+
256375 //======== getVariation calls ========//
257376
258377 public @ Nullable Variation getVariation (@ Nonnull Experiment experiment ,
259378 @ Nonnull String userId ) throws UnknownExperimentException {
260379
261- return getVariation (getProjectConfig (), experiment , Collections .<String , String >emptyMap (), userId );
380+ return getVariation (experiment , userId , Collections .<String , String >emptyMap ());
381+ }
382+
383+ public @ Nullable Variation getVariation (@ Nonnull Experiment experiment ,
384+ @ Nonnull String userId ,
385+ @ Nonnull Map <String , String > attributes ) throws UnknownExperimentException {
386+
387+ return getVariation (getProjectConfig (), experiment , attributes , userId );
262388 }
263389
264390 public @ Nullable Variation getVariation (@ Nonnull String experimentKey ,
@@ -372,6 +498,36 @@ private EventType getEventTypeOrThrow(ProjectConfig projectConfig, String eventN
372498 return eventType ;
373499 }
374500
501+ /**
502+ * Helper method to retrieve the {@link LiveVariable} for the given variable key.
503+ * If {@link RaiseExceptionErrorHandler} is provided, either a live variable is returned, or an exception is
504+ * thrown.
505+ * If {@link NoOpErrorHandler} is used, either a live variable or {@code null} is returned.
506+ *
507+ * @param projectConfig the current project config
508+ * @param variableKey the key for the live variable being retrieved from the current project config
509+ * @return the live variable to retrieve for the given variable key
510+ *
511+ * @throws UnknownLiveVariableException if there are no event types in the current project config with the given
512+ * name
513+ */
514+ private LiveVariable getLiveVariableOrThrow (ProjectConfig projectConfig , String variableKey )
515+ throws UnknownLiveVariableException {
516+
517+ LiveVariable liveVariable = projectConfig
518+ .getLiveVariableKeyMapping ()
519+ .get (variableKey );
520+
521+ if (liveVariable == null ) {
522+ String unknownLiveVariableKeyError =
523+ String .format ("Live variable \" %s\" is not in the datafile." , variableKey );
524+ logger .error (unknownLiveVariableKeyError );
525+ errorHandler .handleError (new UnknownLiveVariableException (unknownLiveVariableKeyError ));
526+ }
527+
528+ return liveVariable ;
529+ }
530+
375531 /**
376532 * Helper method to verify that the given attributes map contains only keys that are present in the
377533 * {@link ProjectConfig}.
@@ -420,6 +576,7 @@ private boolean validateUserId(String userId) {
420576
421577 return true ;
422578 }
579+
423580 //======== Builder ========//
424581
425582 public static Builder builder (@ Nonnull String datafile ,
@@ -439,7 +596,7 @@ public static class Builder {
439596
440597 private String datafile ;
441598 private Bucketer bucketer ;
442- private UserExperimentRecord userExperimentRecord ;
599+ private UserProfile userProfile ;
443600 private ErrorHandler errorHandler ;
444601 private EventHandler eventHandler ;
445602 private EventBuilder eventBuilder ;
@@ -458,8 +615,8 @@ public Builder withErrorHandler(ErrorHandler errorHandler) {
458615 return this ;
459616 }
460617
461- public Builder withUserExperimentRecord ( UserExperimentRecord userExperimentRecord ) {
462- this .userExperimentRecord = userExperimentRecord ;
618+ public Builder withUserProfile ( UserProfile userProfile ) {
619+ this .userProfile = userProfile ;
463620 return this ;
464621 }
465622
@@ -496,7 +653,7 @@ public Optimizely build() throws ConfigParseException {
496653
497654 // use the default bucketer and event builder, if no overrides were provided
498655 if (bucketer == null ) {
499- bucketer = new Bucketer (projectConfig , userExperimentRecord );
656+ bucketer = new Bucketer (projectConfig , userProfile );
500657 }
501658
502659 if (clientEngine == null ) {
@@ -508,7 +665,7 @@ public Optimizely build() throws ConfigParseException {
508665 }
509666
510667 if (eventBuilder == null ) {
511- if (projectConfig .getVersion ().equals (ProjectConfig .V1 )) {
668+ if (projectConfig .getVersion ().equals (ProjectConfig .Version . V1 . toString () )) {
512669 eventBuilder = new EventBuilderV1 ();
513670 } else {
514671 eventBuilder = new EventBuilderV2 (clientEngine , clientVersion );
0 commit comments