-
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
Changes from 2 commits
0738b58
2ae49e1
0112ff9
1ce7345
930975c
db29de1
7411b24
1102191
05deac9
76d155e
a023b08
0586dd8
f97df78
53f43a6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -15,55 +15,68 @@ | |
*/ | ||
var conditionTreeEvaluator = require('../condition_tree_evaluator'); | ||
var customAttributeConditionEvaluator = require('../custom_attribute_condition_evaluator'); | ||
var ERROR_MESSAGES = require('../../utils/enums').ERROR_MESSAGES; | ||
var enums = require('../../utils/enums'); | ||
var sprintf = require('sprintf-js').sprintf; | ||
|
||
var LOG_LEVEL = enums.LOG_LEVEL; | ||
var LOG_MESSAGES = enums.LOG_MESSAGES; | ||
var MODULE_NAME = 'AUDIENCE_EVALUATOR'; | ||
|
||
module.exports = { | ||
/** | ||
* Determine if the given user attributes satisfy the given audience conditions | ||
* @param {Array|String|null|undefined} audienceConditions Audience conditions to match the user attributes against - can be an array | ||
* of audience IDs, a nested array of conditions, or a single leaf condition. | ||
* Examples: ["5", "6"], ["and", ["or", "1", "2"], "3"], "1" | ||
* @param {Object} audiencesById Object providing access to full audience objects for audience IDs | ||
* contained in audienceConditions. Keys should be audience IDs, values | ||
* should be full audience objects with conditions properties | ||
* @param {Object} [userAttributes] User attributes which will be used in determining if audience conditions | ||
* are met. If not provided, defaults to an empty object | ||
* @param {Object} logger Logger instance. | ||
* @return {Boolean} true if the user attributes match the given audience conditions, false | ||
* otherwise | ||
*/ | ||
evaluate: function(audienceConditions, audiencesById, userAttributes, logger) { | ||
// if there are no audiences, return true because that means ALL users are included in the experiment | ||
if (!audienceConditions || audienceConditions.length === 0) { | ||
return true; | ||
} | ||
|
||
if (!userAttributes) { | ||
userAttributes = {}; | ||
} | ||
function AudienceEvaluator(conditionEvaluators) { | ||
this.typeToEvaluatorMap = Object.assign({ | ||
'custom_attribute': customAttributeConditionEvaluator | ||
}, conditionEvaluators); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 commentThe 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 commentThe 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. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. TODO: additional unit tests, eventually |
||
} | ||
|
||
var evaluateConditionWithUserAttributes = function(condition) { | ||
return customAttributeConditionEvaluator.evaluate(condition, userAttributes, logger); | ||
}; | ||
/** | ||
* Determine if the given user attributes satisfy the given audience conditions | ||
* @param {Array|String|null|undefined} audienceConditions Audience conditions to match the user attributes against - can be an array | ||
* of audience IDs, a nested array of conditions, or a single leaf condition. | ||
* Examples: ["5", "6"], ["and", ["or", "1", "2"], "3"], "1" | ||
* @param {Object} audiencesById Object providing access to full audience objects for audience IDs | ||
* contained in audienceConditions. Keys should be audience IDs, values | ||
* should be full audience objects with conditions properties | ||
* @param {Object} [userAttributes] User attributes which will be used in determining if audience conditions | ||
* are met. If not provided, defaults to an empty object | ||
* @param {Object} logger Logger instance. | ||
* @return {Boolean} true if the user attributes match the given audience conditions, false | ||
* otherwise | ||
*/ | ||
AudienceEvaluator.prototype.evaluate = function(audienceConditions, audiencesById, userAttributes, logger) { | ||
// if there are no audiences, return true because that means ALL users are included in the experiment | ||
if (!audienceConditions || audienceConditions.length === 0) { | ||
return true; | ||
} | ||
nchilada marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
var evaluateAudience = function(audienceId) { | ||
var audience = audiencesById[audienceId]; | ||
if (audience) { | ||
logger.log(LOG_LEVEL.DEBUG, sprintf(LOG_MESSAGES.EVALUATING_AUDIENCE, MODULE_NAME, audienceId, JSON.stringify(audience.conditions))); | ||
var result = conditionTreeEvaluator.evaluate(audience.conditions, evaluateConditionWithUserAttributes); | ||
var resultText = result === null ? 'UNKNOWN' : result.toString().toUpperCase(); | ||
logger.log(LOG_LEVEL.INFO, sprintf(LOG_MESSAGES.AUDIENCE_EVALUATION_RESULT, MODULE_NAME, audienceId, resultText)); | ||
return result; | ||
} | ||
if (!userAttributes) { | ||
userAttributes = {}; | ||
} | ||
|
||
return null; | ||
}; | ||
var evaluateConditionWithUserAttributes = function(condition) { | ||
jamesopti marked this conversation as resolved.
Show resolved
Hide resolved
|
||
var evaluator = this.typeToEvaluatorMap[condition.type]; | ||
if (!evaluator) { | ||
logger.log(LOG_LEVEL.WARNING, sprintf(ERROR_MESSAGES.AUDIENCE_EVALUATOR_EXPECTED, MODULE_NAME, condition.type)); | ||
jamesopti marked this conversation as resolved.
Show resolved
Hide resolved
|
||
return false; | ||
jamesopti marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
return evaluator.evaluate(condition, userAttributes, logger); | ||
}.bind(this); | ||
|
||
return conditionTreeEvaluator.evaluate(audienceConditions, evaluateAudience) || false; | ||
}, | ||
var evaluateAudience = function(audienceId) { | ||
var audience = audiencesById[audienceId]; | ||
if (audience) { | ||
logger.log(LOG_LEVEL.DEBUG, sprintf(LOG_MESSAGES.EVALUATING_AUDIENCE, MODULE_NAME, audienceId, JSON.stringify(audience.conditions))); | ||
var result = conditionTreeEvaluator.evaluate(audience.conditions, evaluateConditionWithUserAttributes); | ||
var resultText = result === null ? 'UNKNOWN' : result.toString().toUpperCase(); | ||
logger.log(LOG_LEVEL.INFO, sprintf(LOG_MESSAGES.AUDIENCE_EVALUATION_RESULT, MODULE_NAME, audienceId, resultText)); | ||
return result; | ||
} | ||
|
||
return null; | ||
}; | ||
|
||
return conditionTreeEvaluator.evaluate(audienceConditions, evaluateAudience) || false; | ||
}; | ||
|
||
module.exports = AudienceEvaluator; |
Uh oh!
There was an error while loading. Please reload this page.