From 20fc31ffbd3b47f0d42b22a6d0ebd76024877620 Mon Sep 17 00:00:00 2001 From: Jae Kim Date: Tue, 17 Nov 2020 16:03:00 -0800 Subject: [PATCH 1/8] add temporary mavenLocal for local dependency to java-sdk SNAPSHOT --- build.gradle | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/build.gradle b/build.gradle index 7a4ee3954..77387f84a 100644 --- a/build.gradle +++ b/build.gradle @@ -45,11 +45,11 @@ buildscript { allprojects { repositories { + // Uncomment the next line to use maven locally + mavenLocal() jcenter() google() mavenCentral() - // Uncomment the next line to use maven locally - //mavenLocal() } } @@ -58,7 +58,7 @@ ext { build_tools_version = "29.0.3" min_sdk_version = 14 target_sdk_version = 29 - java_core_ver = "3.6.0" + java_core_ver = "3.7.0-SNAPSHOT" android_logger_ver = "1.3.6" jacksonversion= "2.11.2" support_annotations_ver = "24.2.1" From 254e79e8dab9a6659ab9c379bbeaddc6e3d21656 Mon Sep 17 00:00:00 2001 From: Jae Kim Date: Wed, 18 Nov 2020 13:25:17 -0800 Subject: [PATCH 2/8] add decide-api APIs --- .../ab/android/sdk/OptimizelyClientTest.java | 75 +++++++++++++++++++ .../ab/android/sdk/OptimizelyManagerTest.java | 22 +++--- .../ab/android/sdk/OptimizelyClient.java | 24 ++++++ .../ab/android/sdk/OptimizelyManager.java | 20 ++++- 4 files changed, 128 insertions(+), 13 deletions(-) diff --git a/android-sdk/src/androidTest/java/com/optimizely/ab/android/sdk/OptimizelyClientTest.java b/android-sdk/src/androidTest/java/com/optimizely/ab/android/sdk/OptimizelyClientTest.java index 37951b85f..df6dec0b9 100644 --- a/android-sdk/src/androidTest/java/com/optimizely/ab/android/sdk/OptimizelyClientTest.java +++ b/android-sdk/src/androidTest/java/com/optimizely/ab/android/sdk/OptimizelyClientTest.java @@ -17,12 +17,14 @@ package com.optimizely.ab.android.sdk; import android.content.Context; +import android.graphics.Path; import android.support.test.InstrumentationRegistry; import com.google.gson.Gson; import com.google.gson.JsonElement; import com.google.gson.JsonParser; import com.optimizely.ab.Optimizely; +import com.optimizely.ab.OptimizelyUserContext; import com.optimizely.ab.android.event_handler.DefaultEventHandler; import com.optimizely.ab.bucketing.Bucketer; import com.optimizely.ab.bucketing.DecisionService; @@ -42,8 +44,12 @@ import com.optimizely.ab.notification.TrackNotificationListener; import com.optimizely.ab.notification.UpdateConfigNotification; import com.optimizely.ab.optimizelyconfig.OptimizelyConfig; +import com.optimizely.ab.optimizelydecision.OptimizelyDecideOption; +import com.optimizely.ab.optimizelydecision.OptimizelyDecision; import com.optimizely.ab.optimizelyjson.OptimizelyJSON; +import junit.framework.TestCase; + import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; @@ -2151,6 +2157,75 @@ public void testAddLogEventNotificationHandlerWithInvalidOptimizely() { .getNotificationManager(LogEvent.class).remove(notificationId)); } + // OptimizelyUserContext + Decide API + + @Test + public void testCreateUserContext() { + OptimizelyClient optimizelyClient = new OptimizelyClient(optimizely, logger); + OptimizelyUserContext userContext = optimizelyClient.createUserContext(GENERIC_USER_ID); + assertEquals(userContext.getUserId(), GENERIC_USER_ID); + assert(userContext.getAttributes().isEmpty()); + } + + @Test + public void testCreateUserContext_withAttributes() { + Map attributes = Collections.singletonMap("house", "Gryffindor"); + + OptimizelyClient optimizelyClient = new OptimizelyClient(optimizely, logger); + OptimizelyUserContext userContext = optimizelyClient.createUserContext(GENERIC_USER_ID, attributes); + assertEquals(userContext.getUserId(), GENERIC_USER_ID); + assertEquals(userContext.getAttributes(), attributes); + } + +// @Test +// // this should be enough to validate connection to the core java-sdk +// public void testDecide() { +// assumeTrue(datafileVersion == Integer.parseInt(ProjectConfig.Version.V4.toString())); +// +// String flagKey = "integer_single_variable_feature"; +// Map attributes = Collections.singletonMap("house", "Gryffindor"); +// +// OptimizelyClient optimizelyClient = new OptimizelyClient(optimizely, logger); +// OptimizelyUserContext userContext = optimizelyClient.createUserContext(GENERIC_USER_ID, attributes); +// +// +// List options = Arrays.asList(OptimizelyDecideOption.INCLUDE_REASONS); +// +// OptimizelyDecision decision = userContext.decide(flagKey, options); +// +// OptimizelyJSON variablesExpected = new OptimizelyJSON(Collections.singletonMap("integer_variable", 7)); +// +// assertEquals(decision.getVariationKey(), null); +// assertFalse(decision.getEnabled()); +// assertEquals(decision.getVariables().toMap(), variablesExpected.toMap()); +// assertEquals(decision.getRuleKey(), null); +// assertEquals(decision.getFlagKey(), flagKey); +// assertEquals(decision.getUserContext(), userContext); +// assertTrue(decision.getReasons().isEmpty()); +// } +// +// @Test +// // this should be enough to validate connection to the core java-sdk +// public void testDecide_defaultDecideOptions() throws IOException { +// assumeTrue(datafileVersion == Integer.parseInt(ProjectConfig.Version.V4.toString())); +// +// List defaultDecideOptions = Arrays.asList(OptimizelyDecideOption.EXCLUDE_VARIABLES); +// String datafile = loadRawResource(InstrumentationRegistry.getTargetContext(),R.raw.validprojectconfigv4); +// +// Context context = InstrumentationRegistry.getTargetContext(); +// OptimizelyManager optimizelyManager = OptimizelyManager.builder(testProjectId) +// .withDefaultDecideOptions(defaultDecideOptions) +// .build(context); +// optimizelyManager.initialize(context, datafile); +// +// OptimizelyClient optimizelyClient = optimizelyManager.getOptimizely(); +// OptimizelyUserContext userContext = optimizelyClient.createUserContext(GENERIC_USER_ID); +// OptimizelyDecision decision = userContext.decide("invalid-key"); +// assertNull(decision); +// } + + // Utils + private boolean compareJsonStrings(String str1, String str2) { JsonParser parser = new JsonParser(); diff --git a/android-sdk/src/androidTest/java/com/optimizely/ab/android/sdk/OptimizelyManagerTest.java b/android-sdk/src/androidTest/java/com/optimizely/ab/android/sdk/OptimizelyManagerTest.java index 76a36490c..f50936bcc 100644 --- a/android-sdk/src/androidTest/java/com/optimizely/ab/android/sdk/OptimizelyManagerTest.java +++ b/android-sdk/src/androidTest/java/com/optimizely/ab/android/sdk/OptimizelyManagerTest.java @@ -168,7 +168,7 @@ public void initializeSyncWithEnvironment() { EventHandler eventHandler = mock(DefaultEventHandler.class); EventProcessor eventProcessor = mock(EventProcessor.class); OptimizelyManager optimizelyManager = new OptimizelyManager(testProjectId, testSdkKey, null, logger, 3600L, datafileHandler, null, 3600L, - eventHandler, eventProcessor, null, null); + eventHandler, eventProcessor, null, null, null); /* * Scenario#1: when datafile is not Empty * Scenario#2: when datafile is Empty @@ -227,7 +227,7 @@ public void initializeAsyncWithEnvironment() { EventHandler eventHandler = mock(DefaultEventHandler.class); EventProcessor eventProcessor = mock(EventProcessor.class); final OptimizelyManager optimizelyManager = new OptimizelyManager(testProjectId, testSdkKey, null, logger, 3600L, datafileHandler, null, 3600L, - eventHandler, eventProcessor, null, null); + eventHandler, eventProcessor, null, null, null); /* * Scenario#1: when datafile is not Empty @@ -517,7 +517,7 @@ public void initializeSyncWithUpdateOnNewDatafileDisabled() { Context context = InstrumentationRegistry.getTargetContext(); OptimizelyManager manager = new OptimizelyManager(testProjectId, testSdkKey, null, logger, pollingInterval, datafileHandler, null, 0, - null, null, null, null); + null, null, null, null, null); doAnswer( new Answer() { @@ -550,7 +550,7 @@ public void initializeSyncWithUpdateOnNewDatafileEnabled() { Context context = InstrumentationRegistry.getTargetContext(); OptimizelyManager manager = new OptimizelyManager(testProjectId, testSdkKey, null, logger, pollingInterval, datafileHandler, null, 0, - null, null, null, null); + null, null, null, null, null); doAnswer( new Answer() { @@ -583,7 +583,7 @@ public void initializeSyncWithDownloadToCacheDisabled() { Context context = InstrumentationRegistry.getTargetContext(); OptimizelyManager manager = new OptimizelyManager(testProjectId, testSdkKey, null, logger, pollingInterval, datafileHandler, null, 0, - null, null, null, null); + null, null, null, null, null); doAnswer( new Answer() { @@ -616,7 +616,7 @@ public void initializeSyncWithUpdateOnNewDatafileDisabledWithPeriodicPollingEnab Context context = InstrumentationRegistry.getTargetContext(); OptimizelyManager manager = new OptimizelyManager(testProjectId, testSdkKey, null, logger, pollingInterval, datafileHandler, null, 0, - null, null, null, null); + null, null, null, null, null); doAnswer( (Answer) invocation -> { @@ -648,7 +648,7 @@ public void initializeSyncWithUpdateOnNewDatafileEnabledWithPeriodicPollingEnabl Context context = InstrumentationRegistry.getTargetContext(); OptimizelyManager manager = new OptimizelyManager(testProjectId, testSdkKey, null, logger, pollingInterval, datafileHandler, null, 0, - null, null, null, null); + null, null, null, null, null); doAnswer( new Answer() { @@ -681,7 +681,7 @@ public void initializeSyncWithUpdateOnNewDatafileDisabledWithPeriodicPollingDisa Context context = InstrumentationRegistry.getTargetContext(); OptimizelyManager manager = new OptimizelyManager(testProjectId, testSdkKey, null, logger, pollingInterval, datafileHandler, null, 0, - null, null, null, null); + null, null, null, null, null); doAnswer( new Answer() { @@ -715,7 +715,7 @@ public void initializeSyncWithUpdateOnNewDatafileEnabledWithPeriodicPollingDisab Context context = InstrumentationRegistry.getTargetContext(); OptimizelyManager manager = new OptimizelyManager(testProjectId, testSdkKey, null, logger, pollingInterval, datafileHandler, null, 0, - null, null, null, null); + null, null, null, null, null); doAnswer( new Answer() { @@ -748,7 +748,7 @@ public void initializeSyncWithResourceDatafileNoCache() { Context context = InstrumentationRegistry.getTargetContext(); OptimizelyManager manager = spy(new OptimizelyManager(testProjectId, testSdkKey, null, logger, pollingInterval, datafileHandler, null, 0, - null, null, null, null)); + null, null, null, null, null)); datafileHandler.removeSavedDatafile(context, manager.getDatafileConfig()); OptimizelyClient client = manager.initialize(context, R.raw.datafile, downloadToCache, updateConfigOnNewDatafile); @@ -765,7 +765,7 @@ public void initializeSyncWithResourceDatafileNoCacheWithDefaultParams() { Context context = InstrumentationRegistry.getTargetContext(); OptimizelyManager manager = spy(new OptimizelyManager(testProjectId, testSdkKey, null, logger, pollingInterval, datafileHandler, null, 0, - null, null, null, null)); + null, null, null, null, null)); datafileHandler.removeSavedDatafile(context, manager.getDatafileConfig()); OptimizelyClient client = manager.initialize(context, R.raw.datafile); diff --git a/android-sdk/src/main/java/com/optimizely/ab/android/sdk/OptimizelyClient.java b/android-sdk/src/main/java/com/optimizely/ab/android/sdk/OptimizelyClient.java index 4eabd009d..47be51573 100644 --- a/android-sdk/src/main/java/com/optimizely/ab/android/sdk/OptimizelyClient.java +++ b/android-sdk/src/main/java/com/optimizely/ab/android/sdk/OptimizelyClient.java @@ -21,6 +21,7 @@ import android.support.annotation.Nullable; import com.optimizely.ab.Optimizely; +import com.optimizely.ab.OptimizelyUserContext; import com.optimizely.ab.UnknownEventTypeException; import com.optimizely.ab.config.ProjectConfig; import com.optimizely.ab.config.Variation; @@ -780,6 +781,29 @@ public OptimizelyConfig getOptimizelyConfig() { } } + /** + * Create a context of the user for which decision APIs will be called. + * + * A user context will be created successfully even when the SDK is not fully configured yet. + * + * @param userId The user ID to be used for bucketing. + * @param attributes: A map of attribute names to current user attribute values. + * @return An OptimizelyUserContext associated with this OptimizelyClient. + */ + public OptimizelyUserContext createUserContext(@NonNull String userId, + @NonNull Map attributes) { + if (userId == null) { + logger.warn("The userId parameter must be nonnull."); + return null; + } + + return new OptimizelyUserContext(optimizely, userId, attributes); + } + + public OptimizelyUserContext createUserContext(@NonNull String userId) { + return new OptimizelyUserContext(optimizely, userId); + } + //======== Notification APIs ========// /** diff --git a/android-sdk/src/main/java/com/optimizely/ab/android/sdk/OptimizelyManager.java b/android-sdk/src/main/java/com/optimizely/ab/android/sdk/OptimizelyManager.java index 1510c9e98..3eebae2f2 100644 --- a/android-sdk/src/main/java/com/optimizely/ab/android/sdk/OptimizelyManager.java +++ b/android-sdk/src/main/java/com/optimizely/ab/android/sdk/OptimizelyManager.java @@ -21,6 +21,7 @@ import android.app.Application; import android.content.Context; import android.content.res.Resources; +import android.graphics.Path; import android.os.Build; import android.os.Bundle; import android.support.annotation.NonNull; @@ -48,13 +49,16 @@ import com.optimizely.ab.event.internal.payload.EventBatch; import com.optimizely.ab.notification.NotificationCenter; import com.optimizely.ab.notification.UpdateConfigNotification; +import com.optimizely.ab.optimizelydecision.OptimizelyDecideOption; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; import java.io.InputStream; +import java.util.Collections; import java.util.List; +import java.util.OptionalInt; import java.util.Set; import java.util.concurrent.TimeUnit; @@ -83,6 +87,8 @@ public class OptimizelyManager { @Nullable private OptimizelyStartListener optimizelyStartListener; + @NonNull private final List defaultDecideOptions; + OptimizelyManager(@Nullable String projectId, @Nullable String sdkKey, @Nullable DatafileConfig datafileConfig, @@ -94,7 +100,8 @@ public class OptimizelyManager { @NonNull EventHandler eventHandler, @Nullable EventProcessor eventProcessor, @NonNull UserProfileService userProfileService, - @NonNull NotificationCenter notificationCenter) { + @NonNull NotificationCenter notificationCenter, + @NonNull List defaultDecideOptions) { if (projectId == null && sdkKey == null) { logger.error("projectId and sdkKey are both null!"); @@ -116,6 +123,7 @@ public class OptimizelyManager { this.errorHandler = errorHandler; this.userProfileService = userProfileService; this.notificationCenter = notificationCenter; + this.defaultDecideOptions = defaultDecideOptions; } @VisibleForTesting @@ -582,6 +590,7 @@ private OptimizelyClient buildOptimizely(@NonNull Context context, @NonNull Stri builder.withUserProfileService(userProfileService); builder.withNotificationCenter(notificationCenter); + builder.withDefaultDecideOptions(defaultDecideOptions); Optimizely optimizely = builder.build(); return new OptimizelyClient(optimizely, LoggerFactory.getLogger(OptimizelyClient.class)); } @@ -713,6 +722,7 @@ public static class Builder { @Nullable private UserProfileService userProfileService = null; @Nullable private String sdkKey = null; @Nullable private DatafileConfig datafileConfig = null; + @Nullable private List defaultDecideOptions = null; @Deprecated /** @@ -868,6 +878,11 @@ public Builder withNotificationCenter(NotificationCenter notificationCenter) { return this; } + public Builder withDefaultDecideOptions(List defaultDecideOtions) { + this.defaultDecideOptions = defaultDecideOtions; + return this; + } + /** * Get a new {@link Builder} instance to create {@link OptimizelyManager} with. * @param context the application context used to create default service if not provided. @@ -939,7 +954,8 @@ public OptimizelyManager build(Context context) { eventHandler, eventProcessor, userProfileService, - notificationCenter); + notificationCenter, + defaultDecideOptions); } } } From c2e36760e4856dff543c05c6e97c01171e9bbe01 Mon Sep 17 00:00:00 2001 From: Jae Kim Date: Wed, 18 Nov 2020 14:44:09 -0800 Subject: [PATCH 3/8] add more tests for decide-api --- .../ab/android/sdk/OptimizelyClientTest.java | 129 +++++++++++------- .../ab/android/sdk/OptimizelyManager.java | 3 - .../sdk/OptimizelyManagerIntervalTest.java | 18 ++- 3 files changed, 89 insertions(+), 61 deletions(-) diff --git a/android-sdk/src/androidTest/java/com/optimizely/ab/android/sdk/OptimizelyClientTest.java b/android-sdk/src/androidTest/java/com/optimizely/ab/android/sdk/OptimizelyClientTest.java index df6dec0b9..f97a6aa6c 100644 --- a/android-sdk/src/androidTest/java/com/optimizely/ab/android/sdk/OptimizelyClientTest.java +++ b/android-sdk/src/androidTest/java/com/optimizely/ab/android/sdk/OptimizelyClientTest.java @@ -17,7 +17,6 @@ package com.optimizely.ab.android.sdk; import android.content.Context; -import android.graphics.Path; import android.support.test.InstrumentationRegistry; import com.google.gson.Gson; @@ -48,8 +47,6 @@ import com.optimizely.ab.optimizelydecision.OptimizelyDecision; import com.optimizely.ab.optimizelyjson.OptimizelyJSON; -import junit.framework.TestCase; - import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; @@ -74,10 +71,13 @@ import static junit.framework.Assert.assertNotNull; import static junit.framework.Assert.assertNull; import static junit.framework.Assert.assertTrue; +import static org.hamcrest.Matchers.anyOf; import static org.hamcrest.Matchers.hasEntry; import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertThat; import static org.junit.Assume.assumeTrue; +import static org.mockito.Matchers.anyObject; +import static org.mockito.Matchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; @@ -129,9 +129,19 @@ public OptimizelyClientTest(int datafileVersion,String datafile){ eventHandler = spy(DefaultEventHandler.getInstance(InstrumentationRegistry.getTargetContext())); optimizely = Optimizely.builder(datafile, eventHandler).build(); if(datafileVersion==3) { - when(bucketer.bucket(optimizely.getProjectConfig().getExperiments().get(0), GENERIC_USER_ID, optimizely.getProjectConfig())).thenReturn(optimizely.getProjectConfig().getExperiments().get(0).getVariations().get(0)); + when(bucketer.bucket( + eq(optimizely.getProjectConfig().getExperiments().get(0)), + eq(GENERIC_USER_ID), + eq(optimizely.getProjectConfig()), + anyObject()) + ).thenReturn(optimizely.getProjectConfig().getExperiments().get(0).getVariations().get(0)); } else { - when(bucketer.bucket(optimizely.getProjectConfig().getExperimentKeyMapping().get(FEATURE_MULTI_VARIATE_EXPERIMENT_KEY), GENERIC_USER_ID, optimizely.getProjectConfig())).thenReturn(optimizely.getProjectConfig().getExperimentKeyMapping().get(FEATURE_MULTI_VARIATE_EXPERIMENT_KEY).getVariations().get(1)); + when(bucketer.bucket( + eq(optimizely.getProjectConfig().getExperimentKeyMapping().get(FEATURE_MULTI_VARIATE_EXPERIMENT_KEY)), + eq(GENERIC_USER_ID), + eq(optimizely.getProjectConfig()), + anyObject()) + ).thenReturn(optimizely.getProjectConfig().getExperimentKeyMapping().get(FEATURE_MULTI_VARIATE_EXPERIMENT_KEY).getVariations().get(1)); } spyOnConfig(); } catch (Exception configException) { @@ -370,7 +380,7 @@ public void testGoodActivationBucketingId() { Experiment experiment = optimizelyClient.getProjectConfig().getExperimentKeyMapping().get(FEATURE_ANDROID_EXPERIMENT_KEY); attributes.put(BUCKETING_ATTRIBUTE, bucketingId); Variation v = optimizelyClient.activate(FEATURE_ANDROID_EXPERIMENT_KEY, GENERIC_USER_ID, attributes); - verify(bucketer).bucket(experiment, bucketingId, optimizely.getProjectConfig()); + verify(bucketer).bucket(eq(experiment), eq(bucketingId), eq(optimizely.getProjectConfig()), anyObject()); } @Test @@ -895,7 +905,7 @@ public void testGoodGetVariationBucketingId() { Map attributes = new HashMap<>(); attributes.put(BUCKETING_ATTRIBUTE, bucketingId); Variation v = optimizelyClient.getVariation("android_experiment_key", "userId", attributes); - verify(bucketer).bucket(experiment, bucketingId, optimizely.getProjectConfig()); + verify(bucketer).bucket(eq(experiment), eq(bucketingId), eq(optimizely.getProjectConfig()), anyObject()); } @Test @@ -2177,52 +2187,65 @@ public void testCreateUserContext_withAttributes() { assertEquals(userContext.getAttributes(), attributes); } -// @Test -// // this should be enough to validate connection to the core java-sdk -// public void testDecide() { -// assumeTrue(datafileVersion == Integer.parseInt(ProjectConfig.Version.V4.toString())); -// -// String flagKey = "integer_single_variable_feature"; -// Map attributes = Collections.singletonMap("house", "Gryffindor"); -// -// OptimizelyClient optimizelyClient = new OptimizelyClient(optimizely, logger); -// OptimizelyUserContext userContext = optimizelyClient.createUserContext(GENERIC_USER_ID, attributes); -// -// -// List options = Arrays.asList(OptimizelyDecideOption.INCLUDE_REASONS); -// -// OptimizelyDecision decision = userContext.decide(flagKey, options); -// -// OptimizelyJSON variablesExpected = new OptimizelyJSON(Collections.singletonMap("integer_variable", 7)); -// -// assertEquals(decision.getVariationKey(), null); -// assertFalse(decision.getEnabled()); -// assertEquals(decision.getVariables().toMap(), variablesExpected.toMap()); -// assertEquals(decision.getRuleKey(), null); -// assertEquals(decision.getFlagKey(), flagKey); -// assertEquals(decision.getUserContext(), userContext); -// assertTrue(decision.getReasons().isEmpty()); -// } -// -// @Test -// // this should be enough to validate connection to the core java-sdk -// public void testDecide_defaultDecideOptions() throws IOException { -// assumeTrue(datafileVersion == Integer.parseInt(ProjectConfig.Version.V4.toString())); -// -// List defaultDecideOptions = Arrays.asList(OptimizelyDecideOption.EXCLUDE_VARIABLES); -// String datafile = loadRawResource(InstrumentationRegistry.getTargetContext(),R.raw.validprojectconfigv4); -// -// Context context = InstrumentationRegistry.getTargetContext(); -// OptimizelyManager optimizelyManager = OptimizelyManager.builder(testProjectId) -// .withDefaultDecideOptions(defaultDecideOptions) -// .build(context); -// optimizelyManager.initialize(context, datafile); -// -// OptimizelyClient optimizelyClient = optimizelyManager.getOptimizely(); -// OptimizelyUserContext userContext = optimizelyClient.createUserContext(GENERIC_USER_ID); -// OptimizelyDecision decision = userContext.decide("invalid-key"); -// assertNull(decision); -// } + @Test + // this should be enough to validate connection to the core java-sdk + public void testDecide() { + assumeTrue(datafileVersion == Integer.parseInt(ProjectConfig.Version.V4.toString())); + + String flagKey = INTEGER_FEATURE_KEY; + Map attributes = Collections.singletonMap("house", "Gryffindor"); + + OptimizelyClient optimizelyClient = new OptimizelyClient(optimizely, logger); + OptimizelyUserContext userContext = optimizelyClient.createUserContext(GENERIC_USER_ID, attributes); + OptimizelyDecision decision = userContext.decide(flagKey); + OptimizelyJSON variablesExpected = new OptimizelyJSON(Collections.singletonMap("integer_variable", 2)); + + assertEquals(decision.getVariationKey(), "Feorge"); + assertTrue(decision.getEnabled()); + assertEquals(decision.getVariables().toMap(), variablesExpected.toMap()); + assertEquals(decision.getRuleKey(), FEATURE_MULTI_VARIATE_EXPERIMENT_KEY); + assertEquals(decision.getFlagKey(), flagKey); + assertEquals(decision.getUserContext(), userContext); + assertTrue(decision.getReasons().isEmpty()); + } + + @Test + // this should be enough to validate connection to the core java-sdk + public void testDecide_withoutDefaultDecideOptions() throws IOException { + assumeTrue(datafileVersion == Integer.parseInt(ProjectConfig.Version.V4.toString())); + + String datafile = loadRawResource(InstrumentationRegistry.getTargetContext(),R.raw.validprojectconfigv4); + Context context = InstrumentationRegistry.getTargetContext(); + OptimizelyManager optimizelyManager = OptimizelyManager.builder(testProjectId).build(context); + optimizelyManager.initialize(context, datafile); + + OptimizelyClient optimizelyClient = optimizelyManager.getOptimizely(); + OptimizelyUserContext userContext = optimizelyClient.createUserContext(GENERIC_USER_ID); + OptimizelyDecision decision = userContext.decide(INTEGER_FEATURE_KEY); + + assertTrue(decision.getReasons().isEmpty()); + } + + @Test + // this should be enough to validate connection to the core java-sdk + public void testDecide_withDefaultDecideOptions() throws IOException { + assumeTrue(datafileVersion == Integer.parseInt(ProjectConfig.Version.V4.toString())); + + List defaultDecideOptions = Arrays.asList(OptimizelyDecideOption.INCLUDE_REASONS); + + String datafile = loadRawResource(InstrumentationRegistry.getTargetContext(),R.raw.validprojectconfigv4); + Context context = InstrumentationRegistry.getTargetContext(); + OptimizelyManager optimizelyManager = OptimizelyManager.builder(testProjectId) + .withDefaultDecideOptions(defaultDecideOptions) + .build(context); + optimizelyManager.initialize(context, datafile); + + OptimizelyClient optimizelyClient = optimizelyManager.getOptimizely(); + OptimizelyUserContext userContext = optimizelyClient.createUserContext(GENERIC_USER_ID); + OptimizelyDecision decision = userContext.decide(INTEGER_FEATURE_KEY); + + assertTrue(decision.getReasons().size() > 0); + } // Utils diff --git a/android-sdk/src/main/java/com/optimizely/ab/android/sdk/OptimizelyManager.java b/android-sdk/src/main/java/com/optimizely/ab/android/sdk/OptimizelyManager.java index 3eebae2f2..e8d7bc5d5 100644 --- a/android-sdk/src/main/java/com/optimizely/ab/android/sdk/OptimizelyManager.java +++ b/android-sdk/src/main/java/com/optimizely/ab/android/sdk/OptimizelyManager.java @@ -21,7 +21,6 @@ import android.app.Application; import android.content.Context; import android.content.res.Resources; -import android.graphics.Path; import android.os.Build; import android.os.Bundle; import android.support.annotation.NonNull; @@ -56,9 +55,7 @@ import java.io.IOException; import java.io.InputStream; -import java.util.Collections; import java.util.List; -import java.util.OptionalInt; import java.util.Set; import java.util.concurrent.TimeUnit; diff --git a/android-sdk/src/test/java/com/optimizely/ab/android/sdk/OptimizelyManagerIntervalTest.java b/android-sdk/src/test/java/com/optimizely/ab/android/sdk/OptimizelyManagerIntervalTest.java index 50ab1e81e..93935caa1 100644 --- a/android-sdk/src/test/java/com/optimizely/ab/android/sdk/OptimizelyManagerIntervalTest.java +++ b/android-sdk/src/test/java/com/optimizely/ab/android/sdk/OptimizelyManagerIntervalTest.java @@ -35,6 +35,7 @@ import org.powermock.modules.junit4.PowerMockRunner; import org.slf4j.Logger; +import java.util.List; import java.util.concurrent.BlockingQueue; import java.util.concurrent.ExecutorService; import java.util.concurrent.TimeUnit; @@ -43,8 +44,10 @@ import static junit.framework.Assert.assertFalse; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyBoolean; +import static org.mockito.Matchers.anyList; import static org.mockito.Matchers.anyLong; import static org.mockito.Matchers.anyInt; +import static org.mockito.Matchers.anyObject; import static org.mockito.Matchers.anyString; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.mock; @@ -87,7 +90,8 @@ public void testBuildWithDatafileDownloadInterval() throws Exception { any(EventHandler.class), any(EventProcessor.class), any(UserProfileService.class), - any(NotificationCenter.class)); + any(NotificationCenter.class), + anyList()); } @Test @@ -114,7 +118,8 @@ public void testBuildWithDatafileDownloadIntervalDeprecated() throws Exception { any(EventHandler.class), any(EventProcessor.class), any(UserProfileService.class), - any(NotificationCenter.class)); + any(NotificationCenter.class), + anyList()); } @Test @@ -151,7 +156,8 @@ public void testBuildWithEventDispatchInterval() throws Exception { any(EventHandler.class), any(EventProcessor.class), any(UserProfileService.class), - any(NotificationCenter.class)); + any(NotificationCenter.class), + anyList()); } @Test @@ -190,7 +196,8 @@ public void testBuildWithEventDispatchRetryInterval() throws Exception { any(EventHandler.class), any(EventProcessor.class), any(UserProfileService.class), - any(NotificationCenter.class)); + any(NotificationCenter.class), + anyList()); } @Test @@ -227,7 +234,8 @@ public void testBuildWithEventDispatchIntervalDeprecated() throws Exception { any(EventHandler.class), any(EventProcessor.class), any(UserProfileService.class), - any(NotificationCenter.class)); + any(NotificationCenter.class), + anyList()); } } From d0ceb84f4321af8eb9704e2601140140107c4da9 Mon Sep 17 00:00:00 2001 From: Jae Kim Date: Thu, 19 Nov 2020 14:25:08 -0800 Subject: [PATCH 4/8] add samples for decide-api --- .../ab/android/test_app/SamplesForAPI.java | 67 ++++++++++++++++++- 1 file changed, 66 insertions(+), 1 deletion(-) diff --git a/test-app/src/main/java/com/optimizely/ab/android/test_app/SamplesForAPI.java b/test-app/src/main/java/com/optimizely/ab/android/test_app/SamplesForAPI.java index f9fe7a857..68f225f15 100644 --- a/test-app/src/main/java/com/optimizely/ab/android/test_app/SamplesForAPI.java +++ b/test-app/src/main/java/com/optimizely/ab/android/test_app/SamplesForAPI.java @@ -19,15 +19,80 @@ import android.content.Context; import android.util.Log; +import com.optimizely.ab.OptimizelyUserContext; import com.optimizely.ab.android.sdk.OptimizelyClient; import com.optimizely.ab.android.sdk.OptimizelyManager; import com.optimizely.ab.android.sdk.OptimizelyStartListener; import com.optimizely.ab.config.Variation; - +import com.optimizely.ab.config.parser.JsonParseException; +import com.optimizely.ab.optimizelydecision.OptimizelyDecideOption; +import com.optimizely.ab.optimizelydecision.OptimizelyDecision; +import com.optimizely.ab.optimizelyjson.OptimizelyJSON; + +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; import java.util.concurrent.TimeUnit; public class SamplesForAPI { + static public void samplesForDecide(Context context) { + List defaultDecideOptions = Arrays.asList(OptimizelyDecideOption.DISABLE_DECISION_EVENT); + + OptimizelyManager optimizelyManager = OptimizelyManager.builder() + .withSDKKey("FCnSegiEkRry9rhVMroit4") + .withDefaultDecideOptions(defaultDecideOptions) + .build(context); + OptimizelyClient optimizelyClient = optimizelyManager.initialize(context, R.raw.datafile); + + String userId = "user_123"; + Map attributes = new HashMap<>(); + attributes.put("is_logged_in", false); + attributes.put("app_version", "1.3.2"); + OptimizelyUserContext user = optimizelyClient.createUserContext(userId, attributes); + user.setAttribute("location", "NY"); + + List options = Arrays.asList(OptimizelyDecideOption.INCLUDE_REASONS); + OptimizelyDecision decision = user.decide("show_coupon", options); + + String variationKey = decision.getVariationKey(); + boolean enabled = decision.getEnabled(); + OptimizelyJSON variables = decision.getVariables(); + String vs = null; + try { + vs = variables.getValue("text_color", String.class); + } catch (JsonParseException e) { + e.printStackTrace(); + } + int vb = (int) variables.toMap().get("discount"); + String ruleKey = decision.getRuleKey(); + String flagKey = decision.getFlagKey(); + OptimizelyUserContext userContext = decision.getUserContext(); + List reasons = decision.getReasons(); + + Log.d("Samples", "decision: " + decision); + Log.d("Samples", "items: " + variationKey + " " + String.valueOf(enabled) + " " + vs + " " + String.valueOf(vb) + " " + ruleKey + " " + flagKey + " " + userContext + " " + reasons); + + List keys = Arrays.asList("show_coupon", "bg-feature"); + Map decisionsMultiple = user.decideForKeys(keys); + + OptimizelyDecision decision1 = decisionsMultiple.get(keys.get(0)); + OptimizelyDecision decision2 = decisionsMultiple.get(keys.get(1)); + Log.d("Samples", "decisionsMultiple: " + keys + " " + decision1 + " " + decision2); + + List options2 = Arrays.asList(OptimizelyDecideOption.ENABLED_FLAGS_ONLY); + Map decisionsAll = user.decideAll(options2); + + Set allKeys = decisionsAll.keySet(); + Collection allDecisions = decisionsAll.values(); + Log.d("Samples", "all keys: " + allKeys); + Log.d("Samples", "all decisions: " + allDecisions); + } + static public void samplesForInitialization(Context context) { OptimizelyManager optimizelyManager; OptimizelyClient optimizelyClient; From 64986111489c50eda6a6c95a7501643a645df06c Mon Sep 17 00:00:00 2001 From: Jae Kim Date: Thu, 19 Nov 2020 15:19:57 -0800 Subject: [PATCH 5/8] fix decide sample codes --- .../ab/android/test_app/SamplesForAPI.java | 102 ++++++++++-------- 1 file changed, 58 insertions(+), 44 deletions(-) diff --git a/test-app/src/main/java/com/optimizely/ab/android/test_app/SamplesForAPI.java b/test-app/src/main/java/com/optimizely/ab/android/test_app/SamplesForAPI.java index 68f225f15..247ea4ed2 100644 --- a/test-app/src/main/java/com/optimizely/ab/android/test_app/SamplesForAPI.java +++ b/test-app/src/main/java/com/optimizely/ab/android/test_app/SamplesForAPI.java @@ -47,50 +47,64 @@ static public void samplesForDecide(Context context) { .withSDKKey("FCnSegiEkRry9rhVMroit4") .withDefaultDecideOptions(defaultDecideOptions) .build(context); - OptimizelyClient optimizelyClient = optimizelyManager.initialize(context, R.raw.datafile); - - String userId = "user_123"; - Map attributes = new HashMap<>(); - attributes.put("is_logged_in", false); - attributes.put("app_version", "1.3.2"); - OptimizelyUserContext user = optimizelyClient.createUserContext(userId, attributes); - user.setAttribute("location", "NY"); - - List options = Arrays.asList(OptimizelyDecideOption.INCLUDE_REASONS); - OptimizelyDecision decision = user.decide("show_coupon", options); - - String variationKey = decision.getVariationKey(); - boolean enabled = decision.getEnabled(); - OptimizelyJSON variables = decision.getVariables(); - String vs = null; - try { - vs = variables.getValue("text_color", String.class); - } catch (JsonParseException e) { - e.printStackTrace(); - } - int vb = (int) variables.toMap().get("discount"); - String ruleKey = decision.getRuleKey(); - String flagKey = decision.getFlagKey(); - OptimizelyUserContext userContext = decision.getUserContext(); - List reasons = decision.getReasons(); - - Log.d("Samples", "decision: " + decision); - Log.d("Samples", "items: " + variationKey + " " + String.valueOf(enabled) + " " + vs + " " + String.valueOf(vb) + " " + ruleKey + " " + flagKey + " " + userContext + " " + reasons); - - List keys = Arrays.asList("show_coupon", "bg-feature"); - Map decisionsMultiple = user.decideForKeys(keys); - - OptimizelyDecision decision1 = decisionsMultiple.get(keys.get(0)); - OptimizelyDecision decision2 = decisionsMultiple.get(keys.get(1)); - Log.d("Samples", "decisionsMultiple: " + keys + " " + decision1 + " " + decision2); - - List options2 = Arrays.asList(OptimizelyDecideOption.ENABLED_FLAGS_ONLY); - Map decisionsAll = user.decideAll(options2); - - Set allKeys = decisionsAll.keySet(); - Collection allDecisions = decisionsAll.values(); - Log.d("Samples", "all keys: " + allKeys); - Log.d("Samples", "all decisions: " + allDecisions); + optimizelyManager.initialize(context, R.raw.datafile, optimizelyClient -> { + + // createUserContext + + String userId = "user_123"; + Map attributes = new HashMap<>(); + attributes.put("is_logged_in", false); + attributes.put("app_version", "1.3.2"); + OptimizelyUserContext user = optimizelyClient.createUserContext(userId, attributes); + // attributes can be set in this way too + user.setAttribute("location", "NY"); + + // decide + + List options = Arrays.asList(OptimizelyDecideOption.INCLUDE_REASONS); + OptimizelyDecision decision = user.decide("show_coupon", options); + + String variationKey = decision.getVariationKey(); + boolean enabled = decision.getEnabled(); + OptimizelyJSON variables = decision.getVariables(); + String vs = null; + try { + vs = variables.getValue("text_color", String.class); + } catch (JsonParseException e) { + e.printStackTrace(); + } + int vb = (int) variables.toMap().get("discount"); + String ruleKey = decision.getRuleKey(); + String flagKey = decision.getFlagKey(); + OptimizelyUserContext userContext = decision.getUserContext(); + List reasons = decision.getReasons(); + + Log.d("Samples", "decision: " + decision); + Log.d("Samples", "items: " + variationKey + " " + String.valueOf(enabled) + " " + vs + " " + String.valueOf(vb) + " " + ruleKey + " " + flagKey + " " + userContext + " " + reasons); + + // decideForKeys + + List keys = Arrays.asList("show_coupon", "bg-feature"); + Map decisionsMultiple = user.decideForKeys(keys); + + OptimizelyDecision decision1 = decisionsMultiple.get(keys.get(0)); + OptimizelyDecision decision2 = decisionsMultiple.get(keys.get(1)); + Log.d("Samples", "decisionsMultiple: " + keys + " " + decision1 + " " + decision2); + + // decideAll + + List options2 = Arrays.asList(OptimizelyDecideOption.ENABLED_FLAGS_ONLY); + Map decisionsAll = user.decideAll(options2); + + Set allKeys = decisionsAll.keySet(); + Collection allDecisions = decisionsAll.values(); + Log.d("Samples", "all keys: " + allKeys); + Log.d("Samples", "all decisions: " + allDecisions); + + // trackEvent + + user.trackEvent("sample_conversion"); + }); } static public void samplesForInitialization(Context context) { From f174e1b354ec77546a092e27b2ce19e02efbd534 Mon Sep 17 00:00:00 2001 From: Jae Kim Date: Mon, 11 Jan 2021 16:19:23 -0800 Subject: [PATCH 6/8] fix tests for java-sdk reasons refactoring --- .../ab/android/sdk/OptimizelyClientTest.java | 25 ++++++++++--------- .../ab/android/sdk/OptimizelyClient.java | 12 +++++---- build.gradle | 6 ++--- .../ab/android/test_app/SamplesForAPI.java | 2 ++ 4 files changed, 25 insertions(+), 20 deletions(-) diff --git a/android-sdk/src/androidTest/java/com/optimizely/ab/android/sdk/OptimizelyClientTest.java b/android-sdk/src/androidTest/java/com/optimizely/ab/android/sdk/OptimizelyClientTest.java index f97a6aa6c..cf859f4dc 100644 --- a/android-sdk/src/androidTest/java/com/optimizely/ab/android/sdk/OptimizelyClientTest.java +++ b/android-sdk/src/androidTest/java/com/optimizely/ab/android/sdk/OptimizelyClientTest.java @@ -43,6 +43,7 @@ import com.optimizely.ab.notification.TrackNotificationListener; import com.optimizely.ab.notification.UpdateConfigNotification; import com.optimizely.ab.optimizelyconfig.OptimizelyConfig; +import com.optimizely.ab.optimizelydecision.DecisionResponse; import com.optimizely.ab.optimizelydecision.OptimizelyDecideOption; import com.optimizely.ab.optimizelydecision.OptimizelyDecision; import com.optimizely.ab.optimizelyjson.OptimizelyJSON; @@ -129,19 +130,19 @@ public OptimizelyClientTest(int datafileVersion,String datafile){ eventHandler = spy(DefaultEventHandler.getInstance(InstrumentationRegistry.getTargetContext())); optimizely = Optimizely.builder(datafile, eventHandler).build(); if(datafileVersion==3) { + Variation variation = optimizely.getProjectConfig().getExperiments().get(0).getVariations().get(0); when(bucketer.bucket( - eq(optimizely.getProjectConfig().getExperiments().get(0)), - eq(GENERIC_USER_ID), - eq(optimizely.getProjectConfig()), - anyObject()) - ).thenReturn(optimizely.getProjectConfig().getExperiments().get(0).getVariations().get(0)); + optimizely.getProjectConfig().getExperiments().get(0), + GENERIC_USER_ID, + optimizely.getProjectConfig()) + ).thenReturn(DecisionResponse.responseNoReasons(variation)); } else { + Variation variation = optimizely.getProjectConfig().getExperimentKeyMapping().get(FEATURE_MULTI_VARIATE_EXPERIMENT_KEY).getVariations().get(1); when(bucketer.bucket( - eq(optimizely.getProjectConfig().getExperimentKeyMapping().get(FEATURE_MULTI_VARIATE_EXPERIMENT_KEY)), - eq(GENERIC_USER_ID), - eq(optimizely.getProjectConfig()), - anyObject()) - ).thenReturn(optimizely.getProjectConfig().getExperimentKeyMapping().get(FEATURE_MULTI_VARIATE_EXPERIMENT_KEY).getVariations().get(1)); + optimizely.getProjectConfig().getExperimentKeyMapping().get(FEATURE_MULTI_VARIATE_EXPERIMENT_KEY), + GENERIC_USER_ID, + optimizely.getProjectConfig()) + ).thenReturn(DecisionResponse.responseNoReasons(variation)); } spyOnConfig(); } catch (Exception configException) { @@ -380,7 +381,7 @@ public void testGoodActivationBucketingId() { Experiment experiment = optimizelyClient.getProjectConfig().getExperimentKeyMapping().get(FEATURE_ANDROID_EXPERIMENT_KEY); attributes.put(BUCKETING_ATTRIBUTE, bucketingId); Variation v = optimizelyClient.activate(FEATURE_ANDROID_EXPERIMENT_KEY, GENERIC_USER_ID, attributes); - verify(bucketer).bucket(eq(experiment), eq(bucketingId), eq(optimizely.getProjectConfig()), anyObject()); + verify(bucketer).bucket(experiment, bucketingId, optimizely.getProjectConfig()); } @Test @@ -905,7 +906,7 @@ public void testGoodGetVariationBucketingId() { Map attributes = new HashMap<>(); attributes.put(BUCKETING_ATTRIBUTE, bucketingId); Variation v = optimizelyClient.getVariation("android_experiment_key", "userId", attributes); - verify(bucketer).bucket(eq(experiment), eq(bucketingId), eq(optimizely.getProjectConfig()), anyObject()); + verify(bucketer).bucket(experiment, bucketingId, optimizely.getProjectConfig()); } @Test diff --git a/android-sdk/src/main/java/com/optimizely/ab/android/sdk/OptimizelyClient.java b/android-sdk/src/main/java/com/optimizely/ab/android/sdk/OptimizelyClient.java index 47be51573..8cae539dd 100644 --- a/android-sdk/src/main/java/com/optimizely/ab/android/sdk/OptimizelyClient.java +++ b/android-sdk/src/main/java/com/optimizely/ab/android/sdk/OptimizelyClient.java @@ -772,6 +772,7 @@ public OptimizelyJSON getAllFeatureVariables(@NonNull String featureKey, * * @return {@link OptimizelyConfig} */ + @Nullable public OptimizelyConfig getOptimizelyConfig() { if (isValid()) { return optimizely.getOptimizelyConfig(); @@ -790,18 +791,19 @@ public OptimizelyConfig getOptimizelyConfig() { * @param attributes: A map of attribute names to current user attribute values. * @return An OptimizelyUserContext associated with this OptimizelyClient. */ + @Nullable public OptimizelyUserContext createUserContext(@NonNull String userId, @NonNull Map attributes) { - if (userId == null) { - logger.warn("The userId parameter must be nonnull."); + if (isValid()) { + return optimizely.createUserContext(userId, attributes); + } else { + logger.warn("Optimizely is not initialized, could not create a user context"); return null; } - - return new OptimizelyUserContext(optimizely, userId, attributes); } public OptimizelyUserContext createUserContext(@NonNull String userId) { - return new OptimizelyUserContext(optimizely, userId); + return createUserContext(userId, null); } //======== Notification APIs ========// diff --git a/build.gradle b/build.gradle index 77387f84a..ecbb9e145 100644 --- a/build.gradle +++ b/build.gradle @@ -45,11 +45,11 @@ buildscript { allprojects { repositories { - // Uncomment the next line to use maven locally - mavenLocal() jcenter() google() mavenCentral() + // Uncomment the next line to use maven locally + mavenLocal() } } @@ -58,7 +58,7 @@ ext { build_tools_version = "29.0.3" min_sdk_version = 14 target_sdk_version = 29 - java_core_ver = "3.7.0-SNAPSHOT" + java_core_ver = "3.8.0-SNAPSHOT" android_logger_ver = "1.3.6" jacksonversion= "2.11.2" support_annotations_ver = "24.2.1" diff --git a/test-app/src/main/java/com/optimizely/ab/android/test_app/SamplesForAPI.java b/test-app/src/main/java/com/optimizely/ab/android/test_app/SamplesForAPI.java index 247ea4ed2..db2ba2f1c 100644 --- a/test-app/src/main/java/com/optimizely/ab/android/test_app/SamplesForAPI.java +++ b/test-app/src/main/java/com/optimizely/ab/android/test_app/SamplesForAPI.java @@ -63,6 +63,8 @@ static public void samplesForDecide(Context context) { List options = Arrays.asList(OptimizelyDecideOption.INCLUDE_REASONS); OptimizelyDecision decision = user.decide("show_coupon", options); + // or can be called without options + //OptimizelyDecision decision = user.decide("show_coupon"); String variationKey = decision.getVariationKey(); boolean enabled = decision.getEnabled(); From e0b03898759b7eb02f92811cd494287f039877b4 Mon Sep 17 00:00:00 2001 From: Jae Kim Date: Mon, 11 Jan 2021 17:41:43 -0800 Subject: [PATCH 7/8] fix mockito default to DecisionResponse --- .../com/optimizely/ab/android/sdk/OptimizelyClientTest.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/android-sdk/src/androidTest/java/com/optimizely/ab/android/sdk/OptimizelyClientTest.java b/android-sdk/src/androidTest/java/com/optimizely/ab/android/sdk/OptimizelyClientTest.java index cf859f4dc..090481778 100644 --- a/android-sdk/src/androidTest/java/com/optimizely/ab/android/sdk/OptimizelyClientTest.java +++ b/android-sdk/src/androidTest/java/com/optimizely/ab/android/sdk/OptimizelyClientTest.java @@ -129,6 +129,10 @@ public OptimizelyClientTest(int datafileVersion,String datafile){ this.datafileVersion = datafileVersion; eventHandler = spy(DefaultEventHandler.getInstance(InstrumentationRegistry.getTargetContext())); optimizely = Optimizely.builder(datafile, eventHandler).build(); + + // set to return DecisionResponse with null variation by default (instead of null DecisionResponse) + when(bucketer.bucket(anyObject(), anyObject(), anyObject())).thenReturn(DecisionResponse.nullNoReasons()); + if(datafileVersion==3) { Variation variation = optimizely.getProjectConfig().getExperiments().get(0).getVariations().get(0); when(bucketer.bucket( From ba78807ea7a0c1a9eb52ea9bbae325e24884d440 Mon Sep 17 00:00:00 2001 From: Jae Kim Date: Thu, 14 Jan 2021 11:06:23 -0800 Subject: [PATCH 8/8] use java-sdk 3.8.0-beta2 --- build.gradle | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/build.gradle b/build.gradle index 52e129a1b..bc1972de2 100644 --- a/build.gradle +++ b/build.gradle @@ -49,7 +49,7 @@ allprojects { google() mavenCentral() // Uncomment the next line to use maven locally - mavenLocal() + //mavenLocal() } } @@ -58,8 +58,7 @@ ext { build_tools_version = "29.0.3" min_sdk_version = 14 target_sdk_version = 29 - //java_core_ver = "3.7.0" - java_core_ver = "3.8.0-SNAPSHOT" + java_core_ver = "3.8.0-beta2" android_logger_ver = "1.3.6" jacksonversion= "2.11.2" annotations_ver = "1.0.0"