Skip to content

Commit 5dbee7e

Browse files
author
Tyler Brandt
authored
chore: release 2.0.2 (#194)
* Fix impression sent from feature experiment variation toggled off. (#193)
1 parent 060bca7 commit 5dbee7e

File tree

3 files changed

+106
-14
lines changed

3 files changed

+106
-14
lines changed

CHANGELOG.md

+59-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,61 @@
11
# Optimizely Java X SDK Changelog
22

3+
## 2.0.2
4+
5+
June 19th, 2018
6+
7+
This is a patch release of the Optimizely SDK for 2.0.0 which is a major release.
8+
9+
### Bug Fixes
10+
* Send impression event for Feature Test with Feature disabled ([#193](https://github.com/optimizely/java-sdk/pull/193))
11+
12+
## 2.0.1
13+
14+
April 25th, 2018
15+
16+
This is a patch release of the Optimizely SDK for 2.0.0 which is a major release.
17+
18+
### Bug Fixes
19+
* Checking for invalid variables passed into getEnabledFeature, Activate, getVariation and track.
20+
21+
## 2.0.0
22+
23+
April 12th, 2018
24+
25+
This major release of the Optimizely SDK introduces APIs for Feature Management. It also introduces some breaking changes listed below.
26+
27+
### New Features
28+
* Introduces the `isFeatureEnabled` API to determine whether to show a feature to a user or not.
29+
```
30+
Boolean enabled = optimizelyClient.isFeatureEnabled("my_feature_key", "user_1", userAttributes);
31+
```
32+
33+
* You can also get all the enabled features for the user by calling the following method which returns a list of strings representing the feature keys:
34+
```
35+
ArrayList<String> enabledFeatures = optimizelyClient.getEnabledFeatures("user_1", userAttributes);
36+
```
37+
38+
* Introduces Feature Variables to configure or parameterize your feature. There are four variable types: `Integer`, `String`, `Double`, `Boolean`.
39+
```
40+
String stringVariable = optimizelyClient.getFeatureVariableString("my_feature_key", "string_variable_key", "user_1");
41+
Integer integerVariable = optimizelyClient.getFeatureVariableInteger("my_feature_key", "integer_variable_key", "user_1");
42+
Double doubleVariable = optimizelyClient.getFeatureVariableDouble("my_feature_key", "double_variable_key", "user_1");
43+
Boolean booleanVariable = optimizelyClient.getFeatureVariableBoolean("my_feature_key", "boolean_variable_key", "user_1");
44+
```
45+
46+
### Breaking changes
47+
* The `track` API with revenue value as a stand-alone parameter has been removed. The revenue value should be passed in as an entry of the event tags map. The key for the revenue tag is `revenue` and will be treated by Optimizely as the key for analyzing revenue data in results.
48+
```
49+
Map<String, Object> eventTags = new HashMap<String, Object>();
50+
51+
// reserved "revenue" tag
52+
eventTags.put("revenue", 6432);
53+
54+
optimizelyClient.track("event_key", "user_id", userAttributes, eventTags);
55+
```
56+
57+
* We have removed deprecated classes with the `NotificationBroadcaster` in favor of the new API with the `NotificationCenter`. We have streamlined the API so that it is easily usable with Java Lambdas in *Java 1.8+*. We have also added some convenience methods to add these listeners. Finally, some of the API names have changed slightly (e.g. `clearAllNotifications()` is now `clearAllNotificationListeners()`)
58+
359
## 2.0.0 Beta 6
460
March 8, 2018
561

@@ -31,7 +87,7 @@ It also includes the latest Notification Listeners and bucketing ID changes from
3187

3288
January 30, 2018
3389

34-
This release adds support for bucketing id (By passing in `$opt_bucketing_id` in the attribute map to override the user id as the bucketing variable. This is useful when wanting a set of users to share the same experience such as two players in a game).
90+
This release adds support for bucketing id (By passing in `$opt_bucketing_id` in the attribute map to override the user id as the bucketing variable. This is useful when wanting a set of users to share the same experience such as two players in a game).
3591

3692
This release also depricates the old notification broadcaster in favor of a notification center that supports a wide range of notifications. The notification listener is now registered for the specific notification type such as ACTIVATE and TRACK. This is accomplished by allowing for a variable argument call to notify (a new var arg method added to the NotificationListener). Specific abstract classes exist for the associated notification type (ActivateNotification and TrackNotification). These abstract classes enforce the strong typing that exists in Java. You may also add custom notification types and fire them through the notification center. The notification center is implemented using this var arg approach in all Optimizely SDKs.
3793

@@ -58,7 +114,7 @@ This is a patch release for 1.8.0. It contains two bug fixes mentioned below.
58114
### Bug Fixes
59115
SDK returns NullPointerException when activating with unknown attribute.
60116

61-
Pooled connection times out if it is idle for a long time (AsyncEventHandler's HttpClient uses PoolingHttpClientConnectionManager setting a validate interval).
117+
Pooled connection times out if it is idle for a long time (AsyncEventHandler's HttpClient uses PoolingHttpClientConnectionManager setting a validate interval).
62118

63119
## 2.0.0 Beta 2
64120
October 5, 2017
@@ -93,7 +149,7 @@ You can now use feature flags in the Java SDK. You can experiment on features an
93149
- Remove track with revenue as a parameter. Pass the revenue value as an event tag instead
94150
- `track(String, String, long)`
95151
- `track(String, String, Map<String, String>, long)`
96-
- We will no longer run all unit tests in travis-ci against Java 7.
152+
- We will no longer run all unit tests in travis-ci against Java 7.
97153
We will still continue to set `sourceCompatibility` and `targetCompatibility` to 1.6 so that we build for Java 6.
98154

99155
## 1.8.0

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

+8-6
Original file line numberDiff line numberDiff line change
@@ -347,10 +347,7 @@ else if (userId == null) {
347347
Map<String, String> filteredAttributes = filterAttributes(projectConfig, attributes);
348348

349349
FeatureDecision featureDecision = decisionService.getVariationForFeature(featureFlag, userId, filteredAttributes);
350-
if (featureDecision.variation == null || !featureDecision.variation.getFeatureEnabled()) {
351-
logger.info("Feature \"{}\" is not enabled for user \"{}\".", featureKey, userId);
352-
return false;
353-
} else {
350+
if (featureDecision.variation != null) {
354351
if (featureDecision.decisionSource.equals(FeatureDecision.DecisionSource.EXPERIMENT)) {
355352
sendImpression(
356353
projectConfig,
@@ -362,9 +359,14 @@ else if (userId == null) {
362359
logger.info("The user \"{}\" is not included in an experiment for feature \"{}\".",
363360
userId, featureKey);
364361
}
365-
logger.info("Feature \"{}\" is enabled for user \"{}\".", featureKey, userId);
366-
return true;
362+
if (featureDecision.variation.getFeatureEnabled()) {
363+
logger.info("Feature \"{}\" is enabled for user \"{}\".", featureKey, userId);
364+
return true;
365+
}
367366
}
367+
368+
logger.info("Feature \"{}\" is not enabled for user \"{}\".", featureKey, userId);
369+
return false;
368370
}
369371

370372
/**

core-api/src/test/java/com/optimizely/ab/OptimizelyTest.java

+39-5
Original file line numberDiff line numberDiff line change
@@ -3479,15 +3479,13 @@ public void isFeatureEnabledReturnsTrueButDoesNotSendWhenUserIsBucketedIntoVaria
34793479
* false so feature enabled will return false
34803480
*/
34813481
@Test
3482-
public void isFeatureEnabledWithExperimentKeyForcedOfFeatureEnabledFalse() throws Exception {
3482+
public void isFeatureEnabledWithExperimentKeyForcedOffFeatureEnabledFalse() throws Exception {
34833483
assumeTrue(datafileVersion >= Integer.parseInt(ProjectConfig.Version.V4.toString()));
34843484
Experiment activatedExperiment = validProjectConfig.getExperimentKeyMapping().get(EXPERIMENT_MULTIVARIATE_EXPERIMENT_KEY);
34853485
Variation forcedVariation = activatedExperiment.getVariations().get(2);
3486-
EventBuilder mockEventBuilder = mock(EventBuilder.class);
34873486

34883487
Optimizely optimizely = Optimizely.builder(validDatafile, mockEventHandler)
34893488
.withBucketing(mockBucketer)
3490-
.withEventBuilder(mockEventBuilder)
34913489
.withConfig(validProjectConfig)
34923490
.withErrorHandler(mockErrorHandler)
34933491
.build();
@@ -3506,11 +3504,9 @@ public void isFeatureEnabledWithExperimentKeyForcedWithNoFeatureEnabledSet() thr
35063504
assumeTrue(datafileVersion >= Integer.parseInt(ProjectConfig.Version.V4.toString()));
35073505
Experiment activatedExperiment = validProjectConfig.getExperimentKeyMapping().get(EXPERIMENT_DOUBLE_FEATURE_EXPERIMENT_KEY);
35083506
Variation forcedVariation = activatedExperiment.getVariations().get(1);
3509-
EventBuilder mockEventBuilder = mock(EventBuilder.class);
35103507

35113508
Optimizely optimizely = Optimizely.builder(validDatafile, mockEventHandler)
35123509
.withBucketing(mockBucketer)
3513-
.withEventBuilder(mockEventBuilder)
35143510
.withConfig(validProjectConfig)
35153511
.withErrorHandler(mockErrorHandler)
35163512
.build();
@@ -3584,6 +3580,44 @@ public void isFeatureEnabledFalseWhenFeatureEnabledOfVariationIsFalse() throws E
35843580

35853581
}
35863582

3583+
/**
3584+
* Verify {@link Optimizely#isFeatureEnabled(String, String)} calls into
3585+
* {@link Optimizely#isFeatureEnabled(String, String, Map)} and they both
3586+
* return False
3587+
* when the user is bucketed an feature test variation that is turned off.
3588+
* @throws Exception
3589+
*/
3590+
@Test
3591+
public void isFeatureEnabledReturnsFalseAndDispatchesWhenUserIsBucketedIntoAnExperimentVariationToggleOff() throws Exception {
3592+
assumeTrue(datafileVersion >= Integer.parseInt(ProjectConfig.Version.V4.toString()));
3593+
3594+
String validFeatureKey = FEATURE_MULTI_VARIATE_FEATURE_KEY;
3595+
3596+
Optimizely spyOptimizely = spy(Optimizely.builder(validDatafile, mockEventHandler)
3597+
.withConfig(validProjectConfig)
3598+
.withDecisionService(mockDecisionService)
3599+
.build());
3600+
3601+
Experiment activatedExperiment = validProjectConfig.getExperimentKeyMapping().get(EXPERIMENT_MULTIVARIATE_EXPERIMENT_KEY);
3602+
Variation variation = new Variation("2", "variation_toggled_off", false, null);
3603+
3604+
FeatureDecision featureDecision = new FeatureDecision(activatedExperiment, variation, FeatureDecision.DecisionSource.EXPERIMENT);
3605+
doReturn(featureDecision).when(mockDecisionService).getVariationForFeature(
3606+
any(FeatureFlag.class),
3607+
anyString(),
3608+
anyMapOf(String.class, String.class)
3609+
);
3610+
3611+
assertFalse(spyOptimizely.isFeatureEnabled(validFeatureKey, genericUserId));
3612+
3613+
logbackVerifier.expectMessage(
3614+
Level.INFO,
3615+
"Feature \"" + validFeatureKey +
3616+
"\" is not enabled for user \"" + genericUserId + "\"."
3617+
);
3618+
verify(mockEventHandler, times(1)).dispatchEvent(any(LogEvent.class));
3619+
}
3620+
35873621
/** Integration Test
35883622
* Verify {@link Optimizely#isFeatureEnabled(String, String, Map)}
35893623
* returns True

0 commit comments

Comments
 (0)