@@ -43,33 +43,32 @@ var DECISION_SOURCES = enums.DECISION_SOURCES;
4343 *
4444 * @constructor
4545 * @param {Object } options
46- * @param {Object } options.configObj The parsed project configuration object that contains all the experiment configurations.
4746 * @param {Object } options.userProfileService An instance of the user profile service for sticky bucketing.
4847 * @param {Object } options.logger An instance of a logger to log messages with.
4948 * @returns {Object }
5049 */
5150function DecisionService ( options ) {
52- this . configObj = options . configObj ;
5351 this . userProfileService = options . userProfileService || null ;
5452 this . logger = options . logger ;
5553}
5654
5755/**
5856 * Gets variation where visitor will be bucketed.
57+ * @param {Object } configObj The parsed project configuration object
5958 * @param {string } experimentKey
6059 * @param {string } userId
6160 * @param {Object } attributes
6261 * @return {string|null } the variation the user is bucketed into.
6362 */
64- DecisionService . prototype . getVariation = function ( experimentKey , userId , attributes ) {
63+ DecisionService . prototype . getVariation = function ( configObj , experimentKey , userId , attributes ) {
6564 // by default, the bucketing ID should be the user ID
6665 var bucketingId = this . _getBucketingId ( userId , attributes ) ;
6766
68- if ( ! this . __checkIfExperimentIsActive ( experimentKey , userId ) ) {
67+ if ( ! this . __checkIfExperimentIsActive ( configObj , experimentKey , userId ) ) {
6968 return null ;
7069 }
71- var experiment = this . configObj . experimentKeyMap [ experimentKey ] ;
72- var forcedVariationKey = projectConfig . getForcedVariation ( this . configObj , experimentKey , userId , this . logger ) ;
70+ var experiment = configObj . experimentKeyMap [ experimentKey ] ;
71+ var forcedVariationKey = projectConfig . getForcedVariation ( configObj , experimentKey , userId , this . logger ) ;
7372 if ( ! ! forcedVariationKey ) {
7473 return forcedVariationKey ;
7574 }
@@ -81,20 +80,20 @@ DecisionService.prototype.getVariation = function(experimentKey, userId, attribu
8180
8281 // check for sticky bucketing
8382 var experimentBucketMap = this . __resolveExperimentBucketMap ( userId , attributes ) ;
84- variation = this . __getStoredVariation ( experiment , userId , experimentBucketMap ) ;
83+ variation = this . __getStoredVariation ( configObj , experiment , userId , experimentBucketMap ) ;
8584 if ( ! ! variation ) {
8685 this . logger . log ( LOG_LEVEL . INFO , sprintf ( LOG_MESSAGES . RETURNING_STORED_VARIATION , MODULE_NAME , variation . key , experimentKey , userId ) ) ;
8786 return variation . key ;
8887 }
8988
9089 // Perform regular targeting and bucketing
91- if ( ! this . __checkIfUserIsInAudience ( experimentKey , userId , attributes ) ) {
90+ if ( ! this . __checkIfUserIsInAudience ( configObj , experimentKey , userId , attributes ) ) {
9291 return null ;
9392 }
9493
95- var bucketerParams = this . __buildBucketerParams ( experimentKey , bucketingId , userId ) ;
94+ var bucketerParams = this . __buildBucketerParams ( configObj , experimentKey , bucketingId , userId ) ;
9695 var variationId = bucketer . bucket ( bucketerParams ) ;
97- variation = this . configObj . variationIdMap [ variationId ] ;
96+ variation = configObj . variationIdMap [ variationId ] ;
9897 if ( ! variation ) {
9998 return null ;
10099 }
@@ -120,12 +119,13 @@ DecisionService.prototype.__resolveExperimentBucketMap = function(userId, attrib
120119
121120/**
122121 * Checks whether the experiment is running or launched
122+ * @param {Object } configObj The parsed project configuration object
123123 * @param {string } experimentKey Key of experiment being validated
124124 * @param {string } userId ID of user
125125 * @return {boolean } True if experiment is running
126126 */
127- DecisionService . prototype . __checkIfExperimentIsActive = function ( experimentKey , userId ) {
128- if ( ! projectConfig . isActive ( this . configObj , experimentKey ) ) {
127+ DecisionService . prototype . __checkIfExperimentIsActive = function ( configObj , experimentKey , userId ) {
128+ if ( ! projectConfig . isActive ( configObj , experimentKey ) ) {
129129 var experimentNotRunningLogMessage = sprintf ( LOG_MESSAGES . EXPERIMENT_NOT_RUNNING , MODULE_NAME , experimentKey ) ;
130130 this . logger . log ( LOG_LEVEL . INFO , experimentNotRunningLogMessage ) ;
131131 return false ;
@@ -159,14 +159,15 @@ DecisionService.prototype.__getWhitelistedVariation = function(experiment, userI
159159
160160/**
161161 * Checks whether the user is included in experiment audience
162+ * @param {Object } configObj The parsed project configuration object
162163 * @param {string } experimentKey Key of experiment being validated
163164 * @param {string } userId ID of user
164165 * @param {Object } attributes Optional parameter for user's attributes
165166 * @return {boolean } True if user meets audience conditions
166167 */
167- DecisionService . prototype . __checkIfUserIsInAudience = function ( experimentKey , userId , attributes ) {
168- var experimentAudienceConditions = projectConfig . getExperimentAudienceConditions ( this . configObj , experimentKey ) ;
169- var audiencesById = projectConfig . getAudiencesById ( this . configObj ) ;
168+ DecisionService . prototype . __checkIfUserIsInAudience = function ( configObj , experimentKey , userId , attributes ) {
169+ var experimentAudienceConditions = projectConfig . getExperimentAudienceConditions ( configObj , experimentKey ) ;
170+ var audiencesById = projectConfig . getAudiencesById ( configObj ) ;
170171 this . logger . log ( LOG_LEVEL . DEBUG , sprintf ( LOG_MESSAGES . EVALUATING_AUDIENCES_COMBINED , MODULE_NAME , experimentKey , JSON . stringify ( experimentAudienceConditions ) ) ) ;
171172 var result = audienceEvaluator . evaluate ( experimentAudienceConditions , audiencesById , attributes , this . logger ) ;
172173 this . logger . log ( LOG_LEVEL . INFO , sprintf ( LOG_MESSAGES . AUDIENCE_EVALUATION_RESULT_COMBINED , MODULE_NAME , experimentKey , result . toString ( ) . toUpperCase ( ) ) ) ;
@@ -182,38 +183,40 @@ DecisionService.prototype.__checkIfUserIsInAudience = function(experimentKey, us
182183
183184/**
184185 * Given an experiment key and user ID, returns params used in bucketer call
186+ * @param configObj The parsed project configuration object
185187 * @param experimentKey Experiment key used for bucketer
186188 * @param bucketingId ID to bucket user into
187189 * @param userId ID of user to be bucketed
188190 * @return {Object }
189191 */
190- DecisionService . prototype . __buildBucketerParams = function ( experimentKey , bucketingId , userId ) {
192+ DecisionService . prototype . __buildBucketerParams = function ( configObj , experimentKey , bucketingId , userId ) {
191193 var bucketerParams = { } ;
192194 bucketerParams . experimentKey = experimentKey ;
193- bucketerParams . experimentId = projectConfig . getExperimentId ( this . configObj , experimentKey ) ;
195+ bucketerParams . experimentId = projectConfig . getExperimentId ( configObj , experimentKey ) ;
194196 bucketerParams . userId = userId ;
195- bucketerParams . trafficAllocationConfig = projectConfig . getTrafficAllocation ( this . configObj , experimentKey ) ;
196- bucketerParams . experimentKeyMap = this . configObj . experimentKeyMap ;
197- bucketerParams . groupIdMap = this . configObj . groupIdMap ;
198- bucketerParams . variationIdMap = this . configObj . variationIdMap ;
197+ bucketerParams . trafficAllocationConfig = projectConfig . getTrafficAllocation ( configObj , experimentKey ) ;
198+ bucketerParams . experimentKeyMap = configObj . experimentKeyMap ;
199+ bucketerParams . groupIdMap = configObj . groupIdMap ;
200+ bucketerParams . variationIdMap = configObj . variationIdMap ;
199201 bucketerParams . logger = this . logger ;
200202 bucketerParams . bucketingId = bucketingId ;
201203 return bucketerParams ;
202204} ;
203205
204206/**
205207 * Pull the stored variation out of the experimentBucketMap for an experiment/userId
208+ * @param {Object } configObj The parsed project configuration object
206209 * @param {Object } experiment
207210 * @param {String } userId
208211 * @param {Object } experimentBucketMap mapping experiment => { variation_id: <variationId> }
209212 * @return {Object } the stored variation or null if the user profile does not have one for the given experiment
210213 */
211- DecisionService . prototype . __getStoredVariation = function ( experiment , userId , experimentBucketMap ) {
214+ DecisionService . prototype . __getStoredVariation = function ( configObj , experiment , userId , experimentBucketMap ) {
212215 if ( experimentBucketMap . hasOwnProperty ( experiment . id ) ) {
213216 var decision = experimentBucketMap [ experiment . id ] ;
214217 var variationId = decision . variation_id ;
215- if ( this . configObj . variationIdMap . hasOwnProperty ( variationId ) ) {
216- return this . configObj . variationIdMap [ decision . variation_id ] ;
218+ if ( configObj . variationIdMap . hasOwnProperty ( variationId ) ) {
219+ return configObj . variationIdMap [ decision . variation_id ] ;
217220 } else {
218221 this . logger . log ( LOG_LEVEL . INFO , sprintf ( LOG_MESSAGES . SAVED_VARIATION_NOT_FOUND , MODULE_NAME , userId , variationId , experiment . key ) ) ;
219222 }
@@ -280,23 +283,24 @@ DecisionService.prototype.__saveUserProfile = function(experiment, variation, us
280283 * experiment properties (both objects), as well as a decisionSource property.
281284 * decisionSource indicates whether the decision was due to a rollout or an
282285 * experiment.
286+ * @param {Object } configObj The parsed project configuration object
283287 * @param {Object } feature A feature flag object from project configuration
284288 * @param {String } userId A string identifying the user, for bucketing
285289 * @param {Object } attributes Optional user attributes
286290 * @return {Object } An object with experiment, variation, and decisionSource
287291 * properties. If the user was not bucketed into a variation, the variation
288292 * property is null.
289293 */
290- DecisionService . prototype . getVariationForFeature = function ( feature , userId , attributes ) {
291- var experimentDecision = this . _getVariationForFeatureExperiment ( feature , userId , attributes ) ;
294+ DecisionService . prototype . getVariationForFeature = function ( configObj , feature , userId , attributes ) {
295+ var experimentDecision = this . _getVariationForFeatureExperiment ( configObj , feature , userId , attributes ) ;
292296 if ( experimentDecision . variation !== null ) {
293297 this . logger . log ( LOG_LEVEL . DEBUG , sprintf ( LOG_MESSAGES . USER_IN_FEATURE_EXPERIMENT , MODULE_NAME , userId , experimentDecision . variation . key , experimentDecision . experiment . key , feature . key ) ) ;
294298 return experimentDecision ;
295299 }
296300
297301 this . logger . log ( LOG_LEVEL . DEBUG , sprintf ( LOG_MESSAGES . USER_NOT_IN_FEATURE_EXPERIMENT , MODULE_NAME , userId , feature . key ) ) ;
298302
299- var rolloutDecision = this . _getVariationForRollout ( feature , userId , attributes ) ;
303+ var rolloutDecision = this . _getVariationForRollout ( configObj , feature , userId , attributes ) ;
300304 if ( rolloutDecision . variation !== null ) {
301305 this . logger . log ( LOG_LEVEL . DEBUG , sprintf ( LOG_MESSAGES . USER_IN_ROLLOUT , MODULE_NAME , userId , feature . key ) ) ;
302306 return rolloutDecision ;
@@ -306,24 +310,24 @@ DecisionService.prototype.getVariationForFeature = function(feature, userId, att
306310 return rolloutDecision ;
307311} ;
308312
309- DecisionService . prototype . _getVariationForFeatureExperiment = function ( feature , userId , attributes ) {
313+ DecisionService . prototype . _getVariationForFeatureExperiment = function ( configObj , feature , userId , attributes ) {
310314 var experiment = null ;
311315 var variationKey = null ;
312316
313317 if ( feature . hasOwnProperty ( 'groupId' ) ) {
314- var group = this . configObj . groupIdMap [ feature . groupId ] ;
318+ var group = configObj . groupIdMap [ feature . groupId ] ;
315319 if ( group ) {
316- experiment = this . _getExperimentInGroup ( group , userId ) ;
320+ experiment = this . _getExperimentInGroup ( configObj , group , userId ) ;
317321 if ( experiment && feature . experimentIds . indexOf ( experiment . id ) !== - 1 ) {
318- variationKey = this . getVariation ( experiment . key , userId , attributes ) ;
322+ variationKey = this . getVariation ( configObj , experiment . key , userId , attributes ) ;
319323 }
320324 }
321325 } else if ( feature . experimentIds . length > 0 ) {
322326 // If the feature does not have a group ID, then it can only be associated
323327 // with one experiment, so we look at the first experiment ID only
324- experiment = projectConfig . getExperimentFromId ( this . configObj , feature . experimentIds [ 0 ] , this . logger ) ;
328+ experiment = projectConfig . getExperimentFromId ( configObj , feature . experimentIds [ 0 ] , this . logger ) ;
325329 if ( experiment ) {
326- variationKey = this . getVariation ( experiment . key , userId , attributes ) ;
330+ variationKey = this . getVariation ( configObj , experiment . key , userId , attributes ) ;
327331 }
328332 } else {
329333 this . logger . log ( LOG_LEVEL . DEBUG , sprintf ( LOG_MESSAGES . FEATURE_HAS_NO_EXPERIMENTS , MODULE_NAME , feature . key ) ) ;
@@ -340,11 +344,11 @@ DecisionService.prototype._getVariationForFeatureExperiment = function(feature,
340344 } ;
341345} ;
342346
343- DecisionService . prototype . _getExperimentInGroup = function ( group , userId ) {
347+ DecisionService . prototype . _getExperimentInGroup = function ( configObj , group , userId ) {
344348 var experimentId = bucketer . bucketUserIntoExperiment ( group , userId , userId , this . logger ) ;
345349 if ( experimentId !== null ) {
346350 this . logger . log ( LOG_LEVEL . INFO , sprintf ( LOG_MESSAGES . USER_BUCKETED_INTO_EXPERIMENT_IN_GROUP , MODULE_NAME , userId , experimentId , group . id ) ) ;
347- var experiment = projectConfig . getExperimentFromId ( this . configObj , experimentId , this . logger ) ;
351+ var experiment = projectConfig . getExperimentFromId ( configObj , experimentId , this . logger ) ;
348352 if ( experiment ) {
349353 return experiment ;
350354 }
@@ -354,7 +358,7 @@ DecisionService.prototype._getExperimentInGroup = function(group, userId) {
354358 return null ;
355359} ;
356360
357- DecisionService . prototype . _getVariationForRollout = function ( feature , userId , attributes ) {
361+ DecisionService . prototype . _getVariationForRollout = function ( configObj , feature , userId , attributes ) {
358362 if ( ! feature . rolloutId ) {
359363 this . logger . log ( LOG_LEVEL . DEBUG , sprintf ( LOG_MESSAGES . NO_ROLLOUT_EXISTS , MODULE_NAME , feature . key ) ) ;
360364 return {
@@ -364,7 +368,7 @@ DecisionService.prototype._getVariationForRollout = function(feature, userId, at
364368 } ;
365369 }
366370
367- var rollout = this . configObj . rolloutIdMap [ feature . rolloutId ] ;
371+ var rollout = configObj . rolloutIdMap [ feature . rolloutId ] ;
368372 if ( ! rollout ) {
369373 this . logger . log ( LOG_LEVEL . ERROR , sprintf ( ERROR_MESSAGES . INVALID_ROLLOUT_ID , MODULE_NAME , feature . rolloutId , feature . key ) ) ;
370374 return {
@@ -394,17 +398,17 @@ DecisionService.prototype._getVariationForRollout = function(feature, userId, at
394398 var variationId ;
395399 var variation ;
396400 for ( index = 0 ; index < endIndex ; index ++ ) {
397- experiment = this . configObj . experimentKeyMap [ rollout . experiments [ index ] . key ] ;
401+ experiment = configObj . experimentKeyMap [ rollout . experiments [ index ] . key ] ;
398402
399- if ( ! this . __checkIfUserIsInAudience ( experiment . key , userId , attributes ) ) {
403+ if ( ! this . __checkIfUserIsInAudience ( configObj , experiment . key , userId , attributes ) ) {
400404 this . logger . log ( LOG_LEVEL . DEBUG , sprintf ( LOG_MESSAGES . USER_DOESNT_MEET_CONDITIONS_FOR_TARGETING_RULE , MODULE_NAME , userId , index + 1 ) ) ;
401405 continue ;
402406 }
403407
404408 this . logger . log ( LOG_LEVEL . DEBUG , sprintf ( LOG_MESSAGES . USER_MEETS_CONDITIONS_FOR_TARGETING_RULE , MODULE_NAME , userId , index + 1 ) ) ;
405- bucketerParams = this . __buildBucketerParams ( experiment . key , bucketingId , userId ) ;
409+ bucketerParams = this . __buildBucketerParams ( configObj , experiment . key , bucketingId , userId ) ;
406410 variationId = bucketer . bucket ( bucketerParams ) ;
407- variation = this . configObj . variationIdMap [ variationId ] ;
411+ variation = configObj . variationIdMap [ variationId ] ;
408412 if ( variation ) {
409413 this . logger . log ( LOG_LEVEL . DEBUG , sprintf ( LOG_MESSAGES . USER_BUCKETED_INTO_TARGETING_RULE , MODULE_NAME , userId , index + 1 ) ) ;
410414 return {
@@ -418,11 +422,11 @@ DecisionService.prototype._getVariationForRollout = function(feature, userId, at
418422 }
419423 }
420424
421- var everyoneElseExperiment = this . configObj . experimentKeyMap [ rollout . experiments [ endIndex ] . key ] ;
422- if ( this . __checkIfUserIsInAudience ( everyoneElseExperiment . key , userId , attributes ) ) {
423- bucketerParams = this . __buildBucketerParams ( everyoneElseExperiment . key , bucketingId , userId ) ;
425+ var everyoneElseExperiment = configObj . experimentKeyMap [ rollout . experiments [ endIndex ] . key ] ;
426+ if ( this . __checkIfUserIsInAudience ( configObj , everyoneElseExperiment . key , userId , attributes ) ) {
427+ bucketerParams = this . __buildBucketerParams ( configObj , everyoneElseExperiment . key , bucketingId , userId ) ;
424428 variationId = bucketer . bucket ( bucketerParams ) ;
425- variation = this . configObj . variationIdMap [ variationId ] ;
429+ variation = configObj . variationIdMap [ variationId ] ;
426430 if ( variation ) {
427431 this . logger . log ( LOG_LEVEL . DEBUG , sprintf ( LOG_MESSAGES . USER_BUCKETED_INTO_EVERYONE_TARGETING_RULE , MODULE_NAME , userId ) ) ;
428432 return {
@@ -468,7 +472,6 @@ module.exports = {
468472 /**
469473 * Creates an instance of the DecisionService.
470474 * @param {Object } options Configuration options
471- * @param {Object } options.configObj
472475 * @param {Object } options.userProfileService
473476 * @param {Object } options.logger
474477 * @return {Object } An instance of the DecisionService
0 commit comments