-
Notifications
You must be signed in to change notification settings - Fork 83
DO NOT REVIEW - SEE https://github.com/optimizely/javascript-sdk/pull/288 #216
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
Conversation
6c02325
to
2ae49e1
Compare
function AudienceEvaluator(conditionEvaluators) { | ||
this.typeToEvaluatorMap = Object.assign({ | ||
'custom_attribute': customAttributeConditionEvaluator | ||
}, conditionEvaluators); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we just want allow for additional condition types beyond those supported by the javascript-sdk, or do we also want to allow customers to modify how the built-in condition types are evaluated?
If the former, I wonder if we can add some kind of namespacing (as in Optimizely Web audience integrations) to protect custom conditions types from conflicting with (existing and future) built-in condition types.
If the latter, I have serious reservations, since that could make it difficult for us to modify our own condition types without affecting existing customers.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yikes, we don't seem to catch errors when constructing the decision service. So if we apply any validation at this stage, or do anything that could throw an error, that error will bubble up into the customer's application.
Maybe we should do something about that! Though for the moment, I'm just pointing this out so that we remain careful not to throw.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like the approach of not allowing the built in evaluators to be overridden. If you need to do that, then you'll be able to write your own.
Good call on the error catching. Added an error message for that which is used in a try/catch now.
function AudienceEvaluator(conditionEvaluators) { | ||
this.typeToEvaluatorMap = Object.assign({ | ||
'custom_attribute': customAttributeConditionEvaluator | ||
}, conditionEvaluators); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TODO: additional unit tests, eventually
Unit tests aren't passing in Travis, might wanna fix that and also merge |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the approach is fine so far. CCing @mjc1283 and @jordangarcia who have been doing work on extensibility and new components.
return null; | ||
} | ||
try { | ||
return evaluator.evaluate(condition, userAttributes, this.logger); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if the evaluator we get is not the custom_attribute
, we should log a debug message to let the user know that. Might be helpful when trying to debug. Maybe we can even just log which evaluator we ended using (by key)
@@ -52,6 +52,7 @@ function DecisionService(options) { | |||
this.configObj = options.configObj; | |||
this.userProfileService = options.userProfileService || null; | |||
this.logger = options.logger; | |||
this.audienceEvaluator = new AudienceEvaluator(options.logger, options.__exploratoryConditionEvaluators); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This feels like we are polluting our factory method haha but I see no other easy way to integrate into the current setup.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You mean the constructor?
Yea in the current setup, all this state must be threaded through (config, logger instance, etc...)
Would be nice to have a global registration system by instance ID so that this kinda global state can be easily accessed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is totally fine, by adding configuration to the AudienceEvaluator
we must capture that state, so making this an instantiatable class makes sense.
As far as global state, things like audience evaluators should not be considered as global state. If some sort of global registry existed you'd still have to know the instance ID here to resolve the audience evaluators (which would have to be threaded through).
However things like the logger and errorHandler should be global and having to thread them through here is not cool (this is changing soon in another PR)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A few minor things about naming, but I think the structure of how the evaluator was made extensible looks good.
In terms of naming I've seen React use the prefix UNSTABLE_
in the public API and think that probably gets the point across more the "exploratory".
@@ -238,6 +238,7 @@ describe('lib/optimizely', function() { | |||
configObj: optlyInstance.configObj, | |||
userProfileService: userProfileServiceInstance, | |||
logger: createdLogger, | |||
__exploratoryConditionEvaluators: undefined |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is kind of a weird test assertion. It's probably fine, but I also think it makes sense for __exporatoryConditionEvaluators
to be required in new Optimizely
or Optimizely
defaults this to {}
@@ -52,6 +52,7 @@ function DecisionService(options) { | |||
this.configObj = options.configObj; | |||
this.userProfileService = options.userProfileService || null; | |||
this.logger = options.logger; | |||
this.audienceEvaluator = new AudienceEvaluator(options.logger, options.__exploratoryConditionEvaluators); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is totally fine, by adding configuration to the AudienceEvaluator
we must capture that state, so making this an instantiatable class makes sense.
As far as global state, things like audience evaluators should not be considered as global state. If some sort of global registry existed you'd still have to know the instance ID here to resolve the audience evaluators (which would have to be threaded through).
However things like the logger and errorHandler should be global and having to thread them through here is not cool (this is changing soon in another PR)
@@ -56,7 +56,6 @@ EVALUATORS_BY_MATCH_TYPE[SUBSTRING_MATCH_TYPE] = substringEvaluator; | |||
*/ | |||
function evaluate(condition, userAttributes, logger) { | |||
if (condition.type !== CUSTOM_ATTRIBUTE_CONDITION_TYPE) { | |||
logger.log(LOG_LEVEL.WARNING, sprintf(LOG_MESSAGES.UNKNOWN_CONDITION_TYPE, MODULE_NAME, JSON.stringify(condition))); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@jamesopti I see this log message now in AudienceEvaluator.prototype.evaluateConditionWithUserAttributes
- what is the purpose of this evaluate function, now?
DO NOT REVIEW - SEE #288 |
Obsolete |
Summary
This PR adds the ability for an SDK consumer to pass their own set of condition evaluators when creating an optimizely instance.
The core motivator for this is to be able to evaluate WEB audience conditions using the Javascript SDK as part of the EDGE project (go/edge).
Future fullstack use cases for this functionality might include audience condition plugins, where the customer would define their own match types and provide their own matchers.
Test plan
Unit tests would be pretty straightforward to add for this
Issues