Skip to content

chore: release 2.0.2 #194

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jun 19, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 59 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,61 @@
# Optimizely Java X SDK Changelog

## 2.0.2

June 19th, 2018

This is a patch release of the Optimizely SDK for 2.0.0 which is a major release.

### Bug Fixes
* Send impression event for Feature Test with Feature disabled ([#193](https://github.com/optimizely/java-sdk/pull/193))

## 2.0.1

April 25th, 2018

This is a patch release of the Optimizely SDK for 2.0.0 which is a major release.

### Bug Fixes
* Checking for invalid variables passed into getEnabledFeature, Activate, getVariation and track.

## 2.0.0

April 12th, 2018

This major release of the Optimizely SDK introduces APIs for Feature Management. It also introduces some breaking changes listed below.

### New Features
* Introduces the `isFeatureEnabled` API to determine whether to show a feature to a user or not.
```
Boolean enabled = optimizelyClient.isFeatureEnabled("my_feature_key", "user_1", userAttributes);
```

* 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:
```
ArrayList<String> enabledFeatures = optimizelyClient.getEnabledFeatures("user_1", userAttributes);
```

* Introduces Feature Variables to configure or parameterize your feature. There are four variable types: `Integer`, `String`, `Double`, `Boolean`.
```
String stringVariable = optimizelyClient.getFeatureVariableString("my_feature_key", "string_variable_key", "user_1");
Integer integerVariable = optimizelyClient.getFeatureVariableInteger("my_feature_key", "integer_variable_key", "user_1");
Double doubleVariable = optimizelyClient.getFeatureVariableDouble("my_feature_key", "double_variable_key", "user_1");
Boolean booleanVariable = optimizelyClient.getFeatureVariableBoolean("my_feature_key", "boolean_variable_key", "user_1");
```

### Breaking changes
* 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.
```
Map<String, Object> eventTags = new HashMap<String, Object>();
// reserved "revenue" tag
eventTags.put("revenue", 6432);
optimizelyClient.track("event_key", "user_id", userAttributes, eventTags);
```

* 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()`)

## 2.0.0 Beta 6
March 8, 2018

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

January 30, 2018

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).
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).

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.

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

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

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

## 1.8.0
Expand Down
14 changes: 8 additions & 6 deletions core-api/src/main/java/com/optimizely/ab/Optimizely.java
Original file line number Diff line number Diff line change
Expand Up @@ -347,10 +347,7 @@ else if (userId == null) {
Map<String, String> filteredAttributes = filterAttributes(projectConfig, attributes);

FeatureDecision featureDecision = decisionService.getVariationForFeature(featureFlag, userId, filteredAttributes);
if (featureDecision.variation == null || !featureDecision.variation.getFeatureEnabled()) {
logger.info("Feature \"{}\" is not enabled for user \"{}\".", featureKey, userId);
return false;
} else {
if (featureDecision.variation != null) {
if (featureDecision.decisionSource.equals(FeatureDecision.DecisionSource.EXPERIMENT)) {
sendImpression(
projectConfig,
Expand All @@ -362,9 +359,14 @@ else if (userId == null) {
logger.info("The user \"{}\" is not included in an experiment for feature \"{}\".",
userId, featureKey);
}
logger.info("Feature \"{}\" is enabled for user \"{}\".", featureKey, userId);
return true;
if (featureDecision.variation.getFeatureEnabled()) {
logger.info("Feature \"{}\" is enabled for user \"{}\".", featureKey, userId);
return true;
}
}

logger.info("Feature \"{}\" is not enabled for user \"{}\".", featureKey, userId);
return false;
}

/**
Expand Down
44 changes: 39 additions & 5 deletions core-api/src/test/java/com/optimizely/ab/OptimizelyTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -3479,15 +3479,13 @@ public void isFeatureEnabledReturnsTrueButDoesNotSendWhenUserIsBucketedIntoVaria
* false so feature enabled will return false
*/
@Test
public void isFeatureEnabledWithExperimentKeyForcedOfFeatureEnabledFalse() throws Exception {
public void isFeatureEnabledWithExperimentKeyForcedOffFeatureEnabledFalse() throws Exception {
assumeTrue(datafileVersion >= Integer.parseInt(ProjectConfig.Version.V4.toString()));
Experiment activatedExperiment = validProjectConfig.getExperimentKeyMapping().get(EXPERIMENT_MULTIVARIATE_EXPERIMENT_KEY);
Variation forcedVariation = activatedExperiment.getVariations().get(2);
EventBuilder mockEventBuilder = mock(EventBuilder.class);

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

Optimizely optimizely = Optimizely.builder(validDatafile, mockEventHandler)
.withBucketing(mockBucketer)
.withEventBuilder(mockEventBuilder)
.withConfig(validProjectConfig)
.withErrorHandler(mockErrorHandler)
.build();
Expand Down Expand Up @@ -3584,6 +3580,44 @@ public void isFeatureEnabledFalseWhenFeatureEnabledOfVariationIsFalse() throws E

}

/**
* Verify {@link Optimizely#isFeatureEnabled(String, String)} calls into
* {@link Optimizely#isFeatureEnabled(String, String, Map)} and they both
* return False
* when the user is bucketed an feature test variation that is turned off.
* @throws Exception
*/
@Test
public void isFeatureEnabledReturnsFalseAndDispatchesWhenUserIsBucketedIntoAnExperimentVariationToggleOff() throws Exception {
assumeTrue(datafileVersion >= Integer.parseInt(ProjectConfig.Version.V4.toString()));

String validFeatureKey = FEATURE_MULTI_VARIATE_FEATURE_KEY;

Optimizely spyOptimizely = spy(Optimizely.builder(validDatafile, mockEventHandler)
.withConfig(validProjectConfig)
.withDecisionService(mockDecisionService)
.build());

Experiment activatedExperiment = validProjectConfig.getExperimentKeyMapping().get(EXPERIMENT_MULTIVARIATE_EXPERIMENT_KEY);
Variation variation = new Variation("2", "variation_toggled_off", false, null);

FeatureDecision featureDecision = new FeatureDecision(activatedExperiment, variation, FeatureDecision.DecisionSource.EXPERIMENT);
doReturn(featureDecision).when(mockDecisionService).getVariationForFeature(
any(FeatureFlag.class),
anyString(),
anyMapOf(String.class, String.class)
);

assertFalse(spyOptimizely.isFeatureEnabled(validFeatureKey, genericUserId));

logbackVerifier.expectMessage(
Level.INFO,
"Feature \"" + validFeatureKey +
"\" is not enabled for user \"" + genericUserId + "\"."
);
verify(mockEventHandler, times(1)).dispatchEvent(any(LogEvent.class));
}

/** Integration Test
* Verify {@link Optimizely#isFeatureEnabled(String, String, Map)}
* returns True
Expand Down