Skip to content

feat(api): Decision Notification Listener for feature variable APIs. #246

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 23 commits into from
Apr 2, 2019

Conversation

mfahadahmed
Copy link
Contributor

Summary

  • Implemented onDecisionListener in activate and getVariation APIs.

Test plan

  • Added unit tests.

@coveralls
Copy link

coveralls commented Mar 8, 2019

Coverage Status

Coverage increased (+0.05%) to 97.792% when pulling 69fb7d6 on fahad/on-decision-listener-feature-variables into 9827911 on master.

@mfahadahmed mfahadahmed changed the title feat(api): OnDecision Notification Listener for feature variable APIs. feat(api): Decision Notification Listener for feature variable APIs. Mar 13, 2019
Copy link
Contributor

@aliabbasrizvi aliabbasrizvi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@mjc1283 or @jordangarcia can one of you take a look as well?

sandbox.stub(optlyInstance.decisionService, 'getVariationForFeature').returns({
experiment: null,
variation: null,
decisionSource: null,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel the actual decisionService needs to be updated to set decisionSource as rollout.

Thoughts @jordangarcia @mjc1283 ?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I think decisionSource should be rollout even if the user is not evaluated into the experiment, as it's jkind of the bottom of evaluation chain.

@jordangarcia jordangarcia self-requested a review March 13, 2019 15:20
Copy link
Contributor

@jordangarcia jordangarcia left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is some logic that was not implemented correctly around the value of the variable when feature is not enabled.

Also please change the decision service to return ROLLOUT when the variation is null.

Lastly the tests need to be updated to not directly modify configObj that's loaded in the Optimizely instance.

variableValue = projectConfig.getVariableValueForVariation(this.configObj, variable, decision.variation, this.logger);
this.logger.log(LOG_LEVEL.INFO, sprintf(LOG_MESSAGES.USER_RECEIVED_VARIABLE_VALUE, MODULE_NAME, variableKey, featureFlag.key, variableValue, userId));
featureEnabled = decision.variation.featureEnabled;
if (featureEnabled === true) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This logic is incorrect.

Feature variables should always be the variation variable value, even if the feature is disabled.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jordangarcia it is actually supposed to be the default feature value

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@aliabbasrizvi, agree that this is the default behavior. And the datafile constraint of a feature experiment variation (where featureEnabled == false) always has variables that mirror the variable defaults makes it so this works today.

Given the scope of this PR (just adding notificationCenter listener), i'd prefer not to change this core logic.

Java also mirrors the existing JS implementation https://github.com/optimizely/java-sdk/blob/master/core-api/src/main/java/com/optimizely/ab/Optimizely.java#L615-L668

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Modified the logic to return variation's variable value even when the featureEnabled property is set to false.

this.logger.log(LOG_LEVEL.INFO, sprintf(LOG_MESSAGES.USER_RECEIVED_VARIABLE_VALUE, MODULE_NAME, variableKey, featureFlag.key, variableValue, userId));
} else {
this.logger.log(LOG_LEVEL.INFO, sprintf(LOG_MESSAGES.FEATURE_NOT_ENABLED_RETURN_DEFAULT_VARIABLE_VALUE, MODULE_NAME, featureFlag.key, userId, variableKey));
featureEnabled = false;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is no reason to re-card featureEnabled here.

optlyInstance = new Optimizely({
clientEngine: 'node-sdk',
datafile: testData.getTestProjectConfigWithFeatures(),
eventBuilder: eventBuilder,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

eventBuilder isn't a valid option to pass to new Optimizely

describe('when the variation is toggled OFF', function() {
beforeEach(function() {
// Setting featureEnabled to false for verifying feature toggle off.
optlyInstance.configObj.experimentKeyMap.testing_my_feature.variations[0].featureEnabled = false;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We shouldn't be reaching into the configObj like this.

We should change the test datafile that were using to include a feature where a variation has featureEnabled = false

beforeEach(function() {
// Setting featureEnabled to false for verifying feature toggle off.
optlyInstance.configObj.experimentKeyMap.testing_my_feature.variations[0].featureEnabled = false;
var experiment = optlyInstance.configObj.experimentKeyMap.testing_my_feature;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use projectConfig.getExperimentFromKey(optlyInstance.configObj, 'testing_my_feature') instead of directly inspecting the configObj

describe('bucketed into variation of a rollout with variable values', function() {
describe('when the variation is toggled ON', function() {
beforeEach(function() {
var experiment = optlyInstance.configObj.experimentKeyMap['594031'];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use projectConfig function here instead.

sandbox.stub(optlyInstance.decisionService, 'getVariationForFeature').returns({
experiment: null,
variation: null,
decisionSource: null,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I think decisionSource should be rollout even if the user is not evaluated into the experiment, as it's jkind of the bottom of evaluation chain.

@@ -2608,6 +3117,7 @@ describe('lib/optimizely', function() {
sandbox.stub(uuid, 'v4').returns('a68cf1ad-0393-4e18-af87-efe8f01a7c9c');
sandbox.stub(fns, 'currentTimestamp').returns(1509489766569);
clock = sinon.useFakeTimers(new Date().getTime());
onDecisionListener = sinon.spy();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this still needed?

LOG_LEVEL.INFO,
sprintf(LOG_MESSAGES.USER_RECEIVED_DEFAULT_VARIABLE_VALUE, MODULE_NAME, userId, variableKey, featureFlag.key)
);
variableValue = variable.defaultValue;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fix this indentation.

@@ -199,8 +199,8 @@ exports.DECISION_INFO_TYPES = {
* Optimizely.
*/
exports.DECISION_SOURCES = {
EXPERIMENT: 'experiment',
ROLLOUT: 'rollout',
EXPERIMENT: 'EXPERIMENT',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be lowercase, right?

@aliabbasrizvi

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it will be upper case as @aliabbasrizvi suggested here
#245 (comment)

@aliabbasrizvi
Copy link
Contributor

@jordangarcia can you take a look again and approve/decline?

…istener-feature-variables

# Conflicts:
#	packages/optimizely-sdk/lib/optimizely/index.tests.js
#	packages/optimizely-sdk/lib/utils/enums/index.js
@msohailhussain
Copy link
Contributor

@jordangarcia can you please rereview and let me know if any change is needed?

mfahadahmed and others added 2 commits March 30, 2019 00:34
…istener-feature-variables

# Conflicts:
#	packages/optimizely-sdk/lib/optimizely/index.js
@aliabbasrizvi aliabbasrizvi requested a review from mjc1283 April 2, 2019 20:31
Copy link
Contributor

@mjc1283 mjc1283 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This LGTM, but I am leaving it for @jordangarcia to approve.

@jordangarcia
Copy link
Contributor

LGTM

@jordangarcia jordangarcia merged commit 3c69a02 into master Apr 2, 2019
@aliabbasrizvi aliabbasrizvi deleted the fahad/on-decision-listener-feature-variables branch April 17, 2019 18:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants