Skip to content

(feat): Feature Management getFeatureVariable Listener #270

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 23, 2019

Conversation

mnoman09
Copy link
Contributor

@mnoman09 mnoman09 commented Mar 8, 2019

Summary

  • Introduction of OnDecision Notification Listener.
  • Returning default feature variable value if featureEnabled is false.
  • Implemented onDecisionListener in:
  • get_feature_variable_string
  • get_feature_variable_boolean
  • get_feature_variable_integer
  • get_feature_variable_double

##Ticket:
OASIS-4228

@coveralls
Copy link

Pull Request Test Coverage Report for Build 903

  • 61 of 64 (95.31%) changed or added relevant lines in 4 files are covered.
  • No unchanged relevant lines lost coverage.
  • Overall coverage increased (+0.2%) to 89.219%

Changes Missing Coverage Covered Lines Changed/Added Lines %
core-api/src/main/java/com/optimizely/ab/notification/DecisionInfoEnums.java 13 14 92.86%
core-api/src/main/java/com/optimizely/ab/notification/NotificationCenter.java 17 19 89.47%
Totals Coverage Status
Change from base Build 892: 0.2%
Covered Lines: 2706
Relevant Lines: 3033

💛 - Coveralls

@coveralls
Copy link

coveralls commented Mar 8, 2019

Pull Request Test Coverage Report for Build 976

  • 73 of 73 (100.0%) changed or added relevant lines in 2 files are covered.
  • No unchanged relevant lines lost coverage.
  • Overall coverage increased (+0.2%) to 89.581%

Totals Coverage Status
Change from base Build 975: 0.2%
Covered Lines: 2846
Relevant Lines: 3177

💛 - Coveralls

@aliabbasrizvi aliabbasrizvi requested a review from a team March 11, 2019 17:22
@aliabbasrizvi
Copy link
Contributor

aliabbasrizvi commented Mar 13, 2019

Returning default feature variable value if featureEnabled is false.

Isn't this already the case?

@mnoman09
Copy link
Contributor Author

Returning default feature variable value if featureEnabled is false.

Isn't this already the case?

No it was not the case already, we did that change in all sdks

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.

Where are we asserting that the notification listener is being called with correct params?


public class DecisionInfoEnums {
public enum GetFeatureVariableDecisionInfo {
SOURCE_EXPERIMENT_KEY("source_experiment_key"),
Copy link
Contributor

Choose a reason for hiding this comment

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

nit. I think alphabetizing will be a good order for this.

} else {
logger.info("User \"{}\" was not bucketed into any variation for feature flag \"{}\". " +
"The default value \"{}\" for \"{}\" is being returned.",
userId, featureKey, variableValue, variableKey
);
}

Object convertedValue = convertStringToType(variableValue, variableType);
Copy link
Contributor

Choose a reason for hiding this comment

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

This conversion is now happening twice per api call. Once here and once in the originating method.

} else {
logger.info("User \"{}\" was not bucketed into any variation for feature flag \"{}\". " +
"The default value \"{}\" for \"{}\" is being returned.",
userId, featureKey, variableValue, variableKey
);
}

Object convertedValue = convertStringToType(variableValue, variableType);
Map<String, Object> decisionInfo = new HashMap<>();
decisionInfo.put(DecisionInfoEnums.FeatureVariableDecisionInfo.FEATURE_KEY.toString(), featureKey);
Copy link
Contributor

Choose a reason for hiding this comment

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

Not a valid use of Enums. What you're doing here just requires constants or can even be a builder.
Via constants this would reduce to:

decisionInfo.put(DECISION_FEATURE_KEY, featureKey);
decisionInfo.put(DECISION_FEATURE_ENABLED, featureEnabled);
...

Via a builder it would be:

Map<String, Object> decisionInfo = DecisionInfoMap.builder()
    .withFeatureKey(featureKey)
    .withFeatureEnabled(featureEnabled)
    ...
    .build();

I personally like the builder approach since within the builder you'd abstract some of the conditional logic below.
Actually, in the context of the notification listener the entire thing can benefit from the builder pattern which actually transforms into a fluent pattern if you in corporate the notification itself.

DecisionNotification.builder()
    .withUserId(userId)
    .withAttributes(attributes)
    .withFeatureKey(featureKey)
    .withFeatureEnabled(featureEnabled)
    ...
    .send();

A much cleaner interface :)

return variableValue;
}

// Helper method which takes type and variable value and convert it to object to use in Listener DecisionInfo object variable value
private Object convertStringToType(String variableValue, FeatureVariable.VariableType type) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Refactor so we're not doing String to Type conversion multiple times and in multiple places.


package com.optimizely.ab.notification;

public class DecisionInfoEnums {
Copy link
Contributor

Choose a reason for hiding this comment

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

Remove Enum class in favor of something more appropriate. See previous comment about constants vs builder vs fluent approaches.

}

@Override
public abstract void onDecision(@Nonnull String type,
Copy link
Contributor

Choose a reason for hiding this comment

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

Would require a larger refactor, but we can generalize much better if we used actual notification Objects as opposed to positional arguments which as we can see in this example can lead to bugs since one can easily swap type for userId and attributes for decisionInfo. In this example if we had a DecisionNotification object with members type, userId etc, then everything becomes much more explicit.

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.

Minor feedback. Looks good otherwise.

return variableValue;
} catch (Exception exception) {
logger.error("BooleanTypeException while trying to parse \"" + variableValue +
"\" as Boolean. " + exception);
}
return null;
Copy link
Contributor

Choose a reason for hiding this comment

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

You can just return variableValue here

attributes,
FeatureVariable.VariableType.DOUBLE
);
if (variableValue != null) {
Copy link
Contributor

Choose a reason for hiding this comment

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

This check seems redundant. You can just change last line to be return variableValue

@mnoman09 mnoman09 requested a review from aliabbasrizvi April 18, 2019 14:25
mnoman09 and others added 2 commits April 19, 2019 12:48
… per new design (#280)

* Changed DecisionSource experiment to feature-test and added source_info

* Updated feature_variable to feature-variable

* Update core-api/src/main/java/com/optimizely/ab/notification/DecisionNotification.java

Co-Authored-By: mnoman09 <Muhammadnoman@folio3.com>
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.

Looks good. Lets make the change we talked about in a follow up.

@mnoman09 mnoman09 requested a review from aliabbasrizvi April 23, 2019 15:28
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.

Going to merge it in its current form. We will have to follow up with changes proposed in the different PR.

@aliabbasrizvi aliabbasrizvi dismissed mikecdavis’s stale review April 23, 2019 16:09

Synced offline with Mike about going ahead for now.

@aliabbasrizvi aliabbasrizvi merged commit 1fc9758 into master Apr 23, 2019
@aliabbasrizvi aliabbasrizvi deleted the mnoman/getFeatVarListener branch April 23, 2019 16:09
return variableValue;
} catch (Exception exception) {
logger.error("NumberFormatException while trying to parse \"" + variableValue +
"\" as Double. " + exception);
Copy link
Contributor

Choose a reason for hiding this comment

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

@mnoman09 this and a other log statements in this PR append the exception as a string on log message. the logger should be passed the exception object as a last argument

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.

7 participants